From d109cd61e415e13a8b97ba179b3a4a5a65096e49 Mon Sep 17 00:00:00 2001 From: jimco Date: Wed, 4 Dec 2024 10:36:50 +0800 Subject: [PATCH 001/172] feat: sync workflow feature from old lib --- apps/web/README.md | 55 +-- apps/web/package.json | 1 + apps/web/src/components/sidebar/index.tsx | 7 +- apps/web/src/components/sidebar/style.less | 4 + apps/web/src/main.tsx | 7 + .../workflow/components/action-log/index.tsx | 1 + .../workflow/components/edit-modal/index.tsx | 1 + .../components/import-modal/index.tsx | 1 + .../src/pages/workflow/components/index.ts | 0 .../workflow/components/log-modal/index.tsx | 1 + apps/web/src/pages/workflow/hooks/index.ts | 1 + .../src/pages/workflow/hooks/useColumns.tsx | 115 ++++++ apps/web/src/pages/workflow/index.tsx | 168 +++++++++ apps/web/src/pages/workflow/style.less | 10 + .../editor/components/config-panel/helper.ts | 20 + .../components/config-panel/hooks/index.ts | 1 + .../config-panel/hooks/useCommonFormItems.tsx | 65 ++++ .../editor/components/config-panel/index.tsx | 106 ++++++ .../editor/components/config-panel/style.less | 46 +++ .../editor/components/controls/index.tsx | 78 ++++ .../editor/components/controls/style.less | 20 + .../views/editor/components/edge/index.tsx | 74 ++++ .../views/editor/components/edge/style.less | 17 + .../views/editor/components/handle/index.tsx | 78 ++++ .../views/editor/components/handle/style.less | 87 +++++ .../editor/components/helper-lines/index.tsx | 65 ++++ .../editor/components/helper-lines/utils.ts | 168 +++++++++ .../editor/components/ifelse-node/index.tsx | 58 +++ .../workflow/views/editor/components/index.ts | 10 + .../components/node-container/index.tsx | 211 +++++++++++ .../components/node-container/style.less | 28 ++ .../editor/components/node-menu/index.tsx | 140 +++++++ .../editor/components/node-menu/style.less | 30 ++ .../views/editor/components/topbar/index.tsx | 27 ++ .../views/editor/components/topbar/style.less | 4 + .../pages/workflow/views/editor/constant.tsx | 170 +++++++++ .../workflow/views/editor/demo-data.json | 126 +++++++ .../workflow/views/editor/hooks/index.ts | 2 + .../views/editor/hooks/useInteractions.tsx | 143 +++++++ .../views/editor/hooks/useNodeTypes.tsx | 62 +++ .../workflow/views/editor/hooks/userTest.ts | 7 + .../src/pages/workflow/views/editor/index.tsx | 118 ++++++ .../pages/workflow/views/editor/style.less | 97 +++++ apps/web/src/routes/routes.tsx | 40 ++ apps/web/src/services/http/workflow.ts | 0 apps/web/src/typings.d.ts | 9 - apps/web/vite.config.ts | 7 +- packages/locales/src/helper.ts | 2 +- packages/locales/src/lang/cn/workflow.json | 1 + packages/locales/src/lang/en/global.json | 8 +- packages/locales/src/lang/en/workflow.json | 20 + packages/shared/package.json | 1 + .../icons/components/entity-filled.tsx | 34 ++ .../src/components/icons/components/index.ts | 2 + .../components/icons/components/workflow.tsx | 21 ++ .../shared/src/components/icons/index.tsx | 16 + packages/shared/src/utils/validators/index.ts | 3 +- packages/shared/types/common.d.ts | 9 + packages/shared/types/index.d.ts | 1 + packages/shared/types/workflow.d.ts | 353 ++++++++++++++++++ pnpm-lock.yaml | 142 ++++++- 61 files changed, 3054 insertions(+), 45 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/action-log/index.tsx create mode 100644 apps/web/src/pages/workflow/components/edit-modal/index.tsx create mode 100644 apps/web/src/pages/workflow/components/import-modal/index.tsx create mode 100644 apps/web/src/pages/workflow/components/index.ts create mode 100644 apps/web/src/pages/workflow/components/log-modal/index.tsx create mode 100644 apps/web/src/pages/workflow/hooks/index.ts create mode 100644 apps/web/src/pages/workflow/hooks/useColumns.tsx create mode 100644 apps/web/src/pages/workflow/index.tsx create mode 100644 apps/web/src/pages/workflow/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/controls/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/controls/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/edge/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/edge/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/handle/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/handle/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/index.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/node-container/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/node-menu/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/topbar/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/constant.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/demo-data.json create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/index.ts create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/userTest.ts create mode 100644 apps/web/src/pages/workflow/views/editor/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/style.less create mode 100644 apps/web/src/services/http/workflow.ts create mode 100644 packages/locales/src/lang/cn/workflow.json create mode 100644 packages/locales/src/lang/en/workflow.json create mode 100644 packages/shared/src/components/icons/components/entity-filled.tsx create mode 100644 packages/shared/src/components/icons/components/workflow.tsx create mode 100644 packages/shared/types/workflow.d.ts diff --git a/apps/web/README.md b/apps/web/README.md index 250fe996..e0a6015d 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,50 +1,51 @@ # Beaver IoT Web -This project is the Beaver IoT Web application, built upon the following core technologies: +本项目是 Beaver IoT Web 应用,基础技术栈包括: -- View Library: React -- Request Library: Axios -- Component Library: Ysd-iot -- State Management: Zustand -- Internationalization: react-intl-universal -- General Hooks: Ahooks +- 视图库:React +- 请求库:Axios +- 组件库:Ysd-iot +- 状态管理:Zustand +- 国际化:react-intl-universal +- 通用 Hook:Ahooks -## Directory Structure +## 目录结构 ``` @app/web ├── public ├── src -│ ├── assets # Image assets -│ ├── components # Global components -│ ├── hooks # Global hooks -│ ├── layouts # Layout components -│ ├── pages # Route page resources +│ ├── assets # 图片资源 +│ ├── components # 全局组件 +│ ├── hooks # 全局 Hooks +│ ├── layouts # 布局组件 +│ ├── pages # 路由页面资源 │ │ ├── page-a -│ │ │ ├── components # Page components -│ │ │ ├── index.ts # Page entry -│ │ │ ├── store.ts # Page shared state +│ │ │ ├── components # 页面组件 +│ │ │ ├── index.ts # 页面入口 +│ │ │ ├── store.ts # 页面共享状态 │ │ │ ├── style.ts │ │ │ └── ... │ │ │ │ │ └── page-b │ │ -│ ├── routes # Route configuration -│ ├── services # Common services -│ ├── stores # Global state -│ ├── styles # Global styles -│ ├── main.ts # Application entry -│ └── typings.d.ts # Type definitions +│ ├── routes # 路由配置 +│ ├── services # 通用服务 +│ ├── stores # 全局状态 +│ ├── styles # 全局样式 +│ ├── main.ts # 应用入口 +│ └── typings.d.ts # 类型约束 │ -├── index.html # Entry HTML +├── index.html # 入口 Html ├── package.json ├── tsconfig.json ├── tsconfig.node.json -└── vite.config.ts # Build configuration +└── vite.config.ts # 构建配置 ``` -## Development and Maintenance +## 开发维护 -### Alias Support +### 别名支持 + +- `'@': './src/*'` src 路径别名 -- `'@': './src/*'` src path alias \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index 29704203..6c5bebff 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -20,6 +20,7 @@ "@mui/material": "^6.1.0", "@mui/x-data-grid": "^7.16.0", "@mui/x-date-pickers": "^7.18.0", + "@xyflow/react": "^12.3.5", "ahooks": "^3.8.1", "axios": "^1.7.7", "chart.js": "^4.4.4", diff --git a/apps/web/src/components/sidebar/index.tsx b/apps/web/src/components/sidebar/index.tsx index bd916ee4..e55537a4 100644 --- a/apps/web/src/components/sidebar/index.tsx +++ b/apps/web/src/components/sidebar/index.tsx @@ -71,7 +71,12 @@ const Sidebar: React.FC = memo(({ menus, logoLinkTo = '/' }) => { // console.log({ userInfo }); return ( -
+
{menus?.map(menu => ( diff --git a/apps/web/src/components/sidebar/style.less b/apps/web/src/components/sidebar/style.less index c3a8682e..e5b08cea 100644 --- a/apps/web/src/components/sidebar/style.less +++ b/apps/web/src/components/sidebar/style.less @@ -8,6 +8,10 @@ background-color: var(--component-background); border-right: 1px solid var(--border-color-base); + &.hidden { + display: none; + } + &-logo { margin: @margin-md; } diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 3294ba1f..b1c0426d 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -8,10 +8,17 @@ import '@/styles/index.less'; const router = createBrowserRouter(routes, { basename: '/' }); const root = createRoot(document.getElementById('root')!); +// 国际化初始化 i18n.initI18n('web', 'EN'); +// 系统主题初始化 theme.initTheme(); +/** + * 注意:严格模式,且开发环境下,React 应用初始化时会刻意执行两次渲染,用于突出显示潜在问题。 + * + * https://zh-hans.react.dev/reference/react/StrictMode + */ root.render( diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx new file mode 100644 index 00000000..fb7c0970 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -0,0 +1 @@ +// 运行日志组件 diff --git a/apps/web/src/pages/workflow/components/edit-modal/index.tsx b/apps/web/src/pages/workflow/components/edit-modal/index.tsx new file mode 100644 index 00000000..fd933a5d --- /dev/null +++ b/apps/web/src/pages/workflow/components/edit-modal/index.tsx @@ -0,0 +1 @@ +// 添加/编辑弹窗组件 diff --git a/apps/web/src/pages/workflow/components/import-modal/index.tsx b/apps/web/src/pages/workflow/components/import-modal/index.tsx new file mode 100644 index 00000000..462e21db --- /dev/null +++ b/apps/web/src/pages/workflow/components/import-modal/index.tsx @@ -0,0 +1 @@ +// DSL 导入弹窗组件 diff --git a/apps/web/src/pages/workflow/components/index.ts b/apps/web/src/pages/workflow/components/index.ts new file mode 100644 index 00000000..e69de29b diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx new file mode 100644 index 00000000..762f0ce9 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -0,0 +1 @@ +// 日志弹窗组件 diff --git a/apps/web/src/pages/workflow/hooks/index.ts b/apps/web/src/pages/workflow/hooks/index.ts new file mode 100644 index 00000000..c121352e --- /dev/null +++ b/apps/web/src/pages/workflow/hooks/index.ts @@ -0,0 +1 @@ +export { default as useColumns, type UseColumnsProps, type TableRowDataType } from './useColumns'; diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx new file mode 100644 index 00000000..c9be75ae --- /dev/null +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -0,0 +1,115 @@ +import { useMemo } from 'react'; +import { Stack, IconButton } from '@mui/material'; +import { useI18n, useTime } from '@milesight/shared/src/hooks'; +import { ListAltIcon, DeleteOutlineIcon } from '@milesight/shared/src/components'; +import { Tooltip, type ColumnType } from '@/components'; +import { type DeviceAPISchema } from '@/services/http'; + +type OperationType = 'detail' | 'delete'; + +export type TableRowDataType = ObjectToCamelCase< + DeviceAPISchema['getList']['response']['content'][0] +>; + +export interface UseColumnsProps { + /** + * 操作 Button 点击回调 + */ + onButtonClick: (type: OperationType, record: T) => void; +} + +const useColumns = ({ onButtonClick }: UseColumnsProps) => { + const { getIntlText } = useI18n(); + const { getTimeFormat } = useTime(); + + const columns: ColumnType[] = useMemo(() => { + return [ + { + field: 'name', + headerName: getIntlText('common.label.name'), + flex: 1, + minWidth: 150, + ellipsis: true, + // disableColumnMenu: false, + }, + { + field: 'remark', + headerName: getIntlText('common.label.remark'), + flex: 1, + minWidth: 150, + ellipsis: true, + // disableColumnMenu: false, + }, + { + field: 'createdAt', + headerName: getIntlText('common.label.create_time'), + flex: 1, + minWidth: 150, + ellipsis: true, + renderCell({ value }) { + return getTimeFormat(value); + }, + }, + { + field: 'updatedAt', + headerName: getIntlText('common.label.update_time'), + flex: 1, + minWidth: 150, + ellipsis: true, + renderCell({ value }) { + return getTimeFormat(value); + }, + }, + { + field: 'status', + headerName: getIntlText('common.label.enable_status'), + ellipsis: true, + flex: 2, + minWidth: 200, + }, + { + field: '$operation', + headerName: getIntlText('common.label.operation'), + flex: 2, + minWidth: 100, + renderCell({ row }) { + return ( + + + onButtonClick('detail', row)} + > + + + + + onButtonClick('delete', row)} + > + + + + + ); + }, + }, + ]; + }, [getIntlText, getTimeFormat, onButtonClick]); + + return columns; +}; + +export default useColumns; diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx new file mode 100644 index 00000000..e9b90d70 --- /dev/null +++ b/apps/web/src/pages/workflow/index.tsx @@ -0,0 +1,168 @@ +import { useState, useMemo, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button, Stack } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { + AddIcon, + DeleteOutlineIcon, + SystemUpdateAltIcon, + toast, +} from '@milesight/shared/src/components'; +import { Breadcrumbs, TablePro, useConfirm } from '@/components'; +import { deviceAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; +import './style.less'; + +const Workflow = () => { + const navigate = useNavigate(); + const { getIntlText } = useI18n(); + + // ---------- 列表数据相关逻辑 ---------- + const [keyword, setKeyword] = useState(); + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); + const [selectedIds, setSelectedIds] = useState([]); + const { + data: workflowList, + loading, + run: getWorkflowList, + } = useRequest( + async () => { + // const { page, pageSize } = paginationModel; + // const [error, resp] = await awaitWrap( + // deviceAPI.getList({ + // name: keyword, + // page_size: pageSize, + // page_number: page + 1, + // }), + // ); + // const data = getResponseData(resp); + + // // console.log({ error, resp }); + // if (error || !data || !isRequestSuccess(resp)) return; + + // return objectToCamelCase(data); + return {}; + }, + { + debounceWait: 300, + refreshDeps: [keyword, paginationModel], + }, + ); + + // ---------- 数据删除相关逻辑 ---------- + const confirm = useConfirm(); + const handleDeleteConfirm = useCallback( + (ids?: ApiKey[]) => { + const idsToDelete = ids || [...selectedIds]; + + confirm({ + title: getIntlText('common.label.delete'), + description: getIntlText('device.message.delete_tip'), + confirmButtonText: getIntlText('common.label.delete'), + confirmButtonProps: { + color: 'error', + }, + onConfirm: async () => { + const [error, resp] = await awaitWrap( + deviceAPI.deleteDevices({ device_id_list: idsToDelete }), + ); + + // console.log({ error, resp }); + if (error || !isRequestSuccess(resp)) return; + + getWorkflowList(); + setSelectedIds([]); + toast.success(getIntlText('common.message.delete_success')); + }, + }); + }, + [confirm, getIntlText, getWorkflowList, selectedIds], + ); + + // ---------- Table 渲染相关 ---------- + const toolbarRender = useMemo(() => { + return ( + + + + + + ); + }, [getIntlText, handleDeleteConfirm, selectedIds]); + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( + (type, record) => { + // console.log(type, record); + switch (type) { + case 'detail': { + // TODO: workflow id 传参 + navigate('/workflow/editor'); + break; + } + case 'delete': { + handleDeleteConfirm([record.id]); + break; + } + default: { + break; + } + } + }, + [navigate, handleDeleteConfirm], + ); + const columns = useColumns({ onButtonClick: handleTableBtnClick }); + + return ( +
+ +
+
+ + checkboxSelection + loading={loading} + columns={columns} + // rows={deviceData?.content} + // rowCount={workflowList?.total || 0} + paginationModel={paginationModel} + rowSelectionModel={selectedIds} + // isRowSelectable={({ row }) => row.deletable} + toolbarRender={toolbarRender} + onPaginationModelChange={setPaginationModel} + onRowSelectionModelChange={setSelectedIds} + onRowDoubleClick={({ row }) => { + navigate(`/device/detail/${row.id}`, { state: row }); + }} + onSearch={setKeyword} + onRefreshButtonClick={getWorkflowList} + /> +
+
+
+ ); +}; + +export default Workflow; diff --git a/apps/web/src/pages/workflow/style.less b/apps/web/src/pages/workflow/style.less new file mode 100644 index 00000000..d57aa551 --- /dev/null +++ b/apps/web/src/pages/workflow/style.less @@ -0,0 +1,10 @@ +.@{prefix}-view-workflow { + position: relative; + padding: @padding-lg; + + .@{prefix}-view__inner { + display: flex; + flex-direction: column; + padding: @padding-lg; + } +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts new file mode 100644 index 00000000..92fe1daf --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts @@ -0,0 +1,20 @@ +import { + checkRequired, + checkRangeLength, + type Validate, +} from '@milesight/shared/src/utils/validators'; + +/** + * 节点数据校验器配置 + */ +export const validatorsConfig: Record> = { + /** 节点名称 */ + name: { + checkRequired: checkRequired(), + checkRangeLength: checkRangeLength({ min: 1, max: 50 }), + }, + /** 节点备注 */ + remark: { + checkRangeLength: checkRangeLength({ min: 1, max: 1000 }), + }, +}; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts new file mode 100644 index 00000000..740fb8fa --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts @@ -0,0 +1 @@ +export { default as useCommonFormItems, type CommonFormDataProps } from './useCommonFormItems'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx new file mode 100644 index 00000000..a4733f18 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx @@ -0,0 +1,65 @@ +import { useMemo } from 'react'; +import { type ControllerProps } from 'react-hook-form'; +import { TextField } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { checkRequired } from '@milesight/shared/src/utils/validators'; + +/** + * 表单数据类型 + */ +export type CommonFormDataProps = Record; + +const useCommonFormItems = () => { + const { getIntlText } = useI18n(); + + const formItems = useMemo(() => { + const result: ControllerProps[] = []; + + result.push( + { + name: 'name', + rules: { + validate: { checkRequired: checkRequired() }, + }, + defaultValue: '', + render({ field: { onChange, value }, fieldState: { error } }) { + return ( + + ); + }, + }, + { + name: 'remark', + defaultValue: '', + render({ field: { onChange, value }, fieldState: { error } }) { + return ( + + ); + }, + }, + ); + + return result; + }, [getIntlText]); + + return formItems; +}; + +export default useCommonFormItems; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx new file mode 100644 index 00000000..307e018c --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -0,0 +1,106 @@ +import { useState, useCallback, useMemo } from 'react'; +import { + Panel, + useNodes, + useReactFlow, + type Node, + type ReactFlowProps, + type UseOnSelectionChangeOptions, +} from '@xyflow/react'; +import cls from 'classnames'; +import { Paper, Stack, IconButton } from '@mui/material'; +import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; +// import { FormControl, InputLabel, Select, MenuItem } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; +import { basicNodeConfigs } from '../../constant'; +import { useCommonFormItems, type CommonFormDataProps } from './hooks'; +import './style.less'; + +/** + * 配置面板组件 + */ +const ConfigPanel = () => { + const { getIntlText } = useI18n(); + + // ---------- 节点相关逻辑处理 ---------- + const nodes = useNodes(); + const { updateNode } = useReactFlow(); + const selectedNode = useMemo(() => { + const selectedNodes = nodes.filter(item => item.selected); + const node = selectedNodes?.[0]; + + if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { + return; + } + + return node; + }, [nodes]); + const nodeConfig = useMemo(() => { + if (!selectedNode) return; + + return basicNodeConfigs[selectedNode.type as WorkflowNodeType]; + }, [selectedNode]); + + // ---------- 表单相关逻辑处理 ---------- + const { control, formState, handleSubmit, reset } = useForm(); + const commonFormItems = useCommonFormItems(); + + return ( + + {nodeConfig?.labelIntlKey && ( +
+
+ + + {nodeConfig.icon} + + {getIntlText(nodeConfig.labelIntlKey)} + + + {nodeConfig.testable && ( + + console.log('execute testing or popup test panel') + } + > + + + )} + { + if (!selectedNode) return; + updateNode(selectedNode.id, { + selected: false, + }); + }} + > + + + +
+
+ {commonFormItems.map(props => ( + + {...props} + key={props.name} + control={control} + /> + ))} + Other Arguments... +
+
+ )} +
+ ); +}; + +export default ConfigPanel; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less new file mode 100644 index 00000000..4e5a4142 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less @@ -0,0 +1,46 @@ +.@{prefix}-workflow-panel-config-root.react-flow__panel.right { + z-index: 20; + width: 445px; + height: 100%; + padding: @margin-xs; + margin: 0; + + &.hidden { + display: none; + } +} + +.@{prefix}-workflow-panel-config { + height: 100%; + background-color: var(--component-background); + border-radius: @border-radius-sm; + box-shadow: @shadow-2; + + &-header { + position: relative; + display: flex; + align-items: center; + justify-content: space-between; + padding: @padding-lg @padding-xl; + + .icon { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + color: var(--white); + border-radius: @border-radius-sm; + } + + .title { + .text-size(@font-size-xxl); + + font-weight: @font-weight-bold; + } + } + + &-body { + padding: 0 @padding-xl @padding-lg; + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx new file mode 100644 index 00000000..1779716f --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx @@ -0,0 +1,78 @@ +import React, { useState, useCallback } from 'react'; +import { Panel, useReactFlow, useViewport } from '@xyflow/react'; +import { Stack, Paper, ButtonGroup, Button } from '@mui/material'; +import { + ZoomInIcon, + ZoomOutIcon, + MyLocationIcon, + AddCircleIcon, +} from '@milesight/shared/src/components'; +import NodeMenu from '../node-menu'; +import './style.less'; + +export interface ControlsProps { + /** + * 最小缩放比例 + */ + minZoom?: number; + + /** + * 最大缩放比例 + */ + maxZoom?: number; +} + +/** + * 工作流编辑器工具栏 + */ +const Controls: React.FC = ({ minZoom, maxZoom }) => { + const { zoom } = useViewport(); + const { zoomIn, zoomOut, fitView } = useReactFlow(); + + // ---------- Add Button Click Callback ---------- + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = useCallback((e: React.MouseEvent) => { + setAnchorEl(e.currentTarget); + e.stopPropagation(); + }, []); + + return ( + + + + + + + + + + + + setAnchorEl(null)} + /> + + + + ); +}; + +export default React.memo(Controls); diff --git a/apps/web/src/pages/workflow/views/editor/components/controls/style.less b/apps/web/src/pages/workflow/views/editor/components/controls/style.less new file mode 100644 index 00000000..445a8a2e --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/controls/style.less @@ -0,0 +1,20 @@ +.@{prefix}-workflow-controls-root { + &.react-flow__panel { + margin: @margin-lg; + } + + .@{mui-prefix}Paper-root { + box-shadow: @shadow-2; + } + + .@{mui-prefix}ButtonGroup-root { + .@{mui-prefix}ButtonBase-root { + color: var(--text-color-secondary); + border-color: var(--border-color-base); + + &.@{mui-prefix}-disabled { + opacity: 0.5; + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx new file mode 100644 index 00000000..969659ea --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx @@ -0,0 +1,74 @@ +import React, { useState, useCallback } from 'react'; +import { + BaseEdge, + EdgeLabelRenderer, + getBezierPath, + useEdges, + type EdgeProps, +} from '@xyflow/react'; +import { Stack } from '@mui/material'; +import { AddCircleIcon } from '@milesight/shared/src/components'; +import NodeMenu from '../node-menu'; +import './style.less'; + +/** + * 自定义连线组件 + */ +const AddableEdge = ({ + id, + data, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, + selected, + style = {}, + markerEnd, +}: EdgeProps) => { + const edges = useEdges(); + const edge = edges.find(edge => edge.id === id); + const [edgePath, labelX, labelY] = getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + // ---------- Add Button Click Callback ---------- + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = useCallback((e: React.MouseEvent) => { + setAnchorEl(e.currentTarget); + e.stopPropagation(); + }, []); + + return ( + <> + + {(selected || data?.$hovering) && ( + +
+ + + + setAnchorEl(null)} + anchorEl={anchorEl} + /> +
+
+ )} + + ); +}; + +export default React.memo(AddableEdge); diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/style.less b/apps/web/src/pages/workflow/views/editor/components/edge/style.less new file mode 100644 index 00000000..fae3c718 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/edge/style.less @@ -0,0 +1,17 @@ +.@{prefix}-workflow-edge-label { + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + color: var(--primary-color-base); + background-color: var(--component-background); + border-radius: 50%; + pointer-events: all; + cursor: pointer; + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-lg; + pointer-events: none; + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx b/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx new file mode 100644 index 00000000..e33d2d05 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx @@ -0,0 +1,78 @@ +import React, { useState, useCallback, forwardRef } from 'react'; +import cls from 'classnames'; +import { useDebounceFn } from 'ahooks'; +import { Handle as XHandle, useEdges, type HandleProps, type NodeProps } from '@xyflow/react'; +import { Stack } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { AddCircleIcon } from '@milesight/shared/src/components'; +import NodeMenu from '../node-menu'; +import './style.less'; + +export interface Props extends HandleProps { + /** + * Node Props + */ + nodeProps: NodeProps; +} + +/** + * Custom Handle Component + */ +const Handle = forwardRef & Props>( + ({ nodeProps, className, ...props }, ref) => { + const { getIntlHtml } = useI18n(); + const edges = useEdges(); + const targetAddEnabled = edges.every(edge => edge.target !== nodeProps.id); + + // ---------- Handle Tooltip Open ---------- + const [showTooltip, setShowTooltip] = useState(false); + const { run: handleMouseEnter, cancel: cancelHandleMouseEnter } = useDebounceFn( + () => { + if (props.type === 'target' && !targetAddEnabled) return; + setShowTooltip(true); + }, + { wait: 500 }, + ); + + // ---------- Handle Click Callback ---------- + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = useCallback((e: React.MouseEvent) => { + setAnchorEl(e.currentTarget); + e.stopPropagation(); + }, []); + + return ( + <> + { + setShowTooltip(false); + cancelHandleMouseEnter(); + }} + > + {/* Use Custom Tooltip, resolve the issue of Edge connect failure when Mui Tooltip component is enabled */} + + {getIntlHtml('workflow.label.handle_tooltip')} + + + + + + setAnchorEl(null)} anchorEl={anchorEl} /> + + ); + }, +); + +export default Handle; diff --git a/apps/web/src/pages/workflow/views/editor/components/handle/style.less b/apps/web/src/pages/workflow/views/editor/components/handle/style.less new file mode 100644 index 00000000..03fdf0a1 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/handle/style.less @@ -0,0 +1,87 @@ +.@{prefix}-workflow { + & &-handle { + width: 2px; + min-width: auto; + height: 16px; + color: var(--primary-color-base); + background-color: var(--primary-color-base); + border: none; + border-radius: 0; + + .@{mui-prefix}SvgIcon-root { + display: none; + font-size: @font-size-lg; + pointer-events: none; + } + + // &.react-flow__handle-left { + // left: -1px; + // } + + // &.react-flow__handle-right { + // right: -1px; + // } + + &.connectingto { + .@{prefix}-workflow-handle-tooltip { + display: none; + } + } + } + + & &-handle-tooltip { + position: absolute; + top: -12px; + left: 0; + z-index: 1; + min-width: 145px; + padding: @padding-xs @padding-sm; + font-size: @font-size-base; + color: var(--white); + text-align: left; + background-color: var(--gray-background-3); + border-radius: @border-radius-base; + pointer-events: none; + transform: translate(-50%, -100%); + + &::after { + position: absolute; + bottom: -15px; + left: 50%; + content: ''; + .triangle(8px, { top: var(--gray-background-3) }); + } + + &.hidden { + display: none; + } + } + + .react-flow__node { + .@{prefix}-workflow-handle.is-menu-open { + width: 16px; + background-color: var(--component-background); + border-radius: 16px; + cursor: pointer; + + .@{mui-prefix}SvgIcon-root { + display: block; + } + } + } + + .react-flow__node:hover, + .react-flow__node.selected { + .@{prefix}-workflow-handle.source, + .@{prefix}-workflow-handle.target.target-enable-add { + width: 16px; + background-color: var(--component-background); + border-radius: 16px; + cursor: pointer; + + .@{mui-prefix}SvgIcon-root { + display: block; + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx b/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx new file mode 100644 index 00000000..2a4e2add --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx @@ -0,0 +1,65 @@ +import { CSSProperties, useEffect, useRef } from 'react'; +import { ReactFlowState, useStore } from '@xyflow/react'; + +export * from './utils'; + +const canvasStyle: CSSProperties = { + width: '100%', + height: '100%', + position: 'absolute', + zIndex: 10, + pointerEvents: 'none', +}; + +const storeSelector = (state: ReactFlowState) => ({ + width: state.width, + height: state.height, + transform: state.transform, +}); + +export type HelperLinesProps = { + horizontal?: number; + vertical?: number; +}; + +/** + * 辅助线渲染器 + */ +const HelperLinesRenderer = ({ horizontal, vertical }: HelperLinesProps) => { + const { width, height, transform } = useStore(storeSelector); + + const canvasRef = useRef(null); + + useEffect(() => { + const canvas = canvasRef.current; + const ctx = canvas?.getContext('2d'); + + if (!ctx || !canvas) { + return; + } + + const dpi = window.devicePixelRatio; + canvas.width = width * dpi; + canvas.height = height * dpi; + + ctx.scale(dpi, dpi); + ctx.clearRect(0, 0, width, height); + ctx.strokeStyle = '#0041d0'; + + if (typeof vertical === 'number') { + ctx.moveTo(vertical * transform[2] + transform[0], 0); + ctx.lineTo(vertical * transform[2] + transform[0], height); + ctx.stroke(); + } + + if (typeof horizontal === 'number') { + ctx.moveTo(0, horizontal * transform[2] + transform[1]); + ctx.lineTo(width, horizontal * transform[2] + transform[1]); + ctx.stroke(); + } + }, [width, height, transform, horizontal, vertical]); + + return ; +}; + +export default HelperLinesRenderer; diff --git a/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts b/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts new file mode 100644 index 00000000..d2d3ec63 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts @@ -0,0 +1,168 @@ +import { Node, NodePositionChange, XYPosition } from '@xyflow/react'; + +type GetHelperLinesResult = { + horizontal?: number; + vertical?: number; + snapPosition: Partial; +}; + +/** + * 根据当前节点及周围节点位置计算辅助线位置 + */ +export function getHelperLines( + change: NodePositionChange, + nodes: WorkflowNode[], + distance = 5, +): GetHelperLinesResult { + const defaultResult = { + horizontal: undefined, + vertical: undefined, + snapPosition: { x: undefined, y: undefined }, + }; + const nodeA = nodes.find(node => node.id === change.id); + + if (!nodeA || !change.position) { + return defaultResult; + } + + const nodeABounds = { + left: change.position.x, + right: change.position.x + (nodeA.measured?.width ?? 0), + top: change.position.y, + bottom: change.position.y + (nodeA.measured?.height ?? 0), + width: nodeA.measured?.width ?? 0, + height: nodeA.measured?.height ?? 0, + }; + + let horizontalDistance = distance; + let verticalDistance = distance; + + return nodes + .filter(node => node.id !== nodeA.id) + .reduce((result, nodeB) => { + const nodeBBounds = { + left: nodeB.position.x, + right: nodeB.position.x + (nodeB.measured?.width ?? 0), + top: nodeB.position.y, + bottom: nodeB.position.y + (nodeB.measured?.height ?? 0), + width: nodeB.measured?.width ?? 0, + height: nodeB.measured?.height ?? 0, + }; + + // |‾‾‾‾‾‾‾‾‾‾‾| + // | A | + // |___________| + // | + // | + // |‾‾‾‾‾‾‾‾‾‾‾| + // | B | + // |___________| + const distanceLeftLeft = Math.abs(nodeABounds.left - nodeBBounds.left); + + if (distanceLeftLeft < verticalDistance) { + result.snapPosition.x = nodeBBounds.left; + result.vertical = nodeBBounds.left; + verticalDistance = distanceLeftLeft; + } + + // |‾‾‾‾‾‾‾‾‾‾‾| + // | A | + // |___________| + // | + // | + // |‾‾‾‾‾‾‾‾‾‾‾| + // | B | + // |___________| + const distanceRightRight = Math.abs(nodeABounds.right - nodeBBounds.right); + + if (distanceRightRight < verticalDistance) { + result.snapPosition.x = nodeBBounds.right - nodeABounds.width; + result.vertical = nodeBBounds.right; + verticalDistance = distanceRightRight; + } + + // |‾‾‾‾‾‾‾‾‾‾‾| + // | A | + // |___________| + // | + // | + // |‾‾‾‾‾‾‾‾‾‾‾| + // | B | + // |___________| + const distanceLeftRight = Math.abs(nodeABounds.left - nodeBBounds.right); + + if (distanceLeftRight < verticalDistance) { + result.snapPosition.x = nodeBBounds.right; + result.vertical = nodeBBounds.right; + verticalDistance = distanceLeftRight; + } + + // |‾‾‾‾‾‾‾‾‾‾‾| + // | A | + // |___________| + // | + // | + // |‾‾‾‾‾‾‾‾‾‾‾| + // | B | + // |___________| + const distanceRightLeft = Math.abs(nodeABounds.right - nodeBBounds.left); + + if (distanceRightLeft < verticalDistance) { + result.snapPosition.x = nodeBBounds.left - nodeABounds.width; + result.vertical = nodeBBounds.left; + verticalDistance = distanceRightLeft; + } + + // |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾| + // | A | | B | + // |___________| |___________| + const distanceTopTop = Math.abs(nodeABounds.top - nodeBBounds.top); + + if (distanceTopTop < horizontalDistance) { + result.snapPosition.y = nodeBBounds.top; + result.horizontal = nodeBBounds.top; + horizontalDistance = distanceTopTop; + } + + // |‾‾‾‾‾‾‾‾‾‾‾| + // | A | + // |___________|_________________ + // | | + // | B | + // |___________| + const distanceBottomTop = Math.abs(nodeABounds.bottom - nodeBBounds.top); + + if (distanceBottomTop < horizontalDistance) { + result.snapPosition.y = nodeBBounds.top - nodeABounds.height; + result.horizontal = nodeBBounds.top; + horizontalDistance = distanceBottomTop; + } + + // |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾| + // | A | | B | + // |___________|_____|___________| + const distanceBottomBottom = Math.abs(nodeABounds.bottom - nodeBBounds.bottom); + + if (distanceBottomBottom < horizontalDistance) { + result.snapPosition.y = nodeBBounds.bottom - nodeABounds.height; + result.horizontal = nodeBBounds.bottom; + horizontalDistance = distanceBottomBottom; + } + + // |‾‾‾‾‾‾‾‾‾‾‾| + // | B | + // | | + // |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ + // | A | + // |___________| + const distanceTopBottom = Math.abs(nodeABounds.top - nodeBBounds.bottom); + + if (distanceTopBottom < horizontalDistance) { + result.snapPosition.y = nodeBBounds.bottom; + result.horizontal = nodeBBounds.bottom; + horizontalDistance = distanceTopBottom; + } + + return result; + }, defaultResult); +} diff --git a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx new file mode 100644 index 00000000..3adbd7c0 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx @@ -0,0 +1,58 @@ +import React from 'react'; +import { Position, type Node, type NodeProps } from '@xyflow/react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import Handle from '../handle'; +import NodeContainer from '../node-container'; +import { basicNodeConfigs } from '../../constant'; + +export type IfElseNode = Node; + +const nodeConfig = basicNodeConfigs.ifelse; + +/** + * 输入节点 + */ +const IfElseNode: React.FC> = props => { + const { getIntlText } = useI18n(); + console.log(props); + + return ( + , + // TODO: 根据条件动态渲染多个操作柄 + , + , + , + ]} + > + {/* TODO: render conditions detail... */} + render cases... + + ); +}; + +export default React.memo(IfElseNode); diff --git a/apps/web/src/pages/workflow/views/editor/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/index.ts new file mode 100644 index 00000000..04e28b11 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/index.ts @@ -0,0 +1,10 @@ +export { default as Topbar } from './topbar'; +export { default as Controls, type ControlsProps } from './controls'; +export { default as ConfigPanel } from './config-panel'; + +export { default as IfElseNode } from './ifelse-node'; +export { default as NodeContainer, type NodeContainerProps } from './node-container'; +export { default as Handle, type Props as HandleProps } from './handle'; +export { default as Edge } from './edge'; +export { default as HelperLines, getHelperLines } from './helper-lines'; +export { default as NodeMenu } from './node-menu'; diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx new file mode 100644 index 00000000..35ee4ce0 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -0,0 +1,211 @@ +import React, { Fragment, useState, useMemo } from 'react'; +import { Position, type NodeProps } from '@xyflow/react'; +import cls from 'classnames'; +import { Menu, MenuItem } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { CheckCircleIcon, ErrorIcon, LoopIcon } from '@milesight/shared/src/components'; +import Handle from '../handle'; +import { basicNodeConfigs } from '../../constant'; +import './style.less'; + +export type NodeContainerProps = { + /** + * 节点类型 + */ + type: WorkflowNodeType; + + /** + * 节点 Title 的国际化文案 Key + */ + title: string; + + /** + * 节点 Icon + */ + icon: React.ReactNode; + + /** + * 节点 Icon 背景色 + */ + iconBgColor: string; + + /** + * 节点操作柄集合,默认会有左右操作柄 + */ + handles?: React.ReactNode[]; + + /** + * 节点所有属性 + */ + nodeProps: NodeProps; + + /** + * 节点详情内容 + */ + children?: React.ReactNode; +}; + +/** + * Node Status Map + */ +const statusMap: Record< + WorkflowNodeStatus, + { + icon: React.ReactNode; + } +> = { + error: { + icon: , + }, + success: { + icon: , + }, + loading: { + icon: , + }, +}; + +/** + * 通用节点容器 + */ +const NodeContainer: React.FC = ({ + type, + title, + icon, + iconBgColor, + nodeProps, + handles = [ + , + , + ], + children, +}) => { + const { getIntlText } = useI18n(); + const status = nodeProps?.data?.$status as WorkflowNodeStatus; + + // ---------- 右键菜单 ---------- + const [contextMenu, setContextMenu] = useState<{ + mouseX: number; + mouseY: number; + } | null>(null); + const [anchorEl, setAnchorEl] = useState(); + const isEntryNode = basicNodeConfigs[nodeProps.type as WorkflowNodeType]?.category === 'entry'; + + /** + * 「变更节点」子菜单项集合 + * + * TODO: 入口节点只可变更为其他入口接口,不可删除? + */ + const nodeMenus = useMemo(() => { + const result = Object.values(basicNodeConfigs).filter(item => { + return item.category === 'entry' && item.type !== nodeProps.type; + }); + + return result; + }, [nodeProps]); + + /** + * 右键菜单点击回调 + */ + const handleContextMenu = (event: React.MouseEvent) => { + event.preventDefault(); + setContextMenu( + contextMenu === null + ? { + mouseX: event.clientX + 2, + mouseY: event.clientY - 6, + } + : null, + ); + }; + + /** + * 菜单项点击回调 + */ + const handleMenuItemClick = ( + type: 'change' | 'delete', + record: NodeProps, + targetNodeType?: WorkflowNodeType, + ) => { + console.log({ type, record, targetNodeType }); + + setAnchorEl(null); + setContextMenu(null); + }; + + return ( + <> + {/* eslint-disable-next-line react/no-array-index-key */} + {handles?.map((handle, index) => {handle})} +
+ setContextMenu(null)} + anchorReference="anchorPosition" + anchorPosition={ + contextMenu !== null + ? { top: contextMenu.mouseY, left: contextMenu.mouseX } + : undefined + } + > + {isEntryNode && ( + { + setAnchorEl(e.currentTarget); + }} + > + {getIntlText('workflow.context_menu.title_change_node')} + + )} + handleMenuItemClick('delete', nodeProps)}> + {getIntlText('common.label.delete')} + + + setAnchorEl(null)} + > + {nodeMenus.map(node => ( + handleMenuItemClick('change', nodeProps, node.type)} + > + + {node.icon} + + {getIntlText(node.labelIntlKey)} + + ))} + +
+ + {icon} + + {title} + {!!status && ( + {statusMap[status].icon} + )} +
+ {children &&
{children}
} +
+ + ); +}; + +export default NodeContainer; diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/style.less b/apps/web/src/pages/workflow/views/editor/components/node-container/style.less new file mode 100644 index 00000000..3999b4f6 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/style.less @@ -0,0 +1,28 @@ +.@{prefix}-workflow-node-contextmenu { + .@{mui-prefix}Menu-list { + width: 160px; + } +} + +.@{prefix}-workflow-node-contextmenu-sub { + .@{mui-prefix}Menu-list { + width: 240px; + } + + .@{mui-prefix}MenuItem-root { + .icon { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-right: @margin-sm; + color: var(--white); + border-radius: @border-radius-sm; + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-lg; + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx new file mode 100644 index 00000000..0727aa48 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx @@ -0,0 +1,140 @@ +import { useState, useMemo, useLayoutEffect } from 'react'; +import { useReactFlow, type HandleType } from '@xyflow/react'; +import { Menu, MenuItem, type MenuProps } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { nodeCategoryConfigs, basicNodeConfigs, type NodeConfigItemType } from '../../constant'; +import useInteractions from '../../hooks/useInteractions'; +import './style.less'; + +interface Props extends MenuProps { + /** + * 节点信息 + */ + node?: WorkflowNode; + + /** + * 边信息 + */ + edge?: WorkflowEdge; + + /** + * 操作柄类型 + */ + handleType?: HandleType; + + /** + * 菜单项点击回调 + */ + onItemClick?: (nodeType: WorkflowNodeType) => void; +} + +/** + * 节点菜单 + */ +const NodeMenu = ({ + node, + edge, + handleType = 'source', + open, + onItemClick, + onClose, + ...menuProps +}: Props) => { + const { getIntlText } = useI18n(); + const menuOptions = useMemo(() => { + const result: Partial< + Record< + WorkflowNodeCategoryType, + (NodeConfigItemType & { + nodeName: string; + categoryName: string; + })[] + > + > = {}; + + Object.values(basicNodeConfigs).forEach(item => { + const { category, labelIntlKey } = item; + const cateConfig = nodeCategoryConfigs[category]; + + if (!category || category === 'entry') return; + result[category] = result[category] || []; + result[category].push({ + ...item, + nodeName: getIntlText(labelIntlKey), + categoryName: getIntlText(cateConfig.labelIntlKey), + }); + }); + + return result; + }, [getIntlText]); + + // ---------- Menu Open ---------- + const [innerOpen, setInnerOpen] = useState(false); + const handleInnerClose: MenuProps['onClose'] = (e, reason) => { + setInnerOpen(false); + onClose?.(e, reason); + // @ts-ignore + e.stopPropagation?.(); + }; + + // Sync open state from parent component + useLayoutEffect(() => setInnerOpen(!!open), [open]); + + // ---------- Menu Item Click ---------- + const { getNodes, getEdges } = useReactFlow(); + const { addNode } = useInteractions(); + const handleClick = ( + e: React.MouseEvent, + type: WorkflowNodeType, + ) => { + e.stopPropagation(); + + addNode({ nodeType: type }); + onItemClick?.(type); + handleInnerClose({}, 'backdropClick'); + }; + + return ( + + {Object.entries(menuOptions).map(([category, menus]) => { + const categoryName = menus[0]?.categoryName; + const children = [ + + {categoryName} + , + ]; + + children.push( + ...menus.map(menu => ( + handleClick(e, menu.type)}> + + {menu.icon} + + {menu.nodeName} + + )), + ); + + return children; + })} + + ); +}; + +export default NodeMenu; diff --git a/apps/web/src/pages/workflow/views/editor/components/node-menu/style.less b/apps/web/src/pages/workflow/views/editor/components/node-menu/style.less new file mode 100644 index 00000000..8c662608 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/node-menu/style.less @@ -0,0 +1,30 @@ +.@{prefix}-workflow-node-menu { + .@{mui-prefix}Menu-paper { + box-shadow: @shadow-2; + } + + .@{mui-prefix}MenuItem-root { + display: flex; + + &.@{mui-prefix}-disabled { + .text-size(@font-size-base); + + font-weight: @font-weight-medium; + } + + .icon { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-right: @margin-sm; + color: var(--white); + border-radius: @border-radius-sm; + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-lg; + } + } + } +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx new file mode 100644 index 00000000..e414949a --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx @@ -0,0 +1,27 @@ +import { useNavigate } from 'react-router-dom'; +import { Button } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { ArrowBackIcon } from '@milesight/shared/src/components'; +import './style.less'; + +/** + * Workflow 头部工具栏组件 + */ +const Topbar = () => { + const navigate = useNavigate(); + const { getIntlText } = useI18n(); + + return ( +
+ +
+ ); +}; + +export default Topbar; diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/style.less b/apps/web/src/pages/workflow/views/editor/components/topbar/style.less new file mode 100644 index 00000000..5cce7dcf --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/style.less @@ -0,0 +1,4 @@ +.@{prefix}-workflow-topbar { + padding: @padding-xs @padding-lg; + background-color: var(--component-background); +} diff --git a/apps/web/src/pages/workflow/views/editor/constant.tsx b/apps/web/src/pages/workflow/views/editor/constant.tsx new file mode 100644 index 00000000..b6476a06 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/constant.tsx @@ -0,0 +1,170 @@ +import { + SettingsEthernetIcon, + EntityIcon, + RoomServiceIcon, + EmailIcon, + WebhookIcon, + TimerIcon, + HearingIcon, + InputIcon, + CallSplitIcon, + FactCheckIcon, + // FlagIcon, +} from '@milesight/shared/src/components'; + +/** + * Minimum scaling ratio + */ +export const MIN_ZOOM = 0.25; + +/** + * Maximum scaling ratio + */ +export const MAX_ZOOM = 2; + +type NodeCategoryConfigItemType = { + /** Node Category i18n key */ + labelIntlKey: string; +}; + +/** + * Node category configs + */ +export const nodeCategoryConfigs: Record = { + entry: { + labelIntlKey: 'workflow.label.node_category_entry', + }, + control: { + labelIntlKey: 'workflow.label.node_category_control', + }, + action: { + labelIntlKey: 'workflow.label.node_category_action', + }, + external: { + labelIntlKey: 'workflow.label.node_category_external', + }, +}; + +/** + * Node config item type + */ +export type NodeConfigItemType = { + /** + * Node Type + */ + type: WorkflowNodeType; + /** + * Label i18n key + */ + labelIntlKey: string; + /** + * Node Icon + */ + icon: React.ReactNode; + /** + * Node Icon background color + */ + iconBgColor: string; + /** + * Node Category + */ + category: WorkflowNodeCategoryType; + /** + * Independent testing enabled + */ + testable?: boolean; +}; + +/** + * Basic node configs + */ +export const basicNodeConfigs: Record = { + trigger: { + type: 'trigger', + labelIntlKey: 'workflow.label.trigger_node_name', + icon: , + iconBgColor: '#3491FA', + category: 'entry', + }, + timer: { + type: 'timer', + labelIntlKey: 'workflow.label.timer_node_name', + icon: , + iconBgColor: '#3491FA', + category: 'entry', + }, + listener: { + type: 'listener', + labelIntlKey: 'workflow.label.listener_node_name', + icon: , + iconBgColor: '#3491FA', + category: 'entry', + }, + ifelse: { + type: 'ifelse', + labelIntlKey: 'workflow.label.ifelse_node_name', + icon: , + iconBgColor: '#F57C00', + category: 'control', + testable: true, + }, + // end: { + // type: 'end', + // labelIntlKey: 'workflow.label.end_node_name', + // icon: , + // iconBgColor: '#F57C00', + // category: 'control', + // }, + code: { + type: 'code', + labelIntlKey: 'workflow.label.code_node_name', + icon: , + iconBgColor: '#26A69A', + category: 'action', + testable: true, + }, + assigner: { + type: 'assigner', + labelIntlKey: 'workflow.label.assigner_node_name', + icon: , + iconBgColor: '#26A69A', + category: 'action', + }, + service: { + type: 'service', + labelIntlKey: 'workflow.label.service_node_name', + icon: , + iconBgColor: '#26A69A', + category: 'action', + testable: true, + }, + select: { + type: 'select', + labelIntlKey: 'workflow.label.select_node_name', + icon: , + iconBgColor: '#26A69A', + category: 'action', + testable: true, + }, + email: { + type: 'email', + labelIntlKey: 'workflow.label.email_node_name', + icon: , + iconBgColor: '#7E57C2', + category: 'external', + testable: true, + }, + webhook: { + type: 'webhook', + labelIntlKey: 'workflow.label.webhook_node_name', + icon: , + iconBgColor: '#7E57C2', + category: 'external', + testable: true, + }, +}; + +/** + * Parallel nesting layer limit + */ +export const PARALLEL_DEPTH_LIMIT = 3; diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json new file mode 100644 index 00000000..3c223375 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -0,0 +1,126 @@ +{ + "nodes": [ + { + "id": "1", + "data": { + "label": "Hello" + }, + "position": { + "x": 0, + "y": 0 + }, + "type": "trigger" + }, + { + "id": "2", + "data": { + "label": "World", + "cases": [ + { + "id": "case-1", + "conditions:": [ + { + "id": "condition-1", + "key": "var1", + "value": "a", + "operator": "CONTAINS" + }, + { + "id": "condition-2", + "key": "var2", + "value": "b", + "operator": "START_WITH" + } + ], + "logic": "AND" + }, + { + "id": "case-2", + "conditions:": [ + { + "id": "condition-3", + "key": "var4", + "value": "aa", + "operator": "CONTAINS" + }, + { + "id": "condition-4", + "key": "var5", + "value": "bb", + "operator": "START_WITH" + } + ], + "logic": "OR" + } + ] + }, + "position": { + "x": 300, + "y": 100 + }, + "type": "ifelse" + }, + { + "id": "3", + "data": { + "label": "Code" + }, + "position": { + "x": 600, + "y": 0 + }, + "type": "code" + }, + { + "id": "4", + "data": { + "label": "Service" + }, + "position": { + "x": 600, + "y": 100 + }, + "type": "service" + }, + { + "id": "5", + "data": { + "label": "Select" + }, + "position": { + "x": 600, + "y": 200 + }, + "type": "select" + } + ], + "edges": [ + { + "id": "1-2", + "source": "1", + "target": "2", + "type": "addable" + }, + { + "id": "2-3", + "source": "2", + "target": "3", + "type": "addable", + "sourceHandle": "case-1" + }, + { + "id": "2-4", + "source": "2", + "target": "4", + "type": "addable", + "sourceHandle": "case-2" + }, + { + "id": "2-5", + "source": "2", + "target": "5", + "type": "addable", + "sourceHandle": "case-else" + } + ] +} diff --git a/apps/web/src/pages/workflow/views/editor/hooks/index.ts b/apps/web/src/pages/workflow/views/editor/hooks/index.ts new file mode 100644 index 00000000..073ed974 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/index.ts @@ -0,0 +1,2 @@ +export { default as useNodeTypes } from './useNodeTypes'; +export { default as useInteractions } from './useInteractions'; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx new file mode 100644 index 00000000..109f4d04 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -0,0 +1,143 @@ +import { useCallback } from 'react'; +import { + useReactFlow, + addEdge, + applyNodeChanges, + applyEdgeChanges, + getOutgoers, + type OnNodesChange, + type OnEdgesChange, + type OnConnect, + type ReactFlowProps, + type IsValidConnection, +} from '@xyflow/react'; +import { genRandomString } from '@milesight/shared/src/utils/tools'; + +type RFProps = ReactFlowProps; + +interface AddNodeOptions { + newNodePayload: { + nodeType: WorkflowNodeType; + sourceHandle?: ApiKey; + targetHandle?: ApiKey; + }; +} + +type AddNodeFunc = ( + newNodePayload: { + nodeType: WorkflowNodeType; + sourceHandle?: ApiKey; + targetHandle?: ApiKey; + }, + atNodePayload?: { + atNodeId?: ApiKey; + atNodeTargetHandle?: ApiKey; + atNodeSourceHandle?: ApiKey; + }, +) => void; + +/** + * Generate Workflow Node or Edge uuid, format as `{node}:{timestamp}:{8-bit random string}` + * @param type node/edge + */ +const genUuid = (type: 'node' | 'edge') => { + return `${type}:${Date.now()}:${genRandomString()}`; +}; + +/** + * 工作流通用交互逻辑 + */ +const useInteractions = () => { + const { getNodes, getEdges, setNodes, setEdges } = useReactFlow(); + + const addNode = useCallback( + ( + { nodeType, sourceHandle, targetHandle }, + { atNodeId, atNodeSourceHandle, atNodeTargetHandle } = {}, + ) => { + console.log({ nodeType, sourceHandle, targetHandle }); + const nodes = getNodes(); + const node: WorkflowNode = { + id: genUuid('node'), + type: nodeType, + position: { + x: 0, + y: 0, + }, + data: {}, + }; + }, + [getNodes], + ); + + const handleNodesChange = useCallback>( + changes => { + setNodes(nds => applyNodeChanges(changes, nds)); + }, + [setNodes], + ); + + const handleEdgesChange = useCallback>( + changes => { + setEdges(eds => applyEdgeChanges(changes, eds)); + }, + [setEdges], + ); + + const handleConnect = useCallback( + connection => { + setEdges(eds => addEdge({ ...connection, type: 'addable' }, eds!)); + }, + [setEdges], + ); + + // Check node connection cycle + const isValidConnection = useCallback( + connection => { + // we are using getNodes and getEdges helpers here + // to make sure we create isValidConnection function only once + const nodes = getNodes(); + const edges = getEdges(); + const target = nodes.find(node => node.id === connection.target); + const hasCycle = (node: WorkflowNode, visited = new Set()) => { + if (visited.has(node.id)) return false; + + visited.add(node.id); + + for (const outgoer of getOutgoers(node, nodes, edges)) { + if (outgoer.id === connection.source) return true; + if (hasCycle(outgoer, visited)) return true; + } + }; + + if (target?.id === connection.source) return false; + return !hasCycle(target!); + }, + [getNodes, getEdges], + ); + + // Check before node delete + const handleBeforeDelete = useCallback>( + async ({ nodes }) => { + const hasEntryNode = nodes.some( + node => + node.type === 'trigger' || node.type === 'timer' || node.type === 'listener', + ); + + if (hasEntryNode) return false; + return true; + }, + [], + ); + + return { + addNode, + handleNodesChange, + handleEdgesChange, + handleConnect, + handleBeforeDelete, + isValidConnection, + }; +}; + +export default useInteractions; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx new file mode 100644 index 00000000..6b2a9c01 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx @@ -0,0 +1,62 @@ +import React, { useMemo } from 'react'; +import { Position, type NodeProps } from '@xyflow/react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { basicNodeConfigs } from '../constant'; +import { Handle, IfElseNode, NodeContainer } from '../components'; + +/** + * 生成所有节点类型 + */ +const useNodeTypes = () => { + const { getIntlText } = useI18n(); + + const nodeTypes = useMemo(() => { + const result = (Object.keys(basicNodeConfigs) as WorkflowNodeType[]).reduce( + (acc, type) => { + const config = { ...basicNodeConfigs[type] }; + const generateHandle = (type: WorkflowNodeType, props: NodeProps) => { + switch (type) { + case 'trigger': + case 'timer': + case 'listener': { + return [ + , + ]; + } + // case 'end': { + // return [ + // , + // ]; + // } + default: { + break; + } + } + }; + + acc[type] = props => ( + + ); + + if (type === 'ifelse') acc[type] = IfElseNode; + return acc; + }, + {} as Record>, + ); + + return result; + }, [getIntlText]); + + return nodeTypes; +}; + +export default useNodeTypes; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/userTest.ts b/apps/web/src/pages/workflow/views/editor/hooks/userTest.ts new file mode 100644 index 00000000..5fd33631 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/userTest.ts @@ -0,0 +1,7 @@ +import { type Node } from '@xyflow/react'; + +const useTest = (nodes: Node | Node[]) => { + // TODO: 数据校验,实时渲染各节点状态,调用运行日志等,待实现 +}; + +export default useTest; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx new file mode 100644 index 00000000..55f5809d --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -0,0 +1,118 @@ +import { memo, useState, useCallback, useEffect } from 'react'; +import { + ReactFlow, + Background, + SelectionMode, + useNodesState, + useEdgesState, + ReactFlowProvider, + type ReactFlowProps, + type NodeChange, +} from '@xyflow/react'; +import { useTheme } from '@milesight/shared/src/hooks'; +import { MIN_ZOOM, MAX_ZOOM } from './constant'; +import { useNodeTypes, useInteractions } from './hooks'; +import { Topbar, Controls, ConfigPanel, Edge, HelperLines, getHelperLines } from './components'; +import demoData from './demo-data.json'; + +import '@xyflow/react/dist/style.css'; +import './style.less'; + +const edgeTypes: Record> = { + addable: Edge, +}; + +type RFProps = ReactFlowProps; + +/** + * Workflow Editor + */ +const WorkflowEditor = () => { + const { grey } = useTheme(); + const nodeTypes = useNodeTypes(); + const { handleConnect, handleBeforeDelete, isValidConnection } = useInteractions(); + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + + // TODO: Init data may come from api or local draft + useEffect(() => { + setNodes(demoData.nodes as WorkflowNode[]); + setEdges(demoData.edges as WorkflowEdge[]); + }, [setNodes, setEdges]); + + // ---------- Show Helper Lines when node change ---------- + const [helperLineHorizontal, setHelperLineHorizontal] = useState(undefined); + const [helperLineVertical, setHelperLineVertical] = useState(undefined); + const handleNodesChange = useCallback( + (changes: NodeChange[]) => { + // reset the helper lines (clear existing lines, if any) + setHelperLineHorizontal(undefined); + setHelperLineVertical(undefined); + + if ( + changes.length === 1 && + changes[0].type === 'position' && + changes[0].dragging && + changes[0].position + ) { + const helperLines = getHelperLines(changes[0], nodes || []); + + // if we have a helper line, we snap the node to the helper line position + // this is being done by manipulating the node position inside the change object + changes[0].position.x = helperLines.snapPosition.x ?? changes[0].position.x; + changes[0].position.y = helperLines.snapPosition.y ?? changes[0].position.y; + + // if helper lines are returned, we set them so that they can be displayed + setHelperLineHorizontal(helperLines.horizontal); + setHelperLineVertical(helperLines.vertical); + } + + onNodesChange(changes); + }, + [nodes, onNodesChange], + ); + + return ( +
+ +
+
+ + fitView + className="ms-workflow" + minZoom={MIN_ZOOM} + maxZoom={MAX_ZOOM} + selectionOnDrag={false} + selectNodesOnDrag={false} + selectionKeyCode={null} + multiSelectionKeyCode={null} + selectionMode={SelectionMode.Partial} + isValidConnection={isValidConnection} + nodeTypes={nodeTypes} + edgeTypes={edgeTypes} + nodes={nodes} + edges={edges} + onBeforeDelete={handleBeforeDelete} + onNodesChange={handleNodesChange} + onEdgesChange={onEdgesChange} + onConnect={handleConnect} + > + + + + + +
+
+
+ ); +}; + +export default memo(() => ( + + + +)); diff --git a/apps/web/src/pages/workflow/views/editor/style.less b/apps/web/src/pages/workflow/views/editor/style.less new file mode 100644 index 00000000..f7781279 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/style.less @@ -0,0 +1,97 @@ +.@{prefix}-view-wf_editor { + .@{prefix}-workflow { + // ---------- Node style variable override ---------- + --xy-node-border: none; + --xy-node-border-radius: @border-radius-sm; + --xy-node-boxshadow-selected: none; + + // ---------- Edge style variable override ---------- + --xy-edge-stroke: var(--gray-color-4); + --xy-edge-stroke-selected: var(--gray-color-5); + --xy-edge-stroke-width: 2; + --xy-connectionline-stroke-width: 2; + + // ---------- Node Style ---------- + .react-flow__node { + width: 240px; + padding: 0; + border-radius: var(--xy-node-border-radius); + + &.selectable { + &:hover { + box-shadow: var(--xy-node-boxshadow-hover-default); + } + + &.selected { + .@{prefix}-workflow-node { + border: 1px solid var(--border-color-main); + box-shadow: 0 0 0 1px var(--border-color-main) inset; + + &.success, + &.error { + border-color: var(--border-color-main); + } + } + } + } + } + + &-node { + padding: @padding-sm; + background-color: var(--component-background); + border: 1px solid var(--component-background); + border-radius: var(--xy-node-border-radius); + + &.success { + border-color: var(--border-color-green); + } + + &.error { + border-color: var(--border-color-red); + } + } + + &-node-header { + position: relative; + display: flex; + align-items: center; + + &:has(> .@{prefix}-workflow-node-status) { + padding-right: @padding-lg + @padding-xxs; + } + + .@{prefix}-workflow-node-icon { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-right: @margin-sm; + color: var(--white); + border-radius: @border-radius-sm; + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-lg; + } + } + + .@{prefix}-workflow-node-title { + .text-size(@font-size-base); + + font-weight: @font-weight-bold; + } + + .@{prefix}-workflow-node-status { + position: absolute; + top: 50%; + right: 0; + display: flex; + transform: translateY(-50%); + } + } + + &-node-body { + margin-top: @margin-sm; + } + } +} diff --git a/apps/web/src/routes/routes.tsx b/apps/web/src/routes/routes.tsx index 081f6d20..4b42d263 100644 --- a/apps/web/src/routes/routes.tsx +++ b/apps/web/src/routes/routes.tsx @@ -4,6 +4,8 @@ import { DashboardCustomizeIcon, DevicesIcon, SettingsIcon, + WorkflowIcon, + EntityFilledIcon, } from '@milesight/shared/src/components'; import ErrorBoundaryComponent from './error-boundary'; @@ -24,6 +26,9 @@ type RouteObjectType = RouteObject & { /** 是否无需登录便可访问,默认 `false` (需要登录) */ authFree?: boolean; + + /** 隐藏侧边栏 */ + hideSidebar?: boolean; }; /** 子路由 */ @@ -118,6 +123,41 @@ const routes: RouteObjectType[] = [ }, ], }, + { + path: '/workflow', + element: , + ErrorBoundary, + handle: { + get title() { + return intl.get('common.label.workflow'); + }, + icon: , + }, + children: [ + { + index: true, + async lazy() { + const { default: Component } = await import('@/pages/workflow'); + return { Component }; + }, + ErrorBoundary, + }, + { + path: 'editor', + handle: { + get title() { + return intl.get('common.label.editor'); + }, + hideSidebar: true, + }, + async lazy() { + const { default: Component } = await import('@/pages/workflow/views/editor'); + return { Component }; + }, + ErrorBoundary, + }, + ], + }, { path: '/auth', handle: { diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts new file mode 100644 index 00000000..e69de29b diff --git a/apps/web/src/typings.d.ts b/apps/web/src/typings.d.ts index eabcba79..ce05cdec 100644 --- a/apps/web/src/typings.d.ts +++ b/apps/web/src/typings.d.ts @@ -1,11 +1,2 @@ /// /// - -/** - * forwardRef 定义 Hack - * - * Inspired by: https://fettblog.eu/typescript-react-generic-forward-refs/ - */ -type FixedForwardRef = ( - render: (props: P, ref: React.Ref) => React.ReactNode, -) => (props: P & React.RefAttributes) => React.ReactNode; diff --git a/apps/web/vite.config.ts b/apps/web/vite.config.ts index 43ab0556..44549a8e 100644 --- a/apps/web/vite.config.ts +++ b/apps/web/vite.config.ts @@ -60,6 +60,9 @@ export default defineConfig({ cacheLocation: path.join(__dirname, 'node_modules/.cache/.stylelintcache'), emitWarning: !isProd, }), + /** + * 优化构建速度,减少在编译时的 Tree-Shaking 检查及资源处理 + */ vitePluginImport({ libList: [ { @@ -82,7 +85,7 @@ export default defineConfig({ ], resolve: { alias: { - '@': path.resolve(__dirname, 'src'), + '@': path.resolve(__dirname, 'src'), // src 路径别名 }, }, @@ -103,7 +106,7 @@ export default defineConfig({ }, '/websocket': { target: WEB_SOCKET_PROXY, - ws: true, // Enable WebSocket Proxy + ws: true, // 启用 WebSocket 代理 changeOrigin: true, }, }, diff --git a/packages/locales/src/helper.ts b/packages/locales/src/helper.ts index ea1aca99..2ab1f13a 100644 --- a/packages/locales/src/helper.ts +++ b/packages/locales/src/helper.ts @@ -8,7 +8,7 @@ interface OptInterface { const languages = Object.values(LANGUAGE); /** 各应用依赖语言包模块配置 */ const appLocalModules: Record = { - web: ['global', 'dashboard', 'device', 'error', 'setting'], + web: ['global', 'dashboard', 'device', 'error', 'setting', 'workflow'], }; /** diff --git a/packages/locales/src/lang/cn/workflow.json b/packages/locales/src/lang/cn/workflow.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/locales/src/lang/cn/workflow.json @@ -0,0 +1 @@ +{} diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 80c27443..9a116784 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -151,5 +151,11 @@ "valid.input.username": "The username must include both upper and lower case letters.", "valid.input.value": "Input value: {0} only.", "valid.input.version": "The version number does not comply with the specification.", - "valid.resp.at_least_one": "Please select at least one {1}." + "valid.resp.at_least_one": "Please select at least one {1}.", + "common.label.workflow": "Workflow", + "common.label.editor": "Editor", + "common.label.remark": "Remark", + "common.label.enable_status": "Enable Status", + "common.label.update_time": "Update Time", + "common.label.back": "Back" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json new file mode 100644 index 00000000..5aec9b8b --- /dev/null +++ b/packages/locales/src/lang/en/workflow.json @@ -0,0 +1,20 @@ +{ + "workflow.button.label_import_from_dsl": "Import from DSL", + "workflow.label.trigger_node_name": "Trigger", + "workflow.label.end_node_name": "End", + "workflow.label.select_node_name": "Entity Select", + "workflow.label.ifelse_node_name": "IF/ELSE", + "workflow.label.assigner_node_name": "Entity Assigner", + "workflow.label.timer_node_name": "Timer", + "workflow.label.listener_node_name": "Entity Listener", + "workflow.label.code_node_name": "Code", + "workflow.label.service_node_name": "Service Invocation", + "workflow.label.email_node_name": "Email Notify", + "workflow.label.webhook_node_name": "Webhook Push", + "workflow.label.handle_tooltip": "Click to add Node
Drag to connect", + "workflow.context_menu.title_change_node": "Change Node", + "workflow.label.node_category_entry": "Entry", + "workflow.label.node_category_control": "Control", + "workflow.label.node_category_action": "Action", + "workflow.label.node_category_external": "External" +} diff --git a/packages/shared/package.json b/packages/shared/package.json index 6ff21328..9228bfe9 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -16,6 +16,7 @@ "@mui/icons-material": "^6.0.2", "@mui/material": "^6.1.0", "@mui/x-date-pickers": "^7.18.0", + "@xyflow/react": "^12.3.5", "ahooks": "^3.8.1", "axios": "^1.7.7", "classnames": "^2.5.1", diff --git a/packages/shared/src/components/icons/components/entity-filled.tsx b/packages/shared/src/components/icons/components/entity-filled.tsx new file mode 100644 index 00000000..8333008c --- /dev/null +++ b/packages/shared/src/components/icons/components/entity-filled.tsx @@ -0,0 +1,34 @@ +import { createSvgIcon } from '@mui/material/utils'; + +const EntityFilledIcon = createSvgIcon( + + + + + + + + + + + + , + 'EntityFilledIcon', +); + +export default EntityFilledIcon; diff --git a/packages/shared/src/components/icons/components/index.ts b/packages/shared/src/components/icons/components/index.ts index a779ef54..b5d32afc 100644 --- a/packages/shared/src/components/icons/components/index.ts +++ b/packages/shared/src/components/icons/components/index.ts @@ -1 +1,3 @@ export { default as EntityIcon } from './entity'; +export { default as EntityFilledIcon } from './entity-filled'; +export { default as WorkflowIcon } from './workflow'; diff --git a/packages/shared/src/components/icons/components/workflow.tsx b/packages/shared/src/components/icons/components/workflow.tsx new file mode 100644 index 00000000..e83a3492 --- /dev/null +++ b/packages/shared/src/components/icons/components/workflow.tsx @@ -0,0 +1,21 @@ +import { createSvgIcon } from '@mui/material/utils'; + +const WorkflowIcon = createSvgIcon( + + + , + 'WorkflowIcon', +); + +export default WorkflowIcon; diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 2757fea7..874d04e0 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -70,6 +70,22 @@ export { Share as ShareIcon, Https as HttpsIcon, AdsClick as AdsClickIcon, + Timeline as TimelineIcon, + SystemUpdateAlt as SystemUpdateAltIcon, + Input as InputIcon, + ZoomIn as ZoomInIcon, + ZoomOut as ZoomOutIcon, + MyLocation as MyLocationIcon, + AddCircle as AddCircleIcon, + Flag as FlagIcon, + CallSplit as CallSplitIcon, + SettingsEthernet as SettingsEthernetIcon, + RoomService as RoomServiceIcon, + Webhook as WebhookIcon, + Timer as TimerIcon, + Hearing as HearingIcon, + FactCheck as FactCheckIcon, + Loop as LoopIcon, } from '@mui/icons-material'; export * from './iot-icons'; diff --git a/packages/shared/src/utils/validators/index.ts b/packages/shared/src/utils/validators/index.ts index 839f5ef5..8ce97b6e 100644 --- a/packages/shared/src/utils/validators/index.ts +++ b/packages/shared/src/utils/validators/index.ts @@ -18,11 +18,12 @@ import { checkRangeValue, checkPort, } from './validator'; -import type { TValidator } from './typings'; +import type { Validate, TValidator } from './typings'; // 导出所有的单条 validator export * from './validator'; +export type { Validate, TValidator }; export type TChecker = () => Record>; /** diff --git a/packages/shared/types/common.d.ts b/packages/shared/types/common.d.ts index 75ba9648..3db9886d 100644 --- a/packages/shared/types/common.d.ts +++ b/packages/shared/types/common.d.ts @@ -148,3 +148,12 @@ declare interface OptionsProps { value?: T; options?: OptionsProps[]; } + +/** + * forwardRef 定义 Hack + * + * Inspired by: https://fettblog.eu/typescript-react-generic-forward-refs/ + */ +declare type FixedForwardRef = ( + render: (props: P, ref: React.Ref) => React.ReactNode, +) => (props: P & React.RefAttributes) => React.ReactNode; diff --git a/packages/shared/types/index.d.ts b/packages/shared/types/index.d.ts index df8d4ea4..c3695fce 100644 --- a/packages/shared/types/index.d.ts +++ b/packages/shared/types/index.d.ts @@ -6,3 +6,4 @@ /// /// /// +/// diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts new file mode 100644 index 00000000..72c01429 --- /dev/null +++ b/packages/shared/types/workflow.d.ts @@ -0,0 +1,353 @@ +/** + * ReactFlow 节点模型 + */ +declare type ReactFlowNode< + D extends Record = Record, + T extends string = string, +> = import('@xyflow/react').Node; + +/** + * ReactFlow 边模型 + */ +declare type ReactFlowEdge< + D extends Record = Record, + T extends string = string, +> = import('@xyflow/react').Edge; + +/** + * ReactFlow 视口模型 + */ +declare type ReactFlowViewport = import('@xyflow/react').Viewport; + +/** + * 节点类型 + * @param trigger 触发器节点 + * @param timer 定时节点 + * @param listener 监听器节点 + * @param ifelse 条件节点 + * @param code 代码节点 + * @param service 实体服务调用节点 + * @param assigner 实体赋值节点 + * @param select 实体选择节点 + * @param email 邮件节点 + * @param webhook webhook 节点 + */ +declare type WorkflowNodeType = + | 'trigger' + | 'timer' + | 'listener' + | 'ifelse' + | 'code' + | 'service' + | 'assigner' + | 'select' + | 'email' + | 'webhook'; + +/** + * 边类型 + * @param addable 可添加节点的边 + */ +declare type WorkflowEdgeType = 'addable'; + +/** + * 节点分类 + * @param entry 入口节点 + * @param control 控制节点 + * @param action 动作节点 + * @param external 外部节点 + */ +declare type WorkflowNodeCategoryType = 'entry' | 'control' | 'action' | 'external'; + +/** + * Node Status Type + */ +declare type WorkflowNodeStatus = 'error' | 'success' | 'loading'; + +/** + * 节点基础数据类型(以 $ 开头的均为前端私有属性) + */ +declare type BaseNodeDataType> = { + /** 名称 */ + name: string; + /** 描述 */ + remark?: string; + /** 后端组件 ID */ + componentId?: string; + /** 状态 */ + $status?: WorkflowNodeStatus; + /** 错误信息 */ + $errMsg?: React.ReactNode; + /** 流程参数 */ + parameters?: T; +}; + +/** + * 触发器节点参数类型 + */ +declare type TriggerNodeDataType = BaseNodeDataType<{ + /** 输入参数 */ + entityConfigs: { + name: string; + type: EntityValueDataType; + value: any; + }[]; +}>; + +/** + * 定时器节点参数类型 + */ +declare type TimerNodeDataType = BaseNodeDataType<{ + /** + * 执行类型 + * @param ONCE 单次执行 + * @param CYCLE 周期执行 + */ + type: 'ONCE' | 'CYCLE'; + /** 首次执行时间 */ + firstExecutionTime?: number; + /** 过期时间,默认 2035/01/01 00:00 */ + expireTime?: number; + /** 周期配置 */ + settings?: { + /** 执行周期 */ + period: + | 'EVERYDAY' + | 'Monday' + | 'Tuesday' + | 'Wednesday' + | 'Thursday' + | 'Friday' + | 'Saturday' + | 'Sunday'; + /** + * 执行时间,该数据为零点到所选时间点的毫秒数,默认 32400000(09:00) + */ + time: number; + }[]; +}>; + +/** + * 事件监听节点参数类型 + */ +declare type ListenerNodeDataType = BaseNodeDataType<{ + /** + * 监听类型 + * @param change 实体数据变更 + * @param call 服务调用 + * @param report 事件上报 + */ + type: 'change' | 'call' | 'report'; + /** 监听目标 */ + target: ApiKey; + /** 输出参数 */ + // outputs?: { + // key: ApiKey; + // name: string; + // type: EntityType; + // value: any; + // }[]; +}>; + +declare type WorkflowLogicOperator = 'AND' | 'OR'; + +declare type WorkflowFilterOperator = + | 'CONTAINS' + | 'NOT_CONTAINS' + | 'START_WITH' + | 'END_WITH' + | 'IS' + | 'IS_NOT' + | 'IS_EMPTY' + | 'IS_NOT_EMPTY'; + +/** + * 条件节点参数类型 + * + * 注意:实际节点渲染时需默认增加一个 else 分支 + */ +declare type IfElseNodeDataType = BaseNodeDataType<{ + when: { + id: ApiKey; + [logic: WorkflowLogicOperator]: { + /** + * 表达式类型(默认 `condition`,且当前仅支持 `condition`) + * @param mvel mvel 表达式 + * @param condition 条件表达式 + */ + expressionType: 'mvel' | 'condition'; + /** 表达式值 */ + expressionValue: { + id: ApiKey; + key: ApiKey; + operator: WorkflowFilterOperator; + value?: string; + }; + }[]; + }[]; + otherwise: { + id: ApiKey; + }; +}>; + +/** + * 结束节点参数类型 + */ +// declare type EndNodeDataType = BaseNodeDataType<{ +// /** 输出参数 */ +// outputs: { +// key: ApiKey; +// type: EntityValueDataType; +// value: any; +// }[]; +// }>; + +/** + * 代码节点参数类型 + */ +declare type CodeNodeDataType = BaseNodeDataType<{ + /** 代码语言 */ + language: string; + /** 代码内容 */ + expression: string; + /** 输入参数 */ + inputArguments: Record; + /** 输出参数 */ + outputArguments: { + name: ApiKey; + type: EntityValueDataType; + }[]; +}>; + +/** + * 服务节点参数类型 + */ +declare type ServiceNodeDataType = BaseNodeDataType<{ + /** 服务实体 Key */ + serviceEntity: ApiKey; + /** + * 输入参数 + * + * TODO: 该参数后端设计与需求输入限制表不符,待确认 + */ + serviceParams: Record; + // inputs: { + // name: ApiKey; + // type: EntityValueDataType; + // value: any; + // source: ApiKey; + // }[]; +}>; + +/** + * 赋值节点参数类型 + */ +declare type AssignerNodeDataType = BaseNodeDataType<{ + exchangePayload: Record; +}>; + +/** + * 实体选择节点参数类型 + */ +declare type SelectNodeDataType = BaseNodeDataType<{ + settings: { + /** + * 监听类型 + * @param change 实体数据变更 + * @param call 服务调用 + * @param report 事件上报 + */ + type: 'change' | 'call' | 'report'; + /** 监听目标 */ + target: ApiKey; + }[]; +}>; + +/** + * 邮件节点参数类型 + */ +declare type EmailNodeDataType = BaseNodeDataType & { + /** 邮箱类型 */ + type: 'gmail'; + /** 邮箱 API Key */ + apiKey: ApiKey; + /** 邮箱 */ + email: string | string[]; + /** 邮件内容 */ + content: string; + /** 输出参数 */ + // outputs: { + // name: ApiKey; + // type: EntityValueDataType; + // value: any; + // }[]; +}; + +/** + * Webhook 节点参数类型 + */ +declare type WebhookNodeDataType = BaseNodeDataType & { + /** 推送数据(来源于上个节点) */ + data: ApiKey[]; + /** 自定义数据 */ + customData: { + key: ApiKey; + type: EntityValueDataType; + value: any; + }[]; + /** Webhook URL */ + url: string; + /** Webhook 密钥 */ + secret: string; + /** 输出参数 */ + outputs: { + name: ApiKey; + type: EntityValueDataType; + value: any; + }[]; +}; + +/** + * 工作流节点模型 + */ +declare type WorkflowNode = + | ReactFlowNode, 'trigger'> + | ReactFlowNode, 'timer'> + | ReactFlowNode, 'listener'> + | ReactFlowNode, 'ifelse'> + // | ReactFlowNode + | ReactFlowNode, 'code'> + | ReactFlowNode, 'service'> + | ReactFlowNode, 'assigner'> + | ReactFlowNode, 'select'> + | ReactFlowNode, 'email'> + | ReactFlowNode, 'webhook'>; + +/** + * 工作流边模型 + */ +declare type WorkflowEdge = ReactFlowEdge< + { + /** 标识鼠标是否 hover */ + $hovering?: boolean; + }, + WorkflowEdgeType +>; + +/** + * 工作流数据模型 + */ +declare type WorkflowSchema = { + /** 版本号 */ + version: string; + /** 名称 */ + name: string; + /** 描述 */ + remark?: string; + /** 节点 */ + nodes: WorkflowNode[]; + /** 边 */ + edges: WorkflowEdge[]; + /** 视口 */ + viewport: ReactFlowViewport; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35ec9dca..31dfcc46 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -77,6 +77,9 @@ importers: '@mui/x-date-pickers': specifier: ^7.18.0 version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + '@xyflow/react': + specifier: ^12.3.5 + version: 12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1)(react@18.3.1) ahooks: specifier: ^3.8.1 version: 3.8.1(react@18.3.1) @@ -91,7 +94,7 @@ importers: version: 3.0.0(chart.js@4.4.4)(date-fns@4.1.0) chartjs-plugin-zoom: specifier: ^2.1.0 - version: 2.1.0(chart.js@4.4.4) + version: 2.2.0(chart.js@4.4.4) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -340,6 +343,9 @@ importers: '@mui/x-date-pickers': specifier: ^7.18.0 version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + '@xyflow/react': + specifier: ^12.3.5 + version: 12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1)(react@18.3.1) ahooks: specifier: ^3.8.1 version: 3.8.1(react@18.3.1) @@ -2162,6 +2168,39 @@ packages: dependencies: '@types/node': 20.16.5 + /@types/d3-color@3.1.3: + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + dev: false + + /@types/d3-drag@3.0.7: + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + dependencies: + '@types/d3-selection': 3.0.11 + dev: false + + /@types/d3-interpolate@3.0.4: + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + dependencies: + '@types/d3-color': 3.1.3 + dev: false + + /@types/d3-selection@3.0.11: + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + dev: false + + /@types/d3-transition@3.0.9: + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + dependencies: + '@types/d3-selection': 3.0.11 + dev: false + + /@types/d3-zoom@3.0.8: + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + dev: false + /@types/estree@1.0.6: resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -2436,6 +2475,34 @@ packages: - supports-color dev: true + /@xyflow/react@12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-wAYqpicdrVo1rxCu0X3M9s3YIF45Agqfabw0IBryTGqjWvr2NyfciI8gIP4MB+NKpWWN5kxZ9tiZ9u8lwC7iAg==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + dependencies: + '@xyflow/system': 0.0.46 + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + dev: false + + /@xyflow/system@0.0.46: + resolution: {integrity: sha512-bmFXvboVdiydIFZmDCjrbBCYgB0d5pYdkcZPWbAxGmhMRUZ+kW3CksYgYxWabrw51rwpWitLEadvLrivG0mVfA==} + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + dev: false + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -2950,8 +3017,8 @@ packages: date-fns: 4.1.0 dev: false - /chartjs-plugin-zoom@2.1.0(chart.js@4.4.4): - resolution: {integrity: sha512-7lMimfQCUaIJLhPJaWSAA4gw+1m8lyR3Wn+M3MxjHbM/XxRUnOxN7cM5RR9jUmxmyW0h7L2hZ8KhvUsqrFxy/Q==} + /chartjs-plugin-zoom@2.2.0(chart.js@4.4.4): + resolution: {integrity: sha512-in6kcdiTlP6npIVLMd4zXZ08PDUXC52gZ4FAy5oyjk1zX3gKarXMAof7B9eFiisf9WOC3bh2saHg+J5WtLXZeA==} peerDependencies: chart.js: '>=3.2.0' dependencies: @@ -2987,6 +3054,10 @@ packages: safe-buffer: 5.2.1 dev: true + /classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + dev: false + /classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} dev: false @@ -3320,6 +3391,71 @@ packages: /csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + /d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + dev: false + + /d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + dev: false + + /d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + dev: false + + /d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + dev: false + + /d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + dependencies: + d3-color: 3.1.0 + dev: false + + /d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + dev: false + + /d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + dev: false + + /d3-transition@3.0.1(d3-selection@3.0.0): + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + dev: false + + /d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + dev: false + /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: false From 64056a44ba65fa0ffd2b5f6452e31f86b607cd25 Mon Sep 17 00:00:00 2001 From: jimco Date: Wed, 4 Dec 2024 19:59:13 +0800 Subject: [PATCH 002/172] feat: Complete the add node feature --- .../views/editor/components/edge/index.tsx | 6 +- .../views/editor/components/handle/index.tsx | 21 ++- .../components/node-container/index.tsx | 68 +++++-- .../editor/components/node-menu/index.tsx | 42 ++--- .../pages/workflow/views/editor/constant.tsx | 15 ++ .../views/editor/hooks/useInteractions.tsx | 168 +++++++++++++++--- 6 files changed, 255 insertions(+), 65 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx index 969659ea..93b53bd1 100644 --- a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx @@ -61,8 +61,12 @@ const AddableEdge = ({ setAnchorEl(null)} anchorEl={anchorEl} + prevNodeId={edge?.source} + prevNodeSourceHandle={edge?.sourceHandle} + nextNodeId={edge?.target} + nextNodeTargetHandle={edge?.targetHandle} + onClose={() => setAnchorEl(null)} />
diff --git a/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx b/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx index e33d2d05..0deded80 100644 --- a/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback, forwardRef } from 'react'; +import React, { useState, useCallback, useMemo, forwardRef } from 'react'; import cls from 'classnames'; import { useDebounceFn } from 'ahooks'; import { Handle as XHandle, useEdges, type HandleProps, type NodeProps } from '@xyflow/react'; @@ -41,6 +41,18 @@ const Handle = forwardRef & e.stopPropagation(); }, []); + const closestNodeProps = useMemo(() => { + return props.type === 'target' + ? { + nextNodeId: nodeProps.id, + nextNodeTargetHandle: props.id, + } + : { + prevNodeId: nodeProps.id, + prevNodeSourceHandle: props.id, + }; + }, [props, nodeProps]); + return ( <> & - setAnchorEl(null)} anchorEl={anchorEl} /> + setAnchorEl(null)} + /> ); }, diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index 35ee4ce0..2dc259a4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -1,5 +1,5 @@ import React, { Fragment, useState, useMemo } from 'react'; -import { Position, type NodeProps } from '@xyflow/react'; +import { useReactFlow, Position, type NodeProps } from '@xyflow/react'; import cls from 'classnames'; import { Menu, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; @@ -65,8 +65,12 @@ const statusMap: Record< }, }; +const entryNodeTypes = Object.values(basicNodeConfigs) + .filter(item => item.category === 'entry') + .map(item => item.type); + /** - * 通用节点容器 + * Common Node Container */ const NodeContainer: React.FC = ({ type, @@ -83,7 +87,7 @@ const NodeContainer: React.FC = ({ const { getIntlText } = useI18n(); const status = nodeProps?.data?.$status as WorkflowNodeStatus; - // ---------- 右键菜单 ---------- + // ---------- ContextMenu ---------- const [contextMenu, setContextMenu] = useState<{ mouseX: number; mouseY: number; @@ -92,9 +96,9 @@ const NodeContainer: React.FC = ({ const isEntryNode = basicNodeConfigs[nodeProps.type as WorkflowNodeType]?.category === 'entry'; /** - * 「变更节点」子菜单项集合 + * Collection of modifiable node menus * - * TODO: 入口节点只可变更为其他入口接口,不可删除? + * Note: The entry node can not be deleted. */ const nodeMenus = useMemo(() => { const result = Object.values(basicNodeConfigs).filter(item => { @@ -105,7 +109,7 @@ const NodeContainer: React.FC = ({ }, [nodeProps]); /** - * 右键菜单点击回调 + * Set Context Menu Position */ const handleContextMenu = (event: React.MouseEvent) => { event.preventDefault(); @@ -120,17 +124,39 @@ const NodeContainer: React.FC = ({ }; /** - * 菜单项点击回调 + * Menu Item click callback */ - const handleMenuItemClick = ( - type: 'change' | 'delete', - record: NodeProps, - targetNodeType?: WorkflowNodeType, + const { updateNode, deleteElements } = useReactFlow(); + const handleMenuItemClick = async ( + e: React.MouseEvent, + { + type, + targetNodeType, + }: { + type: 'change' | 'delete'; + targetNodeType?: WorkflowNodeType; + }, ) => { - console.log({ type, record, targetNodeType }); - + e.stopPropagation(); setAnchorEl(null); setContextMenu(null); + + switch (type) { + case 'change': { + updateNode(nodeProps.id, { + type: targetNodeType, + data: {}, + }); + break; + } + case 'delete': { + await deleteElements({ nodes: [nodeProps] }); + break; + } + default: { + break; + } + } }; return ( @@ -158,15 +184,18 @@ const NodeContainer: React.FC = ({ {isEntryNode && ( { + e.stopPropagation(); setAnchorEl(e.currentTarget); }} > {getIntlText('workflow.context_menu.title_change_node')} )} - handleMenuItemClick('delete', nodeProps)}> - {getIntlText('common.label.delete')} - + {!entryNodeTypes.includes(nodeProps.type) && ( + handleMenuItemClick(e, { type: 'delete' })}> + {getIntlText('common.label.delete')} + + )} = ({ {nodeMenus.map(node => ( handleMenuItemClick('change', nodeProps, node.type)} + onClick={e => + handleMenuItemClick(e, { + type: 'change', + targetNodeType: node.type, + }) + } > {node.icon} diff --git a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx index 0727aa48..ab24c26b 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx @@ -1,29 +1,14 @@ import { useState, useMemo, useLayoutEffect } from 'react'; -import { useReactFlow, type HandleType } from '@xyflow/react'; +import { useReactFlow } from '@xyflow/react'; import { Menu, MenuItem, type MenuProps } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { nodeCategoryConfigs, basicNodeConfigs, type NodeConfigItemType } from '../../constant'; -import useInteractions from '../../hooks/useInteractions'; +import useInteractions, { type AddNodeClosestPayloadParam } from '../../hooks/useInteractions'; import './style.less'; -interface Props extends MenuProps { +interface Props extends MenuProps, AddNodeClosestPayloadParam { /** - * 节点信息 - */ - node?: WorkflowNode; - - /** - * 边信息 - */ - edge?: WorkflowEdge; - - /** - * 操作柄类型 - */ - handleType?: HandleType; - - /** - * 菜单项点击回调 + * Menu Item click callback */ onItemClick?: (nodeType: WorkflowNodeType) => void; } @@ -32,9 +17,10 @@ interface Props extends MenuProps { * 节点菜单 */ const NodeMenu = ({ - node, - edge, - handleType = 'source', + prevNodeId, + prevNodeSourceHandle, + nextNodeId, + nextNodeTargetHandle, open, onItemClick, onClose, @@ -81,7 +67,7 @@ const NodeMenu = ({ useLayoutEffect(() => setInnerOpen(!!open), [open]); // ---------- Menu Item Click ---------- - const { getNodes, getEdges } = useReactFlow(); + const { screenToFlowPosition } = useReactFlow(); const { addNode } = useInteractions(); const handleClick = ( e: React.MouseEvent, @@ -89,7 +75,15 @@ const NodeMenu = ({ ) => { e.stopPropagation(); - addNode({ nodeType: type }); + let position: { x: number; y: number } | undefined; + if (!prevNodeId && !nextNodeId) { + position = screenToFlowPosition({ x: e.clientX - 20, y: e.clientY - 20 }); + } + + addNode( + { nodeType: type, position }, + { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle }, + ); onItemClick?.(type); handleInnerClose({}, 'backdropClick'); }; diff --git a/apps/web/src/pages/workflow/views/editor/constant.tsx b/apps/web/src/pages/workflow/views/editor/constant.tsx index b6476a06..0363a825 100644 --- a/apps/web/src/pages/workflow/views/editor/constant.tsx +++ b/apps/web/src/pages/workflow/views/editor/constant.tsx @@ -168,3 +168,18 @@ export const basicNodeConfigs: Record = { * Parallel nesting layer limit */ export const PARALLEL_DEPTH_LIMIT = 3; + +/** + * The addable Edge type + */ +export const EDGE_TYPE_ADDABLE: WorkflowEdgeType = 'addable'; + +/** + * Node X-axis spacing + */ +export const NODE_SPACING_X = 300; + +/** + * Node Y-axis spacing + */ +export const NODE_SPACING_Y = 100; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index 109f4d04..0e480d05 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -4,6 +4,7 @@ import { addEdge, applyNodeChanges, applyEdgeChanges, + getIncomers, getOutgoers, type OnNodesChange, type OnEdgesChange, @@ -11,29 +12,33 @@ import { type ReactFlowProps, type IsValidConnection, } from '@xyflow/react'; +import { cloneDeep, maxBy } from 'lodash-es'; import { genRandomString } from '@milesight/shared/src/utils/tools'; +import { NODE_SPACING_X, NODE_SPACING_Y, EDGE_TYPE_ADDABLE } from '../constant'; type RFProps = ReactFlowProps; -interface AddNodeOptions { - newNodePayload: { - nodeType: WorkflowNodeType; - sourceHandle?: ApiKey; - targetHandle?: ApiKey; - }; -} +/** + * The closest node payload type for AddNode function + */ +export type AddNodeClosestPayloadParam = { + prevNodeId?: ApiKey; + prevNodeSourceHandle?: string | null; + nextNodeId?: ApiKey; + nextNodeTargetHandle?: string | null; +}; type AddNodeFunc = ( newNodePayload: { nodeType: WorkflowNodeType; sourceHandle?: ApiKey; targetHandle?: ApiKey; + position?: { + x: number; + y: number; + }; }, - atNodePayload?: { - atNodeId?: ApiKey; - atNodeTargetHandle?: ApiKey; - atNodeSourceHandle?: ApiKey; - }, + closestNodePayload?: AddNodeClosestPayloadParam, ) => void; /** @@ -41,33 +46,154 @@ type AddNodeFunc = ( * @param type node/edge */ const genUuid = (type: 'node' | 'edge') => { - return `${type}:${Date.now()}:${genRandomString()}`; + return `${type}:${Date.now()}:${genRandomString(8, { lowerCase: true })}`; }; /** - * 工作流通用交互逻辑 + * Workflow Interactions Hook */ const useInteractions = () => { - const { getNodes, getEdges, setNodes, setEdges } = useReactFlow(); + const { getNodes, getEdges, setNodes, setEdges, addNodes, addEdges } = useReactFlow< + WorkflowNode, + WorkflowEdge + >(); const addNode = useCallback( ( - { nodeType, sourceHandle, targetHandle }, - { atNodeId, atNodeSourceHandle, atNodeTargetHandle } = {}, + { nodeType, position }, + { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle } = {}, ) => { - console.log({ nodeType, sourceHandle, targetHandle }); + console.log({ + nodeType, + prevNodeId, + prevNodeSourceHandle, + nextNodeId, + nextNodeTargetHandle, + }); const nodes = getNodes(); - const node: WorkflowNode = { + const edges = getEdges(); + const prevNode = nodes.find(node => node.id === prevNodeId); + const nextNode = nodes.find(node => node.id === nextNodeId); + const newNode: WorkflowNode = { id: genUuid('node'), type: nodeType, - position: { + position: position || { x: 0, y: 0, }, data: {}, }; + + // Button at the edge + if (prevNode && nextNode) { + const newEdges = cloneDeep(edges); + const newNodes = cloneDeep(nodes); + + // Update current edge + const edge = newEdges.find( + edge => edge.source === prevNodeId && edge.target === nextNodeId, + )!; + edge.target = newNode.id; + + // Add new Edge + if (newNode.type !== 'ifelse') { + newEdges.push({ + id: genUuid('edge'), + type: EDGE_TYPE_ADDABLE, + source: newNode.id, + target: nextNode.id, + targetHandle: nextNodeTargetHandle, + }); + } + + newNode.position = { + x: nextNode.position.x, + y: nextNode.position.y, + }; + newNodes.push(newNode); + + const updateNodesPosition = (node: WorkflowNode) => { + const tempNode = newNodes.find(item => item.id === node.id)!; + + tempNode.position = { + x: node.position.x + NODE_SPACING_X, + y: node.position.y, + }; + + const outgoers = getOutgoers(node, newNodes, newEdges); + + outgoers.forEach(node => updateNodesPosition(node)); + }; + updateNodesPosition(nextNode); + + setNodes(newNodes); + setEdges(newEdges); + return; + } + + // Button at the node target handle + if (!prevNode && nextNode) { + const newEdge: WorkflowEdge | undefined = { + id: genUuid('edge'), + type: 'addable', + source: newNode.id, + target: nextNode.id, + targetHandle: nextNodeTargetHandle, + }; + const incomers = getIncomers(nextNode, nodes, edges); + + if (!incomers.length) { + newNode.position = { + x: nextNode.position.x - NODE_SPACING_X, + y: nextNode.position.y, + }; + } else { + const maxYIncomer = maxBy(incomers, item => item.position.y)!; + + newNode.position = { + x: maxYIncomer.position.x, + y: maxYIncomer.position.y + NODE_SPACING_Y, + }; + } + + addNodes([newNode]); + addEdges([newEdge]); + return; + } + + // Button at the node source handle + if (prevNode && !nextNode) { + const newEdge: WorkflowEdge | undefined = { + id: genUuid('edge'), + type: EDGE_TYPE_ADDABLE, + source: prevNode.id, + target: newNode.id, + sourceHandle: prevNodeSourceHandle, + }; + const outgoers = getOutgoers(prevNode, nodes, edges); + + if (!outgoers.length) { + newNode.position = { + x: prevNode.position.x + NODE_SPACING_X, + y: prevNode.position.y, + }; + } else { + const maxYOutgoers = maxBy(outgoers, item => item.position.y)!; + + newNode.position = { + x: maxYOutgoers.position.x, + y: maxYOutgoers.position.y + NODE_SPACING_Y, + }; + } + + addNodes([newNode]); + addEdges([newEdge]); + return; + } + + addNodes([newNode]); }, - [getNodes], + [addEdges, addNodes, getEdges, getNodes], ); const handleNodesChange = useCallback>( From 28db887145d33ba8001e43dd2244a586e86bac1e Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 5 Dec 2024 10:39:29 +0800 Subject: [PATCH 003/172] feat: Calculate the new node position based on node size; add edge hover interaction; --- .../views/editor/components/edge/index.tsx | 11 ++- .../components/node-container/index.tsx | 13 ++- .../pages/workflow/views/editor/constant.tsx | 14 +++- .../views/editor/hooks/useInteractions.tsx | 82 +++++++++++++------ .../src/pages/workflow/views/editor/index.tsx | 12 ++- 5 files changed, 96 insertions(+), 36 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx index 93b53bd1..a8071f19 100644 --- a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx @@ -4,6 +4,7 @@ import { EdgeLabelRenderer, getBezierPath, useEdges, + useReactFlow, type EdgeProps, } from '@xyflow/react'; import { Stack } from '@mui/material'; @@ -23,7 +24,7 @@ const AddableEdge = ({ targetY, sourcePosition, targetPosition, - selected, + // selected, style = {}, markerEnd, }: EdgeProps) => { @@ -39,6 +40,7 @@ const AddableEdge = ({ }); // ---------- Add Button Click Callback ---------- + const { updateEdgeData } = useReactFlow(); const [anchorEl, setAnchorEl] = useState(null); const handleClick = useCallback((e: React.MouseEvent) => { setAnchorEl(e.currentTarget); @@ -48,7 +50,7 @@ const AddableEdge = ({ return ( <> - {(selected || data?.$hovering) && ( + {data?.$hovering && (
setAnchorEl(null)} + onClose={() => { + setAnchorEl(null); + updateEdgeData(id, { $hovering: false }); + }} />
diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index 2dc259a4..01d9967a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -173,7 +173,11 @@ const NodeContainer: React.FC = ({ setContextMenu(null)} + onClose={e => { + // @ts-ignore + e.stopPropagation?.(); + setContextMenu(null); + }} anchorReference="anchorPosition" anchorPosition={ contextMenu !== null @@ -205,7 +209,12 @@ const NodeContainer: React.FC = ({ vertical: 'bottom', horizontal: 'center', }} - onClose={() => setAnchorEl(null)} + onClose={e => { + // @ts-ignore + e.stopPropagation?.(); + setAnchorEl(null); + setContextMenu(null); + }} > {nodeMenus.map(node => ( ; @@ -53,23 +59,14 @@ const genUuid = (type: 'node' | 'edge') => { * Workflow Interactions Hook */ const useInteractions = () => { - const { getNodes, getEdges, setNodes, setEdges, addNodes, addEdges } = useReactFlow< - WorkflowNode, - WorkflowEdge - >(); + const { getNodes, getEdges, setNodes, setEdges, addNodes, addEdges, updateEdgeData } = + useReactFlow(); const addNode = useCallback( ( { nodeType, position }, { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle } = {}, ) => { - console.log({ - nodeType, - prevNodeId, - prevNodeSourceHandle, - nextNodeId, - nextNodeTargetHandle, - }); const nodes = getNodes(); const edges = getEdges(); const prevNode = nodes.find(node => node.id === prevNodeId); @@ -112,17 +109,20 @@ const useInteractions = () => { }; newNodes.push(newNode); - const updateNodesPosition = (node: WorkflowNode) => { - const tempNode = newNodes.find(item => item.id === node.id)!; + const updateNodesPosition = (startNode: WorkflowNode) => { + const innerNextNode = newNodes.find(item => item.id === startNode.id)!; - tempNode.position = { - x: node.position.x + NODE_SPACING_X, - y: node.position.y, + innerNextNode.position = { + x: + startNode.position.x + + NODE_SPACING_X + + (innerNextNode.measured?.width || DEFAULT_NODE_WIDTH), + y: startNode.position.y, }; - const outgoers = getOutgoers(node, newNodes, newEdges); + const outgoers = getOutgoers(startNode, newNodes, newEdges); - outgoers.forEach(node => updateNodesPosition(node)); + outgoers.forEach(item => updateNodesPosition(item)); }; updateNodesPosition(nextNode); @@ -144,7 +144,10 @@ const useInteractions = () => { if (!incomers.length) { newNode.position = { - x: nextNode.position.x - NODE_SPACING_X, + x: + nextNode.position.x - + NODE_SPACING_X - + (nextNode.measured?.width || DEFAULT_NODE_WIDTH), y: nextNode.position.y, }; } else { @@ -152,7 +155,10 @@ const useInteractions = () => { newNode.position = { x: maxYIncomer.position.x, - y: maxYIncomer.position.y + NODE_SPACING_Y, + y: + maxYIncomer.position.y + + NODE_SPACING_Y + + (maxYIncomer.measured?.height || DEFAULT_NODE_HEIGHT), }; } @@ -174,15 +180,21 @@ const useInteractions = () => { if (!outgoers.length) { newNode.position = { - x: prevNode.position.x + NODE_SPACING_X, + x: + prevNode.position.x + + NODE_SPACING_X + + (prevNode.measured?.width || DEFAULT_NODE_WIDTH), y: prevNode.position.y, }; } else { - const maxYOutgoers = maxBy(outgoers, item => item.position.y)!; + const maxYOutgoer = maxBy(outgoers, item => item.position.y)!; newNode.position = { - x: maxYOutgoers.position.x, - y: maxYOutgoers.position.y + NODE_SPACING_Y, + x: maxYOutgoer.position.x, + y: + maxYOutgoer.position.y + + NODE_SPACING_Y + + (maxYOutgoer.measured?.height || DEFAULT_NODE_HEIGHT), }; } @@ -193,7 +205,7 @@ const useInteractions = () => { addNodes([newNode]); }, - [addEdges, addNodes, getEdges, getNodes], + [addEdges, addNodes, getEdges, getNodes, setEdges, setNodes], ); const handleNodesChange = useCallback>( @@ -256,6 +268,22 @@ const useInteractions = () => { [], ); + const handleEdgeMouseEnter = useCallback>( + (e, edge) => { + e.stopPropagation(); + updateEdgeData(edge.id, { $hovering: true }); + }, + [updateEdgeData], + ); + + const handleEdgeMouseLeave = useCallback>( + (e, edge) => { + e.stopPropagation(); + updateEdgeData(edge.id, { $hovering: false }); + }, + [updateEdgeData], + ); + return { addNode, handleNodesChange, @@ -263,6 +291,8 @@ const useInteractions = () => { handleConnect, handleBeforeDelete, isValidConnection, + handleEdgeMouseEnter, + handleEdgeMouseLeave, }; }; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 55f5809d..345a32f2 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -22,15 +22,19 @@ const edgeTypes: Record> = { addable: Edge, }; -type RFProps = ReactFlowProps; - /** * Workflow Editor */ const WorkflowEditor = () => { const { grey } = useTheme(); const nodeTypes = useNodeTypes(); - const { handleConnect, handleBeforeDelete, isValidConnection } = useInteractions(); + const { + handleConnect, + handleBeforeDelete, + isValidConnection, + handleEdgeMouseEnter, + handleEdgeMouseLeave, + } = useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); @@ -96,6 +100,8 @@ const WorkflowEditor = () => { onNodesChange={handleNodesChange} onEdgesChange={onEdgesChange} onConnect={handleConnect} + onEdgeMouseEnter={handleEdgeMouseEnter} + onEdgeMouseLeave={handleEdgeMouseLeave} > From c9e479680897cb260614367862242282193eac72 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 5 Dec 2024 17:07:51 +0800 Subject: [PATCH 004/172] feat: workflow parallel depth limit check --- .../editor/components/controls/index.tsx | 2 +- .../workflow/views/editor/hooks/index.ts | 1 + .../views/editor/hooks/useInteractions.tsx | 233 +++++++++--------- .../editor/hooks/{userTest.ts => useRun.ts} | 7 +- .../views/editor/hooks/useWorkflow.ts | 68 +++++ .../workflow/views/editor/hooks/utils.ts | 182 ++++++++++++++ .../src/pages/workflow/views/editor/index.tsx | 12 +- packages/locales/src/lang/en/workflow.json | 3 +- 8 files changed, 384 insertions(+), 124 deletions(-) rename apps/web/src/pages/workflow/views/editor/hooks/{userTest.ts => useRun.ts} (60%) create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/utils.ts diff --git a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx index 1779716f..3f29a5e9 100644 --- a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx @@ -47,7 +47,7 @@ const Controls: React.FC = ({ minZoom, maxZoom }) => { - diff --git a/apps/web/src/pages/workflow/views/editor/hooks/index.ts b/apps/web/src/pages/workflow/views/editor/hooks/index.ts index 073ed974..a8392564 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/index.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/index.ts @@ -1,2 +1,3 @@ export { default as useNodeTypes } from './useNodeTypes'; export { default as useInteractions } from './useInteractions'; +export { default as useWorkflow } from './useWorkflow'; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index 281fb124..ea1c0b44 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -1,17 +1,16 @@ import { useCallback } from 'react'; import { - useReactFlow, - addEdge, - applyNodeChanges, - applyEdgeChanges, getIncomers, getOutgoers, + applyNodeChanges, + applyEdgeChanges, + useReactFlow, type OnNodesChange, type OnEdgesChange, type OnConnect, type ReactFlowProps, - type IsValidConnection, } from '@xyflow/react'; +import { useSize } from 'ahooks'; import { cloneDeep, maxBy } from 'lodash-es'; import { genRandomString } from '@milesight/shared/src/utils/tools'; import { @@ -21,6 +20,7 @@ import { DEFAULT_NODE_HEIGHT, EDGE_TYPE_ADDABLE, } from '../constant'; +import useWorkflow from './useWorkflow'; type RFProps = ReactFlowProps; @@ -59,16 +59,86 @@ const genUuid = (type: 'node' | 'edge') => { * Workflow Interactions Hook */ const useInteractions = () => { - const { getNodes, getEdges, setNodes, setEdges, addNodes, addEdges, updateEdgeData } = - useReactFlow(); + const { + getNodes, + getEdges, + setNodes, + setEdges, + addEdges, + updateEdgeData, + fitView, + flowToScreenPosition, + } = useReactFlow(); + const { checkNestedParallelLimit } = useWorkflow(); + const { width: bodyWidth, height: bodyHeight } = useSize(document.querySelector('body')) || {}; + + const handleNodesChange = useCallback>( + changes => { + setNodes(nds => applyNodeChanges(changes, nds)); + }, + [setNodes], + ); + + const handleEdgesChange = useCallback>( + changes => { + setEdges(eds => applyEdgeChanges(changes, eds)); + }, + [setEdges], + ); + + // Handle nodes connect + const handleConnect = useCallback( + connection => { + const nodes = getNodes(); + const edges = getEdges(); + const newEdge = { ...connection, id: genUuid('edge'), type: EDGE_TYPE_ADDABLE }; + + if (!checkNestedParallelLimit(nodes, [...edges, newEdge])) return; + addEdges([newEdge]); + }, + [addEdges, checkNestedParallelLimit, getEdges, getNodes], + ); + // Check before node delete + const handleBeforeDelete = useCallback>( + async ({ nodes }) => { + const hasEntryNode = nodes.some( + node => + node.type === 'trigger' || node.type === 'timer' || node.type === 'listener', + ); + + if (hasEntryNode) return false; + return true; + }, + [], + ); + + // Handle edge mouse enter + const handleEdgeMouseEnter = useCallback>( + (e, edge) => { + e.stopPropagation(); + updateEdgeData(edge.id, { $hovering: true }); + }, + [updateEdgeData], + ); + + // Handle edge mouse leave + const handleEdgeMouseLeave = useCallback>( + (e, edge) => { + e.stopPropagation(); + updateEdgeData(edge.id, { $hovering: false }); + }, + [updateEdgeData], + ); + + // Add New Node const addNode = useCallback( ( { nodeType, position }, { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle } = {}, ) => { - const nodes = getNodes(); - const edges = getEdges(); + const nodes = cloneDeep(getNodes()); + const edges = cloneDeep(getEdges()); const prevNode = nodes.find(node => node.id === prevNodeId); const nextNode = nodes.find(node => node.id === nextNodeId); const newNode: WorkflowNode = { @@ -81,20 +151,17 @@ const useInteractions = () => { data: {}, }; - // Button at the edge if (prevNode && nextNode) { - const newEdges = cloneDeep(edges); - const newNodes = cloneDeep(nodes); - + // ----- Button at the edge ----- // Update current edge - const edge = newEdges.find( + const edge = edges.find( edge => edge.source === prevNodeId && edge.target === nextNodeId, )!; edge.target = newNode.id; // Add new Edge if (newNode.type !== 'ifelse') { - newEdges.push({ + edges.push({ id: genUuid('edge'), type: EDGE_TYPE_ADDABLE, source: newNode.id, @@ -107,10 +174,10 @@ const useInteractions = () => { x: nextNode.position.x, y: nextNode.position.y, }; - newNodes.push(newNode); + nodes.push(newNode); const updateNodesPosition = (startNode: WorkflowNode) => { - const innerNextNode = newNodes.find(item => item.id === startNode.id)!; + const innerNextNode = nodes.find(item => item.id === startNode.id)!; innerNextNode.position = { x: @@ -120,19 +187,13 @@ const useInteractions = () => { y: startNode.position.y, }; - const outgoers = getOutgoers(startNode, newNodes, newEdges); + const outgoers = getOutgoers(startNode, nodes, edges); outgoers.forEach(item => updateNodesPosition(item)); }; updateNodesPosition(nextNode); - - setNodes(newNodes); - setEdges(newEdges); - return; - } - - // Button at the node target handle - if (!prevNode && nextNode) { + } else if (!prevNode && nextNode) { + // ----- Button at the node target handle ----- const newEdge: WorkflowEdge | undefined = { id: genUuid('edge'), type: 'addable', @@ -162,13 +223,10 @@ const useInteractions = () => { }; } - addNodes([newNode]); - addEdges([newEdge]); - return; - } - - // Button at the node source handle - if (prevNode && !nextNode) { + nodes.push(newNode); + edges.push(newEdge); + } else if (prevNode && !nextNode) { + // ----- Button at the node source handle ----- const newEdge: WorkflowEdge | undefined = { id: genUuid('edge'), type: EDGE_TYPE_ADDABLE, @@ -198,90 +256,42 @@ const useInteractions = () => { }; } - addNodes([newNode]); - addEdges([newEdge]); - return; + nodes.push(newNode); + edges.push(newEdge); + } else { + // ----- Button at the control bar ----- + nodes.push(newNode); } - addNodes([newNode]); - }, - [addEdges, addNodes, getEdges, getNodes, setEdges, setNodes], - ); - - const handleNodesChange = useCallback>( - changes => { - setNodes(nds => applyNodeChanges(changes, nds)); - }, - [setNodes], - ); - - const handleEdgesChange = useCallback>( - changes => { - setEdges(eds => applyEdgeChanges(changes, eds)); - }, - [setEdges], - ); - - const handleConnect = useCallback( - connection => { - setEdges(eds => addEdge({ ...connection, type: 'addable' }, eds!)); - }, - [setEdges], - ); - - // Check node connection cycle - const isValidConnection = useCallback( - connection => { - // we are using getNodes and getEdges helpers here - // to make sure we create isValidConnection function only once - const nodes = getNodes(); - const edges = getEdges(); - const target = nodes.find(node => node.id === connection.target); - const hasCycle = (node: WorkflowNode, visited = new Set()) => { - if (visited.has(node.id)) return false; - - visited.add(node.id); - - for (const outgoer of getOutgoers(node, nodes, edges)) { - if (outgoer.id === connection.source) return true; - if (hasCycle(outgoer, visited)) return true; - } - }; + if (!checkNestedParallelLimit(nodes, edges)) return; - if (target?.id === connection.source) return false; - return !hasCycle(target!); - }, - [getNodes, getEdges], - ); + setNodes(nodes); + setEdges(edges); - // Check before node delete - const handleBeforeDelete = useCallback>( - async ({ nodes }) => { - const hasEntryNode = nodes.some( - node => - node.type === 'trigger' || node.type === 'timer' || node.type === 'listener', - ); + if (!bodyWidth || !bodyHeight) return; + const screenPosition = flowToScreenPosition({ + x: newNode.position.x, + y: newNode.position.y, + }); - if (hasEntryNode) return false; - return true; - }, - [], - ); - - const handleEdgeMouseEnter = useCallback>( - (e, edge) => { - e.stopPropagation(); - updateEdgeData(edge.id, { $hovering: true }); - }, - [updateEdgeData], - ); - - const handleEdgeMouseLeave = useCallback>( - (e, edge) => { - e.stopPropagation(); - updateEdgeData(edge.id, { $hovering: false }); + if ( + screenPosition.x + DEFAULT_NODE_WIDTH > bodyWidth || + screenPosition.y + DEFAULT_NODE_HEIGHT > bodyHeight + ) { + setTimeout(() => fitView({ duration: 300 }), 0); + } }, - [updateEdgeData], + [ + bodyWidth, + bodyHeight, + getNodes, + getEdges, + setNodes, + setEdges, + fitView, + flowToScreenPosition, + checkNestedParallelLimit, + ], ); return { @@ -290,7 +300,6 @@ const useInteractions = () => { handleEdgesChange, handleConnect, handleBeforeDelete, - isValidConnection, handleEdgeMouseEnter, handleEdgeMouseLeave, }; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/userTest.ts b/apps/web/src/pages/workflow/views/editor/hooks/useRun.ts similarity index 60% rename from apps/web/src/pages/workflow/views/editor/hooks/userTest.ts rename to apps/web/src/pages/workflow/views/editor/hooks/useRun.ts index 5fd33631..6e63b500 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/userTest.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useRun.ts @@ -1,7 +1,10 @@ import { type Node } from '@xyflow/react'; -const useTest = (nodes: Node | Node[]) => { +/** + * Nodes Test Run + */ +const useRun = (nodes: Node | Node[]) => { // TODO: 数据校验,实时渲染各节点状态,调用运行日志等,待实现 }; -export default useTest; +export default useRun; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts new file mode 100644 index 00000000..2954e9f6 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -0,0 +1,68 @@ +import { useCallback } from 'react'; +import { useReactFlow, getOutgoers, type IsValidConnection } from '@xyflow/react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { toast } from '@milesight/shared/src/components'; +import { PARALLEL_DEPTH_LIMIT } from '../constant'; +import { getParallelInfo } from './utils'; + +const useWorkflow = () => { + const { getNodes, getEdges } = useReactFlow(); + const { getIntlText } = useI18n(); + + // Check node connection cycle + const isValidConnection = useCallback( + connection => { + // we are using getNodes and getEdges helpers here + // to make sure we create isValidConnection function only once + const nodes = getNodes(); + const edges = getEdges(); + const target = nodes.find(node => node.id === connection.target); + const hasCycle = (node: WorkflowNode, visited = new Set()) => { + if (visited.has(node.id)) return false; + + visited.add(node.id); + + for (const outgoer of getOutgoers(node, nodes, edges)) { + if (outgoer.id === connection.source) return true; + if (hasCycle(outgoer, visited)) return true; + } + }; + + if (target?.id === connection.source) return false; + return !hasCycle(target!); + }, + [getNodes, getEdges], + ); + + // Check nested parallel limit + const checkNestedParallelLimit = useCallback( + (nodes: WorkflowNode[], edges: WorkflowEdge[], parentNodeId?: ApiKey) => { + const { parallelList, hasAbnormalEdges } = getParallelInfo(nodes, edges, parentNodeId); + + console.log({ parallelList, hasAbnormalEdges }); + if (hasAbnormalEdges) return false; + + const isGtLimit = parallelList.some(item => item.depth > PARALLEL_DEPTH_LIMIT); + + if (isGtLimit) { + toast.error({ + key: 'parallel-depth-limit', + content: getIntlText('workflow.label.parallel_depth_limit_tip', { + 1: PARALLEL_DEPTH_LIMIT, + }), + }); + return false; + } + + return true; + }, + [getIntlText], + ); + + return { + isValidConnection, + checkNestedParallelLimit, + }; +}; + +export default useWorkflow; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/utils.ts b/apps/web/src/pages/workflow/views/editor/hooks/utils.ts new file mode 100644 index 00000000..ec2c8f35 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/utils.ts @@ -0,0 +1,182 @@ +import { isEqual, groupBy } from 'lodash-es'; +import { getIncomers, getOutgoers, getConnectedEdges } from '@xyflow/react'; +import { basicNodeConfigs } from '../constant'; + +type ParallelInfoItem = { + parallelNodeId: ApiKey; + depth: number; + isBranch?: boolean; +}; + +type NodeParallelInfo = { + parallelNodeId: ApiKey; + edgeHandleId: ApiKey; + depth: number; +}; + +type NodeHandle = { + node: WorkflowNode; + handle?: string; +}; + +type NodeStreamInfo = { + upstreamNodes: Set; + downstreamEdges: Set; +}; + +const entryNodeTypes = Object.values(basicNodeConfigs) + .filter(item => item.category === 'entry') + .map(item => item.type); + +/** + * Get Parallel Info + */ +export const getParallelInfo = ( + nodes: WorkflowNode[], + edges: WorkflowEdge[], + parentNodeId?: ApiKey, +) => { + let startNode; + + if (parentNodeId) { + const parentNode = nodes.find(node => node.id === parentNodeId); + if (!parentNode) throw new Error('Parent node not found'); + + startNode = nodes.find(node => node.id === (parentNode.data as any).start_node_id); + } else { + startNode = nodes.find(node => entryNodeTypes.includes(node.type!)); + } + if (!startNode) throw new Error('Start node not found'); + + const parallelList = [] as ParallelInfoItem[]; + const nextNodeHandles: NodeHandle[] = [{ node: startNode }]; + let hasAbnormalEdges = false; + + const traverse = (firstNodeHandle: NodeHandle) => { + const nodeEdgesSet = {} as Record>; + const totalEdgesSet = new Set(); + const nextHandles = [firstNodeHandle]; + const streamInfo = {} as Record; + const parallelListItem = { + parallelNodeId: '', + depth: 0, + } as ParallelInfoItem; + const nodeParallelInfoMap = {} as Record; + nodeParallelInfoMap[firstNodeHandle.node.id] = { + parallelNodeId: '', + edgeHandleId: '', + depth: 0, + }; + + while (nextHandles.length) { + const currentNodeHandle = nextHandles.shift()!; + const { node: currentNode, handle: currentHandle } = currentNodeHandle; + const currentNodeHandleKey = currentNode.id; + const connectedEdges = getConnectedEdges([currentNode], edges).filter( + edge => edge.source === currentNode.id, + ); + const connectedEdgesLength = connectedEdges.length; + const outgoers = getOutgoers(currentNode, nodes, edges); + const incomers = getIncomers(currentNode, nodes, edges); + + if (!streamInfo[currentNodeHandleKey]) { + streamInfo[currentNodeHandleKey] = { + upstreamNodes: new Set(), + downstreamEdges: new Set(), + }; + } + + if (nodeEdgesSet[currentNodeHandleKey]?.size > 0 && incomers.length > 1) { + const newSet = new Set(); + for (const item of totalEdgesSet) { + if (!streamInfo[currentNodeHandleKey].downstreamEdges.has(item)) + newSet.add(item); + } + if (isEqual(nodeEdgesSet[currentNodeHandleKey], newSet)) { + parallelListItem.depth = nodeParallelInfoMap[currentNode.id].depth; + nextNodeHandles.push({ node: currentNode, handle: currentHandle }); + break; + } + } + + if (nodeParallelInfoMap[currentNode.id].depth > parallelListItem.depth) + parallelListItem.depth = nodeParallelInfoMap[currentNode.id].depth; + + // eslint-disable-next-line no-loop-func + outgoers.forEach(outgoer => { + const outgoerConnectedEdges = getConnectedEdges([outgoer], edges).filter( + edge => edge.source === outgoer.id, + ); + const sourceEdgesGroup = groupBy(outgoerConnectedEdges, 'sourceHandle'); + const incomers = getIncomers(outgoer, nodes, edges); + + if (outgoers.length > 1 && incomers.length > 1) hasAbnormalEdges = true; + + Object.keys(sourceEdgesGroup).forEach(sourceHandle => { + nextHandles.push({ node: outgoer, handle: sourceHandle }); + }); + if (!outgoerConnectedEdges.length) + nextHandles.push({ node: outgoer, handle: 'source' }); + + const outgoerKey = outgoer.id; + if (!nodeEdgesSet[outgoerKey]) nodeEdgesSet[outgoerKey] = new Set(); + + if (nodeEdgesSet[currentNodeHandleKey]) { + for (const item of nodeEdgesSet[currentNodeHandleKey]) + nodeEdgesSet[outgoerKey].add(item); + } + + if (!streamInfo[outgoerKey]) { + streamInfo[outgoerKey] = { + upstreamNodes: new Set(), + downstreamEdges: new Set(), + }; + } + + if (!nodeParallelInfoMap[outgoer.id]) { + nodeParallelInfoMap[outgoer.id] = { + ...nodeParallelInfoMap[currentNode.id], + }; + } + + if (connectedEdgesLength > 1) { + const edge = connectedEdges.find(edge => edge.target === outgoer.id)!; + nodeEdgesSet[outgoerKey].add(edge.id); + totalEdgesSet.add(edge.id); + + streamInfo[currentNodeHandleKey].downstreamEdges.add(edge.id); + streamInfo[outgoerKey].upstreamNodes.add(currentNodeHandleKey); + + for (const item of streamInfo[currentNodeHandleKey].upstreamNodes) + streamInfo[item].downstreamEdges.add(edge.id); + + if (!parallelListItem.parallelNodeId) + parallelListItem.parallelNodeId = currentNode.id; + + const prevDepth = nodeParallelInfoMap[currentNode.id].depth + 1; + const currentDepth = nodeParallelInfoMap[outgoer.id].depth; + + nodeParallelInfoMap[outgoer.id].depth = Math.max(prevDepth, currentDepth); + } else { + for (const item of streamInfo[currentNodeHandleKey].upstreamNodes) + streamInfo[outgoerKey].upstreamNodes.add(item); + + nodeParallelInfoMap[outgoer.id].depth = + nodeParallelInfoMap[currentNode.id].depth; + } + }); + } + + parallelList.push(parallelListItem); + }; + + while (nextNodeHandles.length) { + const nodeHandle = nextNodeHandles.shift()!; + traverse(nodeHandle); + } + + return { + parallelList, + hasAbnormalEdges, + }; +}; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 345a32f2..b6e54a59 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -11,7 +11,7 @@ import { } from '@xyflow/react'; import { useTheme } from '@milesight/shared/src/hooks'; import { MIN_ZOOM, MAX_ZOOM } from './constant'; -import { useNodeTypes, useInteractions } from './hooks'; +import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; import { Topbar, Controls, ConfigPanel, Edge, HelperLines, getHelperLines } from './components'; import demoData from './demo-data.json'; @@ -28,13 +28,9 @@ const edgeTypes: Record> = { const WorkflowEditor = () => { const { grey } = useTheme(); const nodeTypes = useNodeTypes(); - const { - handleConnect, - handleBeforeDelete, - isValidConnection, - handleEdgeMouseEnter, - handleEdgeMouseLeave, - } = useInteractions(); + const { isValidConnection } = useWorkflow(); + const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = + useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 5aec9b8b..046e8d45 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -16,5 +16,6 @@ "workflow.label.node_category_entry": "Entry", "workflow.label.node_category_control": "Control", "workflow.label.node_category_action": "Action", - "workflow.label.node_category_external": "External" + "workflow.label.node_category_external": "External", + "workflow.label.parallel_depth_limit_tip": "并行嵌套层数限制 {1} 层" } From 8863af3e2324dd14c5a7e4bcc2c15caf470c9a99 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 5 Dec 2024 17:42:52 +0800 Subject: [PATCH 005/172] fix: Make the tooltip appear on top of other nodes --- .../views/editor/components/handle/index.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx b/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx index 0deded80..658cd330 100644 --- a/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/handle/index.tsx @@ -1,7 +1,13 @@ import React, { useState, useCallback, useMemo, forwardRef } from 'react'; import cls from 'classnames'; import { useDebounceFn } from 'ahooks'; -import { Handle as XHandle, useEdges, type HandleProps, type NodeProps } from '@xyflow/react'; +import { + Handle as XHandle, + useEdges, + useReactFlow, + type HandleProps, + type NodeProps, +} from '@xyflow/react'; import { Stack } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { AddCircleIcon } from '@milesight/shared/src/components'; @@ -25,11 +31,15 @@ const Handle = forwardRef & const targetAddEnabled = edges.every(edge => edge.target !== nodeProps.id); // ---------- Handle Tooltip Open ---------- + const [originNodeZIndex] = useState(nodeProps.zIndex); + const { updateNode } = useReactFlow(); const [showTooltip, setShowTooltip] = useState(false); const { run: handleMouseEnter, cancel: cancelHandleMouseEnter } = useDebounceFn( () => { if (props.type === 'target' && !targetAddEnabled) return; setShowTooltip(true); + // Increase the zIndex of the node to make the tooltip appear on top of other nodes + updateNode(nodeProps.id, { zIndex: 1 }); }, { wait: 500 }, ); @@ -67,6 +77,7 @@ const Handle = forwardRef & onMouseLeave={() => { setShowTooltip(false); cancelHandleMouseEnter(); + updateNode(nodeProps.id, { zIndex: originNodeZIndex }); }} > {/* Use Custom Tooltip, resolve the issue of Edge connect failure when Mui Tooltip component is enabled */} From 8549f0aa4c24b41511ebc4a13094708966f86b7b Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 6 Dec 2024 11:18:07 +0800 Subject: [PATCH 006/172] feat: add entry panel for workflow editor --- .../editor/components/controls/index.tsx | 9 +- .../editor/components/controls/style.less | 4 + .../editor/components/entry-panel/index.tsx | 69 +++++++++++ .../editor/components/entry-panel/style.less | 110 ++++++++++++++++++ .../workflow/views/editor/components/index.ts | 1 + .../pages/workflow/views/editor/constant.tsx | 7 ++ .../views/editor/hooks/useInteractions.tsx | 10 +- .../src/pages/workflow/views/editor/index.tsx | 22 ++-- packages/locales/src/lang/en/global.json | 3 +- packages/locales/src/lang/en/workflow.json | 7 +- 10 files changed, 224 insertions(+), 18 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/entry-panel/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx index 3f29a5e9..0d39d945 100644 --- a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx @@ -1,6 +1,7 @@ import React, { useState, useCallback } from 'react'; -import { Panel, useReactFlow, useViewport } from '@xyflow/react'; +import { Panel, useNodes, useReactFlow, useViewport } from '@xyflow/react'; import { Stack, Paper, ButtonGroup, Button } from '@mui/material'; +import cls from 'classnames'; import { ZoomInIcon, ZoomOutIcon, @@ -26,6 +27,7 @@ export interface ControlsProps { * 工作流编辑器工具栏 */ const Controls: React.FC = ({ minZoom, maxZoom }) => { + const nodes = useNodes(); const { zoom } = useViewport(); const { zoomIn, zoomOut, fitView } = useReactFlow(); @@ -37,7 +39,10 @@ const Controls: React.FC = ({ minZoom, maxZoom }) => { }, []); return ( - + diff --git a/apps/web/src/pages/workflow/views/editor/components/controls/style.less b/apps/web/src/pages/workflow/views/editor/components/controls/style.less index 445a8a2e..867a6cd9 100644 --- a/apps/web/src/pages/workflow/views/editor/components/controls/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/controls/style.less @@ -3,6 +3,10 @@ margin: @margin-lg; } + &.hidden { + display: none; + } + .@{mui-prefix}Paper-root { box-shadow: @shadow-2; } diff --git a/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx new file mode 100644 index 00000000..d6fe6d6f --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx @@ -0,0 +1,69 @@ +import { memo, useState } from 'react'; +import { Panel, useNodes } from '@xyflow/react'; +import cls from 'classnames'; +import { Button } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { basicNodeConfigs } from '../../constant'; +import useInteractions from '../../hooks/useInteractions'; +import './style.less'; + +const entryNodeConfigs = Object.values(basicNodeConfigs).filter(node => node.category === 'entry'); + +const EntryModal = () => { + const { getIntlText } = useI18n(); + const nodes = useNodes(); + const { addNode } = useInteractions(); + const [selectedNodeType, setSelectedNodeType] = useState(); + + const handleCreate = () => { + if (!selectedNodeType) return; + addNode({ nodeType: selectedNodeType, position: { x: 0, y: 0 } }); + }; + + return nodes.length ? null : ( + +
+
+
+ {getIntlText('workflow.modal.entry_node_create_title')} +
+
+
+
+ {entryNodeConfigs.map(config => ( +
setSelectedNodeType(config.type)} + > +
+ {config.icon} +
+
+
+ {getIntlText(config.labelIntlKey)} +
+
+ {getIntlText(config.descIntlKey || '')} +
+
+
+ ))} +
+
+
+ +
+
+
+ ); +}; + +export default memo(EntryModal); diff --git a/apps/web/src/pages/workflow/views/editor/components/entry-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/entry-panel/style.less new file mode 100644 index 00000000..332d995b --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/entry-panel/style.less @@ -0,0 +1,110 @@ +.@{prefix}-workflow-panel-entry-root { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + margin: 0; +} + +.@{prefix}-workflow-panel-entry { + width: 600px; + background-color: var(--component-background); + border-radius: @border-radius-base; + box-shadow: @shadow-2; + + &-header { + padding: @padding-md @padding-xl; + font-weight: @font-weight-bold; + .text-size(@font-size-xxl); + } + + &-body { + padding: 0 @padding-xl; + + .@{prefix}-workflow-entry-nodes { + padding-top: @padding-xs; + } + + .@{prefix}-node-item { + position: relative; + display: flex; + padding: @padding-sm @padding-md; + margin-bottom: @margin-sm; + border: 1px solid var(--border-color-base); + border-radius: @border-radius-base; + cursor: pointer; + transition: .3s; + + &::after { + position: absolute; + inset: 0; + z-index: 0; + background-color: transparent; + content: ''; + } + + &:hover { + &::after { + background-color: var(--component-background-gray); + opacity: 0.5; + } + } + + &.selected { + background-color: transparent; + border-color: var(--primary-color-base); + + &::after { + background-color: var(--primary-color-base); + opacity: 0.08; + } + } + + &:last-child { + margin-bottom: 0; + } + + &-icon { + position: relative; + z-index: 1; + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-right: @margin-sm; + color: var(--white); + border-radius: @border-radius-sm; + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-lg; + } + } + + &-info { + position: relative; + z-index: 1; + } + + &-name { + .text-size(@font-size-lg); + + font-weight: @font-weight-bold; + } + + &-desc { + .text-size(@font-size-sm); + + margin-top: @margin-xxs; + color: var(--text-color-secondary); + } + } + } + + &-footer { + padding: @padding-xl; + text-align: right; + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/index.ts index 04e28b11..8155e4f3 100644 --- a/apps/web/src/pages/workflow/views/editor/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/index.ts @@ -8,3 +8,4 @@ export { default as Handle, type Props as HandleProps } from './handle'; export { default as Edge } from './edge'; export { default as HelperLines, getHelperLines } from './helper-lines'; export { default as NodeMenu } from './node-menu'; +export { default as EntryPanel } from './entry-panel'; diff --git a/apps/web/src/pages/workflow/views/editor/constant.tsx b/apps/web/src/pages/workflow/views/editor/constant.tsx index 18ac1afb..866ae07b 100644 --- a/apps/web/src/pages/workflow/views/editor/constant.tsx +++ b/apps/web/src/pages/workflow/views/editor/constant.tsx @@ -57,6 +57,10 @@ export type NodeConfigItemType = { * Label i18n key */ labelIntlKey: string; + /** + * Description i18n key + */ + descIntlKey?: string; /** * Node Icon */ @@ -82,6 +86,7 @@ export const basicNodeConfigs: Record = { trigger: { type: 'trigger', labelIntlKey: 'workflow.label.trigger_node_name', + descIntlKey: 'workflow.label.trigger_node_desc', icon: , iconBgColor: '#3491FA', category: 'entry', @@ -89,6 +94,7 @@ export const basicNodeConfigs: Record = { timer: { type: 'timer', labelIntlKey: 'workflow.label.timer_node_name', + descIntlKey: 'workflow.label.timer_node_desc', icon: , iconBgColor: '#3491FA', category: 'entry', @@ -96,6 +102,7 @@ export const basicNodeConfigs: Record = { listener: { type: 'listener', labelIntlKey: 'workflow.label.listener_node_name', + descIntlKey: 'workflow.label.listener_node_desc', icon: , iconBgColor: '#3491FA', category: 'entry', diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index ea1c0b44..df1ff3d6 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -269,15 +269,13 @@ const useInteractions = () => { setEdges(edges); if (!bodyWidth || !bodyHeight) return; + // Node bottom right corner position const screenPosition = flowToScreenPosition({ - x: newNode.position.x, - y: newNode.position.y, + x: newNode.position.x + DEFAULT_NODE_WIDTH, + y: newNode.position.y + DEFAULT_NODE_HEIGHT, }); - if ( - screenPosition.x + DEFAULT_NODE_WIDTH > bodyWidth || - screenPosition.y + DEFAULT_NODE_HEIGHT > bodyHeight - ) { + if (screenPosition.x > bodyWidth || screenPosition.y > bodyHeight) { setTimeout(() => fitView({ duration: 300 }), 0); } }, diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index b6e54a59..16537b4c 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -6,13 +6,20 @@ import { useNodesState, useEdgesState, ReactFlowProvider, - type ReactFlowProps, type NodeChange, } from '@xyflow/react'; import { useTheme } from '@milesight/shared/src/hooks'; import { MIN_ZOOM, MAX_ZOOM } from './constant'; import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; -import { Topbar, Controls, ConfigPanel, Edge, HelperLines, getHelperLines } from './components'; +import { + Topbar, + Controls, + ConfigPanel, + Edge, + HelperLines, + getHelperLines, + EntryPanel, +} from './components'; import demoData from './demo-data.json'; import '@xyflow/react/dist/style.css'; @@ -34,11 +41,11 @@ const WorkflowEditor = () => { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); - // TODO: Init data may come from api or local draft - useEffect(() => { - setNodes(demoData.nodes as WorkflowNode[]); - setEdges(demoData.edges as WorkflowEdge[]); - }, [setNodes, setEdges]); + // TODO: Init workflow data + // useEffect(() => { + // setNodes(demoData.nodes as WorkflowNode[]); + // setEdges(demoData.edges as WorkflowEdge[]); + // }, [setNodes, setEdges]); // ---------- Show Helper Lines when node change ---------- const [helperLineHorizontal, setHelperLineHorizontal] = useState(undefined); @@ -106,6 +113,7 @@ const WorkflowEditor = () => { vertical={helperLineVertical} /> +
diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 9a116784..8a85789c 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -157,5 +157,6 @@ "common.label.remark": "Remark", "common.label.enable_status": "Enable Status", "common.label.update_time": "Update Time", - "common.label.back": "Back" + "common.label.back": "Back", + "common.label.create": "Create" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 046e8d45..63ff30ad 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -1,12 +1,14 @@ { "workflow.button.label_import_from_dsl": "Import from DSL", "workflow.label.trigger_node_name": "Trigger", - "workflow.label.end_node_name": "End", + "workflow.label.trigger_node_desc": "It needs to be triggered by the user to run the whole workflow, the starting node is a Trigger node, and input parameters are allowed to be set when triggering.", "workflow.label.select_node_name": "Entity Select", "workflow.label.ifelse_node_name": "IF/ELSE", "workflow.label.assigner_node_name": "Entity Assigner", "workflow.label.timer_node_name": "Timer", + "workflow.label.timer_node_desc": "Based on the scheduled time or cycle as a trigger to run the entire workflow conditions, without the need for active triggering, the starting node is the Timer node.", "workflow.label.listener_node_name": "Entity Listener", + "workflow.label.listener_node_desc": "Based on the listening entity object changes as a trigger to run the entire workflow conditions, without the need for active triggering, the starting node for the Entity Listening node.", "workflow.label.code_node_name": "Code", "workflow.label.service_node_name": "Service Invocation", "workflow.label.email_node_name": "Email Notify", @@ -17,5 +19,6 @@ "workflow.label.node_category_control": "Control", "workflow.label.node_category_action": "Action", "workflow.label.node_category_external": "External", - "workflow.label.parallel_depth_limit_tip": "并行嵌套层数限制 {1} 层" + "workflow.label.parallel_depth_limit_tip": "并行嵌套层数限制 {1} 层", + "workflow.modal.entry_node_create_title": "What type of starting Node do you want to create?" } From b84f64c1ec90b1aa476149049568407713745713 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 6 Dec 2024 16:52:11 +0800 Subject: [PATCH 007/172] feat: add workflow api service --- .../editor/components/entry-panel/index.tsx | 81 +++--- .../src/pages/workflow/views/editor/index.tsx | 36 ++- apps/web/src/services/http/index.ts | 1 + apps/web/src/services/http/workflow.ts | 262 ++++++++++++++++++ 4 files changed, 339 insertions(+), 41 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx index d6fe6d6f..aec3f931 100644 --- a/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx @@ -1,15 +1,20 @@ import { memo, useState } from 'react'; import { Panel, useNodes } from '@xyflow/react'; import cls from 'classnames'; -import { Button } from '@mui/material'; +import { Button, CircularProgress } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { basicNodeConfigs } from '../../constant'; import useInteractions from '../../hooks/useInteractions'; import './style.less'; +interface Props { + /** Is Data Loading */ + loading?: boolean; +} + const entryNodeConfigs = Object.values(basicNodeConfigs).filter(node => node.category === 'entry'); -const EntryModal = () => { +const EntryModal: React.FC = ({ loading }) => { const { getIntlText } = useI18n(); const nodes = useNodes(); const { addNode } = useInteractions(); @@ -22,46 +27,54 @@ const EntryModal = () => { return nodes.length ? null : ( -
-
-
- {getIntlText('workflow.modal.entry_node_create_title')} + {loading ? ( + + ) : ( +
+
+
+ {getIntlText('workflow.modal.entry_node_create_title')} +
-
-
-
- {entryNodeConfigs.map(config => ( -
setSelectedNodeType(config.type)} - > +
+
+ {entryNodeConfigs.map(config => (
setSelectedNodeType(config.type)} > - {config.icon} -
-
-
- {getIntlText(config.labelIntlKey)} +
+ {config.icon}
-
- {getIntlText(config.descIntlKey || '')} +
+
+ {getIntlText(config.labelIntlKey)} +
+
+ {getIntlText(config.descIntlKey || '')} +
-
- ))} + ))} +
+
+
+
-
- -
-
+ )} ); }; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 16537b4c..2eeee796 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -1,4 +1,6 @@ -import { memo, useState, useCallback, useEffect } from 'react'; +import { memo, useState, useCallback } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { useRequest } from 'ahooks'; import { ReactFlow, Background, @@ -9,6 +11,7 @@ import { type NodeChange, } from '@xyflow/react'; import { useTheme } from '@milesight/shared/src/hooks'; +import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { MIN_ZOOM, MAX_ZOOM } from './constant'; import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; import { @@ -41,11 +44,30 @@ const WorkflowEditor = () => { const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); - // TODO: Init workflow data - // useEffect(() => { - // setNodes(demoData.nodes as WorkflowNode[]); - // setEdges(demoData.edges as WorkflowEdge[]); - // }, [setNodes, setEdges]); + // ---------- Fetch Data ---------- + const [searchParams] = useSearchParams(); + const wid = searchParams.get('wid'); + const { loading } = useRequest( + async () => { + if (!wid) return; + // TODO: Call workflow detail API + // const [error, resp] = await awaitWrap(workflowAPI.getFlowDesign({ id: wid })); + + // if (error || !isRequestSuccess(resp)) return; + // const data = getResponseData(resp); + // console.log(data); + + await new Promise(resolve => { + setTimeout(resolve, 500); + }); + setNodes(demoData.nodes as WorkflowNode[]); + setEdges(demoData.edges as WorkflowEdge[]); + }, + { + debounceWait: 300, + refreshDeps: [wid], + }, + ); // ---------- Show Helper Lines when node change ---------- const [helperLineHorizontal, setHelperLineHorizontal] = useState(undefined); @@ -113,7 +135,7 @@ const WorkflowEditor = () => { vertical={helperLineVertical} /> - +
diff --git a/apps/web/src/services/http/index.ts b/apps/web/src/services/http/index.ts index f779281c..cfaa999f 100644 --- a/apps/web/src/services/http/index.ts +++ b/apps/web/src/services/http/index.ts @@ -6,3 +6,4 @@ export { default as entityAPI, type EntityAPISchema } from './entity'; export { default as integrationAPI, type IntegrationAPISchema } from './integration'; export { default as globalAPI, type GlobalAPISchema } from './global'; export { default as dashboardAPI, type DashboardAPISchema } from './dashboard'; +export { default as workflowAPI, type WorkflowStatus } from './workflow'; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index e69de29b..2f847bb5 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -0,0 +1,262 @@ +import { client, attachAPI, API_PREFIX } from './client'; + +export type WorkflowStatus = 'enabled' | 'disabled'; + +export interface WorkflowAPISchema extends APISchema { + /** Get workflow list */ + getList: { + request: { + name?: string; + status?: WorkflowStatus; + }; + response: { + /** ID */ + id: ApiKey; + /** Name */ + name: string; + /** Reamrk */ + description: string; + /** Create Time */ + created_at: number; + /** Update Time */ + updated_at: number; + /** Enabled */ + enabled: boolean; + /** User Email */ + user_email: string; + }; + }; + + /** Add a workflow */ + addFlow: { + request: { + name: string; + description: string; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Update a workflow */ + updateFlow: { + request: { + id: ApiKey; + name: string; + description: string; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Delete a workflow */ + deleteFlow: { + request: { + id: ApiKey; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Import workflow from DSL */ + importFlow: { + request: { + dsl: unknown; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Export workflow as DSL */ + exportFlow: { + request: { + id: ApiKey; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Enable/Disable a workflow */ + enableFlow: { + request: { + id: ApiKey; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Get workflow log list */ + getLogList: { + request: { + // TODO: use workflow log status enum + status?: string; + }; + response: { + /** ID */ + id: ApiKey; + /** Start Time */ + start_time: number; + /** Running status */ + status: string; + }; + }; + + /** Get workflow log detail */ + getLogDetail: { + request: { + id: ApiKey; + }; + response: { + /** Node ID */ + node_id: ApiKey; + /** Node Name */ + node_name: string; + /** Running status */ + status: string; + /** Cost Time */ + cost: number; + // TODO + input: Record; + // TODO + output: Record; + }[]; + }; + + /** Get workflow Design */ + getFlowDesign: { + request: { + id: ApiKey; + }; + response: { + /** ID */ + id: ApiKey; + /** Name */ + name: string; + /** Description */ + description: string; + /** Created At */ + created_at: number; + /** Updated At */ + updated_at: number; + /** Enabled */ + enabled: boolean; + /** User Email */ + user_email: string; + /** Workflow DSL */ + dsl: string; + }; + }; + + /** Check workflow Design */ + checkFlowDesign: { + request: { + dsl: string; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Save workflow Design */ + saveFlowDesign: { + request: { + id: ApiKey; + dsl: string; + }; + response: { + /** ID */ + id: ApiKey; + }; + }; + + /** Run workflow */ + runFlow: { + request: { + id: ApiKey; + }; + response: { + // TODO: use workflow log status enum + status: string; + trace_infos: { + node_id: ApiKey; + node_name: string; + status: string; + const: number; + input: Record; + output: Record; + }[]; + }; + }; + + /** Run single node */ + runSingleNode: { + request: { + node_config: Record; + input: Record; + }; + response: { + node_id: ApiKey; + node_name: string; + status: string; + const: number; + input: Record; + output: Record; + }; + }; + + /** Get workflow nodes info */ + getFlowNodes: { + request: void; + response: Record< + WorkflowNodeCategoryType, + { + componentId: string; + componentName: string; + }[] + >; + }; + + /** Get node form schema */ + getNodeForm: { + request: { + id: ApiKey; + }; + response: unknown; + }; +} + +/** + * Workflow API Service + */ +export default attachAPI(client, { + apis: { + getList: `POST ${API_PREFIX}/workflow/flows/search`, + addFlow: `POST ${API_PREFIX}/workflow/flows`, + updateFlow: `PUT ${API_PREFIX}/workflow/flows/:id`, + deleteFlow: `DELETE ${API_PREFIX}/workflow/flows/:id`, + importFlow: `POST ${API_PREFIX}/workflow/flows/import`, + exportFlow: `GET ${API_PREFIX}/workflow/flows/:id/export`, + enableFlow: `GET ${API_PREFIX}/workflow/flows/:id/:status`, + getLogList: `POST ${API_PREFIX}/workflow/flows/:id/logs/search`, + getLogDetail: `GET ${API_PREFIX}/workflow/flows/logs/:id`, + getFlowDesign: `GET ${API_PREFIX}/workflow/flows/:id/design`, + checkFlowDesign: `POST ${API_PREFIX}/workflow/flows/:id/design/validate`, + saveFlowDesign: `POST ${API_PREFIX}/workflow/flows/:id/design`, + runFlow: `POST ${API_PREFIX}/workflow/flows/:id/test`, + runSingleNode: `POST ${API_PREFIX}/workflow/flows/node/test`, + getFlowNodes: `GET ${API_PREFIX}/workflow/components`, + getNodeForm: `GET ${API_PREFIX}/workflow/components/:id`, + }, +}); From 869fc35ffb7bdbc49dda081efe886d6776ce4be4 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 6 Dec 2024 17:09:43 +0800 Subject: [PATCH 008/172] fix: use currentColor to fill icon path --- .../icons/components/entity-filled.tsx | 17 ++++------------- .../src/components/icons/components/entity.tsx | 2 +- .../components/icons/components/workflow.tsx | 3 +-- 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/packages/shared/src/components/icons/components/entity-filled.tsx b/packages/shared/src/components/icons/components/entity-filled.tsx index 8333008c..1bef2d95 100644 --- a/packages/shared/src/components/icons/components/entity-filled.tsx +++ b/packages/shared/src/components/icons/components/entity-filled.tsx @@ -3,24 +3,15 @@ import { createSvgIcon } from '@mui/material/utils'; const EntityFilledIcon = createSvgIcon( - - - + + + diff --git a/packages/shared/src/components/icons/components/entity.tsx b/packages/shared/src/components/icons/components/entity.tsx index 3709bba5..6d08211d 100644 --- a/packages/shared/src/components/icons/components/entity.tsx +++ b/packages/shared/src/components/icons/components/entity.tsx @@ -3,7 +3,7 @@ import { createSvgIcon } from '@mui/material/utils'; const EntityIcon = createSvgIcon( , 'WorkflowIcon', From 61d55d7e5fc075c1ee4726b6bf7a340d1a9ff443 Mon Sep 17 00:00:00 2001 From: Nian Date: Fri, 6 Dec 2024 18:35:29 +0800 Subject: [PATCH 009/172] feat: Workflow Editing Modal --- .../edit-modal/hook/useEditFormItems.tsx | 66 +++++++++++++++++++ .../workflow/components/edit-modal/index.tsx | 60 ++++++++++++++++- .../src/pages/workflow/components/index.ts | 1 + packages/locales/src/lang/en/global.json | 4 +- 4 files changed, 129 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx diff --git a/apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx b/apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx new file mode 100644 index 00000000..2b313388 --- /dev/null +++ b/apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx @@ -0,0 +1,66 @@ +import { useMemo } from 'react'; +import { type ControllerProps } from 'react-hook-form'; +import { TextField } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { checkRequired } from '@milesight/shared/src/utils/validators'; + +/** + * type of dataSource + */ +export type FormDataProps = { + name: string; + remark: string; +}; + +const useEditFormItems = (initialData: FormDataProps) => { + const { getIntlText } = useI18n(); + const { name, remark } = initialData; + const formItems = useMemo(() => { + const result: ControllerProps[] = []; + result.push( + { + name: 'name', + defaultValue: name, + rules: { + validate: { checkRequired: checkRequired() }, + }, + render({ field: { onChange, value }, fieldState: { error } }) { + return ( + + ); + }, + }, + { + name: 'remark', + defaultValue: remark, + render({ field: { onChange, value }, fieldState: { error } }) { + return ( + + ); + }, + }, + ); + + return result; + }, [getIntlText]); + + return formItems; +}; + +export default useEditFormItems; diff --git a/apps/web/src/pages/workflow/components/edit-modal/index.tsx b/apps/web/src/pages/workflow/components/edit-modal/index.tsx index fd933a5d..e9e06f4d 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/index.tsx @@ -1 +1,59 @@ -// 添加/编辑弹窗组件 +import React from 'react'; +import { useMemoizedFn } from 'ahooks'; +import cls from 'classnames'; +import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal, toast, type ModalProps } from '@milesight/shared/src/components'; +import useEditFormItems, { type FormDataProps } from './hook/useEditFormItems'; + +interface Props extends Omit { + /** error callback */ + onError?: (err: any) => void; + + /** success callback */ + onSuccess?: (params: FormDataProps) => void; + + /** table-row item */ + data: FormDataProps; +} + +/** + * workflow edit modal + */ +const EditModal: React.FC = ({ visible, data, onCancel, onError, onSuccess, ...props }) => { + const { getIntlText } = useI18n(); + + // ---------- forms processing ---------- + const { control, formState, handleSubmit, reset } = useForm(); + const formItems = useEditFormItems(data); + + const onSubmit: SubmitHandler = async ({ ...params }) => { + reset(); + onSuccess?.(params); + // TODO: success tips: Save successfully? + toast.success(getIntlText('common.message.operation_success')); + }; + + const handleCancel = useMemoizedFn(() => { + reset(); + onCancel?.(); + }); + + return ( + + {formItems.map(props => ( + {...props} key={props.name} control={control} /> + ))} + + ); +}; + +export default EditModal; diff --git a/apps/web/src/pages/workflow/components/index.ts b/apps/web/src/pages/workflow/components/index.ts index e69de29b..af1b9d71 100644 --- a/apps/web/src/pages/workflow/components/index.ts +++ b/apps/web/src/pages/workflow/components/index.ts @@ -0,0 +1 @@ +export { default as EditModal } from './edit-modal'; diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 8a85789c..22ddaf2b 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -158,5 +158,7 @@ "common.label.enable_status": "Enable Status", "common.label.update_time": "Update Time", "common.label.back": "Back", - "common.label.create": "Create" + "common.label.create": "Create", + "common.label.edit_workflow_modal": "Edit Workflow", + "common.label.workflow_name": "Workflow Name" } From 81ad25ef1792720070bb8061e4cc4bd2d0db3445 Mon Sep 17 00:00:00 2001 From: Nian Date: Mon, 9 Dec 2024 09:50:23 +0800 Subject: [PATCH 010/172] fix: Add a workflow addition dialog mode and make modifications for PR reviews (code standards and field validation) --- ...FormItems.tsx => useWorkflowFormItems.tsx} | 18 ++++++---- .../workflow/components/edit-modal/index.tsx | 35 ++++++++++++++----- packages/locales/src/lang/en/global.json | 4 +-- packages/locales/src/lang/en/workflow.json | 5 ++- 4 files changed, 43 insertions(+), 19 deletions(-) rename apps/web/src/pages/workflow/components/edit-modal/hook/{useEditFormItems.tsx => useWorkflowFormItems.tsx} (74%) diff --git a/apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx similarity index 74% rename from apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx rename to apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx index 2b313388..080e1f61 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/hook/useEditFormItems.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx @@ -2,19 +2,19 @@ import { useMemo } from 'react'; import { type ControllerProps } from 'react-hook-form'; import { TextField } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; -import { checkRequired } from '@milesight/shared/src/utils/validators'; +import { checkRequired, checkMaxLength } from '@milesight/shared/src/utils/validators'; /** * type of dataSource */ export type FormDataProps = { name: string; - remark: string; + remark?: string; }; -const useEditFormItems = (initialData: FormDataProps) => { +const useEditFormItems = (initialData?: FormDataProps) => { const { getIntlText } = useI18n(); - const { name, remark } = initialData; + const { name = '', remark = '' } = initialData || {}; const formItems = useMemo(() => { const result: ControllerProps[] = []; result.push( @@ -22,7 +22,10 @@ const useEditFormItems = (initialData: FormDataProps) => { name: 'name', defaultValue: name, rules: { - validate: { checkRequired: checkRequired() }, + validate: { + checkRequired: checkRequired(), + checkMaxLength: checkMaxLength({ max: 50 }), + }, }, render({ field: { onChange, value }, fieldState: { error } }) { return ( @@ -30,7 +33,7 @@ const useEditFormItems = (initialData: FormDataProps) => { required fullWidth type="text" - label={getIntlText('common.label.workflow_name')} + label={getIntlText('workflow.modal.workflow_name')} error={!!error} helperText={error ? error.message : null} value={value} @@ -42,6 +45,9 @@ const useEditFormItems = (initialData: FormDataProps) => { { name: 'remark', defaultValue: remark, + rules: { + validate: { checkMaxLength: checkMaxLength({ max: 1000 }) }, + }, render({ field: { onChange, value }, fieldState: { error } }) { return ( { /** error callback */ onError?: (err: any) => void; /** success callback */ - onSuccess?: (params: FormDataProps) => void; + onConfirm?: (params: FormDataProps) => void; /** table-row item */ - data: FormDataProps; + data?: FormDataProps; + + /** table-row item */ + isAdd?: boolean; } /** - * workflow edit modal + * workflow add/edit modal */ -const EditModal: React.FC = ({ visible, data, onCancel, onError, onSuccess, ...props }) => { +const EditModal: React.FC = ({ + visible, + isAdd = false, + data, + onCancel, + onError, + onConfirm, + ...props +}) => { const { getIntlText } = useI18n(); // ---------- forms processing ---------- const { control, formState, handleSubmit, reset } = useForm(); const formItems = useEditFormItems(data); + const modalTitle = useMemo(() => { + return isAdd + ? getIntlText('workflow.modal.add_workflow_modal') + : getIntlText('workflow.modal.edit_workflow_modal'); + }, [isAdd]); + const onSubmit: SubmitHandler = async ({ ...params }) => { reset(); - onSuccess?.(params); + onConfirm?.(params); // TODO: success tips: Save successfully? toast.success(getIntlText('common.message.operation_success')); }; @@ -42,9 +59,9 @@ const EditModal: React.FC = ({ visible, data, onCancel, onError, onSucces return ( Date: Mon, 9 Dec 2024 10:09:06 +0800 Subject: [PATCH 011/172] fix: handle the confirmation callback function for the edit dialog --- .../workflow/components/edit-modal/index.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/apps/web/src/pages/workflow/components/edit-modal/index.tsx b/apps/web/src/pages/workflow/components/edit-modal/index.tsx index da7d22cb..56cb6787 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/index.tsx @@ -3,15 +3,12 @@ import { useMemoizedFn } from 'ahooks'; import cls from 'classnames'; import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; import { useI18n } from '@milesight/shared/src/hooks'; -import { Modal, toast, type ModalProps } from '@milesight/shared/src/components'; +import { Modal, type ModalProps } from '@milesight/shared/src/components'; import useEditFormItems, { type FormDataProps } from './hook/useWorkflowFormItems'; interface Props extends Omit { - /** error callback */ - onError?: (err: any) => void; - - /** success callback */ - onConfirm?: (params: FormDataProps) => void; + /** confirm callback */ + onConfirm?: (params: FormDataProps) => Promise | void; /** table-row item */ data?: FormDataProps; @@ -28,7 +25,6 @@ const EditModal: React.FC = ({ isAdd = false, data, onCancel, - onError, onConfirm, ...props }) => { @@ -45,10 +41,11 @@ const EditModal: React.FC = ({ }, [isAdd]); const onSubmit: SubmitHandler = async ({ ...params }) => { + if (onConfirm) { + await onConfirm(params); + } + // Clear the form data upon confirmation. reset(); - onConfirm?.(params); - // TODO: success tips: Save successfully? - toast.success(getIntlText('common.message.operation_success')); }; const handleCancel = useMemoizedFn(() => { @@ -61,7 +58,7 @@ const EditModal: React.FC = ({ visible={visible} title={modalTitle} className={cls({ loading: formState.isSubmitting })} - onOk={handleSubmit(onSubmit, onError)} + onOk={handleSubmit(onSubmit)} onCancel={handleCancel} sx={{ '& .MuiDialog-paper': { width: 600 } }} {...props} From 5c393a9a51d457bf0b65b6ace4d4f3d33c473fc4 Mon Sep 17 00:00:00 2001 From: Nian Date: Mon, 9 Dec 2024 13:33:10 +0800 Subject: [PATCH 012/172] feat: add/edit dialog UI interaction --- .../edit-modal/hook/useWorkflowFormItems.tsx | 8 ++- .../workflow/components/edit-modal/index.tsx | 18 ++++-- .../src/pages/workflow/hooks/useColumns.tsx | 12 +++- apps/web/src/pages/workflow/index.tsx | 62 ++++++++++++++++++- packages/locales/src/lang/en/workflow.json | 3 +- 5 files changed, 90 insertions(+), 13 deletions(-) diff --git a/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx index 080e1f61..2ca8e0f8 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx @@ -12,7 +12,9 @@ export type FormDataProps = { remark?: string; }; -const useEditFormItems = (initialData?: FormDataProps) => { +export type FormDataKeys = keyof FormDataProps; + +const useWorkflowFormItems = (initialData?: FormDataProps) => { const { getIntlText } = useI18n(); const { name = '', remark = '' } = initialData || {}; const formItems = useMemo(() => { @@ -64,9 +66,9 @@ const useEditFormItems = (initialData?: FormDataProps) => { ); return result; - }, [getIntlText]); + }, [getIntlText, initialData]); return formItems; }; -export default useEditFormItems; +export default useWorkflowFormItems; diff --git a/apps/web/src/pages/workflow/components/edit-modal/index.tsx b/apps/web/src/pages/workflow/components/edit-modal/index.tsx index 56cb6787..9a7cf44f 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/index.tsx @@ -1,10 +1,13 @@ -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo } from 'react'; import { useMemoizedFn } from 'ahooks'; import cls from 'classnames'; import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; import { useI18n } from '@milesight/shared/src/hooks'; import { Modal, type ModalProps } from '@milesight/shared/src/components'; -import useEditFormItems, { type FormDataProps } from './hook/useWorkflowFormItems'; +import useWorkflowFormItems, { + type FormDataProps, + type FormDataKeys, +} from './hook/useWorkflowFormItems'; interface Props extends Omit { /** confirm callback */ @@ -31,9 +34,14 @@ const EditModal: React.FC = ({ const { getIntlText } = useI18n(); // ---------- forms processing ---------- - const { control, formState, handleSubmit, reset } = useForm(); - const formItems = useEditFormItems(data); - + const { control, formState, handleSubmit, reset, setValue } = useForm(); + const formItems = useWorkflowFormItems(data); + // Update the initial form data + useEffect(() => { + Object.keys(data || { name: '', remark: '' }).forEach(key => { + setValue(key as FormDataKeys, data?.[key as FormDataKeys] ?? ''); + }); + }, [formItems]); const modalTitle = useMemo(() => { return isAdd ? getIntlText('workflow.modal.add_workflow_modal') diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index c9be75ae..d97d94f5 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -1,11 +1,11 @@ import { useMemo } from 'react'; import { Stack, IconButton } from '@mui/material'; import { useI18n, useTime } from '@milesight/shared/src/hooks'; -import { ListAltIcon, DeleteOutlineIcon } from '@milesight/shared/src/components'; +import { ListAltIcon, DeleteOutlineIcon, EditIcon } from '@milesight/shared/src/components'; import { Tooltip, type ColumnType } from '@/components'; import { type DeviceAPISchema } from '@/services/http'; -type OperationType = 'detail' | 'delete'; +type OperationType = 'detail' | 'delete' | 'edit'; export type TableRowDataType = ObjectToCamelCase< DeviceAPISchema['getList']['response']['content'][0] @@ -79,6 +79,14 @@ const useColumns = ({ onButtonClick }: UseColumnsPro spacing="4px" sx={{ height: '100%', alignItems: 'center', justifyContent: 'end' }} > + + onButtonClick('edit', row)} + > + + + { const navigate = useNavigate(); const { getIntlText } = useI18n(); // ---------- 列表数据相关逻辑 ---------- const [keyword, setKeyword] = useState(); + const [editOption, SetEditOption] = useState({ + isAdd: false, + openModal: false, + dataSource: { + name: '', + remark: '', + }, + }); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); const { @@ -89,7 +105,7 @@ const Workflow = () => { variant="contained" sx={{ height: 36, textTransform: 'none' }} startIcon={} - onClick={() => navigate('/workflow/editor')} + onClick={() => handlerEditModal(true, true)} > {getIntlText('common.label.add')} @@ -109,15 +125,50 @@ const Workflow = () => { startIcon={} onClick={() => handleDeleteConfirm()} > - {getIntlText('common.label.delete')} + {getIntlText('workflow.button.delete_filter')} ); }, [getIntlText, handleDeleteConfirm, selectedIds]); + const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: FormDataProps): void => { + const newEditOption: ModalOption = { + isAdd, + openModal: isOpen, + dataSource: { + name: '', + remark: '', + }, + }; + if (!isAdd && isOpen) { + newEditOption.dataSource = { + name: row?.name ?? '', + remark: row?.remark ?? '', + }; + } + SetEditOption(newEditOption); + }; + const submitEditModal = async (data: FormDataProps) => { + const { isAdd } = editOption; + // const [error, res] = await awaitWrap(isAdd ? WorkflowAPI.addWorkflow(data) : WorkflowAPI.updateWorkflow(data)); + // if (isRequestSuccess(res)) { + handlerEditModal(false, false); + if (isAdd) { + navigate('/workflow/editor', { state: data }); + } else { + // toast.success(getIntlText('common.message.operation_success')); + } + // } else { + // toast.error(error); + // } + }; const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); switch (type) { + case 'edit': { + handlerEditModal(false, true, record); + break; + } case 'detail': { // TODO: workflow id 传参 navigate('/workflow/editor'); @@ -161,6 +212,13 @@ const Workflow = () => { />
+ handlerEditModal(false, false)} + onConfirm={submitEditModal} + />
); }; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index fb83e21e..09e2e378 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -23,5 +23,6 @@ "workflow.modal.entry_node_create_title": "What type of starting Node do you want to create?", "workflow.modal.add_workflow_modal": "Add Workflow", "workflow.modal.edit_workflow_modal": "Edit Workflow", - "workflow.modal.workflow_name": "Workflow Name" + "workflow.modal.workflow_name": "Workflow Name", + "workflow.button.delete_filter": "More Filters" } From cf83e8adb7bcea8459b421f096763892769e0bf0 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 9 Dec 2024 14:36:29 +0800 Subject: [PATCH 013/172] fix: Interactive optimization during data editing --- .../edit-modal/hook/useWorkflowFormItems.tsx | 8 ++-- .../workflow/components/edit-modal/index.tsx | 47 ++++++++----------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx index 080e1f61..e6a32ed1 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx @@ -12,15 +12,14 @@ export type FormDataProps = { remark?: string; }; -const useEditFormItems = (initialData?: FormDataProps) => { +const useEditFormItems = () => { const { getIntlText } = useI18n(); - const { name = '', remark = '' } = initialData || {}; const formItems = useMemo(() => { const result: ControllerProps[] = []; result.push( { name: 'name', - defaultValue: name, + defaultValue: '', rules: { validate: { checkRequired: checkRequired(), @@ -44,7 +43,7 @@ const useEditFormItems = (initialData?: FormDataProps) => { }, { name: 'remark', - defaultValue: remark, + defaultValue: '', rules: { validate: { checkMaxLength: checkMaxLength({ max: 1000 }) }, }, @@ -53,6 +52,7 @@ const useEditFormItems = (initialData?: FormDataProps) => { { +export interface Props extends Omit { /** confirm callback */ onConfirm?: (params: FormDataProps) => Promise | void; - /** table-row item */ + /** When data is not empty, it is in edit mode */ data?: FormDataProps; - - /** table-row item */ - isAdd?: boolean; } /** * workflow add/edit modal */ -const EditModal: React.FC = ({ - visible, - isAdd = false, - data, - onCancel, - onConfirm, - ...props -}) => { +const EditModal: React.FC = ({ visible, data, onConfirm, ...props }) => { const { getIntlText } = useI18n(); // ---------- forms processing ---------- - const { control, formState, handleSubmit, reset } = useForm(); - const formItems = useEditFormItems(data); + const { control, formState, handleSubmit, setValue, reset } = useForm(); + const formItems = useEditFormItems(); + const isEditMode = !!data; const modalTitle = useMemo(() => { - return isAdd + return !isEditMode ? getIntlText('workflow.modal.add_workflow_modal') : getIntlText('workflow.modal.edit_workflow_modal'); - }, [isAdd]); + }, [getIntlText, isEditMode]); const onSubmit: SubmitHandler = async ({ ...params }) => { if (onConfirm) { await onConfirm(params); } - // Clear the form data upon confirmation. - reset(); }; - const handleCancel = useMemoizedFn(() => { - reset(); - onCancel?.(); - }); + useEffect(() => { + if (!visible || !data) { + setTimeout(reset, 100); + return; + } + + Object.entries(data).forEach(([key, value]) => { + setValue(key as keyof FormDataProps, value); + }); + }, [data, visible, reset, setValue]); return ( {formItems.map(props => ( From 2d938938d471262494f640d198b6c36f979bddf8 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 9 Dec 2024 17:50:18 +0800 Subject: [PATCH 014/172] feat: add features for workflow topbar --- .../src/pages/workflow/components/index.ts | 2 +- .../workflow/views/editor/components/index.ts | 4 +- .../editor/components/log-panel/index.tsx | 16 ++ .../editor/components/test-button/index.tsx | 37 ++++ .../editor/components/test-button/style.less | 14 ++ .../views/editor/components/topbar/index.tsx | 158 ++++++++++++++++-- .../views/editor/components/topbar/style.less | 75 +++++++++ .../src/pages/workflow/views/editor/index.tsx | 53 +++++- apps/web/src/routes/routes.tsx | 1 - apps/web/src/services/http/workflow.ts | 11 +- apps/web/src/styles/index.less | 2 +- packages/locales/src/lang/en/global.json | 3 +- packages/locales/src/lang/en/workflow.json | 4 +- .../shared/src/components/icons/index.tsx | 1 + 14 files changed, 357 insertions(+), 24 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/test-button/style.less diff --git a/apps/web/src/pages/workflow/components/index.ts b/apps/web/src/pages/workflow/components/index.ts index af1b9d71..1b39ba1c 100644 --- a/apps/web/src/pages/workflow/components/index.ts +++ b/apps/web/src/pages/workflow/components/index.ts @@ -1 +1 @@ -export { default as EditModal } from './edit-modal'; +export { default as EditModal, type Props as EditModalProps } from './edit-modal'; diff --git a/apps/web/src/pages/workflow/views/editor/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/index.ts index 8155e4f3..719452e2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/index.ts @@ -1,4 +1,4 @@ -export { default as Topbar } from './topbar'; +export { default as Topbar, type DesignMode } from './topbar'; export { default as Controls, type ControlsProps } from './controls'; export { default as ConfigPanel } from './config-panel'; @@ -9,3 +9,5 @@ export { default as Edge } from './edge'; export { default as HelperLines, getHelperLines } from './helper-lines'; export { default as NodeMenu } from './node-menu'; export { default as EntryPanel } from './entry-panel'; +export { default as LogPanel } from './log-panel'; +export { default as TestButton } from './test-button'; diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx new file mode 100644 index 00000000..ac2e6a88 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -0,0 +1,16 @@ +import { Panel } from '@xyflow/react'; + +/** + * Log Detail Panel + */ +const LogPanel = () => { + return ( + +
+ Log Detail +
+
+ ); +}; + +export default LogPanel; diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx new file mode 100644 index 00000000..5a8d30d2 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -0,0 +1,37 @@ +import { ButtonGroup, Button, Popover } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { PlayArrowIcon, HistoryIcon } from '@milesight/shared/src/components'; +import './style.less'; + +export type ButtonType = 'test' | 'history'; + +interface Props { + onClick?: (type: ButtonType) => void; +} + +/** + * Test Button + */ +const TestButton: React.FC = ({ onClick }) => { + const { getIntlText } = useI18n(); + const handleClick = (type: ButtonType) => { + onClick?.(type); + }; + + return ( + + + + + ); +}; + +export default TestButton; diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/style.less b/apps/web/src/pages/workflow/views/editor/components/test-button/style.less new file mode 100644 index 00000000..28d99f74 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/style.less @@ -0,0 +1,14 @@ +.@{prefix}-workflow-test-button { + .@{mui-prefix}Button-root { + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + + &:last-child { + padding: @padding-xs; + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx index e414949a..6a25deae 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx @@ -1,25 +1,163 @@ +import React, { useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import { Button } from '@mui/material'; +import { type AxiosError } from 'axios'; +import { Button, IconButton, Grid2, Switch, ToggleButtonGroup, ToggleButton } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; -import { ArrowBackIcon } from '@milesight/shared/src/components'; +import { ArrowBackIcon, EditIcon, toast } from '@milesight/shared/src/components'; +import { Tooltip } from '@/components'; +import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { EditModal, type EditModalProps } from '@/pages/workflow/components'; import './style.less'; +/** + * Workflow Design Mode + */ +export type DesignMode = 'canvas' | 'advanced'; + +interface Props { + /** Workflow Detail Data */ + data?: { + id: ApiKey; + name: string; + remark?: string; + enabled?: boolean; + }; + + /** Default Workflow Design Mode */ + mode?: DesignMode; + + /** Right Slot */ + rightSlot?: React.ReactNode; + + /** Edit Error Callback */ + onEditError?: (error: AxiosError | null) => void; + + /** Edit Success Callback */ + onEditSuccess?: () => void; + + /** Design Mode Change Callback */ + onDesignModeChange: (mode: DesignMode) => void; +} + /** * Workflow 头部工具栏组件 */ -const Topbar = () => { +const Topbar: React.FC = ({ + data, + mode = 'canvas', + rightSlot, + onEditError, + onEditSuccess, + onDesignModeChange, +}) => { const navigate = useNavigate(); const { getIntlText } = useI18n(); + // ---------- Workflow Name/Remark/Status Edit ---------- + const [flowData, setFlowData] = useState(); + const [openEditModal, setOpenEditModal] = useState(false); + + const handleEditConfirm: EditModalProps['onConfirm'] = async params => { + console.log({ params }); + if (!flowData?.id) return; + // const [error, resp] = await awaitWrap( + // workflowAPI.updateFlow({ id: flowData.id, ...params }), + // ); + + // if (error || !isRequestSuccess(resp)) { + // onEditError?.(error); + // return; + // } + // TODO: Replace with real data + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + + setOpenEditModal(false); + setFlowData(data => ({ ...data!, ...params })); + toast.success(getIntlText('common.message.operation_success')); + }; + + const handleSwitchChange = async (e: React.ChangeEvent) => { + if (!flowData?.id) return; + const { checked } = e.target; + + // const [error, resp] = await awaitWrap( + // workflowAPI.enableFlow({ id: flowData.id, status: checked ? 'enabled' : 'disabled' }), + // ); + + // if (error || !isRequestSuccess(resp)) { + // onEditError?.(error); + // return; + // } + // TODO: Replace with real data + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + + setFlowData(data => ({ ...data!, enabled: checked })); + toast.success(getIntlText('common.message.operation_success')); + }; + + useEffect(() => setFlowData(data), [data]); + return (
- + + + +
+ + setOpenEditModal(true)}> + + +
+
+ + { + if (!value) return; + onDesignModeChange(value as DesignMode); + }} + > + + {getIntlText('workflow.label.design_mode_canvas_name')} + + + {getIntlText('workflow.label.design_mode_advanced_name')} + + + + + + {rightSlot} + +
+ setOpenEditModal(false)} + onConfirm={handleEditConfirm} + />
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/style.less b/apps/web/src/pages/workflow/views/editor/components/topbar/style.less index 5cce7dcf..bda7b445 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/style.less @@ -1,4 +1,79 @@ .@{prefix}-workflow-topbar { padding: @padding-xs @padding-lg; background-color: var(--component-background); + + .@{mui-prefix}Grid2-root > .@{mui-prefix}Grid2-root { + display: flex; + align-items: center; + } + + .@{prefix}-workflow-topbar-left { + .btn-back { + flex-shrink: 0; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } + + .title { + display: flex; + align-items: center; + width: 100%; + margin-left: @margin-md; + + .@{prefix}-tooltip { + max-width: calc(100% - 150px); + } + + .@{mui-prefix}IconButton-root { + margin-left: @margin-xs; + font-size: @font-size-lg; + } + } + } + + .@{prefix}-workflow-topbar-center { + justify-content: center; + + .@{mui-prefix}ButtonBase-root { + text-transform: none; + } + + .@{prefix}-workflow-mode-buttons { + background-color: var(--gray-color-2); + border: 2px solid var(--gray-color-2); + + .@{mui-prefix}ToggleButtonGroup-grouped { + display: inline-block; + padding: @padding-xs @padding-sm; + border: none; + border-radius: @border-radius-base; + .text-size(@font-size-base); + + &::after { + display: block; + height: 1px; + margin-bottom: -1px; + overflow: hidden; + font-weight: @font-weight-bold; + visibility: hidden; + content: attr(aria-label); + } + + &.@{mui-prefix}-selected { + font-weight: @font-weight-bold; + background-color: var(--component-background); + } + } + } + } + + .@{prefix}-workflow-topbar-right { + gap: @margin-sm; + justify-content: end; + } } diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 2eeee796..09e90f28 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -7,10 +7,13 @@ import { SelectionMode, useNodesState, useEdgesState, + useReactFlow, ReactFlowProvider, type NodeChange, } from '@xyflow/react'; -import { useTheme } from '@milesight/shared/src/hooks'; +import { Button } from '@mui/material'; +import { useI18n, useTheme } from '@milesight/shared/src/hooks'; +import { CheckIcon } from '@milesight/shared/src/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { MIN_ZOOM, MAX_ZOOM } from './constant'; import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; @@ -22,6 +25,9 @@ import { HelperLines, getHelperLines, EntryPanel, + LogPanel, + TestButton, + type DesignMode, } from './components'; import demoData from './demo-data.json'; @@ -37,7 +43,9 @@ const edgeTypes: Record> = { */ const WorkflowEditor = () => { const { grey } = useTheme(); + const { getIntlText } = useI18n(); const nodeTypes = useNodeTypes(); + const { toObject } = useReactFlow(); const { isValidConnection } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); @@ -47,7 +55,11 @@ const WorkflowEditor = () => { // ---------- Fetch Data ---------- const [searchParams] = useSearchParams(); const wid = searchParams.get('wid'); - const { loading } = useRequest( + const { + loading, + data: flowData, + run: getFlowDesign, + } = useRequest( async () => { if (!wid) return; // TODO: Call workflow detail API @@ -62,6 +74,8 @@ const WorkflowEditor = () => { }); setNodes(demoData.nodes as WorkflowNode[]); setEdges(demoData.edges as WorkflowEdge[]); + + return { id: 'xxx', name: 'Workflow Name', remark: 'Workflow Remark', enabled: false }; }, { debounceWait: 300, @@ -101,9 +115,42 @@ const WorkflowEditor = () => { [nodes, onNodesChange], ); + // ---------- Design Mode Change ---------- + const [designMode, setDesignMode] = useState('canvas'); + const handleDesignModeChange = useCallback((mode: DesignMode) => { + const data = toObject(); + + // TODO: check the workflow json data is valid + console.log('workflow data', data); + setDesignMode(mode); + }, []); + + // ---------- Save Workflow ---------- + const handleSave = () => { + const data = toObject(); + + // TODO: check the workflow json is valid + console.log('workflow data', data); + }; + return (
- + , + , + ]} + />
diff --git a/apps/web/src/routes/routes.tsx b/apps/web/src/routes/routes.tsx index 4b42d263..d703ae0f 100644 --- a/apps/web/src/routes/routes.tsx +++ b/apps/web/src/routes/routes.tsx @@ -5,7 +5,6 @@ import { DevicesIcon, SettingsIcon, WorkflowIcon, - EntityFilledIcon, } from '@milesight/shared/src/components'; import ErrorBoundaryComponent from './error-boundary'; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 2f847bb5..07546b82 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -31,7 +31,7 @@ export interface WorkflowAPISchema extends APISchema { addFlow: { request: { name: string; - description: string; + remark?: string; }; response: { /** ID */ @@ -44,7 +44,7 @@ export interface WorkflowAPISchema extends APISchema { request: { id: ApiKey; name: string; - description: string; + remark?: string; }; response: { /** ID */ @@ -89,6 +89,7 @@ export interface WorkflowAPISchema extends APISchema { enableFlow: { request: { id: ApiKey; + status: WorkflowStatus; }; response: { /** ID */ @@ -143,8 +144,8 @@ export interface WorkflowAPISchema extends APISchema { id: ApiKey; /** Name */ name: string; - /** Description */ - description: string; + /** Remark */ + remark?: string; /** Created At */ created_at: number; /** Updated At */ @@ -154,7 +155,7 @@ export interface WorkflowAPISchema extends APISchema { /** User Email */ user_email: string; /** Workflow DSL */ - dsl: string; + dsl?: string; }; }; diff --git a/apps/web/src/styles/index.less b/apps/web/src/styles/index.less index c15d2ed0..bd38f2d4 100644 --- a/apps/web/src/styles/index.less +++ b/apps/web/src/styles/index.less @@ -91,7 +91,7 @@ body { overflow: hidden; font-weight: @font-weight-bold; visibility: hidden; - content: attr(title) + content: attr(title); } } diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 8a85789c..abd8d5f4 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -158,5 +158,6 @@ "common.label.enable_status": "Enable Status", "common.label.update_time": "Update Time", "common.label.back": "Back", - "common.label.create": "Create" + "common.label.create": "Create", + "common.label.test": "Test" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index fb83e21e..4ccbdf8d 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -23,5 +23,7 @@ "workflow.modal.entry_node_create_title": "What type of starting Node do you want to create?", "workflow.modal.add_workflow_modal": "Add Workflow", "workflow.modal.edit_workflow_modal": "Edit Workflow", - "workflow.modal.workflow_name": "Workflow Name" + "workflow.modal.workflow_name": "Workflow Name", + "workflow.label.design_mode_canvas_name": "Canvas Mode", + "workflow.label.design_mode_advanced_name": "Advances Mode" } diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 874d04e0..8d3d0c6c 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -86,6 +86,7 @@ export { Hearing as HearingIcon, FactCheck as FactCheckIcon, Loop as LoopIcon, + History as HistoryIcon, } from '@mui/icons-material'; export * from './iot-icons'; From aa67d3c8162b152d4dc48c56854a9b4d61d78a98 Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 10 Dec 2024 14:26:30 +0800 Subject: [PATCH 015/172] feat: import from DSL modal --- .../import-modal/components/style.less | 64 +++++++ .../import-modal/components/uploadField.tsx | 177 ++++++++++++++++++ .../import-modal/hook/useImportFormItems.tsx | 47 +++++ .../components/import-modal/index.tsx | 55 +++++- .../src/pages/workflow/components/index.ts | 1 + packages/locales/src/lang/en/workflow.json | 3 +- .../shared/src/components/icons/index.tsx | 2 + 7 files changed, 347 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/import-modal/components/style.less create mode 100644 apps/web/src/pages/workflow/components/import-modal/components/uploadField.tsx create mode 100644 apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx diff --git a/apps/web/src/pages/workflow/components/import-modal/components/style.less b/apps/web/src/pages/workflow/components/import-modal/components/style.less new file mode 100644 index 00000000..b81a0029 --- /dev/null +++ b/apps/web/src/pages/workflow/components/import-modal/components/style.less @@ -0,0 +1,64 @@ +.@{prefix}-import-box { + .@{prefix}-import-file { + display: flex; + flex: 1 0 0; + flex-direction: column; + gap: 10px; + align-items: center; + align-self: stretch; + justify-content: center; + padding: 35px 10px 41px; + background: var(--gray-1); + border: 1px dashed var(--border-color-base); + border-radius: 4px; + cursor: pointer; + } + .@{prefix}-import-drag { + border-color: var(--purple-8); + } + .@{prefix}-import-error { + border-color: var(--mui-palette-error-main); + } + .@{prefix}-import-error-tip { + margin: 4px 14px 0; + font-size: 0.75rem; + font-weight: 400; + color: var(--mui-palette-error-main); + } + .@{prefix}-file-tip { + margin: 0; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; + color: var(--purple-8); + } + + .@{prefix}-file-list { + padding: 0 10px; + list-style-type: none; + border: 1px dashed var(--border-color-base); + + li { + display: flex; + align-items: center; + justify-content: space-between; + padding: 3px; + + span { + display: flex; + align-items: center; + + .@{prefix}-file-name { + padding: 0; + margin-top: 0; + font-size: 12px; + font-style: normal; + font-weight: 400; + line-height: normal; + } + } + } + + } +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/components/import-modal/components/uploadField.tsx b/apps/web/src/pages/workflow/components/import-modal/components/uploadField.tsx new file mode 100644 index 00000000..8d617d96 --- /dev/null +++ b/apps/web/src/pages/workflow/components/import-modal/components/uploadField.tsx @@ -0,0 +1,177 @@ +import React, { useState, useCallback, useEffect, useMemo } from 'react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import './style.less'; +import { AttachFileIcon, DeleteIcon } from '@milesight/shared/src/components'; +import { IconButton } from '@mui/material'; +import cls from 'classnames'; + +type FileUploadProps = { + /** accept filetype */ + accept?: string; + /** multiple */ + multiple?: boolean; + /** file list */ + value: File | File[]; + /** field error */ + error?: string; + /** upload tips */ + uploadTips?: string; + /** upload Icon */ + uploadIcon?: React.ReactNode; + /** upload callback */ + onChange?: (files: File[]) => void; +}; + +const FileUpload: React.FC = ({ + value, + error, + accept, + multiple = false, + uploadTips, + uploadIcon, + onChange, +}) => { + const [fileList, setFileList] = useState([]); + const [fileDrag, setFileDrag] = useState(false); + const { getIntlText } = useI18n(); + const iconStyle = { + width: 12, + height: 12, + marginRight: '2px', + }; + const uploadLabel = useMemo(() => { + if (uploadTips) { + return uploadTips; + } + return getIntlText('workflow.modal.upload_tips'); + }, [uploadTips]); + useEffect(() => { + if (!value) { + setFileList([]); + } else if (Array.isArray(value)) { + setFileList(value); + } else { + setFileList([value]); + } + }, [value]); + const filterFilesByAccept = (files: File[]): File[] => { + if (!accept) { + return files; + } + const acceptRules = accept.split(',').map(rule => rule.trim().toLowerCase()); + return files.filter(file => { + const fileType = file.type.toLowerCase(); + const fileExtension = `.${file.name.split('.').pop()?.toLowerCase()}`; + return acceptRules.some(rule => { + if (rule.startsWith('.')) { + return fileExtension === rule; + } + if (rule.endsWith('/*')) { + return fileType.startsWith(rule.replace('/*', '')); + } + return fileType === rule; + }); + }); + }; + + const handleFiles = (files: FileList) => { + let fileArray = Array.from(files); + fileArray = filterFilesByAccept(fileArray); + if (fileArray.length === 0) { + return; + } + setFileList(prev => { + if (multiple) { + return [...prev, ...fileArray]; + } + return [...fileArray]; + }); + onChange && onChange(fileArray); + }; + const handleDrop = useCallback( + (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (e.dataTransfer.files) { + handleFiles(e.dataTransfer.files); + } + }, + [onChange], + ); + const handleDragEnter = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setFileDrag(true); + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + setFileDrag(false); + }; + const handleClick = () => { + const input = document.createElement('input'); + input.type = 'file'; + input.accept = accept || ''; + input.multiple = multiple; + input.onchange = e => { + const target = e.target as HTMLInputElement; + if (target.files) { + handleFiles(target.files); + } + }; + input.click(); + }; + const clearFileList = () => { + setFileList([]); + }; + const renderFileList = useMemo(() => { + if (fileList.length > 0) { + return ( +
    + {fileList.map(file => { + return ( +
  • + + + {file.name} + + + + +
  • + ); + })} +
+ ); + } + return null; + }, [fileList]); + return ( +
+
+ {uploadIcon || null} +

{uploadLabel}

+
+ {error ? {error} : null} + {renderFileList} +
+ ); +}; + +export default FileUpload; diff --git a/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx b/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx new file mode 100644 index 00000000..7daba2e7 --- /dev/null +++ b/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx @@ -0,0 +1,47 @@ +import { useMemo } from 'react'; +import { type ControllerProps } from 'react-hook-form'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { checkRequired } from '@milesight/shared/src/utils/validators'; +import { UploadFileIcon } from '@milesight/shared/src/components'; +import UploadField from '../components/uploadField'; + +/** + * type of dataSource + */ +export type FormDataProps = { + file: File[]; +}; + +const useImportFormItems = () => { + const { getIntlText } = useI18n(); + const formItems = useMemo(() => { + const result: ControllerProps[] = []; + result.push({ + name: 'file', + rules: { + validate: { + checkRequired: checkRequired(), + }, + }, + render({ field: { onChange, value }, fieldState: { error } }) { + return ( + + } + uploadTips={getIntlText('workflow.modal.upload_tips')} + onChange={onChange} + /> + ); + }, + }); + + return result; + }, [getIntlText]); + + return formItems; +}; + +export default useImportFormItems; diff --git a/apps/web/src/pages/workflow/components/import-modal/index.tsx b/apps/web/src/pages/workflow/components/import-modal/index.tsx index 462e21db..7012a183 100644 --- a/apps/web/src/pages/workflow/components/import-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/import-modal/index.tsx @@ -1 +1,54 @@ -// DSL 导入弹窗组件 +import React from 'react'; +import { useMemoizedFn } from 'ahooks'; +import cls from 'classnames'; +import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal, type ModalProps } from '@milesight/shared/src/components'; +import useImportFormItems, { type FormDataProps } from './hook/useImportFormItems'; + +interface Props extends Omit { + /** upload callback */ + onUpload?: (params: FormDataProps) => Promise | void; +} + +/** + * workflow import modal + */ +const ImportModal: React.FC = ({ visible, onCancel, onUpload, ...props }) => { + const { getIntlText } = useI18n(); + const formItems = useImportFormItems(); + + // ---------- forms processing ---------- + const { control, formState, handleSubmit, reset } = useForm(); + + const onSubmit: SubmitHandler = async ({ ...params }) => { + if (onUpload) { + await onUpload(params); + } + // Clear the form data upon confirmation. + reset(); + }; + + const handleCancel = useMemoizedFn(() => { + reset(); + onCancel && onCancel(); + }); + + return ( + + {formItems.map(props => ( + {...props} key={props.name} control={control} /> + ))} + + ); +}; + +export default ImportModal; diff --git a/apps/web/src/pages/workflow/components/index.ts b/apps/web/src/pages/workflow/components/index.ts index af1b9d71..42379f62 100644 --- a/apps/web/src/pages/workflow/components/index.ts +++ b/apps/web/src/pages/workflow/components/index.ts @@ -1 +1,2 @@ export { default as EditModal } from './edit-modal'; +export { default as ImportModal } from './import-modal'; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 09e2e378..120c2547 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -24,5 +24,6 @@ "workflow.modal.add_workflow_modal": "Add Workflow", "workflow.modal.edit_workflow_modal": "Edit Workflow", "workflow.modal.workflow_name": "Workflow Name", - "workflow.button.delete_filter": "More Filters" + "workflow.button.delete_filter": "More Filters", + "workflow.modal.upload_tips": "Click here or drag file to this area to upload" } diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 874d04e0..3082bdf2 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -86,6 +86,8 @@ export { Hearing as HearingIcon, FactCheck as FactCheckIcon, Loop as LoopIcon, + UploadFile as UploadFileIcon, + AttachFile as AttachFileIcon, } from '@mui/icons-material'; export * from './iot-icons'; From 2766fb861de4804a95ffecf13e3780307011ac8d Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 10 Dec 2024 15:44:06 +0800 Subject: [PATCH 016/172] fix: use the Import the DSL dialog on the workflow list page --- apps/web/src/pages/workflow/index.tsx | 60 +++++++++++++++++---------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 199aaf5c..286ebdb1 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -12,15 +12,16 @@ import { } from '@milesight/shared/src/components'; import { Breadcrumbs, TablePro, useConfirm } from '@/components'; import { deviceAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; -import { type FormDataProps } from '@/pages/workflow/components/edit-modal/hook/useWorkflowFormItems'; -import { EditModal } from '@/pages/workflow/components'; +import { type FormDataProps as EditFormDataProps } from '@/pages/workflow/components/edit-modal/hook/useWorkflowFormItems'; +import { type FormDataProps as ImportFormDataProps } from '@/pages/workflow/components/import-modal/hook/useImportFormItems'; +import { EditModal, ImportModal } from '@/pages/workflow/components'; import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; import './style.less'; -type ModalOption = { +type EditModalOption = { isAdd: boolean; openModal: boolean; - dataSource?: FormDataProps; + dataSource?: EditFormDataProps; }; const Workflow = () => { @@ -29,10 +30,11 @@ const Workflow = () => { // ---------- 列表数据相关逻辑 ---------- const [keyword, setKeyword] = useState(); - const [editOption, SetEditOption] = useState({ + const [editOption, SetEditOption] = useState({ isAdd: false, openModal: false, }); + const [importModal, setImportModal] = useState(false); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); const { @@ -109,7 +111,7 @@ const Workflow = () => { variant="outlined" sx={{ height: 36, textTransform: 'none' }} startIcon={} - // onClick={() => setModalOpen(true)} + onClick={() => handlerImportModal(true)} > {getIntlText('workflow.button.label_import_from_dsl')} @@ -126,8 +128,14 @@ const Workflow = () => { ); }, [getIntlText, handleDeleteConfirm, selectedIds]); - const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: FormDataProps): void => { - const newEditOption: ModalOption = { + const handlerImportModal = (isOpen: boolean, param?: ImportFormDataProps) => { + if (param) { + // TODO: Pass the DSL to the workflow. + } + setImportModal(isOpen); + }; + const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: EditFormDataProps): void => { + const newEditOption: EditModalOption = { isAdd, openModal: isOpen, }; @@ -139,20 +147,23 @@ const Workflow = () => { } SetEditOption(newEditOption); }; - const submitEditModal = async (data: FormDataProps) => { - const { isAdd } = editOption; - // const [error, res] = await awaitWrap(isAdd ? WorkflowAPI.addWorkflow(data) : WorkflowAPI.updateWorkflow(data)); - // if (isRequestSuccess(res)) { - handlerEditModal(false, false); - if (isAdd) { - navigate('/workflow/editor', { state: data }); - } else { - // toast.success(getIntlText('common.message.operation_success')); - } - // } else { - // toast.error(error); - // } - }; + const submitEditModal = useCallback( + async (data: EditFormDataProps) => { + const { isAdd } = editOption; + // const [error, res] = await awaitWrap(isAdd ? WorkflowAPI.addWorkflow(data) : WorkflowAPI.updateWorkflow(data)); + // if (isRequestSuccess(res)) { + handlerEditModal(false, false); + if (isAdd) { + navigate('/workflow/editor', { state: data }); + } else { + // toast.success(getIntlText('common.message.operation_success')); + } + // } else { + // toast.error(error); + // } + }, + [navigate], + ); const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); @@ -210,6 +221,11 @@ const Workflow = () => { onCancel={() => handlerEditModal(false, false)} onConfirm={submitEditModal} /> + handlerImportModal(false, param)} + onCancel={() => handlerImportModal(false)} + />
); }; From c3129f1b026fd39928d2753341ecb53f16b3e692 Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 10 Dec 2024 19:03:41 +0800 Subject: [PATCH 017/172] fix: error use useCallback API --- apps/web/src/pages/workflow/index.tsx | 31 ++++++++++++--------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 286ebdb1..4c5b8409 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -147,23 +147,20 @@ const Workflow = () => { } SetEditOption(newEditOption); }; - const submitEditModal = useCallback( - async (data: EditFormDataProps) => { - const { isAdd } = editOption; - // const [error, res] = await awaitWrap(isAdd ? WorkflowAPI.addWorkflow(data) : WorkflowAPI.updateWorkflow(data)); - // if (isRequestSuccess(res)) { - handlerEditModal(false, false); - if (isAdd) { - navigate('/workflow/editor', { state: data }); - } else { - // toast.success(getIntlText('common.message.operation_success')); - } - // } else { - // toast.error(error); - // } - }, - [navigate], - ); + const submitEditModal = async (data: EditFormDataProps) => { + const { isAdd } = editOption; + // const [error, res] = await awaitWrap(isAdd ? WorkflowAPI.addWorkflow(data) : WorkflowAPI.updateWorkflow(data)); + // if (isRequestSuccess(res)) { + handlerEditModal(false, false); + if (isAdd) { + navigate('/workflow/editor', { state: data }); + } else { + // toast.success(getIntlText('common.message.operation_success')); + } + // } else { + // toast.error(error); + // } + }; const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); From 9653deed5e03193fa2aca1e522ebfc258a937395 Mon Sep 17 00:00:00 2001 From: jimco Date: Tue, 10 Dec 2024 19:25:57 +0800 Subject: [PATCH 018/172] feat: add TestButton, LogPanel components --- .../editor/components/config-panel/index.tsx | 2 +- .../editor/components/log-panel/index.tsx | 137 ++++++++++- .../editor/components/log-panel/style.less | 30 +++ .../editor/components/test-button/index.tsx | 226 ++++++++++++++++-- .../components/test-button/log-list.tsx | 83 +++++++ .../editor/components/test-button/style.less | 79 ++++++ .../views/editor/components/topbar/index.tsx | 2 +- .../src/pages/workflow/views/editor/index.tsx | 16 +- .../pages/workflow/views/editor/log-store.ts | 76 ++++++ apps/web/src/services/http/index.ts | 2 +- apps/web/src/services/http/workflow.ts | 9 +- apps/web/src/styles/index.less | 10 - apps/web/src/styles/mui-polyfill.less | 14 ++ packages/locales/src/lang/en/global.json | 3 +- packages/locales/src/lang/en/workflow.json | 9 +- packages/shared/src/hooks/index.ts | 1 + packages/shared/src/hooks/useVirtualList.ts | 168 +++++++++++++ 17 files changed, 821 insertions(+), 46 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/log-panel/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/log-store.ts create mode 100644 packages/shared/src/hooks/useVirtualList.ts diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 307e018c..bca1970c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -8,7 +8,7 @@ import { type UseOnSelectionChangeOptions, } from '@xyflow/react'; import cls from 'classnames'; -import { Paper, Stack, IconButton } from '@mui/material'; +import { Stack, IconButton } from '@mui/material'; import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; // import { FormControl, InputLabel, Select, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index ac2e6a88..bca62241 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -1,13 +1,144 @@ -import { Panel } from '@xyflow/react'; +import { useState, useMemo, useEffect } from 'react'; +import { Stack, IconButton, Button, TextField, CircularProgress } from '@mui/material'; +import { Panel, useNodes, useReactFlow } from '@xyflow/react'; +import cls from 'classnames'; +import { useRequest } from 'ahooks'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; +import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; +import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import useLogStore from '../../log-store'; +import './style.less'; /** * Log Detail Panel */ const LogPanel = () => { + const { getIntlText } = useI18n(); + const { + openLogPanel, + logPanelMode, + logDetail, + logDetailLoading, + testLogs, + setOpenLogPanel, + setLogDetail, + setLogDetailLoading, + } = useLogStore( + useStoreShallow([ + 'openLogPanel', + 'logPanelMode', + 'logDetail', + 'logDetailLoading', + 'testLogs', + 'setOpenLogPanel', + 'setLogDetail', + 'setLogDetailLoading', + ]), + ); + const nodes = useNodes(); + const { toObject } = useReactFlow(); + const title = useMemo(() => { + switch (logPanelMode) { + case 'testRun': + return getIntlText('workflow.editor.log_panel_title_test_run'); + case 'runLog': + return getIntlText('workflow.editor.log_title_run'); + case 'testLog': + return getIntlText('workflow.editor.log_title_test'); + case 'feVerify': + return getIntlText('workflow.editor.log_panel_title_verification_result'); + default: + return ''; + } + }, [logPanelMode, getIntlText]); + + const handleClose = () => { + // TODO: remove node `$status` prop and close the panel + setOpenLogPanel(false); + }; + + // ---------- Run Test ---------- + const [entryInput, setEntryInput] = useState(''); + const showTestInput = useMemo(() => { + if (logPanelMode !== 'testRun') return false; + const hasTriggerNode = nodes.find(node => node.type === 'trigger'); + + return !!hasTriggerNode; + }, [nodes, logPanelMode]); + const { run: runFlowTest } = useRequest( + async () => { + if (logPanelMode !== 'testRun') return; + setLogDetailLoading(true); + const dsl = toObject(); + // TODO: insert entryInput value into dsl + const [error, resp] = await awaitWrap(workflowAPI.runFlow({ dsl })); + setLogDetailLoading(false); + + if (error || !isRequestSuccess(resp)) return; + const data = getResponseData(resp); + + setLogDetail(data?.trace_infos); + }, + { + manual: true, + debounceWait: 300, + refreshDeps: [logPanelMode, entryInput, toObject], + }, + ); + + // Auto run flow test when there is not trigger node in workflow + useEffect(() => { + if (logPanelMode !== 'testRun' || showTestInput) return; + runFlowTest(); + }, [logPanelMode, showTestInput, runFlowTest]); + return ( - +
- Log Detail +
+ + {title} + + + + + + +
+
+ {showTestInput && ( +
+ {/* TODO: Replace TextField with JSON Editor */} + setEntryInput(e.target.value)} + /> + +
+ )} +
+ Render LogDetail... +
+
); diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less new file mode 100644 index 00000000..ea7b98b9 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less @@ -0,0 +1,30 @@ +.@{prefix}-workflow-panel-log-root.react-flow__panel.right { + z-index: 20; + width: 445px; + height: 100%; + padding: @padding-xs; + margin: 0; + + &.hidden { + display: none; + } + + &:not(.hidden) ~ .@{prefix}-workflow-panel-config-root { + right: 445px - @padding-xs; + } +} + +.@{prefix}-workflow-panel-log { + height: 100%; + background-color: var(--component-background); + border-radius: @border-radius-sm; + box-shadow: @shadow-2; + + .input-area { + ~ .log-detail-area { + padding-top: @padding-xl; + margin-top: @margin-xl; + border-top: 1px solid var(--border-color-base); + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx index 5a8d30d2..d409b61b 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -1,36 +1,226 @@ -import { ButtonGroup, Button, Popover } from '@mui/material'; -import { useI18n } from '@milesight/shared/src/hooks'; +import { useCallback, useMemo, useState } from 'react'; +import { ButtonGroup, Button, Popover, Tabs, Tab, CircularProgress } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { useNodes } from '@xyflow/react'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { PlayArrowIcon, HistoryIcon } from '@milesight/shared/src/components'; +import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { TabPanel } from '@/components'; +import LogList, { type LogType, type LogListProps } from './log-list'; +import useLogStore from '../../log-store'; import './style.less'; -export type ButtonType = 'test' | 'history'; +export type TestButtonType = 'test' | 'history'; + +type LogTabItem = { + key: LogType; + label: string; + component: React.ReactNode; +}; interface Props { - onClick?: (type: ButtonType) => void; + onClick?: () => void | Promise; } +const DEFAULT_TAB_KEY: LogType = 'test'; + /** * Test Button */ -const TestButton: React.FC = ({ onClick }) => { +const TestButton: React.FC = () => { const { getIntlText } = useI18n(); - const handleClick = (type: ButtonType) => { - onClick?.(type); + const nodes = useNodes(); + const { + runLogs, + testLogs, + logDetailLoading, + setRunLogs, + setLogPanelMode, + setOpenLogPanel, + setLogDetail, + setLogDetailLoading, + } = useLogStore( + useStoreShallow([ + 'runLogs', + 'testLogs', + 'logDetailLoading', + 'setRunLogs', + 'setLogPanelMode', + 'setOpenLogPanel', + 'setLogDetail', + 'setLogDetailLoading', + ]), + ); + + const { loading, run: getRunLogList } = useRequest( + async () => { + // const [error, resp] = await awaitWrap(workflowAPI.getLogList()); + // if (error || !isRequestSuccess(resp)) { + // return; + // } + // const data = getResponseData(resp); + + // TODO: get log list + const log = { + id: '1', + start_time: Date.now(), + status: 'success', + }; + const data = new Array(100).fill(0).map((_, index) => ({ + ...log, + id: index + 1, + status: Math.floor(Math.random() * 10) % 2 === 0 ? 'success' : 'error', + })); + + await new Promise(resolve => { + setTimeout(resolve, 1000); + }); + + setRunLogs(data); + }, + { + manual: true, + debounceWait: 300, + }, + ); + + // ---------- Popover ---------- + const [anchorEl, setAnchorEl] = useState(null); + const handleClick = async (e: React.MouseEvent, type: TestButtonType) => { + if (type === 'test') { + setOpenLogPanel(true); + setLogPanelMode('testRun'); + return; + } + + getRunLogList(); + setTabKey(DEFAULT_TAB_KEY); + setAnchorEl(e.currentTarget); }; + const handleLogItemClick = useCallback( + async (type: LogType, record: Parameters>[0]) => { + setAnchorEl(null); + setOpenLogPanel(true); + switch (type) { + case 'test': { + setLogPanelMode('testLog'); + break; + } + case 'run': { + setLogPanelMode('runLog'); + break; + } + default: { + break; + } + } + + // TODO: get log detail + setLogDetailLoading(true); + const [error, resp] = await awaitWrap(workflowAPI.getLogDetail({ id: record.id })); + setLogDetailLoading(false); + + if (error || !isRequestSuccess(resp)) return; + const data = getResponseData(resp); + + setLogDetail(data); + }, + [setLogDetail, setLogDetailLoading, setLogPanelMode, setOpenLogPanel], + ); + + // ---------- Tab ---------- + const [tabKey, setTabKey] = useState(DEFAULT_TAB_KEY); + const tabs = useMemo(() => { + return [ + { + key: 'test', + label: getIntlText('workflow.editor.logs_popover_title_test'), + component: ( + handleLogItemClick('test', record)} + /> + ), + }, + { + key: 'run', + label: getIntlText('workflow.editor.logs_popover_title_run'), + component: ( + handleLogItemClick('run', record)} + /> + ), + }, + ]; + }, [runLogs, testLogs, loading, getIntlText, handleLogItemClick]); return ( - - + + + setAnchorEl(null)} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'right', + }} + transformOrigin={{ + vertical: -8, + horizontal: 'right', + }} > - {getIntlText('common.label.test')} - - - +
+
+ setTabKey(value)} + > + {tabs.map(({ key, label }) => ( + + ))} + +
+
+ {tabs.map(({ key, component }) => ( + + {component} + + ))} +
+
+
+ ); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx new file mode 100644 index 00000000..8dbc9996 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx @@ -0,0 +1,83 @@ +import { useMemo, useRef } from 'react'; +import { useI18n, useTime, useVirtualList } from '@milesight/shared/src/hooks'; +import { ErrorIcon, CheckCircleIcon } from '@milesight/shared/src/components'; +import { type WorkflowAPISchema } from '@/services/http'; +import { Empty } from '@/components'; +import './style.less'; + +export type LogType = 'test' | 'run'; + +export interface LogListProps { + type: LogType; + + data?: WorkflowAPISchema['getLogList']['response']; + + loading?: boolean; + + onSelect?: (record: NonNullable[number]) => void; +} + +const LogList: React.FC = ({ type, data = [], loading, onSelect }) => { + const { getIntlText } = useI18n(); + const { getTimeFormat } = useTime(); + const titlePrefix = useMemo(() => { + switch (type) { + case 'test': + return getIntlText('workflow.editor.log_title_test'); + case 'run': + return getIntlText('workflow.editor.log_title_run'); + default: + return ''; + } + }, [type, getIntlText]); + + // ---------- Virtual List ---------- + const containerRef = useRef(null); + const wrapperRef = useRef(null); + const [virtualList] = useVirtualList(data, { + containerTarget: containerRef, + wrapperTarget: wrapperRef, + itemHeight: 58, + overscan: 10, + }); + + // TODO: Infinite Scroll Loading + + return ( +
+
+ {!virtualList?.length ? ( + + ) : ( + virtualList.map(({ data: record }) => ( +
{ + onSelect?.(record); + }} + > +
+ {record.status === 'success' ? ( + + ) : ( + + )} +
+
+
+ {`${titlePrefix}#${record.id}`} +
+
+ {getTimeFormat(record.start_time)} +
+
+
+ )) + )} +
+
+ ); +}; + +export default LogList; diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/style.less b/apps/web/src/pages/workflow/views/editor/components/test-button/style.less index 28d99f74..d1ac3a18 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/style.less @@ -7,8 +7,87 @@ background-color: var(--component-background-gray); } + &:first-child { + .@{mui-prefix}Button-startIcon { + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + } + } + &:last-child { padding: @padding-xs; } } } + +.@{prefix}-workflow-test-popover { + width: 280px; + border-radius: @border-radius-base; + + &-root { + .@{mui-prefix}Paper-root { + box-shadow: @shadow-2; + } + } + + &-header { + padding: 0 @padding-md; + border-bottom: 1px solid var(--border-color-base); + + .@{mui-prefix}Tabs-root { + margin-bottom: -1px; + } + } +} + +.@{prefix}-workflow-com-log-list { + position: relative; + height: 248px; + overflow: auto; + + .@{prefix}-empty { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + + &-img { + width: 80px; + height: 80px; + } + } + + &-item { + display: flex; + gap: @margin-xxs; + padding: @padding-xs @padding-md; + cursor: pointer; + user-select: none; + + &:hover, + &.selected { + background-color: var(--component-background-gray); + } + + &:active { + opacity: .8; + } + + &:first-child { + margin-top: @margin-xs; + } + + &__title { + font-weight: @font-weight-bold; + .text-size(@font-size-base); + } + + &__time { + color: var(--text-color-secondary); + .text-size(@font-size-sm); + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx index 6a25deae..2755556b 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx @@ -114,7 +114,7 @@ const Topbar: React.FC = ({ {getIntlText('common.label.back')}
- + setOpenEditModal(true)}> diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 09e90f28..38252353 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -117,13 +117,16 @@ const WorkflowEditor = () => { // ---------- Design Mode Change ---------- const [designMode, setDesignMode] = useState('canvas'); - const handleDesignModeChange = useCallback((mode: DesignMode) => { - const data = toObject(); + const handleDesignModeChange = useCallback( + (mode: DesignMode) => { + const data = toObject(); - // TODO: check the workflow json data is valid - console.log('workflow data', data); - setDesignMode(mode); - }, []); + // TODO: check the workflow json data is valid + console.log('workflow data', data); + setDesignMode(mode); + }, + [toObject], + ); // ---------- Save Workflow ---------- const handleSave = () => { @@ -181,6 +184,7 @@ const WorkflowEditor = () => { horizontal={helperLineHorizontal} vertical={helperLineVertical} /> + diff --git a/apps/web/src/pages/workflow/views/editor/log-store.ts b/apps/web/src/pages/workflow/views/editor/log-store.ts new file mode 100644 index 00000000..52260c71 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/log-store.ts @@ -0,0 +1,76 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import { type WorkflowAPISchema } from '@/services/http'; + +interface LogStore { + /** + * Log Panel Mode + * + * @param testRun Render Test Run Panel + * @param testLog Render Test Log Detail + * @param runLog Render Run Log Detail + * @param feVerify Render Frontend Verification Detail + */ + logPanelMode?: 'testRun' | 'testLog' | 'runLog' | 'feVerify'; + + /** + * Open Log Panel + */ + openLogPanel?: boolean; + + /** + * Test Log List + */ + testLogs?: WorkflowAPISchema['getLogList']['response']; + + /** + * Run Log List + */ + runLogs?: WorkflowAPISchema['getLogList']['response']; + + logDetail?: WorkflowAPISchema['getLogDetail']['response']; + + logDetailLoading?: boolean; + + setLogPanelMode: (logPanelMode: LogStore['logPanelMode']) => void; + + setOpenLogPanel: (open: LogStore['openLogPanel']) => void; + + setTestLogs: (testLogs: LogStore['testLogs']) => void; + + setRunLogs: (runLogs: LogStore['runLogs']) => void; + + setLogDetail: (detail?: LogStore['logDetail']) => void; + + setLogDetailLoading: (loading: LogStore['logDetailLoading']) => void; +} + +const useLogStore = create( + immer(set => ({ + testLogs: [ + { + id: '1', + start_time: 1733809691235, + status: 'success', + }, + { + id: '2', + start_time: 1733809691235, + status: 'error', + }, + { + id: '3', + start_time: 1733809691235, + status: 'success', + }, + ], + setLogPanelMode: logPanelMode => set({ logPanelMode }), + setOpenLogPanel: open => set({ openLogPanel: open }), + setTestLogs: testLogs => set({ testLogs }), + setRunLogs: runLogs => set({ runLogs }), + setLogDetail: detail => set({ logDetail: detail }), + setLogDetailLoading: loading => set({ logDetailLoading: loading }), + })), +); + +export default useLogStore; diff --git a/apps/web/src/services/http/index.ts b/apps/web/src/services/http/index.ts index cfaa999f..62fa5928 100644 --- a/apps/web/src/services/http/index.ts +++ b/apps/web/src/services/http/index.ts @@ -6,4 +6,4 @@ export { default as entityAPI, type EntityAPISchema } from './entity'; export { default as integrationAPI, type IntegrationAPISchema } from './integration'; export { default as globalAPI, type GlobalAPISchema } from './global'; export { default as dashboardAPI, type DashboardAPISchema } from './dashboard'; -export { default as workflowAPI, type WorkflowStatus } from './workflow'; +export { default as workflowAPI, type WorkflowStatus, type WorkflowAPISchema } from './workflow'; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 07546b82..e1a7db76 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -99,7 +99,7 @@ export interface WorkflowAPISchema extends APISchema { /** Get workflow log list */ getLogList: { - request: { + request: void | { // TODO: use workflow log status enum status?: string; }; @@ -110,7 +110,7 @@ export interface WorkflowAPISchema extends APISchema { start_time: number; /** Running status */ status: string; - }; + }[]; }; /** Get workflow log detail */ @@ -185,7 +185,8 @@ export interface WorkflowAPISchema extends APISchema { /** Run workflow */ runFlow: { request: { - id: ApiKey; + // TODO: use workflow data type + dsl: any; }; response: { // TODO: use workflow log status enum @@ -194,7 +195,7 @@ export interface WorkflowAPISchema extends APISchema { node_id: ApiKey; node_name: string; status: string; - const: number; + cost: number; input: Record; output: Record; }[]; diff --git a/apps/web/src/styles/index.less b/apps/web/src/styles/index.less index bd38f2d4..5d901d06 100644 --- a/apps/web/src/styles/index.less +++ b/apps/web/src/styles/index.less @@ -83,16 +83,6 @@ body { &:not(.@{mui-prefix}-selected):hover { color: var(--text-color-main); } - - &::after { - display: block; - height: 1px; - margin-bottom: -1px; - overflow: hidden; - font-weight: @font-weight-bold; - visibility: hidden; - content: attr(title); - } } .@{mui-prefix}Tabs-indicator { diff --git a/apps/web/src/styles/mui-polyfill.less b/apps/web/src/styles/mui-polyfill.less index 31826ad5..a867779d 100644 --- a/apps/web/src/styles/mui-polyfill.less +++ b/apps/web/src/styles/mui-polyfill.less @@ -15,6 +15,20 @@ padding-left: 0; margin-right: 32px; text-transform: none; + + &.@{mui-prefix}-selected { + font-weight: @font-weight-bold; + } + + &::after { + display: block; + height: 1px; + margin-bottom: -1px; + overflow: hidden; + font-weight: @font-weight-bold; + visibility: hidden; + content: attr(title); + } } } diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index abd8d5f4..5b5e15a8 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -159,5 +159,6 @@ "common.label.update_time": "Update Time", "common.label.back": "Back", "common.label.create": "Create", - "common.label.test": "Test" + "common.label.test": "Test", + "common.label.run": "Run" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 4ccbdf8d..6b606032 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -25,5 +25,12 @@ "workflow.modal.edit_workflow_modal": "Edit Workflow", "workflow.modal.workflow_name": "Workflow Name", "workflow.label.design_mode_canvas_name": "Canvas Mode", - "workflow.label.design_mode_advanced_name": "Advances Mode" + "workflow.label.design_mode_advanced_name": "Advances Mode", + "workflow.editor.logs_popover_title_test": "Test Logs", + "workflow.editor.logs_popover_title_run": "Run Logs", + "workflow.editor.log_title_test": "Test Log", + "workflow.editor.log_title_run": "Run Log", + "workflow.editor.log_panel_title_test_run": "Test Run", + "workflow.editor.log_panel_title_verification_result": "Verification Result", + "workflow.editor.save_warning_message": "The current workflow has been referenced, and saving it will update the workflow information synchronously. Are you sure you want to save it" } diff --git a/packages/shared/src/hooks/index.ts b/packages/shared/src/hooks/index.ts index 5a08775c..6693c3cf 100644 --- a/packages/shared/src/hooks/index.ts +++ b/packages/shared/src/hooks/index.ts @@ -5,3 +5,4 @@ export { default as useCopy } from './useCopy'; export { default as useTime } from './useTime'; export { default as useEntity } from './useEntity'; export { default as usePreventLeave } from './usePreventLeave'; +export { default as useVirtualList } from './useVirtualList'; diff --git a/packages/shared/src/hooks/useVirtualList.ts b/packages/shared/src/hooks/useVirtualList.ts new file mode 100644 index 00000000..f54ad993 --- /dev/null +++ b/packages/shared/src/hooks/useVirtualList.ts @@ -0,0 +1,168 @@ +import React, { useEffect, useState, useRef, useCallback } from 'react'; +import { isNumber } from 'lodash-es'; +import { useEventListener, useLatest, useMemoizedFn, useSize, useUpdateEffect } from 'ahooks'; +import { getTargetElement, type BasicTarget } from 'ahooks/es/utils/domTarget'; + +type ItemHeight = (index: number, data: T) => number; + +export interface Options { + containerTarget: BasicTarget; + wrapperTarget: BasicTarget; + itemHeight: number | ItemHeight; + overscan?: number; +} + +/** + * Virtual List Hook + * + * Inspired By AHooks useVirtualList + */ +const useVirtualList = (list: T[], options: Options) => { + const { containerTarget, wrapperTarget, itemHeight, overscan = 5 } = options; + + const itemHeightRef = useLatest(itemHeight); + + const size = useSize(containerTarget); + + const scrollTriggerByScrollToFunc = useRef(false); + + const [targetList, setTargetList] = useState<{ index: number; data: T }[]>([]); + + const [wrapperStyle, setWrapperStyle] = useState({}); + + const getVisibleCount = (containerHeight: number, fromIndex: number) => { + if (isNumber(itemHeightRef.current)) { + return Math.ceil(containerHeight / itemHeightRef.current); + } + + let sum = 0; + let endIndex = fromIndex + 1; + // The starting index may only have a partial area within the container viewport, + // so it is calculated from the next index of the starting index + for (let i = fromIndex + 1; i < list.length; i++) { + const height = itemHeightRef.current(i, list[i]); + sum += height; + endIndex = i; + if (sum >= containerHeight) { + break; + } + } + + // The starting index and ending index may only have some areas within the container viewport, + // so the calculation of the number of visible items should be uniformly increased by 1 + return endIndex - fromIndex + 1; + }; + + const getOffset = (scrollTop: number) => { + if (isNumber(itemHeightRef.current)) { + return Math.floor(scrollTop / itemHeightRef.current); + } + let sum = 0; + let offset = 0; + for (let i = 0; i < list.length; i++) { + const height = itemHeightRef.current(i, list[i]); + sum += height; + if (sum >= scrollTop) { + offset = i; + break; + } + } + + return offset; + }; + + // Get hidden sub item height + const getDistanceTop = (index: number) => { + if (isNumber(itemHeightRef.current)) { + const height = index * itemHeightRef.current; + return height; + } + const height = list + .slice(0, index) + .reduce((sum, _, i) => sum + (itemHeightRef.current as ItemHeight)(i, list[i]), 0); + return height; + }; + + const getTotalHeight = useCallback(() => { + if (isNumber(itemHeightRef.current)) { + return list.length * itemHeightRef.current; + } + return list.reduce( + (sum, _, index) => sum + (itemHeightRef.current as ItemHeight)(index, list[index]), + 0, + ); + }, [list]); + + const calculateRange = () => { + const container = getTargetElement(containerTarget); + + if (container) { + const { scrollTop, clientHeight } = container; + + const offset = getOffset(scrollTop); + const visibleCount = getVisibleCount(clientHeight, offset); + + const start = Math.max(0, offset - overscan); + const end = Math.min(list.length, offset + visibleCount + overscan); + + const offsetTop = getDistanceTop(start); + const totalHeight = getTotalHeight(); + + setWrapperStyle({ + height: `${totalHeight - offsetTop}px`, + paddingTop: `${offsetTop}px`, + }); + + setTargetList( + list.slice(start, end).map((ele, index) => ({ + data: ele, + index: index + start, + })), + ); + } + }; + + useUpdateEffect(() => { + const wrapper = getTargetElement(wrapperTarget) as HTMLElement; + if (wrapper) { + // @ts-ignore + // eslint-disable-next-line no-return-assign + Object.keys(wrapperStyle).forEach(key => (wrapper.style[key] = wrapperStyle[key])); + } + }, [wrapperStyle]); + + useEffect(() => { + if (!size?.width || !size?.height) { + return; + } + calculateRange(); + }, [size?.width, size?.height, list]); + + useEventListener( + 'scroll', + e => { + if (scrollTriggerByScrollToFunc.current) { + scrollTriggerByScrollToFunc.current = false; + return; + } + e.preventDefault(); + calculateRange(); + }, + { + target: containerTarget, + }, + ); + + const scrollTo = (index: number) => { + const container = getTargetElement(containerTarget); + if (container) { + scrollTriggerByScrollToFunc.current = true; + container.scrollTop = getDistanceTop(index); + calculateRange(); + } + }; + + return [targetList, useMemoizedFn(scrollTo)] as const; +}; + +export default useVirtualList; From 2e32e62ba05d11b902e9f61c2e20c9b76de2aabe Mon Sep 17 00:00:00 2001 From: Nian Date: Wed, 11 Dec 2024 10:18:08 +0800 Subject: [PATCH 019/172] style: use color variables --- .../import-modal/components/style.less | 33 ++++++++----------- .../import-modal/hook/useImportFormItems.tsx | 2 ++ .../components/import-modal/index.tsx | 2 +- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/apps/web/src/pages/workflow/components/import-modal/components/style.less b/apps/web/src/pages/workflow/components/import-modal/components/style.less index b81a0029..9f79e469 100644 --- a/apps/web/src/pages/workflow/components/import-modal/components/style.less +++ b/apps/web/src/pages/workflow/components/import-modal/components/style.less @@ -7,35 +7,31 @@ align-items: center; align-self: stretch; justify-content: center; - padding: 35px 10px 41px; - background: var(--gray-1); + padding: 35px @padding-sm 41px; + background: var(--component-background-gray); border: 1px dashed var(--border-color-base); - border-radius: 4px; + border-radius: @border-radius-xs; cursor: pointer; } .@{prefix}-import-drag { - border-color: var(--purple-8); + border-color: var(--border-color-active); } .@{prefix}-import-error { - border-color: var(--mui-palette-error-main); + border-color: var(--red-base); } .@{prefix}-import-error-tip { - margin: 4px 14px 0; - font-size: 0.75rem; - font-weight: 400; - color: var(--mui-palette-error-main); + margin: @margin-xxs @margin-sm; + font-size: @font-size-sm; + color: var(--red-base); } .@{prefix}-file-tip { margin: 0; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; - color: var(--purple-8); + font-size: @font-size-sm; + color: var(--border-color-active); } .@{prefix}-file-list { - padding: 0 10px; + padding: 0 @padding-xs; list-style-type: none; border: 1px dashed var(--border-color-base); @@ -43,7 +39,7 @@ display: flex; align-items: center; justify-content: space-between; - padding: 3px; + padding: @padding-xxs; span { display: flex; @@ -52,10 +48,7 @@ .@{prefix}-file-name { padding: 0; margin-top: 0; - font-size: 12px; - font-style: normal; - font-weight: 400; - line-height: normal; + font-size: @font-size-sm; } } } diff --git a/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx b/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx index 7daba2e7..82d335ca 100644 --- a/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx +++ b/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx @@ -26,6 +26,8 @@ const useImportFormItems = () => { render({ field: { onChange, value }, fieldState: { error } }) { return ( = ({ visible, onCancel, onUpload, ...props }) return ( {formItems.map(props => ( From cefed2219b2f7d64b5a1433ad254ede6e9f5deee Mon Sep 17 00:00:00 2001 From: jimco Date: Wed, 11 Dec 2024 10:21:00 +0800 Subject: [PATCH 020/172] fix: use theme color, translate the code note to en --- .../views/editor/components/edge/index.tsx | 2 +- .../editor/components/helper-lines/index.tsx | 7 ++-- .../views/editor/hooks/useInteractions.tsx | 20 ------------ packages/shared/src/hooks/useTheme.ts | 32 +++++++++++-------- 4 files changed, 24 insertions(+), 37 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx index a8071f19..c0502a4e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx @@ -13,7 +13,7 @@ import NodeMenu from '../node-menu'; import './style.less'; /** - * 自定义连线组件 + * AddableEdge */ const AddableEdge = ({ id, diff --git a/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx b/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx index 2a4e2add..88cfc68e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx @@ -1,5 +1,6 @@ import { CSSProperties, useEffect, useRef } from 'react'; import { ReactFlowState, useStore } from '@xyflow/react'; +import { useTheme } from '@milesight/shared/src/hooks'; export * from './utils'; @@ -27,6 +28,8 @@ export type HelperLinesProps = { */ const HelperLinesRenderer = ({ horizontal, vertical }: HelperLinesProps) => { const { width, height, transform } = useStore(storeSelector); + const { purple } = useTheme(); + const lineColor = purple[500]; const canvasRef = useRef(null); @@ -44,7 +47,7 @@ const HelperLinesRenderer = ({ horizontal, vertical }: HelperLinesProps) => { ctx.scale(dpi, dpi); ctx.clearRect(0, 0, width, height); - ctx.strokeStyle = '#0041d0'; + ctx.strokeStyle = lineColor; if (typeof vertical === 'number') { ctx.moveTo(vertical * transform[2] + transform[0], 0); @@ -57,7 +60,7 @@ const HelperLinesRenderer = ({ horizontal, vertical }: HelperLinesProps) => { ctx.lineTo(width, horizontal * transform[2] + transform[1]); ctx.stroke(); } - }, [width, height, transform, horizontal, vertical]); + }, [width, height, transform, lineColor, horizontal, vertical]); return ; }; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index df1ff3d6..c973b28b 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -2,11 +2,7 @@ import { useCallback } from 'react'; import { getIncomers, getOutgoers, - applyNodeChanges, - applyEdgeChanges, useReactFlow, - type OnNodesChange, - type OnEdgesChange, type OnConnect, type ReactFlowProps, } from '@xyflow/react'; @@ -72,20 +68,6 @@ const useInteractions = () => { const { checkNestedParallelLimit } = useWorkflow(); const { width: bodyWidth, height: bodyHeight } = useSize(document.querySelector('body')) || {}; - const handleNodesChange = useCallback>( - changes => { - setNodes(nds => applyNodeChanges(changes, nds)); - }, - [setNodes], - ); - - const handleEdgesChange = useCallback>( - changes => { - setEdges(eds => applyEdgeChanges(changes, eds)); - }, - [setEdges], - ); - // Handle nodes connect const handleConnect = useCallback( connection => { @@ -294,8 +276,6 @@ const useInteractions = () => { return { addNode, - handleNodesChange, - handleEdgesChange, handleConnect, handleBeforeDelete, handleEdgeMouseEnter, diff --git a/packages/shared/src/hooks/useTheme.ts b/packages/shared/src/hooks/useTheme.ts index fc0d615f..494081f6 100644 --- a/packages/shared/src/hooks/useTheme.ts +++ b/packages/shared/src/hooks/useTheme.ts @@ -1,5 +1,5 @@ /** - * 系统主题相关 Hook + * System Theme Hook */ import { useMemo, useCallback, useEffect } from 'react'; import { useColorScheme } from '@mui/material/styles'; @@ -38,22 +38,23 @@ export default () => { [setMode, setTheme], ); - // 主动变更 MUI 主题,否则首次进入时组件库主题默认跟随系统主题 + // Change the MUI theme proactively, otherwise the component library + // theme will follow the system theme by default on first entry. useEffect(() => { changeTheme(theme, false); }, [theme, changeTheme]); return { - /** 当前主题 */ + /** Current Theme */ theme, - /** MUI 主题配置 */ + /** MUI Theme Config */ themeConfig, - /** 切换主题 */ + /** Change Theme */ changeTheme, - /** 根据传入的 CSS 变量名获取对应值 */ + /** Get the value based on the CSS variable name passed in */ getCSSVariableValue: useCallback( vars => { return themeService.getCSSVariableValue(vars); @@ -62,28 +63,31 @@ export default () => { [theme], ), - /** 主题色 - 白 */ + /** Theme Color - white */ white: themeService.white, - /** 主题色 - 黑 */ + /** Theme Color - black */ black: themeService.black, - /** 主题色 - 蓝 */ + /** Theme Color - blue */ blue: themeService.blue, - /** 主题色 - 绿 */ + /** Theme Color - green */ green: themeService.green, - /** 主题色 - 黄 */ + /** Theme Color - yellow */ yellow: themeService.yellow, - /** 主题色 - 橙 */ + /** Theme Color - deepOrange */ deepOrange: themeService.deepOrange, - /** 主题色 - 红 */ + /** Theme Color - red */ red: themeService.red, - /** 主题色 - 灰 */ + /** Theme Color - grey */ grey: themeService.grey, + + /** Theme Color - purple */ + purple: themeService.purple, }; }; From 36339407d754200ffb1d7a11de38c174c8d38324 Mon Sep 17 00:00:00 2001 From: Nian Date: Wed, 11 Dec 2024 16:15:53 +0800 Subject: [PATCH 021/172] fix: after importing the DSL, navigate to the edit page --- apps/web/src/pages/workflow/index.tsx | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 4c5b8409..b6238add 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -128,12 +128,24 @@ const Workflow = () => { ); }, [getIntlText, handleDeleteConfirm, selectedIds]); - const handlerImportModal = (isOpen: boolean, param?: ImportFormDataProps) => { - if (param) { - // TODO: Pass the DSL to the workflow. - } - setImportModal(isOpen); - }; + const handlerImportModal = useCallback( + (isOpen: boolean, param?: ImportFormDataProps) => { + if (param) { + // TODO: Pass the DSL to the workflow. + // valid method to be add + const valid = true; + if (valid) { + navigate('/workflow/editor', { + state: { + file: param?.file?.[0] ?? null, + }, + }); + } + } + setImportModal(isOpen); + }, + [navigate], + ); const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: EditFormDataProps): void => { const newEditOption: EditModalOption = { isAdd, @@ -149,7 +161,7 @@ const Workflow = () => { }; const submitEditModal = async (data: EditFormDataProps) => { const { isAdd } = editOption; - // const [error, res] = await awaitWrap(isAdd ? WorkflowAPI.addWorkflow(data) : WorkflowAPI.updateWorkflow(data)); + // const [error, res] = await awaitWrap(WorkflowAPI.updateWorkflow(data)); // if (isRequestSuccess(res)) { handlerEditModal(false, false); if (isAdd) { From 543ce83f4515f47a95ca8b5fce10e95d16b790d4 Mon Sep 17 00:00:00 2001 From: jimco Date: Wed, 11 Dec 2024 19:29:19 +0800 Subject: [PATCH 022/172] feat: add form components definition --- .../{views/editor/constant.tsx => config.tsx} | 40 ------------------- .../components/conditions-input/index.tsx | 8 ++++ .../components/entity-assign-input/index.tsx | 8 ++++ .../components/entity-select/index.tsx | 8 ++++ .../config-panel/components/index.ts | 7 ++++ .../components/markdown-editor/index.tsx | 8 ++++ .../components/param-input/index.tsx | 8 ++++ .../components/param-select/index.tsx | 8 ++++ .../components/timer-input/index.tsx | 8 ++++ .../editor/components/config-panel/helper.ts | 4 +- .../config-panel/hooks/useCommonFormItems.tsx | 2 +- .../editor/components/config-panel/index.tsx | 10 ++--- .../editor/components/controls/index.tsx | 6 +-- .../editor/components/entry-panel/index.tsx | 2 +- .../editor/components/helper-lines/index.tsx | 2 +- .../editor/components/helper-lines/utils.ts | 5 ++- .../editor/components/ifelse-node/index.tsx | 4 +- .../components/node-container/index.tsx | 16 ++++---- .../editor/components/node-menu/index.tsx | 8 +++- .../views/editor/components/topbar/index.tsx | 2 +- .../pages/workflow/views/editor/constants.ts | 39 ++++++++++++++++++ .../views/editor/hooks/useInteractions.tsx | 2 +- .../views/editor/hooks/useNodeTypes.tsx | 4 +- .../views/editor/hooks/useWorkflow.ts | 2 +- .../workflow/views/editor/hooks/utils.ts | 6 +-- .../src/pages/workflow/views/editor/index.tsx | 4 +- 26 files changed, 143 insertions(+), 78 deletions(-) rename apps/web/src/pages/workflow/{views/editor/constant.tsx => config.tsx} (87%) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/constants.ts diff --git a/apps/web/src/pages/workflow/views/editor/constant.tsx b/apps/web/src/pages/workflow/config.tsx similarity index 87% rename from apps/web/src/pages/workflow/views/editor/constant.tsx rename to apps/web/src/pages/workflow/config.tsx index 866ae07b..f9f940aa 100644 --- a/apps/web/src/pages/workflow/views/editor/constant.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -12,16 +12,6 @@ import { // FlagIcon, } from '@milesight/shared/src/components'; -/** - * Minimum scaling ratio - */ -export const MIN_ZOOM = 0.25; - -/** - * Maximum scaling ratio - */ -export const MAX_ZOOM = 2; - type NodeCategoryConfigItemType = { /** Node Category i18n key */ labelIntlKey: string; @@ -170,33 +160,3 @@ export const basicNodeConfigs: Record = { testable: true, }, }; - -/** - * Parallel nesting layer limit - */ -export const PARALLEL_DEPTH_LIMIT = 3; - -/** - * The addable Edge type - */ -export const EDGE_TYPE_ADDABLE: WorkflowEdgeType = 'addable'; - -/** - * The default node width - */ -export const DEFAULT_NODE_WIDTH = 240; - -/** - * The default node height - */ -export const DEFAULT_NODE_HEIGHT = 50; - -/** - * Node X-axis spacing - */ -export const NODE_SPACING_X = 50; - -/** - * Node Y-axis spacing - */ -export const NODE_SPACING_Y = 50; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx new file mode 100644 index 00000000..6902f157 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -0,0 +1,8 @@ +/** + * Conditions Input Component + * + * Note: use in IfelseNode + */ +const ConditionsInput = () => {}; + +export default ConditionsInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx new file mode 100644 index 00000000..09733ae7 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx @@ -0,0 +1,8 @@ +/** + * Entity Assignment Input Component + * + * Note: use in EntityAssignmentNode + */ +const EntityAssignInput = () => {}; + +export default EntityAssignInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx new file mode 100644 index 00000000..e3da319b --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx @@ -0,0 +1,8 @@ +/** + * Entity Select Component + * + * Note: This is a basic component, use in EntityListeningNode, ServiceNode, EntitySelectNode + */ +const EntitySelect = () => {}; + +export default EntitySelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts new file mode 100644 index 00000000..f68da69b --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -0,0 +1,7 @@ +export { default as ConditionsInput } from './conditions-input'; +export { default as EntityAssignInput } from './entity-assign-input'; +export { default as EntitySelect } from './entity-select'; +export { default as MarkdownEditor } from './markdown-editor'; +export { default as ParamInput } from './param-input'; +export { default as ParamSelect } from './param-select'; +export { default as TimerInput } from './timer-input'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx new file mode 100644 index 00000000..0492728d --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx @@ -0,0 +1,8 @@ +/** + * Markdown Editor Component + * + * Note: Use in EmailNode + */ +const MarkdownEditor = () => {}; + +export default MarkdownEditor; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx new file mode 100644 index 00000000..270dc256 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -0,0 +1,8 @@ +/** + * Param Input Component + * + * Note: use in TriggerNode, CodeNode + */ +const ParamInput = () => {}; + +export default ParamInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx new file mode 100644 index 00000000..54055802 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -0,0 +1,8 @@ +/** + * Param Select Component + * + * Note: This is a basic component., use in CodeNode, ServiceNode, EntityAssignmentNode + */ +const ParamSelect = () => {}; + +export default ParamSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx new file mode 100644 index 00000000..bcf6016a --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx @@ -0,0 +1,8 @@ +/** + * Timer Input Component + * + * Note: use in TimerNode + */ +const TimerInput = () => {}; + +export default TimerInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts index 92fe1daf..c75bc5e8 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts @@ -5,15 +5,13 @@ import { } from '@milesight/shared/src/utils/validators'; /** - * 节点数据校验器配置 + * Node Data Validators Config */ export const validatorsConfig: Record> = { - /** 节点名称 */ name: { checkRequired: checkRequired(), checkRangeLength: checkRangeLength({ min: 1, max: 50 }), }, - /** 节点备注 */ remark: { checkRangeLength: checkRangeLength({ min: 1, max: 1000 }), }, diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx index a4733f18..8a88ef4a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx @@ -5,7 +5,7 @@ import { useI18n } from '@milesight/shared/src/hooks'; import { checkRequired } from '@milesight/shared/src/utils/validators'; /** - * 表单数据类型 + * Form Item Props */ export type CommonFormDataProps = Record; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index bca1970c..9d43d7fb 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -13,17 +13,17 @@ import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; // import { FormControl, InputLabel, Select, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; -import { basicNodeConfigs } from '../../constant'; +import { basicNodeConfigs } from '@/pages/workflow/config'; import { useCommonFormItems, type CommonFormDataProps } from './hooks'; import './style.less'; /** - * 配置面板组件 + * Config Panel */ const ConfigPanel = () => { const { getIntlText } = useI18n(); - // ---------- 节点相关逻辑处理 ---------- + // ---------- Handle Node-related logic ---------- const nodes = useNodes(); const { updateNode } = useReactFlow(); const selectedNode = useMemo(() => { @@ -42,7 +42,7 @@ const ConfigPanel = () => { return basicNodeConfigs[selectedNode.type as WorkflowNodeType]; }, [selectedNode]); - // ---------- 表单相关逻辑处理 ---------- + // ---------- Handle Form-related logic ---------- const { control, formState, handleSubmit, reset } = useForm(); const commonFormItems = useCommonFormItems(); @@ -50,7 +50,7 @@ const ConfigPanel = () => { {nodeConfig?.labelIntlKey && ( diff --git a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx index 0d39d945..7e365198 100644 --- a/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/controls/index.tsx @@ -13,18 +13,18 @@ import './style.less'; export interface ControlsProps { /** - * 最小缩放比例 + * Minimum zoom */ minZoom?: number; /** - * 最大缩放比例 + * Maximum zoom */ maxZoom?: number; } /** - * 工作流编辑器工具栏 + * Workflow Editor Controls */ const Controls: React.FC = ({ minZoom, maxZoom }) => { const nodes = useNodes(); diff --git a/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx index aec3f931..929581f4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/entry-panel/index.tsx @@ -3,7 +3,7 @@ import { Panel, useNodes } from '@xyflow/react'; import cls from 'classnames'; import { Button, CircularProgress } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; -import { basicNodeConfigs } from '../../constant'; +import { basicNodeConfigs } from '@/pages/workflow/config'; import useInteractions from '../../hooks/useInteractions'; import './style.less'; diff --git a/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx b/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx index 88cfc68e..d52d0f44 100644 --- a/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/helper-lines/index.tsx @@ -24,7 +24,7 @@ export type HelperLinesProps = { }; /** - * 辅助线渲染器 + * HelperLines Renderer */ const HelperLinesRenderer = ({ horizontal, vertical }: HelperLinesProps) => { const { width, height, transform } = useStore(storeSelector); diff --git a/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts b/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts index d2d3ec63..5e97f2b1 100644 --- a/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts +++ b/apps/web/src/pages/workflow/views/editor/components/helper-lines/utils.ts @@ -1,4 +1,4 @@ -import { Node, NodePositionChange, XYPosition } from '@xyflow/react'; +import type { NodePositionChange, XYPosition } from '@xyflow/react'; type GetHelperLinesResult = { horizontal?: number; @@ -7,7 +7,8 @@ type GetHelperLinesResult = { }; /** - * 根据当前节点及周围节点位置计算辅助线位置 + * Calculate the position of the helper line based on the + * current node and surrounding node positions */ export function getHelperLines( change: NodePositionChange, diff --git a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx index 3adbd7c0..82d88673 100644 --- a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx @@ -1,16 +1,16 @@ import React from 'react'; import { Position, type Node, type NodeProps } from '@xyflow/react'; import { useI18n } from '@milesight/shared/src/hooks'; +import { basicNodeConfigs } from '@/pages/workflow/config'; import Handle from '../handle'; import NodeContainer from '../node-container'; -import { basicNodeConfigs } from '../../constant'; export type IfElseNode = Node; const nodeConfig = basicNodeConfigs.ifelse; /** - * 输入节点 + * IFELSE Node */ const IfElseNode: React.FC> = props => { const { getIntlText } = useI18n(); diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index 01d9967a..9dcf1338 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -4,43 +4,43 @@ import cls from 'classnames'; import { Menu, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { CheckCircleIcon, ErrorIcon, LoopIcon } from '@milesight/shared/src/components'; +import { basicNodeConfigs } from '@/pages/workflow/config'; import Handle from '../handle'; -import { basicNodeConfigs } from '../../constant'; import './style.less'; export type NodeContainerProps = { /** - * 节点类型 + * Node Type */ type: WorkflowNodeType; /** - * 节点 Title 的国际化文案 Key + * Node title i18n key */ title: string; /** - * 节点 Icon + * Node Icon */ icon: React.ReactNode; /** - * 节点 Icon 背景色 + * Node Icon Background Color */ iconBgColor: string; /** - * 节点操作柄集合,默认会有左右操作柄 + * Node Handles */ handles?: React.ReactNode[]; /** - * 节点所有属性 + * Node Props */ nodeProps: NodeProps; /** - * 节点详情内容 + * Custom Node Children */ children?: React.ReactNode; }; diff --git a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx index ab24c26b..a60bc32c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx @@ -2,7 +2,11 @@ import { useState, useMemo, useLayoutEffect } from 'react'; import { useReactFlow } from '@xyflow/react'; import { Menu, MenuItem, type MenuProps } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; -import { nodeCategoryConfigs, basicNodeConfigs, type NodeConfigItemType } from '../../constant'; +import { + nodeCategoryConfigs, + basicNodeConfigs, + type NodeConfigItemType, +} from '@/pages/workflow/config'; import useInteractions, { type AddNodeClosestPayloadParam } from '../../hooks/useInteractions'; import './style.less'; @@ -14,7 +18,7 @@ interface Props extends MenuProps, AddNodeClosestPayloadParam { } /** - * 节点菜单 + * Node Menu */ const NodeMenu = ({ prevNodeId, diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx index 2755556b..d3fbdda5 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx @@ -40,7 +40,7 @@ interface Props { } /** - * Workflow 头部工具栏组件 + * Workflow Editor Topbar */ const Topbar: React.FC = ({ data, diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts new file mode 100644 index 00000000..a97ccfb0 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -0,0 +1,39 @@ +/** + * Minimum scaling ratio + */ +export const MIN_ZOOM = 0.25; + +/** + * Maximum scaling ratio + */ +export const MAX_ZOOM = 2; + +/** + * Parallel nesting layer limit + */ +export const PARALLEL_DEPTH_LIMIT = 3; + +/** + * The addable Edge type + */ +export const EDGE_TYPE_ADDABLE: WorkflowEdgeType = 'addable'; + +/** + * The default node width + */ +export const DEFAULT_NODE_WIDTH = 240; + +/** + * The default node height + */ +export const DEFAULT_NODE_HEIGHT = 50; + +/** + * Node X-axis spacing + */ +export const NODE_SPACING_X = 50; + +/** + * Node Y-axis spacing + */ +export const NODE_SPACING_Y = 50; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index c973b28b..2822d67a 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -15,7 +15,7 @@ import { DEFAULT_NODE_WIDTH, DEFAULT_NODE_HEIGHT, EDGE_TYPE_ADDABLE, -} from '../constant'; +} from '../constants'; import useWorkflow from './useWorkflow'; type RFProps = ReactFlowProps; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx index 6b2a9c01..79f22ffa 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useNodeTypes.tsx @@ -1,11 +1,11 @@ import React, { useMemo } from 'react'; import { Position, type NodeProps } from '@xyflow/react'; import { useI18n } from '@milesight/shared/src/hooks'; -import { basicNodeConfigs } from '../constant'; +import { basicNodeConfigs } from '@/pages/workflow/config'; import { Handle, IfElseNode, NodeContainer } from '../components'; /** - * 生成所有节点类型 + * Get Node Types */ const useNodeTypes = () => { const { getIntlText } = useI18n(); diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 2954e9f6..62b3ecc8 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -2,7 +2,7 @@ import { useCallback } from 'react'; import { useReactFlow, getOutgoers, type IsValidConnection } from '@xyflow/react'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; -import { PARALLEL_DEPTH_LIMIT } from '../constant'; +import { PARALLEL_DEPTH_LIMIT } from '../constants'; import { getParallelInfo } from './utils'; const useWorkflow = () => { diff --git a/apps/web/src/pages/workflow/views/editor/hooks/utils.ts b/apps/web/src/pages/workflow/views/editor/hooks/utils.ts index ec2c8f35..f02a8fad 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/utils.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/utils.ts @@ -1,6 +1,6 @@ import { isEqual, groupBy } from 'lodash-es'; import { getIncomers, getOutgoers, getConnectedEdges } from '@xyflow/react'; -import { basicNodeConfigs } from '../constant'; +import { basicNodeConfigs } from '@/pages/workflow/config'; type ParallelInfoItem = { parallelNodeId: ApiKey; @@ -110,13 +110,13 @@ export const getParallelInfo = ( const sourceEdgesGroup = groupBy(outgoerConnectedEdges, 'sourceHandle'); const incomers = getIncomers(outgoer, nodes, edges); + // Parallel nodes are not allowed as multiple target nodes if (outgoers.length > 1 && incomers.length > 1) hasAbnormalEdges = true; Object.keys(sourceEdgesGroup).forEach(sourceHandle => { nextHandles.push({ node: outgoer, handle: sourceHandle }); }); - if (!outgoerConnectedEdges.length) - nextHandles.push({ node: outgoer, handle: 'source' }); + if (!outgoerConnectedEdges.length) nextHandles.push({ node: outgoer }); const outgoerKey = outgoer.id; if (!nodeEdgesSet[outgoerKey]) nodeEdgesSet[outgoerKey] = new Set(); diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 38252353..27403132 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -15,7 +15,7 @@ import { Button } from '@mui/material'; import { useI18n, useTheme } from '@milesight/shared/src/hooks'; import { CheckIcon } from '@milesight/shared/src/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; -import { MIN_ZOOM, MAX_ZOOM } from './constant'; +import { MIN_ZOOM, MAX_ZOOM } from './constants'; import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; import { Topbar, @@ -45,7 +45,7 @@ const WorkflowEditor = () => { const { grey } = useTheme(); const { getIntlText } = useI18n(); const nodeTypes = useNodeTypes(); - const { toObject } = useReactFlow(); + const { toObject } = useReactFlow(); const { isValidConnection } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); From 3856ba434eeb416c76ccc3343c57ca6e1a82e06a Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 12 Dec 2024 09:33:56 +0800 Subject: [PATCH 023/172] feat: add a Log Modal Component --- .../src/pages/workflow/components/index.ts | 1 + .../components/log-modal/components/index.tsx | 1 + .../log-modal/components/log-item/index.tsx | 46 +++++++++++ .../log-modal/components/log-item/style.less | 49 ++++++++++++ .../workflow/components/log-modal/index.tsx | 78 ++++++++++++++++++- .../workflow/components/log-modal/style.less | 38 +++++++++ .../workflow/components/log-modal/types.d.ts | 8 ++ apps/web/src/pages/workflow/index.tsx | 6 ++ packages/locales/src/lang/en/workflow.json | 3 +- .../shared/src/components/modal/index.tsx | 4 +- 10 files changed, 230 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/index.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less create mode 100644 apps/web/src/pages/workflow/components/log-modal/style.less create mode 100644 apps/web/src/pages/workflow/components/log-modal/types.d.ts diff --git a/apps/web/src/pages/workflow/components/index.ts b/apps/web/src/pages/workflow/components/index.ts index af1b9d71..41fae63c 100644 --- a/apps/web/src/pages/workflow/components/index.ts +++ b/apps/web/src/pages/workflow/components/index.ts @@ -1 +1,2 @@ export { default as EditModal } from './edit-modal'; +export { default as LogModal } from './log-modal'; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/index.tsx new file mode 100644 index 00000000..890cfb94 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/index.tsx @@ -0,0 +1 @@ +export { default as LogItem } from './log-item'; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx new file mode 100644 index 00000000..d71e26b1 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx @@ -0,0 +1,46 @@ +import React, { useMemo } from 'react'; +import cls from 'classnames'; +import { ErrorIcon, CheckCircleIcon } from '@milesight/shared/src/components'; +import { useTime } from '@milesight/shared/src/hooks'; +import type { LOG_STATUS, LogItemProps } from '../../types'; +import './style.less'; + +export interface IProps { + isActive?: boolean; + data: LogItemProps; + onClick?: (data: LogItemProps) => void; +} +const StatusMap: Record = { + success: { + className: 'ms-log-status__success', + Icon: CheckCircleIcon, + }, + failed: { + className: 'ms-log-status__error', + Icon: ErrorIcon, + }, +}; +export default React.memo(({ data, isActive, onClick }: IProps) => { + const { status, title, timestamp } = data || {}; + const { getTimeFormat } = useTime(); + + const { className: statusClassName, Icon } = useMemo(() => StatusMap[status], [status]); + return ( +
onClick?.(data)} + > +
+ +
+
+

{title}

+

+ {timestamp && getTimeFormat(timestamp, 'fullDateTimeSecondFormat')} +

+
+
+ ); +}); diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less new file mode 100644 index 00000000..a7854f4f --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less @@ -0,0 +1,49 @@ +.ms-log-item { + display: flex; + padding: @padding-xs @padding-md; + cursor: pointer; + + &:hover { + background-color: var(--component-background-gray); + transition: background-color 0.3s; + } + + &--active { + background-color: var(--main-background); + + &:hover { + background-color: var(--main-background); + } + } + + .ms-log-status { + margin-right: @margin-xxs; + + &__success { + color: var(--icon-color-success); + } + + &__error { + color: var(--icon-color-error); + } + } + + .ms-log-content { + p { + margin: 0; + .text-size(@font-size-md); + } + + .ms-log-title { + margin-bottom: @margin-xxs; + font-weight: @font-weight-medium; + .text-size(@font-size-md); + } + + .ms-log-timestamp { + font-weight: @font-weight-regular; + color: var(--text-color-secondary); + .text-size(@font-size-sm); + } + } +} diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx index 762f0ce9..28cc0771 100644 --- a/apps/web/src/pages/workflow/components/log-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -1 +1,77 @@ -// 日志弹窗组件 +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useVirtualList } from 'ahooks'; +import { Modal, type ModalProps } from '@milesight/shared/src/components'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { LogItem } from './components'; +import type { LogItemProps } from './types'; +import './style.less'; + +export type IProps = ModalProps; +export default React.memo(({ visible, ...props }: IProps) => { + const containerRef = useRef(null); + const listRef = useRef(null); + const { getIntlText } = useI18n(); + const [activeItem, setActiveItem] = useState(); + + // TODO: mock data + const list: LogItemProps[] = useMemo(() => { + return Array.from({ length: 1000 }).map(() => ({ + key: Math.random().toString(36).substring(2, 9), + title: Math.random().toString(36).substring(2, 9), + status: Math.random() > 0.5 ? 'success' : 'failed', + timestamp: Date.now(), + })); + }, []); + + /** virtual list */ + const [virtualList, scrollTo] = useVirtualList(list, { + containerTarget: containerRef, + wrapperTarget: listRef, + itemHeight: 62, + overscan: 10, + }); + + /** When initializing, set the first as the default value */ + useEffect(() => { + const [firstItem] = list || []; + + setActiveItem(firstItem); + scrollTo(0); + }, [list]); + + /** handle click left bar */ + const handleClick = useCallback((data: LogItemProps) => { + setActiveItem(data); + }, []); + + return ( + +
+
+
+ {virtualList.map(({ data }) => ( + + ))} +
+
+
+
{activeItem?.title || ''}
+
xxxx
+
+
+
+ ); +}); diff --git a/apps/web/src/pages/workflow/components/log-modal/style.less b/apps/web/src/pages/workflow/components/log-modal/style.less new file mode 100644 index 00000000..12e12da0 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/style.less @@ -0,0 +1,38 @@ +.ms-log-modal { + .MuiPaper-root { + & > .MuiTypography-root { + border-bottom: @border-base; + } + + & > .MuiDialogContent-root { + height: 500px; + padding: 0; + } + } +} + +.ms-log-container { + display: flex; + height: 100%; + + .ms-log-left-bar { + width: 200px; + overflow: auto; + background-color: var(--body-background); + } + + .ms-log-right-bar { + flex: 1; + padding: @padding-lg; + } + + .ms-log-list { + // + } + + .ms-log-title { + margin-bottom: @margin-sm; + font-weight: @font-weight-medium; + .text-size(@font-size-xxl); + } +} diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts new file mode 100644 index 00000000..182c9d48 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -0,0 +1,8 @@ +export type LOG_STATUS = 'success' | 'failed'; + +export interface LogItemProps { + key: string | number; + status: LOG_STATUS; + title: string; + timestamp: number; +} diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index e9b90d70..3bbda276 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -13,6 +13,7 @@ import { import { Breadcrumbs, TablePro, useConfirm } from '@/components'; import { deviceAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; +import { LogModal } from './components'; import './style.less'; const Workflow = () => { @@ -23,6 +24,7 @@ const Workflow = () => { const [keyword, setKeyword] = useState(); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); + const [logModalVisible, setLogModalVisible] = useState(false); const { data: workflowList, loading, @@ -136,6 +138,7 @@ const Workflow = () => { ); const columns = useColumns({ onButtonClick: handleTableBtnClick }); + const handleCloseLogModal = useCallback(() => setLogModalVisible(false), []); return (
@@ -161,6 +164,9 @@ const Workflow = () => { />
+ {logModalVisible && ( + + )}
); }; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index fb83e21e..e197f63f 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -23,5 +23,6 @@ "workflow.modal.entry_node_create_title": "What type of starting Node do you want to create?", "workflow.modal.add_workflow_modal": "Add Workflow", "workflow.modal.edit_workflow_modal": "Edit Workflow", - "workflow.modal.workflow_name": "Workflow Name" + "workflow.modal.workflow_name": "Workflow Name", + "workflow.modal.running_log": "Running Log" } diff --git a/packages/shared/src/components/modal/index.tsx b/packages/shared/src/components/modal/index.tsx index 9446c67b..3a827240 100644 --- a/packages/shared/src/components/modal/index.tsx +++ b/packages/shared/src/components/modal/index.tsx @@ -61,7 +61,7 @@ export interface ModalProps { /** * 确认按钮回调 */ - onOk: () => void; + onOk?: () => void; /** * 取消按钮回调 @@ -133,7 +133,7 @@ const Modal: React.FC = ({ const handleOk = useMemoizedFn(async () => { setLoading(true); - await onOk(); + await onOk?.(); setLoading(false); }); From 52a59c45597b7b3788c3de867994c361f9fb944b Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 12 Dec 2024 17:26:07 +0800 Subject: [PATCH 024/172] feat: Add a Action Log Component --- .../action-log/components/card/index.tsx | 24 +++++ .../action-log/components/card/style.less | 54 ++++++++++++ .../action-log/components/header/index.tsx | 47 ++++++++++ .../action-log/components/header/style.less | 48 ++++++++++ .../action-log/components/index.tsx | 3 + .../action-log/components/tree/index.tsx | 21 +++++ .../action-log/components/tree/style.less | 53 +++++++++++ .../workflow/components/action-log/index.tsx | 88 ++++++++++++++++++- .../workflow/components/action-log/style.less | 6 ++ .../workflow/components/action-log/types.d.ts | 53 +++++++++++ .../log-modal/components/log-item/index.tsx | 32 +++---- .../log-modal/components/log-item/style.less | 3 + .../workflow/components/log-modal/index.tsx | 10 ++- .../workflow/components/log-modal/style.less | 5 +- .../workflow/components/log-modal/types.d.ts | 16 +++- apps/web/src/pages/workflow/config.tsx | 17 ++++ .../shared/src/components/icons/index.tsx | 2 + 17 files changed, 455 insertions(+), 27 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/action-log/components/card/index.tsx create mode 100644 apps/web/src/pages/workflow/components/action-log/components/card/style.less create mode 100644 apps/web/src/pages/workflow/components/action-log/components/header/index.tsx create mode 100644 apps/web/src/pages/workflow/components/action-log/components/header/style.less create mode 100644 apps/web/src/pages/workflow/components/action-log/components/index.tsx create mode 100644 apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx create mode 100644 apps/web/src/pages/workflow/components/action-log/components/tree/style.less create mode 100644 apps/web/src/pages/workflow/components/action-log/style.less create mode 100644 apps/web/src/pages/workflow/components/action-log/types.d.ts diff --git a/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx new file mode 100644 index 00000000..c567a285 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import Accordion from '@mui/material/Accordion'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import { ArrowForwardIosIcon } from '@milesight/shared/src/components'; +import './style.less'; + +interface IProps { + header: React.ReactNode; + children: React.ReactNode; +} +export default React.memo(({ header, children }: IProps) => { + return ( + + } + className="ms-log-accordion__header" + > + {header} + + {children} + + ); +}); diff --git a/apps/web/src/pages/workflow/components/action-log/components/card/style.less b/apps/web/src/pages/workflow/components/action-log/components/card/style.less new file mode 100644 index 00000000..dbabf51e --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/card/style.less @@ -0,0 +1,54 @@ +.ms-log-accordion { + &::before { + display: none; + } + + &.MuiPaper-root { + margin-bottom: @margin-xxs; + border: @border-base; + border-radius: @border-radius-sm; + box-shadow: none; + + &:first-of-type { + border-radius: @border-radius-sm; + } + + &.Mui-expanded { + margin-top: 0; + margin-bottom: @margin-xxs; + } + } + + & &__header.MuiButtonBase-root { + display: flex; + flex-direction: row-reverse; + width: 100%; + min-height: unset; + padding: 0 @padding-sm; + overflow: hidden; + + & > .MuiAccordionSummary { + &-content { + flex: 1; + margin: @padding-sm 0 @padding-sm @margin-xs; + overflow: hidden; + } + + &-expandIconWrapper { + padding: @padding-xxs; + + &.Mui-expanded { + transform: rotate(90deg); + } + + .MuiSvgIcon-root { + font-size: @font-size-sm; + } + } + } + } + + & &__content { + padding-top: 0; + } +} diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx new file mode 100644 index 00000000..832717a6 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx @@ -0,0 +1,47 @@ +import React, { useMemo } from 'react'; +import cls from 'classnames'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Tooltip } from '@/components'; +import { basicNodeConfigs, LogStatusMap } from '@/pages/workflow/config'; +import type { AccordionLog } from '../../types'; +import './style.less'; + +interface IProps { + data: AccordionLog; +} +export default React.memo(({ data }: IProps) => { + const { getIntlText } = useI18n(); + + /** Get the header render config */ + const { icon, iconBgColor, labelIntlKey, status } = useMemo(() => { + const { type, config, status } = data || {}; + const { icon, iconBgColor, labelIntlKey } = config || {}; + const result = basicNodeConfigs[type]; + + return { + status, + icon: icon || result?.icon, + iconBgColor: iconBgColor || result?.iconBgColor, + labelIntlKey: labelIntlKey || result?.labelIntlKey, + }; + }, [data]); + + /** Get this state render config */ + const { className: statusClassName, icon: statusIcon } = useMemo( + () => LogStatusMap[status], + [status], + ); + return ( +
+
+
+ {icon} +
+
+ +
+
+
{statusIcon}
+
+ ); +}); diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/style.less b/apps/web/src/pages/workflow/components/action-log/components/header/style.less new file mode 100644 index 00000000..a6da62e9 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/header/style.less @@ -0,0 +1,48 @@ +.ms-accordion-header { + display: flex; + justify-content: space-between; + width: 100%; + + .ms-header-type { + display: flex; + flex: 1; + overflow: hidden; + + &__icon { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-right: @margin-xs; + color: var(--white); + border-radius: @border-radius-xs; + + .MuiSvgIcon-root { + font-size: @font-size-md; + } + } + + &__name { + flex: 1; + overflow: hidden; + font-weight: @font-weight-medium; + } + } + + .ms-header-status { + display: flex; + align-items: center; + margin-left: @margin-xxs; + } + + .ms-log-status { + &__success { + color: var(--icon-color-success); + } + + &__error { + color: var(--icon-color-error); + } + } +} diff --git a/apps/web/src/pages/workflow/components/action-log/components/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/index.tsx new file mode 100644 index 00000000..5f592928 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/index.tsx @@ -0,0 +1,3 @@ +export { default as AccordionHeader } from './header'; +export { default as AccordionCard } from './card'; +export { default as AccordionTree } from './tree'; diff --git a/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx new file mode 100644 index 00000000..3e1375d0 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx @@ -0,0 +1,21 @@ +import React from 'react'; +import Accordion from '@mui/material/Accordion'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import { ArrowRightIcon } from '@milesight/shared/src/components'; +import './style.less'; + +interface IProps { + header: React.ReactNode; + children: React.ReactNode; +} +export default React.memo(({ header, children }: IProps) => { + return ( + + } className="ms-log-tree__header"> + {header} + + {children} + + ); +}); diff --git a/apps/web/src/pages/workflow/components/action-log/components/tree/style.less b/apps/web/src/pages/workflow/components/action-log/components/tree/style.less new file mode 100644 index 00000000..bfaaec1e --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/tree/style.less @@ -0,0 +1,53 @@ +.ms-log-tree { + &::before { + display: none; + } + + &.MuiPaper-root { + box-shadow: none; + + &.Mui-expanded { + margin-top: 0; + } + } + + & &__header.MuiButtonBase-root { + flex-direction: row-reverse; + min-height: unset; + padding: calc(0 * var(--mui-spacing)) calc(2 * var(--mui-spacing) - 4px); + + & > .MuiAccordionSummary { + &-content { + margin: 0 0 0 @margin-xxs; + font-weight: @font-weight-medium; + .text-size(@font-size-sm); + } + + &-expandIconWrapper { + &.Mui-expanded { + transform: rotate(90deg); + } + + .MuiSvgIcon-root { + font-size: @font-size-xxl; + } + } + } + } + + & &__content { + position: relative; + padding: 0 0 0 calc(2 * var(--mui-spacing) + 20px); + margin-top: 2px; + + &::before { + position: absolute; + top: 0; + left: calc((2 * var(--mui-spacing)) + 6px); + width: 1px; + height: 100%; + background: var(--border-color-base); + content: ''; + } + } +} diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index fb7c0970..45c133ce 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1 +1,87 @@ -// 运行日志组件 +import { Fragment } from 'react/jsx-runtime'; +import { AccordionCard, AccordionHeader, AccordionTree } from './components'; +import type { AccordionLog } from './types'; +import './style.less'; + +// TODO mock data +const treeData: AccordionLog[] = [ + { + key: '1', + type: 'trigger', + status: 'success', + input: 'xxxx', + output: 'yyyyyy', + }, + { + key: '2', + type: 'code', + status: 'failed', + input: 'xxxx', + output: 'yyyyyy', + children: [ + { + key: '2-1', + type: 'service', + status: 'failed', + input: 'gsagsagas', + output: 'yyyyyy', + children: [ + { + key: '2-1-1', + type: 'service', + status: 'failed', + input: 'gsagsagas', + output: 'yyyyyy', + }, + { + key: '2-1-2', + type: 'timer', + status: 'failed', + input: '4444', + output: 'yyyyyy', + }, + ], + }, + { + key: '2-2', + type: 'timer', + status: 'failed', + input: '4444', + output: 'yyyyyy', + }, + ], + }, +]; + +export default function AccordionUsage() { + /** recursive rendering */ + const renderAccordion = (data: AccordionLog) => { + const { children, ...item } = data || {}; + + return ( + <> + }> +
{item.input}
+
+ {children && ( + + {children.map(child => ( + +
xxxx
+ {renderAccordion(child)} +
+ ))} +
+ )} + + ); + }; + + return ( +
+ {treeData.map(item => ( + {renderAccordion(item)} + ))} +
+ ); +} diff --git a/apps/web/src/pages/workflow/components/action-log/style.less b/apps/web/src/pages/workflow/components/action-log/style.less new file mode 100644 index 00000000..e3e673cd --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/style.less @@ -0,0 +1,6 @@ +.ms-action-log { + .ms-log-branch { + color: var(--text-color-secondary); + .text-size(@font-size-sm); + } +} diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts new file mode 100644 index 00000000..a61da600 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -0,0 +1,53 @@ +import type { LogStatus } from '../../config'; + +/** + * Action Log Type + */ +export interface AccordionLog { + /** + * Key for Component render + */ + key: string | number; + /** + * Node Type + */ + type: WorkflowNodeType; + /** + * Custom header render config + */ + config?: CustomConfigItemType; + /** + * Node status + */ + status: LogStatus; + /** + * Input + */ + input: string; + /** + * Output + */ + output: string; + /** + * Children + */ + children?: AccordionLog[]; +} + +/** + * Custom header render config + */ +export type CustomConfigItemType = { + /** + * Label i18n key + */ + labelIntlKey?: string; + /** + * Node Icon + */ + icon?: React.ReactNode; + /** + * Node Icon background color + */ + iconBgColor?: string; +}; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx index d71e26b1..26b103a2 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx @@ -1,8 +1,9 @@ import React, { useMemo } from 'react'; import cls from 'classnames'; -import { ErrorIcon, CheckCircleIcon } from '@milesight/shared/src/components'; import { useTime } from '@milesight/shared/src/hooks'; -import type { LOG_STATUS, LogItemProps } from '../../types'; +import { Tooltip } from '@/components'; +import { LogStatusMap } from '@/pages/workflow/config'; +import type { LogItemProps } from '../../types'; import './style.less'; export interface IProps { @@ -10,21 +11,11 @@ export interface IProps { data: LogItemProps; onClick?: (data: LogItemProps) => void; } -const StatusMap: Record = { - success: { - className: 'ms-log-status__success', - Icon: CheckCircleIcon, - }, - failed: { - className: 'ms-log-status__error', - Icon: ErrorIcon, - }, -}; export default React.memo(({ data, isActive, onClick }: IProps) => { const { status, title, timestamp } = data || {}; const { getTimeFormat } = useTime(); - const { className: statusClassName, Icon } = useMemo(() => StatusMap[status], [status]); + const { className: statusClassName, icon } = useMemo(() => LogStatusMap[status], [status]); return (
{ })} onClick={() => onClick?.(data)} > -
- -
+
{icon}
-

{title}

+

+ +

- {timestamp && getTimeFormat(timestamp, 'fullDateTimeSecondFormat')} +

diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less index a7854f4f..acd69084 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less @@ -29,6 +29,9 @@ } .ms-log-content { + flex: 1; + overflow: hidden; + p { margin: 0; .text-size(@font-size-md); diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx index 28cc0771..3017e06b 100644 --- a/apps/web/src/pages/workflow/components/log-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -2,7 +2,9 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useVirtualList } from 'ahooks'; import { Modal, type ModalProps } from '@milesight/shared/src/components'; import { useI18n } from '@milesight/shared/src/hooks'; +import { Tooltip } from '@/components'; import { LogItem } from './components'; +import ActionLog from '../action-log'; import type { LogItemProps } from './types'; import './style.less'; @@ -68,8 +70,12 @@ export default React.memo(({ visible, ...props }: IProps) => {
-
{activeItem?.title || ''}
-
xxxx
+
+ +
+
+ +
diff --git a/apps/web/src/pages/workflow/components/log-modal/style.less b/apps/web/src/pages/workflow/components/log-modal/style.less index 12e12da0..66ad09b5 100644 --- a/apps/web/src/pages/workflow/components/log-modal/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/style.less @@ -24,10 +24,7 @@ .ms-log-right-bar { flex: 1; padding: @padding-lg; - } - - .ms-log-list { - // + overflow-y: auto; } .ms-log-title { diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts index 182c9d48..193498d1 100644 --- a/apps/web/src/pages/workflow/components/log-modal/types.d.ts +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -1,8 +1,20 @@ -export type LOG_STATUS = 'success' | 'failed'; +import type { LogStatus } from '../../config'; export interface LogItemProps { + /** + * Key for Component render + */ key: string | number; - status: LOG_STATUS; + /** + * Node status + */ + status: LogStatus; + /** + * Title + */ title: string; + /** + * Timestamp + */ timestamp: number; } diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index f9f940aa..f0f2033c 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -9,6 +9,8 @@ import { InputIcon, CallSplitIcon, FactCheckIcon, + CheckCircleIcon, + ErrorIcon, // FlagIcon, } from '@milesight/shared/src/components'; @@ -160,3 +162,18 @@ export const basicNodeConfigs: Record = { testable: true, }, }; + +export type LogStatus = 'success' | 'failed'; +/** + * Status Render Map + */ +export const LogStatusMap: Record = { + success: { + className: 'ms-log-status__success', + icon: , + }, + failed: { + className: 'ms-log-status__error', + icon: , + }, +}; diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 63749587..2a7df694 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -89,6 +89,8 @@ export { History as HistoryIcon, UploadFile as UploadFileIcon, AttachFile as AttachFileIcon, + ArrowForwardIos as ArrowForwardIosIcon, + ArrowRight as ArrowRightIcon, } from '@mui/icons-material'; export * from './iot-icons'; From 310d2dc94c21cf33a2ce3d695f9d4c07b117b67f Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 12 Dec 2024 19:21:59 +0800 Subject: [PATCH 025/172] feat: add EntitySelect, EntityFilterSelect components --- .../components/entity-filter-select/index.tsx | 133 ++++++++++ .../entity-filter-select/style.less | 32 +++ .../components/entity-select/index.tsx | 235 +++++++++++++++++- .../components/entity-select/style.less | 21 ++ .../config-panel/components/index.ts | 3 +- .../config-panel/hooks/useNodeFormItems.tsx | 26 ++ .../editor/components/config-panel/style.less | 13 + apps/web/src/services/http/entity.ts | 2 +- packages/locales/src/lang/en/global.json | 3 +- 9 files changed, 463 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx new file mode 100644 index 00000000..3c12f767 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx @@ -0,0 +1,133 @@ +import { useLayoutEffect } from 'react'; +import { + Select, + Button, + IconButton, + FormControl, + InputLabel, + MenuItem, + type SelectProps, +} from '@mui/material'; +import { useDynamicList, useControllableValue } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; +import EntitySelect, { type EntitySelectProps, type EntitySelectValueType } from '../entity-select'; +import './style.less'; + +export type EntityFilterSelectValueType = EntitySelectValueType & { + type?: EntityType; +}; + +export interface EntityFilterSelectProps { + required?: boolean; + disabled?: boolean; + multiple?: boolean; + error?: boolean; + helperText?: React.ReactNode; + value?: EntityFilterSelectValueType[]; + onChange?: (value: EntityFilterSelectValueType[]) => void; + typeSelectProps?: SelectProps; + entitySelectProps?: EntitySelectProps; +} + +const DEFAULT_EMPTY_VALUE: EntityFilterSelectValueType = { + label: '', + value: '', +}; + +const MAX_VALUE_LENGTH = 10; + +const entityTypes: EntityType[] = ['PROPERTY', 'EVENT', 'SERVICE']; + +/** + * Entity Filter Select Component + */ +const EntityFilterSelect: React.FC = ({ + required, + disabled, + error, + helperText, + typeSelectProps, + entitySelectProps, + ...props +}) => { + const { getIntlText } = useI18n(); + const [innerValue, setInnerValue] = useControllableValue(props, { + defaultValue: [DEFAULT_EMPTY_VALUE], + }); + const { list, remove, getKey, insert, replace } = + useDynamicList(innerValue); + + useLayoutEffect(() => { + setInnerValue?.(list); + }, [list, setInnerValue]); + + return ( +
+ {list.map((item, index) => ( +
+ + + {typeSelectProps?.label || getIntlText('common.label.type')} + + + notched + labelId="entity-filter-select-type-label" + label={typeSelectProps?.label || getIntlText('common.label.type')} + value={item.type || ''} + onChange={e => + replace(index, { ...item, type: e.target.value as EntityType }) + } + > + {entityTypes.map(type => ( + + {type} + + ))} + + + { + if (!data) { + replace(index, DEFAULT_EMPTY_VALUE); + return; + } + + replace(index, { + ...item, + ...data, + }); + }} + /> + {list.length > 1 && ( + remove(index)}> + + + )} +
+ ))} + +
+ ); +}; + +export default EntityFilterSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/style.less new file mode 100644 index 00000000..9a30a833 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/style.less @@ -0,0 +1,32 @@ +.@{prefix}-entity-filter-select { + &-item { + display: flex; + gap: @margin-xs; + margin-bottom: @margin-md; + + .@{mui-prefix}FormControl-root { + margin: 0; + } + + > .@{mui-prefix}FormControl-root, + > .@{mui-prefix}Autocomplete-root { + flex: 1; + } + + > .@{mui-prefix}ButtonBase-root { + width: 40px; + height: 40px; + } + } + + & &-add-btn { + font-weight: @font-weight-bold; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx index e3da319b..4d93fd46 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx @@ -1,8 +1,239 @@ +import React, { useCallback, useMemo, useRef, forwardRef } from 'react'; +import { useRequest } from 'ahooks'; +import { + Autocomplete, + TextField, + MenuItem, + type AutocompleteProps, + type AutocompleteRenderInputParams, +} from '@mui/material'; +import { useI18n, useVirtualList } from '@milesight/shared/src/hooks'; +import { Tooltip } from '@/components'; +import { + entityAPI, + awaitWrap, + getResponseData, + isRequestSuccess, + type EntityAPISchema, +} from '@/services/http'; +import './style.less'; + +export type EntitySelectValueType = { + /** Entity Name */ + label: string; + /** Entity ID */ + value: ApiKey; + /** Entity Value Type */ + valueType?: string; + /** Custom Description */ + description?: string; + rawData?: Omit< + EntityAPISchema['getList']['response']['content'][number], + 'entity_value_attribute' + > & { + entity_value_attribute: EntityValueAttributeType; + }; +}; + +export interface EntitySelectProps< + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, + FreeSolo extends boolean | undefined = false, +> extends Omit< + AutocompleteProps, + 'loading' | 'options' | 'renderInput' + > { + label?: string; + + required?: boolean; + + /** + * API Filter Model + */ + filterModel?: { + /** Search Keyword */ + keyword?: string; + /** Entity Type */ + type?: EntityType | EntityType[]; + /** Entity Value Type */ + valueType?: EntityValueDataType | EntityValueDataType[]; + /** Entity Access Mode */ + accessMode?: EntityAccessMode | EntityAccessMode[]; + /** Exclude Children */ + excludeChildren?: boolean; + }; +} + +/** + * Virtual List Component + */ +const Listbox = forwardRef>( + ({ children, ...props }, ref) => { + const containerRef = useRef(null); + const wrapperRef = useRef(null); + const list = useMemo(() => { + const result: React.ReactElement[] = []; + (children as React.ReactElement[]).forEach( + ( + item: React.ReactElement & { + children?: React.ReactElement[]; + }, + ) => { + result.push(item); + result.push(...(item.children || [])); + }, + ); + + return result; + }, [children]); + const [virtualList] = useVirtualList(list, { + containerTarget: containerRef, + wrapperTarget: wrapperRef, + itemHeight: 58, + overscan: 10, + }); + + return ( +
+
+
+ {virtualList.map(({ data }) => data)} +
+
+
+ ); + }, +); + /** * Entity Select Component * * Note: This is a basic component, use in EntityListeningNode, ServiceNode, EntitySelectNode */ -const EntitySelect = () => {}; +const EntitySelect: React.FC = ({ label, required, filterModel, ...props }) => { + const { getIntlText } = useI18n(); + const { + loading, + data: entityList, + run: getEntityList, + } = useRequest( + async (keyword?: string) => { + const entityType = + filterModel?.type && + (Array.isArray(filterModel.type) ? filterModel.type : [filterModel.type]); + const valueType = + filterModel?.valueType && + (Array.isArray(filterModel.valueType) + ? filterModel.valueType + : [filterModel.valueType]); + const accessMode = + filterModel?.accessMode && + (Array.isArray(filterModel.accessMode) + ? filterModel.accessMode + : [filterModel.accessMode]); + const [error, resp] = await awaitWrap( + entityAPI.getList({ + keyword, + entity_type: entityType, + entity_value_type: valueType, + entity_access_mod: accessMode, + exclude_children: filterModel?.excludeChildren, + page_number: 1, + page_size: 999, + }), + ); + + if (error || !isRequestSuccess(resp)) return; + const data = getResponseData(resp)!; + return data?.content || []; + }, + { + manual: true, + debounceWait: 300, + refreshDeps: [filterModel], + }, + ); + const options = useMemo(() => { + if (!entityList?.length) return []; + const result: EntitySelectValueType[] = entityList.map(item => { + const entityValueAttribute = (() => { + try { + return JSON.parse(item.entity_value_attribute); + } catch (e) { + return item.entity_value_attribute; + } + })(); + return { + label: item.entity_name, + value: item.entity_id, + valueType: item.entity_value_type, + description: [item.device_name, item.integration_name].filter(Boolean).join(', '), + rawData: { + ...item, + entity_value_attribute: entityValueAttribute as EntityValueAttributeType, + }, + }; + }); + + return result; + }, [entityList]); + + const renderInput = useCallback( + (params: AutocompleteRenderInputParams) => { + return ( + + ); + }, + [label, required, getIntlText], + ); + + const renderOption = useCallback>( + (optionProps, option) => { + const { label, value, description } = option || {}; + + return ( + +
+
+ +
+
+ +
+
+
+ ); + }, + [], + ); + + return ( + x} + isOptionEqualToValue={(option, currentVal) => option.value === currentVal.value} + slotProps={{ + listbox: { component: Listbox }, + }} + onInputChange={(_, keyword, reason) => { + if (reason !== 'input') { + getEntityList(); + return; + } + + getEntityList(keyword); + }} + onOpen={() => getEntityList()} + /> + ); +}; -export default EntitySelect; +export default React.memo(EntitySelect); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/style.less new file mode 100644 index 00000000..ecd46695 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/style.less @@ -0,0 +1,21 @@ +.@{prefix}-entity-select { + &-options-container { + height: 300px; + overflow: auto; + } + + &-item { + flex: 1; + width: 0; + height: 46px; + + &__label { + .text-size(@font-size-lg); + } + + &__description { + color: var(--text-color-tertiary); + .text-size(@font-size-md); + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index f68da69b..c19a8918 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -1,7 +1,8 @@ export { default as ConditionsInput } from './conditions-input'; export { default as EntityAssignInput } from './entity-assign-input'; -export { default as EntitySelect } from './entity-select'; +export { default as EntitySelect, type EntitySelectProps } from './entity-select'; export { default as MarkdownEditor } from './markdown-editor'; export { default as ParamInput } from './param-input'; export { default as ParamSelect } from './param-select'; export { default as TimerInput } from './timer-input'; +export { default as EntityFilterSelect } from './entity-filter-select'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx new file mode 100644 index 00000000..e7ae1c08 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -0,0 +1,26 @@ +import { useMemo } from 'react'; +import { type ControllerProps } from 'react-hook-form'; +import { TextField } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { checkRequired } from '@milesight/shared/src/utils/validators'; + +/** + * Form Item Props + */ +export type NodeFormDataProps = Record; + +const useNodeFormItems = (node: WorkflowNode) => { + const { getIntlText } = useI18n(); + + const formItems = useMemo(() => { + const result: Partial[]>> = { + trigger: [], + }; + + return result[node.type!]; + }, [node]); + + return formItems; +}; + +export default useNodeFormItems; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less index 4e5a4142..fbaac891 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less @@ -11,6 +11,8 @@ } .@{prefix}-workflow-panel-config { + display: flex; + flex-direction: column; height: 100%; background-color: var(--component-background); border-radius: @border-radius-sm; @@ -41,6 +43,17 @@ } &-body { + flex: 1; padding: 0 @padding-xl @padding-lg; + + .@{prefix}-common-form-items { + .@{mui-prefix}FormControl-root:last-child { + margin-bottom: 0; + } + } + + .@{prefix}-divider { + margin: @margin-xl 0; + } } } diff --git a/apps/web/src/services/http/entity.ts b/apps/web/src/services/http/entity.ts index c770c605..17d976ab 100644 --- a/apps/web/src/services/http/entity.ts +++ b/apps/web/src/services/http/entity.ts @@ -7,7 +7,7 @@ export interface EntityAPISchema extends APISchema { /** 搜索关键字 */ keyword?: string; /** 实体类型 */ - entity_type?: EntitySchema['type']; + entity_type?: EntitySchema['type'][]; /** 实体值类型 */ entity_value_type?: EntityValueDataType[]; /** 实体属性(可读、可写、只读) */ diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 5b5e15a8..51f75046 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -160,5 +160,6 @@ "common.label.back": "Back", "common.label.create": "Create", "common.label.test": "Test", - "common.label.run": "Run" + "common.label.run": "Run", + "common.label.target": "Target" } From 7aa43973321ba4cb7c236e3d9fe906c796418f43 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 12 Dec 2024 19:32:23 +0800 Subject: [PATCH 026/172] fix: add multiple prop for EntityFilterSelect component --- .../components/entity-filter-select/index.tsx | 34 +++++++++++-------- .../config-panel/components/index.ts | 11 ++++-- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx index 3c12f767..4e2ef5f2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx @@ -45,6 +45,7 @@ const entityTypes: EntityType[] = ['PROPERTY', 'EVENT', 'SERVICE']; const EntityFilterSelect: React.FC = ({ required, disabled, + multiple = true, error, helperText, typeSelectProps, @@ -55,8 +56,9 @@ const EntityFilterSelect: React.FC = ({ const [innerValue, setInnerValue] = useControllableValue(props, { defaultValue: [DEFAULT_EMPTY_VALUE], }); - const { list, remove, getKey, insert, replace } = - useDynamicList(innerValue); + const { list, remove, getKey, insert, replace } = useDynamicList( + innerValue || [DEFAULT_EMPTY_VALUE], + ); useLayoutEffect(() => { setInnerValue?.(list); @@ -113,19 +115,21 @@ const EntityFilterSelect: React.FC = ({ )} ))} - + {multiple && ( + + )} ); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index c19a8918..70c46b95 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -1,8 +1,15 @@ export { default as ConditionsInput } from './conditions-input'; export { default as EntityAssignInput } from './entity-assign-input'; -export { default as EntitySelect, type EntitySelectProps } from './entity-select'; +export { + default as EntitySelect, + type EntitySelectProps, + type EntitySelectValueType, +} from './entity-select'; export { default as MarkdownEditor } from './markdown-editor'; export { default as ParamInput } from './param-input'; export { default as ParamSelect } from './param-select'; export { default as TimerInput } from './timer-input'; -export { default as EntityFilterSelect } from './entity-filter-select'; +export { + default as EntityFilterSelect, + type EntityFilterSelectValueType, +} from './entity-filter-select'; From 6f5c9b0ba2ae956b6832bc9515ae6742c1f4f0cb Mon Sep 17 00:00:00 2001 From: Nian Date: Thu, 12 Dec 2024 10:52:34 +0800 Subject: [PATCH 027/172] fix: adjust the API interaction for the edit workflow modal --- apps/web/src/pages/workflow/index.tsx | 48 +++++++++++++++++++-------- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index b6238add..406bfac5 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -11,17 +11,26 @@ import { toast, } from '@milesight/shared/src/components'; import { Breadcrumbs, TablePro, useConfirm } from '@/components'; -import { deviceAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { + deviceAPI, + awaitWrap, + getResponseData, + isRequestSuccess, + workflowAPI, +} from '@/services/http'; import { type FormDataProps as EditFormDataProps } from '@/pages/workflow/components/edit-modal/hook/useWorkflowFormItems'; import { type FormDataProps as ImportFormDataProps } from '@/pages/workflow/components/import-modal/hook/useImportFormItems'; import { EditModal, ImportModal } from '@/pages/workflow/components'; import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; import './style.less'; +type EditRowData = EditFormDataProps & { + id: ApiKey; +}; type EditModalOption = { isAdd: boolean; openModal: boolean; - dataSource?: EditFormDataProps; + dataSource?: EditRowData; }; const Workflow = () => { @@ -146,32 +155,45 @@ const Workflow = () => { }, [navigate], ); - const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: EditFormDataProps): void => { + const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: EditRowData): void => { const newEditOption: EditModalOption = { isAdd, openModal: isOpen, }; - if (!isAdd && isOpen) { + if (row && isOpen) { newEditOption.dataSource = { - name: row?.name ?? '', + id: row.id, + name: row.name, remark: row?.remark ?? '', }; } SetEditOption(newEditOption); }; const submitEditModal = async (data: EditFormDataProps) => { - const { isAdd } = editOption; - // const [error, res] = await awaitWrap(WorkflowAPI.updateWorkflow(data)); - // if (isRequestSuccess(res)) { - handlerEditModal(false, false); + const { isAdd, dataSource } = editOption; if (isAdd) { navigate('/workflow/editor', { state: data }); } else { - // toast.success(getIntlText('common.message.operation_success')); + let errorMsg = ''; + if (dataSource) { + const [error, res] = await awaitWrap( + workflowAPI.updateFlow({ + id: dataSource.id, + name: data.name, + remark: data?.remark ?? '', + }), + ); + if (isRequestSuccess(res)) { + handlerEditModal(false, false); + toast.success(getIntlText('common.message.operation_success')); + return; + } + errorMsg = error?.message ?? getIntlText('common.message.something_wrong_title'); + } + if (errorMsg) { + toast.error(errorMsg); + } } - // } else { - // toast.error(error); - // } }; const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { From ee041277c7fe77bf63540f2fead5bd0f7682124d Mon Sep 17 00:00:00 2001 From: Nian Date: Fri, 13 Dec 2024 10:49:11 +0800 Subject: [PATCH 028/172] fix: delete workflow interaction logic --- .../src/pages/workflow/hooks/useColumns.tsx | 8 +- apps/web/src/pages/workflow/index.tsx | 103 +++++------------- apps/web/src/pages/workflow/style.less | 7 ++ apps/web/src/services/http/workflow.ts | 2 +- packages/locales/src/lang/en/workflow.json | 4 +- 5 files changed, 41 insertions(+), 83 deletions(-) diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index d97d94f5..417ea05e 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -3,13 +3,11 @@ import { Stack, IconButton } from '@mui/material'; import { useI18n, useTime } from '@milesight/shared/src/hooks'; import { ListAltIcon, DeleteOutlineIcon, EditIcon } from '@milesight/shared/src/components'; import { Tooltip, type ColumnType } from '@/components'; -import { type DeviceAPISchema } from '@/services/http'; +import { type WorkflowAPISchema } from '@/services/http'; type OperationType = 'detail' | 'delete' | 'edit'; -export type TableRowDataType = ObjectToCamelCase< - DeviceAPISchema['getList']['response']['content'][0] ->; +export type TableRowDataType = ObjectToCamelCase; export interface UseColumnsProps { /** @@ -98,7 +96,7 @@ const useColumns = ({ onButtonClick }: UseColumnsPro { const navigate = useNavigate(); const { getIntlText } = useI18n(); // ---------- 列表数据相关逻辑 ---------- const [keyword, setKeyword] = useState(); - const [editOption, SetEditOption] = useState({ - isAdd: false, - openModal: false, - }); + const [addModal, setAddModal] = useState(false); const [importModal, setImportModal] = useState(false); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); @@ -76,27 +59,28 @@ const Workflow = () => { // ---------- 数据删除相关逻辑 ---------- const confirm = useConfirm(); + const warnIcon = useMemo(() => { + return ; + }, []); const handleDeleteConfirm = useCallback( (ids?: ApiKey[]) => { const idsToDelete = ids || [...selectedIds]; - confirm({ - title: getIntlText('common.label.delete'), - description: getIntlText('device.message.delete_tip'), - confirmButtonText: getIntlText('common.label.delete'), - confirmButtonProps: { - color: 'error', - }, + title: getIntlText('workflow.label.deletion'), + icon: warnIcon, + description: getIntlText('workflow.message.delete_tip'), onConfirm: async () => { const [error, resp] = await awaitWrap( - deviceAPI.deleteDevices({ device_id_list: idsToDelete }), + workflowAPI.deleteFlow({ id: idsToDelete }), ); // console.log({ error, resp }); if (error || !isRequestSuccess(resp)) return; getWorkflowList(); - setSelectedIds([]); + setSelectedIds(pre => { + return pre.filter(ids => !idsToDelete.includes(ids)); + }); toast.success(getIntlText('common.message.delete_success')); }, }); @@ -112,7 +96,7 @@ const Workflow = () => { variant="contained" sx={{ height: 36, textTransform: 'none' }} startIcon={} - onClick={() => handlerEditModal(true, true)} + onClick={() => setAddModal(true)} > {getIntlText('common.label.add')} @@ -155,52 +139,15 @@ const Workflow = () => { }, [navigate], ); - const handlerEditModal = (isAdd: boolean, isOpen: boolean, row?: EditRowData): void => { - const newEditOption: EditModalOption = { - isAdd, - openModal: isOpen, - }; - if (row && isOpen) { - newEditOption.dataSource = { - id: row.id, - name: row.name, - remark: row?.remark ?? '', - }; - } - SetEditOption(newEditOption); - }; - const submitEditModal = async (data: EditFormDataProps) => { - const { isAdd, dataSource } = editOption; - if (isAdd) { - navigate('/workflow/editor', { state: data }); - } else { - let errorMsg = ''; - if (dataSource) { - const [error, res] = await awaitWrap( - workflowAPI.updateFlow({ - id: dataSource.id, - name: data.name, - remark: data?.remark ?? '', - }), - ); - if (isRequestSuccess(res)) { - handlerEditModal(false, false); - toast.success(getIntlText('common.message.operation_success')); - return; - } - errorMsg = error?.message ?? getIntlText('common.message.something_wrong_title'); - } - if (errorMsg) { - toast.error(errorMsg); - } - } + const handlerAddModal = async (data: EditFormDataProps) => { + navigate('/workflow/editor', { state: data }); }; const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); switch (type) { case 'edit': { - handlerEditModal(false, true, record); + navigate(`/workflow/editor?wid=${record.id}`); break; } case 'detail': { @@ -220,7 +167,12 @@ const Workflow = () => { [navigate, handleDeleteConfirm], ); const columns = useColumns({ onButtonClick: handleTableBtnClick }); - + const isRowSelectable = useCallback( + ({ row }: { row: TableRowDataType }) => { + return !row.enabled; + }, + [columns], + ); return (
@@ -234,7 +186,7 @@ const Workflow = () => { // rowCount={workflowList?.total || 0} paginationModel={paginationModel} rowSelectionModel={selectedIds} - // isRowSelectable={({ row }) => row.deletable} + isRowSelectable={isRowSelectable} toolbarRender={toolbarRender} onPaginationModelChange={setPaginationModel} onRowSelectionModelChange={setSelectedIds} @@ -247,10 +199,9 @@ const Workflow = () => {
handlerEditModal(false, false)} - onConfirm={submitEditModal} + visible={addModal} + onCancel={() => setAddModal(false)} + onConfirm={handlerAddModal} /> Date: Fri, 13 Dec 2024 11:17:40 +0800 Subject: [PATCH 029/172] fix: add workflow interaction logic --- apps/web/src/pages/workflow/index.tsx | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index af00e804..a1eaf0de 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -13,9 +13,8 @@ import { } from '@milesight/shared/src/components'; import { Breadcrumbs, TablePro, useConfirm } from '@/components'; import { awaitWrap, isRequestSuccess, workflowAPI } from '@/services/http'; -import { type FormDataProps as EditFormDataProps } from '@/pages/workflow/components/edit-modal/hook/useWorkflowFormItems'; import { type FormDataProps as ImportFormDataProps } from '@/pages/workflow/components/import-modal/hook/useImportFormItems'; -import { EditModal, ImportModal } from '@/pages/workflow/components'; +import { ImportModal } from '@/pages/workflow/components'; import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; import './style.less'; @@ -25,7 +24,6 @@ const Workflow = () => { // ---------- 列表数据相关逻辑 ---------- const [keyword, setKeyword] = useState(); - const [addModal, setAddModal] = useState(false); const [importModal, setImportModal] = useState(false); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); @@ -87,7 +85,9 @@ const Workflow = () => { }, [confirm, getIntlText, getWorkflowList, selectedIds], ); - + const handlerAddModal = () => { + navigate('/workflow/editor'); + }; // ---------- Table 渲染相关 ---------- const toolbarRender = useMemo(() => { return ( @@ -96,7 +96,7 @@ const Workflow = () => { variant="contained" sx={{ height: 36, textTransform: 'none' }} startIcon={} - onClick={() => setAddModal(true)} + onClick={handlerAddModal} > {getIntlText('common.label.add')} @@ -139,9 +139,7 @@ const Workflow = () => { }, [navigate], ); - const handlerAddModal = async (data: EditFormDataProps) => { - navigate('/workflow/editor', { state: data }); - }; + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); @@ -198,11 +196,6 @@ const Workflow = () => { /> - setAddModal(false)} - onConfirm={handlerAddModal} - /> handlerImportModal(false, param)} From 0a36371584fee96d968c5036c033bd72b36eee31 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 13 Dec 2024 22:35:52 +0800 Subject: [PATCH 030/172] feat: add ParamSelect, ParamInputSelect components --- .../components/entity-filter-select/index.tsx | 9 +- .../components/entity-select/index.tsx | 7 + .../config-panel/components/index.ts | 1 + .../components/param-input-select/index.tsx | 121 ++++++++++++++++++ .../components/param-input-select/style.less | 5 + .../components/param-select/index.tsx | 96 +++++++++++++- .../components/param-select/style.less | 57 +++++++++ .../editor/components/config-panel/style.less | 1 + .../views/editor/hooks/useWorkflow.ts | 35 ++++- packages/locales/src/lang/en/workflow.json | 3 +- .../shared/src/components/icons/index.tsx | 3 + 11 files changed, 332 insertions(+), 6 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx index 4e2ef5f2..77544357 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx @@ -10,7 +10,11 @@ import { } from '@mui/material'; import { useDynamicList, useControllableValue } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; -import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; +import { + DeleteOutlineIcon, + AddIcon, + KeyboardArrowDownIcon, +} from '@milesight/shared/src/components'; import EntitySelect, { type EntitySelectProps, type EntitySelectValueType } from '../entity-select'; import './style.less'; @@ -76,6 +80,7 @@ const EntityFilterSelect: React.FC = ({ notched labelId="entity-filter-select-type-label" label={typeSelectProps?.label || getIntlText('common.label.type')} + IconComponent={KeyboardArrowDownIcon} value={item.type || ''} onChange={e => replace(index, { ...item, type: e.target.value as EntityType }) @@ -98,7 +103,7 @@ const EntityFilterSelect: React.FC = ({ }} onChange={(_, data) => { if (!data) { - replace(index, DEFAULT_EMPTY_VALUE); + replace(index, { ...item, ...DEFAULT_EMPTY_VALUE }); return; } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx index 4d93fd46..851fa2f7 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx @@ -8,6 +8,7 @@ import { type AutocompleteRenderInputParams, } from '@mui/material'; import { useI18n, useVirtualList } from '@milesight/shared/src/hooks'; +import { KeyboardArrowDownIcon } from '@milesight/shared/src/components'; import { Tooltip } from '@/components'; import { entityAPI, @@ -185,6 +186,7 @@ const EntitySelect: React.FC = ({ label, required, filterMode {...params} required={required} label={label || getIntlText('common.label.entity')} + // slotProps={{ input: { readOnly: true } }} /> ); }, @@ -222,7 +224,12 @@ const EntitySelect: React.FC = ({ label, required, filterMode isOptionEqualToValue={(option, currentVal) => option.value === currentVal.value} slotProps={{ listbox: { component: Listbox }, + popper: { + sx: { minWidth: 300 }, + placement: 'bottom-end', + }, }} + popupIcon={} onInputChange={(_, keyword, reason) => { if (reason !== 'input') { getEntityList(); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 70c46b95..aa40e397 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -8,6 +8,7 @@ export { export { default as MarkdownEditor } from './markdown-editor'; export { default as ParamInput } from './param-input'; export { default as ParamSelect } from './param-select'; +export { default as ParamInputSelect } from './param-input-select'; export { default as TimerInput } from './timer-input'; export { default as EntityFilterSelect, diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx new file mode 100644 index 00000000..d208ca91 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx @@ -0,0 +1,121 @@ +import { useEffect, useLayoutEffect, useState } from 'react'; +import { useControllableValue } from 'ahooks'; +import { Autocomplete, TextField, MenuItem, type AutocompleteProps } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { SettingsOutlinedIcon } from '@milesight/shared/src/components'; +import useWorkflow from '@/pages/workflow/views/editor/hooks/useWorkflow'; +import './style.less'; + +type ParamInputSelectValueType = { + type: 'custom' | 'reference'; + value?: string; +}; + +type OptionItemType = { + nodeId: ApiKey; + nodeName: string; + nodeType: WorkflowNodeType; + outputs: { + name: string; + type: string; + key: string; + }[]; +}; + +export interface ParamInputSelectProps { + label?: string; + + required?: boolean; + + disabled?: boolean; + + /** + * Param Select Placeholder + */ + placeholder?: string; + + value?: ParamInputSelectValueType; + + defaultValue?: ParamInputSelectValueType; + + onChange?: (value: ParamInputSelectValueType) => void; + + /** + * Autocomplete Props + */ + autocompleteProps?: Omit< + AutocompleteProps, + 'options' | 'renderInput' + >; +} + +/** + * Param Input Select Component + * + * Note: This is a basic component., use in CodeNode, ServiceNode, EntityAssignmentNode + * + * TODO: render nodes params + */ +const ParamSelect: React.FC = ({ + label, + required, + disabled, + placeholder, + autocompleteProps, + ...props +}) => { + const { getIntlText } = useI18n(); + const [value, setValue] = useControllableValue(props); + const [popperOpen, setPopperOpen] = useState(false); + const [inputValue, setInputValue] = useState(''); + const [innerValue, setInnerValue] = useState(''); + + useLayoutEffect(() => { + if (value?.type === 'custom') { + setInputValue(value?.value || ''); + } else if (value?.type === 'reference') { + setInnerValue(value?.value || ''); + } + }, [value]); + + return ( +
+ setPopperOpen(false)} + value={innerValue} + onChange={(_, value) => { + setValue({ type: 'reference', value: value || '' }); + }} + onInputChange={(e, value) => { + setValue({ type: 'custom', value }); + }} + options={['111', '222', '333']} + renderInput={params => ( + + )} + filterOptions={options => options} + popupIcon={} + slotProps={{ + popupIndicator: { + onClick() { + setPopperOpen(true); + }, + }, + }} + /> +
+ ); +}; + +export default ParamSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less new file mode 100644 index 00000000..677d4b35 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less @@ -0,0 +1,5 @@ +.@{prefix}-param-input-select { + .@{mui-prefix}InputBase-root { + padding-right: @padding-xs; + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx index 54055802..2bba2ac2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -1,8 +1,100 @@ +import { useCallback } from 'react'; +import { + Select, + FormControl, + InputLabel, + MenuItem, + ListSubheader, + type SelectProps, +} from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { KeyboardArrowDownIcon } from '@milesight/shared/src/components'; +import { Tooltip } from '@/components'; +import useWorkflow from '@/pages/workflow/views/editor/hooks/useWorkflow'; +import './style.less'; + +type ParamSelectValueType = string; + +type OptionItemType = { + nodeId: ApiKey; + nodeName?: string; + nodeType?: WorkflowNodeType; + outputs: { + name: string; + type: string; + key: string; + }[]; +}; + +export type ParamSelectProps = SelectProps; + /** * Param Select Component * - * Note: This is a basic component., use in CodeNode, ServiceNode, EntityAssignmentNode + * Note: This is a basic component, use in IfelseNode */ -const ParamSelect = () => {}; +const ParamSelect: React.FC = ({ label, required, disabled, ...props }) => { + const { getIntlText } = useI18n(); + const { getIncomeNodes } = useWorkflow(); + + const renderOptions = useCallback(() => { + const incomeNodes = getIncomeNodes(); + // TODO: get the correct nodes params + const data: OptionItemType[] = incomeNodes.map(node => ({ + nodeId: node.id, + nodeName: node.data?.name, + nodeType: node.type, + outputs: [ + { + name: 'output11', + type: 'string', + key: `${node.type}.${node.id}.1132e3123132`, + }, + { + name: 'output22', + type: 'number', + key: `${node.type}.${node.id}.11eyu3123132`, + }, + ], + })); + + return data.map(item => [ + + {item.nodeType} + , + item.outputs.map(output => ( + +
+ + {output.name} + + {output.type} +
+
+ )), + ]); + }, [getIncomeNodes]); + + return ( +
+ + + {label || getIntlText('common.label.value')} + + + {...props} + // @ts-ignore + notched + defaultValue="" + labelId="param-select-label" + label={label || getIntlText('common.label.value')} + IconComponent={KeyboardArrowDownIcon} + > + {renderOptions()} + + +
+ ); +}; export default ParamSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less new file mode 100644 index 00000000..92c68a22 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less @@ -0,0 +1,57 @@ +.@{prefix}-param-select { + // .@{mui-prefix}InputBase-root { + // padding-right: @padding-xs; + // } + + &-option-groupname.@{mui-prefix}ListSubheader-root { + .text-size(@font-size-sm); + + margin-top: @margin-xs; + } + + &-option.@{mui-prefix}MenuItem-root { + padding: @padding-xs @padding-md; + + .@{prefix}-param-select-item { + display: flex; + .text-size(@font-size-base); + + .name { + max-width: 50%; + } + + .type { + display: inline-block; + padding: 0 @padding-xxs; + margin-left: @margin-md; + color: var(--text-color-tertiary); + background-color: var(--component-background-gray); + border-radius: @border-radius-base; + } + } + } + + .@{mui-prefix}InputBase-root { + .@{prefix}-param-select-item { + display: inline-flex; + padding: 0 @padding-sm; + background-color: var(--component-background-gray); + border-radius: @border-radius-base; + .text-size(@font-size-base); + + .name { + color: var(--text-color-secondary); + + &::after { + display: inline-block; + margin: 0 @margin-xxs; + content: '/'; + } + } + + .type { + font-size: @font-size-sm; + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less index fbaac891..17bda124 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less @@ -45,6 +45,7 @@ &-body { flex: 1; padding: 0 @padding-xl @padding-lg; + overflow: auto; .@{prefix}-common-form-items { .@{mui-prefix}FormControl-root:last-child { diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 62b3ecc8..dc684a66 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { useReactFlow, getOutgoers, type IsValidConnection } from '@xyflow/react'; +import { useReactFlow, getIncomers, getOutgoers, type IsValidConnection } from '@xyflow/react'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { PARALLEL_DEPTH_LIMIT } from '../constants'; @@ -59,9 +59,42 @@ const useWorkflow = () => { [getIntlText], ); + // Get the only selected node that is not dragging + const getSelectedNode = useCallback(() => { + const nodes = getNodes(); + const selectedNodes = nodes.filter(item => item.selected); + + const node = selectedNodes?.[0]; + + if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { + return; + } + + return node; + }, [getNodes]); + + const getIncomeNodes = useCallback( + (currentNode?: WorkflowNode) => { + currentNode = currentNode || getSelectedNode(); + const getAllIncomers = (node: WorkflowNode, result: WorkflowNode[] = []) => { + const incomers = getIncomers(node, getNodes(), getEdges()); + + result.push(...incomers); + incomers.forEach(item => getAllIncomers(item, result)); + + return result; + }; + + return getAllIncomers(currentNode!); + }, + [getEdges, getNodes, getSelectedNode], + ); + return { isValidConnection, checkNestedParallelLimit, + getSelectedNode, + getIncomeNodes, }; }; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 544d7410..f31b3bd6 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -36,5 +36,6 @@ "workflow.editor.log_panel_title_verification_result": "Verification Result", "workflow.editor.save_warning_message": "The current workflow has been referenced, and saving it will update the workflow information synchronously. Are you sure you want to save it", "workflow.message.delete_tip": "Are you sure you want to delete the workflow? It cannot be restore after deletion.", - "workflow.label.deletion": "Deletion" + "workflow.label.deletion": "Deletion", + "workflow.editor.form_param_select_placeholder": "Enter or reference a variable" } diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 63749587..f68c4276 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -89,6 +89,9 @@ export { History as HistoryIcon, UploadFile as UploadFileIcon, AttachFile as AttachFileIcon, + SettingsOutlined as SettingsOutlinedIcon, + KeyboardArrowUp as KeyboardArrowUpIcon, + KeyboardArrowDown as KeyboardArrowDownIcon, } from '@mui/icons-material'; export * from './iot-icons'; From 6722dfb642a3681d672e5bf88bcf5fa85155f814 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 16 Dec 2024 08:41:40 +0800 Subject: [PATCH 031/172] fix: jump to the login page, if the user api error --- apps/web/src/layouts/BasicLayout.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/web/src/layouts/BasicLayout.tsx b/apps/web/src/layouts/BasicLayout.tsx index cb7aa575..0a043c57 100644 --- a/apps/web/src/layouts/BasicLayout.tsx +++ b/apps/web/src/layouts/BasicLayout.tsx @@ -35,12 +35,12 @@ function BasicLayout() { useRequest( async () => { - if (!token) { - // 判断客户端是否已注册,若已注册则跳转登录页,否则跳转注册页 - const target = iotLocalStorage.getItem(REGISTERED_KEY) - ? '/auth/login' - : '/auth/register'; + // 判断客户端是否已注册,若已注册则跳转登录页,否则跳转注册页 + const target = iotLocalStorage.getItem(REGISTERED_KEY) + ? '/auth/login' + : '/auth/register'; + if (!token) { navigate(target, { replace: true }); return; } @@ -54,7 +54,11 @@ function BasicLayout() { const [error, resp] = await awaitWrap(globalAPI.getUserInfo()); setLoading(false); - if (error || !isRequestSuccess(resp)) return; + if (error || !isRequestSuccess(resp)) { + navigate(target, { replace: true }); + return; + } + setUserInfo(getResponseData(resp)); }, { From 1bb6b814ca5c63ccfd7ef9c4d9fceea431e744ad Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 16 Dec 2024 17:37:54 +0800 Subject: [PATCH 032/172] feat: Add Code editor customizes components --- apps/web/package.json | 9 + .../code-editor/components/editor/index.tsx | 82 + .../code-editor/components/editor/style.less | 39 + .../code-editor/components/header/index.tsx | 65 + .../code-editor/components/header/style.less | 38 + .../code-editor/components/index.tsx | 2 + .../src/components/code-editor/constant.tsx | 32 + .../components/code-editor/hooks/index.tsx | 1 + .../code-editor/hooks/useEditorTheme.tsx | 27 + apps/web/src/components/code-editor/index.tsx | 37 + .../web/src/components/code-editor/style.less | 10 + .../web/src/components/code-editor/types.d.ts | 46 + apps/web/src/components/index.ts | 2 + pnpm-lock.yaml | 9615 ++++++++++------- 14 files changed, 6056 insertions(+), 3949 deletions(-) create mode 100644 apps/web/src/components/code-editor/components/editor/index.tsx create mode 100644 apps/web/src/components/code-editor/components/editor/style.less create mode 100644 apps/web/src/components/code-editor/components/header/index.tsx create mode 100644 apps/web/src/components/code-editor/components/header/style.less create mode 100644 apps/web/src/components/code-editor/components/index.tsx create mode 100644 apps/web/src/components/code-editor/constant.tsx create mode 100644 apps/web/src/components/code-editor/hooks/index.tsx create mode 100644 apps/web/src/components/code-editor/hooks/useEditorTheme.tsx create mode 100644 apps/web/src/components/code-editor/index.tsx create mode 100644 apps/web/src/components/code-editor/style.less create mode 100644 apps/web/src/components/code-editor/types.d.ts diff --git a/apps/web/package.json b/apps/web/package.json index 6c5bebff..8db75875 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,6 +13,13 @@ "ts-check": "tsc --noEmit" }, "dependencies": { + "@codemirror/lang-java": "^6.0.1", + "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-python": "^6.1.6", + "@codemirror/lang-yaml": "^6.1.2", + "@codemirror/language": "^6.10.6", + "@codemirror/legacy-modes": "^6.4.2", "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@milesight/locales": "workspace:*", @@ -20,6 +27,8 @@ "@mui/material": "^6.1.0", "@mui/x-data-grid": "^7.16.0", "@mui/x-date-pickers": "^7.18.0", + "@uiw/codemirror-theme-vscode": "^4.23.7", + "@uiw/react-codemirror": "^4.23.6", "@xyflow/react": "^12.3.5", "ahooks": "^3.8.1", "axios": "^1.7.7", diff --git a/apps/web/src/components/code-editor/components/editor/index.tsx b/apps/web/src/components/code-editor/components/editor/index.tsx new file mode 100644 index 00000000..3cb26a60 --- /dev/null +++ b/apps/web/src/components/code-editor/components/editor/index.tsx @@ -0,0 +1,82 @@ +import React, { useCallback, useMemo } from 'react'; +import cls from 'classnames'; +import CodeMirror from '@uiw/react-codemirror'; +// support language +import { yaml } from '@codemirror/lang-yaml'; +import { json } from '@codemirror/lang-json'; +import { java } from '@codemirror/lang-java'; +import { python } from '@codemirror/lang-python'; +import { javascript } from '@codemirror/lang-javascript'; +// extension language +import { StreamLanguage } from '@codemirror/language'; +import { groovy } from '@codemirror/legacy-modes/mode/groovy'; + +import { useEditorTheme } from '../../hooks'; +import { EditorContentProps, type EditorSupportLang } from '../../types'; +import './style.less'; + +interface IProps extends EditorContentProps { + editorLang?: EditorSupportLang; + editorValue: string; + setEditorValue: (value: string) => void; +} +export default React.memo( + ({ + showLineNumber = true, + showFold = true, + editable = true, + readOnly = false, + editorLang, + editorValue, + fontSize, + setEditorValue, + ...rest + }: IProps) => { + const { editorTheme } = useEditorTheme({ fontSize }); + + /** editor input change callback */ + const onInputChange = useCallback( + (value: string) => { + setEditorValue(value); + }, + [setEditorValue], + ); + + /** Select the corresponding extension based on the language */ + const extensions = useMemo(() => { + switch (editorLang) { + case 'yaml': + return [yaml()]; + case 'json': + return [json()]; + case 'mvel': + // The mvel language is similar to java, which is used here for highlighting + return [java()]; + case 'python': + return [python()]; + case 'javascript': + return [javascript()]; + case 'groovy': + return [StreamLanguage.define(groovy)]; + default: + return []; + } + }, [editorLang]); + + return ( + + ); + }, +); diff --git a/apps/web/src/components/code-editor/components/editor/style.less b/apps/web/src/components/code-editor/components/editor/style.less new file mode 100644 index 00000000..b0c77f07 --- /dev/null +++ b/apps/web/src/components/code-editor/components/editor/style.less @@ -0,0 +1,39 @@ +.ms-code-editor-content { + flex: 1; + overflow: hidden; + + .cm-editor { + height: 100%; + outline: none; + + .cm-gutters { + border: unset; + user-select: none; + } + } +} + +.ms-editor { + &__lineNumbers { + &--hide { + .cm-lineNumbers, + .cm-foldGutter { + width: 0; + } + } + } + + &__foldGutter { + &--hide { + .cm-foldGutter { + pointer-events: none; + + .cm-gutterElement { + span { + visibility: hidden; + } + } + } + } + } +} diff --git a/apps/web/src/components/code-editor/components/header/index.tsx b/apps/web/src/components/code-editor/components/header/index.tsx new file mode 100644 index 00000000..6acfded6 --- /dev/null +++ b/apps/web/src/components/code-editor/components/header/index.tsx @@ -0,0 +1,65 @@ +import React, { useCallback, useMemo } from 'react'; +import { MenuItem, Select } from '@mui/material'; +import { type SelectInputProps } from '@mui/material/Select/SelectInput'; +import { ContentCopyIcon, ExpandMoreIcon } from '@milesight/shared/src/components'; +import { useCopy } from '@milesight/shared/src/hooks'; +import { editorLangOptions } from '../../constant'; +import { type EditorSupportLang, EditorProps } from '../../types'; +import './style.less'; + +interface IProps extends Pick { + editorValue: string; + editorLang?: EditorSupportLang; + setEditorLang: (lang: EditorSupportLang) => void; +} +export default React.memo( + ({ editorValue, editorLang, setEditorLang, header, supportLangs }: IProps) => { + const { icon, title } = header || {}; + const { handleCopy } = useCopy(); + + /** select change callback */ + const handleChange = useCallback>['onChange']>( + e => { + const { value } = e.target; + setEditorLang(value as EditorSupportLang); + }, + [setEditorLang], + ); + + /** copy button callback */ + const handleCopyIcon = useCallback(() => { + handleCopy?.(editorValue); + }, [editorValue, handleCopy]); + + /** custom select options */ + const customEditorLangOptions = useMemo(() => { + if (!supportLangs?.length) return editorLangOptions; + + return editorLangOptions.filter(item => supportLangs.includes(item.lang)); + }, [supportLangs]); + + return ( +
+ {title || ( + + )} + {icon || ( +
+ +
+ )} +
+ ); + }, +); diff --git a/apps/web/src/components/code-editor/components/header/style.less b/apps/web/src/components/code-editor/components/header/style.less new file mode 100644 index 00000000..4d4c7e00 --- /dev/null +++ b/apps/web/src/components/code-editor/components/header/style.less @@ -0,0 +1,38 @@ +.ms-code-editor-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: @padding-xs @padding-sm; + background-color: var(--component-background-gray); + border-bottom: @border-base; + + .ms-header-select { + min-width: 100px; + color: var(--text-color-secondary); + .text-size(@font-size-md); + + .MuiSelect-select.MuiInputBase-input { + padding: 0; + padding-right: @padding-xxl; + } + + .MuiOutlinedInput-notchedOutline { + border: unset; + } + } + + .ms-header-action { + display: flex; + align-items: center; + justify-content: space-between; + + .MuiSvgIcon-root { + cursor: pointer; + + &:hover { + color: var(--icon-color-hover); + transition: color 0.3s; + } + } + } +} diff --git a/apps/web/src/components/code-editor/components/index.tsx b/apps/web/src/components/code-editor/components/index.tsx new file mode 100644 index 00000000..ff887c20 --- /dev/null +++ b/apps/web/src/components/code-editor/components/index.tsx @@ -0,0 +1,2 @@ +export { default as EditorHeader } from './header'; +export { default as EditorComponent } from './editor'; diff --git a/apps/web/src/components/code-editor/constant.tsx b/apps/web/src/components/code-editor/constant.tsx new file mode 100644 index 00000000..c667b032 --- /dev/null +++ b/apps/web/src/components/code-editor/constant.tsx @@ -0,0 +1,32 @@ +import type { EditorSupportLang } from './types'; + +/** Editor language options */ +export const editorLangOptions: { + lang: EditorSupportLang; + label: string; +}[] = [ + { + lang: 'javascript', + label: 'JavaScript', + }, + { + lang: 'python', + label: 'Python', + }, + { + lang: 'json', + label: 'JSON', + }, + { + lang: 'yaml', + label: 'YAML', + }, + { + lang: 'groovy', + label: 'Groovy', + }, + { + lang: 'mvel', + label: 'mvel', + }, +]; diff --git a/apps/web/src/components/code-editor/hooks/index.tsx b/apps/web/src/components/code-editor/hooks/index.tsx new file mode 100644 index 00000000..a0561be9 --- /dev/null +++ b/apps/web/src/components/code-editor/hooks/index.tsx @@ -0,0 +1 @@ +export { useEditorTheme } from './useEditorTheme'; diff --git a/apps/web/src/components/code-editor/hooks/useEditorTheme.tsx b/apps/web/src/components/code-editor/hooks/useEditorTheme.tsx new file mode 100644 index 00000000..e54350a8 --- /dev/null +++ b/apps/web/src/components/code-editor/hooks/useEditorTheme.tsx @@ -0,0 +1,27 @@ +import { useMemo } from 'react'; +import { useTheme } from '@milesight/shared/src/hooks'; +import { vscodeDarkInit, vscodeLightInit } from '@uiw/codemirror-theme-vscode'; +import { type EditorProps } from '../types'; + +export const useEditorTheme = ({ fontSize = 18 }: Pick) => { + const { theme, getCSSVariableValue } = useTheme(); + + /** editor theme */ + const editorTheme = useMemo(() => { + const vscodeThemeInit = theme === 'dark' ? vscodeDarkInit : vscodeLightInit; + + return vscodeThemeInit({ + settings: { + gutterBorder: getCSSVariableValue('--component-background-gray'), + gutterBackground: getCSSVariableValue('--component-background-gray'), + background: getCSSVariableValue('--component-background-gray'), + gutterForeground: getCSSVariableValue('--text-color-tertiary'), + fontSize: `${fontSize}`, + }, + }); + }, [theme, getCSSVariableValue, fontSize]); + + return { + editorTheme, + }; +}; diff --git a/apps/web/src/components/code-editor/index.tsx b/apps/web/src/components/code-editor/index.tsx new file mode 100644 index 00000000..b1959bcc --- /dev/null +++ b/apps/web/src/components/code-editor/index.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { useControllableValue } from 'ahooks'; +import { EditorComponent, EditorHeader } from './components'; +import type { EditorSupportLang, EditorProps } from './types'; +import './style.less'; + +export default React.memo((props: EditorProps) => { + const { header, supportLangs, ...rest } = props; + const [editorLang, setEditorLang] = useControllableValue(props, { + valuePropName: 'editorLang', + trigger: 'onLangChange', + }); + const [editorValue, setEditorValue] = useControllableValue(props, { + valuePropName: 'value', + trigger: 'onValueChange', + }); + + return ( +
+ {header === null ? null : ( + + )} + +
+ ); +}); diff --git a/apps/web/src/components/code-editor/style.less b/apps/web/src/components/code-editor/style.less new file mode 100644 index 00000000..15158775 --- /dev/null +++ b/apps/web/src/components/code-editor/style.less @@ -0,0 +1,10 @@ +.ms-code-editor { + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + overflow: hidden; + background-color: var(--main-background); + border: @border-base; + border-radius: @border-radius-xs; +} diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts new file mode 100644 index 00000000..03043c42 --- /dev/null +++ b/apps/web/src/components/code-editor/types.d.ts @@ -0,0 +1,46 @@ +import { ReactCodeMirrorProps } from '@uiw/react-codemirror'; + +/** Supported languages for the code editor. */ +export type EditorSupportLang = 'groovy' | 'javascript' | 'python' | 'mvel' | 'json' | 'yaml'; + +/** Props for the code editor component. */ +export interface EditorProps extends EditorContentProps { + /** The programming language used in the editor. */ + editorLang?: EditorSupportLang; + /** + * Callback function triggered when the language changes. + * @param value - The new language value. + */ + onLangChange?: (value: string) => void; + + /** The content value of the editor. */ + value?: string; + /** + * Callback function triggered when the content value changes. + * @param value - The new content value. + */ + onValueChange?: (value: string) => void; + + /** Props for the editor toolbar header. */ + header?: EditorToolbarProps | null; + /** Customize supported languages */ + supportLangs?: EditorSupportLang[]; +} + +/** Code Editor Header Props */ +export interface EditorToolbarProps { + /** The title displayed in the toolbar. */ + title?: React.ReactNode; + /** The icon displayed in the toolbar. */ + icon?: React.ReactNode; +} + +/** Props for the code editor content. */ +export interface EditorContentProps extends ReactCodeMirrorProps { + /** Whether to show line numbers in the editor. */ + showLineNumber?: boolean; + /** Whether to enable code folding in the editor. */ + showFold?: boolean; + /** The font size used in the editor. */ + fontSize?: number; +} diff --git a/apps/web/src/components/index.ts b/apps/web/src/components/index.ts index 4411950e..2c2cf0b2 100644 --- a/apps/web/src/components/index.ts +++ b/apps/web/src/components/index.ts @@ -8,3 +8,5 @@ export { default as Tooltip } from './tooltip'; export { default as DateRangePicker } from './date-range-picker'; export { default as RouteLoadingIndicator } from './route-loading-indicator'; export { default as Empty } from './empty'; +export { default as CodeEditor } from './code-editor'; +export type { EditorProps, EditorSupportLang } from './code-editor/types'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 31dfcc46..39e3a8f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -56,12 +56,33 @@ importers: apps/web: dependencies: + '@codemirror/lang-java': + specifier: ^6.0.1 + version: 6.0.1 + '@codemirror/lang-javascript': + specifier: ^6.2.2 + version: 6.2.2 + '@codemirror/lang-json': + specifier: ^6.0.1 + version: 6.0.1 + '@codemirror/lang-python': + specifier: ^6.1.6 + version: 6.1.6(@codemirror/view@6.35.3) + '@codemirror/lang-yaml': + specifier: ^6.1.2 + version: 6.1.2(@codemirror/view@6.35.3) + '@codemirror/language': + specifier: ^6.10.6 + version: 6.10.6 + '@codemirror/legacy-modes': + specifier: ^6.4.2 + version: 6.4.2 '@emotion/react': specifier: ^11.13.3 version: 11.13.3(@types/react@18.3.5)(react@18.3.1) '@emotion/styled': specifier: ^11.13.0 - version: 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) + version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@milesight/locales': specifier: workspace:* version: link:../../packages/locales @@ -70,16 +91,22 @@ importers: version: link:../../packages/shared '@mui/material': specifier: ^6.1.0 - version: 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) + version: 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-data-grid': specifier: ^7.16.0 - version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) + version: 7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.18.0 - version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + version: 7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@uiw/codemirror-theme-vscode': + specifier: ^4.23.7 + version: 4.23.7(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3) + '@uiw/react-codemirror': + specifier: ^4.23.6 + version: 4.23.6(@babel/runtime@7.25.6)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3))(@codemirror/language@6.10.6)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.3)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@xyflow/react': specifier: ^12.3.5 - version: 12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1)(react@18.3.1) + version: 12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ahooks: specifier: ^3.8.1 version: 3.8.1(react@18.3.1) @@ -118,7 +145,7 @@ importers: version: 18.3.1(react@18.3.1) react-grid-layout: specifier: ^1.5.0 - version: 1.5.0(react-dom@18.3.1)(react@18.3.1) + version: 1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-hook-form: specifier: ^7.53.0 version: 7.53.0(react@18.3.1) @@ -130,7 +157,7 @@ importers: version: 6.26.1(react@18.3.1) react-router-dom: specifier: ^6.26.1 - version: 6.26.1(react-dom@18.3.1)(react@18.3.1) + version: 6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) zustand: specifier: ^4.5.5 version: 4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1) @@ -164,7 +191,7 @@ importers: version: 1.3.5 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.4.9) + version: 4.3.1(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) eslint: specifier: ^8.56.0 version: 8.57.0 @@ -185,19 +212,19 @@ importers: version: 5.4.9(@types/node@20.16.5)(less@4.2.0) vite-plugin-html-config: specifier: ^2.0.2 - version: 2.0.2(vite@5.4.9) + version: 2.0.2(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-imp: specifier: ^2.4.0 - version: 2.4.0(vite@5.4.9) + version: 2.4.0(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(vite@5.4.9) + version: 0.22.0(rollup@4.24.0)(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-progress: specifier: ^0.0.7 - version: 0.0.7(vite@5.4.9) + version: 0.0.7(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-stylelint: specifier: ^5.3.1 - version: 5.3.1(postcss@8.4.47)(stylelint@15.11.0)(vite@5.4.9) + version: 5.3.1(postcss@8.4.47)(rollup@4.24.0)(stylelint@15.11.0(typescript@5.6.2))(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) packages/locales: devDependencies: @@ -209,7 +236,7 @@ importers: version: link:../spec '@rollup/plugin-babel': specifier: ^6.0.3 - version: 6.0.4(@babel/core@7.25.2)(rollup@4.24.0) + version: 6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.24.0) '@rollup/plugin-dynamic-import-vars': specifier: ^2.0.5 version: 2.1.2(rollup@4.24.0) @@ -221,7 +248,7 @@ importers: version: 15.2.3(rollup@4.24.0) '@rollup/plugin-typescript': specifier: ^12.1.0 - version: 12.1.1(rollup@4.24.0)(typescript@5.6.2) + version: 12.1.1(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2) '@types/fs-extra': specifier: ^9.0.13 version: 9.0.13 @@ -300,7 +327,7 @@ importers: version: link:../spec '@rollup/plugin-babel': specifier: ^6.0.3 - version: 6.0.4(@babel/core@7.25.2)(rollup@4.24.0) + version: 6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.24.0) '@rollup/plugin-commonjs': specifier: ^28.0.1 version: 28.0.1(rollup@4.24.0) @@ -312,7 +339,7 @@ importers: version: 15.2.3(rollup@4.24.0) '@rollup/plugin-typescript': specifier: ^12.1.0 - version: 12.1.1(rollup@4.24.0)(typescript@5.6.2) + version: 12.1.1(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2) '@types/less': specifier: ^3.0.6 version: 3.0.6 @@ -336,16 +363,16 @@ importers: version: link:../locales '@mui/icons-material': specifier: ^6.0.2 - version: 6.0.2(@mui/material@6.1.0)(@types/react@18.3.5)(react@18.3.1) + version: 6.0.2(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/material': specifier: ^6.1.0 - version: 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) + version: 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.18.0 - version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + version: 7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@xyflow/react': specifier: ^12.3.5 - version: 12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1)(react@18.3.1) + version: 12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ahooks: specifier: ^3.8.1 version: 3.8.1(react@18.3.1) @@ -390,13 +417,13 @@ importers: version: 2.11.1(react@18.3.1) react-resizable: specifier: ^3.0.5 - version: 3.0.5(react-dom@18.3.1)(react@18.3.1) + version: 3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-router: specifier: ^6.26.1 version: 6.26.1(react@18.3.1) react-router-dom: specifier: ^6.26.1 - version: 6.26.1(react-dom@18.3.1)(react@18.3.1) + version: 6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) validator: specifier: ^13.12.0 version: 13.12.0 @@ -445,34 +472,34 @@ importers: version: 1.10.4 '@typescript-eslint/eslint-plugin': specifier: ^8.4.0 - version: 8.5.0(@typescript-eslint/parser@8.5.0)(eslint@8.57.0)(typescript@5.6.2) + version: 8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) '@typescript-eslint/parser': specifier: ^8.4.0 version: 8.5.0(eslint@8.57.0)(typescript@5.6.2) eslint-config-airbnb: specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0)(eslint-plugin-react-hooks@4.6.2)(eslint-plugin-react@7.35.2)(eslint@8.57.0) + version: 19.0.4(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.35.2(eslint@8.57.0))(eslint@8.57.0) eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@8.57.0) eslint-import-resolver-alias: specifier: ^1.1.2 - version: 1.1.2(eslint-plugin-import@2.30.0) + version: 1.1.2(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0)) eslint-import-resolver-typescript: specifier: ^3.6.3 - version: 3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0) + version: 3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0) eslint-plugin-check-file: specifier: ^2.8.0 version: 2.8.0(eslint@8.57.0) eslint-plugin-import: specifier: ^2.30.0 - version: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + version: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: specifier: ^6.10.0 version: 6.10.0(eslint@8.57.0) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) + version: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-react: specifier: ^7.35.2 version: 7.35.2(eslint@8.57.0) @@ -499,16 +526,16 @@ importers: version: 15.11.0(typescript@5.6.2) stylelint-config-prettier: specifier: ^9.0.5 - version: 9.0.5(stylelint@15.11.0) + version: 9.0.5(stylelint@15.11.0(typescript@5.6.2)) stylelint-config-property-sort-order-smacss: specifier: ^10.0.0 - version: 10.0.0(stylelint@15.11.0) + version: 10.0.0(stylelint@15.11.0(typescript@5.6.2)) stylelint-config-standard: specifier: ^34.0.0 - version: 34.0.0(stylelint@15.11.0) + version: 34.0.0(stylelint@15.11.0(typescript@5.6.2)) stylelint-order: specifier: ^6.0.4 - version: 6.0.4(stylelint@15.11.0) + version: 6.0.4(stylelint@15.11.0(typescript@5.6.2)) devDependencies: eslint: specifier: ^8.56.0 @@ -519,615 +546,311 @@ importers: packages: - /@ampproject/remapping@2.3.0: + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - dev: true - /@babel/code-frame@7.24.7: + '@babel/code-frame@7.24.7': resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/highlight': 7.24.7 - picocolors: 1.1.0 - /@babel/compat-data@7.25.4: + '@babel/compat-data@7.25.4': resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} engines: {node: '>=6.9.0'} - dev: true - /@babel/core@7.25.2: + '@babel/core@7.25.2': resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} engines: {node: '>=6.9.0'} - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.6 - '@babel/helper-compilation-targets': 7.25.2 - '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) - '@babel/helpers': 7.25.6 - '@babel/parser': 7.25.6 - '@babel/template': 7.25.0 - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 - convert-source-map: 2.0.0 - debug: 4.3.7 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/generator@7.25.6: + '@babel/generator@7.25.6': resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/types': 7.25.6 - '@jridgewell/gen-mapping': 0.3.5 - '@jridgewell/trace-mapping': 0.3.25 - jsesc: 2.5.2 - /@babel/helper-compilation-targets@7.25.2: + '@babel/helper-compilation-targets@7.25.2': resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/compat-data': 7.25.4 - '@babel/helper-validator-option': 7.24.8 - browserslist: 4.23.3 - lru-cache: 5.1.1 - semver: 6.3.1 - dev: true - /@babel/helper-module-imports@7.24.7: + '@babel/helper-module-imports@7.24.7': resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 - transitivePeerDependencies: - - supports-color - /@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2): + '@babel/helper-module-transforms@7.25.2': resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-imports': 7.24.7 - '@babel/helper-simple-access': 7.24.7 - '@babel/helper-validator-identifier': 7.24.7 - '@babel/traverse': 7.25.6 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helper-plugin-utils@7.24.8: + '@babel/helper-plugin-utils@7.24.8': resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helper-simple-access@7.24.7: + '@babel/helper-simple-access@7.24.7': resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/traverse': 7.25.6 - '@babel/types': 7.25.6 - transitivePeerDependencies: - - supports-color - dev: true - /@babel/helper-string-parser@7.24.8: + '@babel/helper-string-parser@7.24.8': resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-identifier@7.24.7: + '@babel/helper-validator-identifier@7.24.7': resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} engines: {node: '>=6.9.0'} - /@babel/helper-validator-option@7.24.8: + '@babel/helper-validator-option@7.24.8': resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} engines: {node: '>=6.9.0'} - dev: true - /@babel/helpers@7.25.6: + '@babel/helpers@7.25.6': resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/template': 7.25.0 - '@babel/types': 7.25.6 - dev: true - /@babel/highlight@7.24.7: + '@babel/highlight@7.24.7': resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-validator-identifier': 7.24.7 - chalk: 2.4.2 - js-tokens: 4.0.0 - picocolors: 1.1.0 - /@babel/parser@7.25.6: + '@babel/parser@7.25.6': resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} engines: {node: '>=6.0.0'} hasBin: true - dependencies: - '@babel/types': 7.25.6 - /@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2): + '@babel/plugin-transform-react-jsx-self@7.24.7': resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - dev: true - /@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2): + '@babel/plugin-transform-react-jsx-source@7.24.7': resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-plugin-utils': 7.24.8 - dev: true - /@babel/runtime@7.25.6: + '@babel/runtime@7.25.6': resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} engines: {node: '>=6.9.0'} - dependencies: - regenerator-runtime: 0.14.1 - /@babel/template@7.25.0: + '@babel/template@7.25.0': resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/parser': 7.25.6 - '@babel/types': 7.25.6 - /@babel/traverse@7.25.6: + '@babel/traverse@7.25.6': resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/code-frame': 7.24.7 - '@babel/generator': 7.25.6 - '@babel/parser': 7.25.6 - '@babel/template': 7.25.0 - '@babel/types': 7.25.6 - debug: 4.3.7 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color - /@babel/types@7.25.6: + '@babel/types@7.25.6': resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} engines: {node: '>=6.9.0'} - dependencies: - '@babel/helper-string-parser': 7.24.8 - '@babel/helper-validator-identifier': 7.24.7 - to-fast-properties: 2.0.0 - /@changesets/apply-release-plan@7.0.5: + '@changesets/apply-release-plan@7.0.5': resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} - dependencies: - '@changesets/config': 3.0.3 - '@changesets/get-version-range-type': 0.4.0 - '@changesets/git': 3.0.1 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - detect-indent: 6.1.0 - fs-extra: 7.0.1 - lodash.startcase: 4.4.0 - outdent: 0.5.0 - prettier: 2.8.8 - resolve-from: 5.0.0 - semver: 7.6.3 - dev: true - /@changesets/assemble-release-plan@6.0.4: + '@changesets/assemble-release-plan@6.0.4': resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - semver: 7.6.3 - dev: true - /@changesets/changelog-git@0.2.0: + '@changesets/changelog-git@0.2.0': resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} - dependencies: - '@changesets/types': 6.0.0 - dev: true - /@changesets/cli@2.27.8: + '@changesets/cli@2.27.8': resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} hasBin: true - dependencies: - '@changesets/apply-release-plan': 7.0.5 - '@changesets/assemble-release-plan': 6.0.4 - '@changesets/changelog-git': 0.2.0 - '@changesets/config': 3.0.3 - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/get-release-plan': 4.0.4 - '@changesets/git': 3.0.1 - '@changesets/logger': 0.1.1 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.1 - '@changesets/should-skip-package': 0.1.1 - '@changesets/types': 6.0.0 - '@changesets/write': 0.3.2 - '@manypkg/get-packages': 1.1.3 - '@types/semver': 7.5.8 - ansi-colors: 4.1.3 - ci-info: 3.9.0 - enquirer: 2.4.1 - external-editor: 3.1.0 - fs-extra: 7.0.1 - mri: 1.2.0 - outdent: 0.5.0 - p-limit: 2.3.0 - package-manager-detector: 0.2.0 - picocolors: 1.1.0 - resolve-from: 5.0.0 - semver: 7.6.3 - spawndamnit: 2.0.0 - term-size: 2.2.1 - dev: true - /@changesets/config@3.0.3: + '@changesets/config@3.0.3': resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/get-dependents-graph': 2.1.2 - '@changesets/logger': 0.1.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - micromatch: 4.0.8 - dev: true - /@changesets/errors@0.2.0: + '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} - dependencies: - extendable-error: 0.1.7 - dev: true - /@changesets/get-dependents-graph@2.1.2: + '@changesets/get-dependents-graph@2.1.2': resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - picocolors: 1.1.0 - semver: 7.6.3 - dev: true - /@changesets/get-release-plan@4.0.4: + '@changesets/get-release-plan@4.0.4': resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} - dependencies: - '@changesets/assemble-release-plan': 6.0.4 - '@changesets/config': 3.0.3 - '@changesets/pre': 2.0.1 - '@changesets/read': 0.6.1 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/get-version-range-type@0.4.0: + '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: true - /@changesets/git@3.0.1: + '@changesets/git@3.0.1': resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} - dependencies: - '@changesets/errors': 0.2.0 - '@manypkg/get-packages': 1.1.3 - is-subdir: 1.2.0 - micromatch: 4.0.8 - spawndamnit: 2.0.0 - dev: true - /@changesets/logger@0.1.1: + '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - dependencies: - picocolors: 1.1.0 - dev: true - /@changesets/parse@0.4.0: + '@changesets/parse@0.4.0': resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} - dependencies: - '@changesets/types': 6.0.0 - js-yaml: 3.14.1 - dev: true - /@changesets/pre@2.0.1: + '@changesets/pre@2.0.1': resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} - dependencies: - '@changesets/errors': 0.2.0 - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - fs-extra: 7.0.1 - dev: true - /@changesets/read@0.6.1: + '@changesets/read@0.6.1': resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} - dependencies: - '@changesets/git': 3.0.1 - '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.0 - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - p-filter: 2.1.0 - picocolors: 1.1.0 - dev: true - /@changesets/should-skip-package@0.1.1: + '@changesets/should-skip-package@0.1.1': resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} - dependencies: - '@changesets/types': 6.0.0 - '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/types@4.1.0: + '@changesets/types@4.1.0': resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: true - /@changesets/types@6.0.0: + '@changesets/types@6.0.0': resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - dev: true - /@changesets/write@0.3.2: + '@changesets/write@0.3.2': resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} - dependencies: - '@changesets/types': 6.0.0 - fs-extra: 7.0.1 - human-id: 1.0.2 - prettier: 2.8.8 - dev: true - /@commitlint/cli@19.5.0(@types/node@20.16.5)(typescript@5.6.2): + '@codemirror/autocomplete@6.18.3': + resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==} + peerDependencies: + '@codemirror/language': ^6.0.0 + '@codemirror/state': ^6.0.0 + '@codemirror/view': ^6.0.0 + '@lezer/common': ^1.0.0 + + '@codemirror/commands@6.7.1': + resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==} + + '@codemirror/lang-java@6.0.1': + resolution: {integrity: sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==} + + '@codemirror/lang-javascript@6.2.2': + resolution: {integrity: sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==} + + '@codemirror/lang-json@6.0.1': + resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==} + + '@codemirror/lang-python@6.1.6': + resolution: {integrity: sha512-ai+01WfZhWqM92UqjnvorkxosZ2aq2u28kHvr+N3gu012XqY2CThD67JPMHnGceRfXPDBmn1HnyqowdpF57bNg==} + + '@codemirror/lang-yaml@6.1.2': + resolution: {integrity: sha512-dxrfG8w5Ce/QbT7YID7mWZFKhdhsaTNOYjOkSIMt1qmC4VQnXSDSYVHHHn8k6kJUfIhtLo8t1JJgltlxWdsITw==} + + '@codemirror/language@6.10.6': + resolution: {integrity: sha512-KrsbdCnxEztLVbB5PycWXFxas4EOyk/fPAfruSOnDDppevQgid2XZ+KbJ9u+fDikP/e7MW7HPBTvTb8JlZK9vA==} + + '@codemirror/legacy-modes@6.4.2': + resolution: {integrity: sha512-HsvWu08gOIIk303eZQCal4H4t65O/qp1V4ul4zVa3MHK5FJ0gz3qz3O55FIkm+aQUcshUOjBx38t2hPiJwW5/g==} + + '@codemirror/lint@6.8.4': + resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==} + + '@codemirror/search@6.5.8': + resolution: {integrity: sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==} + + '@codemirror/state@6.5.0': + resolution: {integrity: sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==} + + '@codemirror/theme-one-dark@6.1.2': + resolution: {integrity: sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==} + + '@codemirror/view@6.35.3': + resolution: {integrity: sha512-ScY7L8+EGdPl4QtoBiOzE4FELp7JmNUsBvgBcCakXWM2uiv/K89VAzU3BMDscf0DsACLvTKePbd5+cFDTcei6g==} + + '@commitlint/cli@19.5.0': resolution: {integrity: sha512-gaGqSliGwB86MDmAAKAtV9SV1SHdmN8pnGq4EJU4+hLisQ7IFfx4jvU4s+pk6tl0+9bv6yT+CaZkufOinkSJIQ==} engines: {node: '>=v18'} hasBin: true - dependencies: - '@commitlint/format': 19.5.0 - '@commitlint/lint': 19.5.0 - '@commitlint/load': 19.5.0(@types/node@20.16.5)(typescript@5.6.2) - '@commitlint/read': 19.5.0 - '@commitlint/types': 19.5.0 - tinyexec: 0.3.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - typescript - dev: true - /@commitlint/config-conventional@19.4.1: + '@commitlint/config-conventional@19.4.1': resolution: {integrity: sha512-D5S5T7ilI5roybWGc8X35OBlRXLAwuTseH1ro0XgqkOWrhZU8yOwBOslrNmSDlTXhXLq8cnfhQyC42qaUCzlXA==} engines: {node: '>=v18'} - dependencies: - '@commitlint/types': 19.5.0 - conventional-changelog-conventionalcommits: 7.0.2 - /@commitlint/config-validator@19.5.0: + '@commitlint/config-validator@19.5.0': resolution: {integrity: sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==} engines: {node: '>=v18'} - dependencies: - '@commitlint/types': 19.5.0 - ajv: 8.17.1 - dev: true - /@commitlint/ensure@19.5.0: + '@commitlint/ensure@19.5.0': resolution: {integrity: sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==} engines: {node: '>=v18'} - dependencies: - '@commitlint/types': 19.5.0 - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.snakecase: 4.1.1 - lodash.startcase: 4.4.0 - lodash.upperfirst: 4.3.1 - dev: true - /@commitlint/execute-rule@19.5.0: + '@commitlint/execute-rule@19.5.0': resolution: {integrity: sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==} engines: {node: '>=v18'} - dev: true - /@commitlint/format@19.5.0: + '@commitlint/format@19.5.0': resolution: {integrity: sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==} engines: {node: '>=v18'} - dependencies: - '@commitlint/types': 19.5.0 - chalk: 5.3.0 - dev: true - /@commitlint/is-ignored@19.5.0: + '@commitlint/is-ignored@19.5.0': resolution: {integrity: sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w==} engines: {node: '>=v18'} - dependencies: - '@commitlint/types': 19.5.0 - semver: 7.6.3 - dev: true - /@commitlint/lint@19.5.0: + '@commitlint/lint@19.5.0': resolution: {integrity: sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg==} engines: {node: '>=v18'} - dependencies: - '@commitlint/is-ignored': 19.5.0 - '@commitlint/parse': 19.5.0 - '@commitlint/rules': 19.5.0 - '@commitlint/types': 19.5.0 - dev: true - /@commitlint/load@19.5.0(@types/node@20.16.5)(typescript@5.6.2): + '@commitlint/load@19.5.0': resolution: {integrity: sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==} engines: {node: '>=v18'} - dependencies: - '@commitlint/config-validator': 19.5.0 - '@commitlint/execute-rule': 19.5.0 - '@commitlint/resolve-extends': 19.5.0 - '@commitlint/types': 19.5.0 - chalk: 5.3.0 - cosmiconfig: 9.0.0(typescript@5.6.2) - cosmiconfig-typescript-loader: 5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0)(typescript@5.6.2) - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 - lodash.uniq: 4.5.0 - transitivePeerDependencies: - - '@types/node' - - typescript - dev: true - /@commitlint/message@19.5.0: + '@commitlint/message@19.5.0': resolution: {integrity: sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==} engines: {node: '>=v18'} - dev: true - /@commitlint/parse@19.5.0: + '@commitlint/parse@19.5.0': resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} engines: {node: '>=v18'} - dependencies: - '@commitlint/types': 19.5.0 - conventional-changelog-angular: 7.0.0 - conventional-commits-parser: 5.0.0 - dev: true - /@commitlint/read@19.5.0: + '@commitlint/read@19.5.0': resolution: {integrity: sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==} engines: {node: '>=v18'} - dependencies: - '@commitlint/top-level': 19.5.0 - '@commitlint/types': 19.5.0 - git-raw-commits: 4.0.0 - minimist: 1.2.8 - tinyexec: 0.3.0 - dev: true - /@commitlint/resolve-extends@19.5.0: + '@commitlint/resolve-extends@19.5.0': resolution: {integrity: sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==} engines: {node: '>=v18'} - dependencies: - '@commitlint/config-validator': 19.5.0 - '@commitlint/types': 19.5.0 - global-directory: 4.0.1 - import-meta-resolve: 4.1.0 - lodash.mergewith: 4.6.2 - resolve-from: 5.0.0 - dev: true - /@commitlint/rules@19.5.0: + '@commitlint/rules@19.5.0': resolution: {integrity: sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw==} engines: {node: '>=v18'} - dependencies: - '@commitlint/ensure': 19.5.0 - '@commitlint/message': 19.5.0 - '@commitlint/to-lines': 19.5.0 - '@commitlint/types': 19.5.0 - dev: true - /@commitlint/to-lines@19.5.0: + '@commitlint/to-lines@19.5.0': resolution: {integrity: sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==} engines: {node: '>=v18'} - dev: true - /@commitlint/top-level@19.5.0: + '@commitlint/top-level@19.5.0': resolution: {integrity: sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==} engines: {node: '>=v18'} - dependencies: - find-up: 7.0.0 - dev: true - /@commitlint/types@19.5.0: + '@commitlint/types@19.5.0': resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} engines: {node: '>=v18'} - dependencies: - '@types/conventional-commits-parser': 5.0.0 - chalk: 5.3.0 - /@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1): + '@csstools/css-parser-algorithms@2.7.1': resolution: {integrity: sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==} engines: {node: ^14 || ^16 || >=18} peerDependencies: '@csstools/css-tokenizer': ^2.4.1 - dependencies: - '@csstools/css-tokenizer': 2.4.1 - /@csstools/css-tokenizer@2.4.1: + '@csstools/css-tokenizer@2.4.1': resolution: {integrity: sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==} engines: {node: ^14 || ^16 || >=18} - /@csstools/media-query-list-parser@2.1.13(@csstools/css-parser-algorithms@2.7.1)(@csstools/css-tokenizer@2.4.1): + '@csstools/media-query-list-parser@2.1.13': resolution: {integrity: sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==} engines: {node: ^14 || ^16 || >=18} peerDependencies: '@csstools/css-parser-algorithms': ^2.7.1 '@csstools/css-tokenizer': ^2.4.1 - dependencies: - '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) - '@csstools/css-tokenizer': 2.4.1 - /@csstools/selector-specificity@3.1.1(postcss-selector-parser@6.1.2): + '@csstools/selector-specificity@3.1.1': resolution: {integrity: sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss-selector-parser: ^6.0.13 - dependencies: - postcss-selector-parser: 6.1.2 - /@emotion/babel-plugin@11.12.0: + '@emotion/babel-plugin@11.12.0': resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} - dependencies: - '@babel/helper-module-imports': 7.24.7 - '@babel/runtime': 7.25.6 - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/serialize': 1.3.1 - babel-plugin-macros: 3.1.0 - convert-source-map: 1.9.0 - escape-string-regexp: 4.0.0 - find-root: 1.1.0 - source-map: 0.5.7 - stylis: 4.2.0 - transitivePeerDependencies: - - supports-color - dev: false - /@emotion/cache@11.13.1: + '@emotion/cache@11.13.1': resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} - dependencies: - '@emotion/memoize': 0.9.0 - '@emotion/sheet': 1.4.0 - '@emotion/utils': 1.4.0 - '@emotion/weak-memoize': 0.4.0 - stylis: 4.2.0 - dev: false - /@emotion/hash@0.9.2: + '@emotion/hash@0.9.2': resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - dev: false - /@emotion/is-prop-valid@1.3.0: + '@emotion/is-prop-valid@1.3.0': resolution: {integrity: sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==} - dependencies: - '@emotion/memoize': 0.9.0 - dev: false - /@emotion/memoize@0.9.0: + '@emotion/memoize@0.9.0': resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} - dev: false - /@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1): + '@emotion/react@11.13.3': resolution: {integrity: sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==} peerDependencies: '@types/react': '*' @@ -1135,36 +858,14 @@ packages: peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/babel-plugin': 11.12.0 - '@emotion/cache': 11.13.1 - '@emotion/serialize': 1.3.1 - '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) - '@emotion/utils': 1.4.0 - '@emotion/weak-memoize': 0.4.0 - '@types/react': 18.3.5 - hoist-non-react-statics: 3.3.2 - react: 18.3.1 - transitivePeerDependencies: - - supports-color - dev: false - /@emotion/serialize@1.3.1: + '@emotion/serialize@1.3.1': resolution: {integrity: sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==} - dependencies: - '@emotion/hash': 0.9.2 - '@emotion/memoize': 0.9.0 - '@emotion/unitless': 0.10.0 - '@emotion/utils': 1.4.0 - csstype: 3.1.3 - dev: false - /@emotion/sheet@1.4.0: + '@emotion/sheet@1.4.0': resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} - dev: false - /@emotion/styled@11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1): + '@emotion/styled@11.13.0': resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==} peerDependencies: '@emotion/react': ^11.0.0-rc.0 @@ -1173,370 +874,271 @@ packages: peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/babel-plugin': 11.12.0 - '@emotion/is-prop-valid': 1.3.0 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/serialize': 1.3.1 - '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) - '@emotion/utils': 1.4.0 - '@types/react': 18.3.5 - react: 18.3.1 - transitivePeerDependencies: - - supports-color - dev: false - /@emotion/unitless@0.10.0: + '@emotion/unitless@0.10.0': resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} - dev: false - /@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1): + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} peerDependencies: react: '>=16.8.0' - dependencies: - react: 18.3.1 - dev: false - /@emotion/utils@1.4.0: + '@emotion/utils@1.4.0': resolution: {integrity: sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==} - dev: false - /@emotion/weak-memoize@0.4.0: + '@emotion/weak-memoize@0.4.0': resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - dev: false - /@esbuild/aix-ppc64@0.23.1: + '@esbuild/aix-ppc64@0.23.1': resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - requiresBuild: true - optional: true - /@esbuild/android-arm64@0.23.1: + '@esbuild/android-arm64@0.23.1': resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} engines: {node: '>=18'} cpu: [arm64] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-arm@0.23.1: + '@esbuild/android-arm@0.23.1': resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - requiresBuild: true - optional: true - /@esbuild/android-x64@0.23.1: + '@esbuild/android-x64@0.23.1': resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} engines: {node: '>=18'} cpu: [x64] os: [android] - requiresBuild: true - optional: true - /@esbuild/darwin-arm64@0.23.1: + '@esbuild/darwin-arm64@0.23.1': resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - requiresBuild: true - optional: true - /@esbuild/darwin-x64@0.23.1: + '@esbuild/darwin-x64@0.23.1': resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - requiresBuild: true - optional: true - /@esbuild/freebsd-arm64@0.23.1: + '@esbuild/freebsd-arm64@0.23.1': resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - requiresBuild: true - optional: true - /@esbuild/freebsd-x64@0.23.1: + '@esbuild/freebsd-x64@0.23.1': resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - requiresBuild: true - optional: true - /@esbuild/linux-arm64@0.23.1: + '@esbuild/linux-arm64@0.23.1': resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-arm@0.23.1: + '@esbuild/linux-arm@0.23.1': resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-ia32@0.23.1: + '@esbuild/linux-ia32@0.23.1': resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-loong64@0.23.1: + '@esbuild/linux-loong64@0.23.1': resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-mips64el@0.23.1: + '@esbuild/linux-mips64el@0.23.1': resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-ppc64@0.23.1: + '@esbuild/linux-ppc64@0.23.1': resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-riscv64@0.23.1: + '@esbuild/linux-riscv64@0.23.1': resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-s390x@0.23.1: + '@esbuild/linux-s390x@0.23.1': resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - requiresBuild: true - optional: true - /@esbuild/linux-x64@0.23.1: + '@esbuild/linux-x64@0.23.1': resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} engines: {node: '>=18'} cpu: [x64] os: [linux] - requiresBuild: true - optional: true - /@esbuild/netbsd-x64@0.23.1: + '@esbuild/netbsd-x64@0.23.1': resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - requiresBuild: true - optional: true - /@esbuild/openbsd-arm64@0.23.1: + '@esbuild/openbsd-arm64@0.23.1': resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - requiresBuild: true - optional: true - /@esbuild/openbsd-x64@0.23.1: + '@esbuild/openbsd-x64@0.23.1': resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - requiresBuild: true - optional: true - /@esbuild/sunos-x64@0.23.1: + '@esbuild/sunos-x64@0.23.1': resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - requiresBuild: true - optional: true - /@esbuild/win32-arm64@0.23.1: + '@esbuild/win32-arm64@0.23.1': resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-ia32@0.23.1: + '@esbuild/win32-ia32@0.23.1': resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - requiresBuild: true - optional: true - /@esbuild/win32-x64@0.23.1: + '@esbuild/win32-x64@0.23.1': resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} engines: {node: '>=18'} cpu: [x64] os: [win32] - requiresBuild: true - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): + '@eslint-community/eslint-utils@4.4.0': resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.57.0 - eslint-visitor-keys: 3.4.3 - /@eslint-community/regexpp@4.11.0: + '@eslint-community/regexpp@4.11.0': resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - /@eslint/eslintrc@2.1.4: + '@eslint/eslintrc@2.1.4': resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - dependencies: - ajv: 6.12.6 - debug: 4.3.7 - espree: 9.6.1 - globals: 13.24.0 - ignore: 5.3.2 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - /@eslint/js@8.57.0: + '@eslint/js@8.57.0': resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - /@formatjs/intl-unified-numberformat@3.3.7: + '@formatjs/intl-unified-numberformat@3.3.7': resolution: {integrity: sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==} deprecated: We have renamed the package to @formatjs/intl-numberformat - dependencies: - '@formatjs/intl-utils': 2.3.0 - dev: false - /@formatjs/intl-utils@2.3.0: + '@formatjs/intl-utils@2.3.0': resolution: {integrity: sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==} deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package - dev: false - /@humanwhocodes/config-array@0.11.14: + '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} deprecated: Use @eslint/config-array instead - dependencies: - '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.7 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - /@humanwhocodes/module-importer@1.0.1: + '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - /@humanwhocodes/object-schema@2.0.3: + '@humanwhocodes/object-schema@2.0.3': resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} deprecated: Use @eslint/object-schema instead - /@icons/material@0.2.4(react@18.3.1): + '@icons/material@0.2.4': resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} peerDependencies: react: '*' - dependencies: - react: 18.3.1 - dev: true - /@isaacs/cliui@8.0.2: + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - dependencies: - string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true - /@jridgewell/gen-mapping@0.3.5: + '@jridgewell/gen-mapping@0.3.5': resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} engines: {node: '>=6.0.0'} - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.2: + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - /@jridgewell/set-array@1.2.1: + '@jridgewell/set-array@1.2.1': resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - /@jridgewell/sourcemap-codec@1.5.0: + '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - /@jridgewell/trace-mapping@0.3.25: + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - /@kurkle/color@0.3.2: + '@kurkle/color@0.3.2': resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} - dev: false - /@manypkg/find-root@1.1.0: + '@lezer/common@1.2.3': + resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + + '@lezer/highlight@1.2.1': + resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + + '@lezer/java@1.1.3': + resolution: {integrity: sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==} + + '@lezer/javascript@1.4.21': + resolution: {integrity: sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==} + + '@lezer/json@1.0.2': + resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + + '@lezer/python@1.1.15': + resolution: {integrity: sha512-aVQ43m2zk4FZYedCqL0KHPEUsqZOrmAvRhkhHlVPnDD1HODDyyQv5BRIuod4DadkgBEZd53vQOtXTonNbEgjrQ==} + + '@lezer/yaml@1.0.3': + resolution: {integrity: sha512-GuBLekbw9jDBDhGur82nuwkxKQ+a3W5H0GfaAthDXcAu+XdpS43VlnxA9E9hllkpSP5ellRDKjLLj7Lu9Wr6xA==} + + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} - dependencies: - '@babel/runtime': 7.25.6 - '@types/node': 12.20.55 - find-up: 4.1.0 - fs-extra: 8.1.0 - dev: true - /@manypkg/get-packages@1.1.3: + '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - dependencies: - '@babel/runtime': 7.25.6 - '@changesets/types': 4.1.0 - '@manypkg/find-root': 1.1.0 - fs-extra: 8.1.0 - globby: 11.1.0 - read-yaml-file: 1.1.0 - dev: true - /@mui/core-downloads-tracker@6.1.0: + '@marijn/find-cluster-break@1.0.2': + resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==} + + '@mui/core-downloads-tracker@6.1.0': resolution: {integrity: sha512-covEnIn/2er5YdtuukDRA52kmARhKrHjOvPsyTFMQApZdrTBI4h8jbEy2mxZqwMwcAFS9coonQXnEZKL1rUNdQ==} - dev: false - /@mui/icons-material@6.0.2(@mui/material@6.1.0)(@types/react@18.3.5)(react@18.3.1): + '@mui/icons-material@6.0.2': resolution: {integrity: sha512-WaTPSvKcx8X7NdWAHzJWDZv+YXvK0MUY8+JI/r4/q2GgIa5RW+n4+08CGX6jB7sWhU1R3zy28NfsDUwwQjOThw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1546,14 +1148,8 @@ packages: peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.5 - react: 18.3.1 - dev: false - /@mui/material@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1): + '@mui/material@6.1.0': resolution: {integrity: sha512-4MJ46vmy1xbm8x+ZdRcWm8jEMMowdS8pYlhKQzg/qoKhOcLhImZvf2Jn6z9Dj6gl+lY+C/0MxaHF/avAAGys3Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1572,27 +1168,8 @@ packages: optional: true '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - '@mui/core-downloads-tracker': 6.1.0 - '@mui/system': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1) - '@mui/types': 7.2.16(@types/react@18.3.5) - '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@popperjs/core': 2.11.8 - '@types/react': 18.3.5 - '@types/react-transition-group': 4.4.11 - clsx: 2.1.1 - csstype: 3.1.3 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-is: 18.3.1 - react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) - dev: false - /@mui/private-theming@6.1.0(@types/react@18.3.5)(react@18.3.1): + '@mui/private-theming@6.1.0': resolution: {integrity: sha512-+L5qccs4gwsR0r1dgjqhN24QEQRkqIbfOdxILyMbMkuI50x6wNyt9XrV+J3WtjtZTMGJCrUa5VmZBE6OEPGPWA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1601,15 +1178,8 @@ packages: peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@types/react': 18.3.5 - prop-types: 15.8.1 - react: 18.3.1 - dev: false - /@mui/styled-engine@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1): + '@mui/styled-engine@6.1.0': resolution: {integrity: sha512-MZ+vtaCkjamrT41+b0Er9OMenjAtP/32+L6fARL9/+BZKuV2QbR3q3TmavT2x0NhDu35IM03s4yKqj32Ziqnyg==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1621,18 +1191,8 @@ packages: optional: true '@emotion/styled': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/cache': 11.13.1 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/sheet': 1.4.0 - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - csstype: 3.1.3 - prop-types: 15.8.1 - react: 18.3.1 - dev: false - /@mui/system@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1): + '@mui/system@6.1.0': resolution: {integrity: sha512-NumkGDqT6EdXfcoFLYQ+M4XlTW5hH3+aK48xAbRqKPXJfxl36CBt4DLduw/Voa5dcayGus9T6jm1AwU2hoJ5hQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1647,33 +1207,16 @@ packages: optional: true '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - '@mui/private-theming': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@mui/styled-engine': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1) - '@mui/types': 7.2.16(@types/react@18.3.5) - '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@types/react': 18.3.5 - clsx: 2.1.1 - csstype: 3.1.3 - prop-types: 15.8.1 - react: 18.3.1 - dev: false - /@mui/types@7.2.16(@types/react@18.3.5): + '@mui/types@7.2.16': resolution: {integrity: sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==} peerDependencies: '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@types/react': 18.3.5 - dev: false - /@mui/utils@5.16.6(@types/react@18.3.5)(react@18.3.1): + '@mui/utils@5.16.6': resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==} engines: {node: '>=12.0.0'} peerDependencies: @@ -1682,18 +1225,8 @@ packages: peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@mui/types': 7.2.16(@types/react@18.3.5) - '@types/prop-types': 15.7.12 - '@types/react': 18.3.5 - clsx: 2.1.1 - prop-types: 15.8.1 - react: 18.3.1 - react-is: 18.3.1 - dev: false - /@mui/utils@6.1.0(@types/react@18.3.5)(react@18.3.1): + '@mui/utils@6.1.0': resolution: {integrity: sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1702,18 +1235,8 @@ packages: peerDependenciesMeta: '@types/react': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@mui/types': 7.2.16(@types/react@18.3.5) - '@types/prop-types': 15.7.12 - '@types/react': 18.3.5 - clsx: 2.1.1 - prop-types: 15.8.1 - react: 18.3.1 - react-is: 18.3.1 - dev: false - /@mui/x-data-grid@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1): + '@mui/x-data-grid@7.18.0': resolution: {integrity: sha512-41UjJbRxWk+Yk/lfvaO55Pwo5p+F5s3rOTiHLl53ikCT5GuJ5OCCvik0Bi3c6DzTuUBdrEucae2618rydc2DGw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1728,24 +1251,8 @@ packages: optional: true '@emotion/styled': optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) - '@mui/system': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1) - '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) - '@mui/x-internals': 7.18.0(@types/react@18.3.5)(react@18.3.1) - clsx: 2.1.1 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - reselect: 5.1.1 - transitivePeerDependencies: - - '@types/react' - dev: false - /@mui/x-date-pickers@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1): + '@mui/x-date-pickers@7.18.0': resolution: {integrity: sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1781,96 +1288,54 @@ packages: optional: true moment-jalaali: optional: true - dependencies: - '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) - '@mui/system': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1) - '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) - '@mui/x-internals': 7.18.0(@types/react@18.3.5)(react@18.3.1) - '@types/react-transition-group': 4.4.11 - clsx: 2.1.1 - date-fns: 4.1.0 - dayjs: 1.11.13 - prop-types: 15.8.1 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - dev: false - /@mui/x-internals@7.18.0(@types/react@18.3.5)(react@18.3.1): + '@mui/x-internals@7.18.0': resolution: {integrity: sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==} engines: {node: '>=14.0.0'} peerDependencies: react: ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.25.6 - '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) - react: 18.3.1 - transitivePeerDependencies: - - '@types/react' - dev: false - /@nodelib/fs.scandir@2.1.5: + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.stat': 2.0.5 - run-parallel: 1.2.0 - /@nodelib/fs.stat@2.0.5: + '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} - /@nodelib/fs.walk@1.2.8: + '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - dependencies: - '@nodelib/fs.scandir': 2.1.5 - fastq: 1.17.1 - /@nolyfill/is-core-module@1.0.39: + '@nolyfill/is-core-module@1.0.39': resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - dev: false - /@pkgjs/parseargs@0.11.0: + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - requiresBuild: true - dev: true - optional: true - /@pkgr/core@0.1.1: + '@pkgr/core@0.1.1': resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dev: false - /@popperjs/core@2.11.8: + '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - dev: false - /@react-dnd/asap@5.0.2: + '@react-dnd/asap@5.0.2': resolution: {integrity: sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==} - dev: false - /@react-dnd/invariant@4.0.2: + '@react-dnd/invariant@4.0.2': resolution: {integrity: sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==} - dev: false - /@react-dnd/shallowequal@4.0.2: + '@react-dnd/shallowequal@4.0.2': resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==} - dev: false - /@remix-run/router@1.19.1: + '@remix-run/router@1.19.1': resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==} engines: {node: '>=14.0.0'} - dev: false - /@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(rollup@4.24.0): + '@rollup/plugin-babel@6.0.4': resolution: {integrity: sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1882,16 +1347,8 @@ packages: optional: true rollup: optional: true - dependencies: - '@babel/core': 7.25.2 - '@babel/helper-module-imports': 7.24.7 - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - rollup: 4.24.0 - transitivePeerDependencies: - - supports-color - dev: true - /@rollup/plugin-commonjs@28.0.1(rollup@4.24.0): + '@rollup/plugin-commonjs@28.0.1': resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: @@ -1899,18 +1356,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - commondir: 1.0.1 - estree-walker: 2.0.2 - fdir: 6.4.2(picomatch@4.0.2) - is-reference: 1.2.1 - magic-string: 0.30.11 - picomatch: 4.0.2 - rollup: 4.24.0 - dev: true - /@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.24.0): + '@rollup/plugin-dynamic-import-vars@2.1.2': resolution: {integrity: sha512-4lr2oXxs9hcxtGGaK8s0i9evfjzDrAs7ngw28TqruWKTEm0+U4Eljb+F6HXGYdFv8xRojQlrQwV7M/yxeh3yzQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1918,16 +1365,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - astring: 1.9.0 - estree-walker: 2.0.2 - fast-glob: 3.3.2 - magic-string: 0.30.11 - rollup: 4.24.0 - dev: true - /@rollup/plugin-inject@5.0.5: + '@rollup/plugin-inject@5.0.5': resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1935,13 +1374,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - estree-walker: 2.0.2 - magic-string: 0.30.11 - dev: true - /@rollup/plugin-json@6.1.0(rollup@4.24.0): + '@rollup/plugin-json@6.1.0': resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1949,12 +1383,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - rollup: 4.24.0 - dev: true - /@rollup/plugin-node-resolve@15.2.3(rollup@4.24.0): + '@rollup/plugin-node-resolve@15.2.3': resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1962,17 +1392,8 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - '@types/resolve': 1.20.2 - deepmerge: 4.3.1 - is-builtin-module: 3.2.1 - is-module: 1.0.0 - resolve: 1.22.8 - rollup: 4.24.0 - dev: true - /@rollup/plugin-typescript@12.1.1(rollup@4.24.0)(typescript@5.6.2): + '@rollup/plugin-typescript@12.1.1': resolution: {integrity: sha512-t7O653DpfB5MbFrqPe/VcKFFkvRuFNp9qId3xq4Eth5xlyymzxNpye2z8Hrl0RIMuXTSr5GGcFpkdlMeacUiFQ==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1984,14 +1405,8 @@ packages: optional: true tslib: optional: true - dependencies: - '@rollup/pluginutils': 5.1.2(rollup@4.24.0) - resolve: 1.22.8 - rollup: 4.24.0 - typescript: 5.6.2 - dev: true - /@rollup/pluginutils@5.1.2(rollup@4.24.0): + '@rollup/pluginutils@5.1.2': resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} engines: {node: '>=14.0.0'} peerDependencies: @@ -1999,338 +1414,217 @@ packages: peerDependenciesMeta: rollup: optional: true - dependencies: - '@types/estree': 1.0.6 - estree-walker: 2.0.2 - picomatch: 2.3.1 - rollup: 4.24.0 - /@rollup/rollup-android-arm-eabi@4.24.0: + '@rollup/rollup-android-arm-eabi@4.24.0': resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] os: [android] - requiresBuild: true - optional: true - /@rollup/rollup-android-arm64@4.24.0: + '@rollup/rollup-android-arm64@4.24.0': resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} cpu: [arm64] os: [android] - requiresBuild: true - optional: true - /@rollup/rollup-darwin-arm64@4.24.0: + '@rollup/rollup-darwin-arm64@4.24.0': resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} cpu: [arm64] os: [darwin] - requiresBuild: true - optional: true - /@rollup/rollup-darwin-x64@4.24.0: + '@rollup/rollup-darwin-x64@4.24.0': resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} cpu: [x64] os: [darwin] - requiresBuild: true - optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.24.0: + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] libc: [glibc] - requiresBuild: true - optional: true - /@rollup/rollup-linux-arm-musleabihf@4.24.0: + '@rollup/rollup-linux-arm-musleabihf@4.24.0': resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] libc: [musl] - requiresBuild: true - optional: true - /@rollup/rollup-linux-arm64-gnu@4.24.0: + '@rollup/rollup-linux-arm64-gnu@4.24.0': resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] libc: [glibc] - requiresBuild: true - optional: true - /@rollup/rollup-linux-arm64-musl@4.24.0: + '@rollup/rollup-linux-arm64-musl@4.24.0': resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] libc: [musl] - requiresBuild: true - optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.24.0: + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] libc: [glibc] - requiresBuild: true - optional: true - /@rollup/rollup-linux-riscv64-gnu@4.24.0: + '@rollup/rollup-linux-riscv64-gnu@4.24.0': resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] libc: [glibc] - requiresBuild: true - optional: true - /@rollup/rollup-linux-s390x-gnu@4.24.0: + '@rollup/rollup-linux-s390x-gnu@4.24.0': resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] libc: [glibc] - requiresBuild: true - optional: true - /@rollup/rollup-linux-x64-gnu@4.24.0: + '@rollup/rollup-linux-x64-gnu@4.24.0': resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] libc: [glibc] - requiresBuild: true - optional: true - /@rollup/rollup-linux-x64-musl@4.24.0: + '@rollup/rollup-linux-x64-musl@4.24.0': resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] libc: [musl] - requiresBuild: true - optional: true - /@rollup/rollup-win32-arm64-msvc@4.24.0: + '@rollup/rollup-win32-arm64-msvc@4.24.0': resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} cpu: [arm64] os: [win32] - requiresBuild: true - optional: true - /@rollup/rollup-win32-ia32-msvc@4.24.0: + '@rollup/rollup-win32-ia32-msvc@4.24.0': resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} cpu: [ia32] os: [win32] - requiresBuild: true - optional: true - /@rollup/rollup-win32-x64-msvc@4.24.0: + '@rollup/rollup-win32-x64-msvc@4.24.0': resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} cpu: [x64] os: [win32] - requiresBuild: true - optional: true - /@rtsao/scc@1.1.0: + '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - dev: false - /@rushstack/eslint-patch@1.10.4: + '@rushstack/eslint-patch@1.10.4': resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} - dev: false - /@types/babel__core@7.20.5: + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - dependencies: - '@babel/parser': 7.25.6 - '@babel/types': 7.25.6 - '@types/babel__generator': 7.6.8 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 - dev: true - /@types/babel__generator@7.6.8: + '@types/babel__generator@7.6.8': resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} - dependencies: - '@babel/types': 7.25.6 - dev: true - /@types/babel__template@7.4.4: + '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - dependencies: - '@babel/parser': 7.25.6 - '@babel/types': 7.25.6 - dev: true - /@types/babel__traverse@7.20.6: + '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} - dependencies: - '@babel/types': 7.25.6 - dev: true - /@types/conventional-commits-parser@5.0.0: + '@types/conventional-commits-parser@5.0.0': resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} - dependencies: - '@types/node': 20.16.5 - /@types/d3-color@3.1.3: + '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} - dev: false - /@types/d3-drag@3.0.7: + '@types/d3-drag@3.0.7': resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} - dependencies: - '@types/d3-selection': 3.0.11 - dev: false - /@types/d3-interpolate@3.0.4: + '@types/d3-interpolate@3.0.4': resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} - dependencies: - '@types/d3-color': 3.1.3 - dev: false - /@types/d3-selection@3.0.11: + '@types/d3-selection@3.0.11': resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} - dev: false - /@types/d3-transition@3.0.9: + '@types/d3-transition@3.0.9': resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} - dependencies: - '@types/d3-selection': 3.0.11 - dev: false - /@types/d3-zoom@3.0.8: + '@types/d3-zoom@3.0.8': resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} - dependencies: - '@types/d3-interpolate': 3.0.4 - '@types/d3-selection': 3.0.11 - dev: false - /@types/estree@1.0.6: + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} - /@types/fs-extra@9.0.13: + '@types/fs-extra@9.0.13': resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} - dependencies: - '@types/node': 20.16.5 - dev: true - /@types/hammerjs@2.0.46: + '@types/hammerjs@2.0.46': resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} - dev: false - /@types/inquirer@8.2.10: + '@types/inquirer@8.2.10': resolution: {integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==} - dependencies: - '@types/through': 0.0.33 - rxjs: 7.8.1 - dev: true - /@types/json5@0.0.29: + '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: false - /@types/less@3.0.6: + '@types/less@3.0.6': resolution: {integrity: sha512-PecSzorDGdabF57OBeQO/xFbAkYWo88g4Xvnsx7LRwqLC17I7OoKtA3bQB9uXkY6UkMWCOsA8HSVpaoitscdXw==} - dev: true - /@types/lodash-es@4.17.12: + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} - dependencies: - '@types/lodash': 4.17.7 - dev: true - /@types/lodash@4.17.7: + '@types/lodash@4.17.7': resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} - dev: true - /@types/minimist@1.2.5: + '@types/minimist@1.2.5': resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} - /@types/node@10.17.60: + '@types/node@10.17.60': resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - dev: true - /@types/node@12.20.55: + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: true - /@types/node@20.16.5: + '@types/node@20.16.5': resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==} - dependencies: - undici-types: 6.19.8 - /@types/normalize-package-data@2.4.4: + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - /@types/parse-json@4.0.2: + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - dev: false - /@types/prop-types@15.7.12: + '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} - /@types/qs@6.9.15: + '@types/qs@6.9.15': resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - dev: true - /@types/react-color@3.0.12: + '@types/react-color@3.0.12': resolution: {integrity: sha512-pr3uKE3lSvf7GFo1Rn2K3QktiZQFFrSgSGJ/3iMvSOYWt2pPAJ97rVdVfhWxYJZ8prAEXzoP2XX//3qGSQgu7Q==} - dependencies: - '@types/react': 18.3.5 - '@types/reactcss': 1.2.12 - dev: true - /@types/react-dom@18.3.0: + '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} - dependencies: - '@types/react': 18.3.5 - dev: true - /@types/react-grid-layout@1.3.5: + '@types/react-grid-layout@1.3.5': resolution: {integrity: sha512-WH/po1gcEcoR6y857yAnPGug+ZhkF4PaTUxgAbwfeSH/QOgVSakKHBXoPGad/sEznmkiaK3pqHk+etdWisoeBQ==} - dependencies: - '@types/react': 18.3.5 - dev: true - /@types/react-resizable@3.0.8: + '@types/react-resizable@3.0.8': resolution: {integrity: sha512-Pcvt2eGA7KNXldt1hkhVhAgZ8hK41m0mp89mFgQi7LAAEZiaLgm4fHJ5zbJZ/4m2LVaAyYrrRRv1LHDcrGQanA==} - dependencies: - '@types/react': 18.3.5 - dev: true - /@types/react-transition-group@4.4.11: + '@types/react-transition-group@4.4.11': resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} - dependencies: - '@types/react': 18.3.5 - dev: false - /@types/react@18.3.5: + '@types/react@18.3.5': resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} - dependencies: - '@types/prop-types': 15.7.12 - csstype: 3.1.3 - /@types/reactcss@1.2.12: + '@types/reactcss@1.2.12': resolution: {integrity: sha512-BrXUQ86/wbbFiZv8h/Q1/Q1XOsaHneYmCb/tHe9+M8XBAAUc2EHfdY0DY22ZZjVSaXr5ix7j+zsqO2eGZub8lQ==} - dependencies: - '@types/react': 18.3.5 - dev: true - /@types/resolve@1.20.2: + '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - dev: true - /@types/semver@7.5.8: + '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true - /@types/through@0.0.33: + '@types/through@0.0.33': resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} - dependencies: - '@types/node': 20.16.5 - dev: true - /@types/validator@13.12.2: + '@types/validator@13.12.2': resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} - dev: true - /@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0)(eslint@8.57.0)(typescript@5.6.2): + '@typescript-eslint/eslint-plugin@8.5.0': resolution: {integrity: sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -2340,24 +1634,8 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@eslint-community/regexpp': 4.11.0 - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/scope-manager': 8.5.0 - '@typescript-eslint/type-utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - '@typescript-eslint/visitor-keys': 8.5.0 - eslint: 8.57.0 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.6.2) - typescript: 5.6.2 - transitivePeerDependencies: - - supports-color - dev: false - /@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2): + '@typescript-eslint/parser@8.5.0': resolution: {integrity: sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -2366,27 +1644,12 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/scope-manager': 8.5.0 - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) - '@typescript-eslint/visitor-keys': 8.5.0 - debug: 4.3.7 - eslint: 8.57.0 - typescript: 5.6.2 - transitivePeerDependencies: - - supports-color - dev: false - /@typescript-eslint/scope-manager@8.5.0: + '@typescript-eslint/scope-manager@8.5.0': resolution: {integrity: sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/visitor-keys': 8.5.0 - dev: false - /@typescript-eslint/type-utils@8.5.0(eslint@8.57.0)(typescript@5.6.2): + '@typescript-eslint/type-utils@8.5.0': resolution: {integrity: sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -2394,23 +1657,12 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) - '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) - debug: 4.3.7 - ts-api-utils: 1.3.0(typescript@5.6.2) - typescript: 5.6.2 - transitivePeerDependencies: - - eslint - - supports-color - dev: false - /@typescript-eslint/types@8.5.0: + '@typescript-eslint/types@8.5.0': resolution: {integrity: sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: false - /@typescript-eslint/typescript-estree@8.5.0(typescript@5.6.2): + '@typescript-eslint/typescript-estree@8.5.0': resolution: {integrity: sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: @@ -2418,226 +1670,4604 @@ packages: peerDependenciesMeta: typescript: optional: true - dependencies: - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/visitor-keys': 8.5.0 - debug: 4.3.7 - fast-glob: 3.3.2 - is-glob: 4.0.3 - minimatch: 9.0.5 - semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.2) - typescript: 5.6.2 - transitivePeerDependencies: - - supports-color - dev: false - /@typescript-eslint/utils@8.5.0(eslint@8.57.0)(typescript@5.6.2): + '@typescript-eslint/utils@8.5.0': resolution: {integrity: sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) - '@typescript-eslint/scope-manager': 8.5.0 - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) - eslint: 8.57.0 - transitivePeerDependencies: - - supports-color - - typescript - dev: false - /@typescript-eslint/visitor-keys@8.5.0: + '@typescript-eslint/visitor-keys@8.5.0': resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dependencies: - '@typescript-eslint/types': 8.5.0 - eslint-visitor-keys: 3.4.3 - dev: false - /@ungap/structured-clone@1.2.0: + '@uiw/codemirror-extensions-basic-setup@4.23.6': + resolution: {integrity: sha512-bvtq8IOvdkLJMhoJBRGPEzU51fMpPDwEhcAHp9xCR05MtbIokQgsnLXrmD1aZm6e7s/3q47H+qdSfAAkR5MkLA==} + peerDependencies: + '@codemirror/autocomplete': '>=6.0.0' + '@codemirror/commands': '>=6.0.0' + '@codemirror/language': '>=6.0.0' + '@codemirror/lint': '>=6.0.0' + '@codemirror/search': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/codemirror-theme-vscode@4.23.7': + resolution: {integrity: sha512-KDTeBWsLY9L0jBXFZXovuNJeDxR2B7qR5jKDptGT0M4sLCq8XG6jYGZbWDCgR8cq0CUvmrw+26xeTKcnA1BJOA==} + + '@uiw/codemirror-themes@4.23.7': + resolution: {integrity: sha512-UNf1XOx1hG9OmJnrtT86PxKcdcwhaNhbrcD+nsk8WxRJ3n5c8nH6euDvgVPdVLPwbizsaQcZTILACgA/FjRpVg==} + peerDependencies: + '@codemirror/language': '>=6.0.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + + '@uiw/react-codemirror@4.23.6': + resolution: {integrity: sha512-caYKGV6TfGLRV1HHD3p0G3FiVzKL1go7wes5XT2nWjB0+dTdyzyb81MKRSacptgZcotujfNO6QXn65uhETRAMw==} + peerDependencies: + '@babel/runtime': '>=7.11.0' + '@codemirror/state': '>=6.0.0' + '@codemirror/theme-one-dark': '>=6.0.0' + '@codemirror/view': '>=6.0.0' + codemirror: '>=6.0.0' + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} - /@vitejs/plugin-react@4.3.1(vite@5.4.9): + '@vitejs/plugin-react@4.3.1': resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 - dependencies: - '@babel/core': 7.25.2 - '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) - '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) - '@types/babel__core': 7.20.5 - react-refresh: 0.14.2 - vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - transitivePeerDependencies: - - supports-color - dev: true - /@xyflow/react@12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1)(react@18.3.1): + '@xyflow/react@12.3.5': resolution: {integrity: sha512-wAYqpicdrVo1rxCu0X3M9s3YIF45Agqfabw0IBryTGqjWvr2NyfciI8gIP4MB+NKpWWN5kxZ9tiZ9u8lwC7iAg==} peerDependencies: react: '>=17' react-dom: '>=17' - dependencies: - '@xyflow/system': 0.0.46 - classcat: 5.0.5 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - zustand: 4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1) - transitivePeerDependencies: - - '@types/react' - - immer - dev: false - /@xyflow/system@0.0.46: + '@xyflow/system@0.0.46': resolution: {integrity: sha512-bmFXvboVdiydIFZmDCjrbBCYgB0d5pYdkcZPWbAxGmhMRUZ+kW3CksYgYxWabrw51rwpWitLEadvLrivG0mVfA==} - dependencies: - '@types/d3-drag': 3.0.7 - '@types/d3-selection': 3.0.11 - '@types/d3-transition': 3.0.9 - '@types/d3-zoom': 3.0.8 - d3-drag: 3.0.0 - d3-selection: 3.0.0 - d3-zoom: 3.0.0 - dev: false - /JSONStream@1.3.5: + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true - dependencies: - jsonparse: 1.3.1 - through: 2.3.8 - dev: true - /acorn-jsx@5.3.2(acorn@8.12.1): + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.12.1 - /acorn@8.12.1: + acorn@8.12.1: resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} engines: {node: '>=0.4.0'} hasBin: true - /ahooks@3.8.1(react@18.3.1): + ahooks@3.8.1: resolution: {integrity: sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA==} engines: {node: '>=8.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@babel/runtime': 7.25.6 - dayjs: 1.11.13 - intersection-observer: 0.12.2 - js-cookie: 3.0.5 - lodash: 4.17.21 - react: 18.3.1 - react-fast-compare: 3.2.2 - resize-observer-polyfill: 1.5.1 - screenfull: 5.2.0 - tslib: 2.7.0 - dev: false - /ajv@6.12.6: + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - /ajv@8.17.1: + ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.0.1 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - /ansi-colors@4.1.3: + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - dev: true - /ansi-escapes@4.3.2: + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - dependencies: - type-fest: 0.21.3 - dev: true - /ansi-escapes@7.0.0: + ansi-escapes@7.0.0: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} - dependencies: - environment: 1.1.0 - dev: true - /ansi-regex@5.0.1: + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - /ansi-regex@6.0.1: + ansi-regex@6.0.1: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - dev: true - /ansi-styles@3.2.1: + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} - dependencies: - color-convert: 1.9.3 - /ansi-styles@4.3.0: + ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - dependencies: - color-convert: 2.0.1 - /ansi-styles@6.2.1: + ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} - dev: true - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.10.0: + resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} + engines: {node: '>=4'} + + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + + browserify-sign@4.2.3: + resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} + engines: {node: '>= 0.12'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-keys@7.0.2: + resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} + engines: {node: '>=12'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001659: + resolution: {integrity: sha512-Qxxyfv3RdHAfJcXelgf0hU4DFUVXBGTjqrBUZLUh8AtlGnsDo+CnncYtTd95+ZKfnANUOzxyIQCuU/UeBZBYoA==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chart.js@4.4.4: + resolution: {integrity: sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==} + engines: {pnpm: '>=8'} + + chartjs-adapter-date-fns@3.0.0: + resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} + peerDependencies: + chart.js: '>=2.8.0' + date-fns: '>=2.0.0' + + chartjs-plugin-zoom@2.2.0: + resolution: {integrity: sha512-in6kcdiTlP6npIVLMd4zXZ08PDUXC52gZ4FAy5oyjk1zX3gKarXMAof7B9eFiisf9WOC3bh2saHg+J5WtLXZeA==} + peerDependencies: + chart.js: '>=3.2.0' + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + codemirror@6.0.1: + resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.3.1: + resolution: {integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==} + engines: {node: '>= 0.6'} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig-typescript-loader@5.0.0: + resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} + engines: {node: '>=v16'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=8.2' + typescript: '>=4' + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + crelt@1.0.6: + resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==} + + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + + cross-spawn@6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + crypto-browserify@3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + + css-functions-list@3.2.2: + resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} + engines: {node: '>=12 || >=16'} + + css-property-sort-order-smacss@2.2.0: + resolution: {integrity: sha512-nXutswsivIEBOrPo/OZw2KQjFPLvtg68aovJf6Kqrm3L6FmTvvFPaeDrk83hh0+pRJGuP3PeKJwMS0E6DFipdQ==} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-vars-ponyfill@2.4.9: + resolution: {integrity: sha512-aZyLue5bdiGVNCiCclNjo123D8I7kyoYNUaAvz+H1JalX1ye4Ilz7jNRRH5YbM+dYD6ucejiydGwk7lol/GCXQ==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decamelize@5.0.1: + resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} + engines: {node: '>=10'} + + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dnd-core@16.0.1: + resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domain-browser@4.23.0: + resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} + engines: {node: '>=10'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.18: + resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==} + + elliptic@6.5.7: + resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-airbnb-base@15.0.0: + resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.2 + + eslint-config-airbnb@19.0.4: + resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==} + engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.3 + eslint-plugin-jsx-a11y: ^6.5.1 + eslint-plugin-react: ^7.28.0 + eslint-plugin-react-hooks: ^4.3.0 + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-alias@1.1.2: + resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==} + engines: {node: '>= 4'} + peerDependencies: + eslint-plugin-import: '>=1.4.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.6.3: + resolution: {integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.11.0: + resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-check-file@2.8.0: + resolution: {integrity: sha512-FvvafMTam2WJYH9uj+FuMxQ1y+7jY3Z6P9T4j2214cH0FBxNzTcmeCiGTj1Lxp3mI6kbbgsXvmgewvf+llKYyw==} + engines: {node: '>=18'} + peerDependencies: + eslint: '>=7.28.0' + + eslint-plugin-import@2.30.0: + resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.0: + resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-prettier@5.2.1: + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react@7.35.2: + resolution: {integrity: sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-equals@4.0.3: + resolution: {integrity: sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-entry-cache@7.0.2: + resolution: {integrity: sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==} + engines: {node: '>=12.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-css-data@2.1.1: + resolution: {integrity: sha512-JpMa/f5P4mDXKg6l5/2cHL5xNY77Jap7tHyduMa6BF0E2a7bQ6Tvaz2BIMjeVYZYLcmOZ5w2Ro0yVJEI41tMbw==} + + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.8.0: + resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@11.0.0: + resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} + engines: {node: 20 || >=22} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + hammerjs@2.0.8: + resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} + engines: {node: '>=0.8.0'} + + hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-base@3.0.4: + resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} + engines: {node: '>=4'} + + hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.5: + resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + + intl-format-cache@4.3.1: + resolution: {integrity: sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==} + + intl-messageformat-parser@3.6.4: + resolution: {integrity: sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==} + deprecated: We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser + + intl-messageformat@7.8.4: + resolution: {integrity: sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + + is-bun-module@1.2.1: + resolution: {integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + + iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + + jackspeak@4.0.1: + resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} + engines: {node: 20 || >=22} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + known-css-properties@0.29.0: + resolution: {integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + less@4.2.0: + resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} + engines: {node: '>=6'} + hasBin: true + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@15.2.10: + resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.4: + resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} + engines: {node: '>=18.0.0'} + + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@11.0.1: + resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} + engines: {node: 20 || >=22} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + + material-colors@1.2.6: + resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + + meow@10.1.5: + resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + node-stdlib-browser@1.2.0: + resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} + engines: {node: '>=10'} + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-all@4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-manager-detector@0.2.0: + resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.7: + resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + engines: {node: '>= 0.10'} + + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss-html@1.7.0: + resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==} + engines: {node: ^12 || >=14} + + postcss-less@6.0.0: + resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==} + engines: {node: '>=12'} + peerDependencies: + postcss: ^8.3.5 + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-sorting@8.0.2: + resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} + peerDependencies: + postcss: ^8.4.20 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.45: + resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + rd@2.0.1: + resolution: {integrity: sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw==} + + react-color@2.19.3: + resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} + peerDependencies: + react: '*' + + react-dnd-html5-backend@16.0.1: + resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==} + + react-dnd@16.0.1: + resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==} + peerDependencies: + '@types/hoist-non-react-statics': '>= 3.3.1' + '@types/node': '>= 12' + '@types/react': '>= 16' + react: '>= 16.14' + peerDependenciesMeta: + '@types/hoist-non-react-statics': + optional: true + '@types/node': + optional: true + '@types/react': + optional: true + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-draggable@4.4.6: + resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-grid-layout@1.5.0: + resolution: {integrity: sha512-WBKX7w/LsTfI99WskSu6nX2nbJAUD7GD6nIXcwYLyPpnslojtmql2oD3I2g5C3AK8hrxIarYT8awhuDIp7iQ5w==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-hook-form@7.53.0: + resolution: {integrity: sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-intl-universal@2.11.1: + resolution: {integrity: sha512-wkmbd7Qohnr8ch02ZzF6uLZAOvdsHbvMjhiS13WUeberdrraOma4bOnn2hUVnEa/LXEvYRD0ta0RBPOV0g2HoA==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-resizable@3.0.5: + resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} + peerDependencies: + react: '>= 16.3' + + react-router-dom@6.26.1: + resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.26.1: + resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + reactcss@1.2.3: + resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} + peerDependencies: + react: '*' + + read-pkg-up@8.0.0: + resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} + engines: {node: '>=12'} + + read-pkg@3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + + read-pkg@6.0.0: + resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} + engines: {node: '>=12'} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + redent@4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + + rollup-plugin-peer-deps-external@2.2.4: + resolution: {integrity: sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==} + peerDependencies: + rollup: '*' + + rollup@4.24.0: + resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.20: + resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.includes@2.0.0: + resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.padend@3.1.6: + resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-indent@4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + style-mod@4.1.2: + resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==} + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + stylelint-config-prettier@9.0.5: + resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} + engines: {node: '>= 12'} + hasBin: true + peerDependencies: + stylelint: '>= 11.x < 15' + + stylelint-config-property-sort-order-smacss@10.0.0: + resolution: {integrity: sha512-NuiTgyqD8UdYY1IpTBIodBbrWKwaib5r8sq5kGHQ52UrmT8O7Fa8ZWYGipSZw6k9tGoljl9Hng2jtH+wBTMa1Q==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.0 + + stylelint-config-recommended@13.0.0: + resolution: {integrity: sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + stylelint: ^15.10.0 + + stylelint-config-standard@34.0.0: + resolution: {integrity: sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + stylelint: ^15.10.0 + + stylelint-order@6.0.4: + resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + + stylelint@15.11.0: + resolution: {integrity: sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.1.0: + resolution: {integrity: sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + synckit@0.9.1: + resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + trim-newlines@4.1.1: + resolution: {integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==} + engines: {node: '>=12'} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} + engines: {node: '>=18.0.0'} + hasBin: true + + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + + vite-plugin-html-config@2.0.2: + resolution: {integrity: sha512-g09u0XsmgKyMUIp1RZSyNSkJWvIusaXxw3KylyxU3vkCq7/G8hyemLctT+4IvO42fCPlNySmrNC9g0qSoKmvpw==} + engines: {node: '>=12.0.0'} + peerDependencies: + vite: '>=5.0.0' + + vite-plugin-imp@2.4.0: + resolution: {integrity: sha512-L/6/nvOw+MyNh4UxAlCZHsmKd5MitmHamqqAWB15sbUgVIEz/OQ8jpKr6kkQU0eA/AIe8fkCVbQBlP81ajrqWg==} + peerDependencies: + vite: '>= 2.0.0-beta.5' + + vite-plugin-node-polyfills@0.22.0: + resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + vite-plugin-progress@0.0.7: + resolution: {integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ==} + engines: {node: '>=14', pnpm: '>=7.0.0'} + peerDependencies: + vite: '>2.0.0-0' + + vite-plugin-stylelint@5.3.1: + resolution: {integrity: sha512-M/hSdfOwnOVghbJDeuuYIU2xO/MMukYR8QcEyNKFPG8ro1L+DlTdViix2B2d/FvAw14WPX88ckA5A7NvUjJz8w==} + engines: {node: '>=14.18'} + peerDependencies: + '@types/stylelint': ^13.0.0 + postcss: ^7.0.0 || ^8.0.0 + rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 + stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@types/stylelint': + optional: true + postcss: + optional: true + rollup: + optional: true + + vite@5.4.9: + resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + w3c-keyname@2.2.8: + resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.4: + resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + zustand@4.5.5: + resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + +snapshots: + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.1.0 + + '@babel/compat-data@7.25.4': {} + + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.6 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.6 + '@babel/parser': 7.25.6 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + convert-source-map: 2.0.0 + debug: 4.3.7 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.25.6': + dependencies: + '@babel/types': 7.25.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.25.2': + dependencies: + '@babel/compat-data': 7.25.4 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.24.8': {} + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.25.6 + '@babel/types': 7.25.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.24.8': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helpers@7.25.6': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.6 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.0 + + '@babel/parser@7.25.6': + dependencies: + '@babel/types': 7.25.6 + + '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-plugin-utils': 7.24.8 + + '@babel/runtime@7.25.6': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.6 + '@babel/types': 7.25.6 + + '@babel/traverse@7.25.6': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.6 + '@babel/parser': 7.25.6 + '@babel/template': 7.25.0 + '@babel/types': 7.25.6 + debug: 4.3.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.25.6': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@changesets/apply-release-plan@7.0.5': + dependencies: + '@changesets/config': 3.0.3 + '@changesets/get-version-range-type': 0.4.0 + '@changesets/git': 3.0.1 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + detect-indent: 6.1.0 + fs-extra: 7.0.1 + lodash.startcase: 4.4.0 + outdent: 0.5.0 + prettier: 2.8.8 + resolve-from: 5.0.0 + semver: 7.6.3 + + '@changesets/assemble-release-plan@6.0.4': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + semver: 7.6.3 + + '@changesets/changelog-git@0.2.0': + dependencies: + '@changesets/types': 6.0.0 + + '@changesets/cli@2.27.8': + dependencies: + '@changesets/apply-release-plan': 7.0.5 + '@changesets/assemble-release-plan': 6.0.4 + '@changesets/changelog-git': 0.2.0 + '@changesets/config': 3.0.3 + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/get-release-plan': 4.0.4 + '@changesets/git': 3.0.1 + '@changesets/logger': 0.1.1 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.1 + '@changesets/should-skip-package': 0.1.1 + '@changesets/types': 6.0.0 + '@changesets/write': 0.3.2 + '@manypkg/get-packages': 1.1.3 + '@types/semver': 7.5.8 + ansi-colors: 4.1.3 + ci-info: 3.9.0 + enquirer: 2.4.1 + external-editor: 3.1.0 + fs-extra: 7.0.1 + mri: 1.2.0 + outdent: 0.5.0 + p-limit: 2.3.0 + package-manager-detector: 0.2.0 + picocolors: 1.1.0 + resolve-from: 5.0.0 + semver: 7.6.3 + spawndamnit: 2.0.0 + term-size: 2.2.1 + + '@changesets/config@3.0.3': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/get-dependents-graph': 2.1.2 + '@changesets/logger': 0.1.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + micromatch: 4.0.8 + + '@changesets/errors@0.2.0': + dependencies: + extendable-error: 0.1.7 + + '@changesets/get-dependents-graph@2.1.2': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + picocolors: 1.1.0 + semver: 7.6.3 + + '@changesets/get-release-plan@4.0.4': + dependencies: + '@changesets/assemble-release-plan': 6.0.4 + '@changesets/config': 3.0.3 + '@changesets/pre': 2.0.1 + '@changesets/read': 0.6.1 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/get-version-range-type@0.4.0': {} + + '@changesets/git@3.0.1': + dependencies: + '@changesets/errors': 0.2.0 + '@manypkg/get-packages': 1.1.3 + is-subdir: 1.2.0 + micromatch: 4.0.8 + spawndamnit: 2.0.0 + + '@changesets/logger@0.1.1': + dependencies: + picocolors: 1.1.0 + + '@changesets/parse@0.4.0': + dependencies: + '@changesets/types': 6.0.0 + js-yaml: 3.14.1 + + '@changesets/pre@2.0.1': + dependencies: + '@changesets/errors': 0.2.0 + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + fs-extra: 7.0.1 + + '@changesets/read@0.6.1': + dependencies: + '@changesets/git': 3.0.1 + '@changesets/logger': 0.1.1 + '@changesets/parse': 0.4.0 + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + p-filter: 2.1.0 + picocolors: 1.1.0 + + '@changesets/should-skip-package@0.1.1': + dependencies: + '@changesets/types': 6.0.0 + '@manypkg/get-packages': 1.1.3 + + '@changesets/types@4.1.0': {} + + '@changesets/types@6.0.0': {} + + '@changesets/write@0.3.2': + dependencies: + '@changesets/types': 6.0.0 + fs-extra: 7.0.1 + human-id: 1.0.2 + prettier: 2.8.8 + + '@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3)': + dependencies: + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/common': 1.2.3 + + '@codemirror/commands@6.7.1': + dependencies: + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/common': 1.2.3 + + '@codemirror/lang-java@6.0.1': + dependencies: + '@codemirror/language': 6.10.6 + '@lezer/java': 1.1.3 + + '@codemirror/lang-javascript@6.2.2': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/language': 6.10.6 + '@codemirror/lint': 6.8.4 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/common': 1.2.3 + '@lezer/javascript': 1.4.21 + + '@codemirror/lang-json@6.0.1': + dependencies: + '@codemirror/language': 6.10.6 + '@lezer/json': 1.0.2 + + '@codemirror/lang-python@6.1.6(@codemirror/view@6.35.3)': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@lezer/common': 1.2.3 + '@lezer/python': 1.1.15 + transitivePeerDependencies: + - '@codemirror/view' + + '@codemirror/lang-yaml@6.1.2(@codemirror/view@6.35.3)': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + '@lezer/yaml': 1.0.3 + transitivePeerDependencies: + - '@codemirror/view' + + '@codemirror/language@6.10.6': + dependencies: + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + style-mod: 4.1.2 + + '@codemirror/legacy-modes@6.4.2': + dependencies: + '@codemirror/language': 6.10.6 + + '@codemirror/lint@6.8.4': + dependencies: + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + crelt: 1.0.6 + + '@codemirror/search@6.5.8': + dependencies: + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + crelt: 1.0.6 + + '@codemirror/state@6.5.0': + dependencies: + '@marijn/find-cluster-break': 1.0.2 + + '@codemirror/theme-one-dark@6.1.2': + dependencies: + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/highlight': 1.2.1 + + '@codemirror/view@6.35.3': + dependencies: + '@codemirror/state': 6.5.0 + style-mod: 4.1.2 + w3c-keyname: 2.2.8 + + '@commitlint/cli@19.5.0(@types/node@20.16.5)(typescript@5.6.2)': + dependencies: + '@commitlint/format': 19.5.0 + '@commitlint/lint': 19.5.0 + '@commitlint/load': 19.5.0(@types/node@20.16.5)(typescript@5.6.2) + '@commitlint/read': 19.5.0 + '@commitlint/types': 19.5.0 + tinyexec: 0.3.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@19.4.1': + dependencies: + '@commitlint/types': 19.5.0 + conventional-changelog-conventionalcommits: 7.0.2 + + '@commitlint/config-validator@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + ajv: 8.17.1 + + '@commitlint/ensure@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + lodash.camelcase: 4.3.0 + lodash.kebabcase: 4.1.1 + lodash.snakecase: 4.1.1 + lodash.startcase: 4.4.0 + lodash.upperfirst: 4.3.1 + + '@commitlint/execute-rule@19.5.0': {} + + '@commitlint/format@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + chalk: 5.3.0 + + '@commitlint/is-ignored@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + semver: 7.6.3 + + '@commitlint/lint@19.5.0': + dependencies: + '@commitlint/is-ignored': 19.5.0 + '@commitlint/parse': 19.5.0 + '@commitlint/rules': 19.5.0 + '@commitlint/types': 19.5.0 + + '@commitlint/load@19.5.0(@types/node@20.16.5)(typescript@5.6.2)': + dependencies: + '@commitlint/config-validator': 19.5.0 + '@commitlint/execute-rule': 19.5.0 + '@commitlint/resolve-extends': 19.5.0 + '@commitlint/types': 19.5.0 + chalk: 5.3.0 + cosmiconfig: 9.0.0(typescript@5.6.2) + cosmiconfig-typescript-loader: 5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2) + lodash.isplainobject: 4.0.6 + lodash.merge: 4.6.2 + lodash.uniq: 4.5.0 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@19.5.0': {} + + '@commitlint/parse@19.5.0': + dependencies: + '@commitlint/types': 19.5.0 + conventional-changelog-angular: 7.0.0 + conventional-commits-parser: 5.0.0 + + '@commitlint/read@19.5.0': + dependencies: + '@commitlint/top-level': 19.5.0 + '@commitlint/types': 19.5.0 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 0.3.0 + + '@commitlint/resolve-extends@19.5.0': + dependencies: + '@commitlint/config-validator': 19.5.0 + '@commitlint/types': 19.5.0 + global-directory: 4.0.1 + import-meta-resolve: 4.1.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@19.5.0': + dependencies: + '@commitlint/ensure': 19.5.0 + '@commitlint/message': 19.5.0 + '@commitlint/to-lines': 19.5.0 + '@commitlint/types': 19.5.0 + + '@commitlint/to-lines@19.5.0': {} + + '@commitlint/top-level@19.5.0': + dependencies: + find-up: 7.0.0 + + '@commitlint/types@19.5.0': + dependencies: + '@types/conventional-commits-parser': 5.0.0 + chalk: 5.3.0 + + '@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1)': + dependencies: + '@csstools/css-tokenizer': 2.4.1 + + '@csstools/css-tokenizer@2.4.1': {} + + '@csstools/media-query-list-parser@2.1.13(@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1))(@csstools/css-tokenizer@2.4.1)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) + '@csstools/css-tokenizer': 2.4.1 + + '@csstools/selector-specificity@3.1.1(postcss-selector-parser@6.1.2)': + dependencies: + postcss-selector-parser: 6.1.2 + + '@emotion/babel-plugin@11.12.0': + dependencies: + '@babel/helper-module-imports': 7.24.7 + '@babel/runtime': 7.25.6 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.1 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + + '@emotion/cache@11.13.1': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.0 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': {} + + '@emotion/is-prop-valid@1.3.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@emotion/babel-plugin': 11.12.0 + '@emotion/cache': 11.13.1 + '@emotion/serialize': 1.3.1 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.0 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + transitivePeerDependencies: + - supports-color + + '@emotion/serialize@1.3.1': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.0 + csstype: 3.1.3 + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@emotion/babel-plugin': 11.12.0 + '@emotion/is-prop-valid': 1.3.0 + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/serialize': 1.3.1 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.0 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + transitivePeerDependencies: + - supports-color + + '@emotion/unitless@0.10.0': {} + + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@emotion/utils@1.4.0': {} + + '@emotion/weak-memoize@0.4.0': {} + + '@esbuild/aix-ppc64@0.23.1': + optional: true + + '@esbuild/android-arm64@0.23.1': + optional: true + + '@esbuild/android-arm@0.23.1': + optional: true + + '@esbuild/android-x64@0.23.1': + optional: true + + '@esbuild/darwin-arm64@0.23.1': + optional: true + + '@esbuild/darwin-x64@0.23.1': + optional: true + + '@esbuild/freebsd-arm64@0.23.1': + optional: true + + '@esbuild/freebsd-x64@0.23.1': + optional: true + + '@esbuild/linux-arm64@0.23.1': + optional: true + + '@esbuild/linux-arm@0.23.1': + optional: true + + '@esbuild/linux-ia32@0.23.1': + optional: true + + '@esbuild/linux-loong64@0.23.1': + optional: true + + '@esbuild/linux-mips64el@0.23.1': + optional: true + + '@esbuild/linux-ppc64@0.23.1': + optional: true + + '@esbuild/linux-riscv64@0.23.1': + optional: true + + '@esbuild/linux-s390x@0.23.1': + optional: true + + '@esbuild/linux-x64@0.23.1': + optional: true + + '@esbuild/netbsd-x64@0.23.1': + optional: true + + '@esbuild/openbsd-arm64@0.23.1': + optional: true + + '@esbuild/openbsd-x64@0.23.1': + optional: true + + '@esbuild/sunos-x64@0.23.1': + optional: true + + '@esbuild/win32-arm64@0.23.1': + optional: true + + '@esbuild/win32-ia32@0.23.1': + optional: true + + '@esbuild/win32-x64@0.23.1': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': + dependencies: + eslint: 8.57.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.11.0': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.7 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.0': {} + + '@formatjs/intl-unified-numberformat@3.3.7': + dependencies: + '@formatjs/intl-utils': 2.3.0 + + '@formatjs/intl-utils@2.3.0': {} + + '@humanwhocodes/config-array@0.11.14': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.7 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@icons/material@0.2.4(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@kurkle/color@0.3.2': {} + + '@lezer/common@1.2.3': {} + + '@lezer/highlight@1.2.1': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/java@1.1.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/javascript@1.4.21': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/json@1.0.2': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.3 + + '@lezer/python@1.1.15': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@lezer/yaml@1.0.3': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + + '@manypkg/find-root@1.1.0': + dependencies: + '@babel/runtime': 7.25.6 + '@types/node': 12.20.55 + find-up: 4.1.0 + fs-extra: 8.1.0 + + '@manypkg/get-packages@1.1.3': + dependencies: + '@babel/runtime': 7.25.6 + '@changesets/types': 4.1.0 + '@manypkg/find-root': 1.1.0 + fs-extra: 8.1.0 + globby: 11.1.0 + read-yaml-file: 1.1.0 + + '@marijn/find-cluster-break@1.0.2': {} + + '@mui/core-downloads-tracker@6.1.0': {} + + '@mui/icons-material@6.0.2(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/material': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/core-downloads-tracker': 6.1.0 + '@mui/system': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/types': 7.2.16(@types/react@18.3.5) + '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@types/react': 18.3.5 + + '@mui/private-theming@6.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/styled-engine@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@emotion/cache': 11.13.1 + '@emotion/sheet': 1.4.0 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + + '@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/private-theming': 6.1.0(@types/react@18.3.5)(react@18.3.1) + '@mui/styled-engine': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.16(@types/react@18.3.5) + '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@types/react': 18.3.5 + + '@mui/types@7.2.16(@types/react@18.3.5)': + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/utils@5.16.6(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/types': 7.2.16(@types/react@18.3.5) + '@types/prop-types': 15.7.12 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/utils@6.1.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/types': 7.2.16(@types/react@18.3.5) + '@types/prop-types': 15.7.12 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 + + '@mui/x-data-grid@7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/material': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) + '@mui/x-internals': 7.18.0(@types/react@18.3.5)(react@18.3.1) + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + reselect: 5.1.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + + '@mui/x-date-pickers@7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/material': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) + '@mui/x-internals': 7.18.0(@types/react@18.3.5)(react@18.3.1) + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + date-fns: 4.1.0 + dayjs: 1.11.13 + transitivePeerDependencies: + - '@types/react' + + '@mui/x-internals@7.18.0(@types/react@18.3.5)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) + react: 18.3.1 + transitivePeerDependencies: + - '@types/react' + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@pkgr/core@0.1.1': {} + + '@popperjs/core@2.11.8': {} + + '@react-dnd/asap@5.0.2': {} + + '@react-dnd/invariant@4.0.2': {} + + '@react-dnd/shallowequal@4.0.2': {} + + '@remix-run/router@1.19.1': {} + + '@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.24.0)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + optionalDependencies: + '@types/babel__core': 7.20.5 + rollup: 4.24.0 + transitivePeerDependencies: + - supports-color + + '@rollup/plugin-commonjs@28.0.1(rollup@4.24.0)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + fdir: 6.4.2(picomatch@4.0.2) + is-reference: 1.2.1 + magic-string: 0.30.11 + picomatch: 4.0.2 + optionalDependencies: + rollup: 4.24.0 + + '@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.24.0)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + astring: 1.9.0 + estree-walker: 2.0.2 + fast-glob: 3.3.2 + magic-string: 0.30.11 + optionalDependencies: + rollup: 4.24.0 + + '@rollup/plugin-inject@5.0.5(rollup@4.24.0)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + estree-walker: 2.0.2 + magic-string: 0.30.11 + optionalDependencies: + rollup: 4.24.0 + + '@rollup/plugin-json@6.1.0(rollup@4.24.0)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + optionalDependencies: + rollup: 4.24.0 + + '@rollup/plugin-node-resolve@15.2.3(rollup@4.24.0)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.8 + optionalDependencies: + rollup: 4.24.0 + + '@rollup/plugin-typescript@12.1.1(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2)': + dependencies: + '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + resolve: 1.22.8 + typescript: 5.6.2 + optionalDependencies: + rollup: 4.24.0 + tslib: 2.7.0 + + '@rollup/pluginutils@5.1.2(rollup@4.24.0)': + dependencies: + '@types/estree': 1.0.6 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 4.24.0 + + '@rollup/rollup-android-arm-eabi@4.24.0': + optional: true + + '@rollup/rollup-android-arm64@4.24.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.24.0': + optional: true + + '@rollup/rollup-darwin-x64@4.24.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.24.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.24.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.24.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.24.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.24.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.24.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.24.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.24.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.24.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.24.0': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.10.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.25.6 + '@babel/types': 7.25.6 + '@types/babel__generator': 7.6.8 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.20.6 + + '@types/babel__generator@7.6.8': + dependencies: + '@babel/types': 7.25.6 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.25.6 + '@babel/types': 7.25.6 + + '@types/babel__traverse@7.20.6': + dependencies: + '@babel/types': 7.25.6 + + '@types/conventional-commits-parser@5.0.0': + dependencies: + '@types/node': 20.16.5 + + '@types/d3-color@3.1.3': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/estree@1.0.6': {} + + '@types/fs-extra@9.0.13': + dependencies: + '@types/node': 20.16.5 + + '@types/hammerjs@2.0.46': {} + + '@types/inquirer@8.2.10': + dependencies: + '@types/through': 0.0.33 + rxjs: 7.8.1 + + '@types/json5@0.0.29': {} + + '@types/less@3.0.6': {} + + '@types/lodash-es@4.17.12': + dependencies: + '@types/lodash': 4.17.7 + + '@types/lodash@4.17.7': {} + + '@types/minimist@1.2.5': {} + + '@types/node@10.17.60': {} + + '@types/node@12.20.55': {} + + '@types/node@20.16.5': + dependencies: + undici-types: 6.19.8 + + '@types/normalize-package-data@2.4.4': {} + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.12': {} + + '@types/qs@6.9.15': {} + + '@types/react-color@3.0.12': + dependencies: + '@types/react': 18.3.5 + '@types/reactcss': 1.2.12 + + '@types/react-dom@18.3.0': + dependencies: + '@types/react': 18.3.5 + + '@types/react-grid-layout@1.3.5': + dependencies: + '@types/react': 18.3.5 + + '@types/react-resizable@3.0.8': + dependencies: + '@types/react': 18.3.5 + + '@types/react-transition-group@4.4.11': + dependencies: + '@types/react': 18.3.5 + + '@types/react@18.3.5': + dependencies: + '@types/prop-types': 15.7.12 + csstype: 3.1.3 + + '@types/reactcss@1.2.12': + dependencies: + '@types/react': 18.3.5 + + '@types/resolve@1.20.2': {} + + '@types/semver@7.5.8': {} + + '@types/through@0.0.33': + dependencies: + '@types/node': 20.16.5 + + '@types/validator@13.12.2': {} + + '@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/scope-manager': 8.5.0 + '@typescript-eslint/type-utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) + '@typescript-eslint/visitor-keys': 8.5.0 + eslint: 8.57.0 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.6.2) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.5.0 + '@typescript-eslint/types': 8.5.0 + '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) + '@typescript-eslint/visitor-keys': 8.5.0 + debug: 4.3.7 + eslint: 8.57.0 + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.5.0': + dependencies: + '@typescript-eslint/types': 8.5.0 + '@typescript-eslint/visitor-keys': 8.5.0 + + '@typescript-eslint/type-utils@8.5.0(eslint@8.57.0)(typescript@5.6.2)': + dependencies: + '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) + '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) + debug: 4.3.7 + ts-api-utils: 1.3.0(typescript@5.6.2) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - eslint + - supports-color + + '@typescript-eslint/types@8.5.0': {} + + '@typescript-eslint/typescript-estree@8.5.0(typescript@5.6.2)': + dependencies: + '@typescript-eslint/types': 8.5.0 + '@typescript-eslint/visitor-keys': 8.5.0 + debug: 4.3.7 + fast-glob: 3.3.2 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.6.2) + optionalDependencies: + typescript: 5.6.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.5.0(eslint@8.57.0)(typescript@5.6.2)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) + '@typescript-eslint/scope-manager': 8.5.0 + '@typescript-eslint/types': 8.5.0 + '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) + eslint: 8.57.0 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@8.5.0': + dependencies: + '@typescript-eslint/types': 8.5.0 + eslint-visitor-keys: 3.4.3 + + '@uiw/codemirror-extensions-basic-setup@4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.6)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/commands': 6.7.1 + '@codemirror/language': 6.10.6 + '@codemirror/lint': 6.8.4 + '@codemirror/search': 6.5.8 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + + '@uiw/codemirror-theme-vscode@4.23.7(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)': + dependencies: + '@uiw/codemirror-themes': 4.23.7(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3) + transitivePeerDependencies: + - '@codemirror/language' + - '@codemirror/state' + - '@codemirror/view' + + '@uiw/codemirror-themes@4.23.7(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)': + dependencies: + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + + '@uiw/react-codemirror@4.23.6(@babel/runtime@7.25.6)(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3))(@codemirror/language@6.10.6)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.0)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.35.3)(codemirror@6.0.1(@lezer/common@1.2.3))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.6 + '@codemirror/commands': 6.7.1 + '@codemirror/state': 6.5.0 + '@codemirror/theme-one-dark': 6.1.2 + '@codemirror/view': 6.35.3 + '@uiw/codemirror-extensions-basic-setup': 4.23.6(@codemirror/autocomplete@6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3))(@codemirror/commands@6.7.1)(@codemirror/language@6.10.6)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.8)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3) + codemirror: 6.0.1(@lezer/common@1.2.3) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@codemirror/autocomplete' + - '@codemirror/language' + - '@codemirror/lint' + - '@codemirror/search' + + '@ungap/structured-clone@1.2.0': {} + + '@vitejs/plugin-react@4.3.1(vite@5.4.9(@types/node@20.16.5)(less@4.2.0))': + dependencies: + '@babel/core': 7.25.2 + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.25.2) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) + transitivePeerDependencies: + - supports-color + + '@xyflow/react@12.3.5(@types/react@18.3.5)(immer@10.1.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@xyflow/system': 0.0.46 + classcat: 5.0.5 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + zustand: 4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.46': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + JSONStream@1.3.5: + dependencies: + jsonparse: 1.3.1 + through: 2.3.8 + + acorn-jsx@5.3.2(acorn@8.12.1): + dependencies: + acorn: 8.12.1 + + acorn@8.12.1: {} + + ahooks@3.8.1(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.6 + dayjs: 1.11.13 + intersection-observer: 0.12.2 + js-cookie: 3.0.5 + lodash: 4.17.21 + react: 18.3.1 + react-fast-compare: 3.2.2 + resize-observer-polyfill: 1.5.1 + screenfull: 5.2.0 + tslib: 2.7.0 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.0.1 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-colors@4.1.3: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + aria-query@5.1.3: dependencies: deep-equal: 2.2.3 - dev: false - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 - /array-ify@1.0.0: - resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + array-ify@1.0.0: {} - /array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2645,15 +6275,10 @@ packages: es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 - dev: false - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} + array-union@2.1.0: {} - /array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2661,11 +6286,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2673,42 +6295,30 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - dev: false - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -2719,147 +6329,94 @@ packages: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 - /arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} + arrify@1.0.1: {} - /asn1.js@4.10.1: - resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + asn1.js@4.10.1: dependencies: bn.js: 4.12.0 inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true - /assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + assert@2.1.0: dependencies: call-bind: 1.0.7 is-nan: 1.3.2 object-is: 1.1.6 object.assign: 4.1.5 util: 0.12.5 - dev: true - /ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - dev: false + ast-types-flow@0.0.8: {} - /astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} + astral-regex@2.0.0: {} - /astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} - hasBin: true - dev: true + astring@1.9.0: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false + asynckit@0.4.0: {} - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - /axe-core@4.10.0: - resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} - engines: {node: '>=4'} - dev: false + axe-core@4.10.0: {} - /axios@1.7.7: - resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + axios@1.7.7: dependencies: follow-redirects: 1.15.9 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: false - /axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - dev: false + axobject-query@4.1.0: {} - /babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} + babel-plugin-macros@3.1.0: dependencies: '@babel/runtime': 7.25.6 cosmiconfig: 7.1.0 resolve: 1.22.8 - dev: false - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@1.0.2: {} - /balanced-match@2.0.0: - resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + balanced-match@2.0.0: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true + base64-js@1.5.1: {} - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - dev: true - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: true + binary-extensions@2.3.0: {} - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /bn.js@4.12.0: - resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - dev: true + bn.js@4.12.0: {} - /bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: true + bn.js@5.2.1: {} - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - /brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - dev: true + brorand@1.1.0: {} - /browser-resolve@2.0.0: - resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + browser-resolve@2.0.0: dependencies: resolve: 1.22.8 - dev: true - /browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + browserify-aes@1.2.0: dependencies: buffer-xor: 1.0.3 cipher-base: 1.0.4 @@ -2867,35 +6424,26 @@ packages: evp_bytestokey: 1.0.3 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /browserify-cipher@1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + browserify-cipher@1.0.1: dependencies: browserify-aes: 1.2.0 browserify-des: 1.0.2 evp_bytestokey: 1.0.3 - dev: true - /browserify-des@1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + browserify-des@1.0.2: dependencies: cipher-base: 1.0.4 des.js: 1.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /browserify-rsa@4.1.0: - resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + browserify-rsa@4.1.0: dependencies: bn.js: 5.2.1 randombytes: 2.1.0 - dev: true - /browserify-sign@4.2.3: - resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} - engines: {node: '>= 0.12'} + browserify-sign@4.2.3: dependencies: bn.js: 5.2.1 browserify-rsa: 4.1.0 @@ -2907,48 +6455,30 @@ packages: parse-asn1: 5.1.7 readable-stream: 2.3.8 safe-buffer: 5.2.1 - dev: true - /browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserify-zlib@0.2.0: dependencies: pako: 1.0.11 - dev: true - /browserslist@4.23.3: - resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.3: dependencies: caniuse-lite: 1.0.30001659 electron-to-chromium: 1.5.18 node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.23.3) - dev: true - /buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - dev: true + buffer-xor@1.0.3: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - /builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - dev: true + builtin-modules@3.3.0: {} - /builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - dev: true + builtin-status-codes@3.0.0: {} - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 @@ -2956,80 +6486,50 @@ packages: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /camelcase-keys@7.0.2: - resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} - engines: {node: '>=12'} + camelcase-keys@7.0.2: dependencies: camelcase: 6.3.0 map-obj: 4.3.0 quick-lru: 5.1.1 type-fest: 1.4.0 - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} + camelcase@6.3.0: {} - /caniuse-lite@1.0.30001659: - resolution: {integrity: sha512-Qxxyfv3RdHAfJcXelgf0hU4DFUVXBGTjqrBUZLUh8AtlGnsDo+CnncYtTd95+ZKfnANUOzxyIQCuU/UeBZBYoA==} - dev: true + caniuse-lite@1.0.30001659: {} - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.3.0: {} - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: true + chardet@0.7.0: {} - /chart.js@4.4.4: - resolution: {integrity: sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==} - engines: {pnpm: '>=8'} + chart.js@4.4.4: dependencies: '@kurkle/color': 0.3.2 - dev: false - /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.4)(date-fns@4.1.0): - resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} - peerDependencies: - chart.js: '>=2.8.0' - date-fns: '>=2.0.0' + chartjs-adapter-date-fns@3.0.0(chart.js@4.4.4)(date-fns@4.1.0): dependencies: chart.js: 4.4.4 date-fns: 4.1.0 - dev: false - /chartjs-plugin-zoom@2.2.0(chart.js@4.4.4): - resolution: {integrity: sha512-in6kcdiTlP6npIVLMd4zXZ08PDUXC52gZ4FAy5oyjk1zX3gKarXMAof7B9eFiisf9WOC3bh2saHg+J5WtLXZeA==} - peerDependencies: - chart.js: '>=3.2.0' + chartjs-plugin-zoom@2.2.0(chart.js@4.4.4): dependencies: '@types/hammerjs': 2.0.46 chart.js: 4.4.4 hammerjs: 2.0.8 - dev: false - /chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -3040,271 +6540,172 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true + ci-info@3.9.0: {} - /cipher-base@1.0.4: - resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + cipher-base@1.0.4: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /classcat@5.0.5: - resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} - dev: false + classcat@5.0.5: {} - /classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - dev: false + classnames@2.5.1: {} - /cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 - dev: true - /cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 - dev: true - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: true + cli-spinners@2.9.2: {} - /cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} + cli-truncate@4.0.0: dependencies: slice-ansi: 5.0.0 string-width: 7.2.0 - dev: true - /cli-width@3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - dev: true + cli-width@3.0.0: {} - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: true + clone@1.0.4: {} - /clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - dev: false + clsx@1.2.1: {} - /clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - dev: false + clsx@2.1.1: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + codemirror@6.0.1(@lezer/common@1.2.3): + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/commands': 6.7.1 + '@codemirror/language': 6.10.6 + '@codemirror/lint': 6.8.4 + '@codemirror/search': 6.5.8 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + transitivePeerDependencies: + - '@lezer/common' + + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + colord@2.9.3: {} - /colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - dev: true + colorette@2.0.20: {} - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - dev: false - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: true + commander@12.1.0: {} - /commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - dev: true + commander@8.3.0: {} - /commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - dev: true + commondir@1.0.1: {} - /compare-func@2.0.0: - resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + compare-func@2.0.0: dependencies: array-ify: 1.0.0 dot-prop: 5.3.0 - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-map@0.0.1: {} - /confusing-browser-globals@1.0.11: - resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} - dev: false + confusing-browser-globals@1.0.11: {} - /console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - dev: true + console-browserify@1.2.0: {} - /constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - dev: true + constants-browserify@1.0.0: {} - /conventional-changelog-angular@7.0.0: - resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} - engines: {node: '>=16'} + conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 - dev: true - /conventional-changelog-conventionalcommits@7.0.2: - resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} - engines: {node: '>=16'} + conventional-changelog-conventionalcommits@7.0.2: dependencies: compare-func: 2.0.0 - /conventional-commits-parser@5.0.0: - resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} - engines: {node: '>=16'} - hasBin: true + conventional-commits-parser@5.0.0: dependencies: JSONStream: 1.3.5 is-text-path: 2.0.0 meow: 12.1.1 split2: 4.2.0 - dev: true - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: false + convert-source-map@1.9.0: {} - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true + convert-source-map@2.0.0: {} - /cookie@0.3.1: - resolution: {integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.3.1: {} - /copy-anything@2.0.6: - resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-anything@2.0.6: dependencies: is-what: 3.14.1 - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true + core-util-is@1.0.3: {} - /cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0)(typescript@5.6.2): - resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} - engines: {node: '>=v16'} - peerDependencies: - '@types/node': '*' - cosmiconfig: '>=8.2' - typescript: '>=4' + cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2): dependencies: '@types/node': 20.16.5 cosmiconfig: 9.0.0(typescript@5.6.2) jiti: 1.21.6 typescript: 5.6.2 - dev: true - /cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 - dev: false - /cosmiconfig@8.3.6(typescript@5.6.2): - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@8.3.6(typescript@5.6.2): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 + optionalDependencies: typescript: 5.6.2 - /cosmiconfig@9.0.0(typescript@5.6.2): - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@9.0.0(typescript@5.6.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 + optionalDependencies: typescript: 5.6.2 - dev: true - /create-ecdh@4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + create-ecdh@4.0.4: dependencies: bn.js: 4.12.0 elliptic: 6.5.7 - dev: true - /create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + create-hash@1.2.0: dependencies: cipher-base: 1.0.4 inherits: 2.0.4 md5.js: 1.3.5 ripemd160: 2.0.2 sha.js: 2.4.11 - dev: true - /create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + create-hmac@1.1.7: dependencies: cipher-base: 1.0.4 create-hash: 1.2.0 @@ -3312,41 +6713,32 @@ packages: ripemd160: 2.0.2 safe-buffer: 5.2.1 sha.js: 2.4.11 - dev: true - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true + create-require@1.1.1: {} - /cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + crelt@1.0.6: {} + + cross-spawn@5.1.0: dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@6.0.5: - resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} - engines: {node: '>=4.8'} + cross-spawn@6.0.5: dependencies: nice-try: 1.0.5 path-key: 2.0.1 semver: 5.7.2 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - /crypto-browserify@3.12.0: - resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + crypto-browserify@3.12.0: dependencies: browserify-cipher: 1.0.1 browserify-sign: 4.2.3 @@ -3359,83 +6751,45 @@ packages: public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 - dev: true - /css-functions-list@3.2.2: - resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} - engines: {node: '>=12 || >=16'} + css-functions-list@3.2.2: {} - /css-property-sort-order-smacss@2.2.0: - resolution: {integrity: sha512-nXutswsivIEBOrPo/OZw2KQjFPLvtg68aovJf6Kqrm3L6FmTvvFPaeDrk83hh0+pRJGuP3PeKJwMS0E6DFipdQ==} - dev: false + css-property-sort-order-smacss@2.2.0: {} - /css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 source-map-js: 1.2.1 - /css-vars-ponyfill@2.4.9: - resolution: {integrity: sha512-aZyLue5bdiGVNCiCclNjo123D8I7kyoYNUaAvz+H1JalX1ye4Ilz7jNRRH5YbM+dYD6ucejiydGwk7lol/GCXQ==} + css-vars-ponyfill@2.4.9: dependencies: balanced-match: 1.0.2 get-css-data: 2.1.1 - dev: false - /cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true + cssesc@3.0.0: {} - /csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.1.3: {} - /d3-color@3.1.0: - resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} - engines: {node: '>=12'} - dev: false + d3-color@3.1.0: {} - /d3-dispatch@3.0.1: - resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} - engines: {node: '>=12'} - dev: false + d3-dispatch@3.0.1: {} - /d3-drag@3.0.0: - resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} - engines: {node: '>=12'} + d3-drag@3.0.0: dependencies: d3-dispatch: 3.0.1 d3-selection: 3.0.0 - dev: false - /d3-ease@3.0.1: - resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} - engines: {node: '>=12'} - dev: false + d3-ease@3.0.1: {} - /d3-interpolate@3.0.1: - resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} - engines: {node: '>=12'} + d3-interpolate@3.0.1: dependencies: d3-color: 3.1.0 - dev: false - /d3-selection@3.0.0: - resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} - engines: {node: '>=12'} - dev: false + d3-selection@3.0.0: {} - /d3-timer@3.0.1: - resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} - engines: {node: '>=12'} - dev: false + d3-timer@3.0.1: {} - /d3-transition@3.0.1(d3-selection@3.0.0): - resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} - engines: {node: '>=12'} - peerDependencies: - d3-selection: 2 - 3 + d3-transition@3.0.1(d3-selection@3.0.0): dependencies: d3-color: 3.1.0 d3-dispatch: 3.0.1 @@ -3443,100 +6797,59 @@ packages: d3-interpolate: 3.0.1 d3-selection: 3.0.0 d3-timer: 3.0.1 - dev: false - /d3-zoom@3.0.0: - resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} - engines: {node: '>=12'} + d3-zoom@3.0.0: dependencies: d3-dispatch: 3.0.1 d3-drag: 3.0.0 d3-interpolate: 3.0.1 d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - dev: false - /damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - dev: false + damerau-levenshtein@1.0.8: {} - /dargs@8.1.0: - resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} - engines: {node: '>=12'} - dev: true + dargs@8.1.0: {} - /data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - /data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - /data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - /date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dev: false + date-fns@4.1.0: {} - /dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - dev: false + dayjs@1.11.13: {} - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: false - /debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.7: dependencies: ms: 2.1.3 - /decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 map-obj: 1.0.1 - /decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} + decamelize@1.2.0: {} - /decamelize@5.0.1: - resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} - engines: {node: '>=10'} + decamelize@5.0.1: {} - /deep-equal@2.2.3: - resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} - engines: {node: '>= 0.4'} + deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -3556,156 +6869,101 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: false - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-is@0.1.4: {} - /deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - dev: true + deepmerge@4.3.1: {} - /defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defaults@1.0.4: dependencies: clone: 1.0.4 - dev: true - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 gopd: 1.0.1 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false + delayed-stream@1.0.0: {} - /des.js@1.1.0: - resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + des.js@1.1.0: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true + detect-indent@6.1.0: {} - /diffie-hellman@5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + diffie-hellman@5.0.3: dependencies: bn.js: 4.12.0 miller-rabin: 4.0.1 randombytes: 2.1.0 - dev: true - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - /dnd-core@16.0.1: - resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} + dnd-core@16.0.1: dependencies: '@react-dnd/asap': 5.0.2 '@react-dnd/invariant': 4.0.2 redux: 4.2.1 - dev: false - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: false - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + doctrine@3.0.0: dependencies: esutils: 2.0.3 - /dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.25.6 csstype: 3.1.3 - dev: false - /dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 - dev: false - /domain-browser@4.23.0: - resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} - engines: {node: '>=10'} - dev: true + domain-browser@4.23.0: {} - /domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - dev: false + domelementtype@2.3.0: {} - /domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 - dev: false - /domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + domutils@3.1.0: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 domhandler: 5.0.3 - dev: false - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dot-case@3.0.4: dependencies: no-case: 3.0.4 tslib: 2.7.0 - dev: true - /dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} + dotenv@16.4.5: {} - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /electron-to-chromium@1.5.18: - resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==} - dev: true + electron-to-chromium@1.5.18: {} - /elliptic@6.5.7: - resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} + elliptic@6.5.7: dependencies: bn.js: 4.12.0 brorand: 1.1.0 @@ -3714,65 +6972,39 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: true - /emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} - dev: true + emoji-regex@10.4.0: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emoji-regex@9.2.2: {} - /enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} - engines: {node: '>=10.13.0'} + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 - dev: false - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - dev: true - /entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - dev: false + entities@4.5.0: {} - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true + env-paths@2.2.1: {} - /environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - dev: true + environment@1.1.0: {} - /errno@0.1.8: - resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} - hasBin: true - requiresBuild: true + errno@0.1.8: dependencies: prr: 1.0.1 optional: true - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - /es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 @@ -3821,18 +7053,13 @@ packages: unbox-primitive: 1.0.2 which-typed-array: 1.1.15 - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + es-errors@1.3.0: {} - /es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-get-iterator@1.1.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 @@ -3843,11 +7070,8 @@ packages: is-string: 1.0.7 isarray: 2.0.5 stop-iteration-iterator: 1.0.0 - dev: false - /es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} - engines: {node: '>= 0.4'} + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -3863,45 +7087,30 @@ packages: internal-slot: 1.0.7 iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 - dev: false - /es-module-lexer@1.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} - dev: false + es-module-lexer@1.5.4: {} - /es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 - /es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 hasown: 2.0.2 - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.0.2: dependencies: hasown: 2.0.2 - dev: false - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - /esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + esbuild@0.23.1: optionalDependencies: '@esbuild/aix-ppc64': 0.23.1 '@esbuild/android-arm': 0.23.1 @@ -3928,169 +7137,89 @@ packages: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 - /escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - dev: true + escalade@3.2.0: {} - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false + escape-html@1.0.3: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} + escape-string-regexp@4.0.0: {} - /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.0): - resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.2 + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0))(eslint@8.57.0): dependencies: confusing-browser-globals: 1.0.11 eslint: 8.57.0 - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) object.assign: 4.1.5 object.entries: 1.1.8 semver: 6.3.1 - dev: false - /eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0)(eslint-plugin-react-hooks@4.6.2)(eslint-plugin-react@7.35.2)(eslint@8.57.0): - resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==} - engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.3 - eslint-plugin-jsx-a11y: ^6.5.1 - eslint-plugin-react: ^7.28.0 - eslint-plugin-react-hooks: ^4.3.0 + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0))(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.35.2(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 - eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.0) - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.0) eslint-plugin-react: 7.35.2(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) object.assign: 4.1.5 object.entries: 1.1.8 - dev: false - /eslint-config-prettier@9.1.0(eslint@8.57.0): - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + eslint-config-prettier@9.1.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: false - /eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.30.0): - resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==} - engines: {node: '>= 4'} - peerDependencies: - eslint-plugin-import: '>=1.4.0' + eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0)): dependencies: - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) - dev: false + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.15.1 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: false - /eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0): - resolution: {integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - eslint-plugin-import-x: '*' - peerDependenciesMeta: - eslint-plugin-import: - optional: true - eslint-plugin-import-x: - optional: true + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 is-glob: 4.0.3 + optionalDependencies: + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): - resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0) transitivePeerDependencies: - supports-color - dev: false - /eslint-plugin-check-file@2.8.0(eslint@8.57.0): - resolution: {integrity: sha512-FvvafMTam2WJYH9uj+FuMxQ1y+7jY3Z6P9T4j2214cH0FBxNzTcmeCiGTj1Lxp3mI6kbbgsXvmgewvf+llKYyw==} - engines: {node: '>=18'} - peerDependencies: - eslint: '>=7.28.0' + eslint-plugin-check-file@2.8.0(eslint@8.57.0): dependencies: eslint: 8.57.0 is-glob: 4.0.3 micromatch: 4.0.8 - dev: false - /eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): - resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -4099,7 +7228,7 @@ packages: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0))(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -4109,17 +7238,14 @@ packages: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0): - resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0): dependencies: aria-query: 5.1.3 array-includes: 3.1.8 @@ -4138,43 +7264,21 @@ packages: object.fromentries: 2.0.8 safe-regex-test: 1.0.3 string.prototype.includes: 2.0.0 - dev: false - /eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3): - resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '*' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3): dependencies: eslint: 8.57.0 - eslint-config-prettier: 9.1.0(eslint@8.57.0) prettier: 3.3.3 prettier-linter-helpers: 1.0.0 synckit: 0.9.1 - dev: false + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@8.57.0) - /eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): - resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: false - /eslint-plugin-react@7.35.2(eslint@8.57.0): - resolution: {integrity: sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-react@7.35.2(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -4195,24 +7299,15 @@ packages: semver: 6.3.1 string.prototype.matchall: 4.0.11 string.prototype.repeat: 1.0.0 - dev: false - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@3.4.3: {} - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true + eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.11.0 @@ -4255,62 +7350,38 @@ packages: transitivePeerDependencies: - supports-color - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@9.6.1: dependencies: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true + esprima@4.0.1: {} - /esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} + esquery@1.6.0: dependencies: estraverse: 5.3.0 - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} + estraverse@5.3.0: {} - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@2.0.2: {} - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + esutils@2.0.3: {} - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - dev: true + eventemitter3@5.0.1: {} - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - dev: true + events@3.3.0: {} - /evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + evp_bytestokey@1.0.3: dependencies: md5.js: 1.3.5 safe-buffer: 5.2.1 - dev: true - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 get-stream: 8.0.1 @@ -4321,35 +7392,22 @@ packages: onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 - dev: true - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: true + extendable-error@0.1.7: {} - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} + external-editor@3.1.0: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: true - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: false + fast-diff@1.3.0: {} - /fast-equals@4.0.3: - resolution: {integrity: sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==} - dev: false + fast-equals@4.0.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -4357,205 +7415,124 @@ packages: merge2: 1.4.1 micromatch: 4.0.8 - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-levenshtein@2.0.6: {} - /fast-uri@3.0.1: - resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fast-uri@3.0.1: {} - /fastest-levenshtein@1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} + fastest-levenshtein@1.0.16: {} - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - /fdir@6.4.2(picomatch@4.0.2): - resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true - dependencies: + fdir@6.4.2(picomatch@4.0.2): + optionalDependencies: picomatch: 4.0.2 - dev: true - /figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 - dev: true - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - /file-entry-cache@7.0.2: - resolution: {integrity: sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==} - engines: {node: '>=12.0.0'} + file-entry-cache@7.0.2: dependencies: flat-cache: 3.2.0 - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - /find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - dev: false + find-root@1.1.0: {} - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - /find-up@7.0.0: - resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} - engines: {node: '>=18'} + find-up@7.0.0: dependencies: locate-path: 7.2.0 path-exists: 5.0.0 unicorn-magic: 0.1.0 - dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@3.2.0: dependencies: flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 - /flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flatted@3.3.1: {} - /follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false + follow-redirects@1.15.9: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.3: dependencies: is-callable: 1.2.7 - /foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} - engines: {node: '>=14'} + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - dev: true - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 functions-have-names: 1.2.3 - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + functions-have-names@1.2.3: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-css-data@2.1.1: - resolution: {integrity: sha512-JpMa/f5P4mDXKg6l5/2cHL5xNY77Jap7tHyduMa6BF0E2a7bQ6Tvaz2BIMjeVYZYLcmOZ5w2Ro0yVJEI41tMbw==} - dev: false + get-css-data@2.1.1: {} - /get-east-asian-width@1.2.0: - resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} - engines: {node: '>=18'} - dev: true + get-east-asian-width@1.2.0: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 @@ -4563,50 +7540,33 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true + get-stream@8.0.1: {} - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - /get-tsconfig@4.8.0: - resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + get-tsconfig@4.8.0: dependencies: resolve-pkg-maps: 1.0.0 - /git-raw-commits@4.0.0: - resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} - engines: {node: '>=16'} - hasBin: true + git-raw-commits@4.0.0: dependencies: dargs: 8.1.0 meow: 12.1.1 split2: 4.2.0 - dev: true - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - /glob@11.0.0: - resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} - engines: {node: 20 || >=22} - hasBin: true + glob@11.0.0: dependencies: foreground-child: 3.3.0 jackspeak: 4.0.1 @@ -4614,11 +7574,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 2.0.0 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -4627,47 +7584,32 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} + global-directory@4.0.1: dependencies: ini: 4.1.1 - dev: true - /global-modules@2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} + global-modules@2.0.0: dependencies: global-prefix: 3.0.0 - /global-prefix@3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} + global-prefix@3.0.0: dependencies: ini: 1.3.8 kind-of: 6.0.3 which: 1.3.1 - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@13.24.0: dependencies: type-fest: 0.20.2 - /globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.0.1 - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -4676,223 +7618,134 @@ packages: merge2: 1.4.1 slash: 3.0.0 - /globjoin@0.1.4: - resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + globjoin@0.1.4: {} - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graceful-fs@4.2.11: {} - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphemer@1.4.0: {} - /hammerjs@2.0.8: - resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} - engines: {node: '>=0.8.0'} - dev: false + hammerjs@2.0.8: {} - /hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} + hard-rejection@2.1.0: {} - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-bigints@1.0.2: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.0.3: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - /hash-base@3.0.4: - resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} - engines: {node: '>=4'} + hash-base@3.0.4: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /hash-base@3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} + hash-base@3.1.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 safe-buffer: 5.2.1 - dev: true - /hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + hash.js@1.1.7: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: true - /hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 - dev: false - /hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - dev: true + hosted-git-info@2.8.9: {} - /hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} + hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 - /html-tags@3.3.1: - resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} - engines: {node: '>=8'} + html-tags@3.3.1: {} - /htmlparser2@8.0.2: - resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.1.0 entities: 4.5.0 - dev: false - /https-browserify@1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - dev: true + https-browserify@1.0.0: {} - /human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - dev: true + human-id@1.0.2: {} - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true + human-signals@5.0.0: {} - /husky@9.1.5: - resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==} - engines: {node: '>=18'} - hasBin: true - dev: true + husky@9.1.5: {} - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: true - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - requiresBuild: true + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 optional: true - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true + ieee754@1.2.1: {} - /ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} + ignore@5.3.2: {} - /image-size@0.5.5: - resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} - engines: {node: '>=0.10.0'} - hasBin: true - requiresBuild: true + image-size@0.5.5: optional: true - /immer@10.1.1: - resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} - dev: false + immer@10.1.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} + import-lazy@4.0.0: {} - /import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - dev: true + import-meta-resolve@4.1.0: {} - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} + imurmurhash@0.1.4: {} - /indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} + indent-string@5.0.0: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@1.3.8: {} - /ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + ini@4.1.1: {} - /inquirer@8.2.6: - resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} - engines: {node: '>=12.0.0'} + inquirer@8.2.6: dependencies: ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -4909,462 +7762,277 @@ packages: strip-ansi: 6.0.1 through: 2.3.8 wrap-ansi: 6.2.0 - dev: true - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 - /intersection-observer@0.12.2: - resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} - dev: false + intersection-observer@0.12.2: {} - /intl-format-cache@4.3.1: - resolution: {integrity: sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==} - dev: false + intl-format-cache@4.3.1: {} - /intl-messageformat-parser@3.6.4: - resolution: {integrity: sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==} - deprecated: We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser + intl-messageformat-parser@3.6.4: dependencies: '@formatjs/intl-unified-numberformat': 3.3.7 - dev: false - /intl-messageformat@7.8.4: - resolution: {integrity: sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==} + intl-messageformat@7.8.4: dependencies: intl-format-cache: 4.3.1 intl-messageformat-parser: 3.6.4 - dev: false - /invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + invariant@2.2.4: dependencies: loose-envify: 1.4.0 - dev: false - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} + is-arguments@1.1.1: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.2.1: {} - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 - dev: false - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - dev: true - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 - dev: true - /is-bun-module@1.2.1: - resolution: {integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==} + is-bun-module@1.2.1: dependencies: semver: 7.6.3 - dev: false - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} + is-callable@1.2.7: {} - /is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} + is-core-module@2.15.1: dependencies: hasown: 2.0.2 - /is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} + is-extglob@2.1.1: {} - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.0.2: dependencies: call-bind: 1.0.7 - dev: false - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + is-fullwidth-code-point@3.0.0: {} - /is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - dev: true + is-fullwidth-code-point@4.0.0: {} - /is-fullwidth-code-point@5.0.0: - resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} - engines: {node: '>=18'} + is-fullwidth-code-point@5.0.0: dependencies: get-east-asian-width: 1.2.0 - dev: true - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} + is-generator-function@1.0.10: dependencies: has-tostringtag: 1.0.2 - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - /is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - dev: true + is-interactive@1.0.0: {} - /is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - dev: false + is-map@2.0.3: {} - /is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - dev: true + is-module@1.0.0: {} - /is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} + is-nan@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - dev: true - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} + is-number@7.0.0: {} - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} + is-obj@2.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} + is-path-inside@3.0.3: {} - /is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} + is-plain-obj@1.1.0: {} - /is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} + is-plain-object@5.0.0: {} - /is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 - dev: true - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - dev: false + is-set@2.0.3: {} - /is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-stream@3.0.0: {} - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - dev: true - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - /is-text-path@2.0.0: - resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} - engines: {node: '>=8'} + is-text-path@2.0.0: dependencies: text-extensions: 2.4.0 - dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - dev: false + is-weakmap@2.0.2: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - /is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} - engines: {node: '>= 0.4'} + is-weakset@2.0.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: false - /is-what@3.14.1: - resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + is-what@3.14.1: {} - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: true + is-windows@1.0.2: {} - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true + isarray@1.0.0: {} - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isarray@2.0.5: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@2.0.0: {} - /isomorphic-timers-promises@1.0.1: - resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} - engines: {node: '>=10'} - dev: true + isomorphic-timers-promises@1.0.1: {} - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 get-intrinsic: 1.2.4 has-symbols: 1.0.3 reflect.getprototypeof: 1.0.6 set-function-name: 2.0.2 - dev: false - /jackspeak@4.0.1: - resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} - engines: {node: 20 || >=22} + jackspeak@4.0.1: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jiti@1.21.6: - resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} - hasBin: true - dev: true + jiti@1.21.6: {} - /js-cookie@3.0.5: - resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} - engines: {node: '>=14'} - dev: false + js-cookie@3.0.5: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@4.0.0: {} - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: false + js-tokens@9.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-buffer@3.0.1: {} - /json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - dev: true + json-parse-better-errors@1.0.2: {} - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@2.3.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@0.4.1: {} - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stable-stringify-without-jsonify@1.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: false - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: true + jsonparse@1.3.1: {} - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 object.values: 1.2.0 - dev: false - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - /kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} + kind-of@6.0.3: {} - /known-css-properties@0.29.0: - resolution: {integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==} + known-css-properties@0.29.0: {} - /language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - dev: false + language-subtag-registry@0.3.23: {} - /language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} + language-tags@1.0.9: dependencies: language-subtag-registry: 0.3.23 - dev: false - /less@4.2.0: - resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} - engines: {node: '>=6'} - hasBin: true + less@4.2.0: dependencies: copy-anything: 2.0.6 parse-node-version: 1.0.1 @@ -5378,25 +8046,16 @@ packages: needle: 3.3.1 source-map: 0.6.1 - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - /lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} - engines: {node: '>=14'} - dev: true + lilconfig@3.1.2: {} - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lines-and-columns@1.2.4: {} - /lint-staged@15.2.10: - resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} - engines: {node: '>=18.12.0'} - hasBin: true + lint-staged@15.2.10: dependencies: chalk: 5.3.0 commander: 12.1.0 @@ -5410,11 +8069,8 @@ packages: yaml: 2.5.1 transitivePeerDependencies: - supports-color - dev: true - /listr2@8.2.4: - resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} - engines: {node: '>=18.0.0'} + listr2@8.2.4: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 @@ -5422,185 +8078,115 @@ packages: log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 9.0.0 - dev: true - /load-json-file@4.0.0: - resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} - engines: {node: '>=4'} + load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 parse-json: 4.0.0 pify: 3.0.0 strip-bom: 3.0.0 - dev: true - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - /locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + locate-path@7.2.0: dependencies: p-locate: 6.0.0 - dev: true - /lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash-es@4.17.21: {} - /lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - dev: true + lodash.camelcase@4.3.0: {} - /lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: true + lodash.isplainobject@4.0.6: {} - /lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - dev: true + lodash.kebabcase@4.1.1: {} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.merge@4.6.2: {} - /lodash.mergewith@4.6.2: - resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} - dev: true + lodash.mergewith@4.6.2: {} - /lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - dev: true + lodash.snakecase@4.1.1: {} - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: true + lodash.startcase@4.4.0: {} - /lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.truncate@4.4.2: {} - /lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - dev: true + lodash.uniq@4.5.0: {} - /lodash.upperfirst@4.3.1: - resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - dev: true + lodash.upperfirst@4.3.1: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.21: {} - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} + log-update@6.1.0: dependencies: ansi-escapes: 7.0.0 cli-cursor: 5.0.0 slice-ansi: 7.1.0 strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - dev: true - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lower-case@2.0.2: dependencies: tslib: 2.7.0 - dev: true - /lru-cache@11.0.1: - resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} - engines: {node: 20 || >=22} - dev: true + lru-cache@11.0.1: {} - /lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: true - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - /make-dir@2.1.0: - resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} - engines: {node: '>=6'} - requiresBuild: true + make-dir@2.1.0: dependencies: pify: 4.0.1 semver: 5.7.2 optional: true - /map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} + map-obj@1.0.1: {} - /map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} + map-obj@4.3.0: {} - /material-colors@1.2.6: - resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} - dev: true + material-colors@1.2.6: {} - /mathml-tag-names@2.1.3: - resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mathml-tag-names@2.1.3: {} - /md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + md5.js@1.3.5: dependencies: hash-base: 3.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdn-data@2.0.30: {} - /memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - dev: true + memorystream@0.3.1: {} - /meow@10.1.5: - resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + meow@10.1.5: dependencies: '@types/minimist': 1.2.5 camelcase-keys: 7.0.2 @@ -5615,163 +8201,91 @@ packages: type-fest: 1.4.0 yargs-parser: 20.2.9 - /meow@12.1.1: - resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} - engines: {node: '>=16.10'} - dev: true + meow@12.1.1: {} - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true + merge-stream@2.0.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} + merge2@1.4.1: {} - /micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 - /miller-rabin@4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true + miller-rabin@4.0.1: dependencies: bn.js: 4.12.0 brorand: 1.1.0 - dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - dev: false - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - requiresBuild: true + mime@1.6.0: optional: true - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true + mimic-fn@2.1.0: {} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true + mimic-fn@4.0.0: {} - /mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - dev: true + mimic-function@5.0.1: {} - /min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} + min-indent@1.0.1: {} - /minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: true + minimalistic-assert@1.0.1: {} - /minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - dev: true + minimalistic-crypto-utils@1.0.1: {} - /minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} + minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: false - /minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} + minimist-options@4.1.0: dependencies: arrify: 1.0.1 is-plain-obj: 1.1.0 kind-of: 6.0.3 - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimist@1.2.8: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + mri@1.2.0: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - dev: true + mute-stream@0.0.8: {} - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true + nanoid@3.3.7: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + natural-compare@1.4.0: {} - /needle@3.3.1: - resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} - engines: {node: '>= 4.4.x'} - hasBin: true - requiresBuild: true + needle@3.3.1: dependencies: iconv-lite: 0.6.3 sax: 1.4.1 optional: true - /nice-try@1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - dev: true + nice-try@1.0.5: {} - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + no-case@3.0.4: dependencies: lower-case: 2.0.2 tslib: 2.7.0 - dev: true - /node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - dev: true + node-releases@2.0.18: {} - /node-stdlib-browser@1.2.0: - resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} - engines: {node: '>=10'} + node-stdlib-browser@1.2.0: dependencies: assert: 2.1.0 browser-resolve: 2.0.0 @@ -5800,34 +8314,24 @@ packages: url: 0.11.4 util: 0.12.5 vm-browserify: 1.1.2 - dev: true - /normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 resolve: 1.22.8 semver: 5.7.2 validate-npm-package-license: 3.0.4 - dev: true - /normalize-package-data@3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} + normalize-package-data@3.0.3: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.15.1 semver: 7.6.3 validate-npm-package-license: 3.0.4 - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + normalize-path@3.0.0: {} - /npm-run-all@4.1.5: - resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} - engines: {node: '>= 4'} - hasBin: true + npm-run-all@4.1.5: dependencies: ansi-styles: 3.2.1 chalk: 2.4.2 @@ -5838,109 +8342,71 @@ packages: read-pkg: 3.0.0 shell-quote: 1.8.1 string.prototype.padend: 3.1.6 - dev: true - /npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + object-assign@4.1.1: {} - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} + object-inspect@1.13.2: {} - /object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} + object-is@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} + object-keys@1.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - /object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} + object.entries@1.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: false - /object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: false - /object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} + object.groupby@1.0.3: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 - dev: false - /object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: false - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - dev: true - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - dev: true - /onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} + onetime@7.0.0: dependencies: mimic-function: 5.0.1 - dev: true - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: deep-is: 0.1.4 fast-levenshtein: 2.0.6 @@ -5949,9 +8415,7 @@ packages: type-check: 0.4.0 word-wrap: 1.2.5 - /ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} + ora@5.4.1: dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -5962,106 +8426,61 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true - /os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - dev: true + os-browserify@0.3.0: {} - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: true + os-tmpdir@1.0.2: {} - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: true + outdent@0.5.0: {} - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} + p-filter@2.1.0: dependencies: p-map: 2.1.0 - dev: true - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + p-limit@2.3.0: dependencies: p-try: 2.2.0 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@4.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + p-locate@4.1.0: dependencies: p-limit: 2.3.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - /p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@6.0.0: dependencies: p-limit: 4.0.0 - dev: true - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + p-map@2.1.0: {} - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true + p-try@2.2.0: {} - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /package-manager-detector@0.2.0: - resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} - dev: true + package-manager-detector@0.2.0: {} - /pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - dev: true + pako@1.0.11: {} - /param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + param-case@3.0.4: dependencies: dot-case: 3.0.4 tslib: 2.7.0 - dev: true - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-asn1@5.1.7: - resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} - engines: {node: '>= 0.10'} + parse-asn1@5.1.7: dependencies: asn1.js: 4.10.1 browserify-aes: 1.2.0 @@ -6069,277 +8488,155 @@ packages: hash-base: 3.0.4 pbkdf2: 3.1.2 safe-buffer: 5.2.1 - dev: true - /parse-json@4.0.0: - resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} - engines: {node: '>=4'} + parse-json@4.0.0: dependencies: error-ex: 1.3.2 json-parse-better-errors: 1.0.2 - dev: true - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parse-node-version@1.0.1: - resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} - engines: {node: '>= 0.10'} + parse-node-version@1.0.1: {} - /pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + pascal-case@3.1.2: dependencies: no-case: 3.0.4 tslib: 2.7.0 - dev: true - /path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - dev: true + path-browserify@1.0.1: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + path-exists@4.0.0: {} - /path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + path-exists@5.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} + path-is-absolute@1.0.1: {} - /path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - dev: true + path-key@2.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + path-key@3.1.1: {} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true + path-key@4.0.0: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-parse@1.0.7: {} - /path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} - engines: {node: 20 || >=22} + path-scurry@2.0.0: dependencies: lru-cache: 11.0.1 minipass: 7.1.2 - dev: true - /path-type@3.0.0: - resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} - engines: {node: '>=4'} + path-type@3.0.0: dependencies: pify: 3.0.0 - dev: true - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + path-type@4.0.0: {} - /pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} + pbkdf2@3.1.2: dependencies: create-hash: 1.2.0 create-hmac: 1.1.7 ripemd160: 2.0.2 safe-buffer: 5.2.1 sha.js: 2.4.11 - dev: true - /picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + picocolors@1.1.0: {} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} + picomatch@2.3.1: {} - /picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - dev: true + picomatch@4.0.2: {} - /pidtree@0.3.1: - resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} - engines: {node: '>=0.10'} - hasBin: true - dev: true + pidtree@0.3.1: {} - /pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - dev: true + pidtree@0.6.0: {} - /pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - dev: true + pify@3.0.0: {} - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} + pify@4.0.1: {} - /pkg-dir@5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} + pkg-dir@5.0.0: dependencies: find-up: 5.0.0 - dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} + possible-typed-array-names@1.0.0: {} - /postcss-html@1.7.0: - resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==} - engines: {node: ^12 || >=14} + postcss-html@1.7.0: dependencies: htmlparser2: 8.0.2 js-tokens: 9.0.0 postcss: 8.4.47 postcss-safe-parser: 6.0.0(postcss@8.4.47) - dev: false - /postcss-less@6.0.0(postcss@8.4.45): - resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==} - engines: {node: '>=12'} - peerDependencies: - postcss: ^8.3.5 + postcss-less@6.0.0(postcss@8.4.45): dependencies: postcss: 8.4.45 - dev: false - /postcss-resolve-nested-selector@0.1.6: - resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + postcss-resolve-nested-selector@0.1.6: {} - /postcss-safe-parser@6.0.0(postcss@8.4.47): - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.3.3 + postcss-safe-parser@6.0.0(postcss@8.4.47): dependencies: - postcss: 8.4.47 - - /postcss-scss@4.0.9(postcss@8.4.45): - resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.4.29 + postcss: 8.4.47 + + postcss-scss@4.0.9(postcss@8.4.45): dependencies: postcss: 8.4.45 - dev: false - /postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-sorting@8.0.2(postcss@8.4.47): - resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} - peerDependencies: - postcss: ^8.4.20 + postcss-sorting@8.0.2(postcss@8.4.47): dependencies: postcss: 8.4.47 - dev: false - /postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss-value-parser@4.2.0: {} - /postcss@8.4.45: - resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.45: dependencies: nanoid: 3.3.7 picocolors: 1.1.0 source-map-js: 1.2.1 - dev: false - /postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.47: dependencies: nanoid: 3.3.7 picocolors: 1.1.0 source-map-js: 1.2.1 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} + prelude-ls@1.2.1: {} - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} + prettier-linter-helpers@1.0.0: dependencies: fast-diff: 1.3.0 - dev: false - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + prettier@2.8.8: {} - /prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} - engines: {node: '>=14'} - hasBin: true - dev: false + prettier@3.3.3: {} - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true + process-nextick-args@2.0.1: {} - /process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - dev: true + process@0.11.10: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true + progress@2.0.3: {} - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false + proxy-from-env@1.1.0: {} - /prr@1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - requiresBuild: true + prr@1.0.1: optional: true - /pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: true + pseudomap@1.0.2: {} - /public-encrypt@4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + public-encrypt@4.0.3: dependencies: bn.js: 4.12.0 browserify-rsa: 4.1.0 @@ -6347,57 +8644,35 @@ packages: parse-asn1: 5.1.7 randombytes: 2.1.0 safe-buffer: 5.2.1 - dev: true - /punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - dev: true + punycode@1.4.1: {} - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} + qs@6.13.0: dependencies: side-channel: 1.0.6 - /querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - dev: true + querystring-es3@0.2.1: {} - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue-microtask@1.2.3: {} - /quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} + quick-lru@5.1.1: {} - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - dev: true - /randomfill@1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + randomfill@1.0.4: dependencies: randombytes: 2.1.0 safe-buffer: 5.2.1 - dev: true - /rd@2.0.1: - resolution: {integrity: sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw==} + rd@2.0.1: dependencies: '@types/node': 10.17.60 - dev: true - /react-color@2.19.3(react@18.3.1): - resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} - peerDependencies: - react: '*' + react-color@2.19.3(react@18.3.1): dependencies: '@icons/material': 0.2.4(react@18.3.1) lodash: 4.17.21 @@ -6407,95 +8682,54 @@ packages: react: 18.3.1 reactcss: 1.2.3(react@18.3.1) tinycolor2: 1.6.0 - dev: true - /react-dnd-html5-backend@16.0.1: - resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==} + react-dnd-html5-backend@16.0.1: dependencies: dnd-core: 16.0.1 - dev: false - /react-dnd@16.0.1(@types/node@20.16.5)(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==} - peerDependencies: - '@types/hoist-non-react-statics': '>= 3.3.1' - '@types/node': '>= 12' - '@types/react': '>= 16' - react: '>= 16.14' - peerDependenciesMeta: - '@types/hoist-non-react-statics': - optional: true - '@types/node': - optional: true - '@types/react': - optional: true + react-dnd@16.0.1(@types/node@20.16.5)(@types/react@18.3.5)(react@18.3.1): dependencies: '@react-dnd/invariant': 4.0.2 '@react-dnd/shallowequal': 4.0.2 - '@types/node': 20.16.5 - '@types/react': 18.3.5 dnd-core: 16.0.1 fast-deep-equal: 3.1.3 hoist-non-react-statics: 3.3.2 react: 18.3.1 - dev: false + optionalDependencies: + '@types/node': 20.16.5 + '@types/react': 18.3.5 - /react-dom@18.3.1(react@18.3.1): - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} - peerDependencies: - react: ^18.3.1 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 - dev: false - /react-draggable@4.4.6(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' + react-draggable@4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 1.2.1 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: false - /react-fast-compare@3.2.2: - resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - dev: false + react-fast-compare@3.2.2: {} - /react-grid-layout@1.5.0(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-WBKX7w/LsTfI99WskSu6nX2nbJAUD7GD6nIXcwYLyPpnslojtmql2oD3I2g5C3AK8hrxIarYT8awhuDIp7iQ5w==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' + react-grid-layout@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 2.1.1 fast-equals: 4.0.3 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) - react-resizable: 3.0.5(react-dom@18.3.1)(react@18.3.1) + react-draggable: 4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-resizable: 3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) resize-observer-polyfill: 1.5.1 - dev: false - /react-hook-form@7.53.0(react@18.3.1): - resolution: {integrity: sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 + react-hook-form@7.53.0(react@18.3.1): dependencies: react: 18.3.1 - dev: false - /react-intl-universal@2.11.1(react@18.3.1): - resolution: {integrity: sha512-wkmbd7Qohnr8ch02ZzF6uLZAOvdsHbvMjhiS13WUeberdrraOma4bOnn2hUVnEa/LXEvYRD0ta0RBPOV0g2HoA==} - requiresBuild: true - peerDependencies: - react: '*' + react-intl-universal@2.11.1(react@18.3.1): dependencies: cookie: 0.3.1 escape-html: 1.0.3 @@ -6504,60 +8738,34 @@ packages: lodash.merge: 4.6.2 object-keys: 1.1.1 react: 18.3.1 - dev: false - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@16.13.1: {} - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: false + react-is@18.3.1: {} - /react-refresh@0.14.2: - resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} - engines: {node: '>=0.10.0'} - dev: true + react-refresh@0.14.2: {} - /react-resizable@3.0.5(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} - peerDependencies: - react: '>= 16.3' + react-resizable@3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: prop-types: 15.8.1 react: 18.3.1 - react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) + react-draggable: 4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - react-dom - dev: false - /react-router-dom@6.26.1(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' + react-router-dom@6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@remix-run/router': 1.19.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-router: 6.26.1(react@18.3.1) - dev: false - /react-router@6.26.1(react@18.3.1): - resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' + react-router@6.26.1(react@18.3.1): dependencies: '@remix-run/router': 1.19.1 react: 18.3.1 - dev: false - /react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.6 dom-helpers: 5.2.1 @@ -6565,61 +8773,43 @@ packages: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: false - /react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} + react@18.3.1: dependencies: loose-envify: 1.4.0 - /reactcss@1.2.3(react@18.3.1): - resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} - peerDependencies: - react: '*' + reactcss@1.2.3(react@18.3.1): dependencies: lodash: 4.17.21 react: 18.3.1 - dev: true - /read-pkg-up@8.0.0: - resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} - engines: {node: '>=12'} + read-pkg-up@8.0.0: dependencies: find-up: 5.0.0 read-pkg: 6.0.0 type-fest: 1.4.0 - /read-pkg@3.0.0: - resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} - engines: {node: '>=4'} + read-pkg@3.0.0: dependencies: load-json-file: 4.0.0 normalize-package-data: 2.5.0 path-type: 3.0.0 - dev: true - /read-pkg@6.0.0: - resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} - engines: {node: '>=12'} + read-pkg@6.0.0: dependencies: '@types/normalize-package-data': 2.4.4 normalize-package-data: 3.0.3 parse-json: 5.2.0 type-fest: 1.4.0 - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -6628,40 +8818,27 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - dev: true - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - /redent@4.0.0: - resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} - engines: {node: '>=12'} + redent@4.0.0: dependencies: indent-string: 5.0.0 strip-indent: 4.0.0 - /redux@4.2.1: - resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + redux@4.2.1: dependencies: '@babel/runtime': 7.25.6 - dev: false - /reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} - engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -6670,124 +8847,75 @@ packages: get-intrinsic: 1.2.4 globalthis: 1.0.4 which-builtin-type: 1.1.4 - dev: false - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regenerator-runtime@0.14.1: {} - /regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - dev: false + reselect@5.1.1: {} - /resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - dev: false + resize-observer-polyfill@1.5.1: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@4.0.0: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} + resolve-from@5.0.0: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-pkg-maps@1.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: false - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: true - /restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 signal-exit: 4.1.0 - dev: true - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + reusify@1.0.4: {} - /rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - dev: true + rfdc@1.4.1: {} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - /rimraf@6.0.1: - resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} - engines: {node: 20 || >=22} - hasBin: true + rimraf@6.0.1: dependencies: glob: 11.0.0 package-json-from-dist: 1.0.0 - dev: true - /ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + ripemd160@2.0.2: dependencies: hash-base: 3.1.0 inherits: 2.0.4 - dev: true - /rollup-plugin-peer-deps-external@2.2.4(rollup@4.24.0): - resolution: {integrity: sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==} - peerDependencies: - rollup: '*' + rollup-plugin-peer-deps-external@2.2.4(rollup@4.24.0): dependencies: rollup: 4.24.0 - dev: true - /rollup@4.24.0: - resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.24.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: @@ -6809,83 +8937,51 @@ packages: '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 - /run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - dev: true + run-async@2.4.1: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.1: dependencies: tslib: 2.7.0 - dev: true - /safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + safer-buffer@2.1.2: {} - /sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - requiresBuild: true + sax@1.4.1: optional: true - /scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 - dev: false - /screenfull@5.2.0: - resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} - engines: {node: '>=0.10.0'} - dev: false + screenfull@5.2.0: {} - /semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - requiresBuild: true + semver@5.7.2: {} - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true + semver@6.3.1: {} - /semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.3: {} - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -6894,211 +8990,135 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.2 - /set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 - /setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: true + setimmediate@1.0.5: {} - /sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true + sha.js@2.4.11: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 - dev: true - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - /shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - dev: true + shebang-regex@1.0.0: {} - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} + side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 object-inspect: 1.13.2 - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} + signal-exit@4.1.0: {} - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + slash@3.0.0: {} - /slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - /slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.1 is-fullwidth-code-point: 4.0.0 - dev: true - /slice-ansi@7.1.0: - resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} - engines: {node: '>=18'} + slice-ansi@7.1.0: dependencies: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 - dev: true - /source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + source-map-js@1.2.1: {} - /source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - dev: false + source-map@0.5.7: {} - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - requiresBuild: true + source-map@0.6.1: optional: true - /spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spawndamnit@2.0.0: dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 - dev: true - /spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.20 - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.20 - /spdx-license-ids@3.0.20: - resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + spdx-license-ids@3.0.20: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - dev: true + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + sprintf-js@1.0.3: {} - /stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} + stop-iteration-iterator@1.0.0: dependencies: internal-slot: 1.0.7 - dev: false - /stream-browserify@3.0.0: - resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-browserify@3.0.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /stream-http@3.2.0: - resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + stream-http@3.2.0: dependencies: builtin-status-codes: 3.0.0 inherits: 2.0.4 readable-stream: 3.6.2 xtend: 4.0.2 - dev: true - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true + string-argv@0.3.2: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - /string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} + string-width@7.2.0: dependencies: emoji-regex: 10.4.0 get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 - dev: true - /string.prototype.includes@2.0.0: - resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} + string.prototype.includes@2.0.0: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 - dev: false - /string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} - engines: {node: '>= 0.4'} + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -7112,154 +9132,98 @@ packages: regexp.prototype.flags: 1.5.2 set-function-name: 2.0.2 side-channel: 1.0.6 - dev: false - /string.prototype.padend@3.1.6: - resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} - engines: {node: '>= 0.4'} + string.prototype.padend@3.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 - dev: false - /string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - /string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - /string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - dev: true - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - dev: true - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} + strip-bom@3.0.0: {} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true + strip-final-newline@3.0.0: {} - /strip-indent@4.0.0: - resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} - engines: {node: '>=12'} + strip-indent@4.0.0: dependencies: min-indent: 1.0.1 - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} + strip-json-comments@3.1.1: {} - /style-search@0.1.0: - resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + style-mod@4.1.2: {} - /stylelint-config-prettier@9.0.5(stylelint@15.11.0): - resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} - engines: {node: '>= 12'} - hasBin: true - peerDependencies: - stylelint: '>= 11.x < 15' + style-search@0.1.0: {} + + stylelint-config-prettier@9.0.5(stylelint@15.11.0(typescript@5.6.2)): dependencies: stylelint: 15.11.0(typescript@5.6.2) - dev: false - /stylelint-config-property-sort-order-smacss@10.0.0(stylelint@15.11.0): - resolution: {integrity: sha512-NuiTgyqD8UdYY1IpTBIodBbrWKwaib5r8sq5kGHQ52UrmT8O7Fa8ZWYGipSZw6k9tGoljl9Hng2jtH+wBTMa1Q==} - engines: {node: '>=18.12.0'} - peerDependencies: - stylelint: ^14.0.0 || ^15.0.0 || ^16.0.0 + stylelint-config-property-sort-order-smacss@10.0.0(stylelint@15.11.0(typescript@5.6.2)): dependencies: css-property-sort-order-smacss: 2.2.0 stylelint: 15.11.0(typescript@5.6.2) - stylelint-order: 6.0.4(stylelint@15.11.0) - dev: false + stylelint-order: 6.0.4(stylelint@15.11.0(typescript@5.6.2)) - /stylelint-config-recommended@13.0.0(stylelint@15.11.0): - resolution: {integrity: sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ==} - engines: {node: ^14.13.1 || >=16.0.0} - peerDependencies: - stylelint: ^15.10.0 + stylelint-config-recommended@13.0.0(stylelint@15.11.0(typescript@5.6.2)): dependencies: stylelint: 15.11.0(typescript@5.6.2) - dev: false - /stylelint-config-standard@34.0.0(stylelint@15.11.0): - resolution: {integrity: sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ==} - engines: {node: ^14.13.1 || >=16.0.0} - peerDependencies: - stylelint: ^15.10.0 + stylelint-config-standard@34.0.0(stylelint@15.11.0(typescript@5.6.2)): dependencies: stylelint: 15.11.0(typescript@5.6.2) - stylelint-config-recommended: 13.0.0(stylelint@15.11.0) - dev: false + stylelint-config-recommended: 13.0.0(stylelint@15.11.0(typescript@5.6.2)) - /stylelint-order@6.0.4(stylelint@15.11.0): - resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} - peerDependencies: - stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + stylelint-order@6.0.4(stylelint@15.11.0(typescript@5.6.2)): dependencies: postcss: 8.4.47 postcss-sorting: 8.0.2(postcss@8.4.47) stylelint: 15.11.0(typescript@5.6.2) - dev: false - /stylelint@15.11.0(typescript@5.6.2): - resolution: {integrity: sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==} - engines: {node: ^14.13.1 || >=16.0.0} - hasBin: true + stylelint@15.11.0(typescript@5.6.2): dependencies: '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-tokenizer': 2.4.1 - '@csstools/media-query-list-parser': 2.1.13(@csstools/css-parser-algorithms@2.7.1)(@csstools/css-tokenizer@2.4.1) + '@csstools/media-query-list-parser': 2.1.13(@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1))(@csstools/css-tokenizer@2.4.1) '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) balanced-match: 2.0.0 colord: 2.9.3 @@ -7301,47 +9265,31 @@ packages: - supports-color - typescript - /stylis@4.2.0: - resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - dev: false + stylis@4.2.0: {} - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-hyperlinks@3.1.0: - resolution: {integrity: sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==} - engines: {node: '>=14.18'} + supports-hyperlinks@3.1.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /svg-tags@1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + svg-tags@1.0.0: {} - /synckit@0.9.1: - resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} - engines: {node: ^14.18.0 || >=16.0.0} + synckit@0.9.1: dependencies: '@pkgr/core': 0.1.1 tslib: 2.7.0 - dev: false - /table@6.8.2: - resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} - engines: {node: '>=10.0.0'} + table@6.8.2: dependencies: ajv: 8.17.1 lodash.truncate: 4.4.2 @@ -7349,130 +9297,75 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - dev: false + tapable@2.2.1: {} - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true + term-size@2.2.1: {} - /text-extensions@2.4.0: - resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} - engines: {node: '>=8'} - dev: true + text-extensions@2.4.0: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + text-table@0.2.0: {} - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true + through@2.3.8: {} - /timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} + timers-browserify@2.0.12: dependencies: setimmediate: 1.0.5 - dev: true - /tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - dev: true + tinycolor2@1.6.0: {} - /tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - dev: true + tinyexec@0.3.0: {} - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - dev: true - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + to-fast-properties@2.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - /trim-newlines@4.1.1: - resolution: {integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==} - engines: {node: '>=12'} + trim-newlines@4.1.1: {} - /ts-api-utils@1.3.0(typescript@5.6.2): - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' + ts-api-utils@1.3.0(typescript@5.6.2): dependencies: typescript: 5.6.2 - dev: false - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: false - /tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + tslib@2.7.0: {} - /tsx@4.19.0: - resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} - engines: {node: '>=18.0.0'} - hasBin: true + tsx@4.19.0: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /tty-browserify@0.0.1: - resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - dev: true + tty-browserify@0.0.1: {} - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} + type-fest@0.20.2: {} - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true + type-fest@0.21.3: {} - /type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} + type-fest@1.4.0: {} - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 - /typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -7480,9 +9373,7 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 - /typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -7491,9 +9382,7 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 - /typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -7502,106 +9391,64 @@ packages: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - /typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} - engines: {node: '>=14.17'} - hasBin: true + typescript@5.6.2: {} - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.19.8: {} - /unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} - engines: {node: '>=18'} - dev: true + unicorn-magic@0.1.0: {} - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + universalify@0.1.2: {} - /universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - dev: true + universalify@2.0.1: {} - /update-browserslist-db@1.1.0(browserslist@4.23.3): - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0(browserslist@4.23.3): dependencies: browserslist: 4.23.3 escalade: 3.2.0 picocolors: 1.1.0 - dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /url@0.11.4: - resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} - engines: {node: '>= 0.4'} + url@0.11.4: dependencies: punycode: 1.4.1 qs: 6.13.0 - dev: true - /use-sync-external-store@1.2.2(react@18.3.1): - resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-sync-external-store@1.2.2(react@18.3.1): dependencies: react: 18.3.1 - dev: false - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + util@0.12.5: dependencies: inherits: 2.0.4 is-arguments: 1.1.1 is-generator-function: 1.0.10 is-typed-array: 1.1.13 which-typed-array: 1.1.15 - dev: true - /validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - /validator@13.12.0: - resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} - engines: {node: '>= 0.10'} - dev: false + validator@13.12.0: {} - /vite-plugin-html-config@2.0.2(vite@5.4.9): - resolution: {integrity: sha512-g09u0XsmgKyMUIp1RZSyNSkJWvIusaXxw3KylyxU3vkCq7/G8hyemLctT+4IvO42fCPlNySmrNC9g0qSoKmvpw==} - engines: {node: '>=12.0.0'} - peerDependencies: - vite: '>=5.0.0' + vite-plugin-html-config@2.0.2(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - dev: true - /vite-plugin-imp@2.4.0(vite@5.4.9): - resolution: {integrity: sha512-L/6/nvOw+MyNh4UxAlCZHsmKd5MitmHamqqAWB15sbUgVIEz/OQ8jpKr6kkQU0eA/AIe8fkCVbQBlP81ajrqWg==} - peerDependencies: - vite: '>= 2.0.0-beta.5' + vite-plugin-imp@2.4.0(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: '@babel/core': 7.25.2 '@babel/generator': 7.25.6 @@ -7613,110 +9460,54 @@ packages: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - supports-color - dev: true - /vite-plugin-node-polyfills@0.22.0(vite@5.4.9): - resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} - peerDependencies: - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + vite-plugin-node-polyfills@0.22.0(rollup@4.24.0)(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: - '@rollup/plugin-inject': 5.0.5 + '@rollup/plugin-inject': 5.0.5(rollup@4.24.0) node-stdlib-browser: 1.2.0 vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - rollup - dev: true - /vite-plugin-progress@0.0.7(vite@5.4.9): - resolution: {integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ==} - engines: {node: '>=14', pnpm: '>=7.0.0'} - peerDependencies: - vite: '>2.0.0-0' + vite-plugin-progress@0.0.7(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: picocolors: 1.1.0 progress: 2.0.3 rd: 2.0.1 vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - dev: true - /vite-plugin-stylelint@5.3.1(postcss@8.4.47)(stylelint@15.11.0)(vite@5.4.9): - resolution: {integrity: sha512-M/hSdfOwnOVghbJDeuuYIU2xO/MMukYR8QcEyNKFPG8ro1L+DlTdViix2B2d/FvAw14WPX88ckA5A7NvUjJz8w==} - engines: {node: '>=14.18'} - peerDependencies: - '@types/stylelint': ^13.0.0 - postcss: ^7.0.0 || ^8.0.0 - rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 - stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@types/stylelint': - optional: true - postcss: - optional: true - rollup: - optional: true + vite-plugin-stylelint@5.3.1(postcss@8.4.47)(rollup@4.24.0)(stylelint@15.11.0(typescript@5.6.2))(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) chokidar: 3.6.0 debug: 4.3.7 - postcss: 8.4.47 stylelint: 15.11.0(typescript@5.6.2) vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) + optionalDependencies: + postcss: 8.4.47 + rollup: 4.24.0 transitivePeerDependencies: - supports-color - dev: true - /vite@5.4.9(@types/node@20.16.5)(less@4.2.0): - resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.4.9(@types/node@20.16.5)(less@4.2.0): dependencies: - '@types/node': 20.16.5 esbuild: 0.23.1 - less: 4.2.0 postcss: 8.4.47 rollup: 4.24.0 optionalDependencies: + '@types/node': 20.16.5 fsevents: 2.3.3 + less: 4.2.0 - /vm-browserify@1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - dev: true + vm-browserify@1.1.2: {} - /wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + w3c-keyname@2.2.8: {} + + wcwidth@1.0.1: dependencies: defaults: 1.0.4 - dev: true - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 @@ -7724,9 +9515,7 @@ packages: is-string: 1.0.7 is-symbol: 1.0.4 - /which-builtin-type@1.1.4: - resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} - engines: {node: '>= 0.4'} + which-builtin-type@1.1.4: dependencies: function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 @@ -7740,21 +9529,15 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: false - /which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} + which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.3 - dev: false - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -7762,113 +9545,66 @@ packages: gopd: 1.0.1 has-tostringtag: 1.0.2 - /which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} + word-wrap@1.2.5: {} - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} - engines: {node: '>=18'} + wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 string-width: 7.2.0 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrappy@1.0.2: {} - /write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 signal-exit: 4.1.0 - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: true + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: true + yallist@2.1.2: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: false + yaml@1.10.2: {} - /yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} - engines: {node: '>= 14'} - hasBin: true - dev: true + yaml@2.5.1: {} - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} + yargs-parser@20.2.9: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.2.0 @@ -7877,34 +9613,15 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + yocto-queue@0.1.0: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@1.1.1: {} - /zustand@4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1): - resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} - engines: {node: '>=12.7.0'} - peerDependencies: - '@types/react': '>=16.8' - immer: '>=9.0.6' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true + zustand@4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1): dependencies: + use-sync-external-store: 1.2.2(react@18.3.1) + optionalDependencies: '@types/react': 18.3.5 immer: 10.1.1 react: 18.3.1 - use-sync-external-store: 1.2.2(react@18.3.1) - dev: false From 77ab31c4d735563291426d46667a05a666a755ff Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 16 Dec 2024 17:39:22 +0800 Subject: [PATCH 033/172] feat: add form item components for nodes --- .../components/conditions-input/index.tsx | 4 +- .../components/entity-assign-input/index.tsx | 8 - .../components/entity-assign-select/index.tsx | 98 +++++++ .../entity-assign-select/style.less | 38 +++ .../components/entity-filter-select/index.tsx | 55 ++-- .../components/entity-select/index.tsx | 215 ++++++++-------- .../config-panel/components/index.ts | 3 +- .../components/param-assign-input/index.tsx | 99 ++++++++ .../components/param-assign-input/style.less | 38 +++ .../components/param-input-select/index.tsx | 239 +++++++++++++----- .../components/param-input-select/style.less | 65 ++++- .../components/param-select/index.tsx | 8 +- .../components/param-select/style.less | 26 +- .../editor/components/config-panel/helper.ts | 18 -- .../components/config-panel/hooks/index.ts | 1 + .../config-panel/hooks/useNodeFormItems.tsx | 62 ++++- .../editor/components/config-panel/index.tsx | 103 ++++---- .../editor/components/config-panel/store.ts | 73 ++++++ .../pages/workflow/views/editor/constants.ts | 10 + .../src/pages/workflow/views/editor/helper.ts | 42 +++ .../views/editor/hooks/useWorkflow.ts | 59 ++++- packages/shared/types/entity.d.ts | 2 + 22 files changed, 965 insertions(+), 301 deletions(-) delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/style.less delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/store.ts create mode 100644 apps/web/src/pages/workflow/views/editor/helper.ts diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 6902f157..02cb9de0 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -3,6 +3,8 @@ * * Note: use in IfelseNode */ -const ConditionsInput = () => {}; +const ConditionsInput = () => { + return <>Conditions Input; +}; export default ConditionsInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx deleted file mode 100644 index 09733ae7..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-input/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Entity Assignment Input Component - * - * Note: use in EntityAssignmentNode - */ -const EntityAssignInput = () => {}; - -export default EntityAssignInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx new file mode 100644 index 00000000..bd18bdc5 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx @@ -0,0 +1,98 @@ +import React, { useLayoutEffect } from 'react'; +import { Button, IconButton } from '@mui/material'; +import { isEqual } from 'lodash-es'; +import { useDynamicList, useControllableValue } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; +import EntitySelect from '../entity-select'; +import ParamInputSelect from '../param-input-select'; +import './style.less'; + +export type EntityAssignInputValueType = + | undefined + | { + entityKey?: ApiKey; + ref?: string; + value?: string; + }; + +export interface EntityAssignSelectProps { + label?: string[]; + required?: boolean; + multiple?: boolean; + error?: boolean; + helperText?: React.ReactNode; + value?: EntityAssignInputValueType[]; + defaultValue?: EntityAssignInputValueType[]; + onChange?: (value: EntityAssignInputValueType[]) => void; +} + +const MAX_VALUE_LENGTH = 10; + +/** + * Entity Assignment Input Component + * + * Note: use in EntityAssignmentNode + */ +const EntityAssignSelect: React.FC = ({ + label, + required = true, + multiple = true, + ...props +}) => { + const { getIntlText } = useI18n(); + const [data, setData] = useControllableValue(props); + const { list, remove, getKey, insert, replace, resetList } = + useDynamicList(data || []); + + useLayoutEffect(() => { + if (isEqual(data, list)) return; + resetList(data || []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data, resetList]); + + useLayoutEffect(() => { + setData?.(list); + }, [list, setData]); + + return ( +
+ {list.map((item, index) => ( +
+ { + replace(index, { ...item, entityKey: value }); + }} + /> + { + replace(index, { entityKey: item?.entityKey, ...data }); + }} + /> + remove(index)}> + + +
+ ))} + {multiple && ( + + )} +
+ ); +}; + +export default EntityAssignSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/style.less new file mode 100644 index 00000000..a6191deb --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/style.less @@ -0,0 +1,38 @@ +.@{prefix}-entity-assign-select { + &-item { + display: flex; + gap: @margin-xs; + margin-bottom: @margin-md; + + .@{mui-prefix}FormControl-root { + margin: 0; + } + + > .@{prefix}-param-input-select, + > .@{mui-prefix}Autocomplete-root { + flex: 1; + } + + > .@{prefix}-param-input-select { + .@{prefix}-param-input-select-chip .@{mui-prefix}Chip-label .name { + max-width: 40px; + } + } + + > .@{mui-prefix}ButtonBase-root { + width: 40px; + height: 40px; + } + } + + & &-add-btn { + font-weight: @font-weight-bold; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx index 77544357..30c32e52 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-filter-select/index.tsx @@ -1,4 +1,4 @@ -import { useLayoutEffect } from 'react'; +import { memo, useLayoutEffect } from 'react'; import { Select, Button, @@ -8,6 +8,7 @@ import { MenuItem, type SelectProps, } from '@mui/material'; +import { isEqual } from 'lodash-es'; import { useDynamicList, useControllableValue } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; import { @@ -15,11 +16,12 @@ import { AddIcon, KeyboardArrowDownIcon, } from '@milesight/shared/src/components'; -import EntitySelect, { type EntitySelectProps, type EntitySelectValueType } from '../entity-select'; +import EntitySelect, { type EntitySelectProps } from '../entity-select'; import './style.less'; -export type EntityFilterSelectValueType = EntitySelectValueType & { - type?: EntityType; +export type EntityFilterSelectValueType = { + entityType?: EntityType; + entityKey?: ApiKey; }; export interface EntityFilterSelectProps { @@ -34,10 +36,10 @@ export interface EntityFilterSelectProps { entitySelectProps?: EntitySelectProps; } -const DEFAULT_EMPTY_VALUE: EntityFilterSelectValueType = { - label: '', - value: '', -}; +// const DEFAULT_EMPTY_VALUE: EntityFilterSelectValueType = { +// label: '', +// value: '', +// }; const MAX_VALUE_LENGTH = 10; @@ -58,11 +60,16 @@ const EntityFilterSelect: React.FC = ({ }) => { const { getIntlText } = useI18n(); const [innerValue, setInnerValue] = useControllableValue(props, { - defaultValue: [DEFAULT_EMPTY_VALUE], + defaultValue: [], }); - const { list, remove, getKey, insert, replace } = useDynamicList( - innerValue || [DEFAULT_EMPTY_VALUE], - ); + const { list, remove, getKey, insert, replace, resetList } = + useDynamicList(innerValue || []); + + useLayoutEffect(() => { + if (isEqual(innerValue, list)) return; + resetList(innerValue || []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [innerValue, resetList]); useLayoutEffect(() => { setInnerValue?.(list); @@ -81,9 +88,12 @@ const EntityFilterSelect: React.FC = ({ labelId="entity-filter-select-type-label" label={typeSelectProps?.label || getIntlText('common.label.type')} IconComponent={KeyboardArrowDownIcon} - value={item.type || ''} + value={item.entityType || ''} onChange={e => - replace(index, { ...item, type: e.target.value as EntityType }) + replace(index, { + ...item, + entityType: e.target.value as EntityType, + }) } > {entityTypes.map(type => ( @@ -97,19 +107,14 @@ const EntityFilterSelect: React.FC = ({ label={entitySelectProps?.label || getIntlText('common.label.target')} required={required} disabled={disabled} - value={item} + value={item.entityKey} filterModel={{ - type: item.type, + type: item.entityType, }} - onChange={(_, data) => { - if (!data) { - replace(index, { ...item, ...DEFAULT_EMPTY_VALUE }); - return; - } - + onChange={value => { replace(index, { ...item, - ...data, + entityKey: value, }); }} /> @@ -129,7 +134,7 @@ const EntityFilterSelect: React.FC = ({ disabled={list.length >= MAX_VALUE_LENGTH} onClick={() => { if (list.length >= MAX_VALUE_LENGTH) return; - insert(list.length, DEFAULT_EMPTY_VALUE); + insert(list.length, {}); }} > {getIntlText('common.label.add')} @@ -139,4 +144,4 @@ const EntityFilterSelect: React.FC = ({ ); }; -export default EntityFilterSelect; +export default memo(EntityFilterSelect); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx index 851fa2f7..7ae5c3bc 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx @@ -1,5 +1,4 @@ -import React, { useCallback, useMemo, useRef, forwardRef } from 'react'; -import { useRequest } from 'ahooks'; +import React, { useCallback, useMemo, useState, useRef, forwardRef, useLayoutEffect } from 'react'; import { Autocomplete, TextField, @@ -7,19 +6,17 @@ import { type AutocompleteProps, type AutocompleteRenderInputParams, } from '@mui/material'; -import { useI18n, useVirtualList } from '@milesight/shared/src/hooks'; +import { useControllableValue, useDebounceFn } from 'ahooks'; +import { useI18n, useVirtualList, useStoreShallow } from '@milesight/shared/src/hooks'; import { KeyboardArrowDownIcon } from '@milesight/shared/src/components'; import { Tooltip } from '@/components'; -import { - entityAPI, - awaitWrap, - getResponseData, - isRequestSuccess, - type EntityAPISchema, -} from '@/services/http'; +import { type EntityAPISchema } from '@/services/http'; +import useConfigPanelStore, { type EntityFilterParams } from '../../store'; import './style.less'; -export type EntitySelectValueType = { +export type EntitySelectValueType = ApiKey; + +export type EntitySelectOptionType = { /** Entity Name */ label: string; /** Entity ID */ @@ -36,33 +33,23 @@ export type EntitySelectValueType = { }; }; -export interface EntitySelectProps< - Multiple extends boolean | undefined = false, - DisableClearable extends boolean | undefined = false, - FreeSolo extends boolean | undefined = false, -> extends Omit< - AutocompleteProps, - 'loading' | 'options' | 'renderInput' - > { +export interface EntitySelectProps { label?: string; required?: boolean; + disabled?: boolean; + /** * API Filter Model */ - filterModel?: { - /** Search Keyword */ - keyword?: string; - /** Entity Type */ - type?: EntityType | EntityType[]; - /** Entity Value Type */ - valueType?: EntityValueDataType | EntityValueDataType[]; - /** Entity Access Mode */ - accessMode?: EntityAccessMode | EntityAccessMode[]; - /** Exclude Children */ - excludeChildren?: boolean; - }; + filterModel?: Omit; + + value?: EntitySelectValueType; + + defaultValue?: EntitySelectValueType; + + onChange?: (value: EntitySelectValueType) => void; } /** @@ -91,7 +78,7 @@ const Listbox = forwardRef>( containerTarget: containerRef, wrapperTarget: wrapperRef, itemHeight: 58, - overscan: 10, + overscan: 5, }); return ( @@ -111,52 +98,45 @@ const Listbox = forwardRef>( * * Note: This is a basic component, use in EntityListeningNode, ServiceNode, EntitySelectNode */ -const EntitySelect: React.FC = ({ label, required, filterModel, ...props }) => { +const EntitySelect: React.FC = ({ + label, + required, + disabled, + filterModel, + ...props +}) => { const { getIntlText } = useI18n(); - const { - loading, - data: entityList, - run: getEntityList, - } = useRequest( - async (keyword?: string) => { - const entityType = - filterModel?.type && - (Array.isArray(filterModel.type) ? filterModel.type : [filterModel.type]); - const valueType = - filterModel?.valueType && - (Array.isArray(filterModel.valueType) - ? filterModel.valueType - : [filterModel.valueType]); - const accessMode = - filterModel?.accessMode && - (Array.isArray(filterModel.accessMode) - ? filterModel.accessMode - : [filterModel.accessMode]); - const [error, resp] = await awaitWrap( - entityAPI.getList({ - keyword, - entity_type: entityType, - entity_value_type: valueType, - entity_access_mod: accessMode, - exclude_children: filterModel?.excludeChildren, - page_number: 1, - page_size: 999, - }), - ); + const [value, setValue] = useControllableValue(props); - if (error || !isRequestSuccess(resp)) return; - const data = getResponseData(resp)!; - return data?.content || []; - }, - { - manual: true, - debounceWait: 300, - refreshDeps: [filterModel], - }, + // ---------- Entity List Data ---------- + const { entityList, getEntityList } = useConfigPanelStore( + useStoreShallow(['entityList', 'getEntityList']), ); + const filteredEntityList = useMemo(() => { + if (!filterModel) return entityList; + + return entityList?.filter(entity => { + let { type, valueType, accessMode } = filterModel; + + type = type && (Array.isArray(type) ? type : [type]); + valueType = valueType && (Array.isArray(valueType) ? valueType : [valueType]); + accessMode = accessMode && (Array.isArray(accessMode) ? accessMode : [accessMode]); + + return ( + (!type || type.includes(entity.entity_type)) && + (!valueType || valueType.includes(entity.entity_value_type)) && + (!accessMode || accessMode.includes(entity.entity_access_mod)) + ); + }); + }, [entityList, filterModel]); + + // ---------- Search Entity Data ---------- + const [searchedEntityList, setSearchedEntityList] = useState(); + + // ---------- Autocomplete Render ---------- const options = useMemo(() => { - if (!entityList?.length) return []; - const result: EntitySelectValueType[] = entityList.map(item => { + const list = searchedEntityList || filteredEntityList || []; + const result: EntitySelectOptionType[] = list.map(item => { const entityValueAttribute = (() => { try { return JSON.parse(item.entity_value_attribute); @@ -166,7 +146,7 @@ const EntitySelect: React.FC = ({ label, required, filterMode })(); return { label: item.entity_name, - value: item.entity_id, + value: item.entity_key, valueType: item.entity_value_type, description: [item.device_name, item.integration_name].filter(Boolean).join(', '), rawData: { @@ -177,7 +157,7 @@ const EntitySelect: React.FC = ({ label, required, filterMode }); return result; - }, [entityList]); + }, [filteredEntityList, searchedEntityList]); const renderInput = useCallback( (params: AutocompleteRenderInputParams) => { @@ -186,37 +166,71 @@ const EntitySelect: React.FC = ({ label, required, filterMode {...params} required={required} label={label || getIntlText('common.label.entity')} - // slotProps={{ input: { readOnly: true } }} /> ); }, [label, required, getIntlText], ); - const renderOption = useCallback>( - (optionProps, option) => { - const { label, value, description } = option || {}; + const renderOption = useCallback< + NonNullable< + AutocompleteProps< + EntitySelectOptionType, + undefined, + undefined, + undefined + >['renderOption'] + > + >((optionProps, option) => { + const { label, value, description } = option || {}; - return ( - -
-
- -
-
- -
+ return ( + +
+
+
- - ); +
+ +
+
+
+ ); + }, []); + + const { run: handleInputChange } = useDebounceFn< + NonNullable< + AutocompleteProps< + EntitySelectOptionType, + undefined, + undefined, + undefined + >['onInputChange'] + > + >( + async (_, keyword, reason) => { + if (keyword && reason === 'input') { + const list = await getEntityList({ ...filterModel, keyword }); + setSearchedEntityList(list); + return; + } + + setSearchedEntityList(undefined); }, - [], + { wait: 300 }, ); + // ---------- Autocomplete Inner Value ---------- + const [innerValue, setInnerValue] = useState(null); + useLayoutEffect(() => { + const option = options.find(item => item.value === value); + setInnerValue(option || null); + }, [value, options]); + return ( - + value={innerValue} + disabled={disabled} options={options} renderInput={renderInput} renderOption={renderOption} @@ -230,15 +244,10 @@ const EntitySelect: React.FC = ({ label, required, filterMode }, }} popupIcon={} - onInputChange={(_, keyword, reason) => { - if (reason !== 'input') { - getEntityList(); - return; - } - - getEntityList(keyword); + onInputChange={handleInputChange} + onChange={(_, data) => { + setValue(data?.value); }} - onOpen={() => getEntityList()} /> ); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index aa40e397..3cbad2e2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -1,5 +1,5 @@ export { default as ConditionsInput } from './conditions-input'; -export { default as EntityAssignInput } from './entity-assign-input'; +export { default as EntityAssignSelect } from './entity-assign-select'; export { default as EntitySelect, type EntitySelectProps, @@ -14,3 +14,4 @@ export { default as EntityFilterSelect, type EntityFilterSelectValueType, } from './entity-filter-select'; +export { default as ParamAssignInput, type ParamAssignInputValueType } from './param-assign-input'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx new file mode 100644 index 00000000..06d2ec75 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx @@ -0,0 +1,99 @@ +import React, { useLayoutEffect } from 'react'; +import { Button, IconButton, TextField } from '@mui/material'; +import { isEqual } from 'lodash-es'; +import { useDynamicList, useControllableValue } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; +import ParamInputSelect from '../param-input-select'; +import './style.less'; + +export type ParamAssignInputValueType = + | undefined + | { + key?: string; + ref?: string; + value?: string; + }; + +export interface ParamAssignInputProps { + label?: string[]; + required?: boolean; + multiple?: boolean; + error?: boolean; + helperText?: React.ReactNode; + value?: ParamAssignInputValueType[]; + defaultValue?: ParamAssignInputValueType[]; + onChange?: (value: ParamAssignInputValueType[]) => void; +} + +const MAX_VALUE_LENGTH = 10; + +/** + * Param Assignment Input Component + * + * Note: use in CodeNode + */ +const ParamAssignInput: React.FC = ({ + label, + required = true, + multiple = true, + ...props +}) => { + const { getIntlText } = useI18n(); + const [data, setData] = useControllableValue(props); + const { list, remove, getKey, insert, replace, resetList } = + useDynamicList(data); + + useLayoutEffect(() => { + if (isEqual(data, list)) return; + resetList(data || []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data, resetList]); + + useLayoutEffect(() => { + setData?.(list); + }, [list, setData]); + + return ( +
+ {list.map((item, index) => ( +
+ replace(index, { ...item, key: e.target.value })} + /> + { + replace(index, { key: item?.key, ...data }); + }} + /> + remove(index)}> + + +
+ ))} + {multiple && ( + + )} +
+ ); +}; + +export default ParamAssignInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/style.less new file mode 100644 index 00000000..12a2b4ea --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/style.less @@ -0,0 +1,38 @@ +.@{prefix}-param-assign-input { + &-item { + display: flex; + gap: @margin-xs; + margin-bottom: @margin-md; + + .@{mui-prefix}FormControl-root { + margin: 0; + } + + > .@{prefix}-param-input-select, + > .@{mui-prefix}FormControl-root { + flex: 1; + } + + > .@{prefix}-param-input-select { + .@{prefix}-param-input-select-chip .@{mui-prefix}Chip-label .name { + max-width: 40px; + } + } + + > .@{mui-prefix}ButtonBase-root { + width: 40px; + height: 40px; + } + } + + & &-add-btn { + font-weight: @font-weight-bold; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx index d208ca91..4d15f78f 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx @@ -1,25 +1,27 @@ -import { useEffect, useLayoutEffect, useState } from 'react'; -import { useControllableValue } from 'ahooks'; -import { Autocomplete, TextField, MenuItem, type AutocompleteProps } from '@mui/material'; +import { useMemo, useLayoutEffect, useState, useRef } from 'react'; +import { useControllableValue, useSize } from 'ahooks'; +import { TextField, Menu, MenuItem, IconButton, Chip, ListSubheader } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { SettingsOutlinedIcon } from '@milesight/shared/src/components'; +import { Tooltip } from '@/components'; +import { genRefParamKey } from '@/pages/workflow/views/editor/helper'; import useWorkflow from '@/pages/workflow/views/editor/hooks/useWorkflow'; import './style.less'; -type ParamInputSelectValueType = { - type: 'custom' | 'reference'; - value?: string; -}; +type ParamInputSelectValueType = + | undefined + | { + ref?: string; + value?: string; + }; type OptionItemType = { nodeId: ApiKey; nodeName: string; - nodeType: WorkflowNodeType; - outputs: { - name: string; - type: string; - key: string; - }[]; + nodeType?: WorkflowNodeType; + valueName?: string; + valueType?: string; + valueKey?: string; }; export interface ParamInputSelectProps { @@ -27,8 +29,6 @@ export interface ParamInputSelectProps { required?: boolean; - disabled?: boolean; - /** * Param Select Placeholder */ @@ -39,16 +39,21 @@ export interface ParamInputSelectProps { defaultValue?: ParamInputSelectValueType; onChange?: (value: ParamInputSelectValueType) => void; - - /** - * Autocomplete Props - */ - autocompleteProps?: Omit< - AutocompleteProps, - 'options' | 'renderInput' - >; } +const demoOutputs = [ + { + name: 'output11', + type: 'string', + key: '1132e3123132', + }, + { + name: 'output22', + type: 'number', + key: '11eyu3123132', + }, +]; + /** * Param Input Select Component * @@ -56,66 +61,166 @@ export interface ParamInputSelectProps { * * TODO: render nodes params */ -const ParamSelect: React.FC = ({ +const ParamInputSelect: React.FC = ({ label, - required, - disabled, + required = true, placeholder, - autocompleteProps, ...props }) => { const { getIntlText } = useI18n(); - const [value, setValue] = useControllableValue(props); - const [popperOpen, setPopperOpen] = useState(false); + const { getIncomeNodes } = useWorkflow(); + const containerRef = useRef(null); + const [data, setData] = useControllableValue(props); const [inputValue, setInputValue] = useState(''); - const [innerValue, setInnerValue] = useState(''); + const [selectValue, setSelectValue] = useState(); + const [anchorEl, setAnchorEl] = useState(null); + const { width: containerWidth } = useSize(containerRef) || {}; + + const [options, renderedOptions] = useMemo(() => { + const incomeNodes = getIncomeNodes(); + const result = incomeNodes.reduce((acc, node) => { + // TODO: get the correct nodes params + demoOutputs.forEach(output => { + acc.push({ + nodeId: node.id, + nodeName: node.data?.name, + nodeType: node.type, + valueName: output.name, + valueType: output.type, + valueKey: output.key, + }); + }); + return acc; + }, [] as OptionItemType[]); + + // TODO: render Empty component when the options is empty + const renderedOptions = incomeNodes.reduce((acc, node) => { + acc.push( + + {node.type} + , + ); + + // TODO: get the correct nodes params + demoOutputs.forEach(output => { + acc.push( + { + setAnchorEl(null); + setData({ + ref: genRefParamKey(node.type!, node.id, output.key), + }); + }} + > +
+ + {output.type} +
+
, + ); + }); + return acc; + }, [] as React.ReactNode[]); + + return [result, renderedOptions]; + }, [selectValue, getIncomeNodes, setData]); useLayoutEffect(() => { - if (value?.type === 'custom') { - setInputValue(value?.value || ''); - } else if (value?.type === 'reference') { - setInnerValue(value?.value || ''); + // Direct input value + if (data?.value) { + setInputValue(data.value); + setSelectValue(undefined); + return; } - }, [value]); + + // Reference to an entity + if (data?.ref) { + setInputValue(''); + const option = options.find( + item => genRefParamKey(item.nodeType!, item.nodeId, item.valueKey!) === data.ref, + ); + + setSelectValue(val => { + if (val && genRefParamKey(val.nodeType!, val.nodeId, val.valueKey!) === data.ref) { + return val; + } + return option; + }); + return; + } + + setInputValue(''); + setSelectValue(undefined); + }, [data, options]); return ( -
- setPopperOpen(false)} - value={innerValue} - onChange={(_, value) => { - setValue({ type: 'reference', value: value || '' }); - }} - onInputChange={(e, value) => { - setValue({ type: 'custom', value }); - }} - options={['111', '222', '333']} - renderInput={params => ( - - )} - filterOptions={options => options} - popupIcon={} +
+ setAnchorEl(containerRef.current)}> + + + ), }, }} + value={inputValue} + onChange={e => { + setData({ value: e.target.value }); + }} /> + {!!selectValue && ( + + + {selectValue.valueType} + + } + onDelete={() => setData({ ref: undefined })} + /> + )} + setAnchorEl(null)} + sx={{ + '& .MuiList-root': { + width: containerWidth, + minWidth: 300, + maxHeight: 420, + }, + }} + > + {renderedOptions} +
); }; -export default ParamSelect; +export default ParamInputSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less index 677d4b35..be250d6d 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/style.less @@ -1,5 +1,68 @@ .@{prefix}-param-input-select { + position: relative; + + .@{mui-prefix}FormControl-root { + margin: 0; + } + .@{mui-prefix}InputBase-root { - padding-right: @padding-xs; + padding-right: @padding-xxs; + } + + & &-chip { + position: absolute; + top: @margin-xs; + left: @margin-sm; + border-radius: @border-radius-base; + + .@{mui-prefix}Chip-label { + display: flex; + + .name { + max-width: 120px; + .text-size(@font-size-base); + } + + .type { + font-size: @font-size-sm; + color: var(--text-color-tertiary); + + &::before { + display: inline-block; + margin: 0 @margin-xxs; + color: var(--text-color-secondary); + content: '/'; + .text-size(@font-size-base); + } + } + } + } + + &-option-groupname.@{mui-prefix}ListSubheader-root { + .text-size(@font-size-sm); + + margin-top: @margin-xs; + } + + &-option.@{mui-prefix}MenuItem-root { + padding: @padding-xs @padding-md; + + .@{prefix}-param-input-select-item { + display: flex; + .text-size(@font-size-base); + + .name { + max-width: 200px; + } + + .type { + display: inline-block; + padding: 0 @padding-xxs; + margin-left: @margin-md; + color: var(--text-color-tertiary); + background-color: var(--component-background-gray); + border-radius: @border-radius-base; + } + } } } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx index 2bba2ac2..b5e1982e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -46,7 +46,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. nodeType: node.type, outputs: [ { - name: 'output11', + name: 'output112123123123123123123123131231231', type: 'string', key: `${node.type}.${node.id}.1132e3123132`, }, @@ -58,6 +58,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. ], })); + // TODO: render Empty component when the options is empty return data.map(item => [ {item.nodeType} @@ -65,9 +66,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. item.outputs.map(output => (
- - {output.name} - + {output.type}
@@ -89,6 +88,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. labelId="param-select-label" label={label || getIntlText('common.label.value')} IconComponent={KeyboardArrowDownIcon} + MenuProps={{ className: 'ms-param-select-menu' }} > {renderOptions()} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less index 92c68a22..063c5617 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/style.less @@ -1,8 +1,4 @@ .@{prefix}-param-select { - // .@{mui-prefix}InputBase-root { - // padding-right: @padding-xs; - // } - &-option-groupname.@{mui-prefix}ListSubheader-root { .text-size(@font-size-sm); @@ -17,7 +13,7 @@ .text-size(@font-size-base); .name { - max-width: 50%; + max-width: 200px; } .type { @@ -31,6 +27,12 @@ } } + &-menu { + .@{mui-prefix}Paper-root { + max-height: 450px; + } + } + .@{mui-prefix}InputBase-root { .@{prefix}-param-select-item { display: inline-flex; @@ -40,18 +42,20 @@ .text-size(@font-size-base); .name { - color: var(--text-color-secondary); + max-width: 120px; + } + + .type { + font-size: @font-size-sm; - &::after { + &::before { display: inline-block; margin: 0 @margin-xxs; + color: var(--text-color-secondary); content: '/'; + .text-size(@font-size-base); } } - - .type { - font-size: @font-size-sm; - } } } } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts deleted file mode 100644 index c75bc5e8..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/helper.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { - checkRequired, - checkRangeLength, - type Validate, -} from '@milesight/shared/src/utils/validators'; - -/** - * Node Data Validators Config - */ -export const validatorsConfig: Record> = { - name: { - checkRequired: checkRequired(), - checkRangeLength: checkRangeLength({ min: 1, max: 50 }), - }, - remark: { - checkRangeLength: checkRangeLength({ min: 1, max: 1000 }), - }, -}; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts index 740fb8fa..13478a5d 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/index.ts @@ -1 +1,2 @@ export { default as useCommonFormItems, type CommonFormDataProps } from './useCommonFormItems'; +export { default as useNodeFormItems, type NodeFormDataProps } from './useNodeFormItems'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index e7ae1c08..159336b0 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -3,18 +3,76 @@ import { type ControllerProps } from 'react-hook-form'; import { TextField } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { checkRequired } from '@milesight/shared/src/utils/validators'; +import { + ConditionsInput, + EntityAssignSelect, + EntityFilterSelect, + EntitySelect, + MarkdownEditor, + ParamAssignInput, + ParamInputSelect, + TimerInput, +} from '../components'; /** * Form Item Props */ export type NodeFormDataProps = Record; -const useNodeFormItems = (node: WorkflowNode) => { +const useNodeFormItems = (node?: WorkflowNode) => { const { getIntlText } = useI18n(); const formItems = useMemo(() => { + if (!node) return []; + const result: Partial[]>> = { - trigger: [], + trigger: [ + { + name: 'debug', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + { + name: 'debug2', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + ], + assigner: [], + select: [ + { + name: 'entity', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + ], + listener: [ + { + name: 'listener', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + ], + code: [ + { + name: 'input_vars', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + ], + ifelse: [ + { + name: 'condition', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + ], }; return result[node.type!]; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 9d43d7fb..67bcd55e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -1,20 +1,21 @@ -import { useState, useCallback, useMemo } from 'react'; +import { useState, useCallback, useMemo, useLayoutEffect, useEffect } from 'react'; import { Panel, - useNodes, useReactFlow, type Node, type ReactFlowProps, type UseOnSelectionChangeOptions, } from '@xyflow/react'; import cls from 'classnames'; -import { Stack, IconButton } from '@mui/material'; +import { Stack, IconButton, Divider } from '@mui/material'; import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; // import { FormControl, InputLabel, Select, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; -import { useCommonFormItems, type CommonFormDataProps } from './hooks'; +import useWorkflow from '../../hooks/useWorkflow'; +import { useCommonFormItems, useNodeFormItems, type CommonFormDataProps } from './hooks'; +import useConfigPanelStore from './store'; import './style.less'; /** @@ -24,27 +25,27 @@ const ConfigPanel = () => { const { getIntlText } = useI18n(); // ---------- Handle Node-related logic ---------- - const nodes = useNodes(); const { updateNode } = useReactFlow(); - const selectedNode = useMemo(() => { - const selectedNodes = nodes.filter(item => item.selected); - const node = selectedNodes?.[0]; - - if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { - return; - } - - return node; - }, [nodes]); + const { selectedNode } = useWorkflow(); const nodeConfig = useMemo(() => { + console.log({ selectedNode }); if (!selectedNode) return; return basicNodeConfigs[selectedNode.type as WorkflowNodeType]; }, [selectedNode]); + // ---------- Entity List Data Init ---------- + const getEntityList = useConfigPanelStore(state => state.getEntityList); + + useLayoutEffect(() => { + if (!selectedNode) return; + getEntityList(undefined, true); + }, [selectedNode, getEntityList]); + // ---------- Handle Form-related logic ---------- const { control, formState, handleSubmit, reset } = useForm(); const commonFormItems = useCommonFormItems(); + const nodeFormItems = useNodeFormItems(selectedNode); return ( { hidden: !selectedNode, })} > - {nodeConfig?.labelIntlKey && ( -
-
- - - {nodeConfig.icon} - +
+
+ + + {nodeConfig?.icon} + + {!!nodeConfig?.labelIntlKey && ( {getIntlText(nodeConfig.labelIntlKey)} - - - {nodeConfig.testable && ( - - console.log('execute testing or popup test panel') - } - > - - - )} + )} + + + {nodeConfig?.testable && ( { - if (!selectedNode) return; - updateNode(selectedNode.id, { - selected: false, - }); - }} + onClick={() => console.log('execute testing or popup test panel')} > - + - -
-
+ )} + { + if (!selectedNode) return; + updateNode(selectedNode.id, { + selected: false, + }); + }} + > + + + +
+
+
{commonFormItems.map(props => ( {...props} @@ -95,10 +93,19 @@ const ConfigPanel = () => { control={control} /> ))} - Other Arguments... +
+ +
+ {nodeFormItems?.map(props => ( + + {...props} + key={props.name} + control={control} + /> + ))}
- )} +
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/store.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/store.ts new file mode 100644 index 00000000..7be5cc27 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/store.ts @@ -0,0 +1,73 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import { + entityAPI, + awaitWrap, + getResponseData, + isRequestSuccess, + type EntityAPISchema, +} from '@/services/http'; + +export type EntityFilterParams = { + /** Search Keyword */ + keyword?: string; + /** Entity Type */ + type?: EntityType | EntityType[]; + /** Entity Value Type */ + valueType?: EntityValueDataType | EntityValueDataType[]; + /** Entity Access Mode */ + accessMode?: EntityAccessMode | EntityAccessMode[]; + /** Exclude Children */ + excludeChildren?: boolean; +}; + +interface ConfigPanelStore { + entityList?: EntityAPISchema['getList']['response']['content']; + + // searchList?: EntityAPISchema['getList']['response']['content']; + + setEntityList: (list: ConfigPanelStore['entityList']) => void; + + getEntityList: ( + params?: EntityFilterParams, + cached?: boolean, + ) => Promise | undefined; + + // setSearchList: (list: ConfigPanelStore['searchList']) => void; +} + +const useConfigPanelStore = create( + immer(set => ({ + setEntityList: entityList => set({ entityList }), + + getEntityList: async (params, cached) => { + const entityType = + params?.type && (Array.isArray(params.type) ? params.type : [params.type]); + const valueType = + params?.valueType && + (Array.isArray(params.valueType) ? params.valueType : [params.valueType]); + const accessMode = + params?.accessMode && + (Array.isArray(params.accessMode) ? params.accessMode : [params.accessMode]); + const [error, resp] = await awaitWrap( + entityAPI.getList({ + keyword: params?.keyword, + entity_type: entityType, + entity_value_type: valueType, + entity_access_mod: accessMode, + exclude_children: params?.excludeChildren, + page_number: 1, + page_size: 99999, + }), + ); + + if (error || !isRequestSuccess(resp)) return; + const data = getResponseData(resp)!; + + if (cached) set({ entityList: data?.content }); + return data?.content || []; + }, + })), +); + +export default useConfigPanelStore; diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts index a97ccfb0..70a21ac9 100644 --- a/apps/web/src/pages/workflow/views/editor/constants.ts +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -37,3 +37,13 @@ export const NODE_SPACING_X = 50; * Node Y-axis spacing */ export const NODE_SPACING_Y = 50; + +/** + * Global parameter reference prefix + */ +export const PARAM_REFERENCE_PREFIX = '@'; + +/** + * Global parameter reference key divider + */ +export const PARAM_REFERENCE_DIVIDER = '.'; diff --git a/apps/web/src/pages/workflow/views/editor/helper.ts b/apps/web/src/pages/workflow/views/editor/helper.ts new file mode 100644 index 00000000..144d9c59 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/helper.ts @@ -0,0 +1,42 @@ +import { + checkRequired, + checkRangeLength, + type Validate, +} from '@milesight/shared/src/utils/validators'; +import { PARAM_REFERENCE_PREFIX, PARAM_REFERENCE_DIVIDER } from './constants'; + +/** + * Node Data Validators Config + */ +export const validatorsConfig: Record> = { + name: { + checkRequired: checkRequired(), + checkRangeLength: checkRangeLength({ min: 1, max: 50 }), + }, + remark: { + checkRangeLength: checkRangeLength({ min: 1, max: 1000 }), + }, +}; + +/** + * Generate Reference Param Key + */ +export const genRefParamKey = (nodeType: WorkflowNodeType, nodeId: ApiKey, valueKey: ApiKey) => { + return `${PARAM_REFERENCE_PREFIX}${[nodeType, nodeId, valueKey].join(PARAM_REFERENCE_DIVIDER)}`; +}; + +/** + * Check if the value is a reference param key + */ +export const isRefParamKey = (key: string) => { + return key.startsWith(PARAM_REFERENCE_PREFIX); +}; + +/** + * Parse the reference param key + */ +export const parseRefParamKey = (key: string) => { + if (!isRefParamKey(key)) return {}; + const [nodeType, nodeId, valueKey] = key.slice(1).split(PARAM_REFERENCE_DIVIDER); + return { nodeType, nodeId, valueKey }; +}; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index dc684a66..d4df0053 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -1,5 +1,13 @@ -import { useCallback } from 'react'; -import { useReactFlow, getIncomers, getOutgoers, type IsValidConnection } from '@xyflow/react'; +import { useMemo, useCallback } from 'react'; +import { + useReactFlow, + useNodes, + useEdges, + getIncomers, + getOutgoers, + type IsValidConnection, +} from '@xyflow/react'; +import { uniqBy } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { PARALLEL_DEPTH_LIMIT } from '../constants'; @@ -7,8 +15,21 @@ import { getParallelInfo } from './utils'; const useWorkflow = () => { const { getNodes, getEdges } = useReactFlow(); + const nodes = useNodes(); + const edges = useEdges(); const { getIntlText } = useI18n(); + const selectedNode = useMemo(() => { + const selectedNodes = nodes.filter(item => item.selected); + const node = selectedNodes?.[0]; + + if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { + return; + } + + return node; + }, [nodes]); + // Check node connection cycle const isValidConnection = useCallback( connection => { @@ -61,7 +82,6 @@ const useWorkflow = () => { // Get the only selected node that is not dragging const getSelectedNode = useCallback(() => { - const nodes = getNodes(); const selectedNodes = nodes.filter(item => item.selected); const node = selectedNodes?.[0]; @@ -71,26 +91,41 @@ const useWorkflow = () => { } return node; - }, [getNodes]); + }, [nodes]); const getIncomeNodes = useCallback( (currentNode?: WorkflowNode) => { currentNode = currentNode || getSelectedNode(); - const getAllIncomers = (node: WorkflowNode, result: WorkflowNode[] = []) => { - const incomers = getIncomers(node, getNodes(), getEdges()); - - result.push(...incomers); - incomers.forEach(item => getAllIncomers(item, result)); - - return result; + const getAllIncomers = ( + node: WorkflowNode, + data: Record = {}, + depth = 1, + ) => { + if (!node) return []; + const incomers = getIncomers(node, nodes, edges); + + data[depth] = data[depth] || []; + data[depth].push(...incomers); + incomers.forEach(item => getAllIncomers(item, data, depth + 1)); + + const keys = Object.keys(data).sort((a, b) => +a - +b); + const result = keys.reduce((acc, key) => { + acc.push(...data[key]); + return acc; + }, [] as WorkflowNode[]); + + return uniqBy(result, 'id'); }; return getAllIncomers(currentNode!); }, - [getEdges, getNodes, getSelectedNode], + [nodes, edges, getSelectedNode], ); return { + nodes, + edges, + selectedNode, isValidConnection, checkNestedParallelLimit, getSelectedNode, diff --git a/packages/shared/types/entity.d.ts b/packages/shared/types/entity.d.ts index ce275e9c..9a66ed6b 100644 --- a/packages/shared/types/entity.d.ts +++ b/packages/shared/types/entity.d.ts @@ -113,6 +113,8 @@ declare interface EntityData { entity_key: string; /** 实体名称 */ entity_name: string; + /** Entity Type */ + entity_type: EntityType; /** 实体值属性 */ entity_value_attribute: string; /** 实体值类型 */ From 2b38dfaeeb832e6bbd9eced471e52b74cc370fb2 Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 17 Dec 2024 09:37:38 +0800 Subject: [PATCH 034/172] feat: triigger node --- .../components/param-input/index.tsx | 152 +++++++++++++++++- .../components/param-input/style.less | 30 ++++ packages/locales/src/lang/en/workflow.json | 4 +- 3 files changed, 184 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index 270dc256..c47600e7 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -3,6 +3,156 @@ * * Note: use in TriggerNode, CodeNode */ -const ParamInput = () => {}; + +import { useLayoutEffect, useMemo } from 'react'; +import { + Select, + Button, + IconButton, + FormControl, + MenuItem, + TextField, + Switch, + InputLabel, + type SelectProps, + type TextFieldProps, +} from '@mui/material'; +import { useDynamicList, useControllableValue } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; +import './style.less'; + +export type ParamInputValueType = { + arg: string; + type: string; + isCheck?: boolean; +}; + +export interface ParamInputProps { + required?: boolean; + disabled?: boolean; + defaultValue?: ParamInputValueType[]; + showSwitch?: boolean; + typeSelectProps?: SelectProps; + nameInputProps?: TextFieldProps; + maxAddNum?: number; +} + +const DEFAULT_EMPTY_VALUE: ParamInputValueType = { + arg: '', + type: '', +}; +const typeOptions = ['STRING', 'LONG', 'DOUBLE', 'BOOLEAN', 'BINARY']; + +const ParamInput: React.FC = ({ + required, + disabled, + defaultValue, + showSwitch, + typeSelectProps, + nameInputProps, + maxAddNum, + ...props +}) => { + const { getIntlText } = useI18n(); + const [innerValue, setInnerValue] = useControllableValue(props, { + defaultValue: defaultValue || [], + }); + const { list, remove, getKey, insert, replace } = useDynamicList( + innerValue || [], + ); + + useLayoutEffect(() => { + setInnerValue?.(list); + }, [list, setInnerValue]); + + const handlerChange = ( + index: number, + rowItem: ParamInputValueType, + key: string, + value: string | boolean, + ) => { + replace(index, { ...rowItem, [key]: value }); + }; + const disabledAdd = useMemo(() => { + return maxAddNum !== undefined && Number.isInteger(maxAddNum) && list.length >= maxAddNum; + }, [list]); + const handlerAdd = () => { + if (disabledAdd) return; + insert(list.length, DEFAULT_EMPTY_VALUE); + }; + return ( +
+ {list.map((item, index) => ( +
+ + + handlerChange(index, item, 'arg', e.target.value as string) + } + /> + + + + {typeSelectProps?.label || getIntlText('common.label.type')} + + + + {showSwitch && ( + + + handlerChange( + index, + item, + 'isCheck', + e.target.checked as boolean, + ) + } + /> + + {getIntlText('workflow.node.trigger_switch_label')} + + + )} + remove(index)}> + + +
+ ))} + +
+ ); +}; export default ParamInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/style.less new file mode 100644 index 00000000..b7b935b4 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/style.less @@ -0,0 +1,30 @@ +.@{prefix}-param-input { + &-item { + display: flex; + gap: @margin-xs; + align-items: center; + > .@{mui-prefix}FormControl-root { + flex: 1; + &.@{prefix}-param-output-switch { + flex-direction: row; + align-items: center; + + .@{prefix}-param-output-switch-label { + font-size: @font-size-2; + color: var(--gray-9); + } + } + } + } + + & &-add-btn { + font-weight: @font-weight-bold; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } +} diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index f31b3bd6..6b9a8625 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -37,5 +37,7 @@ "workflow.editor.save_warning_message": "The current workflow has been referenced, and saving it will update the workflow information synchronously. Are you sure you want to save it", "workflow.message.delete_tip": "Are you sure you want to delete the workflow? It cannot be restore after deletion.", "workflow.label.deletion": "Deletion", - "workflow.editor.form_param_select_placeholder": "Enter or reference a variable" + "workflow.editor.form_param_select_placeholder": "Enter or reference a variable", + "workflow.node.config_panel_lable_arguments": "Arguments", + "workflow.node.trigger_switch_label": "Conttext" } From dff1ee049df385a9472d4dd40d4f52df30eb4825 Mon Sep 17 00:00:00 2001 From: Lynn-Bing Date: Tue, 17 Dec 2024 11:07:33 +0800 Subject: [PATCH 035/172] feat: Add entity feature --- .env | 4 +- .../components/date-range-picker/index.tsx | 38 +- .../components/add-dashboard/index.tsx | 4 +- .../components/add-from-workflow/index.tsx | 91 + .../entity/components/add-modal/index.tsx | 66 + .../entity/components/add-modal/useForm.tsx | 256 + .../entity/components/custom-entity/index.tsx | 251 + .../entity/components/detail/basicInfo.tsx | 31 + .../pages/entity/components/detail/index.tsx | 154 + .../entity/components/detail/useColumns.tsx | 88 + .../entity/components/edit-entity/index.tsx | 57 + .../pages/entity/components/entity/index.tsx | 192 + apps/web/src/pages/entity/components/index.ts | 1 + apps/web/src/pages/entity/hooks/index.ts | 1 + .../web/src/pages/entity/hooks/useColumns.tsx | 128 + .../pages/entity/hooks/useEntityColumns.tsx | 121 + apps/web/src/pages/entity/index.tsx | 237 + apps/web/src/pages/entity/style.less | 33 + .../components/basic-table/edit-dialog.tsx | 92 + .../detail/components/basic-table/index.tsx | 116 + .../detail/components/entity-table/index.tsx | 110 + .../entity/views/detail/components/index.ts | 2 + .../src/pages/entity/views/detail/index.tsx | 116 + .../src/pages/entity/views/detail/style.less | 36 + .../plugin/components/entity-select/index.tsx | 6 +- .../web/src/plugin/components/input/index.tsx | 1 + .../components/multi-entity-select/index.tsx | 6 +- .../src/plugin/components/select/index.tsx | 2 +- apps/web/src/plugin/config-plugin/style.less | 2 +- .../src/plugin/render/render-configure.tsx | 2 +- apps/web/src/routes/routes.tsx | 22 + apps/web/src/services/http/entity.ts | 45 +- packages/locales/src/helper.ts | 2 +- packages/locales/src/lang/cn/entity.json | 6 + packages/locales/src/lang/en/entity.json | 12 + packages/locales/src/lang/en/global.json | 3 +- .../components/draggable-resizable/style.less | 6 +- packages/shared/src/components/form/index.tsx | 41 +- .../shared/src/components/form/style.less | 23 +- .../shared/src/components/form/typings.d.ts | 7 +- .../shared/src/components/form/useForm.tsx | 100 +- .../shared/src/components/icons/index.tsx | 5 + .../shared/src/components/modal/index.tsx | 11 +- .../shared/src/components/select/index.tsx | 6 +- pnpm-lock.yaml | 8807 ++++++++++------- 45 files changed, 7524 insertions(+), 3816 deletions(-) create mode 100644 apps/web/src/pages/entity/components/add-from-workflow/index.tsx create mode 100644 apps/web/src/pages/entity/components/add-modal/index.tsx create mode 100644 apps/web/src/pages/entity/components/add-modal/useForm.tsx create mode 100644 apps/web/src/pages/entity/components/custom-entity/index.tsx create mode 100644 apps/web/src/pages/entity/components/detail/basicInfo.tsx create mode 100644 apps/web/src/pages/entity/components/detail/index.tsx create mode 100644 apps/web/src/pages/entity/components/detail/useColumns.tsx create mode 100644 apps/web/src/pages/entity/components/edit-entity/index.tsx create mode 100644 apps/web/src/pages/entity/components/entity/index.tsx create mode 100644 apps/web/src/pages/entity/components/index.ts create mode 100644 apps/web/src/pages/entity/hooks/index.ts create mode 100644 apps/web/src/pages/entity/hooks/useColumns.tsx create mode 100644 apps/web/src/pages/entity/hooks/useEntityColumns.tsx create mode 100644 apps/web/src/pages/entity/index.tsx create mode 100644 apps/web/src/pages/entity/style.less create mode 100644 apps/web/src/pages/entity/views/detail/components/basic-table/edit-dialog.tsx create mode 100644 apps/web/src/pages/entity/views/detail/components/basic-table/index.tsx create mode 100644 apps/web/src/pages/entity/views/detail/components/entity-table/index.tsx create mode 100644 apps/web/src/pages/entity/views/detail/components/index.ts create mode 100644 apps/web/src/pages/entity/views/detail/index.tsx create mode 100644 apps/web/src/pages/entity/views/detail/style.less create mode 100644 packages/locales/src/lang/cn/entity.json create mode 100644 packages/locales/src/lang/en/entity.json diff --git a/.env b/.env index 764bb18a..1e21a680 100644 --- a/.env +++ b/.env @@ -5,9 +5,9 @@ WEB_API_ORIGIN=/ # Websocket Host 地址 WEB_WS_HOST=/ # 接口 API 代理地址 -WEB_API_PROXY=https://beaver-iot.milesight.com/api/v1 +WEB_API_PROXY=https://demo.beaver-iot.com/api/v1 # WebSocket 代理地址 -WEB_SOCKET_PROXY=wss://beaver-iot.milesight.com +WEB_SOCKET_PROXY=wss://demo.beaver-iot.com # Oauth client ID OAUTH_CLIENT_ID=iab diff --git a/apps/web/src/components/date-range-picker/index.tsx b/apps/web/src/components/date-range-picker/index.tsx index 9392a7b6..0c7ec4ad 100644 --- a/apps/web/src/components/date-range-picker/index.tsx +++ b/apps/web/src/components/date-range-picker/index.tsx @@ -1,20 +1,22 @@ import React, { useState } from 'react'; -import dayjs, { type Dayjs } from 'dayjs'; +import { type Dayjs } from 'dayjs'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; import { DatePicker, DatePickerProps } from '@mui/x-date-pickers/DatePicker'; -type DateRangePickerValueType = { - start?: Dayjs; - end?: Dayjs; +export type DateRangePickerValueType = { + start?: Dayjs | null; + end?: Dayjs | null; }; -interface DateRangePickerProps extends Omit, 'value' | 'label'> { +interface DateRangePickerProps + extends Omit, 'value' | 'label' | 'onChange'> { value?: DateRangePickerValueType | null; label?: { start?: React.ReactNode; end?: React.ReactNode; }; + onChange?: (value: DateRangePickerValueType | null) => void; } const DateRangePickerStyled = styled('div')(() => ({ @@ -30,18 +32,34 @@ const DateRangePicker: React.FC = ({ label, value, onChang { - setStartDate(start); + // Passing onChange indicates that it is controlled, and no internal value handling is done. + if (onChange) { + onChange({ + start, + end: value?.end || endDate, + }); + } else { + setStartDate(start); + } }} {...props} /> { - setEndDate(start); + value={value?.end || endDate} + onChange={end => { + // Passing onChange indicates that it is controlled, and no internal value handling is done. + if (onChange) { + onChange({ + start: value?.start || startDate, + end, + }); + } else { + setEndDate(end); + } }} {...props} /> diff --git a/apps/web/src/pages/dashboard/components/add-dashboard/index.tsx b/apps/web/src/pages/dashboard/components/add-dashboard/index.tsx index 8f1d91c3..e2a40ee1 100644 --- a/apps/web/src/pages/dashboard/components/add-dashboard/index.tsx +++ b/apps/web/src/pages/dashboard/components/add-dashboard/index.tsx @@ -3,13 +3,13 @@ import { useI18n } from '@milesight/shared/src/hooks'; import { Modal, Form } from '@milesight/shared/src/components'; import { DashboardDetail } from '@/services/http/dashboard'; -interface ConfigPluginProps { +interface IProps { onCancel: () => void; onOk: (data: DashboardDetail) => void; data?: DashboardDetail; } -const AddDashboard = (props: ConfigPluginProps) => { +const AddDashboard = (props: IProps) => { const { getIntlText } = useI18n(); const { onOk, onCancel, data } = props; const formRef = useRef(); diff --git a/apps/web/src/pages/entity/components/add-from-workflow/index.tsx b/apps/web/src/pages/entity/components/add-from-workflow/index.tsx new file mode 100644 index 00000000..1ad08a98 --- /dev/null +++ b/apps/web/src/pages/entity/components/add-from-workflow/index.tsx @@ -0,0 +1,91 @@ +import { useRef } from 'react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal, Form, UseFormItemsProps } from '@milesight/shared/src/components'; +import { TableRowDataType } from '../../hooks/useColumns'; + +interface ConfigPluginProps { + onCancel: () => void; + onOk: (data: TableRowDataType) => void; +} + +const AddEntity = (props: ConfigPluginProps) => { + const { getIntlText } = useI18n(); + const { onOk, onCancel } = props; + const formRef = useRef(); + + // Initial form configuration + const formItems: UseFormItemsProps[] = [ + { + label: getIntlText('device.label.param_entity_name'), + name: 'entityName', + type: 'TextField', + rules: { + required: true, + maxLength: { + value: 64, + message: '', + }, + }, + }, + { + label: getIntlText('common.label.workflow'), + name: 'workflowId', + type: 'Select', + rules: { + required: true, + }, + props: { + componentProps: { + fullWidth: true, + }, + options: [ + { + label: getIntlText('entity.label.entity_type_of_string'), + value: 'string', + }, + { + label: getIntlText('entity.label.entity_type_of_int'), + value: 'int', + }, + { + label: getIntlText('entity.label.entity_type_of_boolean'), + value: 'boolean', + }, + { + label: getIntlText('entity.label.entity_type_of_enum'), + value: 'enum', + }, + ], + }, + }, + ]; + + const handleClose = () => { + onCancel(); + }; + + const handleOk = () => { + formRef.current?.handleSubmit(); + }; + + // Form submission + const handleSubmit = (values: TableRowDataType) => { + console.log(values); + // onOk(values); + }; + + return ( + + ref={formRef} formItems={formItems} onOk={handleSubmit} /> + + ); +}; + +export default AddEntity; diff --git a/apps/web/src/pages/entity/components/add-modal/index.tsx b/apps/web/src/pages/entity/components/add-modal/index.tsx new file mode 100644 index 00000000..433e2315 --- /dev/null +++ b/apps/web/src/pages/entity/components/add-modal/index.tsx @@ -0,0 +1,66 @@ +import { useRef, useState, useEffect } from 'react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal, Form } from '@milesight/shared/src/components'; +import { TableRowDataType } from '../../hooks/useColumns'; +import useForm from './useForm'; + +interface ConfigPluginProps { + onCancel: () => void; + onOk: (data: TableRowDataType) => void; +} + +const AddEntity = (props: ConfigPluginProps) => { + const { getIntlText } = useI18n(); + const { onOk, onCancel } = props; + const [formValues, setFormValues] = useState>({}); + const formRef = useRef(); + const formValuesRef = useRef>({}); + + const setFormValuesRef = (values: Record) => { + formValuesRef.current = { ...values }; + }; + + const formItems = useForm({ + formValues, + preFormValues: { ...formValuesRef.current }, + setPreFormValues: setFormValuesRef, + }); + + const handleClose = () => { + onCancel(); + }; + + const handleOk = () => { + formRef.current?.handleSubmit(); + }; + + // Form submission + const handleSubmit = (values: TableRowDataType) => { + console.log(values); + // onOk(values); + }; + + const handleChange = (values: any) => { + setFormValues(values); + }; + + return ( + + + ref={formRef} + formItems={formItems} + onOk={handleSubmit} + onChange={handleChange} + /> + + ); +}; + +export default AddEntity; diff --git a/apps/web/src/pages/entity/components/add-modal/useForm.tsx b/apps/web/src/pages/entity/components/add-modal/useForm.tsx new file mode 100644 index 00000000..064dce17 --- /dev/null +++ b/apps/web/src/pages/entity/components/add-modal/useForm.tsx @@ -0,0 +1,256 @@ +import { useEffect, useState, useRef } from 'react'; +import { ToggleButtonGroup, ToggleButton, Button } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { UseFormItemsProps } from '@milesight/shared/src/components'; + +interface FormProps { + preFormValues: Record; + formValues: Record; + setPreFormValues: (data: Record) => void; +} + +type ModeType = 'value' | 'enum'; + +const useForm = (props: FormProps) => { + const { preFormValues, formValues, setPreFormValues } = props; + const { getIntlText } = useI18n(); + const [formItems, setFormItems] = useState([]); + const [mode, setMode] = useState('value'); + const formItemsRef = useRef([]); + + // Initial form configuration + const initFormItems: UseFormItemsProps[] = [ + { + label: getIntlText('device.label.param_entity_name'), + name: 'entityName', + type: 'TextField', + rules: { + required: true, + maxLength: { + value: 64, + message: '', + }, + }, + }, + { + label: getIntlText('entity.label.entity_type_of_access'), + name: 'entityAccessMod', + type: 'Select', + rules: { + required: true, + }, + props: { + options: [ + { + label: getIntlText('entity.label.entity_type_of_access_readonly'), + value: 'r', + }, + { + label: getIntlText('entity.label.entity_type_of_access_write'), + value: 'w', + }, + ], + }, + }, + { + label: getIntlText('common.label.data_type'), + name: 'entityValueType', + type: 'Select', + rules: { + required: true, + }, + props: { + componentProps: { + fullWidth: true, + }, + options: [ + { + label: getIntlText('entity.label.entity_type_of_string'), + value: 'string', + }, + { + label: getIntlText('entity.label.entity_type_of_int'), + value: 'int', + }, + { + label: getIntlText('entity.label.entity_type_of_boolean'), + value: 'boolean', + }, + { + label: getIntlText('entity.label.entity_type_of_enum'), + value: 'enum', + }, + ], + }, + }, + ]; + + const handleChangeMode = (_event: React.MouseEvent, value: ModeType) => { + if (value !== null) { + setMode(value); + } + }; + + // Render mode switch component + const renderButtonGroup = () => { + return ( + + Set Value + Set Enumeration Items + + ); + }; + + // Click event for adding a new form button + const addFormItem = () => { + const newFormItems = [...formItemsRef.current]; + const childrenLength = newFormItems?.[newFormItems.length - 1]?.children?.length || 0; + const initAddFormItem: UseFormItemsProps[] = [ + { + label: getIntlText('common.label.key'), + name: `key_${childrenLength - 1}`, + type: 'TextField', + rules: { + required: true, + }, + }, + { + label: getIntlText('common.label.value'), + name: `value_${childrenLength - 1}`, + type: 'TextField', + rules: { + required: true, + }, + }, + ]; + newFormItems[newFormItems.length - 1].children?.splice( + childrenLength - 1, + 0, + ...(initAddFormItem as any), + ); + setFormItems(newFormItems); + }; + + const addButton = () => { + return ( + + ); + }; + + // Switch mode and re-render the form + const renderModeToForm = () => { + if (mode === 'value') { + return [ + { + label: + formValues.entityValueType === 'int' + ? getIntlText('entity.label.entity_maximum_value') + : getIntlText('entity.label.entity_maximum_length'), + name: formValues.entityValueType === 'int' ? 'maxValue' : 'maxLength', + type: 'TextField', + rules: { + required: true, + }, + }, + { + label: + formValues.entityValueType === 'int' + ? getIntlText('entity.label.entity_minimum_value') + : getIntlText('entity.label.entity_minimum_length'), + name: formValues.entityValueType === 'int' ? 'minValue' : 'minLength', + type: 'TextField', + rules: { + required: true, + }, + }, + ]; + } + return [ + { + label: getIntlText('entity.label.entity_items'), + name: '', + type: '', + children: [ + { + label: getIntlText('common.label.key'), + name: 'key_1', + type: 'TextField', + rules: { + required: true, + }, + }, + { + label: getIntlText('common.label.value'), + name: 'value_1', + type: 'TextField', + rules: { + required: true, + }, + }, + { + label: '', + name: '', + type: '', + customRender: () => { + return addButton(); + }, + }, + ], + }, + ]; + }; + + const getFormItems = (type: string): any[] => { + switch (type) { + case 'int': + case 'string': + return [ + ...initFormItems, + { + label: '', + name: '', + customRender: () => { + return renderButtonGroup(); + }, + }, + ...renderModeToForm(), + ]; + default: + return initFormItems; + } + }; + + useEffect(() => { + if ( + preFormValues.entityValueType !== formValues.entityValueType || + !formValues.entityValueType + ) { + const newFormItem = getFormItems(formValues.entityValueType); + setFormItems(newFormItem); + setPreFormValues(formValues); + } + }, [formValues]); + + useEffect(() => { + const newFormItem = getFormItems(formValues.entityValueType); + setFormItems(newFormItem); + }, [mode]); + + useEffect(() => { + formItemsRef.current = [...formItems]; + console.log(formItemsRef.current); + }, [formItems]); + + return formItems; +}; + +export default useForm; diff --git a/apps/web/src/pages/entity/components/custom-entity/index.tsx b/apps/web/src/pages/entity/components/custom-entity/index.tsx new file mode 100644 index 00000000..bd5490f1 --- /dev/null +++ b/apps/web/src/pages/entity/components/custom-entity/index.tsx @@ -0,0 +1,251 @@ +import { useState, useMemo, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button, Stack, Menu, MenuItem } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { + AddIcon, + DeleteOutlineIcon, + NoteAddIcon, + CalculateIcon, + toast, +} from '@milesight/shared/src/components'; +import { TablePro, useConfirm } from '@/components'; +import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { useColumns, type UseColumnsProps, type TableRowDataType } from '../../hooks'; +import AddModal from '../add-modal'; +import AddFromWorkflow from '../add-from-workflow'; +import Detail from '../detail'; + +export default () => { + const navigate = useNavigate(); + const { getIntlText } = useI18n(); + + const [keyword, setKeyword] = useState(); + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); + const [selectedIds, setSelectedIds] = useState([]); + const [anchorEl, setAnchorEl] = useState(null); + const [modalOpen, setModalOpen] = useState(false); + const [workflowModalOpen, setWorkflowModalOpen] = useState(false); + const [detail, setDetail] = useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const { + data: entityData, + loading, + run: getList, + } = useRequest( + async () => { + const { page, pageSize } = paginationModel; + const [error, resp] = await awaitWrap( + entityAPI.getCustomEntityList({ + keyword, + page_size: pageSize, + page_number: page + 1, + }), + ); + const data = getResponseData(resp); + + if (error || !data || !isRequestSuccess(resp)) return; + + return objectToCamelCase(data); + }, + { + debounceWait: 300, + refreshDeps: [keyword, paginationModel], + }, + ); + + const confirm = useConfirm(); + const handleDeleteConfirm = useCallback( + (ids?: ApiKey[]) => { + const idsToDelete = ids || [...selectedIds]; + + confirm({ + title: getIntlText('common.label.delete'), + description: getIntlText('device.message.delete_tip'), + confirmButtonText: getIntlText('common.label.delete'), + confirmButtonProps: { + color: 'error', + }, + onConfirm: async () => { + const [error, resp] = await awaitWrap( + entityAPI.deleteEntities({ entity_id_list: idsToDelete }), + ); + + if (error || !isRequestSuccess(resp)) return; + + getList(); + setSelectedIds([]); + toast.success(getIntlText('common.message.delete_success')); + }, + }); + }, + [confirm, getIntlText, getList, selectedIds], + ); + + /** Details event related */ + const handleDetail = (data: TableRowDataType) => { + setDetail(data); + }; + + const handleDetailClose = () => { + setDetail(null); + }; + + const toolbarRender = useMemo(() => { + return ( + + + + + ); + }, [getIntlText, handleDeleteConfirm, selectedIds]); + + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( + (type, record) => { + switch (type) { + case 'detail': { + handleDetail(record); + break; + } + case 'delete': { + handleDeleteConfirm([record.entityId]); + break; + } + default: { + break; + } + } + }, + [navigate, handleDeleteConfirm], + ); + const columns = useColumns({ onButtonClick: handleTableBtnClick }); + + const handleShowAddOnly = () => { + setModalOpen(true); + handleClose(); + }; + + const handleAddFromWorkflow = () => { + setWorkflowModalOpen(true); + handleClose(); + }; + + return ( +
+ + checkboxSelection + loading={loading} + columns={columns} + getRowId={record => record.entityId} + rows={entityData?.content} + rowCount={entityData?.total || 0} + paginationModel={paginationModel} + rowSelectionModel={selectedIds} + // isRowSelectable={({ row }) => row.deletable} + toolbarRender={toolbarRender} + onPaginationModelChange={setPaginationModel} + onRowSelectionModelChange={setSelectedIds} + onRowDoubleClick={({ row }) => { + navigate(`/device/detail/${row.entityId}`, { state: row }); + }} + onSearch={setKeyword} + onRefreshButtonClick={getList} + /> + {modalOpen && ( + setModalOpen(false)} + onOk={() => { + getList(); + setModalOpen(false); + }} + /> + )} + {workflowModalOpen && ( + setWorkflowModalOpen(false)} + onOk={() => { + getList(); + setWorkflowModalOpen(false); + }} + /> + )} + {!!detail && } + + +
+ {getIntlText('entity.label.create_property')} +
+
+ +
+ + {getIntlText('entity.label.create_entity_only')} +
+
+ +
+ {getIntlText('entity.label.create_service')} +
+
+ +
+ + {getIntlText('entity.label.create_entity_from_workflow')} +
+
+
+
+ ); +}; diff --git a/apps/web/src/pages/entity/components/detail/basicInfo.tsx b/apps/web/src/pages/entity/components/detail/basicInfo.tsx new file mode 100644 index 00000000..21fad48c --- /dev/null +++ b/apps/web/src/pages/entity/components/detail/basicInfo.tsx @@ -0,0 +1,31 @@ +import { useMemo } from 'react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Descriptions } from '@/components'; +import { TableRowDataType } from '../../hooks/useColumns'; + +interface IProps { + data: TableRowDataType; +} + +const BasicInfo = ({ data }: IProps) => { + const { getIntlText } = useI18n(); + + const desList = useMemo(() => { + return [ + { + key: 'name', + label: getIntlText('device.label.param_entity_name'), + content: data.entityName, + }, + { + key: 'dataType', + label: getIntlText('common.label.data_type'), + content: data.entityValueType, + }, + ]; + }, [data]); + + return ; +}; + +export default BasicInfo; diff --git a/apps/web/src/pages/entity/components/detail/index.tsx b/apps/web/src/pages/entity/components/detail/index.tsx new file mode 100644 index 00000000..6773026c --- /dev/null +++ b/apps/web/src/pages/entity/components/detail/index.tsx @@ -0,0 +1,154 @@ +import { useState, useRef, useCallback } from 'react'; +import { Button, Popover } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { cloneDeep } from 'lodash-es'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { Modal } from '@milesight/shared/src/components'; +import { DateRangePickerValueType } from '@/components/date-range-picker'; +import { DateRangePicker, TablePro } from '@/components'; +import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { TableRowDataType } from '../../hooks'; +import BasicInfo from './basicInfo'; +import useColumns, { type UseColumnsProps, type HistoryRowDataType } from './useColumns'; + +interface IProps { + detail: TableRowDataType; + onCancel: () => void; +} + +export default (props: IProps) => { + const { getIntlText } = useI18n(); + const { detail, onCancel } = props; + const [time, setTime] = useState(null); + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); + const [anchorEl, setAnchorEl] = useState(null); + const [isShowFilter, setIsShowFilter] = useState(false); + // Store the selected time range and send it to the backend as a filter condition when the confirm button is clicked. + const timeRef = useRef(null); + + const { + data: entityData, + loading, + run: getList, + } = useRequest( + async () => { + const { page, pageSize } = paginationModel; + const [error, resp] = await awaitWrap( + entityAPI.getHistory({ + entity_id: detail.entityId, + start_timestamp: timeRef.current?.start + ? timeRef.current?.start.valueOf() + : undefined, + end_timestamp: timeRef.current?.end + ? (timeRef.current?.end.valueOf() || 0) + 86399000 + : undefined, // Adding 86399000 manually here is to ensure the end time is 23:59:59. + page_size: pageSize, + page_number: page + 1, + }), + ); + const data = getResponseData(resp); + + if (error || !data || !isRequestSuccess(resp)) return; + + return objectToCamelCase(data); + }, + { + debounceWait: 300, + refreshDeps: [paginationModel], + }, + ); + + /** Filter relevant events */ + const showFilter = (e: React.MouseEvent) => { + setIsShowFilter(true); + setAnchorEl(e.currentTarget); + }; + + const handleCloseFilterPopover = () => { + setIsShowFilter(false); + }; + + const changeFilter = (values: DateRangePickerValueType | null) => { + setTime(values); + }; + + const handleResetFilter = () => { + setTime(null); + timeRef.current = null; + }; + + const handleConfirmFilter = () => { + handleCloseFilterPopover(); + timeRef.current = cloneDeep(time); + getList(); + }; + + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( + (type, e) => { + switch (type) { + case 'filter': { + showFilter(e); + break; + } + default: { + break; + } + } + }, + [showFilter], + ); + const columns = useColumns({ + onButtonClick: handleTableBtnClick, + isShowFilter, + }); + + return ( + + +
+
+ {getIntlText('entity.label.historical_data')} +
+
+ + loading={loading} + columns={columns} + getRowId={record => record.timestamp} + rows={entityData?.content} + rowCount={entityData?.total || 0} + paginationModel={paginationModel} + toolbarRender={false} + onPaginationModelChange={setPaginationModel} + onRefreshButtonClick={getList} + /> + +
+ +
+
+ +
+ +
+
+
+
+ ); +}; diff --git a/apps/web/src/pages/entity/components/detail/useColumns.tsx b/apps/web/src/pages/entity/components/detail/useColumns.tsx new file mode 100644 index 00000000..e23e4a71 --- /dev/null +++ b/apps/web/src/pages/entity/components/detail/useColumns.tsx @@ -0,0 +1,88 @@ +import { useMemo } from 'react'; +import { IconButton } from '@mui/material'; +import classnames from 'classnames'; +import { useI18n, useTime } from '@milesight/shared/src/hooks'; +import { FilterAltIcon } from '@milesight/shared/src/components'; +import { type ColumnType } from '@/components'; +import { type EntityAPISchema } from '@/services/http'; + +export type HistoryRowDataType = ObjectToCamelCase< + EntityAPISchema['getHistory']['response']['content'][0] +>; + +type OperationType = 'filter'; + +export interface UseColumnsProps { + /** + * 操作 Button 点击回调 + */ + onButtonClick: (type: OperationType, e: React.MouseEvent) => void; + isShowFilter: boolean; +} + +const useColumns = ({ + onButtonClick, + isShowFilter, +}: UseColumnsProps) => { + const { getIntlText } = useI18n(); + const { getTimeFormat } = useTime(); + + const columns: ColumnType[] = useMemo(() => { + return [ + { + field: 'value', + headerName: getIntlText('common.label.value'), + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'timestamp', + headerName: getIntlText('common.label.update_time'), + flex: 1, + minWidth: 150, + ellipsis: true, + renderHeader() { + return ( +
+
+ {getIntlText('common.label.update_time')} +
+ ) => + onButtonClick('filter', e) + } + > + + +
+ ); + }, + renderCell({ value }) { + if (!value) { + return null; + } + return getTimeFormat(Number(value)); + }, + }, + { + field: 'entityValueSource', + headerName: getIntlText('entity.label.value_source'), + align: 'left', + headerAlign: 'left', + flex: 1, + minWidth: 150, + ellipsis: true, + }, + ]; + }, [getIntlText, getTimeFormat, isShowFilter]); + + return columns; +}; + +export default useColumns; diff --git a/apps/web/src/pages/entity/components/edit-entity/index.tsx b/apps/web/src/pages/entity/components/edit-entity/index.tsx new file mode 100644 index 00000000..f4fdfc26 --- /dev/null +++ b/apps/web/src/pages/entity/components/edit-entity/index.tsx @@ -0,0 +1,57 @@ +import { useRef } from 'react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal, Form } from '@milesight/shared/src/components'; +import { TableRowDataType } from '../../hooks'; + +interface IProps { + onCancel: () => void; + onOk: (entityName: string) => void; + data?: TableRowDataType; +} + +const EditEntity = (props: IProps) => { + const { getIntlText } = useI18n(); + const { onOk, onCancel, data } = props; + const formRef = useRef(); + + const formItems = [ + { + label: getIntlText('dashboard.dashboard_name'), + name: 'entityName', + type: 'TextField', + defaultValue: data?.entityName, + rules: { + required: true, + maxLength: { + value: 64, + message: '', + }, + }, + }, + ]; + + const handleClose = () => { + onCancel(); + }; + + const handleOk = () => { + formRef.current?.handleSubmit(); + }; + + const handleSubmit = (values: { entityName: string }) => { + onOk(values.entityName); + }; + + return ( + + ref={formRef} formItems={formItems} onOk={handleSubmit} /> + + ); +}; + +export default EditEntity; diff --git a/apps/web/src/pages/entity/components/entity/index.tsx b/apps/web/src/pages/entity/components/entity/index.tsx new file mode 100644 index 00000000..fca429bd --- /dev/null +++ b/apps/web/src/pages/entity/components/entity/index.tsx @@ -0,0 +1,192 @@ +import { useState, useMemo, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button, Stack } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { IosShareIcon, toast } from '@milesight/shared/src/components'; +import { TablePro, useConfirm } from '@/components'; +import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import useColumns, { + type UseColumnsProps, + type TableRowDataType, +} from '../../hooks/useEntityColumns'; +import Detail from '../detail'; +import EditEntity from '../edit-entity'; + +export default () => { + const navigate = useNavigate(); + const { getIntlText } = useI18n(); + + const [keyword, setKeyword] = useState(); + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); + const [selectedIds, setSelectedIds] = useState([]); + const [anchorEl, setAnchorEl] = useState(null); + const [detail, setDetail] = useState(null); + const [detailVisible, setDetailVisible] = useState(false); + const [editVisible, setEditVisible] = useState(false); + const open = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const { + data: entityData, + loading, + run: getList, + } = useRequest( + async () => { + const { page, pageSize } = paginationModel; + const [error, resp] = await awaitWrap( + entityAPI.getList({ + keyword, + page_size: pageSize, + page_number: page + 1, + }), + ); + const data = getResponseData(resp); + + if (error || !data || !isRequestSuccess(resp)) return; + + return objectToCamelCase(data); + }, + { + debounceWait: 300, + refreshDeps: [keyword, paginationModel], + }, + ); + + const confirm = useConfirm(); + const handleExportConfirm = useCallback( + (ids?: ApiKey[]) => { + const idsToDelete = ids || [...selectedIds]; + + confirm({ + title: getIntlText('common.label.delete'), + description: getIntlText('device.message.delete_tip'), + confirmButtonText: getIntlText('common.label.delete'), + confirmButtonProps: { + color: 'error', + }, + onConfirm: async () => { + const [error, resp] = await awaitWrap( + entityAPI.deleteEntities({ entity_id_list: idsToDelete }), + ); + + if (error || !isRequestSuccess(resp)) return; + + getList(); + setSelectedIds([]); + toast.success(getIntlText('common.message.operation_success')); + }, + }); + }, + [confirm, getIntlText, getList, selectedIds], + ); + + /** Details event related */ + const handleDetail = (data: TableRowDataType) => { + setDetail(data); + setDetailVisible(true); + }; + + const handleDetailClose = () => { + setDetailVisible(false); + setDetail(null); + }; + + /** Edit event related */ + const showEdit = (data: TableRowDataType) => { + setDetail(data); + setEditVisible(true); + }; + + const handleEditClose = () => { + setEditVisible(false); + setDetail(null); + }; + + const handleEdit = async (name: string) => { + const [error, resp] = await awaitWrap( + entityAPI.editEntity({ id: detail?.entityId || '', entity_name: name }), + ); + + if (error || !isRequestSuccess(resp)) return; + + setEditVisible(false); + getList(); + setDetail(null); + toast.success(getIntlText('common.message.operation_success')); + }; + + const toolbarRender = useMemo(() => { + return ( + + + + ); + }, [getIntlText, handleExportConfirm, selectedIds]); + + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( + (type, record) => { + switch (type) { + case 'detail': { + handleDetail(record); + break; + } + case 'edit': { + showEdit(record); + break; + } + default: { + break; + } + } + }, + [navigate, handleExportConfirm], + ); + const columns = useColumns({ onButtonClick: handleTableBtnClick }); + + return ( +
+ + checkboxSelection + loading={loading} + columns={columns} + getRowId={record => record.entityId} + rows={entityData?.content} + rowCount={entityData?.total || 0} + paginationModel={paginationModel} + rowSelectionModel={selectedIds} + // isRowSelectable={({ row }) => row.deletable} + toolbarRender={toolbarRender} + onPaginationModelChange={setPaginationModel} + onRowSelectionModelChange={setSelectedIds} + onRowDoubleClick={({ row }) => { + navigate(`/device/detail/${row.entityId}`, { state: row }); + }} + onSearch={setKeyword} + onRefreshButtonClick={getList} + /> + {!!detailVisible && !!detail && } + {!!editVisible && !!detail && ( + + )} +
+ ); +}; diff --git a/apps/web/src/pages/entity/components/index.ts b/apps/web/src/pages/entity/components/index.ts new file mode 100644 index 00000000..ef9b668c --- /dev/null +++ b/apps/web/src/pages/entity/components/index.ts @@ -0,0 +1 @@ +export { default as AddModal } from './add-modal'; diff --git a/apps/web/src/pages/entity/hooks/index.ts b/apps/web/src/pages/entity/hooks/index.ts new file mode 100644 index 00000000..c121352e --- /dev/null +++ b/apps/web/src/pages/entity/hooks/index.ts @@ -0,0 +1 @@ +export { default as useColumns, type UseColumnsProps, type TableRowDataType } from './useColumns'; diff --git a/apps/web/src/pages/entity/hooks/useColumns.tsx b/apps/web/src/pages/entity/hooks/useColumns.tsx new file mode 100644 index 00000000..46ed682c --- /dev/null +++ b/apps/web/src/pages/entity/hooks/useColumns.tsx @@ -0,0 +1,128 @@ +import { useMemo } from 'react'; +import { Stack, IconButton, Chip, type ChipProps } from '@mui/material'; +import { useI18n, useTime } from '@milesight/shared/src/hooks'; +import { ListAltIcon, DeleteOutlineIcon } from '@milesight/shared/src/components'; +import { Tooltip, type ColumnType } from '@/components'; +import { type EntityAPISchema } from '@/services/http'; + +type OperationType = 'detail' | 'delete'; + +export type TableRowDataType = ObjectToCamelCase< + EntityAPISchema['getList']['response']['content'][0] +>; + +// 实体类型 Tag 颜色映射 +const entityTypeColorMap: Record = { + event: 'success', + service: 'warning', + property: 'primary', +}; + +export interface UseColumnsProps { + /** + * 操作 Button 点击回调 + */ + onButtonClick: (type: OperationType, record: T) => void; +} + +const useColumns = ({ onButtonClick }: UseColumnsProps) => { + const { getIntlText } = useI18n(); + const { getTimeFormat } = useTime(); + + const columns: ColumnType[] = useMemo(() => { + return [ + { + field: 'entityName', + headerName: getIntlText('device.label.param_entity_name'), + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'entityId', + headerName: getIntlText('device.label.param_entity_id'), + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'entityType', + headerName: getIntlText('common.label.type'), + flex: 1, + minWidth: 150, + renderCell({ value }) { + return ( + + ); + }, + }, + { + field: 'entityValueType', + headerName: getIntlText('common.label.data_type'), + align: 'left', + headerAlign: 'left', + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'createdAt', + headerName: getIntlText('common.label.create_time'), + flex: 1, + minWidth: 150, + ellipsis: true, + renderCell({ value }) { + return getTimeFormat(value); + }, + }, + { + field: '$operation', + headerName: getIntlText('common.label.operation'), + flex: 2, + minWidth: 100, + renderCell({ row }) { + return ( + + + onButtonClick('detail', row)} + > + + + + + onButtonClick('delete', row)} + > + + + + + ); + }, + }, + ]; + }, [getIntlText, getTimeFormat, onButtonClick]); + + return columns; +}; + +export default useColumns; diff --git a/apps/web/src/pages/entity/hooks/useEntityColumns.tsx b/apps/web/src/pages/entity/hooks/useEntityColumns.tsx new file mode 100644 index 00000000..44b52614 --- /dev/null +++ b/apps/web/src/pages/entity/hooks/useEntityColumns.tsx @@ -0,0 +1,121 @@ +import { useMemo } from 'react'; +import { Stack, IconButton, Chip, type ChipProps } from '@mui/material'; +import { useI18n, useTime } from '@milesight/shared/src/hooks'; +import { ListAltIcon, EditIcon } from '@milesight/shared/src/components'; +import { Tooltip, type ColumnType } from '@/components'; +import { type EntityAPISchema } from '@/services/http'; + +type OperationType = 'detail' | 'edit'; + +export type TableRowDataType = ObjectToCamelCase< + EntityAPISchema['getList']['response']['content'][0] +>; + +// 实体类型 Tag 颜色映射 +const entityTypeColorMap: Record = { + event: 'success', + service: 'warning', + property: 'primary', +}; + +export interface UseColumnsProps { + /** + * 操作 Button 点击回调 + */ + onButtonClick: (type: OperationType, record: T) => void; +} + +const useEntityColumns = ({ onButtonClick }: UseColumnsProps) => { + const { getIntlText } = useI18n(); + const { getTimeFormat } = useTime(); + + const columns: ColumnType[] = useMemo(() => { + return [ + { + field: 'entityName', + headerName: getIntlText('device.label.param_entity_name'), + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'entityKey', + headerName: getIntlText('device.label.param_entity_id'), + flex: 1, + minWidth: 200, + ellipsis: true, + }, + { + field: 'entityType', + headerName: getIntlText('common.label.type'), + flex: 1, + minWidth: 150, + renderCell({ value }) { + return ( + + ); + }, + }, + { + field: 'entityValueType', + headerName: getIntlText('common.label.data_type'), + align: 'left', + headerAlign: 'left', + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'integrationName', + headerName: getIntlText('device.label.param_source'), + flex: 1, + minWidth: 250, + ellipsis: true, + }, + { + field: '$operation', + headerName: getIntlText('common.label.operation'), + flex: 2, + minWidth: 100, + renderCell({ row }) { + return ( + + + onButtonClick('detail', row)} + > + + + + + onButtonClick('edit', row)} + > + + + + + ); + }, + }, + ]; + }, [getIntlText, getTimeFormat, onButtonClick]); + + return columns; +}; + +export default useEntityColumns; diff --git a/apps/web/src/pages/entity/index.tsx b/apps/web/src/pages/entity/index.tsx new file mode 100644 index 00000000..7f8e6543 --- /dev/null +++ b/apps/web/src/pages/entity/index.tsx @@ -0,0 +1,237 @@ +import { useState, useMemo, useCallback } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { Button, Stack, Menu, MenuItem } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { + AddIcon, + DeleteOutlineIcon, + NoteAddIcon, + CalculateIcon, + toast, +} from '@milesight/shared/src/components'; +import { Breadcrumbs, TablePro, useConfirm } from '@/components'; +import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; +import { AddModal } from './components'; +import './style.less'; + +export default () => { + const navigate = useNavigate(); + const { getIntlText } = useI18n(); + + // ---------- 列表数据相关 ---------- + const [keyword, setKeyword] = useState(); + const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); + const [selectedIds, setSelectedIds] = useState([]); + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + const { + data: entityData, + loading, + run: getDeviceList, + } = useRequest( + async () => { + const { page, pageSize } = paginationModel; + const [error, resp] = await awaitWrap( + entityAPI.getList({ + keyword, + page_size: pageSize, + page_number: page + 1, + }), + ); + const data = getResponseData(resp); + + // console.log({ error, resp }); + if (error || !data || !isRequestSuccess(resp)) return; + + return objectToCamelCase(data); + }, + { + debounceWait: 300, + refreshDeps: [keyword, paginationModel], + }, + ); + + // ---------- 设备添加相关 ---------- + const [modalOpen, setModalOpen] = useState(false); + + // ---------- 数据删除相关 ---------- + const confirm = useConfirm(); + const handleDeleteConfirm = useCallback( + (ids?: ApiKey[]) => { + const idsToDelete = ids || [...selectedIds]; + + confirm({ + title: getIntlText('common.label.delete'), + description: getIntlText('device.message.delete_tip'), + confirmButtonText: getIntlText('common.label.delete'), + confirmButtonProps: { + color: 'error', + }, + onConfirm: async () => { + const [error, resp] = await awaitWrap( + entityAPI.deleteEntities({ entity_id_list: idsToDelete }), + ); + + // console.log({ error, resp }); + if (error || !isRequestSuccess(resp)) return; + + getDeviceList(); + setSelectedIds([]); + toast.success(getIntlText('common.message.delete_success')); + }, + }); + }, + [confirm, getIntlText, getDeviceList, selectedIds], + ); + + // ---------- Table 渲染相关 ---------- + const toolbarRender = useMemo(() => { + return ( + + + + + ); + }, [getIntlText, handleDeleteConfirm, selectedIds]); + + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( + (type, record) => { + // console.log(type, record); + switch (type) { + case 'detail': { + navigate(`/device/detail/${record.entityId}`, { state: record }); + break; + } + case 'delete': { + handleDeleteConfirm([record.entityId]); + break; + } + default: { + break; + } + } + }, + [navigate, handleDeleteConfirm], + ); + const columns = useColumns({ onButtonClick: handleTableBtnClick }); + + const handleShowAddOnly = () => { + setModalOpen(true); + handleClose(); + }; + + return ( +
+ +
+
+ + checkboxSelection + loading={loading} + columns={columns} + getRowId={record => record.entityId} + rows={entityData?.content} + rowCount={entityData?.total || 0} + paginationModel={paginationModel} + rowSelectionModel={selectedIds} + // isRowSelectable={({ row }) => row.deletable} + toolbarRender={toolbarRender} + onPaginationModelChange={setPaginationModel} + onRowSelectionModelChange={setSelectedIds} + onRowDoubleClick={({ row }) => { + navigate(`/device/detail/${row.entityId}`, { state: row }); + }} + onSearch={setKeyword} + onRefreshButtonClick={getDeviceList} + /> +
+
+ {modalOpen && ( + setModalOpen(false)} + onOk={() => { + getDeviceList(); + setModalOpen(false); + }} + /> + )} + + +
+ {getIntlText('entity.label.create_property')} +
+
+ +
+ + {getIntlText('entity.label.create_entity_only')} +
+
+ +
+ {getIntlText('entity.label.create_service')} +
+
+ +
+ + {getIntlText('entity.label.create_entity_from_workflow')} +
+
+
+
+ ); +}; diff --git a/apps/web/src/pages/entity/style.less b/apps/web/src/pages/entity/style.less new file mode 100644 index 00000000..100c64ad --- /dev/null +++ b/apps/web/src/pages/entity/style.less @@ -0,0 +1,33 @@ +.@{prefix}-view-entity { + position: relative; + padding: @padding-lg; + + .@{prefix}-view__inner { + display: flex; + flex-direction: column; + padding: @padding-lg; + } +} + +.entity { + &-add-menu-group-name { + font-weight: @font-weight-medium; + color: var(--text-color-secondary); + } + + &-add-menu-item { + display: flex; + align-items: center; + font-weight: @font-weight-medium; + + &-icon { + margin-right: @margin-sm; + font-size: 24px; + color: var(--text-color-secondary); + } + } + + &-modal-button-group { + margin-bottom: @margin-xl; + } +} \ No newline at end of file diff --git a/apps/web/src/pages/entity/views/detail/components/basic-table/edit-dialog.tsx b/apps/web/src/pages/entity/views/detail/components/basic-table/edit-dialog.tsx new file mode 100644 index 00000000..1044deaf --- /dev/null +++ b/apps/web/src/pages/entity/views/detail/components/basic-table/edit-dialog.tsx @@ -0,0 +1,92 @@ +import { useEffect } from 'react'; +import { TextField } from '@mui/material'; +import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; +import cls from 'classnames'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal, toast, type ModalProps } from '@milesight/shared/src/components'; +import { checkRequired } from '@milesight/shared/src/utils/validators'; +import { awaitWrap, deviceAPI, isRequestSuccess, type DeviceAPISchema } from '@/services/http'; + +interface Props extends Omit { + /** 设备详情 */ + data?: ObjectToCamelCase; + + /** 编辑失败回调 */ + onError?: (error?: any) => void; + + /** 编辑成功回调 */ + onSuccess?: () => void; +} + +interface FormDataProps { + name: string; +} + +const EditDialog: React.FC = ({ data, visible, onCancel, onError, onSuccess }) => { + const { getIntlText } = useI18n(); + const { control, formState, handleSubmit, reset, setValue } = useForm(); + const onSubmit: SubmitHandler = async formData => { + console.log(formData); + if (!data?.id) return; + + const [error, resp] = await awaitWrap( + deviceAPI.updateDevice({ id: data.id, name: formData.name }), + ); + + if (error || !isRequestSuccess(resp)) { + onError?.(error); + return; + } + + onSuccess?.(); + toast.success(getIntlText('common.message.operation_success')); + }; + + // 填充/重置表单 + useEffect(() => { + if (!visible || !data) { + setTimeout(reset, 100); + return; + } + + (Object.keys(data) as (keyof FormDataProps)[]).forEach(key => { + setValue(key, data[key]); + }); + }, [data, visible, reset, setValue]); + + return ( + + + name="name" + control={control} + rules={{ + validate: { checkRequired: checkRequired() }, + }} + render={({ field: { onChange, value, disabled }, fieldState: { error } }) => { + return ( + + ); + }} + /> + + ); +}; + +export default EditDialog; diff --git a/apps/web/src/pages/entity/views/detail/components/basic-table/index.tsx b/apps/web/src/pages/entity/views/detail/components/basic-table/index.tsx new file mode 100644 index 00000000..83e6100a --- /dev/null +++ b/apps/web/src/pages/entity/views/detail/components/basic-table/index.tsx @@ -0,0 +1,116 @@ +import { useCallback, useMemo, useState, forwardRef, useImperativeHandle } from 'react'; +import { Stack, IconButton } from '@mui/material'; +import { useI18n, useTime } from '@milesight/shared/src/hooks'; +import { EditIcon } from '@milesight/shared/src/components'; +import { Descriptions, Tooltip } from '@/components'; +import { type DeviceAPISchema } from '@/services/http'; +import EditDialog from './edit-dialog'; + +interface Props { + /** 是否加载中 */ + loading?: boolean; + + /** 设备详情 */ + data?: ObjectToCamelCase; + + /** 编辑成功回调 */ + onEditSuccess?: () => void; +} + +export interface BasicTableInstance { + /** 打开编辑弹窗 */ + openEditDialog: () => void; +} + +/** + * 设备基本信息表格 + */ +const BasicTable = ( + { data, loading, onEditSuccess }: Props, + ref?: React.ForwardedRef, +) => { + const { getIntlText } = useI18n(); + const { getTimeFormat } = useTime(); + const [dialogOpen, setDialogOpen] = useState(false); + const descList = useMemo(() => { + return [ + { + key: 'name', + label: getIntlText('common.label.name'), + content: ( + + + { + setDialogOpen(true); + }} + > + + + + ), + }, + { + key: 'externalId', + label: getIntlText('device.label.param_external_id'), + content: data?.identifier, + }, + { + key: 'source', + label: getIntlText('device.label.param_source'), + content: , + }, + { + key: 'createTime', + label: getIntlText('common.label.create_time'), + content: getTimeFormat(data?.createdAt), + }, + { + key: 'founder', + label: getIntlText('device.label.param_founder'), + content: data?.integrationName, + }, + { + key: 'id', + label: getIntlText('device.label.param_device_id'), + content: data?.id, + }, + ]; + }, [data, getIntlText, getTimeFormat]); + const handleDialogClose = useCallback(() => { + setDialogOpen(false); + }, []); + + // 暴露给父组件的实例 + useImperativeHandle(ref, () => { + return { + openEditDialog: () => { + setDialogOpen(true); + }, + }; + }); + + return ( +
+ + { + handleDialogClose(); + onEditSuccess?.(); + }} + /> +
+ ); +}; + +const ForwardBasicTable = (forwardRef as FixedForwardRef)(BasicTable); + +export default ForwardBasicTable; diff --git a/apps/web/src/pages/entity/views/detail/components/entity-table/index.tsx b/apps/web/src/pages/entity/views/detail/components/entity-table/index.tsx new file mode 100644 index 00000000..bd9326a9 --- /dev/null +++ b/apps/web/src/pages/entity/views/detail/components/entity-table/index.tsx @@ -0,0 +1,110 @@ +import React, { useMemo } from 'react'; +import { Stack, Chip, type ChipProps } from '@mui/material'; +import { getGridSingleSelectOperators, getGridStringOperators } from '@mui/x-data-grid'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { TablePro, type ColumnType } from '@/components'; +import { type DeviceAPISchema } from '@/services/http'; + +interface Props { + data?: ObjectToCamelCase; + + /** 点击 Table 刷新按钮回调 */ + onRefresh?: () => void; +} + +type TableRowDataType = ObjectToCamelCase; + +// 实体类型 Tag 颜色映射 +const entityTypeColorMap: Record = { + event: 'success', + service: 'warning', + property: 'primary', +}; + +/** + * 设备实体数据表格 + */ +const EntityTable: React.FC = ({ data, onRefresh }) => { + const { getIntlText } = useI18n(); + const columns = useMemo(() => { + const entityTypeFilterOptions: { label: EntityType; value: EntityType }[] = [ + { + label: 'PROPERTY', + value: 'PROPERTY', + }, + { + label: 'SERVICE', + value: 'SERVICE', + }, + { + label: 'EVENT', + value: 'EVENT', + }, + ]; + const result: ColumnType[] = [ + { + field: 'name', + headerName: getIntlText('device.label.param_entity_name'), + flex: 1, + minWidth: 150, + ellipsis: true, + filterable: true, + disableColumnMenu: false, + filterOperators: getGridStringOperators().filter(item => item.value === 'contains'), + }, + { + field: 'id', + headerName: getIntlText('device.label.param_entity_id'), + flex: 1, + minWidth: 150, + ellipsis: true, + }, + { + field: 'type', + headerName: getIntlText('common.label.type'), + flex: 1, + minWidth: 150, + filterable: true, + disableColumnMenu: false, + type: 'singleSelect', + valueOptions: entityTypeFilterOptions, + filterOperators: getGridSingleSelectOperators().filter(item => item.value === 'is'), + renderCell({ value }) { + return ( + + ); + }, + }, + { + field: 'valueType', + headerName: getIntlText('common.label.data_type'), + align: 'left', + headerAlign: 'left', + flex: 1, + minWidth: 150, + ellipsis: true, + }, + ]; + + return result; + }, [getIntlText]); + + return ( + + + paginationMode="client" + loading={false} + columns={columns} + rows={data?.entities} + onRefreshButtonClick={onRefresh} + /> + + ); +}; + +export default EntityTable; diff --git a/apps/web/src/pages/entity/views/detail/components/index.ts b/apps/web/src/pages/entity/views/detail/components/index.ts new file mode 100644 index 00000000..74c166a6 --- /dev/null +++ b/apps/web/src/pages/entity/views/detail/components/index.ts @@ -0,0 +1,2 @@ +export { default as BasicTable, type BasicTableInstance } from './basic-table'; +export { default as EntityTable } from './entity-table'; diff --git a/apps/web/src/pages/entity/views/detail/index.tsx b/apps/web/src/pages/entity/views/detail/index.tsx new file mode 100644 index 00000000..b955280f --- /dev/null +++ b/apps/web/src/pages/entity/views/detail/index.tsx @@ -0,0 +1,116 @@ +import { useMemo, useState, useEffect } from 'react'; +import { useParams, useLocation } from 'react-router-dom'; +import { Tabs, Tab } from '@mui/material'; +import { useRequest } from 'ahooks'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { useRouteTab } from '@/hooks'; +import { + deviceAPI, + awaitWrap, + getResponseData, + isRequestSuccess, + type DeviceAPISchema, +} from '@/services/http'; +import { Breadcrumbs, TabPanel } from '@/components'; +import { BasicTable, EntityTable } from './components'; +import './style.less'; + +type DeviceDetailType = ObjectToCamelCase; + +export default () => { + const { state } = useLocation(); + const { deviceId } = useParams(); + const { getIntlText } = useI18n(); + + // ---------- 设备详情相关逻辑 ---------- + const [deviceDetail, setDeviceDetail] = useState(); + const { + loading, + // data: deviceDetail, + run: getDeviceDetail, + } = useRequest( + async () => { + if (!deviceId) return; + const [error, resp] = await awaitWrap(deviceAPI.getDetail({ id: deviceId })); + const respData = getResponseData(resp); + + if (error || !respData || !isRequestSuccess(resp)) return; + const data = objectToCamelCase(respData); + + setDeviceDetail(data); + return data; + }, + { + debounceWait: 300, + refreshDeps: [deviceId], + }, + ); + + // 填充默认数据 + useEffect(() => { + if (!state?.id || state.id !== deviceId) return; + + setDeviceDetail(detail => { + if (detail) return detail; + return state; + }); + }, [state, deviceId]); + + // ---------- Tab 切换相关逻辑 ---------- + const tabs = useMemo(() => { + return [ + { + key: 'basic', + label: getIntlText('device.detail.basic_info'), + component: ( + + ), + }, + { + key: 'entity', + label: getIntlText('device.detail.entity_data'), + component: , + }, + ]; + }, [deviceDetail, loading, getIntlText, getDeviceDetail]); + const [tabKey, setTabKey] = useRouteTab(tabs[0].key); + + return ( +
+ { + const newNavs = [...navs]; + const lastNav = newNavs[newNavs.length - 1]; + + lastNav.title = deviceDetail?.name || lastNav.title; + return newNavs; + }} + /> +
+
+ setTabKey(value)} + > + {tabs.map(({ key, label }) => ( + + ))} + +
+
+ {tabs.map(({ key, component }) => ( + + {component} + + ))} +
+
+
+ ); +}; diff --git a/apps/web/src/pages/entity/views/detail/style.less b/apps/web/src/pages/entity/views/detail/style.less new file mode 100644 index 00000000..da652ff5 --- /dev/null +++ b/apps/web/src/pages/entity/views/detail/style.less @@ -0,0 +1,36 @@ +.@{prefix}-view-device-detail { + display: flex; + flex-direction: column; + padding: 0; + + .topbar { + display: flex; + align-items: center; + + .@{prefix}-tabs { + flex: 1; + } + + .btn-wrap { + display: flex; + align-items: center; + height: 100%; + padding: 0 @padding-lg; + background-color: var(--component-background); + border-bottom: 1px solid var(--border-color-base); + } + } + + .@{prefix}-tab-content { + flex: 1; + height: 0; + padding: @padding-lg; + margin: @margin-lg; + background-color: var(--component-background); + border-radius: @border-radius-base; + + .@{prefix}-tabpanel { + height: 100%; + } + } +} diff --git a/apps/web/src/plugin/components/entity-select/index.tsx b/apps/web/src/plugin/components/entity-select/index.tsx index 6f380db8..2e3255b7 100644 --- a/apps/web/src/plugin/components/entity-select/index.tsx +++ b/apps/web/src/plugin/components/entity-select/index.tsx @@ -7,7 +7,9 @@ import { useEntitySelectOptions } from '../../hooks'; import './style.less'; type EntitySelectProps = AutocompleteProps & - EntitySelectCommonProps; + EntitySelectCommonProps & { + required?: boolean; + }; /** * 实体选择下拉框组件(单选) @@ -21,6 +23,7 @@ const EntitySelect = (props: EntitySelectProps) => { entityAccessMods, entityExcludeChildren, customFilterEntity, + required, ...restProps } = props; @@ -73,6 +76,7 @@ const EntitySelect = (props: EntitySelectProps) => { diff --git a/apps/web/src/plugin/components/input/index.tsx b/apps/web/src/plugin/components/input/index.tsx index 545d37ba..469be6b2 100644 --- a/apps/web/src/plugin/components/input/index.tsx +++ b/apps/web/src/plugin/components/input/index.tsx @@ -4,6 +4,7 @@ type InputType = TextFieldProps; const Input = (props: InputType) => { const { sx, title, ...rest } = props; + return ( & - EntitySelectCommonProps; + EntitySelectCommonProps & { + required?: boolean; + }; /** * 实体选择下拉框组件(多选) @@ -22,6 +24,7 @@ const MultiEntitySelect = (props: EntitySelectProps) => { entityAccessMods, entityExcludeChildren, customFilterEntity, + required, /** * 默认最大可选择 5 个 */ @@ -88,6 +91,7 @@ const MultiEntitySelect = (props: EntitySelectProps) => { {...params} label={getIntlText('common.label.entity')} error={(restProps as any).error} + required={!!required} helperText={ (restProps as any).error ? (
diff --git a/apps/web/src/plugin/components/select/index.tsx b/apps/web/src/plugin/components/select/index.tsx index 9d339d01..13c86bbb 100644 --- a/apps/web/src/plugin/components/select/index.tsx +++ b/apps/web/src/plugin/components/select/index.tsx @@ -47,7 +47,7 @@ const Select = (props: SelectProps) => { return ( {!!title && ( - + {title} )} diff --git a/apps/web/src/plugin/config-plugin/style.less b/apps/web/src/plugin/config-plugin/style.less index 03ac6dbc..ffcd7177 100644 --- a/apps/web/src/plugin/config-plugin/style.less +++ b/apps/web/src/plugin/config-plugin/style.less @@ -25,7 +25,7 @@ &-right { width: 400px; - padding: @padding-sm @padding-lg 56px; + padding: 0 @padding-lg 56px; overflow-y: auto; border-top: 1px solid var(--border-color-base); } diff --git a/apps/web/src/plugin/render/render-configure.tsx b/apps/web/src/plugin/render/render-configure.tsx index 7d355ca1..2413ebf5 100644 --- a/apps/web/src/plugin/render/render-configure.tsx +++ b/apps/web/src/plugin/render/render-configure.tsx @@ -79,11 +79,11 @@ const CreatePlugin = forwardRef((props: IPlugin, ref: any) => { if (!defaultValues[component.key] && value) { defaultValues[component.key] = value; } - return ( , + ErrorBoundary, + handle: { + get title() { + return intl.get('common.label.device'); + }, + icon: , + }, + children: [ + { + index: true, + async lazy() { + const { default: Component } = await import('@/pages/entity'); + return { Component }; + }, + ErrorBoundary, + }, + ], + }, { path: '/setting', element: , diff --git a/apps/web/src/services/http/entity.ts b/apps/web/src/services/http/entity.ts index c770c605..9a448e18 100644 --- a/apps/web/src/services/http/entity.ts +++ b/apps/web/src/services/http/entity.ts @@ -38,9 +38,11 @@ export interface EntityAPISchema extends APISchema { /** 实体 ID */ entity_id: ApiKey; /** 开始时间戳,单位 ms */ - start_timestamp: number; + start_timestamp?: number; /** 结束时间戳,单位 ms */ - end_timestamp: number; + end_timestamp?: number; + page_size?: number; + page_number?: number; }; response: SearchResponseType; }; @@ -130,6 +132,42 @@ export interface EntityAPISchema extends APISchema { }; response: unknown; }; + + /** 删除实体 */ + deleteEntities: { + request: { + entity_id_list: ApiKey[]; + }; + response: unknown; + }; + + /** 获取自定义实体列表 */ + getCustomEntityList: { + request: SearchRequestType & { + /** 搜索关键字 */ + keyword?: string; + /** 实体类型 */ + entity_type?: EntitySchema['type']; + /** 实体值类型 */ + entity_value_type?: EntityValueDataType[]; + /** 实体属性(可读、可写、只读) */ + entity_access_mod?: EntityAccessMode[]; + /** + * 不包含子节点(在选择触发服务实体的时候,不能直接下发子实体/在更新属性实体时,不能只更新某个子实体) + */ + exclude_children?: boolean; + }; + response: SearchResponseType; + }; + + /** 编辑实体 */ + editEntity: { + request: { + id: ApiKey; + entity_name: string; + }; + response: unknown; + }; } /** @@ -147,5 +185,8 @@ export default attachAPI(client, { callService: `POST ${API_PREFIX}/entity/service/call`, getEntityStatus: `GET ${API_PREFIX}/entity/:id/status`, getChildrenEntity: `GET ${API_PREFIX}/entity/:id/children`, + deleteEntities: `POST ${API_PREFIX}/entity/batch-delete`, + getCustomEntityList: `POST ${API_PREFIX}/entity/search`, + editEntity: `POST ${API_PREFIX}/entity/:id`, }, }); diff --git a/packages/locales/src/helper.ts b/packages/locales/src/helper.ts index ea1aca99..01e802c4 100644 --- a/packages/locales/src/helper.ts +++ b/packages/locales/src/helper.ts @@ -8,7 +8,7 @@ interface OptInterface { const languages = Object.values(LANGUAGE); /** 各应用依赖语言包模块配置 */ const appLocalModules: Record = { - web: ['global', 'dashboard', 'device', 'error', 'setting'], + web: ['global', 'dashboard', 'device', 'error', 'setting', 'entity'], }; /** diff --git a/packages/locales/src/lang/cn/entity.json b/packages/locales/src/lang/cn/entity.json new file mode 100644 index 00000000..28755966 --- /dev/null +++ b/packages/locales/src/lang/cn/entity.json @@ -0,0 +1,6 @@ +{ + "entity.label.create_property": "Create Property", + "entity.label.create_entity_only": "Create Entity Only", + "entity.label.create_service": "Create Service", + "entity.label.create_entity_from_workflow": "Create from Workflow" +} \ No newline at end of file diff --git a/packages/locales/src/lang/en/entity.json b/packages/locales/src/lang/en/entity.json new file mode 100644 index 00000000..ecbfb7a2 --- /dev/null +++ b/packages/locales/src/lang/en/entity.json @@ -0,0 +1,12 @@ +{ + "entity.label.create_property": "Create Property", + "entity.label.create_entity_only": "Create Entity Only", + "entity.label.create_service": "Create Service", + "entity.label.create_entity_from_workflow": "Create from Workflow", + "entity.label.entity_type_of_access": "Type Of Access", + "entity.label.entity_maximum_value": "Maximum Value", + "entity.label.entity_minimum_value": "Minimum Value", + "entity.label.entity_maximum_length": "Maximum Length", + "entity.label.entity_minimum_length": "Minimum length", + "entity.label.entity_items": "Items" +} \ No newline at end of file diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 80c27443..6c320d68 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -151,5 +151,6 @@ "valid.input.username": "The username must include both upper and lower case letters.", "valid.input.value": "Input value: {0} only.", "valid.input.version": "The version number does not comply with the specification.", - "valid.resp.at_least_one": "Please select at least one {1}." + "valid.resp.at_least_one": "Please select at least one {1}.", + "common.label.create": "Create" } diff --git a/packages/shared/src/components/draggable-resizable/style.less b/packages/shared/src/components/draggable-resizable/style.less index bfdaa070..990dcef7 100644 --- a/packages/shared/src/components/draggable-resizable/style.less +++ b/packages/shared/src/components/draggable-resizable/style.less @@ -1,12 +1,12 @@ .drag { &-resizable-handle { - width: 20px; - height: 20px; - display: inline-block; position: absolute; right: 0; bottom: 0; z-index: 10; + display: inline-block; + width: 20px; + height: 20px; cursor: nw-resize; } } \ No newline at end of file diff --git a/packages/shared/src/components/form/index.tsx b/packages/shared/src/components/form/index.tsx index 46dc5d66..ef5d0535 100644 --- a/packages/shared/src/components/form/index.tsx +++ b/packages/shared/src/components/form/index.tsx @@ -1,5 +1,6 @@ import React, { useEffect, forwardRef, useImperativeHandle, useRef } from 'react'; import { useForm, Controller, FieldValues, type SubmitHandler } from 'react-hook-form'; +import { Grid, Box } from '@mui/material'; import { isEqual } from 'lodash-es'; import useFormItems from './useForm'; import { UseFormItemsProps, FormItemsProps } from './typings'; @@ -87,16 +88,52 @@ const Forms = (props: formProps, ref: any) => { ); }; + const renderChildrenForm = (item: FormItemsProps) => { + return ( +
+ {item?.label ?
{item?.label}
: null} + + + {item?.children?.map((subItem: FormItemsProps) => { + if (subItem.customRender) { + return subItem.customRender(); + } + // const length = (item?.children?.length || 2) - 1; + // const size: number = 6; + return ( + +
+ + key={subItem.name} + {...subItem} + control={control} + /> +
+
+ ); + })} +
+
+
+ ); + }; + return ( // eslint-disable-next-line react/jsx-no-useless-fragment - <> +
{forms?.map((item: FormItemsProps, index: number) => { if (item.multiple) { return item.multipleIndex === 0 ? renderMulForm(index) : null; } + if (item.children?.length) { + return renderChildrenForm(item); + } + if (item.customRender) { + return item.customRender(); + } return key={item.name} {...item} control={control} />; })} - +
); }; diff --git a/packages/shared/src/components/form/style.less b/packages/shared/src/components/form/style.less index 3b9b41af..7c5ea6ce 100644 --- a/packages/shared/src/components/form/style.less +++ b/packages/shared/src/components/form/style.less @@ -1,19 +1,36 @@ .form { + &-contain { + margin-top: @margin-sm; + + .MuiFormHelperText-root { + margin-left: 0; + } + } + &-box { position: relative; - border: 1px solid rgba(var(--mui-palette-common-onBackgroundChannel) / 0.23); - padding: @padding-sm; + padding: @padding-md @padding-sm @padding-sm; margin-bottom: @margin-md; + border: 1px solid rgba(0,0,0, 0.23); border-radius: @border-radius-base; &-label { position: absolute; - background: var(--main-background); top: -(@padding-sm + @padding-xs); left: @padding-md; padding: @padding-xs; + background: var(--main-background); + } + + &-item { + margin: 0 @margin-xxs; + + .MuiFormControl-root { + margin: 0; + } } } + &-item { margin: 0 !important; // margin-top: @margin-sm !important; diff --git a/packages/shared/src/components/form/typings.d.ts b/packages/shared/src/components/form/typings.d.ts index a1f7b702..4b122665 100644 --- a/packages/shared/src/components/form/typings.d.ts +++ b/packages/shared/src/components/form/typings.d.ts @@ -30,11 +30,15 @@ export interface renderType { export interface FormItemsProps { name: Path; render: any; + customRender?: () => React.ReactNode; // 自定义渲染其他其他内容 multiple?: number; multipleIndex?: number; rules?: rulesType; style?: string; title?: string; + label?: string; + defaultValue?: any; + children?: FormItemsProps[]; // 一行多个子表单 } export type UseFormItemsType = Omit; @@ -42,6 +46,5 @@ export type UseFormItemsType = Omit; export interface UseFormItemsProps extends UseFormItemsType { type?: string; props?: any; - label?: string; - render?: (data: renderType) => void; + render?: (data: any) => any; } diff --git a/packages/shared/src/components/form/useForm.tsx b/packages/shared/src/components/form/useForm.tsx index 7238d969..ce3db41e 100644 --- a/packages/shared/src/components/form/useForm.tsx +++ b/packages/shared/src/components/form/useForm.tsx @@ -14,38 +14,76 @@ const useForm = (props: useFormProps) => { const { getIntlText } = useI18n(); const { formItems } = props; - const forms: FormItemsProps[] = useMemo(() => { - return formItems?.map((items: UseFormItemsProps) => { - const { type, render, label, props, ...formItem } = items; - const Component = type ? { ...(Mui as any), DatePicker, Select, Switch }[type] : null; - const { rules } = items; - if (rules?.required && rules.required === true) { - rules.required = getIntlText('valid.input.required'); - } + const getResultItem = (item: UseFormItemsProps, index: number) => { + const { type, render, label, props, rules, ...formItem } = item; + const Component = type ? { ...(Mui as any), DatePicker, Select, Switch }[type] : null; + if (rules?.required && rules.required === true) { + rules.required = getIntlText('valid.input.required'); + } + if ( + (rules?.maxLength?.value || rules?.maxLength?.value === 0) && + !rules?.maxLength?.message + ) { + rules.maxLength.message = getIntlText('valid.input.max_length', { + 1: rules?.maxLength?.value, + }); + } + if ((rules?.max?.value || rules?.max?.value === 0) && !rules?.max?.message) { + rules.max.message = getIntlText('valid.input.max_value', { + 0: rules?.max?.value, + }); + } + if ( + (rules?.minLength?.value || rules?.minLength?.value === 0) && + !rules?.minLength?.message + ) { + rules.minLength.message = getIntlText('valid.input.max_length', { + 0: rules?.minLength?.value, + }); + } + if ((rules?.min?.value || rules?.min?.value === 0) && !rules?.min?.message) { + rules.min.message = getIntlText('valid.input.max_value', { + 0: rules?.min?.value, + }); + } + const children: FormItemsProps[] = []; + if (item.children) { + item.children.forEach((child, i) => { + children.push(getResultItem(child, i)); + }); + } + return { + ...formItem, + rules, + children, + label, + render: + render || + ((data: any) => { + const value = data?.field?.value; + const onChange = data?.field?.onChange; + const error = data?.fieldState?.error; + return ( + + ); + }), + }; + }; - return { - ...formItem, - rules, - render: - render || - ((data: any) => { - const value = data?.field?.value; - const onChange = data?.field?.onChange; - const error = data?.fieldState?.error; - return ( - - ); - }), - }; + const forms: FormItemsProps[] = useMemo(() => { + return formItems?.map((items: UseFormItemsProps, index: number) => { + return getResultItem(items, index); }); }, [formItems]); diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 2757fea7..e39f6ffa 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -70,6 +70,11 @@ export { Share as ShareIcon, Https as HttpsIcon, AdsClick as AdsClickIcon, + NoteAdd as NoteAddIcon, + Calculate as CalculateIcon, + Earbuds as EarbudsIcon, + IosShare as IosShareIcon, + FilterAlt as FilterAltIcon, } from '@mui/icons-material'; export * from './iot-icons'; diff --git a/packages/shared/src/components/modal/index.tsx b/packages/shared/src/components/modal/index.tsx index 9446c67b..083c3d49 100644 --- a/packages/shared/src/components/modal/index.tsx +++ b/packages/shared/src/components/modal/index.tsx @@ -58,16 +58,17 @@ export interface ModalProps { * 外部传入的样式 */ sx?: DialogProps['sx']; - /** - * 确认按钮回调 - */ - onOk: () => void; /** * 取消按钮回调 */ onCancel: () => void; + /** + * 确认按钮回调 + */ + onOk?: () => void; + /** * 挂载节点 */ @@ -133,7 +134,7 @@ const Modal: React.FC = ({ const handleOk = useMemoizedFn(async () => { setLoading(true); - await onOk(); + !!onOk && (await onOk()); setLoading(false); }); diff --git a/packages/shared/src/components/select/index.tsx b/packages/shared/src/components/select/index.tsx index 73bcd896..3bf45722 100644 --- a/packages/shared/src/components/select/index.tsx +++ b/packages/shared/src/components/select/index.tsx @@ -45,13 +45,13 @@ const Select = (props: SelectProps) => { }, [options]); return ( - + {!!label && ( - + {label} )} - + {renderOptions ? renderOptions(options) : getMenuItems?.map((item: OptionsProps) => { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 35ec9dca..231d1e22 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,4 +1,4 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true @@ -513,29 +513,4102 @@ importers: packages: - /@ampproject/remapping@2.3.0: + '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.25.4': + resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.25.6': + resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.24.8': + resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.25.6': + resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.25.6': + resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.24.7': + resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.24.7': + resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.25.6': + resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.6': + resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.6': + resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} + engines: {node: '>=6.9.0'} + + '@changesets/apply-release-plan@7.0.5': + resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} + + '@changesets/assemble-release-plan@6.0.4': + resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} + + '@changesets/changelog-git@0.2.0': + resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + + '@changesets/cli@2.27.8': + resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} + hasBin: true + + '@changesets/config@3.0.3': + resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} + + '@changesets/errors@0.2.0': + resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + + '@changesets/get-dependents-graph@2.1.2': + resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} + + '@changesets/get-release-plan@4.0.4': + resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} + + '@changesets/get-version-range-type@0.4.0': + resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} + + '@changesets/git@3.0.1': + resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} + + '@changesets/logger@0.1.1': + resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + + '@changesets/parse@0.4.0': + resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + + '@changesets/pre@2.0.1': + resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} + + '@changesets/read@0.6.1': + resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} + + '@changesets/should-skip-package@0.1.1': + resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} + + '@changesets/types@4.1.0': + resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} + + '@changesets/types@6.0.0': + resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} + + '@changesets/write@0.3.2': + resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} + + '@commitlint/cli@19.5.0': + resolution: {integrity: sha512-gaGqSliGwB86MDmAAKAtV9SV1SHdmN8pnGq4EJU4+hLisQ7IFfx4jvU4s+pk6tl0+9bv6yT+CaZkufOinkSJIQ==} + engines: {node: '>=v18'} + hasBin: true + + '@commitlint/config-conventional@19.4.1': + resolution: {integrity: sha512-D5S5T7ilI5roybWGc8X35OBlRXLAwuTseH1ro0XgqkOWrhZU8yOwBOslrNmSDlTXhXLq8cnfhQyC42qaUCzlXA==} + engines: {node: '>=v18'} + + '@commitlint/config-validator@19.5.0': + resolution: {integrity: sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.5.0': + resolution: {integrity: sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==} + engines: {node: '>=v18'} + + '@commitlint/execute-rule@19.5.0': + resolution: {integrity: sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==} + engines: {node: '>=v18'} + + '@commitlint/format@19.5.0': + resolution: {integrity: sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@19.5.0': + resolution: {integrity: sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w==} + engines: {node: '>=v18'} + + '@commitlint/lint@19.5.0': + resolution: {integrity: sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg==} + engines: {node: '>=v18'} + + '@commitlint/load@19.5.0': + resolution: {integrity: sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==} + engines: {node: '>=v18'} + + '@commitlint/message@19.5.0': + resolution: {integrity: sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==} + engines: {node: '>=v18'} + + '@commitlint/parse@19.5.0': + resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} + engines: {node: '>=v18'} + + '@commitlint/read@19.5.0': + resolution: {integrity: sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@19.5.0': + resolution: {integrity: sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==} + engines: {node: '>=v18'} + + '@commitlint/rules@19.5.0': + resolution: {integrity: sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@19.5.0': + resolution: {integrity: sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==} + engines: {node: '>=v18'} + + '@commitlint/top-level@19.5.0': + resolution: {integrity: sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==} + engines: {node: '>=v18'} + + '@commitlint/types@19.5.0': + resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} + engines: {node: '>=v18'} + + '@csstools/css-parser-algorithms@2.7.1': + resolution: {integrity: sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-tokenizer': ^2.4.1 + + '@csstools/css-tokenizer@2.4.1': + resolution: {integrity: sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==} + engines: {node: ^14 || ^16 || >=18} + + '@csstools/media-query-list-parser@2.1.13': + resolution: {integrity: sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.7.1 + '@csstools/css-tokenizer': ^2.4.1 + + '@csstools/selector-specificity@3.1.1': + resolution: {integrity: sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss-selector-parser: ^6.0.13 + + '@emotion/babel-plugin@11.12.0': + resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} + + '@emotion/cache@11.13.1': + resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.0': + resolution: {integrity: sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.13.3': + resolution: {integrity: sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.1': + resolution: {integrity: sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.13.0': + resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.0': + resolution: {integrity: sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + + '@esbuild/aix-ppc64@0.23.1': + resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.23.1': + resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.23.1': + resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.23.1': + resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.23.1': + resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.23.1': + resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.23.1': + resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.23.1': + resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.23.1': + resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.23.1': + resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.23.1': + resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.23.1': + resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.23.1': + resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.23.1': + resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.23.1': + resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.23.1': + resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.23.1': + resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.23.1': + resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.23.1': + resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.23.1': + resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.23.1': + resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.23.1': + resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.23.1': + resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.23.1': + resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.11.0': + resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.0': + resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@formatjs/intl-unified-numberformat@3.3.7': + resolution: {integrity: sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==} + deprecated: We have renamed the package to @formatjs/intl-numberformat + + '@formatjs/intl-utils@2.3.0': + resolution: {integrity: sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==} + deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package + + '@humanwhocodes/config-array@0.11.14': + resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@icons/material@0.2.4': + resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} + peerDependencies: + react: '*' + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@kurkle/color@0.3.2': + resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} + + '@manypkg/find-root@1.1.0': + resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + + '@manypkg/get-packages@1.1.3': + resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + + '@mui/core-downloads-tracker@6.1.0': + resolution: {integrity: sha512-covEnIn/2er5YdtuukDRA52kmARhKrHjOvPsyTFMQApZdrTBI4h8jbEy2mxZqwMwcAFS9coonQXnEZKL1rUNdQ==} + + '@mui/icons-material@6.0.2': + resolution: {integrity: sha512-WaTPSvKcx8X7NdWAHzJWDZv+YXvK0MUY8+JI/r4/q2GgIa5RW+n4+08CGX6jB7sWhU1R3zy28NfsDUwwQjOThw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.0.2 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@6.1.0': + resolution: {integrity: sha512-4MJ46vmy1xbm8x+ZdRcWm8jEMMowdS8pYlhKQzg/qoKhOcLhImZvf2Jn6z9Dj6gl+lY+C/0MxaHF/avAAGys3Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.1.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@6.1.0': + resolution: {integrity: sha512-+L5qccs4gwsR0r1dgjqhN24QEQRkqIbfOdxILyMbMkuI50x6wNyt9XrV+J3WtjtZTMGJCrUa5VmZBE6OEPGPWA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@6.1.0': + resolution: {integrity: sha512-MZ+vtaCkjamrT41+b0Er9OMenjAtP/32+L6fARL9/+BZKuV2QbR3q3TmavT2x0NhDu35IM03s4yKqj32Ziqnyg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@6.1.0': + resolution: {integrity: sha512-NumkGDqT6EdXfcoFLYQ+M4XlTW5hH3+aK48xAbRqKPXJfxl36CBt4DLduw/Voa5dcayGus9T6jm1AwU2hoJ5hQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.16': + resolution: {integrity: sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.16.6': + resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@6.1.0': + resolution: {integrity: sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/x-data-grid@7.18.0': + resolution: {integrity: sha512-41UjJbRxWk+Yk/lfvaO55Pwo5p+F5s3rOTiHLl53ikCT5GuJ5OCCvik0Bi3c6DzTuUBdrEucae2618rydc2DGw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.9.0 + '@emotion/styled': ^11.8.1 + '@mui/material': ^5.15.14 || ^6.0.0 + '@mui/system': ^5.15.14 || ^6.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/x-date-pickers@7.18.0': + resolution: {integrity: sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.9.0 + '@emotion/styled': ^11.8.1 + '@mui/material': ^5.15.14 || ^6.0.0 + '@mui/system': ^5.15.14 || ^6.0.0 + date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0 + date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 + dayjs: ^1.10.7 + luxon: ^3.0.2 + moment: ^2.29.4 + moment-hijri: ^2.1.2 + moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + date-fns: + optional: true + date-fns-jalali: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + moment-hijri: + optional: true + moment-jalaali: + optional: true + + '@mui/x-internals@7.18.0': + resolution: {integrity: sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: ^17.0.0 || ^18.0.0 + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.1.1': + resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + + '@react-dnd/asap@5.0.2': + resolution: {integrity: sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==} + + '@react-dnd/invariant@4.0.2': + resolution: {integrity: sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==} + + '@react-dnd/shallowequal@4.0.2': + resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==} + + '@remix-run/router@1.19.1': + resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==} + engines: {node: '>=14.0.0'} + + '@rollup/plugin-babel@6.0.4': + resolution: {integrity: sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@types/babel__core': ^7.1.9 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + '@types/babel__core': + optional: true + rollup: + optional: true + + '@rollup/plugin-commonjs@28.0.1': + resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} + engines: {node: '>=16.0.0 || 14 >= 14.17'} + peerDependencies: + rollup: ^2.68.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-dynamic-import-vars@2.1.2': + resolution: {integrity: sha512-4lr2oXxs9hcxtGGaK8s0i9evfjzDrAs7ngw28TqruWKTEm0+U4Eljb+F6HXGYdFv8xRojQlrQwV7M/yxeh3yzQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-inject@5.0.5': + resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-json@6.1.0': + resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-node-resolve@15.2.3': + resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/plugin-typescript@12.1.1': + resolution: {integrity: sha512-t7O653DpfB5MbFrqPe/VcKFFkvRuFNp9qId3xq4Eth5xlyymzxNpye2z8Hrl0RIMuXTSr5GGcFpkdlMeacUiFQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.14.0||^3.0.0||^4.0.0 + tslib: '*' + typescript: '>=3.7.0' + peerDependenciesMeta: + rollup: + optional: true + tslib: + optional: true + + '@rollup/pluginutils@5.1.2': + resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.24.0': + resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.24.0': + resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.24.0': + resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.24.0': + resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.24.0': + resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.24.0': + resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.24.0': + resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-gnu@4.24.0': + resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-s390x-gnu@4.24.0': + resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.24.0': + resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.24.0': + resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-win32-arm64-msvc@4.24.0': + resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.24.0': + resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.24.0': + resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.10.4': + resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.6.8': + resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.20.6': + resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + + '@types/conventional-commits-parser@5.0.0': + resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + + '@types/fs-extra@9.0.13': + resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + + '@types/hammerjs@2.0.46': + resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} + + '@types/inquirer@8.2.10': + resolution: {integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/less@3.0.6': + resolution: {integrity: sha512-PecSzorDGdabF57OBeQO/xFbAkYWo88g4Xvnsx7LRwqLC17I7OoKtA3bQB9uXkY6UkMWCOsA8HSVpaoitscdXw==} + + '@types/lodash-es@4.17.12': + resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + + '@types/lodash@4.17.7': + resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} + + '@types/minimist@1.2.5': + resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + + '@types/node@10.17.60': + resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@20.16.5': + resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==} + + '@types/normalize-package-data@2.4.4': + resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + '@types/qs@6.9.15': + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + + '@types/react-color@3.0.12': + resolution: {integrity: sha512-pr3uKE3lSvf7GFo1Rn2K3QktiZQFFrSgSGJ/3iMvSOYWt2pPAJ97rVdVfhWxYJZ8prAEXzoP2XX//3qGSQgu7Q==} + + '@types/react-dom@18.3.0': + resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + + '@types/react-grid-layout@1.3.5': + resolution: {integrity: sha512-WH/po1gcEcoR6y857yAnPGug+ZhkF4PaTUxgAbwfeSH/QOgVSakKHBXoPGad/sEznmkiaK3pqHk+etdWisoeBQ==} + + '@types/react-resizable@3.0.8': + resolution: {integrity: sha512-Pcvt2eGA7KNXldt1hkhVhAgZ8hK41m0mp89mFgQi7LAAEZiaLgm4fHJ5zbJZ/4m2LVaAyYrrRRv1LHDcrGQanA==} + + '@types/react-transition-group@4.4.11': + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + + '@types/react@18.3.5': + resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} + + '@types/reactcss@1.2.12': + resolution: {integrity: sha512-BrXUQ86/wbbFiZv8h/Q1/Q1XOsaHneYmCb/tHe9+M8XBAAUc2EHfdY0DY22ZZjVSaXr5ix7j+zsqO2eGZub8lQ==} + + '@types/resolve@1.20.2': + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + + '@types/semver@7.5.8': + resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} + + '@types/through@0.0.33': + resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + + '@types/validator@13.12.2': + resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} + + '@typescript-eslint/eslint-plugin@8.5.0': + resolution: {integrity: sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@8.5.0': + resolution: {integrity: sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@8.5.0': + resolution: {integrity: sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/type-utils@8.5.0': + resolution: {integrity: sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@8.5.0': + resolution: {integrity: sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.5.0': + resolution: {integrity: sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@8.5.0': + resolution: {integrity: sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + + '@typescript-eslint/visitor-keys@8.5.0': + resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@vitejs/plugin-react@4.3.1': + resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 + + JSONStream@1.3.5: + resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} + hasBin: true + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ahooks@3.8.1: + resolution: {integrity: sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA==} + engines: {node: '>=8.0.0'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.1.3: + resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + + array-buffer-byte-length@1.0.1: + resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} + engines: {node: '>= 0.4'} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.5: + resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.3: + resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} + engines: {node: '>= 0.4'} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + asn1.js@4.10.1: + resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + + assert@2.1.0: + resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + astring@1.9.0: + resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} + hasBin: true + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.10.0: + resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} + engines: {node: '>=4'} + + axios@1.7.7: + resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + better-path-resolve@1.0.0: + resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} + engines: {node: '>=4'} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browser-resolve@2.0.0: + resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + + browserify-sign@4.2.3: + resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} + engines: {node: '>= 0.12'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-keys@7.0.2: + resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} + engines: {node: '>=12'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001659: + resolution: {integrity: sha512-Qxxyfv3RdHAfJcXelgf0hU4DFUVXBGTjqrBUZLUh8AtlGnsDo+CnncYtTd95+ZKfnANUOzxyIQCuU/UeBZBYoA==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chart.js@4.4.4: + resolution: {integrity: sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==} + engines: {pnpm: '>=8'} + + chartjs-adapter-date-fns@3.0.0: + resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} + peerDependencies: + chart.js: '>=2.8.0' + date-fns: '>=2.0.0' + + chartjs-plugin-zoom@2.1.0: + resolution: {integrity: sha512-7lMimfQCUaIJLhPJaWSAA4gw+1m8lyR3Wn+M3MxjHbM/XxRUnOxN7cM5RR9jUmxmyW0h7L2hZ8KhvUsqrFxy/Q==} + peerDependencies: + chart.js: '>=3.2.0' + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + + classnames@2.5.1: + resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clsx@1.2.1: + resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@12.1.0: + resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} + engines: {node: '>=18'} + + commander@8.3.0: + resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} + engines: {node: '>= 12'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + confusing-browser-globals@1.0.11: + resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + conventional-changelog-angular@7.0.0: + resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} + engines: {node: '>=16'} + + conventional-changelog-conventionalcommits@7.0.2: + resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} + engines: {node: '>=16'} + + conventional-commits-parser@5.0.0: + resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} + engines: {node: '>=16'} + hasBin: true + + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.3.1: + resolution: {integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==} + engines: {node: '>= 0.6'} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig-typescript-loader@5.0.0: + resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} + engines: {node: '>=v16'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=8.2' + typescript: '>=4' + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@5.1.0: + resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + + cross-spawn@6.0.5: + resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} + engines: {node: '>=4.8'} + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + crypto-browserify@3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + + css-functions-list@3.2.2: + resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} + engines: {node: '>=12 || >=16'} + + css-property-sort-order-smacss@2.2.0: + resolution: {integrity: sha512-nXutswsivIEBOrPo/OZw2KQjFPLvtg68aovJf6Kqrm3L6FmTvvFPaeDrk83hh0+pRJGuP3PeKJwMS0E6DFipdQ==} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-vars-ponyfill@2.4.9: + resolution: {integrity: sha512-aZyLue5bdiGVNCiCclNjo123D8I7kyoYNUaAvz+H1JalX1ye4Ilz7jNRRH5YbM+dYD6ucejiydGwk7lol/GCXQ==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + data-view-buffer@1.0.1: + resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.1: + resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.0: + resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} + engines: {node: '>= 0.4'} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decamelize@5.0.1: + resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} + engines: {node: '>=10'} + + deep-equal@2.2.3: + resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} + engines: {node: '>= 0.4'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dnd-core@16.0.1: + resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domain-browser@4.23.0: + resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} + engines: {node: '>=10'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dotenv@16.4.5: + resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} + engines: {node: '>=12'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.18: + resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==} + + elliptic@6.5.7: + resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.17.1: + resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} + engines: {node: '>=10.13.0'} + + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.23.3: + resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-get-iterator@1.1.3: + resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + + es-iterator-helpers@1.0.19: + resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.5.4: + resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} + + es-object-atoms@1.0.0: + resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.0.3: + resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.2: + resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.23.1: + resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-airbnb-base@15.0.0: + resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} + engines: {node: ^10.12.0 || >=12.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.2 + + eslint-config-airbnb@19.0.4: + resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==} + engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.32.0 || ^8.2.0 + eslint-plugin-import: ^2.25.3 + eslint-plugin-jsx-a11y: ^6.5.1 + eslint-plugin-react: ^7.28.0 + eslint-plugin-react-hooks: ^4.3.0 + + eslint-config-prettier@9.1.0: + resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-alias@1.1.2: + resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==} + engines: {node: '>= 4'} + peerDependencies: + eslint-plugin-import: '>=1.4.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.6.3: + resolution: {integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.11.0: + resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-check-file@2.8.0: + resolution: {integrity: sha512-FvvafMTam2WJYH9uj+FuMxQ1y+7jY3Z6P9T4j2214cH0FBxNzTcmeCiGTj1Lxp3mI6kbbgsXvmgewvf+llKYyw==} + engines: {node: '>=18'} + peerDependencies: + eslint: '>=7.28.0' + + eslint-plugin-import@2.30.0: + resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.0: + resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-prettier@5.2.1: + resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '*' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react@7.35.2: + resolution: {integrity: sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.0: + resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + extendable-error@0.1.7: + resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-equals@4.0.3: + resolution: {integrity: sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-uri@3.0.1: + resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-entry-cache@7.0.2: + resolution: {integrity: sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==} + engines: {node: '>=12.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + find-up@7.0.0: + resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} + engines: {node: '>=18'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.1: + resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data@4.0.0: + resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} + engines: {node: '>= 6'} + + fs-extra@10.1.0: + resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} + engines: {node: '>=12'} + + fs-extra@7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-css-data@2.1.1: + resolution: {integrity: sha512-JpMa/f5P4mDXKg6l5/2cHL5xNY77Jap7tHyduMa6BF0E2a7bQ6Tvaz2BIMjeVYZYLcmOZ5w2Ro0yVJEI41tMbw==} + + get-east-asian-width@1.2.0: + resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} + engines: {node: '>=18'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.2: + resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.8.0: + resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@11.0.0: + resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} + engines: {node: 20 || >=22} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + hammerjs@2.0.8: + resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} + engines: {node: '>=0.8.0'} + + hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hash-base@3.0.4: + resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} + engines: {node: '>=4'} + + hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + human-id@1.0.2: + resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + husky@9.1.5: + resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==} + engines: {node: '>=18'} + hasBin: true + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immer@10.1.1: + resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + import-meta-resolve@4.1.0: + resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@8.2.6: + resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} + engines: {node: '>=12.0.0'} + + internal-slot@1.0.7: + resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} + engines: {node: '>= 0.4'} + + intersection-observer@0.12.2: + resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} + + intl-format-cache@4.3.1: + resolution: {integrity: sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==} + + intl-messageformat-parser@3.6.4: + resolution: {integrity: sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==} + deprecated: We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser + + intl-messageformat@7.8.4: + resolution: {integrity: sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.4: + resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.0.0: + resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} + engines: {node: '>= 0.4'} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + + is-bun-module@1.2.1: + resolution: {integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.1: + resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} + engines: {node: '>= 0.4'} + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.0.2: + resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + + is-nan@1.3.2: + resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.3: + resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} + engines: {node: '>= 0.4'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-subdir@1.2.0: + resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} + engines: {node: '>=4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-text-path@2.0.0: + resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} + engines: {node: '>=8'} + + is-typed-array@1.1.13: + resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-weakset@2.0.3: + resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} + engines: {node: '>= 0.4'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isomorphic-timers-promises@1.0.1: + resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} + engines: {node: '>=10'} + + iterator.prototype@1.1.2: + resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + + jackspeak@4.0.1: + resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} + engines: {node: 20 || >=22} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.0: + resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonparse@1.3.1: + resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} + engines: {'0': node >= 0.2.0} + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + known-css-properties@0.29.0: + resolution: {integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + less@4.2.0: + resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} + engines: {node: '>=6'} + hasBin: true + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@15.2.10: + resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.2.4: + resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} + engines: {node: '>=18.0.0'} + + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + locate-path@7.2.0: + resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lodash-es@4.17.21: + resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.snakecase@4.1.1: + resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} + + lodash.startcase@4.4.0: + resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash.upperfirst@4.3.1: + resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@11.0.1: + resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} + engines: {node: 20 || >=22} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + + material-colors@1.2.6: + resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + + meow@10.1.5: + resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@10.0.1: + resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} + engines: {node: 20 || >=22} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + node-stdlib-browser@1.2.0: + resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} + engines: {node: '>=10'} + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-all@4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.2: + resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} + engines: {node: '>= 0.4'} + + object-is@1.1.6: + resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} + engines: {node: '>= 0.4'} + + object.entries@1.1.8: + resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.0: + resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + outdent@0.5.0: + resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} + + p-filter@2.1.0: + resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} + engines: {node: '>=8'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-locate@6.0.0: + resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + p-map@2.1.0: + resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} + engines: {node: '>=6'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-manager-detector@0.2.0: + resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.7: + resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} + engines: {node: '>= 0.10'} + + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-exists@5.0.0: + resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@2.0.0: + resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + engines: {node: 20 || >=22} + + path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + + picocolors@1.1.0: + resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pkg-dir@5.0.0: + resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} + engines: {node: '>=10'} + + possible-typed-array-names@1.0.0: + resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + engines: {node: '>= 0.4'} + + postcss-html@1.7.0: + resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==} + engines: {node: ^12 || >=14} + + postcss-less@6.0.0: + resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==} + engines: {node: '>=12'} + peerDependencies: + postcss: ^8.3.5 + + postcss-resolve-nested-selector@0.1.6: + resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-scss@4.0.9: + resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.4.29 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-sorting@8.0.2: + resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} + peerDependencies: + postcss: ^8.4.20 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.45: + resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.3.3: + resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} + engines: {node: '>=14'} + hasBin: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + progress@2.0.3: + resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} + engines: {node: '>=0.4.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + rd@2.0.1: + resolution: {integrity: sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw==} + + react-color@2.19.3: + resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} + peerDependencies: + react: '*' + + react-dnd-html5-backend@16.0.1: + resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==} + + react-dnd@16.0.1: + resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==} + peerDependencies: + '@types/hoist-non-react-statics': '>= 3.3.1' + '@types/node': '>= 12' + '@types/react': '>= 16' + react: '>= 16.14' + peerDependenciesMeta: + '@types/hoist-non-react-statics': + optional: true + '@types/node': + optional: true + '@types/react': + optional: true + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-draggable@4.4.6: + resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-fast-compare@3.2.2: + resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + + react-grid-layout@1.5.0: + resolution: {integrity: sha512-WBKX7w/LsTfI99WskSu6nX2nbJAUD7GD6nIXcwYLyPpnslojtmql2oD3I2g5C3AK8hrxIarYT8awhuDIp7iQ5w==} + peerDependencies: + react: '>= 16.3.0' + react-dom: '>= 16.3.0' + + react-hook-form@7.53.0: + resolution: {integrity: sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + + react-intl-universal@2.11.1: + resolution: {integrity: sha512-wkmbd7Qohnr8ch02ZzF6uLZAOvdsHbvMjhiS13WUeberdrraOma4bOnn2hUVnEa/LXEvYRD0ta0RBPOV0g2HoA==} + peerDependencies: + react: '*' + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-refresh@0.14.2: + resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} + engines: {node: '>=0.10.0'} + + react-resizable@3.0.5: + resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} + peerDependencies: + react: '>= 16.3' + + react-router-dom@6.26.1: + resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.26.1: + resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + reactcss@1.2.3: + resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} + peerDependencies: + react: '*' + + read-pkg-up@8.0.0: + resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} + engines: {node: '>=12'} + + read-pkg@3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + + read-pkg@6.0.0: + resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} + engines: {node: '>=12'} + + read-yaml-file@1.1.0: + resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} + engines: {node: '>=6'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + redent@4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + + redux@4.2.1: + resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + + reflect.getprototypeof@1.0.6: + resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} + engines: {node: '>= 0.4'} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + resize-observer-polyfill@1.5.1: + resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@6.0.1: + resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} + engines: {node: 20 || >=22} + hasBin: true + + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + + rollup-plugin-peer-deps-external@2.2.4: + resolution: {integrity: sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==} + peerDependencies: + rollup: '*' + + rollup@4.24.0: + resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-array-concat@1.1.2: + resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.3: + resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} + engines: {node: '>= 0.4'} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + screenfull@5.2.0: + resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} + engines: {node: '>=0.10.0'} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + spawndamnit@2.0.0: + resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.20: + resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + stop-iteration-iterator@1.0.0: + resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} + engines: {node: '>= 0.4'} + + stream-browserify@3.0.0: + resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + + stream-http@3.2.0: + resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.includes@2.0.0: + resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} + + string.prototype.matchall@4.0.11: + resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} + engines: {node: '>= 0.4'} + + string.prototype.padend@3.1.6: + resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.9: + resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.8: + resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-indent@4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + stylelint-config-prettier@9.0.5: + resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} + engines: {node: '>= 12'} + hasBin: true + peerDependencies: + stylelint: '>= 11.x < 15' + + stylelint-config-property-sort-order-smacss@10.0.0: + resolution: {integrity: sha512-NuiTgyqD8UdYY1IpTBIodBbrWKwaib5r8sq5kGHQ52UrmT8O7Fa8ZWYGipSZw6k9tGoljl9Hng2jtH+wBTMa1Q==} + engines: {node: '>=18.12.0'} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.0 + + stylelint-config-recommended@13.0.0: + resolution: {integrity: sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + stylelint: ^15.10.0 + + stylelint-config-standard@34.0.0: + resolution: {integrity: sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + stylelint: ^15.10.0 + + stylelint-order@6.0.4: + resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} + peerDependencies: + stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + + stylelint@15.11.0: + resolution: {integrity: sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.1.0: + resolution: {integrity: sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + synckit@0.9.1: + resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@6.8.2: + resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} + engines: {node: '>=10.0.0'} + + tapable@2.2.1: + resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} + engines: {node: '>=6'} + + term-size@2.2.1: + resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} + engines: {node: '>=8'} + + text-extensions@2.4.0: + resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} + engines: {node: '>=8'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tinycolor2@1.6.0: + resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} + + tinyexec@0.3.0: + resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + trim-newlines@4.1.1: + resolution: {integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==} + engines: {node: '>=12'} + + ts-api-utils@1.3.0: + resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tsx@4.19.0: + resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} + engines: {node: '>=18.0.0'} + hasBin: true + + tty-browserify@0.0.1: + resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + typed-array-buffer@1.0.2: + resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.1: + resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.2: + resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.6: + resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} + engines: {node: '>= 0.4'} + + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + undici-types@6.19.8: + resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + url@0.11.4: + resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} + engines: {node: '>= 0.4'} + + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + + vite-plugin-html-config@2.0.2: + resolution: {integrity: sha512-g09u0XsmgKyMUIp1RZSyNSkJWvIusaXxw3KylyxU3vkCq7/G8hyemLctT+4IvO42fCPlNySmrNC9g0qSoKmvpw==} + engines: {node: '>=12.0.0'} + peerDependencies: + vite: '>=5.0.0' + + vite-plugin-imp@2.4.0: + resolution: {integrity: sha512-L/6/nvOw+MyNh4UxAlCZHsmKd5MitmHamqqAWB15sbUgVIEz/OQ8jpKr6kkQU0eA/AIe8fkCVbQBlP81ajrqWg==} + peerDependencies: + vite: '>= 2.0.0-beta.5' + + vite-plugin-node-polyfills@0.22.0: + resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + vite-plugin-progress@0.0.7: + resolution: {integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ==} + engines: {node: '>=14', pnpm: '>=7.0.0'} + peerDependencies: + vite: '>2.0.0-0' + + vite-plugin-stylelint@5.3.1: + resolution: {integrity: sha512-M/hSdfOwnOVghbJDeuuYIU2xO/MMukYR8QcEyNKFPG8ro1L+DlTdViix2B2d/FvAw14WPX88ckA5A7NvUjJz8w==} + engines: {node: '>=14.18'} + peerDependencies: + '@types/stylelint': ^13.0.0 + postcss: ^7.0.0 || ^8.0.0 + rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 + stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@types/stylelint': + optional: true + postcss: + optional: true + rollup: + optional: true + + vite@5.4.9: + resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || >=20.0.0 + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-builtin-type@1.1.4: + resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.15: + resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.5.1: + resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yocto-queue@1.1.1: + resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} + engines: {node: '>=12.20'} + + zustand@4.5.5: + resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + +snapshots: + + '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - dev: true - /@babel/code-frame@7.24.7: - resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} - engines: {node: '>=6.9.0'} + '@babel/code-frame@7.24.7': dependencies: '@babel/highlight': 7.24.7 picocolors: 1.1.0 - /@babel/compat-data@7.25.4: - resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} - engines: {node: '>=6.9.0'} - dev: true + '@babel/compat-data@7.25.4': {} - /@babel/core@7.25.2: - resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} - engines: {node: '>=6.9.0'} + '@babel/core@7.25.2': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.24.7 @@ -554,42 +4627,30 @@ packages: semver: 6.3.1 transitivePeerDependencies: - supports-color - dev: true - /@babel/generator@7.25.6: - resolution: {integrity: sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==} - engines: {node: '>=6.9.0'} + '@babel/generator@7.25.6': dependencies: '@babel/types': 7.25.6 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 - /@babel/helper-compilation-targets@7.25.2: - resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} - engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@7.25.2': dependencies: '@babel/compat-data': 7.25.4 '@babel/helper-validator-option': 7.24.8 browserslist: 4.23.3 lru-cache: 5.1.1 semver: 6.3.1 - dev: true - /@babel/helper-module-imports@7.24.7: - resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} - engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@7.24.7': dependencies: '@babel/traverse': 7.25.6 '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color - /@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2): - resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-module-imports': 7.24.7 @@ -598,97 +4659,59 @@ packages: '@babel/traverse': 7.25.6 transitivePeerDependencies: - supports-color - dev: true - /@babel/helper-plugin-utils@7.24.8: - resolution: {integrity: sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==} - engines: {node: '>=6.9.0'} - dev: true + '@babel/helper-plugin-utils@7.24.8': {} - /@babel/helper-simple-access@7.24.7: - resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} - engines: {node: '>=6.9.0'} + '@babel/helper-simple-access@7.24.7': dependencies: '@babel/traverse': 7.25.6 '@babel/types': 7.25.6 transitivePeerDependencies: - supports-color - dev: true - /@babel/helper-string-parser@7.24.8: - resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} - engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@7.24.8': {} - /@babel/helper-validator-identifier@7.24.7: - resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} - engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@7.24.7': {} - /@babel/helper-validator-option@7.24.8: - resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} - engines: {node: '>=6.9.0'} - dev: true + '@babel/helper-validator-option@7.24.8': {} - /@babel/helpers@7.25.6: - resolution: {integrity: sha512-Xg0tn4HcfTijTwfDwYlvVCl43V6h4KyVVX2aEm4qdO/PC6L2YvzLHFdmxhoeSA3eslcE6+ZVXHgWwopXYLNq4Q==} - engines: {node: '>=6.9.0'} + '@babel/helpers@7.25.6': dependencies: '@babel/template': 7.25.0 '@babel/types': 7.25.6 - dev: true - /@babel/highlight@7.24.7: - resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} - engines: {node: '>=6.9.0'} + '@babel/highlight@7.24.7': dependencies: '@babel/helper-validator-identifier': 7.24.7 chalk: 2.4.2 js-tokens: 4.0.0 picocolors: 1.1.0 - /@babel/parser@7.25.6: - resolution: {integrity: sha512-trGdfBdbD0l1ZPmcJ83eNxB9rbEax4ALFTF7fN386TMYbeCQbyme5cOEXQhbGXKebwGaB/J52w1mrklMcbgy6Q==} - engines: {node: '>=6.0.0'} - hasBin: true + '@babel/parser@7.25.6': dependencies: '@babel/types': 7.25.6 - /@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2): - resolution: {integrity: sha512-fOPQYbGSgH0HUp4UJO4sMBFjY6DuWq+2i8rixyUMb3CdGixs/gccURvYOAhajBdKDoGajFr3mUq5rH3phtkGzw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-self@7.24.7(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - dev: true - /@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2): - resolution: {integrity: sha512-J2z+MWzZHVOemyLweMqngXrgGC42jQ//R0KdxqkIz/OrbVIIlhFI3WigZ5fO+nwFvBlncr4MGapd8vTyc7RPNQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-react-jsx-source@7.24.7(@babel/core@7.25.2)': dependencies: '@babel/core': 7.25.2 '@babel/helper-plugin-utils': 7.24.8 - dev: true - /@babel/runtime@7.25.6: - resolution: {integrity: sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==} - engines: {node: '>=6.9.0'} + '@babel/runtime@7.25.6': dependencies: regenerator-runtime: 0.14.1 - /@babel/template@7.25.0: - resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} - engines: {node: '>=6.9.0'} + '@babel/template@7.25.0': dependencies: '@babel/code-frame': 7.24.7 '@babel/parser': 7.25.6 '@babel/types': 7.25.6 - /@babel/traverse@7.25.6: - resolution: {integrity: sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==} - engines: {node: '>=6.9.0'} + '@babel/traverse@7.25.6': dependencies: '@babel/code-frame': 7.24.7 '@babel/generator': 7.25.6 @@ -700,16 +4723,13 @@ packages: transitivePeerDependencies: - supports-color - /@babel/types@7.25.6: - resolution: {integrity: sha512-/l42B1qxpG6RdfYf343Uw1vmDjeNhneUXtzhojE7pDgfpEypmRhI6j1kr17XCVv4Cgl9HdAiQY2x0GwKm7rWCw==} - engines: {node: '>=6.9.0'} + '@babel/types@7.25.6': dependencies: '@babel/helper-string-parser': 7.24.8 '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 - /@changesets/apply-release-plan@7.0.5: - resolution: {integrity: sha512-1cWCk+ZshEkSVEZrm2fSj1Gz8sYvxgUL4Q78+1ZZqeqfuevPTPk033/yUZ3df8BKMohkqqHfzj0HOOrG0KtXTw==} + '@changesets/apply-release-plan@7.0.5': dependencies: '@changesets/config': 3.0.3 '@changesets/get-version-range-type': 0.4.0 @@ -724,10 +4744,8 @@ packages: prettier: 2.8.8 resolve-from: 5.0.0 semver: 7.6.3 - dev: true - /@changesets/assemble-release-plan@6.0.4: - resolution: {integrity: sha512-nqICnvmrwWj4w2x0fOhVj2QEGdlUuwVAwESrUo5HLzWMI1rE5SWfsr9ln+rDqWB6RQ2ZyaMZHUcU7/IRaUJS+Q==} + '@changesets/assemble-release-plan@6.0.4': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.2 @@ -735,17 +4753,12 @@ packages: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 semver: 7.6.3 - dev: true - /@changesets/changelog-git@0.2.0: - resolution: {integrity: sha512-bHOx97iFI4OClIT35Lok3sJAwM31VbUM++gnMBV16fdbtBhgYu4dxsphBF/0AZZsyAHMrnM0yFcj5gZM1py6uQ==} + '@changesets/changelog-git@0.2.0': dependencies: '@changesets/types': 6.0.0 - dev: true - /@changesets/cli@2.27.8: - resolution: {integrity: sha512-gZNyh+LdSsI82wBSHLQ3QN5J30P4uHKJ4fXgoGwQxfXwYFTJzDdvIJasZn8rYQtmKhyQuiBj4SSnLuKlxKWq4w==} - hasBin: true + '@changesets/cli@2.27.8': dependencies: '@changesets/apply-release-plan': 7.0.5 '@changesets/assemble-release-plan': 6.0.4 @@ -777,10 +4790,8 @@ packages: semver: 7.6.3 spawndamnit: 2.0.0 term-size: 2.2.1 - dev: true - /@changesets/config@3.0.3: - resolution: {integrity: sha512-vqgQZMyIcuIpw9nqFIpTSNyc/wgm/Lu1zKN5vECy74u95Qx/Wa9g27HdgO4NkVAaq+BGA8wUc/qvbvVNs93n6A==} + '@changesets/config@3.0.3': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.2 @@ -789,25 +4800,19 @@ packages: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 micromatch: 4.0.8 - dev: true - /@changesets/errors@0.2.0: - resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} + '@changesets/errors@0.2.0': dependencies: extendable-error: 0.1.7 - dev: true - /@changesets/get-dependents-graph@2.1.2: - resolution: {integrity: sha512-sgcHRkiBY9i4zWYBwlVyAjEM9sAzs4wYVwJUdnbDLnVG3QwAaia1Mk5P8M7kraTOZN+vBET7n8KyB0YXCbFRLQ==} + '@changesets/get-dependents-graph@2.1.2': dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 picocolors: 1.1.0 semver: 7.6.3 - dev: true - /@changesets/get-release-plan@4.0.4: - resolution: {integrity: sha512-SicG/S67JmPTrdcc9Vpu0wSQt7IiuN0dc8iR5VScnnTVPfIaLvKmEGRvIaF0kcn8u5ZqLbormZNTO77bCEvyWw==} + '@changesets/get-release-plan@4.0.4': dependencies: '@changesets/assemble-release-plan': 6.0.4 '@changesets/config': 3.0.3 @@ -815,46 +4820,34 @@ packages: '@changesets/read': 0.6.1 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/get-version-range-type@0.4.0: - resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} - dev: true + '@changesets/get-version-range-type@0.4.0': {} - /@changesets/git@3.0.1: - resolution: {integrity: sha512-pdgHcYBLCPcLd82aRcuO0kxCDbw/yISlOtkmwmE8Odo1L6hSiZrBOsRl84eYG7DRCab/iHnOkWqExqc4wxk2LQ==} + '@changesets/git@3.0.1': dependencies: '@changesets/errors': 0.2.0 '@manypkg/get-packages': 1.1.3 is-subdir: 1.2.0 micromatch: 4.0.8 spawndamnit: 2.0.0 - dev: true - /@changesets/logger@0.1.1: - resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} + '@changesets/logger@0.1.1': dependencies: picocolors: 1.1.0 - dev: true - /@changesets/parse@0.4.0: - resolution: {integrity: sha512-TS/9KG2CdGXS27S+QxbZXgr8uPsP4yNJYb4BC2/NeFUj80Rni3TeD2qwWmabymxmrLo7JEsytXH1FbpKTbvivw==} + '@changesets/parse@0.4.0': dependencies: '@changesets/types': 6.0.0 js-yaml: 3.14.1 - dev: true - /@changesets/pre@2.0.1: - resolution: {integrity: sha512-vvBJ/If4jKM4tPz9JdY2kGOgWmCowUYOi5Ycv8dyLnEE8FgpYYUo1mgJZxcdtGGP3aG8rAQulGLyyXGSLkIMTQ==} + '@changesets/pre@2.0.1': dependencies: '@changesets/errors': 0.2.0 '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - dev: true - /@changesets/read@0.6.1: - resolution: {integrity: sha512-jYMbyXQk3nwP25nRzQQGa1nKLY0KfoOV7VLgwucI0bUO8t8ZLCr6LZmgjXsiKuRDc+5A6doKPr9w2d+FEJ55zQ==} + '@changesets/read@0.6.1': dependencies: '@changesets/git': 3.0.1 '@changesets/logger': 0.1.1 @@ -863,36 +4856,24 @@ packages: fs-extra: 7.0.1 p-filter: 2.1.0 picocolors: 1.1.0 - dev: true - /@changesets/should-skip-package@0.1.1: - resolution: {integrity: sha512-H9LjLbF6mMHLtJIc/eHR9Na+MifJ3VxtgP/Y+XLn4BF7tDTEN1HNYtH6QMcjP1uxp9sjaFYmW8xqloaCi/ckTg==} + '@changesets/should-skip-package@0.1.1': dependencies: '@changesets/types': 6.0.0 '@manypkg/get-packages': 1.1.3 - dev: true - /@changesets/types@4.1.0: - resolution: {integrity: sha512-LDQvVDv5Kb50ny2s25Fhm3d9QSZimsoUGBsUioj6MC3qbMUCuC8GPIvk/M6IvXx3lYhAs0lwWUQLb+VIEUCECw==} - dev: true + '@changesets/types@4.1.0': {} - /@changesets/types@6.0.0: - resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - dev: true + '@changesets/types@6.0.0': {} - /@changesets/write@0.3.2: - resolution: {integrity: sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw==} + '@changesets/write@0.3.2': dependencies: '@changesets/types': 6.0.0 fs-extra: 7.0.1 human-id: 1.0.2 prettier: 2.8.8 - dev: true - /@commitlint/cli@19.5.0(@types/node@20.16.5)(typescript@5.6.2): - resolution: {integrity: sha512-gaGqSliGwB86MDmAAKAtV9SV1SHdmN8pnGq4EJU4+hLisQ7IFfx4jvU4s+pk6tl0+9bv6yT+CaZkufOinkSJIQ==} - engines: {node: '>=v18'} - hasBin: true + '@commitlint/cli@19.5.0(@types/node@20.16.5)(typescript@5.6.2)': dependencies: '@commitlint/format': 19.5.0 '@commitlint/lint': 19.5.0 @@ -904,26 +4885,18 @@ packages: transitivePeerDependencies: - '@types/node' - typescript - dev: true - /@commitlint/config-conventional@19.4.1: - resolution: {integrity: sha512-D5S5T7ilI5roybWGc8X35OBlRXLAwuTseH1ro0XgqkOWrhZU8yOwBOslrNmSDlTXhXLq8cnfhQyC42qaUCzlXA==} - engines: {node: '>=v18'} + '@commitlint/config-conventional@19.4.1': dependencies: '@commitlint/types': 19.5.0 conventional-changelog-conventionalcommits: 7.0.2 - /@commitlint/config-validator@19.5.0: - resolution: {integrity: sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==} - engines: {node: '>=v18'} + '@commitlint/config-validator@19.5.0': dependencies: '@commitlint/types': 19.5.0 ajv: 8.17.1 - dev: true - /@commitlint/ensure@19.5.0: - resolution: {integrity: sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==} - engines: {node: '>=v18'} + '@commitlint/ensure@19.5.0': dependencies: '@commitlint/types': 19.5.0 lodash.camelcase: 4.3.0 @@ -931,42 +4904,27 @@ packages: lodash.snakecase: 4.1.1 lodash.startcase: 4.4.0 lodash.upperfirst: 4.3.1 - dev: true - /@commitlint/execute-rule@19.5.0: - resolution: {integrity: sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==} - engines: {node: '>=v18'} - dev: true + '@commitlint/execute-rule@19.5.0': {} - /@commitlint/format@19.5.0: - resolution: {integrity: sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==} - engines: {node: '>=v18'} + '@commitlint/format@19.5.0': dependencies: '@commitlint/types': 19.5.0 chalk: 5.3.0 - dev: true - /@commitlint/is-ignored@19.5.0: - resolution: {integrity: sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w==} - engines: {node: '>=v18'} + '@commitlint/is-ignored@19.5.0': dependencies: '@commitlint/types': 19.5.0 semver: 7.6.3 - dev: true - /@commitlint/lint@19.5.0: - resolution: {integrity: sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg==} - engines: {node: '>=v18'} + '@commitlint/lint@19.5.0': dependencies: '@commitlint/is-ignored': 19.5.0 '@commitlint/parse': 19.5.0 '@commitlint/rules': 19.5.0 '@commitlint/types': 19.5.0 - dev: true - /@commitlint/load@19.5.0(@types/node@20.16.5)(typescript@5.6.2): - resolution: {integrity: sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==} - engines: {node: '>=v18'} + '@commitlint/load@19.5.0(@types/node@20.16.5)(typescript@5.6.2)': dependencies: '@commitlint/config-validator': 19.5.0 '@commitlint/execute-rule': 19.5.0 @@ -981,36 +4939,24 @@ packages: transitivePeerDependencies: - '@types/node' - typescript - dev: true - /@commitlint/message@19.5.0: - resolution: {integrity: sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==} - engines: {node: '>=v18'} - dev: true + '@commitlint/message@19.5.0': {} - /@commitlint/parse@19.5.0: - resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} - engines: {node: '>=v18'} + '@commitlint/parse@19.5.0': dependencies: '@commitlint/types': 19.5.0 conventional-changelog-angular: 7.0.0 conventional-commits-parser: 5.0.0 - dev: true - /@commitlint/read@19.5.0: - resolution: {integrity: sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==} - engines: {node: '>=v18'} + '@commitlint/read@19.5.0': dependencies: '@commitlint/top-level': 19.5.0 '@commitlint/types': 19.5.0 git-raw-commits: 4.0.0 minimist: 1.2.8 tinyexec: 0.3.0 - dev: true - /@commitlint/resolve-extends@19.5.0: - resolution: {integrity: sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==} - engines: {node: '>=v18'} + '@commitlint/resolve-extends@19.5.0': dependencies: '@commitlint/config-validator': 19.5.0 '@commitlint/types': 19.5.0 @@ -1018,69 +4964,41 @@ packages: import-meta-resolve: 4.1.0 lodash.mergewith: 4.6.2 resolve-from: 5.0.0 - dev: true - /@commitlint/rules@19.5.0: - resolution: {integrity: sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw==} - engines: {node: '>=v18'} + '@commitlint/rules@19.5.0': dependencies: '@commitlint/ensure': 19.5.0 '@commitlint/message': 19.5.0 '@commitlint/to-lines': 19.5.0 '@commitlint/types': 19.5.0 - dev: true - /@commitlint/to-lines@19.5.0: - resolution: {integrity: sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==} - engines: {node: '>=v18'} - dev: true + '@commitlint/to-lines@19.5.0': {} - /@commitlint/top-level@19.5.0: - resolution: {integrity: sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==} - engines: {node: '>=v18'} + '@commitlint/top-level@19.5.0': dependencies: find-up: 7.0.0 - dev: true - /@commitlint/types@19.5.0: - resolution: {integrity: sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==} - engines: {node: '>=v18'} + '@commitlint/types@19.5.0': dependencies: '@types/conventional-commits-parser': 5.0.0 chalk: 5.3.0 - /@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1): - resolution: {integrity: sha512-2SJS42gxmACHgikc1WGesXLIT8d/q2l0UFM7TaEeIzdFCE/FPMtTiizcPGGJtlPo2xuQzY09OhrLTzRxqJqwGw==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - '@csstools/css-tokenizer': ^2.4.1 + '@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1)': dependencies: '@csstools/css-tokenizer': 2.4.1 - /@csstools/css-tokenizer@2.4.1: - resolution: {integrity: sha512-eQ9DIktFJBhGjioABJRtUucoWR2mwllurfnM8LuNGAqX3ViZXaUchqk+1s7jjtkFiT9ySdACsFEA3etErkALUg==} - engines: {node: ^14 || ^16 || >=18} + '@csstools/css-tokenizer@2.4.1': {} - /@csstools/media-query-list-parser@2.1.13(@csstools/css-parser-algorithms@2.7.1)(@csstools/css-tokenizer@2.4.1): - resolution: {integrity: sha512-XaHr+16KRU9Gf8XLi3q8kDlI18d5vzKSKCY510Vrtc9iNR0NJzbY9hhTmwhzYZj/ZwGL4VmB3TA9hJW0Um2qFA==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - '@csstools/css-parser-algorithms': ^2.7.1 - '@csstools/css-tokenizer': ^2.4.1 + '@csstools/media-query-list-parser@2.1.13(@csstools/css-parser-algorithms@2.7.1)(@csstools/css-tokenizer@2.4.1)': dependencies: '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-tokenizer': 2.4.1 - /@csstools/selector-specificity@3.1.1(postcss-selector-parser@6.1.2): - resolution: {integrity: sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==} - engines: {node: ^14 || ^16 || >=18} - peerDependencies: - postcss-selector-parser: ^6.0.13 + '@csstools/selector-specificity@3.1.1(postcss-selector-parser@6.1.2)': dependencies: postcss-selector-parser: 6.1.2 - /@emotion/babel-plugin@11.12.0: - resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} + '@emotion/babel-plugin@11.12.0': dependencies: '@babel/helper-module-imports': 7.24.7 '@babel/runtime': 7.25.6 @@ -1095,40 +5013,24 @@ packages: stylis: 4.2.0 transitivePeerDependencies: - supports-color - dev: false - /@emotion/cache@11.13.1: - resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + '@emotion/cache@11.13.1': dependencies: '@emotion/memoize': 0.9.0 '@emotion/sheet': 1.4.0 '@emotion/utils': 1.4.0 '@emotion/weak-memoize': 0.4.0 stylis: 4.2.0 - dev: false - /@emotion/hash@0.9.2: - resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} - dev: false + '@emotion/hash@0.9.2': {} - /@emotion/is-prop-valid@1.3.0: - resolution: {integrity: sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==} + '@emotion/is-prop-valid@1.3.0': dependencies: '@emotion/memoize': 0.9.0 - dev: false - /@emotion/memoize@0.9.0: - resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} - dev: false + '@emotion/memoize@0.9.0': {} - /@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==} - peerDependencies: - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true + '@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/babel-plugin': 11.12.0 @@ -1142,31 +5044,18 @@ packages: react: 18.3.1 transitivePeerDependencies: - supports-color - dev: false - /@emotion/serialize@1.3.1: - resolution: {integrity: sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==} + '@emotion/serialize@1.3.1': dependencies: '@emotion/hash': 0.9.2 '@emotion/memoize': 0.9.0 '@emotion/unitless': 0.10.0 '@emotion/utils': 1.4.0 csstype: 3.1.3 - dev: false - /@emotion/sheet@1.4.0: - resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} - dev: false + '@emotion/sheet@1.4.0': {} - /@emotion/styled@11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==} - peerDependencies: - '@emotion/react': ^11.0.0-rc.0 - '@types/react': '*' - react: '>=16.8.0' - peerDependenciesMeta: - '@types/react': - optional: true + '@emotion/styled@11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/babel-plugin': 11.12.0 @@ -1179,236 +5068,97 @@ packages: react: 18.3.1 transitivePeerDependencies: - supports-color - dev: false - /@emotion/unitless@0.10.0: - resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} - dev: false + '@emotion/unitless@0.10.0': {} - /@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1): - resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} - peerDependencies: - react: '>=16.8.0' + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': dependencies: react: 18.3.1 - dev: false - /@emotion/utils@1.4.0: - resolution: {integrity: sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==} - dev: false + '@emotion/utils@1.4.0': {} - /@emotion/weak-memoize@0.4.0: - resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} - dev: false + '@emotion/weak-memoize@0.4.0': {} - /@esbuild/aix-ppc64@0.23.1: - resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - requiresBuild: true + '@esbuild/aix-ppc64@0.23.1': optional: true - /@esbuild/android-arm64@0.23.1: - resolution: {integrity: sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - requiresBuild: true + '@esbuild/android-arm64@0.23.1': optional: true - /@esbuild/android-arm@0.23.1: - resolution: {integrity: sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - requiresBuild: true + '@esbuild/android-arm@0.23.1': optional: true - /@esbuild/android-x64@0.23.1: - resolution: {integrity: sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - requiresBuild: true + '@esbuild/android-x64@0.23.1': optional: true - /@esbuild/darwin-arm64@0.23.1: - resolution: {integrity: sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - requiresBuild: true + '@esbuild/darwin-arm64@0.23.1': optional: true - /@esbuild/darwin-x64@0.23.1: - resolution: {integrity: sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - requiresBuild: true + '@esbuild/darwin-x64@0.23.1': optional: true - /@esbuild/freebsd-arm64@0.23.1: - resolution: {integrity: sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - requiresBuild: true + '@esbuild/freebsd-arm64@0.23.1': optional: true - /@esbuild/freebsd-x64@0.23.1: - resolution: {integrity: sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - requiresBuild: true + '@esbuild/freebsd-x64@0.23.1': optional: true - /@esbuild/linux-arm64@0.23.1: - resolution: {integrity: sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - requiresBuild: true + '@esbuild/linux-arm64@0.23.1': optional: true - /@esbuild/linux-arm@0.23.1: - resolution: {integrity: sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - requiresBuild: true + '@esbuild/linux-arm@0.23.1': optional: true - /@esbuild/linux-ia32@0.23.1: - resolution: {integrity: sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - requiresBuild: true + '@esbuild/linux-ia32@0.23.1': optional: true - /@esbuild/linux-loong64@0.23.1: - resolution: {integrity: sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - requiresBuild: true + '@esbuild/linux-loong64@0.23.1': optional: true - /@esbuild/linux-mips64el@0.23.1: - resolution: {integrity: sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - requiresBuild: true + '@esbuild/linux-mips64el@0.23.1': optional: true - /@esbuild/linux-ppc64@0.23.1: - resolution: {integrity: sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - requiresBuild: true + '@esbuild/linux-ppc64@0.23.1': optional: true - /@esbuild/linux-riscv64@0.23.1: - resolution: {integrity: sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - requiresBuild: true + '@esbuild/linux-riscv64@0.23.1': optional: true - /@esbuild/linux-s390x@0.23.1: - resolution: {integrity: sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - requiresBuild: true + '@esbuild/linux-s390x@0.23.1': optional: true - /@esbuild/linux-x64@0.23.1: - resolution: {integrity: sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - requiresBuild: true + '@esbuild/linux-x64@0.23.1': optional: true - /@esbuild/netbsd-x64@0.23.1: - resolution: {integrity: sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - requiresBuild: true + '@esbuild/netbsd-x64@0.23.1': optional: true - /@esbuild/openbsd-arm64@0.23.1: - resolution: {integrity: sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - requiresBuild: true + '@esbuild/openbsd-arm64@0.23.1': optional: true - /@esbuild/openbsd-x64@0.23.1: - resolution: {integrity: sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - requiresBuild: true + '@esbuild/openbsd-x64@0.23.1': optional: true - /@esbuild/sunos-x64@0.23.1: - resolution: {integrity: sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - requiresBuild: true + '@esbuild/sunos-x64@0.23.1': optional: true - /@esbuild/win32-arm64@0.23.1: - resolution: {integrity: sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - requiresBuild: true + '@esbuild/win32-arm64@0.23.1': optional: true - /@esbuild/win32-ia32@0.23.1: - resolution: {integrity: sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - requiresBuild: true + '@esbuild/win32-ia32@0.23.1': optional: true - /@esbuild/win32-x64@0.23.1: - resolution: {integrity: sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - requiresBuild: true + '@esbuild/win32-x64@0.23.1': optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.57.0): - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.0)': dependencies: eslint: 8.57.0 eslint-visitor-keys: 3.4.3 - /@eslint-community/regexpp@4.11.0: - resolution: {integrity: sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==} - engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + '@eslint-community/regexpp@4.11.0': {} - /@eslint/eslintrc@2.1.4: - resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 debug: 4.3.7 @@ -1422,26 +5172,15 @@ packages: transitivePeerDependencies: - supports-color - /@eslint/js@8.57.0: - resolution: {integrity: sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@eslint/js@8.57.0': {} - /@formatjs/intl-unified-numberformat@3.3.7: - resolution: {integrity: sha512-KnWgLRHzCAgT9eyt3OS34RHoyD7dPDYhRcuKn+/6Kv2knDF8Im43J6vlSW6Hm1w63fNq3ZIT1cFk7RuVO3Psag==} - deprecated: We have renamed the package to @formatjs/intl-numberformat + '@formatjs/intl-unified-numberformat@3.3.7': dependencies: '@formatjs/intl-utils': 2.3.0 - dev: false - /@formatjs/intl-utils@2.3.0: - resolution: {integrity: sha512-KWk80UPIzPmUg+P0rKh6TqspRw0G6eux1PuJr+zz47ftMaZ9QDwbGzHZbtzWkl5hgayM/qrKRutllRC7D/vVXQ==} - deprecated: the package is rather renamed to @formatjs/ecma-abstract with some changes in functionality (primarily selectUnit is removed and we don't plan to make any further changes to this package - dev: false + '@formatjs/intl-utils@2.3.0': {} - /@humanwhocodes/config-array@0.11.14: - resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} - engines: {node: '>=10.10.0'} - deprecated: Use @eslint/config-array instead + '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 debug: 4.3.7 @@ -1449,74 +5188,50 @@ packages: transitivePeerDependencies: - supports-color - /@humanwhocodes/module-importer@1.0.1: - resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} - engines: {node: '>=12.22'} + '@humanwhocodes/module-importer@1.0.1': {} - /@humanwhocodes/object-schema@2.0.3: - resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} - deprecated: Use @eslint/object-schema instead + '@humanwhocodes/object-schema@2.0.3': {} - /@icons/material@0.2.4(react@18.3.1): - resolution: {integrity: sha512-QPcGmICAPbGLGb6F/yNf/KzKqvFx8z5qx3D1yFqVAjoFmXK35EgyW+cJ57Te3CNsmzblwtzakLGFqHPqrfb4Tw==} - peerDependencies: - react: '*' + '@icons/material@0.2.4(react@18.3.1)': dependencies: react: 18.3.1 - dev: true - /@isaacs/cliui@8.0.2: - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 - string-width-cjs: /string-width@4.2.3 + string-width-cjs: string-width@4.2.3 strip-ansi: 7.1.0 - strip-ansi-cjs: /strip-ansi@6.0.1 + strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 - wrap-ansi-cjs: /wrap-ansi@7.0.0 - dev: true + wrap-ansi-cjs: wrap-ansi@7.0.0 - /@jridgewell/gen-mapping@0.3.5: - resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.5': dependencies: '@jridgewell/set-array': 1.2.1 '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.25 - /@jridgewell/resolve-uri@3.1.2: - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.2': {} - /@jridgewell/set-array@1.2.1: - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/set-array@1.2.1': {} - /@jridgewell/sourcemap-codec@1.5.0: - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.0': {} - /@jridgewell/trace-mapping@0.3.25: - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - /@kurkle/color@0.3.2: - resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==} - dev: false + '@kurkle/color@0.3.2': {} - /@manypkg/find-root@1.1.0: - resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.25.6 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 - dev: true - /@manypkg/get-packages@1.1.3: - resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} + '@manypkg/get-packages@1.1.3': dependencies: '@babel/runtime': 7.25.6 '@changesets/types': 4.1.0 @@ -1524,48 +5239,17 @@ packages: fs-extra: 8.1.0 globby: 11.1.0 read-yaml-file: 1.1.0 - dev: true - /@mui/core-downloads-tracker@6.1.0: - resolution: {integrity: sha512-covEnIn/2er5YdtuukDRA52kmARhKrHjOvPsyTFMQApZdrTBI4h8jbEy2mxZqwMwcAFS9coonQXnEZKL1rUNdQ==} - dev: false + '@mui/core-downloads-tracker@6.1.0': {} - /@mui/icons-material@6.0.2(@mui/material@6.1.0)(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-WaTPSvKcx8X7NdWAHzJWDZv+YXvK0MUY8+JI/r4/q2GgIa5RW+n4+08CGX6jB7sWhU1R3zy28NfsDUwwQjOThw==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@mui/material': ^6.0.2 - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + '@mui/icons-material@6.0.2(@mui/material@6.1.0)(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) '@types/react': 18.3.5 react: 18.3.1 - dev: false - /@mui/material@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-4MJ46vmy1xbm8x+ZdRcWm8jEMMowdS8pYlhKQzg/qoKhOcLhImZvf2Jn6z9Dj6gl+lY+C/0MxaHF/avAAGys3Q==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@mui/material-pigment-css': ^6.1.0 - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@mui/material-pigment-css': - optional: true - '@types/react': - optional: true + '@mui/material@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) @@ -1584,37 +5268,16 @@ packages: react-dom: 18.3.1(react@18.3.1) react-is: 18.3.1 react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) - dev: false - /@mui/private-theming@6.1.0(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-+L5qccs4gwsR0r1dgjqhN24QEQRkqIbfOdxILyMbMkuI50x6wNyt9XrV+J3WtjtZTMGJCrUa5VmZBE6OEPGPWA==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + '@mui/private-theming@6.1.0(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) '@types/react': 18.3.5 prop-types: 15.8.1 react: 18.3.1 - dev: false - /@mui/styled-engine@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1): - resolution: {integrity: sha512-MZ+vtaCkjamrT41+b0Er9OMenjAtP/32+L6fARL9/+BZKuV2QbR3q3TmavT2x0NhDu35IM03s4yKqj32Ziqnyg==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.4.1 - '@emotion/styled': ^11.3.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true + '@mui/styled-engine@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/cache': 11.13.1 @@ -1624,23 +5287,8 @@ packages: csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 - dev: false - /@mui/system@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-NumkGDqT6EdXfcoFLYQ+M4XlTW5hH3+aK48xAbRqKPXJfxl36CBt4DLduw/Voa5dcayGus9T6jm1AwU2hoJ5hQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.5.0 - '@emotion/styled': ^11.3.0 - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - '@types/react': - optional: true + '@mui/system@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) @@ -1654,28 +5302,12 @@ packages: csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 - dev: false - /@mui/types@7.2.16(@types/react@18.3.5): - resolution: {integrity: sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + '@mui/types@7.2.16(@types/react@18.3.5)': dependencies: '@types/react': 18.3.5 - dev: false - /@mui/utils@5.16.6(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 - react: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + '@mui/utils@5.16.6(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/types': 7.2.16(@types/react@18.3.5) @@ -1685,17 +5317,8 @@ packages: prop-types: 15.8.1 react: 18.3.1 react-is: 18.3.1 - dev: false - - /@mui/utils@6.1.0(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-oT8ZzMISRUhTVpdbYzY0CgrCBb3t/YEdcaM13tUnuTjZ15pdA6g5lx15ZJUdgYXV6PbJdw7tDQgMEr4uXK5TXQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@types/react': - optional: true + + '@mui/utils@6.1.0(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/types': 7.2.16(@types/react@18.3.5) @@ -1705,23 +5328,8 @@ packages: prop-types: 15.8.1 react: 18.3.1 react-is: 18.3.1 - dev: false - /@mui/x-data-grid@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-41UjJbRxWk+Yk/lfvaO55Pwo5p+F5s3rOTiHLl53ikCT5GuJ5OCCvik0Bi3c6DzTuUBdrEucae2618rydc2DGw==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.9.0 - '@emotion/styled': ^11.8.1 - '@mui/material': ^5.15.14 || ^6.0.0 - '@mui/system': ^5.15.14 || ^6.0.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true + '@mui/x-data-grid@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) @@ -1737,44 +5345,8 @@ packages: reselect: 5.1.1 transitivePeerDependencies: - '@types/react' - dev: false - /@mui/x-date-pickers@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-12tXIoMj9vpS8fS/bS3kWPCoVrH38vNGCxgplI0vOnUrN9rJuYJz3agLPJe1S0xciTw+9W8ZSe3soaW+owoz1Q==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@emotion/react': ^11.9.0 - '@emotion/styled': ^11.8.1 - '@mui/material': ^5.15.14 || ^6.0.0 - '@mui/system': ^5.15.14 || ^6.0.0 - date-fns: ^2.25.0 || ^3.2.0 || ^4.0.0 - date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 - dayjs: ^1.10.7 - luxon: ^3.0.2 - moment: ^2.29.4 - moment-hijri: ^2.1.2 - moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 - react: ^17.0.0 || ^18.0.0 - react-dom: ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - '@emotion/react': - optional: true - '@emotion/styled': - optional: true - date-fns: - optional: true - date-fns-jalali: - optional: true - dayjs: - optional: true - luxon: - optional: true - moment: - optional: true - moment-hijri: - optional: true - moment-jalaali: - optional: true + '@mui/x-date-pickers@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) @@ -1793,89 +5365,45 @@ packages: react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - '@types/react' - dev: false - /@mui/x-internals@7.18.0(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-lzCHOWIR0cAIY1bGrWSprYerahbnH5C31ql/2OWCEjcngL2NAV1M6oKI2Vp4HheqzJ822c60UyWyapvyjSzY/A==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: ^17.0.0 || ^18.0.0 + '@mui/x-internals@7.18.0(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) react: 18.3.1 transitivePeerDependencies: - '@types/react' - dev: false - /@nodelib/fs.scandir@2.1.5: - resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} - engines: {node: '>= 8'} + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - /@nodelib/fs.stat@2.0.5: - resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} - engines: {node: '>= 8'} + '@nodelib/fs.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} - engines: {node: '>= 8'} + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.17.1 - /@nolyfill/is-core-module@1.0.39: - resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} - engines: {node: '>=12.4.0'} - dev: false + '@nolyfill/is-core-module@1.0.39': {} - /@pkgjs/parseargs@0.11.0: - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - requiresBuild: true - dev: true + '@pkgjs/parseargs@0.11.0': optional: true - /@pkgr/core@0.1.1: - resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} - engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} - dev: false + '@pkgr/core@0.1.1': {} - /@popperjs/core@2.11.8: - resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - dev: false + '@popperjs/core@2.11.8': {} - /@react-dnd/asap@5.0.2: - resolution: {integrity: sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==} - dev: false + '@react-dnd/asap@5.0.2': {} - /@react-dnd/invariant@4.0.2: - resolution: {integrity: sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==} - dev: false + '@react-dnd/invariant@4.0.2': {} - /@react-dnd/shallowequal@4.0.2: - resolution: {integrity: sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==} - dev: false + '@react-dnd/shallowequal@4.0.2': {} - /@remix-run/router@1.19.1: - resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==} - engines: {node: '>=14.0.0'} - dev: false + '@remix-run/router@1.19.1': {} - /@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(rollup@4.24.0): - resolution: {integrity: sha512-YF7Y52kFdFT/xVSuVdjkV5ZdX/3YtmX0QulG+x0taQOtJdHYzVU61aSSkAgVJ7NOv6qPkIYiJSgSWWN/DM5sGw==} - engines: {node: '>=14.0.0'} - peerDependencies: - '@babel/core': ^7.0.0 - '@types/babel__core': ^7.1.9 - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - '@types/babel__core': - optional: true - rollup: - optional: true + '@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(rollup@4.24.0)': dependencies: '@babel/core': 7.25.2 '@babel/helper-module-imports': 7.24.7 @@ -1883,16 +5411,8 @@ packages: rollup: 4.24.0 transitivePeerDependencies: - supports-color - dev: true - /@rollup/plugin-commonjs@28.0.1(rollup@4.24.0): - resolution: {integrity: sha512-+tNWdlWKbpB3WgBN7ijjYkq9X5uhjmcvyjEght4NmH5fAU++zfQzAJ6wumLS+dNcvwEZhKx2Z+skY8m7v0wGSA==} - engines: {node: '>=16.0.0 || 14 >= 14.17'} - peerDependencies: - rollup: ^2.68.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/plugin-commonjs@28.0.1(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) commondir: 1.0.1 @@ -1902,16 +5422,8 @@ packages: magic-string: 0.30.11 picomatch: 4.0.2 rollup: 4.24.0 - dev: true - /@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.24.0): - resolution: {integrity: sha512-4lr2oXxs9hcxtGGaK8s0i9evfjzDrAs7ngw28TqruWKTEm0+U4Eljb+F6HXGYdFv8xRojQlrQwV7M/yxeh3yzQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) astring: 1.9.0 @@ -1919,43 +5431,19 @@ packages: fast-glob: 3.3.2 magic-string: 0.30.11 rollup: 4.24.0 - dev: true - /@rollup/plugin-inject@5.0.5: - resolution: {integrity: sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/plugin-inject@5.0.5': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) estree-walker: 2.0.2 magic-string: 0.30.11 - dev: true - /@rollup/plugin-json@6.1.0(rollup@4.24.0): - resolution: {integrity: sha512-EGI2te5ENk1coGeADSIwZ7G2Q8CJS2sF120T7jLw4xFw9n7wIOXHo+kIYRAoVpJAN+kmqZSoO3Fp4JtoNF4ReA==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/plugin-json@6.1.0(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) rollup: 4.24.0 - dev: true - /@rollup/plugin-node-resolve@15.2.3(rollup@4.24.0): - resolution: {integrity: sha512-j/lym8nf5E21LwBT4Df1VD6hRO2L2iwUeUmP7litikRsVp1H6NWx20NEp0Y7su+7XGc476GnXXc4kFeZNGmaSQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.78.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/plugin-node-resolve@15.2.3(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) '@types/resolve': 1.20.2 @@ -1964,343 +5452,180 @@ packages: is-module: 1.0.0 resolve: 1.22.8 rollup: 4.24.0 - dev: true - /@rollup/plugin-typescript@12.1.1(rollup@4.24.0)(typescript@5.6.2): - resolution: {integrity: sha512-t7O653DpfB5MbFrqPe/VcKFFkvRuFNp9qId3xq4Eth5xlyymzxNpye2z8Hrl0RIMuXTSr5GGcFpkdlMeacUiFQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^2.14.0||^3.0.0||^4.0.0 - tslib: '*' - typescript: '>=3.7.0' - peerDependenciesMeta: - rollup: - optional: true - tslib: - optional: true + '@rollup/plugin-typescript@12.1.1(rollup@4.24.0)(typescript@5.6.2)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) resolve: 1.22.8 rollup: 4.24.0 typescript: 5.6.2 - dev: true - /@rollup/pluginutils@5.1.2(rollup@4.24.0): - resolution: {integrity: sha512-/FIdS3PyZ39bjZlwqFnWqCOVnW7o963LtKMwQOD0NhQqw22gSr2YY1afu3FxRip4ZCZNsD5jq6Aaz6QV3D/Njw==} - engines: {node: '>=14.0.0'} - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/pluginutils@5.1.2(rollup@4.24.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 rollup: 4.24.0 - /@rollup/rollup-android-arm-eabi@4.24.0: - resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} - cpu: [arm] - os: [android] - requiresBuild: true + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true - /@rollup/rollup-android-arm64@4.24.0: - resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} - cpu: [arm64] - os: [android] - requiresBuild: true + '@rollup/rollup-android-arm64@4.24.0': optional: true - /@rollup/rollup-darwin-arm64@4.24.0: - resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} - cpu: [arm64] - os: [darwin] - requiresBuild: true + '@rollup/rollup-darwin-arm64@4.24.0': optional: true - /@rollup/rollup-darwin-x64@4.24.0: - resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} - cpu: [x64] - os: [darwin] - requiresBuild: true + '@rollup/rollup-darwin-x64@4.24.0': optional: true - /@rollup/rollup-linux-arm-gnueabihf@4.24.0: - resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} - cpu: [arm] - os: [linux] - libc: [glibc] - requiresBuild: true + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': optional: true - /@rollup/rollup-linux-arm-musleabihf@4.24.0: - resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} - cpu: [arm] - os: [linux] - libc: [musl] - requiresBuild: true + '@rollup/rollup-linux-arm-musleabihf@4.24.0': optional: true - /@rollup/rollup-linux-arm64-gnu@4.24.0: - resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} - cpu: [arm64] - os: [linux] - libc: [glibc] - requiresBuild: true + '@rollup/rollup-linux-arm64-gnu@4.24.0': optional: true - /@rollup/rollup-linux-arm64-musl@4.24.0: - resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} - cpu: [arm64] - os: [linux] - libc: [musl] - requiresBuild: true + '@rollup/rollup-linux-arm64-musl@4.24.0': optional: true - /@rollup/rollup-linux-powerpc64le-gnu@4.24.0: - resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - requiresBuild: true + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': optional: true - /@rollup/rollup-linux-riscv64-gnu@4.24.0: - resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - requiresBuild: true + '@rollup/rollup-linux-riscv64-gnu@4.24.0': optional: true - /@rollup/rollup-linux-s390x-gnu@4.24.0: - resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} - cpu: [s390x] - os: [linux] - libc: [glibc] - requiresBuild: true + '@rollup/rollup-linux-s390x-gnu@4.24.0': optional: true - /@rollup/rollup-linux-x64-gnu@4.24.0: - resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} - cpu: [x64] - os: [linux] - libc: [glibc] - requiresBuild: true + '@rollup/rollup-linux-x64-gnu@4.24.0': optional: true - /@rollup/rollup-linux-x64-musl@4.24.0: - resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} - cpu: [x64] - os: [linux] - libc: [musl] - requiresBuild: true + '@rollup/rollup-linux-x64-musl@4.24.0': optional: true - /@rollup/rollup-win32-arm64-msvc@4.24.0: - resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} - cpu: [arm64] - os: [win32] - requiresBuild: true + '@rollup/rollup-win32-arm64-msvc@4.24.0': optional: true - /@rollup/rollup-win32-ia32-msvc@4.24.0: - resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} - cpu: [ia32] - os: [win32] - requiresBuild: true + '@rollup/rollup-win32-ia32-msvc@4.24.0': optional: true - /@rollup/rollup-win32-x64-msvc@4.24.0: - resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} - cpu: [x64] - os: [win32] - requiresBuild: true + '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true - /@rtsao/scc@1.1.0: - resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - dev: false + '@rtsao/scc@1.1.0': {} - /@rushstack/eslint-patch@1.10.4: - resolution: {integrity: sha512-WJgX9nzTqknM393q1QJDJmoW28kUfEnybeTfVNcNAPnIx210RXm2DiXiHzfNPJNIUUb1tJnz/l4QGtJ30PgWmA==} - dev: false + '@rushstack/eslint-patch@1.10.4': {} - /@types/babel__core@7.20.5: - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.6 '@babel/types': 7.25.6 '@types/babel__generator': 7.6.8 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.6 - dev: true - /@types/babel__generator@7.6.8: - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + '@types/babel__generator@7.6.8': dependencies: '@babel/types': 7.25.6 - dev: true - /@types/babel__template@7.4.4: - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + '@types/babel__template@7.4.4': dependencies: '@babel/parser': 7.25.6 '@babel/types': 7.25.6 - dev: true - /@types/babel__traverse@7.20.6: - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/babel__traverse@7.20.6': dependencies: '@babel/types': 7.25.6 - dev: true - /@types/conventional-commits-parser@5.0.0: - resolution: {integrity: sha512-loB369iXNmAZglwWATL+WRe+CRMmmBPtpolYzIebFaX4YA3x+BEfLqhUAV9WanycKI3TG1IMr5bMJDajDKLlUQ==} + '@types/conventional-commits-parser@5.0.0': dependencies: '@types/node': 20.16.5 - /@types/estree@1.0.6: - resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/estree@1.0.6': {} - /@types/fs-extra@9.0.13: - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} + '@types/fs-extra@9.0.13': dependencies: '@types/node': 20.16.5 - dev: true - /@types/hammerjs@2.0.46: - resolution: {integrity: sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==} - dev: false + '@types/hammerjs@2.0.46': {} - /@types/inquirer@8.2.10: - resolution: {integrity: sha512-IdD5NmHyVjWM8SHWo/kPBgtzXatwPkfwzyP3fN1jF2g9BWt5WO+8hL2F4o2GKIYsU40PpqeevuUWvkS/roXJkA==} + '@types/inquirer@8.2.10': dependencies: '@types/through': 0.0.33 rxjs: 7.8.1 - dev: true - /@types/json5@0.0.29: - resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - dev: false + '@types/json5@0.0.29': {} - /@types/less@3.0.6: - resolution: {integrity: sha512-PecSzorDGdabF57OBeQO/xFbAkYWo88g4Xvnsx7LRwqLC17I7OoKtA3bQB9uXkY6UkMWCOsA8HSVpaoitscdXw==} - dev: true + '@types/less@3.0.6': {} - /@types/lodash-es@4.17.12: - resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.7 - dev: true - /@types/lodash@4.17.7: - resolution: {integrity: sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==} - dev: true + '@types/lodash@4.17.7': {} - /@types/minimist@1.2.5: - resolution: {integrity: sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==} + '@types/minimist@1.2.5': {} - /@types/node@10.17.60: - resolution: {integrity: sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==} - dev: true + '@types/node@10.17.60': {} - /@types/node@12.20.55: - resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - dev: true + '@types/node@12.20.55': {} - /@types/node@20.16.5: - resolution: {integrity: sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==} + '@types/node@20.16.5': dependencies: undici-types: 6.19.8 - /@types/normalize-package-data@2.4.4: - resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} + '@types/normalize-package-data@2.4.4': {} - /@types/parse-json@4.0.2: - resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} - dev: false + '@types/parse-json@4.0.2': {} - /@types/prop-types@15.7.12: - resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + '@types/prop-types@15.7.12': {} - /@types/qs@6.9.15: - resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} - dev: true + '@types/qs@6.9.15': {} - /@types/react-color@3.0.12: - resolution: {integrity: sha512-pr3uKE3lSvf7GFo1Rn2K3QktiZQFFrSgSGJ/3iMvSOYWt2pPAJ97rVdVfhWxYJZ8prAEXzoP2XX//3qGSQgu7Q==} + '@types/react-color@3.0.12': dependencies: '@types/react': 18.3.5 '@types/reactcss': 1.2.12 - dev: true - /@types/react-dom@18.3.0: - resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-dom@18.3.0': dependencies: '@types/react': 18.3.5 - dev: true - /@types/react-grid-layout@1.3.5: - resolution: {integrity: sha512-WH/po1gcEcoR6y857yAnPGug+ZhkF4PaTUxgAbwfeSH/QOgVSakKHBXoPGad/sEznmkiaK3pqHk+etdWisoeBQ==} + '@types/react-grid-layout@1.3.5': dependencies: '@types/react': 18.3.5 - dev: true - /@types/react-resizable@3.0.8: - resolution: {integrity: sha512-Pcvt2eGA7KNXldt1hkhVhAgZ8hK41m0mp89mFgQi7LAAEZiaLgm4fHJ5zbJZ/4m2LVaAyYrrRRv1LHDcrGQanA==} + '@types/react-resizable@3.0.8': dependencies: '@types/react': 18.3.5 - dev: true - /@types/react-transition-group@4.4.11: - resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react-transition-group@4.4.11': dependencies: '@types/react': 18.3.5 - dev: false - /@types/react@18.3.5: - resolution: {integrity: sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==} + '@types/react@18.3.5': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 - /@types/reactcss@1.2.12: - resolution: {integrity: sha512-BrXUQ86/wbbFiZv8h/Q1/Q1XOsaHneYmCb/tHe9+M8XBAAUc2EHfdY0DY22ZZjVSaXr5ix7j+zsqO2eGZub8lQ==} + '@types/reactcss@1.2.12': dependencies: '@types/react': 18.3.5 - dev: true - /@types/resolve@1.20.2: - resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - dev: true + '@types/resolve@1.20.2': {} - /@types/semver@7.5.8: - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - dev: true + '@types/semver@7.5.8': {} - /@types/through@0.0.33: - resolution: {integrity: sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==} + '@types/through@0.0.33': dependencies: '@types/node': 20.16.5 - dev: true - /@types/validator@13.12.2: - resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} - dev: true + '@types/validator@13.12.2': {} - /@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0)(eslint@8.57.0)(typescript@5.6.2): - resolution: {integrity: sha512-lHS5hvz33iUFQKuPFGheAB84LwcJ60G8vKnEhnfcK1l8kGVLro2SFYW6K0/tj8FUhRJ0VHyg1oAfg50QGbPPHw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0)(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.11.0 '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) @@ -2316,17 +5641,8 @@ packages: typescript: 5.6.2 transitivePeerDependencies: - supports-color - dev: false - /@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2): - resolution: {integrity: sha512-gF77eNv0Xz2UJg/NbpWJ0kqAm35UMsvZf1GHj8D9MRFTj/V3tAciIWXfmPLsAAF/vUlpWPvUDyH1jjsr0cMVWw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/scope-manager': 8.5.0 '@typescript-eslint/types': 8.5.0 @@ -2337,24 +5653,13 @@ packages: typescript: 5.6.2 transitivePeerDependencies: - supports-color - dev: false - /@typescript-eslint/scope-manager@8.5.0: - resolution: {integrity: sha512-06JOQ9Qgj33yvBEx6tpC8ecP9o860rsR22hWMEd12WcTRrfaFgHr2RB/CA/B+7BMhHkXT4chg2MyboGdFGawYg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.5.0': dependencies: - '@typescript-eslint/types': 8.5.0 - '@typescript-eslint/visitor-keys': 8.5.0 - dev: false - - /@typescript-eslint/type-utils@8.5.0(eslint@8.57.0)(typescript@5.6.2): - resolution: {integrity: sha512-N1K8Ix+lUM+cIDhL2uekVn/ZD7TZW+9/rwz8DclQpcQ9rk4sIL5CAlBC0CugWKREmDjBzI/kQqU4wkg46jWLYA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/types': 8.5.0 + '@typescript-eslint/visitor-keys': 8.5.0 + + '@typescript-eslint/type-utils@8.5.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@typescript-eslint/typescript-estree': 8.5.0(typescript@5.6.2) '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) @@ -2364,21 +5669,10 @@ packages: transitivePeerDependencies: - eslint - supports-color - dev: false - /@typescript-eslint/types@8.5.0: - resolution: {integrity: sha512-qjkormnQS5wF9pjSi6q60bKUHH44j2APxfh9TQRXK8wbYVeDYYdYJGIROL87LGZZ2gz3Rbmjc736qyL8deVtdw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - dev: false + '@typescript-eslint/types@8.5.0': {} - /@typescript-eslint/typescript-estree@8.5.0(typescript@5.6.2): - resolution: {integrity: sha512-vEG2Sf9P8BPQ+d0pxdfndw3xIXaoSjliG0/Ejk7UggByZPKXmJmw3GW5jV2gHNQNawBUyfahoSiCFVov0Ruf7Q==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/typescript-estree@8.5.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 8.5.0 '@typescript-eslint/visitor-keys': 8.5.0 @@ -2391,13 +5685,8 @@ packages: typescript: 5.6.2 transitivePeerDependencies: - supports-color - dev: false - /@typescript-eslint/utils@8.5.0(eslint@8.57.0)(typescript@5.6.2): - resolution: {integrity: sha512-6yyGYVL0e+VzGYp60wvkBHiqDWOpT63pdMV2CVG4LVDd5uR6q1qQN/7LafBZtAtNIn/mqXjsSeS5ggv/P0iECw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - peerDependencies: - eslint: ^8.57.0 || ^9.0.0 + '@typescript-eslint/utils@8.5.0(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@typescript-eslint/scope-manager': 8.5.0 @@ -2407,24 +5696,15 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: false - /@typescript-eslint/visitor-keys@8.5.0: - resolution: {integrity: sha512-yTPqMnbAZJNy2Xq2XU8AdtOW9tJIr+UQb64aXB9f3B1498Zx9JorVgFJcZpEc9UBuCCrdzKID2RGAMkYcDtZOw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.5.0': dependencies: '@typescript-eslint/types': 8.5.0 eslint-visitor-keys: 3.4.3 - dev: false - /@ungap/structured-clone@1.2.0: - resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@ungap/structured-clone@1.2.0': {} - /@vitejs/plugin-react@4.3.1(vite@5.4.9): - resolution: {integrity: sha512-m/V2syj5CuVnaxcUJOQRel/Wr31FFXRFlnOoq1TVtkCxsY5veGMTEmpWHndrhB2U8ScHtCQB1e+4hWYExQc6Lg==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.2.0 || ^5.0.0 + '@vitejs/plugin-react@4.3.1(vite@5.4.9)': dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) @@ -2434,33 +5714,19 @@ packages: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - supports-color - dev: true - /JSONStream@1.3.5: - resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} - hasBin: true + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 through: 2.3.8 - dev: true - /acorn-jsx@5.3.2(acorn@8.12.1): - resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-jsx@5.3.2(acorn@8.12.1): dependencies: acorn: 8.12.1 - /acorn@8.12.1: - resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} - engines: {node: '>=0.4.0'} - hasBin: true + acorn@8.12.1: {} - /ahooks@3.8.1(react@18.3.1): - resolution: {integrity: sha512-JoP9+/RWO7MnI/uSKdvQ8WB10Y3oo1PjLv+4Sv4Vpm19Z86VUMdXh+RhWvMGxZZs06sq2p0xVtFk8Oh5ZObsoA==} - engines: {node: '>=8.0.0'} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + ahooks@3.8.1(react@18.3.1): dependencies: '@babel/runtime': 7.25.6 dayjs: 1.11.13 @@ -2472,105 +5738,68 @@ packages: resize-observer-polyfill: 1.5.1 screenfull: 5.2.0 tslib: 2.7.0 - dev: false - /ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - /ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 fast-uri: 3.0.1 json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - /ansi-colors@4.1.3: - resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} - engines: {node: '>=6'} - dev: true + ansi-colors@4.1.3: {} - /ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - dev: true - /ansi-escapes@7.0.0: - resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} - engines: {node: '>=18'} + ansi-escapes@7.0.0: dependencies: environment: 1.1.0 - dev: true - /ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} + ansi-regex@5.0.1: {} - /ansi-regex@6.0.1: - resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} - engines: {node: '>=12'} - dev: true + ansi-regex@6.0.1: {} - /ansi-styles@3.2.1: - resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} - engines: {node: '>=4'} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - dev: true + ansi-styles@6.2.1: {} - /anymatch@3.1.3: - resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} - engines: {node: '>= 8'} + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - dev: true - /argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - dev: true - /argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + argparse@2.0.1: {} - /aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + aria-query@5.1.3: dependencies: deep-equal: 2.2.3 - dev: false - /array-buffer-byte-length@1.0.1: - resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==} - engines: {node: '>= 0.4'} + array-buffer-byte-length@1.0.1: dependencies: call-bind: 1.0.7 is-array-buffer: 3.0.4 - /array-ify@1.0.0: - resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + array-ify@1.0.0: {} - /array-includes@3.1.8: - resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} - engines: {node: '>= 0.4'} + array-includes@3.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2578,15 +5807,10 @@ packages: es-object-atoms: 1.0.0 get-intrinsic: 1.2.4 is-string: 1.0.7 - dev: false - /array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} + array-union@2.1.0: {} - /array.prototype.findlast@1.2.5: - resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} - engines: {node: '>= 0.4'} + array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2594,11 +5818,8 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} - engines: {node: '>= 0.4'} + array.prototype.findlastindex@1.2.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -2606,42 +5827,30 @@ packages: es-errors: 1.3.0 es-object-atoms: 1.0.0 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.flat@1.3.2: - resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} - engines: {node: '>= 0.4'} + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.flatmap@1.3.2: - resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} - engines: {node: '>= 0.4'} + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-shim-unscopables: 1.0.2 - dev: false - /array.prototype.tosorted@1.1.4: - resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} - engines: {node: '>= 0.4'} + array.prototype.tosorted@1.1.4: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-errors: 1.3.0 es-shim-unscopables: 1.0.2 - dev: false - /arraybuffer.prototype.slice@1.0.3: - resolution: {integrity: sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==} - engines: {node: '>= 0.4'} + arraybuffer.prototype.slice@1.0.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -2652,147 +5861,94 @@ packages: is-array-buffer: 3.0.4 is-shared-array-buffer: 1.0.3 - /arrify@1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} + arrify@1.0.1: {} - /asn1.js@4.10.1: - resolution: {integrity: sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==} + asn1.js@4.10.1: dependencies: bn.js: 4.12.0 inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true - /assert@2.1.0: - resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} + assert@2.1.0: dependencies: call-bind: 1.0.7 is-nan: 1.3.2 object-is: 1.1.6 object.assign: 4.1.5 util: 0.12.5 - dev: true - /ast-types-flow@0.0.8: - resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - dev: false + ast-types-flow@0.0.8: {} - /astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} + astral-regex@2.0.0: {} - /astring@1.9.0: - resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} - hasBin: true - dev: true + astring@1.9.0: {} - /asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - dev: false + asynckit@0.4.0: {} - /available-typed-arrays@1.0.7: - resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} - engines: {node: '>= 0.4'} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - /axe-core@4.10.0: - resolution: {integrity: sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==} - engines: {node: '>=4'} - dev: false + axe-core@4.10.0: {} - /axios@1.7.7: - resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} + axios@1.7.7: dependencies: follow-redirects: 1.15.9 form-data: 4.0.0 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: false - /axobject-query@4.1.0: - resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} - engines: {node: '>= 0.4'} - dev: false + axobject-query@4.1.0: {} - /babel-plugin-macros@3.1.0: - resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} - engines: {node: '>=10', npm: '>=6'} + babel-plugin-macros@3.1.0: dependencies: '@babel/runtime': 7.25.6 cosmiconfig: 7.1.0 resolve: 1.22.8 - dev: false - /balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@1.0.2: {} - /balanced-match@2.0.0: - resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + balanced-match@2.0.0: {} - /base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - dev: true + base64-js@1.5.1: {} - /better-path-resolve@1.0.0: - resolution: {integrity: sha512-pbnl5XzGBdrFU/wT4jqmJVPn2B6UHPBOhzMQkY/SPUPB6QtUXtmBHBIwCbXJol93mOpGMnQyP/+BB19q04xj7g==} - engines: {node: '>=4'} + better-path-resolve@1.0.0: dependencies: is-windows: 1.0.2 - dev: true - /binary-extensions@2.3.0: - resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} - engines: {node: '>=8'} - dev: true + binary-extensions@2.3.0: {} - /bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + bl@4.1.0: dependencies: buffer: 5.7.1 inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /bn.js@4.12.0: - resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} - dev: true + bn.js@4.12.0: {} - /bn.js@5.2.1: - resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} - dev: true + bn.js@5.2.1: {} - /brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - /brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.0.1: dependencies: balanced-match: 1.0.2 - /braces@3.0.3: - resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} - engines: {node: '>=8'} + braces@3.0.3: dependencies: fill-range: 7.1.1 - /brorand@1.1.0: - resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} - dev: true + brorand@1.1.0: {} - /browser-resolve@2.0.0: - resolution: {integrity: sha512-7sWsQlYL2rGLy2IWm8WL8DCTJvYLc/qlOnsakDac87SOoCd16WLsaAMdCiAqsTNHIe+SXfaqyxyo6THoWqs8WQ==} + browser-resolve@2.0.0: dependencies: resolve: 1.22.8 - dev: true - /browserify-aes@1.2.0: - resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + browserify-aes@1.2.0: dependencies: buffer-xor: 1.0.3 cipher-base: 1.0.4 @@ -2800,35 +5956,26 @@ packages: evp_bytestokey: 1.0.3 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /browserify-cipher@1.0.1: - resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + browserify-cipher@1.0.1: dependencies: browserify-aes: 1.2.0 browserify-des: 1.0.2 evp_bytestokey: 1.0.3 - dev: true - /browserify-des@1.0.2: - resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + browserify-des@1.0.2: dependencies: cipher-base: 1.0.4 des.js: 1.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /browserify-rsa@4.1.0: - resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + browserify-rsa@4.1.0: dependencies: bn.js: 5.2.1 randombytes: 2.1.0 - dev: true - /browserify-sign@4.2.3: - resolution: {integrity: sha512-JWCZW6SKhfhjJxO8Tyiiy+XYB7cqd2S5/+WeYHsKdNKFlCBhKbblba1A/HN/90YwtxKc8tCErjffZl++UNmGiw==} - engines: {node: '>= 0.12'} + browserify-sign@4.2.3: dependencies: bn.js: 5.2.1 browserify-rsa: 4.1.0 @@ -2840,48 +5987,30 @@ packages: parse-asn1: 5.1.7 readable-stream: 2.3.8 safe-buffer: 5.2.1 - dev: true - /browserify-zlib@0.2.0: - resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + browserify-zlib@0.2.0: dependencies: pako: 1.0.11 - dev: true - /browserslist@4.23.3: - resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true + browserslist@4.23.3: dependencies: caniuse-lite: 1.0.30001659 electron-to-chromium: 1.5.18 node-releases: 2.0.18 update-browserslist-db: 1.1.0(browserslist@4.23.3) - dev: true - /buffer-xor@1.0.3: - resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} - dev: true + buffer-xor@1.0.3: {} - /buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + buffer@5.7.1: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 - dev: true - /builtin-modules@3.3.0: - resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} - engines: {node: '>=6'} - dev: true + builtin-modules@3.3.0: {} - /builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - dev: true + builtin-status-codes@3.0.0: {} - /call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} + call-bind@1.0.7: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 @@ -2889,80 +6018,50 @@ packages: get-intrinsic: 1.2.4 set-function-length: 1.2.2 - /callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} + callsites@3.1.0: {} - /camelcase-keys@7.0.2: - resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} - engines: {node: '>=12'} + camelcase-keys@7.0.2: dependencies: camelcase: 6.3.0 map-obj: 4.3.0 quick-lru: 5.1.1 type-fest: 1.4.0 - /camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} + camelcase@6.3.0: {} - /caniuse-lite@1.0.30001659: - resolution: {integrity: sha512-Qxxyfv3RdHAfJcXelgf0hU4DFUVXBGTjqrBUZLUh8AtlGnsDo+CnncYtTd95+ZKfnANUOzxyIQCuU/UeBZBYoA==} - dev: true + caniuse-lite@1.0.30001659: {} - /chalk@2.4.2: - resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} - engines: {node: '>=4'} + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + chalk@5.3.0: {} - /chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - dev: true + chardet@0.7.0: {} - /chart.js@4.4.4: - resolution: {integrity: sha512-emICKGBABnxhMjUjlYRR12PmOXhJ2eJjEHL2/dZlWjxRAZT1D8xplLFq5M0tMQK8ja+wBS/tuVEJB5C6r7VxJA==} - engines: {pnpm: '>=8'} + chart.js@4.4.4: dependencies: '@kurkle/color': 0.3.2 - dev: false - /chartjs-adapter-date-fns@3.0.0(chart.js@4.4.4)(date-fns@4.1.0): - resolution: {integrity: sha512-Rs3iEB3Q5pJ973J93OBTpnP7qoGwvq3nUnoMdtxO+9aoJof7UFcRbWcIDteXuYd1fgAvct/32T9qaLyLuZVwCg==} - peerDependencies: - chart.js: '>=2.8.0' - date-fns: '>=2.0.0' + chartjs-adapter-date-fns@3.0.0(chart.js@4.4.4)(date-fns@4.1.0): dependencies: chart.js: 4.4.4 date-fns: 4.1.0 - dev: false - /chartjs-plugin-zoom@2.1.0(chart.js@4.4.4): - resolution: {integrity: sha512-7lMimfQCUaIJLhPJaWSAA4gw+1m8lyR3Wn+M3MxjHbM/XxRUnOxN7cM5RR9jUmxmyW0h7L2hZ8KhvUsqrFxy/Q==} - peerDependencies: - chart.js: '>=3.2.0' + chartjs-plugin-zoom@2.1.0(chart.js@4.4.4): dependencies: '@types/hammerjs': 2.0.46 chart.js: 4.4.4 hammerjs: 2.0.8 - dev: false - /chokidar@3.6.0: - resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} - engines: {node: '>= 8.10.0'} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 braces: 3.0.3 @@ -2973,225 +6072,127 @@ packages: readdirp: 3.6.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - dev: true + ci-info@3.9.0: {} - /cipher-base@1.0.4: - resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + cipher-base@1.0.4: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /classnames@2.5.1: - resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} - dev: false + classnames@2.5.1: {} - /cli-cursor@3.1.0: - resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} - engines: {node: '>=8'} + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 - dev: true - /cli-cursor@5.0.0: - resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} - engines: {node: '>=18'} + cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 - dev: true - /cli-spinners@2.9.2: - resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} - engines: {node: '>=6'} - dev: true + cli-spinners@2.9.2: {} - /cli-truncate@4.0.0: - resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} - engines: {node: '>=18'} + cli-truncate@4.0.0: dependencies: slice-ansi: 5.0.0 string-width: 7.2.0 - dev: true - /cli-width@3.0.0: - resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} - engines: {node: '>= 10'} - dev: true + cli-width@3.0.0: {} - /cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} + cliui@8.0.1: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - dev: true - /clone@1.0.4: - resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} - engines: {node: '>=0.8'} - dev: true + clone@1.0.4: {} - /clsx@1.2.1: - resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==} - engines: {node: '>=6'} - dev: false + clsx@1.2.1: {} - /clsx@2.1.1: - resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} - engines: {node: '>=6'} - dev: false + clsx@2.1.1: {} - /color-convert@1.9.3: - resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + color-convert@1.9.3: dependencies: color-name: 1.1.3 - /color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} + color-convert@2.0.1: dependencies: color-name: 1.1.4 - /color-name@1.1.3: - resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + color-name@1.1.3: {} - /color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-name@1.1.4: {} - /colord@2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + colord@2.9.3: {} - /colorette@2.0.20: - resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - dev: true + colorette@2.0.20: {} - /combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 - dev: false - /commander@12.1.0: - resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} - engines: {node: '>=18'} - dev: true + commander@12.1.0: {} - /commander@8.3.0: - resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} - engines: {node: '>= 12'} - dev: true + commander@8.3.0: {} - /commondir@1.0.1: - resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} - dev: true + commondir@1.0.1: {} - /compare-func@2.0.0: - resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + compare-func@2.0.0: dependencies: array-ify: 1.0.0 dot-prop: 5.3.0 - /concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + concat-map@0.0.1: {} - /confusing-browser-globals@1.0.11: - resolution: {integrity: sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA==} - dev: false + confusing-browser-globals@1.0.11: {} - /console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - dev: true + console-browserify@1.2.0: {} - /constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - dev: true + constants-browserify@1.0.0: {} - /conventional-changelog-angular@7.0.0: - resolution: {integrity: sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==} - engines: {node: '>=16'} + conventional-changelog-angular@7.0.0: dependencies: compare-func: 2.0.0 - dev: true - /conventional-changelog-conventionalcommits@7.0.2: - resolution: {integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==} - engines: {node: '>=16'} + conventional-changelog-conventionalcommits@7.0.2: dependencies: compare-func: 2.0.0 - /conventional-commits-parser@5.0.0: - resolution: {integrity: sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==} - engines: {node: '>=16'} - hasBin: true + conventional-commits-parser@5.0.0: dependencies: JSONStream: 1.3.5 is-text-path: 2.0.0 meow: 12.1.1 split2: 4.2.0 - dev: true - /convert-source-map@1.9.0: - resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} - dev: false + convert-source-map@1.9.0: {} - /convert-source-map@2.0.0: - resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} - dev: true + convert-source-map@2.0.0: {} - /cookie@0.3.1: - resolution: {integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==} - engines: {node: '>= 0.6'} - dev: false + cookie@0.3.1: {} - /copy-anything@2.0.6: - resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-anything@2.0.6: dependencies: is-what: 3.14.1 - /core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - dev: true + core-util-is@1.0.3: {} - /cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0)(typescript@5.6.2): - resolution: {integrity: sha512-+8cK7jRAReYkMwMiG+bxhcNKiHJDM6bR9FD/nGBXOWdMLuYawjF5cGrtLilJ+LGd3ZjCXnJjR5DkfWPoIVlqJA==} - engines: {node: '>=v16'} - peerDependencies: - '@types/node': '*' - cosmiconfig: '>=8.2' - typescript: '>=4' + cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0)(typescript@5.6.2): dependencies: '@types/node': 20.16.5 cosmiconfig: 9.0.0(typescript@5.6.2) jiti: 1.21.6 typescript: 5.6.2 - dev: true - /cosmiconfig@7.1.0: - resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} - engines: {node: '>=10'} + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 - dev: false - /cosmiconfig@8.3.6(typescript@5.6.2): - resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@8.3.6(typescript@5.6.2): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 @@ -3199,41 +6200,28 @@ packages: path-type: 4.0.0 typescript: 5.6.2 - /cosmiconfig@9.0.0(typescript@5.6.2): - resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} - engines: {node: '>=14'} - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@9.0.0(typescript@5.6.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 typescript: 5.6.2 - dev: true - /create-ecdh@4.0.4: - resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + create-ecdh@4.0.4: dependencies: bn.js: 4.12.0 elliptic: 6.5.7 - dev: true - /create-hash@1.2.0: - resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + create-hash@1.2.0: dependencies: cipher-base: 1.0.4 inherits: 2.0.4 md5.js: 1.3.5 ripemd160: 2.0.2 sha.js: 2.4.11 - dev: true - /create-hmac@1.1.7: - resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + create-hmac@1.1.7: dependencies: cipher-base: 1.0.4 create-hash: 1.2.0 @@ -3241,41 +6229,30 @@ packages: ripemd160: 2.0.2 safe-buffer: 5.2.1 sha.js: 2.4.11 - dev: true - /create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - dev: true + create-require@1.1.1: {} - /cross-spawn@5.1.0: - resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} + cross-spawn@5.1.0: dependencies: lru-cache: 4.1.5 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@6.0.5: - resolution: {integrity: sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==} - engines: {node: '>=4.8'} + cross-spawn@6.0.5: dependencies: nice-try: 1.0.5 path-key: 2.0.1 semver: 5.7.2 shebang-command: 1.2.0 which: 1.3.1 - dev: true - /cross-spawn@7.0.3: - resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} - engines: {node: '>= 8'} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - /crypto-browserify@3.12.0: - resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + crypto-browserify@3.12.0: dependencies: browserify-cipher: 1.0.1 browserify-sign: 4.2.3 @@ -3288,119 +6265,69 @@ packages: public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 - dev: true - /css-functions-list@3.2.2: - resolution: {integrity: sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==} - engines: {node: '>=12 || >=16'} + css-functions-list@3.2.2: {} - /css-property-sort-order-smacss@2.2.0: - resolution: {integrity: sha512-nXutswsivIEBOrPo/OZw2KQjFPLvtg68aovJf6Kqrm3L6FmTvvFPaeDrk83hh0+pRJGuP3PeKJwMS0E6DFipdQ==} - dev: false + css-property-sort-order-smacss@2.2.0: {} - /css-tree@2.3.1: - resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} - engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 source-map-js: 1.2.1 - /css-vars-ponyfill@2.4.9: - resolution: {integrity: sha512-aZyLue5bdiGVNCiCclNjo123D8I7kyoYNUaAvz+H1JalX1ye4Ilz7jNRRH5YbM+dYD6ucejiydGwk7lol/GCXQ==} + css-vars-ponyfill@2.4.9: dependencies: balanced-match: 1.0.2 get-css-data: 2.1.1 - dev: false - - /cssesc@3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - /csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + cssesc@3.0.0: {} - /damerau-levenshtein@1.0.8: - resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} - dev: false + csstype@3.1.3: {} - /dargs@8.1.0: - resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} - engines: {node: '>=12'} - dev: true + damerau-levenshtein@1.0.8: {} - /data-view-buffer@1.0.1: - resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} - engines: {node: '>= 0.4'} + dargs@8.1.0: {} + + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - /data-view-byte-length@1.0.1: - resolution: {integrity: sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==} - engines: {node: '>= 0.4'} + data-view-byte-length@1.0.1: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - /data-view-byte-offset@1.0.0: - resolution: {integrity: sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==} - engines: {node: '>= 0.4'} + data-view-byte-offset@1.0.0: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-data-view: 1.0.1 - /date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} - dev: false + date-fns@4.1.0: {} - /dayjs@1.11.13: - resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} - dev: false + dayjs@1.11.13: {} - /debug@3.2.7: - resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: false - /debug@4.3.7: - resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.7: dependencies: ms: 2.1.3 - /decamelize-keys@1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 map-obj: 1.0.1 - /decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} + decamelize@1.2.0: {} - /decamelize@5.0.1: - resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} - engines: {node: '>=10'} + decamelize@5.0.1: {} - /deep-equal@2.2.3: - resolution: {integrity: sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==} - engines: {node: '>= 0.4'} + deep-equal@2.2.3: dependencies: array-buffer-byte-length: 1.0.1 call-bind: 1.0.7 @@ -3420,156 +6347,101 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: false - /deep-is@0.1.4: - resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + deep-is@0.1.4: {} - /deepmerge@4.3.1: - resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} - engines: {node: '>=0.10.0'} - dev: true + deepmerge@4.3.1: {} - /defaults@1.0.4: - resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + defaults@1.0.4: dependencies: clone: 1.0.4 - dev: true - /define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} + define-data-property@1.1.4: dependencies: es-define-property: 1.0.0 es-errors: 1.3.0 gopd: 1.0.1 - /define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} + define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 - /delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - dev: false + delayed-stream@1.0.0: {} - /des.js@1.1.0: - resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + des.js@1.1.0: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true - /detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - dev: true + detect-indent@6.1.0: {} - /diffie-hellman@5.0.3: - resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + diffie-hellman@5.0.3: dependencies: bn.js: 4.12.0 miller-rabin: 4.0.1 randombytes: 2.1.0 - dev: true - /dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - /dnd-core@16.0.1: - resolution: {integrity: sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==} + dnd-core@16.0.1: dependencies: '@react-dnd/asap': 5.0.2 '@react-dnd/invariant': 4.0.2 redux: 4.2.1 - dev: false - /doctrine@2.1.0: - resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} - engines: {node: '>=0.10.0'} + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: false - /doctrine@3.0.0: - resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} - engines: {node: '>=6.0.0'} + doctrine@3.0.0: dependencies: esutils: 2.0.3 - /dom-helpers@5.2.1: - resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dom-helpers@5.2.1: dependencies: '@babel/runtime': 7.25.6 csstype: 3.1.3 - dev: false - /dom-serializer@2.0.0: - resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 - dev: false - /domain-browser@4.23.0: - resolution: {integrity: sha512-ArzcM/II1wCCujdCNyQjXrAFwS4mrLh4C7DZWlaI8mdh7h3BfKdNd3bKXITfl2PT9FtfQqaGvhi1vPRQPimjGA==} - engines: {node: '>=10'} - dev: true + domain-browser@4.23.0: {} - /domelementtype@2.3.0: - resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} - dev: false + domelementtype@2.3.0: {} - /domhandler@5.0.3: - resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} - engines: {node: '>= 4'} + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 - dev: false - /domutils@3.1.0: - resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + domutils@3.1.0: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 domhandler: 5.0.3 - dev: false - /dot-case@3.0.4: - resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + dot-case@3.0.4: dependencies: no-case: 3.0.4 tslib: 2.7.0 - dev: true - /dot-prop@5.3.0: - resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} - engines: {node: '>=8'} + dot-prop@5.3.0: dependencies: is-obj: 2.0.0 - /dotenv@16.4.5: - resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} - engines: {node: '>=12'} + dotenv@16.4.5: {} - /eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - dev: true + eastasianwidth@0.2.0: {} - /electron-to-chromium@1.5.18: - resolution: {integrity: sha512-1OfuVACu+zKlmjsNdcJuVQuVE61sZOLbNM4JAQ1Rvh6EOj0/EUKhMJjRH73InPlXSh8HIJk1cVZ8pyOV/FMdUQ==} - dev: true + electron-to-chromium@1.5.18: {} - /elliptic@6.5.7: - resolution: {integrity: sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==} + elliptic@6.5.7: dependencies: bn.js: 4.12.0 brorand: 1.1.0 @@ -3578,65 +6450,39 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: true - /emoji-regex@10.4.0: - resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} - dev: true + emoji-regex@10.4.0: {} - /emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + emoji-regex@8.0.0: {} - /emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emoji-regex@9.2.2: {} - /enhanced-resolve@5.17.1: - resolution: {integrity: sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==} - engines: {node: '>=10.13.0'} + enhanced-resolve@5.17.1: dependencies: graceful-fs: 4.2.11 tapable: 2.2.1 - dev: false - /enquirer@2.4.1: - resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} - engines: {node: '>=8.6'} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 strip-ansi: 6.0.1 - dev: true - /entities@4.5.0: - resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} - engines: {node: '>=0.12'} - dev: false + entities@4.5.0: {} - /env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - dev: true + env-paths@2.2.1: {} - /environment@1.1.0: - resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} - engines: {node: '>=18'} - dev: true + environment@1.1.0: {} - /errno@0.1.8: - resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} - hasBin: true - requiresBuild: true + errno@0.1.8: dependencies: prr: 1.0.1 optional: true - /error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - /es-abstract@1.23.3: - resolution: {integrity: sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==} - engines: {node: '>= 0.4'} + es-abstract@1.23.3: dependencies: array-buffer-byte-length: 1.0.1 arraybuffer.prototype.slice: 1.0.3 @@ -3685,18 +6531,13 @@ packages: unbox-primitive: 1.0.2 which-typed-array: 1.1.15 - /es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} + es-define-property@1.0.0: dependencies: get-intrinsic: 1.2.4 - /es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} + es-errors@1.3.0: {} - /es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} + es-get-iterator@1.1.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 @@ -3707,11 +6548,8 @@ packages: is-string: 1.0.7 isarray: 2.0.5 stop-iteration-iterator: 1.0.0 - dev: false - /es-iterator-helpers@1.0.19: - resolution: {integrity: sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==} - engines: {node: '>= 0.4'} + es-iterator-helpers@1.0.19: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -3727,45 +6565,30 @@ packages: internal-slot: 1.0.7 iterator.prototype: 1.1.2 safe-array-concat: 1.1.2 - dev: false - /es-module-lexer@1.5.4: - resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} - dev: false + es-module-lexer@1.5.4: {} - /es-object-atoms@1.0.0: - resolution: {integrity: sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==} - engines: {node: '>= 0.4'} + es-object-atoms@1.0.0: dependencies: es-errors: 1.3.0 - /es-set-tostringtag@2.0.3: - resolution: {integrity: sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==} - engines: {node: '>= 0.4'} + es-set-tostringtag@2.0.3: dependencies: get-intrinsic: 1.2.4 has-tostringtag: 1.0.2 hasown: 2.0.2 - /es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.0.2: dependencies: hasown: 2.0.2 - dev: false - /es-to-primitive@1.2.1: - resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} - engines: {node: '>= 0.4'} + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - /esbuild@0.23.1: - resolution: {integrity: sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==} - engines: {node: '>=18'} - hasBin: true - requiresBuild: true + esbuild@0.23.1: optionalDependencies: '@esbuild/aix-ppc64': 0.23.1 '@esbuild/android-arm': 0.23.1 @@ -3792,29 +6615,15 @@ packages: '@esbuild/win32-ia32': 0.23.1 '@esbuild/win32-x64': 0.23.1 - /escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - dev: true + escalade@3.2.0: {} - /escape-html@1.0.3: - resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} - dev: false + escape-html@1.0.3: {} - /escape-string-regexp@1.0.5: - resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} - engines: {node: '>=0.8.0'} + escape-string-regexp@1.0.5: {} - /escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} + escape-string-regexp@4.0.0: {} - /eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.0): - resolution: {integrity: sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig==} - engines: {node: ^10.12.0 || >=12.0.0} - peerDependencies: - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.2 + eslint-config-airbnb-base@15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.0): dependencies: confusing-browser-globals: 1.0.11 eslint: 8.57.0 @@ -3822,17 +6631,8 @@ packages: object.assign: 4.1.5 object.entries: 1.1.8 semver: 6.3.1 - dev: false - /eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0)(eslint-plugin-react-hooks@4.6.2)(eslint-plugin-react@7.35.2)(eslint@8.57.0): - resolution: {integrity: sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==} - engines: {node: ^10.12.0 || ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^7.32.0 || ^8.2.0 - eslint-plugin-import: ^2.25.3 - eslint-plugin-jsx-a11y: ^6.5.1 - eslint-plugin-react: ^7.28.0 - eslint-plugin-react-hooks: ^4.3.0 + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0)(eslint-plugin-react-hooks@4.6.2)(eslint-plugin-react@7.35.2)(eslint@8.57.0): dependencies: eslint: 8.57.0 eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.0) @@ -3842,48 +6642,24 @@ packages: eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) object.assign: 4.1.5 object.entries: 1.1.8 - dev: false - /eslint-config-prettier@9.1.0(eslint@8.57.0): - resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==} - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + eslint-config-prettier@9.1.0(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: false - /eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.30.0): - resolution: {integrity: sha512-WdviM1Eu834zsfjHtcGHtGfcu+F30Od3V7I9Fi57uhBEwPkjDcii7/yW8jAT+gOhn4P/vOxxNAXbFAKsrrc15w==} - engines: {node: '>= 4'} - peerDependencies: - eslint-plugin-import: '>=1.4.0' + eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.30.0): dependencies: eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) - dev: false - /eslint-import-resolver-node@0.3.9: - resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.15.1 resolve: 1.22.8 transitivePeerDependencies: - supports-color - dev: false - /eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0): - resolution: {integrity: sha512-ud9aw4szY9cCT1EWWdGv1L1XR6hh2PaRWif0j2QjQ0pgTY/69iw+W0Z4qZv5wHahOl8isEr+k/JnyAqNQkLkIA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' - eslint-plugin-import-x: '*' - peerDependenciesMeta: - eslint-plugin-import: - optional: true - eslint-plugin-import-x: - optional: true + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 @@ -3900,28 +6676,8 @@ packages: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): - resolution: {integrity: sha512-gbBE5Hitek/oG6MUVj6sFuzEjA/ClzNflVrLovHi/JgLdC7fiN5gLAY1WIPW1a0V5I999MnsrvVrCOGmmVqDBQ==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) debug: 3.2.7 @@ -3930,28 +6686,14 @@ packages: eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0) transitivePeerDependencies: - supports-color - dev: false - /eslint-plugin-check-file@2.8.0(eslint@8.57.0): - resolution: {integrity: sha512-FvvafMTam2WJYH9uj+FuMxQ1y+7jY3Z6P9T4j2214cH0FBxNzTcmeCiGTj1Lxp3mI6kbbgsXvmgewvf+llKYyw==} - engines: {node: '>=18'} - peerDependencies: - eslint: '>=7.28.0' + eslint-plugin-check-file@2.8.0(eslint@8.57.0): dependencies: eslint: 8.57.0 is-glob: 4.0.3 micromatch: 4.0.8 - dev: false - /eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): - resolution: {integrity: sha512-/mHNE9jINJfiD2EKkg1BKyPyUk4zdnT54YgbOgfjSakWT5oyX/qQLVNTkehyfpcMxZXMy1zyonZ2v7hZTX43Yw==} - engines: {node: '>=4'} - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) @@ -3977,13 +6719,8 @@ packages: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: false - /eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0): - resolution: {integrity: sha512-ySOHvXX8eSN6zz8Bywacm7CvGNhUtdjvqfQDVe6020TUK34Cywkw7m0KsCCk1Qtm9G1FayfTN1/7mMYnYO2Bhg==} - engines: {node: '>=4.0'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0): dependencies: aria-query: 5.1.3 array-includes: 3.1.8 @@ -4002,43 +6739,20 @@ packages: object.fromentries: 2.0.8 safe-regex-test: 1.0.3 string.prototype.includes: 2.0.0 - dev: false - /eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3): - resolution: {integrity: sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - '@types/eslint': '>=8.0.0' - eslint: '>=8.0.0' - eslint-config-prettier: '*' - prettier: '>=3.0.0' - peerDependenciesMeta: - '@types/eslint': - optional: true - eslint-config-prettier: - optional: true + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3): dependencies: eslint: 8.57.0 eslint-config-prettier: 9.1.0(eslint@8.57.0) prettier: 3.3.3 prettier-linter-helpers: 1.0.0 synckit: 0.9.1 - dev: false - /eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): - resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} - engines: {node: '>=10'} - peerDependencies: - eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): dependencies: eslint: 8.57.0 - dev: false - /eslint-plugin-react@7.35.2(eslint@8.57.0): - resolution: {integrity: sha512-Rbj2R9zwP2GYNcIak4xoAMV57hrBh3hTaR0k7hVjwCQgryE/pw5px4b13EYjduOI0hfXyZhwBxaGpOTbWSGzKQ==} - engines: {node: '>=4'} - peerDependencies: - eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + eslint-plugin-react@7.35.2(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -4059,24 +6773,15 @@ packages: semver: 6.3.1 string.prototype.matchall: 4.0.11 string.prototype.repeat: 1.0.0 - dev: false - /eslint-scope@7.2.2: - resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - /eslint-visitor-keys@3.4.3: - resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@3.4.3: {} - /eslint@8.57.0: - resolution: {integrity: sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. - hasBin: true + eslint@8.57.0: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0) '@eslint-community/regexpp': 4.11.0 @@ -4119,62 +6824,38 @@ packages: transitivePeerDependencies: - supports-color - /espree@9.6.1: - resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + espree@9.6.1: dependencies: acorn: 8.12.1 acorn-jsx: 5.3.2(acorn@8.12.1) eslint-visitor-keys: 3.4.3 - /esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - dev: true + esprima@4.0.1: {} - /esquery@1.6.0: - resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} - engines: {node: '>=0.10'} + esquery@1.6.0: dependencies: estraverse: 5.3.0 - /esrecurse@4.3.0: - resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} - engines: {node: '>=4.0'} + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 - /estraverse@5.3.0: - resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} - engines: {node: '>=4.0'} + estraverse@5.3.0: {} - /estree-walker@2.0.2: - resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@2.0.2: {} - /esutils@2.0.3: - resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} - engines: {node: '>=0.10.0'} + esutils@2.0.3: {} - /eventemitter3@5.0.1: - resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - dev: true + eventemitter3@5.0.1: {} - /events@3.3.0: - resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} - engines: {node: '>=0.8.x'} - dev: true + events@3.3.0: {} - /evp_bytestokey@1.0.3: - resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + evp_bytestokey@1.0.3: dependencies: md5.js: 1.3.5 safe-buffer: 5.2.1 - dev: true - /execa@8.0.1: - resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} - engines: {node: '>=16.17'} + execa@8.0.1: dependencies: cross-spawn: 7.0.3 get-stream: 8.0.1 @@ -4185,35 +6866,22 @@ packages: onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 - dev: true - /extendable-error@0.1.7: - resolution: {integrity: sha512-UOiS2in6/Q0FK0R0q6UY9vYpQ21mr/Qn1KOnte7vsACuNJf514WvCCUHSRCPcgjPT2bAhNIJdlE6bVap1GKmeg==} - dev: true + extendable-error@0.1.7: {} - /external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} + external-editor@3.1.0: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: true - /fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-deep-equal@3.1.3: {} - /fast-diff@1.3.0: - resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} - dev: false + fast-diff@1.3.0: {} - /fast-equals@4.0.3: - resolution: {integrity: sha512-G3BSX9cfKttjr+2o1O22tYMLq0DPluZnYtq1rXumE1SpL/F/SLIfHx08WYQoWSIpeMYf8sRbJ8++71+v6Pnxfg==} - dev: false + fast-equals@4.0.3: {} - /fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} + fast-glob@3.3.2: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 @@ -4221,205 +6889,124 @@ packages: merge2: 1.4.1 micromatch: 4.0.8 - /fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + fast-json-stable-stringify@2.1.0: {} - /fast-levenshtein@2.0.6: - resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-levenshtein@2.0.6: {} - /fast-uri@3.0.1: - resolution: {integrity: sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==} + fast-uri@3.0.1: {} - /fastest-levenshtein@1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} + fastest-levenshtein@1.0.16: {} - /fastq@1.17.1: - resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fastq@1.17.1: dependencies: reusify: 1.0.4 - /fdir@6.4.2(picomatch@4.0.2): - resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} - peerDependencies: - picomatch: ^3 || ^4 - peerDependenciesMeta: - picomatch: - optional: true + fdir@6.4.2(picomatch@4.0.2): dependencies: picomatch: 4.0.2 - dev: true - /figures@3.2.0: - resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} - engines: {node: '>=8'} + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 - dev: true - /file-entry-cache@6.0.1: - resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} - engines: {node: ^10.12.0 || >=12.0.0} + file-entry-cache@6.0.1: dependencies: flat-cache: 3.2.0 - /file-entry-cache@7.0.2: - resolution: {integrity: sha512-TfW7/1iI4Cy7Y8L6iqNdZQVvdXn0f8B4QcIXmkIbtTIe/Okm/nSlHb4IwGzRVOd3WfSieCgvf5cMzEfySAIl0g==} - engines: {node: '>=12.0.0'} + file-entry-cache@7.0.2: dependencies: flat-cache: 3.2.0 - /fill-range@7.1.1: - resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} - engines: {node: '>=8'} + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - /find-root@1.1.0: - resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} - dev: false - - /find-up@4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} + find-root@1.1.0: {} + + find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - dev: true - /find-up@5.0.0: - resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} - engines: {node: '>=10'} + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - /find-up@7.0.0: - resolution: {integrity: sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==} - engines: {node: '>=18'} + find-up@7.0.0: dependencies: locate-path: 7.2.0 path-exists: 5.0.0 unicorn-magic: 0.1.0 - dev: true - /flat-cache@3.2.0: - resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} - engines: {node: ^10.12.0 || >=12.0.0} + flat-cache@3.2.0: dependencies: flatted: 3.3.1 keyv: 4.5.4 rimraf: 3.0.2 - /flatted@3.3.1: - resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} + flatted@3.3.1: {} - /follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: false + follow-redirects@1.15.9: {} - /for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.3: dependencies: is-callable: 1.2.7 - /foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} - engines: {node: '>=14'} + foreground-child@3.3.0: dependencies: cross-spawn: 7.0.3 signal-exit: 4.1.0 - dev: true - /form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} + form-data@4.0.0: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 mime-types: 2.1.35 - dev: false - /fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} + fs-extra@10.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.1 - dev: true - /fs-extra@7.0.1: - resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: true - /fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + fs.realpath@1.0.0: {} - /fsevents@2.3.3: - resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} - engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} - os: [darwin] - requiresBuild: true + fsevents@2.3.3: optional: true - /function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} - engines: {node: '>= 0.4'} + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 functions-have-names: 1.2.3 - /functions-have-names@1.2.3: - resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + functions-have-names@1.2.3: {} - /gensync@1.0.0-beta.2: - resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} - engines: {node: '>=6.9.0'} - dev: true + gensync@1.0.0-beta.2: {} - /get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - dev: true + get-caller-file@2.0.5: {} - /get-css-data@2.1.1: - resolution: {integrity: sha512-JpMa/f5P4mDXKg6l5/2cHL5xNY77Jap7tHyduMa6BF0E2a7bQ6Tvaz2BIMjeVYZYLcmOZ5w2Ro0yVJEI41tMbw==} - dev: false + get-css-data@2.1.1: {} - /get-east-asian-width@1.2.0: - resolution: {integrity: sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==} - engines: {node: '>=18'} - dev: true + get-east-asian-width@1.2.0: {} - /get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} + get-intrinsic@1.2.4: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 @@ -4427,50 +7014,33 @@ packages: has-symbols: 1.0.3 hasown: 2.0.2 - /get-stream@8.0.1: - resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} - engines: {node: '>=16'} - dev: true + get-stream@8.0.1: {} - /get-symbol-description@1.0.2: - resolution: {integrity: sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==} - engines: {node: '>= 0.4'} + get-symbol-description@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 - /get-tsconfig@4.8.0: - resolution: {integrity: sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==} + get-tsconfig@4.8.0: dependencies: resolve-pkg-maps: 1.0.0 - /git-raw-commits@4.0.0: - resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} - engines: {node: '>=16'} - hasBin: true + git-raw-commits@4.0.0: dependencies: dargs: 8.1.0 meow: 12.1.1 split2: 4.2.0 - dev: true - /glob-parent@5.1.2: - resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} - engines: {node: '>= 6'} + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - /glob-parent@6.0.2: - resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} - engines: {node: '>=10.13.0'} + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - /glob@11.0.0: - resolution: {integrity: sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==} - engines: {node: 20 || >=22} - hasBin: true + glob@11.0.0: dependencies: foreground-child: 3.3.0 jackspeak: 4.0.1 @@ -4478,11 +7048,8 @@ packages: minipass: 7.1.2 package-json-from-dist: 1.0.0 path-scurry: 2.0.0 - dev: true - /glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -4491,47 +7058,32 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /global-directory@4.0.1: - resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} - engines: {node: '>=18'} + global-directory@4.0.1: dependencies: ini: 4.1.1 - dev: true - /global-modules@2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} + global-modules@2.0.0: dependencies: global-prefix: 3.0.0 - /global-prefix@3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} + global-prefix@3.0.0: dependencies: ini: 1.3.8 kind-of: 6.0.3 which: 1.3.1 - /globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} + globals@11.12.0: {} - /globals@13.24.0: - resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} - engines: {node: '>=8'} + globals@13.24.0: dependencies: type-fest: 0.20.2 - /globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} + globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.0.1 - /globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 @@ -4540,223 +7092,134 @@ packages: merge2: 1.4.1 slash: 3.0.0 - /globjoin@0.1.4: - resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + globjoin@0.1.4: {} - /gopd@1.0.1: - resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + gopd@1.0.1: dependencies: get-intrinsic: 1.2.4 - /graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + graceful-fs@4.2.11: {} - /graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + graphemer@1.4.0: {} - /hammerjs@2.0.8: - resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==} - engines: {node: '>=0.8.0'} - dev: false + hammerjs@2.0.8: {} - /hard-rejection@2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} + hard-rejection@2.1.0: {} - /has-bigints@1.0.2: - resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-bigints@1.0.2: {} - /has-flag@3.0.0: - resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} - engines: {node: '>=4'} + has-flag@3.0.0: {} - /has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} + has-flag@4.0.0: {} - /has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.0 - /has-proto@1.0.3: - resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} - engines: {node: '>= 0.4'} + has-proto@1.0.3: {} - /has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} - engines: {node: '>= 0.4'} + has-symbols@1.0.3: {} - /has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: dependencies: has-symbols: 1.0.3 - /hash-base@3.0.4: - resolution: {integrity: sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==} - engines: {node: '>=4'} + hash-base@3.0.4: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /hash-base@3.1.0: - resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} - engines: {node: '>=4'} + hash-base@3.1.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 safe-buffer: 5.2.1 - dev: true - /hash.js@1.1.7: - resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + hash.js@1.1.7: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: true - /hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} + hasown@2.0.2: dependencies: function-bind: 1.1.2 - /hmac-drbg@1.0.1: - resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: true - /hoist-non-react-statics@3.3.2: - resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 - dev: false - /hosted-git-info@2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - dev: true + hosted-git-info@2.8.9: {} - /hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} + hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 - /html-tags@3.3.1: - resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} - engines: {node: '>=8'} + html-tags@3.3.1: {} - /htmlparser2@8.0.2: - resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.1.0 entities: 4.5.0 - dev: false - /https-browserify@1.0.0: - resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} - dev: true + https-browserify@1.0.0: {} - /human-id@1.0.2: - resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} - dev: true + human-id@1.0.2: {} - /human-signals@5.0.0: - resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} - engines: {node: '>=16.17.0'} - dev: true + human-signals@5.0.0: {} - /husky@9.1.5: - resolution: {integrity: sha512-rowAVRUBfI0b4+niA4SJMhfQwc107VLkBUgEYYAOQAbqDCnra1nYh83hF/MDmhYs9t9n1E3DuKOrs2LYNC+0Ag==} - engines: {node: '>=18'} - hasBin: true - dev: true + husky@9.1.5: {} - /iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - dev: true - /iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - requiresBuild: true + iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 optional: true - /ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - dev: true + ieee754@1.2.1: {} - /ignore@5.3.2: - resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} - engines: {node: '>= 4'} + ignore@5.3.2: {} - /image-size@0.5.5: - resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} - engines: {node: '>=0.10.0'} - hasBin: true - requiresBuild: true + image-size@0.5.5: optional: true - /immer@10.1.1: - resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} - dev: false + immer@10.1.1: {} - /import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-lazy@4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} + import-lazy@4.0.0: {} - /import-meta-resolve@4.1.0: - resolution: {integrity: sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==} - dev: true + import-meta-resolve@4.1.0: {} - /imurmurhash@0.1.4: - resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} - engines: {node: '>=0.8.19'} + imurmurhash@0.1.4: {} - /indent-string@5.0.0: - resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} - engines: {node: '>=12'} + indent-string@5.0.0: {} - /inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - /inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + inherits@2.0.4: {} - /ini@1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@1.3.8: {} - /ini@4.1.1: - resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - dev: true + ini@4.1.1: {} - /inquirer@8.2.6: - resolution: {integrity: sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==} - engines: {node: '>=12.0.0'} + inquirer@8.2.6: dependencies: ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -4773,462 +7236,277 @@ packages: strip-ansi: 6.0.1 through: 2.3.8 wrap-ansi: 6.2.0 - dev: true - /internal-slot@1.0.7: - resolution: {integrity: sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==} - engines: {node: '>= 0.4'} + internal-slot@1.0.7: dependencies: es-errors: 1.3.0 hasown: 2.0.2 side-channel: 1.0.6 - /intersection-observer@0.12.2: - resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} - dev: false + intersection-observer@0.12.2: {} - /intl-format-cache@4.3.1: - resolution: {integrity: sha512-OEUYNA7D06agqPOYhbTkl0T8HA3QKSuwWh1HiClEnpd9vw7N+3XsQt5iZ0GUEchp5CW1fQk/tary+NsbF3yQ1Q==} - dev: false + intl-format-cache@4.3.1: {} - /intl-messageformat-parser@3.6.4: - resolution: {integrity: sha512-RgPGwue0mJtoX2Ax8EmMzJzttxjnva7gx0Q7mKJ4oALrTZvtmCeAw5Msz2PcjW4dtCh/h7vN/8GJCxZO1uv+OA==} - deprecated: We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser + intl-messageformat-parser@3.6.4: dependencies: '@formatjs/intl-unified-numberformat': 3.3.7 - dev: false - /intl-messageformat@7.8.4: - resolution: {integrity: sha512-yS0cLESCKCYjseCOGXuV4pxJm/buTfyCJ1nzQjryHmSehlptbZbn9fnlk1I9peLopZGGbjj46yHHiTAEZ1qOTA==} + intl-messageformat@7.8.4: dependencies: intl-format-cache: 4.3.1 intl-messageformat-parser: 3.6.4 - dev: false - /invariant@2.2.4: - resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + invariant@2.2.4: dependencies: loose-envify: 1.4.0 - dev: false - /is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} + is-arguments@1.1.1: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-array-buffer@3.0.4: - resolution: {integrity: sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==} - engines: {node: '>= 0.4'} + is-array-buffer@3.0.4: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - /is-arrayish@0.2.1: - resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-arrayish@0.2.1: {} - /is-async-function@2.0.0: - resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==} - engines: {node: '>= 0.4'} + is-async-function@2.0.0: dependencies: has-tostringtag: 1.0.2 - dev: false - /is-bigint@1.0.4: - resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - /is-binary-path@2.1.0: - resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} - engines: {node: '>=8'} + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 - dev: true - /is-boolean-object@1.1.2: - resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} - engines: {node: '>= 0.4'} + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-builtin-module@3.2.1: - resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} - engines: {node: '>=6'} + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 - dev: true - /is-bun-module@1.2.1: - resolution: {integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==} + is-bun-module@1.2.1: dependencies: semver: 7.6.3 - dev: false - /is-callable@1.2.7: - resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} - engines: {node: '>= 0.4'} + is-callable@1.2.7: {} - /is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} + is-core-module@2.15.1: dependencies: hasown: 2.0.2 - /is-data-view@1.0.1: - resolution: {integrity: sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==} - engines: {node: '>= 0.4'} + is-data-view@1.0.1: dependencies: is-typed-array: 1.1.13 - /is-date-object@1.0.5: - resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} - engines: {node: '>= 0.4'} + is-date-object@1.0.5: dependencies: has-tostringtag: 1.0.2 - /is-extglob@2.1.1: - resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} - engines: {node: '>=0.10.0'} + is-extglob@2.1.1: {} - /is-finalizationregistry@1.0.2: - resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==} + is-finalizationregistry@1.0.2: dependencies: call-bind: 1.0.7 - dev: false - /is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} + is-fullwidth-code-point@3.0.0: {} - /is-fullwidth-code-point@4.0.0: - resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} - engines: {node: '>=12'} - dev: true + is-fullwidth-code-point@4.0.0: {} - /is-fullwidth-code-point@5.0.0: - resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} - engines: {node: '>=18'} + is-fullwidth-code-point@5.0.0: dependencies: get-east-asian-width: 1.2.0 - dev: true - /is-generator-function@1.0.10: - resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} - engines: {node: '>= 0.4'} + is-generator-function@1.0.10: dependencies: has-tostringtag: 1.0.2 - /is-glob@4.0.3: - resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} - engines: {node: '>=0.10.0'} + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - /is-interactive@1.0.0: - resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} - engines: {node: '>=8'} - dev: true + is-interactive@1.0.0: {} - /is-map@2.0.3: - resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} - engines: {node: '>= 0.4'} - dev: false + is-map@2.0.3: {} - /is-module@1.0.0: - resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} - dev: true + is-module@1.0.0: {} - /is-nan@1.3.2: - resolution: {integrity: sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==} - engines: {node: '>= 0.4'} + is-nan@1.3.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - dev: true - /is-negative-zero@2.0.3: - resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} - engines: {node: '>= 0.4'} + is-negative-zero@2.0.3: {} - /is-number-object@1.0.7: - resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} - engines: {node: '>= 0.4'} + is-number-object@1.0.7: dependencies: has-tostringtag: 1.0.2 - /is-number@7.0.0: - resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} - engines: {node: '>=0.12.0'} + is-number@7.0.0: {} - /is-obj@2.0.0: - resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} - engines: {node: '>=8'} + is-obj@2.0.0: {} - /is-path-inside@3.0.3: - resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} - engines: {node: '>=8'} + is-path-inside@3.0.3: {} - /is-plain-obj@1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} + is-plain-obj@1.1.0: {} - /is-plain-object@5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} + is-plain-object@5.0.0: {} - /is-reference@1.2.1: - resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@1.2.1: dependencies: '@types/estree': 1.0.6 - dev: true - /is-regex@1.1.4: - resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} - engines: {node: '>= 0.4'} + is-regex@1.1.4: dependencies: call-bind: 1.0.7 has-tostringtag: 1.0.2 - /is-set@2.0.3: - resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} - engines: {node: '>= 0.4'} - dev: false + is-set@2.0.3: {} - /is-shared-array-buffer@1.0.3: - resolution: {integrity: sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==} - engines: {node: '>= 0.4'} + is-shared-array-buffer@1.0.3: dependencies: call-bind: 1.0.7 - /is-stream@3.0.0: - resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + is-stream@3.0.0: {} - /is-string@1.0.7: - resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} - engines: {node: '>= 0.4'} + is-string@1.0.7: dependencies: has-tostringtag: 1.0.2 - /is-subdir@1.2.0: - resolution: {integrity: sha512-2AT6j+gXe/1ueqbW6fLZJiIw3F8iXGJtt0yDrZaBhAZEG1raiTxKWU+IPqMCzQAXOUCKdA4UDMgacKH25XG2Cw==} - engines: {node: '>=4'} + is-subdir@1.2.0: dependencies: better-path-resolve: 1.0.0 - dev: true - /is-symbol@1.0.4: - resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} - engines: {node: '>= 0.4'} + is-symbol@1.0.4: dependencies: has-symbols: 1.0.3 - /is-text-path@2.0.0: - resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} - engines: {node: '>=8'} + is-text-path@2.0.0: dependencies: text-extensions: 2.4.0 - dev: true - /is-typed-array@1.1.13: - resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} - engines: {node: '>= 0.4'} + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 - /is-unicode-supported@0.1.0: - resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} - engines: {node: '>=10'} - dev: true + is-unicode-supported@0.1.0: {} - /is-weakmap@2.0.2: - resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} - engines: {node: '>= 0.4'} - dev: false + is-weakmap@2.0.2: {} - /is-weakref@1.0.2: - resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + is-weakref@1.0.2: dependencies: call-bind: 1.0.7 - /is-weakset@2.0.3: - resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} - engines: {node: '>= 0.4'} + is-weakset@2.0.3: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 - dev: false - /is-what@3.14.1: - resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + is-what@3.14.1: {} - /is-windows@1.0.2: - resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} - engines: {node: '>=0.10.0'} - dev: true + is-windows@1.0.2: {} - /isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - dev: true + isarray@1.0.0: {} - /isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + isarray@2.0.5: {} - /isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@2.0.0: {} - /isomorphic-timers-promises@1.0.1: - resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} - engines: {node: '>=10'} - dev: true + isomorphic-timers-promises@1.0.1: {} - /iterator.prototype@1.1.2: - resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==} + iterator.prototype@1.1.2: dependencies: define-properties: 1.2.1 get-intrinsic: 1.2.4 has-symbols: 1.0.3 reflect.getprototypeof: 1.0.6 set-function-name: 2.0.2 - dev: false - /jackspeak@4.0.1: - resolution: {integrity: sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==} - engines: {node: 20 || >=22} + jackspeak@4.0.1: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true - /jiti@1.21.6: - resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} - hasBin: true - dev: true + jiti@1.21.6: {} - /js-cookie@3.0.5: - resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} - engines: {node: '>=14'} - dev: false + js-cookie@3.0.5: {} - /js-tokens@4.0.0: - resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@4.0.0: {} - /js-tokens@9.0.0: - resolution: {integrity: sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==} - dev: false + js-tokens@9.0.0: {} - /js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} - hasBin: true + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true - /js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - /jsesc@2.5.2: - resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} - engines: {node: '>=4'} - hasBin: true + jsesc@2.5.2: {} - /json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-buffer@3.0.1: {} - /json-parse-better-errors@1.0.2: - resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} - dev: true + json-parse-better-errors@1.0.2: {} - /json-parse-even-better-errors@2.3.1: - resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-parse-even-better-errors@2.3.1: {} - /json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@0.4.1: {} - /json-schema-traverse@1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + json-schema-traverse@1.0.0: {} - /json-stable-stringify-without-jsonify@1.0.1: - resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json-stable-stringify-without-jsonify@1.0.1: {} - /json5@1.0.2: - resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} - hasBin: true + json5@1.0.2: dependencies: minimist: 1.2.8 - dev: false - /json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - dev: true + json5@2.2.3: {} - /jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonfile@6.1.0: - resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + jsonfile@6.1.0: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 - dev: true - /jsonparse@1.3.1: - resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} - engines: {'0': node >= 0.2.0} - dev: true + jsonparse@1.3.1: {} - /jsx-ast-utils@3.3.5: - resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} - engines: {node: '>=4.0'} + jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.8 array.prototype.flat: 1.3.2 object.assign: 4.1.5 object.values: 1.2.0 - dev: false - /keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + keyv@4.5.4: dependencies: json-buffer: 3.0.1 - /kind-of@6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} + kind-of@6.0.3: {} - /known-css-properties@0.29.0: - resolution: {integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==} + known-css-properties@0.29.0: {} - /language-subtag-registry@0.3.23: - resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} - dev: false + language-subtag-registry@0.3.23: {} - /language-tags@1.0.9: - resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} - engines: {node: '>=0.10'} + language-tags@1.0.9: dependencies: language-subtag-registry: 0.3.23 - dev: false - /less@4.2.0: - resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} - engines: {node: '>=6'} - hasBin: true + less@4.2.0: dependencies: copy-anything: 2.0.6 parse-node-version: 1.0.1 @@ -5242,25 +7520,16 @@ packages: needle: 3.3.1 source-map: 0.6.1 - /levn@0.4.1: - resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} - engines: {node: '>= 0.8.0'} + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - /lilconfig@3.1.2: - resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} - engines: {node: '>=14'} - dev: true + lilconfig@3.1.2: {} - /lines-and-columns@1.2.4: - resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + lines-and-columns@1.2.4: {} - /lint-staged@15.2.10: - resolution: {integrity: sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==} - engines: {node: '>=18.12.0'} - hasBin: true + lint-staged@15.2.10: dependencies: chalk: 5.3.0 commander: 12.1.0 @@ -5274,11 +7543,8 @@ packages: yaml: 2.5.1 transitivePeerDependencies: - supports-color - dev: true - /listr2@8.2.4: - resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} - engines: {node: '>=18.0.0'} + listr2@8.2.4: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 @@ -5286,185 +7552,115 @@ packages: log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 9.0.0 - dev: true - /load-json-file@4.0.0: - resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} - engines: {node: '>=4'} + load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 parse-json: 4.0.0 pify: 3.0.0 strip-bom: 3.0.0 - dev: true - /locate-path@5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 - dev: true - /locate-path@6.0.0: - resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} - engines: {node: '>=10'} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - /locate-path@7.2.0: - resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + locate-path@7.2.0: dependencies: p-locate: 6.0.0 - dev: true - /lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} + lodash-es@4.17.21: {} - /lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - dev: true + lodash.camelcase@4.3.0: {} - /lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - dev: true + lodash.isplainobject@4.0.6: {} - /lodash.kebabcase@4.1.1: - resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} - dev: true + lodash.kebabcase@4.1.1: {} - /lodash.merge@4.6.2: - resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash.merge@4.6.2: {} - /lodash.mergewith@4.6.2: - resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} - dev: true + lodash.mergewith@4.6.2: {} - /lodash.snakecase@4.1.1: - resolution: {integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==} - dev: true + lodash.snakecase@4.1.1: {} - /lodash.startcase@4.4.0: - resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} - dev: true + lodash.startcase@4.4.0: {} - /lodash.truncate@4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + lodash.truncate@4.4.2: {} - /lodash.uniq@4.5.0: - resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} - dev: true + lodash.uniq@4.5.0: {} - /lodash.upperfirst@4.3.1: - resolution: {integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==} - dev: true + lodash.upperfirst@4.3.1: {} - /lodash@4.17.21: - resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lodash@4.17.21: {} - /log-symbols@4.1.0: - resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} - engines: {node: '>=10'} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 is-unicode-supported: 0.1.0 - dev: true - /log-update@6.1.0: - resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} - engines: {node: '>=18'} + log-update@6.1.0: dependencies: ansi-escapes: 7.0.0 cli-cursor: 5.0.0 slice-ansi: 7.1.0 strip-ansi: 7.1.0 wrap-ansi: 9.0.0 - dev: true - /loose-envify@1.4.0: - resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} - hasBin: true + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - /lower-case@2.0.2: - resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + lower-case@2.0.2: dependencies: tslib: 2.7.0 - dev: true - /lru-cache@11.0.1: - resolution: {integrity: sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==} - engines: {node: 20 || >=22} - dev: true + lru-cache@11.0.1: {} - /lru-cache@4.1.5: - resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: true - /lru-cache@5.1.1: - resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - dev: true - /lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + magic-string@0.30.11: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - /make-dir@2.1.0: - resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} - engines: {node: '>=6'} - requiresBuild: true + make-dir@2.1.0: dependencies: pify: 4.0.1 semver: 5.7.2 optional: true - /map-obj@1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} + map-obj@1.0.1: {} - /map-obj@4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} + map-obj@4.3.0: {} - /material-colors@1.2.6: - resolution: {integrity: sha512-6qE4B9deFBIa9YSpOc9O0Sgc43zTeVYbgDT5veRKSlB2+ZuHNoVVxA1L/ckMUayV9Ay9y7Z/SZCLcGteW9i7bg==} - dev: true + material-colors@1.2.6: {} - /mathml-tag-names@2.1.3: - resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + mathml-tag-names@2.1.3: {} - /md5.js@1.3.5: - resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + md5.js@1.3.5: dependencies: hash-base: 3.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /mdn-data@2.0.30: - resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + mdn-data@2.0.30: {} - /memorystream@0.3.1: - resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} - engines: {node: '>= 0.10.0'} - dev: true + memorystream@0.3.1: {} - /meow@10.1.5: - resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + meow@10.1.5: dependencies: '@types/minimist': 1.2.5 camelcase-keys: 7.0.2 @@ -5479,163 +7675,91 @@ packages: type-fest: 1.4.0 yargs-parser: 20.2.9 - /meow@12.1.1: - resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} - engines: {node: '>=16.10'} - dev: true + meow@12.1.1: {} - /merge-stream@2.0.0: - resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} - dev: true + merge-stream@2.0.0: {} - /merge2@1.4.1: - resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} - engines: {node: '>= 8'} + merge2@1.4.1: {} - /micromatch@4.0.8: - resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} - engines: {node: '>=8.6'} + micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.1 - /miller-rabin@4.0.1: - resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} - hasBin: true + miller-rabin@4.0.1: dependencies: bn.js: 4.12.0 brorand: 1.1.0 - dev: true - /mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - dev: false + mime-db@1.52.0: {} - /mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - dev: false - /mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - requiresBuild: true + mime@1.6.0: optional: true - /mimic-fn@2.1.0: - resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} - engines: {node: '>=6'} - dev: true + mimic-fn@2.1.0: {} - /mimic-fn@4.0.0: - resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} - engines: {node: '>=12'} - dev: true + mimic-fn@4.0.0: {} - /mimic-function@5.0.1: - resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} - engines: {node: '>=18'} - dev: true + mimic-function@5.0.1: {} - /min-indent@1.0.1: - resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} - engines: {node: '>=4'} + min-indent@1.0.1: {} - /minimalistic-assert@1.0.1: - resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - dev: true + minimalistic-assert@1.0.1: {} - /minimalistic-crypto-utils@1.0.1: - resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} - dev: true + minimalistic-crypto-utils@1.0.1: {} - /minimatch@10.0.1: - resolution: {integrity: sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==} - engines: {node: 20 || >=22} + minimatch@10.0.1: dependencies: brace-expansion: 2.0.1 - dev: true - /minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - /minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 - dev: false - /minimist-options@4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} + minimist-options@4.1.0: dependencies: arrify: 1.0.1 is-plain-obj: 1.1.0 kind-of: 6.0.3 - /minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimist@1.2.8: {} - /minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - dev: true + minipass@7.1.2: {} - /mri@1.2.0: - resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} - engines: {node: '>=4'} - dev: true + mri@1.2.0: {} - /ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + ms@2.1.3: {} - /mute-stream@0.0.8: - resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} - dev: true + mute-stream@0.0.8: {} - /nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true + nanoid@3.3.7: {} - /natural-compare@1.4.0: - resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + natural-compare@1.4.0: {} - /needle@3.3.1: - resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} - engines: {node: '>= 4.4.x'} - hasBin: true - requiresBuild: true + needle@3.3.1: dependencies: iconv-lite: 0.6.3 sax: 1.4.1 optional: true - /nice-try@1.0.5: - resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} - dev: true + nice-try@1.0.5: {} - /no-case@3.0.4: - resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + no-case@3.0.4: dependencies: lower-case: 2.0.2 tslib: 2.7.0 - dev: true - /node-releases@2.0.18: - resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} - dev: true + node-releases@2.0.18: {} - /node-stdlib-browser@1.2.0: - resolution: {integrity: sha512-VSjFxUhRhkyed8AtLwSCkMrJRfQ3e2lGtG3sP6FEgaLKBBbxM/dLfjRe1+iLhjvyLFW3tBQ8+c0pcOtXGbAZJg==} - engines: {node: '>=10'} + node-stdlib-browser@1.2.0: dependencies: assert: 2.1.0 browser-resolve: 2.0.0 @@ -5664,34 +7788,24 @@ packages: url: 0.11.4 util: 0.12.5 vm-browserify: 1.1.2 - dev: true - /normalize-package-data@2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 resolve: 1.22.8 semver: 5.7.2 validate-npm-package-license: 3.0.4 - dev: true - /normalize-package-data@3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} + normalize-package-data@3.0.3: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.15.1 semver: 7.6.3 validate-npm-package-license: 3.0.4 - /normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} + normalize-path@3.0.0: {} - /npm-run-all@4.1.5: - resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} - engines: {node: '>= 4'} - hasBin: true + npm-run-all@4.1.5: dependencies: ansi-styles: 3.2.1 chalk: 2.4.2 @@ -5702,109 +7816,71 @@ packages: read-pkg: 3.0.0 shell-quote: 1.8.1 string.prototype.padend: 3.1.6 - dev: true - /npm-run-path@5.3.0: - resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 - dev: true - /object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} + object-assign@4.1.1: {} - /object-inspect@1.13.2: - resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} - engines: {node: '>= 0.4'} + object-inspect@1.13.2: {} - /object-is@1.1.6: - resolution: {integrity: sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==} - engines: {node: '>= 0.4'} + object-is@1.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - /object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} + object-keys@1.1.1: {} - /object.assign@4.1.5: - resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==} - engines: {node: '>= 0.4'} + object.assign@4.1.5: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - /object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} - engines: {node: '>= 0.4'} + object.entries@1.1.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: false - /object.fromentries@2.0.8: - resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} - engines: {node: '>= 0.4'} + object.fromentries@2.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: false - /object.groupby@1.0.3: - resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} - engines: {node: '>= 0.4'} + object.groupby@1.0.3: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 - dev: false - /object.values@1.2.0: - resolution: {integrity: sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==} - engines: {node: '>= 0.4'} + object.values@1.2.0: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - dev: false - /once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + once@1.4.0: dependencies: wrappy: 1.0.2 - /onetime@5.1.2: - resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} - engines: {node: '>=6'} + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - dev: true - /onetime@6.0.0: - resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} - engines: {node: '>=12'} + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - dev: true - /onetime@7.0.0: - resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} - engines: {node: '>=18'} + onetime@7.0.0: dependencies: mimic-function: 5.0.1 - dev: true - /optionator@0.9.4: - resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} - engines: {node: '>= 0.8.0'} + optionator@0.9.4: dependencies: deep-is: 0.1.4 fast-levenshtein: 2.0.6 @@ -5813,9 +7889,7 @@ packages: type-check: 0.4.0 word-wrap: 1.2.5 - /ora@5.4.1: - resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} - engines: {node: '>=10'} + ora@5.4.1: dependencies: bl: 4.1.0 chalk: 4.1.2 @@ -5826,106 +7900,61 @@ packages: log-symbols: 4.1.0 strip-ansi: 6.0.1 wcwidth: 1.0.1 - dev: true - /os-browserify@0.3.0: - resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} - dev: true + os-browserify@0.3.0: {} - /os-tmpdir@1.0.2: - resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} - engines: {node: '>=0.10.0'} - dev: true + os-tmpdir@1.0.2: {} - /outdent@0.5.0: - resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - dev: true + outdent@0.5.0: {} - /p-filter@2.1.0: - resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} - engines: {node: '>=8'} + p-filter@2.1.0: dependencies: p-map: 2.1.0 - dev: true - /p-limit@2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} + p-limit@2.3.0: dependencies: p-try: 2.2.0 - dev: true - /p-limit@3.1.0: - resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} - engines: {node: '>=10'} + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - /p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@4.0.0: dependencies: yocto-queue: 1.1.1 - dev: true - /p-locate@4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} + p-locate@4.1.0: dependencies: p-limit: 2.3.0 - dev: true - /p-locate@5.0.0: - resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} - engines: {node: '>=10'} + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - /p-locate@6.0.0: - resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@6.0.0: dependencies: p-limit: 4.0.0 - dev: true - /p-map@2.1.0: - resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} - engines: {node: '>=6'} - dev: true + p-map@2.1.0: {} - /p-try@2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true + p-try@2.2.0: {} - /package-json-from-dist@1.0.0: - resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} - dev: true + package-json-from-dist@1.0.0: {} - /package-manager-detector@0.2.0: - resolution: {integrity: sha512-E385OSk9qDcXhcM9LNSe4sdhx8a9mAPrZ4sMLW+tmxl5ZuGtPUcdFu+MPP2jbgiWAZ6Pfe5soGFMd+0Db5Vrog==} - dev: true + package-manager-detector@0.2.0: {} - /pako@1.0.11: - resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - dev: true + pako@1.0.11: {} - /param-case@3.0.4: - resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + param-case@3.0.4: dependencies: dot-case: 3.0.4 tslib: 2.7.0 - dev: true - /parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-asn1@5.1.7: - resolution: {integrity: sha512-CTM5kuWR3sx9IFamcl5ErfPl6ea/N8IYwiJ+vpeB2g+1iknv7zBl5uPwbMbRVznRVbrNY6lGuDoE5b30grmbqg==} - engines: {node: '>= 0.10'} + parse-asn1@5.1.7: dependencies: asn1.js: 4.10.1 browserify-aes: 1.2.0 @@ -5933,277 +7962,155 @@ packages: hash-base: 3.0.4 pbkdf2: 3.1.2 safe-buffer: 5.2.1 - dev: true - /parse-json@4.0.0: - resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} - engines: {node: '>=4'} + parse-json@4.0.0: dependencies: error-ex: 1.3.2 json-parse-better-errors: 1.0.2 - dev: true - /parse-json@5.2.0: - resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} - engines: {node: '>=8'} + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.24.7 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parse-node-version@1.0.1: - resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} - engines: {node: '>= 0.10'} + parse-node-version@1.0.1: {} - /pascal-case@3.1.2: - resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + pascal-case@3.1.2: dependencies: no-case: 3.0.4 tslib: 2.7.0 - dev: true - /path-browserify@1.0.1: - resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} - dev: true + path-browserify@1.0.1: {} - /path-exists@4.0.0: - resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} - engines: {node: '>=8'} + path-exists@4.0.0: {} - /path-exists@5.0.0: - resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: true + path-exists@5.0.0: {} - /path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} + path-is-absolute@1.0.1: {} - /path-key@2.0.1: - resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} - engines: {node: '>=4'} - dev: true + path-key@2.0.1: {} - /path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} + path-key@3.1.1: {} - /path-key@4.0.0: - resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} - engines: {node: '>=12'} - dev: true + path-key@4.0.0: {} - /path-parse@1.0.7: - resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-parse@1.0.7: {} - /path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} - engines: {node: 20 || >=22} + path-scurry@2.0.0: dependencies: lru-cache: 11.0.1 minipass: 7.1.2 - dev: true - /path-type@3.0.0: - resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} - engines: {node: '>=4'} + path-type@3.0.0: dependencies: pify: 3.0.0 - dev: true - /path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} + path-type@4.0.0: {} - /pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} - engines: {node: '>=0.12'} + pbkdf2@3.1.2: dependencies: create-hash: 1.2.0 create-hmac: 1.1.7 ripemd160: 2.0.2 safe-buffer: 5.2.1 sha.js: 2.4.11 - dev: true - - /picocolors@1.1.0: - resolution: {integrity: sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==} - /picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} - engines: {node: '>=8.6'} + picocolors@1.1.0: {} - /picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} - engines: {node: '>=12'} - dev: true + picomatch@2.3.1: {} - /pidtree@0.3.1: - resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} - engines: {node: '>=0.10'} - hasBin: true - dev: true + picomatch@4.0.2: {} - /pidtree@0.6.0: - resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} - engines: {node: '>=0.10'} - hasBin: true - dev: true + pidtree@0.3.1: {} - /pify@3.0.0: - resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} - engines: {node: '>=4'} - dev: true + pidtree@0.6.0: {} - /pify@4.0.1: - resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} - engines: {node: '>=6'} + pify@3.0.0: {} - /pkg-dir@5.0.0: - resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} - engines: {node: '>=10'} + pify@4.0.1: {} + + pkg-dir@5.0.0: dependencies: find-up: 5.0.0 - dev: true - /possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} - engines: {node: '>= 0.4'} + possible-typed-array-names@1.0.0: {} - /postcss-html@1.7.0: - resolution: {integrity: sha512-MfcMpSUIaR/nNgeVS8AyvyDugXlADjN9AcV7e5rDfrF1wduIAGSkL4q2+wgrZgA3sHVAHLDO9FuauHhZYW2nBw==} - engines: {node: ^12 || >=14} + postcss-html@1.7.0: dependencies: htmlparser2: 8.0.2 js-tokens: 9.0.0 postcss: 8.4.47 postcss-safe-parser: 6.0.0(postcss@8.4.47) - dev: false - /postcss-less@6.0.0(postcss@8.4.45): - resolution: {integrity: sha512-FPX16mQLyEjLzEuuJtxA8X3ejDLNGGEG503d2YGZR5Ask1SpDN8KmZUMpzCvyalWRywAn1n1VOA5dcqfCLo5rg==} - engines: {node: '>=12'} - peerDependencies: - postcss: ^8.3.5 + postcss-less@6.0.0(postcss@8.4.45): dependencies: postcss: 8.4.45 - dev: false - /postcss-resolve-nested-selector@0.1.6: - resolution: {integrity: sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==} + postcss-resolve-nested-selector@0.1.6: {} - /postcss-safe-parser@6.0.0(postcss@8.4.47): - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.3.3 + postcss-safe-parser@6.0.0(postcss@8.4.47): dependencies: postcss: 8.4.47 - /postcss-scss@4.0.9(postcss@8.4.45): - resolution: {integrity: sha512-AjKOeiwAitL/MXxQW2DliT28EKukvvbEWx3LBmJIRN8KfBGZbRTxNYW0kSqi1COiTZ57nZ9NW06S6ux//N1c9A==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.4.29 + postcss-scss@4.0.9(postcss@8.4.45): dependencies: postcss: 8.4.45 - dev: false - /postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} - engines: {node: '>=4'} + postcss-selector-parser@6.1.2: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-sorting@8.0.2(postcss@8.4.47): - resolution: {integrity: sha512-M9dkSrmU00t/jK7rF6BZSZauA5MAaBW4i5EnJXspMwt4iqTh/L9j6fgMnbElEOfyRyfLfVbIHj/R52zHzAPe1Q==} - peerDependencies: - postcss: ^8.4.20 + postcss-sorting@8.0.2(postcss@8.4.47): dependencies: postcss: 8.4.47 - dev: false - /postcss-value-parser@4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss-value-parser@4.2.0: {} - /postcss@8.4.45: - resolution: {integrity: sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.45: dependencies: nanoid: 3.3.7 picocolors: 1.1.0 source-map-js: 1.2.1 - dev: false - /postcss@8.4.47: - resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} - engines: {node: ^10 || ^12 || >=14} + postcss@8.4.47: dependencies: nanoid: 3.3.7 picocolors: 1.1.0 source-map-js: 1.2.1 - /prelude-ls@1.2.1: - resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} - engines: {node: '>= 0.8.0'} + prelude-ls@1.2.1: {} - /prettier-linter-helpers@1.0.0: - resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} - engines: {node: '>=6.0.0'} + prettier-linter-helpers@1.0.0: dependencies: fast-diff: 1.3.0 - dev: false - /prettier@2.8.8: - resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} - engines: {node: '>=10.13.0'} - hasBin: true - dev: true + prettier@2.8.8: {} - /prettier@3.3.3: - resolution: {integrity: sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==} - engines: {node: '>=14'} - hasBin: true - dev: false + prettier@3.3.3: {} - /process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - dev: true + process-nextick-args@2.0.1: {} - /process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - dev: true + process@0.11.10: {} - /progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - dev: true + progress@2.0.3: {} - /prop-types@15.8.1: - resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + prop-types@15.8.1: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 react-is: 16.13.1 - /proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - dev: false + proxy-from-env@1.1.0: {} - /prr@1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - requiresBuild: true + prr@1.0.1: optional: true - /pseudomap@1.0.2: - resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} - dev: true + pseudomap@1.0.2: {} - /public-encrypt@4.0.3: - resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + public-encrypt@4.0.3: dependencies: bn.js: 4.12.0 browserify-rsa: 4.1.0 @@ -6211,57 +8118,35 @@ packages: parse-asn1: 5.1.7 randombytes: 2.1.0 safe-buffer: 5.2.1 - dev: true - /punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - dev: true + punycode@1.4.1: {} - /punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@2.3.1: {} - /qs@6.13.0: - resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} - engines: {node: '>=0.6'} + qs@6.13.0: dependencies: side-channel: 1.0.6 - /querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - dev: true + querystring-es3@0.2.1: {} - /queue-microtask@1.2.3: - resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + queue-microtask@1.2.3: {} - /quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} + quick-lru@5.1.1: {} - /randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - dev: true - /randomfill@1.0.4: - resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + randomfill@1.0.4: dependencies: randombytes: 2.1.0 safe-buffer: 5.2.1 - dev: true - /rd@2.0.1: - resolution: {integrity: sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw==} + rd@2.0.1: dependencies: '@types/node': 10.17.60 - dev: true - /react-color@2.19.3(react@18.3.1): - resolution: {integrity: sha512-LEeGE/ZzNLIsFWa1TMe8y5VYqr7bibneWmvJwm1pCn/eNmrabWDh659JSPn9BuaMpEfU83WTOJfnCcjDZwNQTA==} - peerDependencies: - react: '*' + react-color@2.19.3(react@18.3.1): dependencies: '@icons/material': 0.2.4(react@18.3.1) lodash: 4.17.21 @@ -6271,28 +8156,12 @@ packages: react: 18.3.1 reactcss: 1.2.3(react@18.3.1) tinycolor2: 1.6.0 - dev: true - /react-dnd-html5-backend@16.0.1: - resolution: {integrity: sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==} + react-dnd-html5-backend@16.0.1: dependencies: dnd-core: 16.0.1 - dev: false - /react-dnd@16.0.1(@types/node@20.16.5)(@types/react@18.3.5)(react@18.3.1): - resolution: {integrity: sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==} - peerDependencies: - '@types/hoist-non-react-statics': '>= 3.3.1' - '@types/node': '>= 12' - '@types/react': '>= 16' - react: '>= 16.14' - peerDependenciesMeta: - '@types/hoist-non-react-statics': - optional: true - '@types/node': - optional: true - '@types/react': - optional: true + react-dnd@16.0.1(@types/node@20.16.5)(@types/react@18.3.5)(react@18.3.1): dependencies: '@react-dnd/invariant': 4.0.2 '@react-dnd/shallowequal': 4.0.2 @@ -6302,39 +8171,23 @@ packages: fast-deep-equal: 3.1.3 hoist-non-react-statics: 3.3.2 react: 18.3.1 - dev: false - /react-dom@18.3.1(react@18.3.1): - resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} - peerDependencies: - react: ^18.3.1 + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 react: 18.3.1 scheduler: 0.23.2 - dev: false - /react-draggable@4.4.6(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' + react-draggable@4.4.6(react-dom@18.3.1)(react@18.3.1): dependencies: clsx: 1.2.1 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: false - /react-fast-compare@3.2.2: - resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} - dev: false + react-fast-compare@3.2.2: {} - /react-grid-layout@1.5.0(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-WBKX7w/LsTfI99WskSu6nX2nbJAUD7GD6nIXcwYLyPpnslojtmql2oD3I2g5C3AK8hrxIarYT8awhuDIp7iQ5w==} - peerDependencies: - react: '>= 16.3.0' - react-dom: '>= 16.3.0' + react-grid-layout@1.5.0(react-dom@18.3.1)(react@18.3.1): dependencies: clsx: 2.1.1 fast-equals: 4.0.3 @@ -6344,22 +8197,12 @@ packages: react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) react-resizable: 3.0.5(react-dom@18.3.1)(react@18.3.1) resize-observer-polyfill: 1.5.1 - dev: false - /react-hook-form@7.53.0(react@18.3.1): - resolution: {integrity: sha512-M1n3HhqCww6S2hxLxciEXy2oISPnAzxY7gvwVPrtlczTM/1dDadXgUxDpHMrMTblDOcm/AXtXxHwZ3jpg1mqKQ==} - engines: {node: '>=18.0.0'} - peerDependencies: - react: ^16.8.0 || ^17 || ^18 || ^19 + react-hook-form@7.53.0(react@18.3.1): dependencies: react: 18.3.1 - dev: false - /react-intl-universal@2.11.1(react@18.3.1): - resolution: {integrity: sha512-wkmbd7Qohnr8ch02ZzF6uLZAOvdsHbvMjhiS13WUeberdrraOma4bOnn2hUVnEa/LXEvYRD0ta0RBPOV0g2HoA==} - requiresBuild: true - peerDependencies: - react: '*' + react-intl-universal@2.11.1(react@18.3.1): dependencies: cookie: 0.3.1 escape-html: 1.0.3 @@ -6368,60 +8211,34 @@ packages: lodash.merge: 4.6.2 object-keys: 1.1.1 react: 18.3.1 - dev: false - /react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@16.13.1: {} - /react-is@18.3.1: - resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - dev: false + react-is@18.3.1: {} - /react-refresh@0.14.2: - resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} - engines: {node: '>=0.10.0'} - dev: true + react-refresh@0.14.2: {} - /react-resizable@3.0.5(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-vKpeHhI5OZvYn82kXOs1bC8aOXktGU5AmKAgaZS4F5JPburCtbmDPqE7Pzp+1kN4+Wb81LlF33VpGwWwtXem+w==} - peerDependencies: - react: '>= 16.3' + react-resizable@3.0.5(react-dom@18.3.1)(react@18.3.1): dependencies: prop-types: 15.8.1 react: 18.3.1 react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) transitivePeerDependencies: - react-dom - dev: false - /react-router-dom@6.26.1(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' + react-router-dom@6.26.1(react-dom@18.3.1)(react@18.3.1): dependencies: '@remix-run/router': 1.19.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-router: 6.26.1(react@18.3.1) - dev: false - /react-router@6.26.1(react@18.3.1): - resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' + react-router@6.26.1(react@18.3.1): dependencies: '@remix-run/router': 1.19.1 react: 18.3.1 - dev: false - /react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} - peerDependencies: - react: '>=16.6.0' - react-dom: '>=16.6.0' + react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): dependencies: '@babel/runtime': 7.25.6 dom-helpers: 5.2.1 @@ -6429,61 +8246,43 @@ packages: prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - dev: false - /react@18.3.1: - resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} - engines: {node: '>=0.10.0'} + react@18.3.1: dependencies: loose-envify: 1.4.0 - /reactcss@1.2.3(react@18.3.1): - resolution: {integrity: sha512-KiwVUcFu1RErkI97ywr8nvx8dNOpT03rbnma0SSalTYjkrPYaEajR4a/MRt6DZ46K6arDRbWMNHF+xH7G7n/8A==} - peerDependencies: - react: '*' + reactcss@1.2.3(react@18.3.1): dependencies: lodash: 4.17.21 react: 18.3.1 - dev: true - /read-pkg-up@8.0.0: - resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} - engines: {node: '>=12'} + read-pkg-up@8.0.0: dependencies: find-up: 5.0.0 read-pkg: 6.0.0 type-fest: 1.4.0 - /read-pkg@3.0.0: - resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} - engines: {node: '>=4'} + read-pkg@3.0.0: dependencies: load-json-file: 4.0.0 normalize-package-data: 2.5.0 path-type: 3.0.0 - dev: true - /read-pkg@6.0.0: - resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} - engines: {node: '>=12'} + read-pkg@6.0.0: dependencies: '@types/normalize-package-data': 2.4.4 normalize-package-data: 3.0.3 parse-json: 5.2.0 type-fest: 1.4.0 - /read-yaml-file@1.1.0: - resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} - engines: {node: '>=6'} + read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 js-yaml: 3.14.1 pify: 4.0.1 strip-bom: 3.0.0 - dev: true - /readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -6492,40 +8291,27 @@ packages: safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 - dev: true - /readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - dev: true - /readdirp@3.6.0: - resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} - engines: {node: '>=8.10.0'} + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - dev: true - /redent@4.0.0: - resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} - engines: {node: '>=12'} + redent@4.0.0: dependencies: indent-string: 5.0.0 strip-indent: 4.0.0 - /redux@4.2.1: - resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} + redux@4.2.1: dependencies: '@babel/runtime': 7.25.6 - dev: false - /reflect.getprototypeof@1.0.6: - resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==} - engines: {node: '>= 0.4'} + reflect.getprototypeof@1.0.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -6534,124 +8320,75 @@ packages: get-intrinsic: 1.2.4 globalthis: 1.0.4 which-builtin-type: 1.1.4 - dev: false - /regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + regenerator-runtime@0.14.1: {} - /regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-errors: 1.3.0 set-function-name: 2.0.2 - /require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - dev: true + require-directory@2.1.1: {} - /require-from-string@2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} + require-from-string@2.0.2: {} - /reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - dev: false + reselect@5.1.1: {} - /resize-observer-polyfill@1.5.1: - resolution: {integrity: sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==} - dev: false + resize-observer-polyfill@1.5.1: {} - /resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} + resolve-from@4.0.0: {} - /resolve-from@5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} + resolve-from@5.0.0: {} - /resolve-pkg-maps@1.0.0: - resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-pkg-maps@1.0.0: {} - /resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} - hasBin: true + resolve@1.22.8: dependencies: is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /resolve@2.0.0-next.5: - resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} - hasBin: true + resolve@2.0.0-next.5: dependencies: is-core-module: 2.15.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - dev: false - /restore-cursor@3.1.0: - resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} - engines: {node: '>=8'} + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: true - /restore-cursor@5.1.0: - resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} - engines: {node: '>=18'} + restore-cursor@5.1.0: dependencies: onetime: 7.0.0 signal-exit: 4.1.0 - dev: true - /reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} - engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + reusify@1.0.4: {} - /rfdc@1.4.1: - resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - dev: true + rfdc@1.4.1: {} - /rimraf@3.0.2: - resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} - deprecated: Rimraf versions prior to v4 are no longer supported - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - /rimraf@6.0.1: - resolution: {integrity: sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==} - engines: {node: 20 || >=22} - hasBin: true + rimraf@6.0.1: dependencies: glob: 11.0.0 package-json-from-dist: 1.0.0 - dev: true - /ripemd160@2.0.2: - resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + ripemd160@2.0.2: dependencies: hash-base: 3.1.0 inherits: 2.0.4 - dev: true - /rollup-plugin-peer-deps-external@2.2.4(rollup@4.24.0): - resolution: {integrity: sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==} - peerDependencies: - rollup: '*' + rollup-plugin-peer-deps-external@2.2.4(rollup@4.24.0): dependencies: rollup: 4.24.0 - dev: true - /rollup@4.24.0: - resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true + rollup@4.24.0: dependencies: '@types/estree': 1.0.6 optionalDependencies: @@ -6673,83 +8410,51 @@ packages: '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 - /run-async@2.4.1: - resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} - engines: {node: '>=0.12.0'} - dev: true + run-async@2.4.1: {} - /run-parallel@1.2.0: - resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - /rxjs@7.8.1: - resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + rxjs@7.8.1: dependencies: tslib: 2.7.0 - dev: true - /safe-array-concat@1.1.2: - resolution: {integrity: sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==} - engines: {node: '>=0.4'} + safe-array-concat@1.1.2: dependencies: call-bind: 1.0.7 get-intrinsic: 1.2.4 has-symbols: 1.0.3 isarray: 2.0.5 - /safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - dev: true + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - dev: true + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.3: - resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==} - engines: {node: '>= 0.4'} + safe-regex-test@1.0.3: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-regex: 1.1.4 - /safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + safer-buffer@2.1.2: {} - /sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - requiresBuild: true + sax@1.4.1: optional: true - /scheduler@0.23.2: - resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 - dev: false - /screenfull@5.2.0: - resolution: {integrity: sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==} - engines: {node: '>=0.10.0'} - dev: false + screenfull@5.2.0: {} - /semver@5.7.2: - resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} - hasBin: true - requiresBuild: true + semver@5.7.2: {} - /semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true + semver@6.3.1: {} - /semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} - engines: {node: '>=10'} - hasBin: true + semver@7.6.3: {} - /set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 @@ -6758,211 +8463,135 @@ packages: gopd: 1.0.1 has-property-descriptors: 1.0.2 - /set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} + set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 - /setimmediate@1.0.5: - resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - dev: true + setimmediate@1.0.5: {} - /sha.js@2.4.11: - resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} - hasBin: true + sha.js@2.4.11: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: true - /shebang-command@1.2.0: - resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} - engines: {node: '>=0.10.0'} + shebang-command@1.2.0: dependencies: shebang-regex: 1.0.0 - dev: true - /shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - /shebang-regex@1.0.0: - resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} - engines: {node: '>=0.10.0'} - dev: true + shebang-regex@1.0.0: {} - /shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} + shebang-regex@3.0.0: {} - /shell-quote@1.8.1: - resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} - dev: true + shell-quote@1.8.1: {} - /side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} + side-channel@1.0.6: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 get-intrinsic: 1.2.4 object-inspect: 1.13.2 - /signal-exit@3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true + signal-exit@3.0.7: {} - /signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} + signal-exit@4.1.0: {} - /slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} + slash@3.0.0: {} - /slice-ansi@4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - /slice-ansi@5.0.0: - resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} - engines: {node: '>=12'} + slice-ansi@5.0.0: dependencies: ansi-styles: 6.2.1 is-fullwidth-code-point: 4.0.0 - dev: true - /slice-ansi@7.1.0: - resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} - engines: {node: '>=18'} + slice-ansi@7.1.0: dependencies: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 - dev: true - /source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} + source-map-js@1.2.1: {} - /source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - dev: false + source-map@0.5.7: {} - /source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - requiresBuild: true + source-map@0.6.1: optional: true - /spawndamnit@2.0.0: - resolution: {integrity: sha512-j4JKEcncSjFlqIwU5L/rp2N5SIPsdxaRsIv678+TZxZ0SRDJTm8JrxJMjE/XuiEZNEir3S8l0Fa3Ke339WI4qA==} + spawndamnit@2.0.0: dependencies: cross-spawn: 5.1.0 signal-exit: 3.0.7 - dev: true - /spdx-correct@3.2.0: - resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.20 - /spdx-exceptions@2.5.0: - resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + spdx-exceptions@2.5.0: {} - /spdx-expression-parse@3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.20 - /spdx-license-ids@3.0.20: - resolution: {integrity: sha512-jg25NiDV/1fLtSgEgyvVyDunvaNHbuwF9lfNV17gSmPFAlYzdfNBlLtLzXTevwkPj7DhGbmN9VnmJIgLnhvaBw==} + spdx-license-ids@3.0.20: {} - /split2@4.2.0: - resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} - engines: {node: '>= 10.x'} - dev: true + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - dev: true + sprintf-js@1.0.3: {} - /stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} + stop-iteration-iterator@1.0.0: dependencies: internal-slot: 1.0.7 - dev: false - /stream-browserify@3.0.0: - resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-browserify@3.0.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 - dev: true - /stream-http@3.2.0: - resolution: {integrity: sha512-Oq1bLqisTyK3TSCXpPbT4sdeYNdmyZJv1LxpEm2vu1ZhK89kSE5YXwZc3cWk0MagGaKriBh9mCFbVGtO+vY29A==} + stream-http@3.2.0: dependencies: builtin-status-codes: 3.0.0 inherits: 2.0.4 readable-stream: 3.6.2 xtend: 4.0.2 - dev: true - /string-argv@0.3.2: - resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} - engines: {node: '>=0.6.19'} - dev: true + string-argv@0.3.2: {} - /string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 strip-ansi: 7.1.0 - dev: true - - /string-width@7.2.0: - resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} - engines: {node: '>=18'} + + string-width@7.2.0: dependencies: emoji-regex: 10.4.0 get-east-asian-width: 1.2.0 strip-ansi: 7.1.0 - dev: true - /string.prototype.includes@2.0.0: - resolution: {integrity: sha512-E34CkBgyeqNDcrbU76cDjL5JLcVrtSdYq0MEh/B10r17pRP4ciHLwTgnuLV8Ay6cgEMLkcBkFCKyFZ43YldYzg==} + string.prototype.includes@2.0.0: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 - dev: false - /string.prototype.matchall@4.0.11: - resolution: {integrity: sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==} - engines: {node: '>= 0.4'} + string.prototype.matchall@4.0.11: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 @@ -6976,150 +8605,92 @@ packages: regexp.prototype.flags: 1.5.2 set-function-name: 2.0.2 side-channel: 1.0.6 - dev: false - /string.prototype.padend@3.1.6: - resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} - engines: {node: '>= 0.4'} + string.prototype.padend@3.1.6: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - dev: true - /string.prototype.repeat@1.0.0: - resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 es-abstract: 1.23.3 - dev: false - /string.prototype.trim@1.2.9: - resolution: {integrity: sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==} - engines: {node: '>= 0.4'} + string.prototype.trim@1.2.9: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-abstract: 1.23.3 es-object-atoms: 1.0.0 - /string.prototype.trimend@1.0.8: - resolution: {integrity: sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==} + string.prototype.trimend@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - /string.prototype.trimstart@1.0.8: - resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} - engines: {node: '>= 0.4'} + string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 es-object-atoms: 1.0.0 - /string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - dev: true - /string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - dev: true - /strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} + strip-ansi@7.1.0: dependencies: ansi-regex: 6.0.1 - dev: true - /strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} + strip-bom@3.0.0: {} - /strip-final-newline@3.0.0: - resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} - engines: {node: '>=12'} - dev: true + strip-final-newline@3.0.0: {} - /strip-indent@4.0.0: - resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} - engines: {node: '>=12'} + strip-indent@4.0.0: dependencies: min-indent: 1.0.1 - /strip-json-comments@3.1.1: - resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} - engines: {node: '>=8'} + strip-json-comments@3.1.1: {} - /style-search@0.1.0: - resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + style-search@0.1.0: {} - /stylelint-config-prettier@9.0.5(stylelint@15.11.0): - resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} - engines: {node: '>= 12'} - hasBin: true - peerDependencies: - stylelint: '>= 11.x < 15' + stylelint-config-prettier@9.0.5(stylelint@15.11.0): dependencies: stylelint: 15.11.0(typescript@5.6.2) - dev: false - /stylelint-config-property-sort-order-smacss@10.0.0(stylelint@15.11.0): - resolution: {integrity: sha512-NuiTgyqD8UdYY1IpTBIodBbrWKwaib5r8sq5kGHQ52UrmT8O7Fa8ZWYGipSZw6k9tGoljl9Hng2jtH+wBTMa1Q==} - engines: {node: '>=18.12.0'} - peerDependencies: - stylelint: ^14.0.0 || ^15.0.0 || ^16.0.0 + stylelint-config-property-sort-order-smacss@10.0.0(stylelint@15.11.0): dependencies: css-property-sort-order-smacss: 2.2.0 stylelint: 15.11.0(typescript@5.6.2) stylelint-order: 6.0.4(stylelint@15.11.0) - dev: false - /stylelint-config-recommended@13.0.0(stylelint@15.11.0): - resolution: {integrity: sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ==} - engines: {node: ^14.13.1 || >=16.0.0} - peerDependencies: - stylelint: ^15.10.0 + stylelint-config-recommended@13.0.0(stylelint@15.11.0): dependencies: stylelint: 15.11.0(typescript@5.6.2) - dev: false - /stylelint-config-standard@34.0.0(stylelint@15.11.0): - resolution: {integrity: sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ==} - engines: {node: ^14.13.1 || >=16.0.0} - peerDependencies: - stylelint: ^15.10.0 + stylelint-config-standard@34.0.0(stylelint@15.11.0): dependencies: stylelint: 15.11.0(typescript@5.6.2) stylelint-config-recommended: 13.0.0(stylelint@15.11.0) - dev: false - /stylelint-order@6.0.4(stylelint@15.11.0): - resolution: {integrity: sha512-0UuKo4+s1hgQ/uAxlYU4h0o0HS4NiQDud0NAUNI0aa8FJdmYHA5ZZTFHiV5FpmE3071e9pZx5j0QpVJW5zOCUA==} - peerDependencies: - stylelint: ^14.0.0 || ^15.0.0 || ^16.0.1 + stylelint-order@6.0.4(stylelint@15.11.0): dependencies: postcss: 8.4.47 postcss-sorting: 8.0.2(postcss@8.4.47) stylelint: 15.11.0(typescript@5.6.2) - dev: false - /stylelint@15.11.0(typescript@5.6.2): - resolution: {integrity: sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==} - engines: {node: ^14.13.1 || >=16.0.0} - hasBin: true + stylelint@15.11.0(typescript@5.6.2): dependencies: '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-tokenizer': 2.4.1 @@ -7165,47 +8736,31 @@ packages: - supports-color - typescript - /stylis@4.2.0: - resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} - dev: false + stylis@4.2.0: {} - /supports-color@5.5.0: - resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} - engines: {node: '>=4'} + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-hyperlinks@3.1.0: - resolution: {integrity: sha512-2rn0BZ+/f7puLOHZm1HOJfwBggfaHXUpPUSSG/SWM4TWp5KCfmNYwnC3hruy2rZlMnmWZ+QAGpZfchu3f3695A==} - engines: {node: '>=14.18'} + supports-hyperlinks@3.1.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - /supports-preserve-symlinks-flag@1.0.0: - resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} - engines: {node: '>= 0.4'} + supports-preserve-symlinks-flag@1.0.0: {} - /svg-tags@1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + svg-tags@1.0.0: {} - /synckit@0.9.1: - resolution: {integrity: sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==} - engines: {node: ^14.18.0 || >=16.0.0} + synckit@0.9.1: dependencies: '@pkgr/core': 0.1.1 tslib: 2.7.0 - dev: false - /table@6.8.2: - resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==} - engines: {node: '>=10.0.0'} + table@6.8.2: dependencies: ajv: 8.17.1 lodash.truncate: 4.4.2 @@ -7213,130 +8768,75 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 - /tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - dev: false + tapable@2.2.1: {} - /term-size@2.2.1: - resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==} - engines: {node: '>=8'} - dev: true + term-size@2.2.1: {} - /text-extensions@2.4.0: - resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==} - engines: {node: '>=8'} - dev: true + text-extensions@2.4.0: {} - /text-table@0.2.0: - resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + text-table@0.2.0: {} - /through@2.3.8: - resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - dev: true + through@2.3.8: {} - /timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} + timers-browserify@2.0.12: dependencies: setimmediate: 1.0.5 - dev: true - /tinycolor2@1.6.0: - resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} - dev: true + tinycolor2@1.6.0: {} - /tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - dev: true + tinyexec@0.3.0: {} - /tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - dev: true - /to-fast-properties@2.0.0: - resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} - engines: {node: '>=4'} + to-fast-properties@2.0.0: {} - /to-regex-range@5.0.1: - resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} - engines: {node: '>=8.0'} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - /trim-newlines@4.1.1: - resolution: {integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==} - engines: {node: '>=12'} + trim-newlines@4.1.1: {} - /ts-api-utils@1.3.0(typescript@5.6.2): - resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==} - engines: {node: '>=16'} - peerDependencies: - typescript: '>=4.2.0' + ts-api-utils@1.3.0(typescript@5.6.2): dependencies: typescript: 5.6.2 - dev: false - /tsconfig-paths@3.15.0: - resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + tsconfig-paths@3.15.0: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: false - /tslib@2.7.0: - resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + tslib@2.7.0: {} - /tsx@4.19.0: - resolution: {integrity: sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==} - engines: {node: '>=18.0.0'} - hasBin: true + tsx@4.19.0: dependencies: esbuild: 0.23.1 get-tsconfig: 4.8.0 optionalDependencies: fsevents: 2.3.3 - dev: true - /tty-browserify@0.0.1: - resolution: {integrity: sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==} - dev: true + tty-browserify@0.0.1: {} - /type-check@0.4.0: - resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} - engines: {node: '>= 0.8.0'} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - /type-fest@0.20.2: - resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} - engines: {node: '>=10'} + type-fest@0.20.2: {} - /type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} - dev: true + type-fest@0.21.3: {} - /type-fest@1.4.0: - resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} - engines: {node: '>=10'} + type-fest@1.4.0: {} - /typed-array-buffer@1.0.2: - resolution: {integrity: sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==} - engines: {node: '>= 0.4'} + typed-array-buffer@1.0.2: dependencies: call-bind: 1.0.7 es-errors: 1.3.0 is-typed-array: 1.1.13 - /typed-array-byte-length@1.0.1: - resolution: {integrity: sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==} - engines: {node: '>= 0.4'} + typed-array-byte-length@1.0.1: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -7344,9 +8844,7 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 - /typed-array-byte-offset@1.0.2: - resolution: {integrity: sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==} - engines: {node: '>= 0.4'} + typed-array-byte-offset@1.0.2: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -7355,9 +8853,7 @@ packages: has-proto: 1.0.3 is-typed-array: 1.1.13 - /typed-array-length@1.0.6: - resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} - engines: {node: '>= 0.4'} + typed-array-length@1.0.6: dependencies: call-bind: 1.0.7 for-each: 0.3.3 @@ -7366,106 +8862,64 @@ packages: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - /typescript@5.6.2: - resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} - engines: {node: '>=14.17'} - hasBin: true + typescript@5.6.2: {} - /unbox-primitive@1.0.2: - resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.7 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - /undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.19.8: {} - /unicorn-magic@0.1.0: - resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} - engines: {node: '>=18'} - dev: true + unicorn-magic@0.1.0: {} - /universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - dev: true + universalify@0.1.2: {} - /universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - dev: true + universalify@2.0.1: {} - /update-browserslist-db@1.1.0(browserslist@4.23.3): - resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + update-browserslist-db@1.1.0(browserslist@4.23.3): dependencies: browserslist: 4.23.3 escalade: 3.2.0 picocolors: 1.1.0 - dev: true - /uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + uri-js@4.4.1: dependencies: punycode: 2.3.1 - /url@0.11.4: - resolution: {integrity: sha512-oCwdVC7mTuWiPyjLUz/COz5TLk6wgp0RCsN+wHZ2Ekneac9w8uuV0njcbbie2ME+Vs+d6duwmYuR3HgQXs1fOg==} - engines: {node: '>= 0.4'} + url@0.11.4: dependencies: punycode: 1.4.1 qs: 6.13.0 - dev: true - /use-sync-external-store@1.2.2(react@18.3.1): - resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 + use-sync-external-store@1.2.2(react@18.3.1): dependencies: react: 18.3.1 - dev: false - /util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util-deprecate@1.0.2: {} - /util@0.12.5: - resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + util@0.12.5: dependencies: inherits: 2.0.4 is-arguments: 1.1.1 is-generator-function: 1.0.10 is-typed-array: 1.1.13 which-typed-array: 1.1.15 - dev: true - /validate-npm-package-license@3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - /validator@13.12.0: - resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} - engines: {node: '>= 0.10'} - dev: false + validator@13.12.0: {} - /vite-plugin-html-config@2.0.2(vite@5.4.9): - resolution: {integrity: sha512-g09u0XsmgKyMUIp1RZSyNSkJWvIusaXxw3KylyxU3vkCq7/G8hyemLctT+4IvO42fCPlNySmrNC9g0qSoKmvpw==} - engines: {node: '>=12.0.0'} - peerDependencies: - vite: '>=5.0.0' + vite-plugin-html-config@2.0.2(vite@5.4.9): dependencies: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - dev: true - /vite-plugin-imp@2.4.0(vite@5.4.9): - resolution: {integrity: sha512-L/6/nvOw+MyNh4UxAlCZHsmKd5MitmHamqqAWB15sbUgVIEz/OQ8jpKr6kkQU0eA/AIe8fkCVbQBlP81ajrqWg==} - peerDependencies: - vite: '>= 2.0.0-beta.5' + vite-plugin-imp@2.4.0(vite@5.4.9): dependencies: '@babel/core': 7.25.2 '@babel/generator': 7.25.6 @@ -7477,48 +8931,23 @@ packages: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - supports-color - dev: true - /vite-plugin-node-polyfills@0.22.0(vite@5.4.9): - resolution: {integrity: sha512-F+G3LjiGbG8QpbH9bZ//GSBr9i1InSTkaulfUHFa9jkLqVGORFBoqc2A/Yu5Mmh1kNAbiAeKeK+6aaQUf3x0JA==} - peerDependencies: - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + vite-plugin-node-polyfills@0.22.0(vite@5.4.9): dependencies: '@rollup/plugin-inject': 5.0.5 node-stdlib-browser: 1.2.0 vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - rollup - dev: true - /vite-plugin-progress@0.0.7(vite@5.4.9): - resolution: {integrity: sha512-zyvKdcc/X+6hnw3J1HVV1TKrlFKC4Rh8GnDnWG/2qhRXjqytTcM++xZ+SAPnoDsSyWl8O93ymK0wZRgHAoglEQ==} - engines: {node: '>=14', pnpm: '>=7.0.0'} - peerDependencies: - vite: '>2.0.0-0' + vite-plugin-progress@0.0.7(vite@5.4.9): dependencies: picocolors: 1.1.0 progress: 2.0.3 rd: 2.0.1 vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - dev: true - /vite-plugin-stylelint@5.3.1(postcss@8.4.47)(stylelint@15.11.0)(vite@5.4.9): - resolution: {integrity: sha512-M/hSdfOwnOVghbJDeuuYIU2xO/MMukYR8QcEyNKFPG8ro1L+DlTdViix2B2d/FvAw14WPX88ckA5A7NvUjJz8w==} - engines: {node: '>=14.18'} - peerDependencies: - '@types/stylelint': ^13.0.0 - postcss: ^7.0.0 || ^8.0.0 - rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 - stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - '@types/stylelint': - optional: true - postcss: - optional: true - rollup: - optional: true + vite-plugin-stylelint@5.3.1(postcss@8.4.47)(stylelint@15.11.0)(vite@5.4.9): dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) chokidar: 3.6.0 @@ -7528,38 +8957,8 @@ packages: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - supports-color - dev: true - /vite@5.4.9(@types/node@20.16.5)(less@4.2.0): - resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite@5.4.9(@types/node@20.16.5)(less@4.2.0): dependencies: '@types/node': 20.16.5 esbuild: 0.23.1 @@ -7569,18 +8968,13 @@ packages: optionalDependencies: fsevents: 2.3.3 - /vm-browserify@1.1.2: - resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - dev: true + vm-browserify@1.1.2: {} - /wcwidth@1.0.1: - resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 - dev: true - /which-boxed-primitive@1.0.2: - resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 @@ -7588,9 +8982,7 @@ packages: is-string: 1.0.7 is-symbol: 1.0.4 - /which-builtin-type@1.1.4: - resolution: {integrity: sha512-bppkmBSsHFmIMSl8BO9TbsyzsvGjVoppt8xUiGzwiu/bhDCGxnpOKCxgqj6GuyHE0mINMDecBFPlOm2hzY084w==} - engines: {node: '>= 0.4'} + which-builtin-type@1.1.4: dependencies: function.prototype.name: 1.1.6 has-tostringtag: 1.0.2 @@ -7604,21 +8996,15 @@ packages: which-boxed-primitive: 1.0.2 which-collection: 1.0.2 which-typed-array: 1.1.15 - dev: false - /which-collection@1.0.2: - resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} - engines: {node: '>= 0.4'} + which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.3 - dev: false - /which-typed-array@1.1.15: - resolution: {integrity: sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==} - engines: {node: '>= 0.4'} + which-typed-array@1.1.15: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.7 @@ -7626,113 +9012,66 @@ packages: gopd: 1.0.1 has-tostringtag: 1.0.2 - /which@1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - /which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true + which@2.0.2: dependencies: isexe: 2.0.0 - /word-wrap@1.2.5: - resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} - engines: {node: '>=0.10.0'} + word-wrap@1.2.5: {} - /wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@6.2.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - /wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} + wrap-ansi@8.1.0: dependencies: ansi-styles: 6.2.1 string-width: 5.1.2 strip-ansi: 7.1.0 - dev: true - /wrap-ansi@9.0.0: - resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} - engines: {node: '>=18'} + wrap-ansi@9.0.0: dependencies: ansi-styles: 6.2.1 string-width: 7.2.0 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + wrappy@1.0.2: {} - /write-file-atomic@5.0.1: - resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 signal-exit: 4.1.0 - /xtend@4.0.2: - resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} - engines: {node: '>=0.4'} - dev: true + xtend@4.0.2: {} - /y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - dev: true + y18n@5.0.8: {} - /yallist@2.1.2: - resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} - dev: true + yallist@2.1.2: {} - /yallist@3.1.1: - resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - dev: true + yallist@3.1.1: {} - /yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@4.0.0: {} - /yaml@1.10.2: - resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} - engines: {node: '>= 6'} - dev: false + yaml@1.10.2: {} - /yaml@2.5.1: - resolution: {integrity: sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==} - engines: {node: '>= 14'} - hasBin: true - dev: true + yaml@2.5.1: {} - /yargs-parser@20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} + yargs-parser@20.2.9: {} - /yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - dev: true + yargs-parser@21.1.1: {} - /yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} + yargs@17.7.2: dependencies: cliui: 8.0.1 escalade: 3.2.0 @@ -7741,34 +9080,14 @@ packages: string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - dev: true - /yocto-queue@0.1.0: - resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} - engines: {node: '>=10'} + yocto-queue@0.1.0: {} - /yocto-queue@1.1.1: - resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} - engines: {node: '>=12.20'} - dev: true + yocto-queue@1.1.1: {} - /zustand@4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1): - resolution: {integrity: sha512-+0PALYNJNgK6hldkgDq2vLrw5f6g/jCInz52n9RTpropGgeAf/ioFUCdtsjCqu4gNhW9D01rUQBROoRjdzyn2Q==} - engines: {node: '>=12.7.0'} - peerDependencies: - '@types/react': '>=16.8' - immer: '>=9.0.6' - react: '>=16.8' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true + zustand@4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1): dependencies: '@types/react': 18.3.5 immer: 10.1.1 react: 18.3.1 use-sync-external-store: 1.2.2(react@18.3.1) - dev: false From 410f8828e18e25bcdbfeacfe2c9ca62fec0dd5de Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Tue, 17 Dec 2024 19:20:22 +0800 Subject: [PATCH 036/172] feat: Added workflow log data processing logic --- .../action-log/components/header/index.tsx | 12 +- .../components/action-log/constant.ts | 29 ++ .../workflow/components/action-log/index.tsx | 179 +++++---- .../workflow/components/action-log/types.d.ts | 24 +- .../workflow/components/log-modal/index.tsx | 10 +- .../workflow/components/log-modal/trace.json | 186 ++++++++++ .../components/log-modal/workflow.json | 348 ++++++++++++++++++ apps/web/src/services/http/workflow.ts | 13 +- packages/locales/src/lang/en/workflow.json | 4 +- 9 files changed, 719 insertions(+), 86 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/action-log/constant.ts create mode 100644 apps/web/src/pages/workflow/components/log-modal/trace.json create mode 100644 apps/web/src/pages/workflow/components/log-modal/workflow.json diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx index 832717a6..9c40a549 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx @@ -1,6 +1,5 @@ import React, { useMemo } from 'react'; import cls from 'classnames'; -import { useI18n } from '@milesight/shared/src/hooks'; import { Tooltip } from '@/components'; import { basicNodeConfigs, LogStatusMap } from '@/pages/workflow/config'; import type { AccordionLog } from '../../types'; @@ -10,16 +9,15 @@ interface IProps { data: AccordionLog; } export default React.memo(({ data }: IProps) => { - const { getIntlText } = useI18n(); - /** Get the header render config */ - const { icon, iconBgColor, labelIntlKey, status } = useMemo(() => { - const { type, config, status } = data || {}; + const { icon, iconBgColor, name, status } = useMemo(() => { + const { type, config, status, name } = data || {}; const { icon, iconBgColor, labelIntlKey } = config || {}; const result = basicNodeConfigs[type]; return { status, + name, icon: icon || result?.icon, iconBgColor: iconBgColor || result?.iconBgColor, labelIntlKey: labelIntlKey || result?.labelIntlKey, @@ -28,7 +26,7 @@ export default React.memo(({ data }: IProps) => { /** Get this state render config */ const { className: statusClassName, icon: statusIcon } = useMemo( - () => LogStatusMap[status], + () => LogStatusMap[status] || {}, [status], ); return ( @@ -38,7 +36,7 @@ export default React.memo(({ data }: IProps) => { {icon}
- +
{statusIcon}
diff --git a/apps/web/src/pages/workflow/components/action-log/constant.ts b/apps/web/src/pages/workflow/components/action-log/constant.ts new file mode 100644 index 00000000..d50aa293 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/constant.ts @@ -0,0 +1,29 @@ +// A collection of 26 English letters +export const ALPHABET_LIST = [ + 'A', + 'B', + 'C', + 'D', + 'E', + 'F', + 'G', + 'H', + 'I', + 'J', + 'K', + 'L', + 'M', + 'N', + 'O', + 'P', + 'Q', + 'R', + 'S', + 'T', + 'U', + 'V', + 'W', + 'X', + 'Y', + 'Z', +]; diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index 45c133ce..34c3a53d 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,86 +1,137 @@ +import { useCallback, useMemo } from 'react'; import { Fragment } from 'react/jsx-runtime'; +import { getOutgoers } from '@xyflow/react'; +import { cloneDeep, isNil } from 'lodash-es'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; import { AccordionCard, AccordionHeader, AccordionTree } from './components'; -import type { AccordionLog } from './types'; +import { basicNodeConfigs } from '../../config'; +import { ALPHABET_LIST } from './constant'; +import type { AccordionLog, WorkflowDataType, WorkflowTraceType } from './types'; import './style.less'; -// TODO mock data -const treeData: AccordionLog[] = [ - { - key: '1', - type: 'trigger', - status: 'success', - input: 'xxxx', - output: 'yyyyyy', - }, - { - key: '2', - type: 'code', - status: 'failed', - input: 'xxxx', - output: 'yyyyyy', - children: [ - { - key: '2-1', - type: 'service', - status: 'failed', - input: 'gsagsagas', - output: 'yyyyyy', - children: [ - { - key: '2-1-1', - type: 'service', - status: 'failed', - input: 'gsagsagas', - output: 'yyyyyy', - }, - { - key: '2-1-2', - type: 'timer', - status: 'failed', - input: '4444', - output: 'yyyyyy', - }, - ], - }, - { - key: '2-2', - type: 'timer', - status: 'failed', - input: '4444', - output: 'yyyyyy', +interface IProps { + traceData: WorkflowTraceType[]; + workflowData: WorkflowDataType; +} +type WorkflowNestNode = WorkflowNode & { + attrs: AccordionLog; + children?: WorkflowNestNode[]; +}; +type LevelStructType = { + label: string; + value: number; + parentValue?: number; +}; +const entryNodeConfigs = Object.values(basicNodeConfigs).filter(node => node.category === 'entry'); +export default function AccordionUsage({ traceData, workflowData }: IProps) { + const { getIntlText } = useI18n(); + + /** generate trace Map */ + const traceMap = useMemo(() => { + return (traceData || []).reduce( + (acc, cur) => { + const { nodeId } = objectToCamelCase(cur || {}); + acc[nodeId] = cur; + return acc; }, - ], - }, -]; + {} as Record, + ); + }, [traceData]); + /** Add required attributes */ + const wrapperNode = useCallback( + (node: WorkflowNode): WorkflowNestNode => { + const nestNode = cloneDeep(node) as WorkflowNestNode; + // TODO name + const { id, type, $$name } = nestNode || {}; + const { status, input, output } = traceMap[id] || {}; + + nestNode.attrs = { + name: $$name, + type: type!, + status: status === 'SUCCESS' ? 'success' : 'failed', + input, + output, + }; + + return nestNode; + }, + [traceMap], + ); + /** Determine whether it is the root node */ + const isRootNode = (node: WorkflowNestNode) => { + const { type } = node || {}; + return entryNodeConfigs.some(config => config.type === type); + }; + /** Generate Tree Data */ + const treeData = useMemo(() => { + const { nodes, edges } = workflowData || {}; + const nestNodes = (nodes || []).map(node => wrapperNode(node)); + + const root: WorkflowNestNode[] = []; + (nestNodes || []).forEach(node => { + /** get Root Node */ + const isRoot = isRootNode(node); + isRoot && root.push(node); + + const outgoers = getOutgoers(node, nestNodes, edges) as WorkflowNestNode[]; + /** Generating nested struct data */ + if (outgoers.length) { + node.children = [...(node.children || []), ...outgoers]; + } + }); + + return root; + }, [workflowData, wrapperNode]); -export default function AccordionUsage() { /** recursive rendering */ - const renderAccordion = (data: AccordionLog) => { - const { children, ...item } = data || {}; + const renderAccordion = (data: WorkflowNestNode, levelStruct: LevelStructType) => { + const { children, attrs, ...item } = data || {}; + /** get parallel text */ + const { label: levelLabel, value: levelValue, parentValue } = levelStruct || {}; + const parentText = !isNil(parentValue) ? `${ALPHABET_LIST[parentValue]}` : ''; + const parallelLabel = `${levelLabel}${parentText}`; + const parallelText = getIntlText('workflow.label.parallel', { + 1: `-${parallelLabel}`, + }); return ( <> - }> -
{item.input}
+ }> + {/* // TODO */} +
input
{children && ( - - {children.map(child => ( - -
xxxx
- {renderAccordion(child)} -
- ))} + + {children.map((child, ind) => { + const nextLevel = levelValue + 1; + const branchLabel = ALPHABET_LIST[ind]; + const branchText = getIntlText('workflow.label.branch', { + 1: `-${parallelLabel}-${branchLabel}`, + }); + + return ( + +
{branchText}
+ {renderAccordion(child, { + label: `${nextLevel + 1}`, + value: nextLevel, + parentValue: children.length > 1 ? ind : void 0, + })} +
+ ); + })}
)} ); }; - return (
- {treeData.map(item => ( - {renderAccordion(item)} + {treeData.map((item, index) => ( + + {renderAccordion(item, { label: `${index + 1}`, value: index })} + ))}
); diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index a61da600..31e99d7f 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -1,17 +1,19 @@ +import { type ReactFlowJsonObject } from '@xyflow/react'; +import { type WorkflowAPISchema } from '@/services/http'; import type { LogStatus } from '../../config'; /** * Action Log Type */ export interface AccordionLog { - /** - * Key for Component render - */ - key: string | number; /** * Node Type */ type: WorkflowNodeType; + /** + * Node Name + */ + name: string; /** * Custom header render config */ @@ -21,13 +23,13 @@ export interface AccordionLog { */ status: LogStatus; /** - * Input + * TODO Input */ - input: string; + input: Record; /** - * Output + * TODO Output */ - output: string; + output: Record; /** * Children */ @@ -51,3 +53,9 @@ export type CustomConfigItemType = { */ iconBgColor?: string; }; + +/** Workflow Trace Type */ +export type WorkflowTraceType = WorkflowAPISchema['getLogDetail']['response'][number]; + +/** Workflow Data Type */ +export type WorkflowDataType = ReactFlowJsonObject; diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx index 3017e06b..eb74a789 100644 --- a/apps/web/src/pages/workflow/components/log-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -8,6 +8,10 @@ import ActionLog from '../action-log'; import type { LogItemProps } from './types'; import './style.less'; +// TODO mock Data +import traceData from './trace.json'; +import workflowData from './workflow.json'; + export type IProps = ModalProps; export default React.memo(({ visible, ...props }: IProps) => { const containerRef = useRef(null); @@ -74,7 +78,11 @@ export default React.memo(({ visible, ...props }: IProps) => {
- +
diff --git a/apps/web/src/pages/workflow/components/log-modal/trace.json b/apps/web/src/pages/workflow/components/log-modal/trace.json new file mode 100644 index 00000000..8ca54ca2 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/trace.json @@ -0,0 +1,186 @@ +{ + "traceInfo": [ + { + "message_id": "msg_001", + "node_id": "node:1734403663609:tqhoyisd", + "node_label": "Trigger Node", + "status": "SUCCESS", + "time_cost": 100, + "start_time": 1672531200000, + "input": { + "trigger": "start" + }, + "output": { + "result": "initiated" + } + }, + { + "message_id": "msg_002", + "node_id": "node:1734403696051:mmijxipj", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 150, + "start_time": 1672531300000, + "input": { + "param1": "value1" + }, + "output": { + "result": "processed" + } + }, + { + "message_id": "msg_003", + "node_id": "node:1734403741746:cvlebfnk", + "node_label": "Code Node", + "status": "ERROR", + "time_cost": 200, + "start_time": 1672531400000, + "input": { + "param1": "value2" + }, + "output": { + "error": "runtime error" + } + }, + { + "message_id": "msg_004", + "node_id": "node:1734403754076:tunkfwyp", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 120, + "start_time": 1672531500000, + "input": { + "param1": "value3" + }, + "output": { + "result": "completed" + } + }, + { + "message_id": "msg_005", + "node_id": "node:1734403760131:nxrtmtqt", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 110, + "start_time": 1672531600000, + "input": { + "param1": "value4" + }, + "output": { + "result": "validated" + } + }, + { + "message_id": "msg_006", + "node_id": "node:1734403762609:tojsahsk", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 130, + "start_time": 1672531700000, + "input": { + "param1": "value5" + }, + "output": { + "result": "executed" + } + }, + { + "message_id": "msg_007", + "node_id": "node:1734403764786:iwvgqkbl", + "node_label": "Code Node", + "status": "ERROR", + "time_cost": 180, + "start_time": 1672531800000, + "input": { + "param1": "value6" + }, + "output": { + "error": "timeout error" + } + }, + { + "message_id": "msg_008", + "node_id": "node:1734403768531:pioadrla", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 90, + "start_time": 1672531900000, + "input": { + "param1": "value7" + }, + "output": { + "result": "success" + } + }, + { + "message_id": "msg_009", + "node_id": "node:1734403779597:krdbgvbs", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 140, + "start_time": 1672532000000, + "input": { + "param1": "value8" + }, + "output": { + "result": "processed" + } + }, + { + "message_id": "msg_010", + "node_id": "node:1734403781283:dwnkqolz", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 160, + "start_time": 1672532100000, + "input": { + "param1": "value9" + }, + "output": { + "result": "executed" + } + }, + { + "message_id": "msg_011", + "node_id": "node:1734403783794:wjhlqruk", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 110, + "start_time": 1672532200000, + "input": { + "param1": "value10" + }, + "output": { + "result": "completed" + } + }, + { + "message_id": "msg_012", + "node_id": "node:1734403795500:xceqicsa", + "node_label": "Code Node", + "status": "ERROR", + "time_cost": 170, + "start_time": 1672532300000, + "input": { + "param1": "value11" + }, + "output": { + "error": "network error" + } + }, + { + "message_id": "msg_013", + "node_id": "node:1734403803395:tcmtlast", + "node_label": "Code Node", + "status": "SUCCESS", + "time_cost": 100, + "start_time": 1672532400000, + "input": { + "param1": "value12" + }, + "output": { + "result": "finalized" + } + } + ] +} diff --git a/apps/web/src/pages/workflow/components/log-modal/workflow.json b/apps/web/src/pages/workflow/components/log-modal/workflow.json new file mode 100644 index 00000000..3389f8dd --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/workflow.json @@ -0,0 +1,348 @@ +{ + "nodes": [ + { + "id": "node:1734403663609:tqhoyisd", + "type": "trigger", + "position": { + "x": 0, + "y": 0 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "zIndex": 0, + "selected": false, + "$$name": "START" + }, + { + "id": "node:1734403696051:mmijxipj", + "type": "code", + "position": { + "x": 290, + "y": 0 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "zIndex": 0, + "$$name": "CODE 1" + }, + { + "id": "node:1734403741746:cvlebfnk", + "type": "code", + "position": { + "x": 580, + "y": 0 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "zIndex": 0, + "selected": false, + "$$name": "CODE 4" + }, + { + "id": "node:1734403754076:tunkfwyp", + "type": "code", + "position": { + "x": 580, + "y": 100 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "zIndex": 0, + "selected": false, + "$$name": "CODE 2" + }, + { + "id": "node:1734403760131:nxrtmtqt", + "type": "code", + "position": { + "x": 870, + "y": 100 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "zIndex": 0, + "$$name": "CODE 6" + }, + { + "id": "node:1734403762609:tojsahsk", + "type": "code", + "position": { + "x": 870, + "y": 200 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "zIndex": 0, + "$$name": "CODE 12" + }, + { + "id": "node:1734403764786:iwvgqkbl", + "type": "code", + "position": { + "x": 870, + "y": 300 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "zIndex": 0, + "$$name": "代码 6" + }, + { + "id": "node:1734403768531:pioadrla", + "type": "code", + "position": { + "x": 1293, + "y": -35.00000000000001 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "dragging": false, + "$$name": "CODE 7" + }, + { + "id": "node:1734403779597:krdbgvbs", + "type": "code", + "position": { + "x": 1176, + "y": 150 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "dragging": false, + "$$name": "代码 9" + }, + { + "id": "node:1734403781283:dwnkqolz", + "type": "code", + "position": { + "x": 1176, + "y": 226.99999999999997 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "zIndex": 0, + "dragging": false, + "$$name": "代码 11" + }, + { + "id": "node:1734403783794:wjhlqruk", + "type": "code", + "position": { + "x": 1176, + "y": 300 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "dragging": false, + "zIndex": 0, + "$$name": "代码 7" + }, + { + "id": "node:1734403795500:xceqicsa", + "type": "code", + "position": { + "x": 1176, + "y": 400 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "zIndex": 0, + "selected": false, + "dragging": false, + "$$name": "代码 8" + }, + { + "id": "node:1734403803395:tcmtlast", + "type": "code", + "position": { + "x": 1466, + "y": 400 + }, + "data": {}, + "measured": { + "width": 240, + "height": 50 + }, + "selected": false, + "$$name": "代码 12" + } + ], + "edges": [ + { + "id": "edge:1734403696051:haikqpgy", + "type": "addable", + "source": "node:1734403663609:tqhoyisd", + "target": "node:1734403696051:mmijxipj", + "data": { + "$hovering": false + }, + "selected": false, + "$$name": "start" + }, + { + "id": "edge:1734403741746:dhqkxsqr", + "type": "addable", + "source": "node:1734403696051:mmijxipj", + "target": "node:1734403741746:cvlebfnk", + "data": { + "$hovering": false + }, + "selected": false, + "$$name": "CODE 1" + }, + { + "id": "edge:1734403754076:gbofxrfa", + "type": "addable", + "source": "node:1734403696051:mmijxipj", + "target": "node:1734403754076:tunkfwyp", + "selected": false, + "$$name": "CODE 4" + }, + { + "id": "edge:1734403760131:lojptzjc", + "type": "addable", + "source": "node:1734403754076:tunkfwyp", + "target": "node:1734403760131:nxrtmtqt", + "data": { + "$hovering": false + }, + "selected": false, + "$$name": "CODE 2" + }, + { + "id": "edge:1734403762609:psbmmpcl", + "type": "addable", + "source": "node:1734403754076:tunkfwyp", + "target": "node:1734403762609:tojsahsk", + "data": { + "$hovering": false + }, + "selected": false, + "$$name": "CODE 6" + }, + { + "id": "edge:1734403764786:siwnxxoz", + "type": "addable", + "source": "node:1734403754076:tunkfwyp", + "target": "node:1734403764786:iwvgqkbl", + "selected": false, + "$$name": "CODE 12" + }, + { + "id": "edge:1734403768531:rzwgnydv", + "type": "addable", + "source": "node:1734403741746:cvlebfnk", + "target": "node:1734403768531:pioadrla", + "selected": false, + "$$name": "代码 6" + }, + { + "source": "node:1734403760131:nxrtmtqt", + "sourceHandle": null, + "target": "node:1734403768531:pioadrla", + "targetHandle": null, + "id": "edge:1734403776080:vlmahrru", + "type": "addable", + "data": { + "$hovering": false + }, + "selected": false, + "$$name": "CODE 7" + }, + { + "id": "edge:1734403779597:qjaweigh", + "type": "addable", + "source": "node:1734403762609:tojsahsk", + "target": "node:1734403779597:krdbgvbs", + "data": { + "$hovering": false + }, + "selected": false, + "$$name": "代码 9" + }, + { + "id": "edge:1734403781283:wiremvxr", + "type": "addable", + "source": "node:1734403762609:tojsahsk", + "target": "node:1734403781283:dwnkqolz", + "selected": false, + "$$name": "代码 11" + }, + { + "id": "edge:1734403783794:yztayqju", + "type": "addable", + "source": "node:1734403764786:iwvgqkbl", + "target": "node:1734403783794:wjhlqruk", + "selected": false, + "data": { + "$hovering": false + }, + "$$name": "代码 7" + }, + { + "id": "edge:1734403795500:lflujxjh", + "type": "addable", + "source": "node:1734403764786:iwvgqkbl", + "target": "node:1734403795500:xceqicsa", + "selected": false, + "$$name": "代码 8" + }, + { + "id": "edge:1734403803395:uwqdlnzp", + "type": "addable", + "source": "node:1734403795500:xceqicsa", + "target": "node:1734403803395:tcmtlast", + "selected": false, + "$$name": "代码 12" + } + ], + "viewport": { + "x": 119.37734719140894, + "y": 263.2629915858977, + "zoom": 0.49999999999999983 + } +} diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index fb73ae5a..cf4271ff 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -119,14 +119,17 @@ export interface WorkflowAPISchema extends APISchema { id: ApiKey; }; response: { + message_id: string; /** Node ID */ - node_id: ApiKey; - /** Node Name */ - node_name: string; + node_id: string; + /** Node's tag (not a custom name) */ + node_label: string; /** Running status */ - status: string; + status: 'SUCCESS' | 'ERROR'; /** Cost Time */ - cost: number; + time_cost: number; + /** Start Time */ + start_time: number; // TODO input: Record; // TODO diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index a9e3fc7b..0abd5654 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -38,5 +38,7 @@ "workflow.editor.save_warning_message": "The current workflow has been referenced, and saving it will update the workflow information synchronously. Are you sure you want to save it", "workflow.message.delete_tip": "Are you sure you want to delete the workflow? It cannot be restore after deletion.", "workflow.label.deletion": "Deletion", - "workflow.editor.form_param_select_placeholder": "Enter or reference a variable" + "workflow.editor.form_param_select_placeholder": "Enter or reference a variable", + "workflow.label.branch": "Branch{1}", + "workflow.label.parallel": "Parallel{1}" } From 5f9406dcbc8e0e86075de955c9cb0feb2c638494 Mon Sep 17 00:00:00 2001 From: jimco Date: Tue, 17 Dec 2024 19:57:13 +0800 Subject: [PATCH 037/172] feat: add node form compnents and render nodes form --- .../components/code-editor/index.tsx | 10 + .../components/conditions-input/index.tsx | 90 +++++- .../components/conditions-input/style.less | 115 ++++++++ .../config-panel/components/index.ts | 1 + .../components/markdown-editor/index.tsx | 4 +- .../components/param-input/index.tsx | 2 + .../components/param-select/index.tsx | 17 +- .../components/timer-input/index.tsx | 234 +++++++++++++++- .../components/timer-input/style.less | 50 ++++ .../config-panel/hooks/useNodeFormItems.tsx | 256 +++++++++++++++--- .../editor/components/config-panel/index.tsx | 105 +++++-- .../editor/components/config-panel/style.less | 19 ++ .../pages/workflow/views/editor/constants.ts | 5 + .../workflow/views/editor/demo-data.json | 47 +++- .../views/editor/hooks/useInteractions.tsx | 5 +- .../views/editor/hooks/useWorkflow.ts | 83 ++++-- packages/locales/src/lang/en/global.json | 3 +- packages/locales/src/lang/en/workflow.json | 18 +- .../src/components/icons/components/code.tsx | 17 ++ .../src/components/icons/components/index.ts | 1 + .../shared/src/components/icons/index.tsx | 1 + packages/shared/types/workflow.d.ts | 26 +- 22 files changed, 1000 insertions(+), 109 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/style.less create mode 100644 packages/shared/src/components/icons/components/code.tsx diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx new file mode 100644 index 00000000..fbef3be9 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -0,0 +1,10 @@ +/** + * Code Editor Component + * + * Note: Use in CodeNode, IfelseNode + */ +const CodeEditor = () => { + return <>Code Editor; +}; + +export default CodeEditor; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 02cb9de0..120f7c00 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -1,10 +1,98 @@ +import { + ToggleButtonGroup, + ToggleButton, + Button, + IconButton, + Chip, + Select, + FormControl, + InputLabel, + MenuItem, + TextField, +} from '@mui/material'; +import { + AddIcon, + DeleteOutlineIcon, + InputIcon, + CodeIcon, + SyncIcon, + KeyboardArrowDownIcon, +} from '@milesight/shared/src/components'; +import ParamSelect from '../param-select'; +import './style.less'; + /** * Conditions Input Component * * Note: use in IfelseNode */ const ConditionsInput = () => { - return <>Conditions Input; + return ( +
+
+
+
IF
+
+ + + + + + + + + + + +
+
+
+
+ + AND + + } + /> +
+
+
+
+ + +
+ +
+ + + +
+ +
+
+ +
+ ); }; export default ConditionsInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less new file mode 100644 index 00000000..be876751 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less @@ -0,0 +1,115 @@ +.@{prefix}-conditions-input { + &-item-topbar { + display: flex; + align-items: center; + justify-content: space-between; + height: 32px; + margin-bottom: @margin-md; + + .btns { + display: flex; + gap: @margin-xs; + align-items: center; + } + + .@{mui-prefix}ToggleButton-root { + padding: @padding-xxs @padding-xs; + } + } + + &-item-content { + position: relative; + margin-bottom: @margin-xl; + border-bottom: 1px solid var(--border-color-base); + + &.multiple-conditions { + padding-left: 64px; + + .logic-operator { + display: block; + } + } + + .logic-operator { + position: absolute; + top: 12px; + bottom: 96px; + left: 28px; + display: none; + width: 36px; + border: 1px solid var(--border-color-base); + border-right: none; + border-radius: @border-radius-base; + + .@{mui-prefix}Chip-root { + position: absolute; + top: 50%; + left: 0; + max-width: none; + background-color: var(--component-background); + border-color: var(--border-color-base); + border-radius: @border-radius-base; + cursor: pointer; + transform: translate(-50%, -50%); + .text-size(@font-size-sm); + + .@{mui-prefix}Chip-label { + display: flex; + align-items: center; + } + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-base; + } + } + } + + .field-item { + display: flex; + gap: @margin-xs; + + .input-wrapper { + flex: 1; + + .select { + display: flex; + + > .@{prefix}-param-select { + flex: 1; + .@{mui-prefix}OutlinedInput-notchedOutline { + border-radius: @border-radius-base 0 0 0; + } + } + + > .@{mui-prefix}InputBase-root { + flex: 1; + margin-left: -1px; + + .@{mui-prefix}OutlinedInput-notchedOutline { + border-radius: 0 @border-radius-base 0 0; + } + } + } + + > .@{mui-prefix}FormControl-root { + margin: 0; + margin-top: -1px; + + .@{mui-prefix}OutlinedInput-notchedOutline { + border-radius: 0 0 @border-radius-base @border-radius-base; + } + } + } + + > .@{mui-prefix}ButtonBase-root { + width: 30px; + height: 30px; + margin-top: 5px; + } + } + + .@{prefix}-conditions-input-item-add-btn { + margin: @margin-sm 0 @margin-xl; + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 3cbad2e2..0d2c551a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -1,3 +1,4 @@ +export { default as CodeEditor } from './code-editor'; export { default as ConditionsInput } from './conditions-input'; export { default as EntityAssignSelect } from './entity-assign-select'; export { diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx index 0492728d..d45ab8ae 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx @@ -3,6 +3,8 @@ * * Note: Use in EmailNode */ -const MarkdownEditor = () => {}; +const MarkdownEditor = () => { + return <>Markdown Editor; +}; export default MarkdownEditor; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index c47600e7..072cabd8 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -36,6 +36,8 @@ export interface ParamInputProps { typeSelectProps?: SelectProps; nameInputProps?: TextFieldProps; maxAddNum?: number; + value?: ParamInputValueType[]; + onChange?: (value: ParamInputValueType[]) => void; } const DEFAULT_EMPTY_VALUE: ParamInputValueType = { diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx index b5e1982e..1b2dff9e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -7,6 +7,7 @@ import { ListSubheader, type SelectProps, } from '@mui/material'; +import { isNil } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { KeyboardArrowDownIcon } from '@milesight/shared/src/components'; import { Tooltip } from '@/components'; @@ -78,7 +79,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, ..
- {label || getIntlText('common.label.value')} + {!isNil(label) ? label : getIntlText('common.label.value')} {...props} @@ -86,9 +87,19 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. notched defaultValue="" labelId="param-select-label" - label={label || getIntlText('common.label.value')} + label={!isNil(label) ? label : getIntlText('common.label.value')} IconComponent={KeyboardArrowDownIcon} - MenuProps={{ className: 'ms-param-select-menu' }} + MenuProps={{ + className: 'ms-param-select-menu', + anchorOrigin: { + vertical: 'bottom', + horizontal: 'right', + }, + transformOrigin: { + vertical: 'top', + horizontal: 'right', + }, + }} > {renderOptions()} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx index bcf6016a..ae0c1532 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/index.tsx @@ -1,8 +1,240 @@ +import React, { useMemo, useLayoutEffect } from 'react'; +import { useDynamicList, useControllableValue } from 'ahooks'; +import { isEqual } from 'lodash-es'; +import { + Select, + FormControl, + InputLabel, + MenuItem, + Button, + IconButton, + Stack, +} from '@mui/material'; +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker'; +import { TimePicker } from '@mui/x-date-pickers/TimePicker'; +import { useI18n, useTime } from '@milesight/shared/src/hooks'; +import { AddIcon, DeleteOutlineIcon } from '@milesight/shared/src/components'; +import './style.less'; + +export type TimerInputValueType = Partial>; + +export interface TimerInputProps { + // label?: string; + + required?: boolean; + + /** + * Param Select Placeholder + */ + // placeholder?: string; + + value?: TimerInputValueType; + + defaultValue?: TimerInputValueType; + + onChange?: (value: TimerInputValueType) => void; +} + +const timerTypeConfigs: Record< + NonNullable, + { + labelIntlKey: string; + } +> = { + ONCE: { + labelIntlKey: 'workflow.editor.form_param_timer_type_once', + }, + CYCLE: { + labelIntlKey: 'workflow.editor.form_param_timer_type_cycle', + }, +}; + +const periodConfigs: Record< + TimePeriodType, + { + labelIntlKey: string; + } +> = { + EVERYDAY: { + labelIntlKey: 'workflow.editor.form_param_timer_period_everyday', + }, + Monday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_monday', + }, + Tuesday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_tuesday', + }, + Wednesday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_wednesday', + }, + Thursday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_thursday', + }, + Friday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_friday', + }, + Saturday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_saturday', + }, + Sunday: { + labelIntlKey: 'workflow.editor.form_param_timer_period_sunday', + }, +}; + +const MAX_VALUE_LENGTH = 10; + /** * Timer Input Component * * Note: use in TimerNode */ -const TimerInput = () => {}; +const TimerInput: React.FC = ({ required, ...props }) => { + const { getIntlText } = useI18n(); + const { getTime } = useTime(); + const [data, setData] = useControllableValue(props); + const { list, remove, getKey, insert, replace, resetList } = useDynamicList< + Partial[number]> + >(data?.settings); + const typeOptions = useMemo(() => { + return Object.entries(timerTypeConfigs).map(([type, config]) => ( + + {getIntlText(config.labelIntlKey)} + + )); + }, [getIntlText]); + const periodOptions = useMemo(() => { + return Object.entries(periodConfigs).map(([period, config]) => ( + + {getIntlText(config.labelIntlKey)} + + )); + }, [getIntlText]); + + useLayoutEffect(() => { + if (data?.type === 'ONCE' || isEqual(data?.settings, list)) return; + resetList(data?.settings || []); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data, resetList]); + + useLayoutEffect(() => { + setData(d => ({ + ...d, + settings: list || [], + })); + }, [list, setData]); + + return ( + + + + {getIntlText('workflow.editor.form_param_timer_type')} + + + notched + labelId="time-input-type-label" + label={getIntlText('workflow.editor.form_param_timer_type')} + value={data?.type || ''} + onChange={e => { + const type = e.target.value as TimerInputValueType['type']; + setData({ type, settings: type === 'CYCLE' ? [{}] : undefined }); + }} + > + {typeOptions} + + + {data?.type === 'ONCE' && ( + { + setData({ + ...data, + expireTime: undefined, + settings: undefined, + executionTime: getTime(time, true).valueOf(), + }); + }} + /> + )} + {data?.type === 'CYCLE' && ( + <> +
+ + {getIntlText('workflow.editor.form_param_execution_time_queue')} + + {list.map((item, index) => ( +
+ + + {getIntlText('workflow.editor.form_param_timer_type')} + + + + { + replace(index, { + ...item, + time: getTime(time, true).valueOf(), + }); + }} + /> + {list.length > 1 && ( + remove(index)}> + + + )} +
+ ))} + +
+ { + setData({ + ...data, + executionTime: undefined, + expireTime: getTime(time, true).valueOf(), + }); + }} + /> + + )} +
+ ); +}; export default TimerInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/style.less new file mode 100644 index 00000000..be8e3227 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/timer-input/style.less @@ -0,0 +1,50 @@ +.@{prefix}-timer-input { + &-exec-queue { + position: relative; + padding: @padding-md; + border: 1px solid var(--border-color-base); + border-radius: @border-radius-base; + + > .label { + position: absolute; + top: -(@font-size-sm / 2); + left: @margin-xs; + padding: 0 @padding-xxs; + font-size: @font-size-sm; + line-height: @font-size-sm; + color: var(--text-color-secondary); + background-color: var(--component-background); + } + + .queue-item { + display: flex; + gap: @margin-xs; + margin-bottom: @margin-md; + + > .@{mui-prefix}FormControl-root { + flex: 1; + margin: 0; + } + + > .@{mui-prefix}ButtonBase-root { + width: 40px; + height: 40px; + } + + &:last-child { + margin-bottom: 0; + } + } + + & &-add-btn { + font-weight: @font-weight-bold; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 159336b0..cd173747 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -4,81 +4,263 @@ import { TextField } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { checkRequired } from '@milesight/shared/src/utils/validators'; import { + CodeEditor, ConditionsInput, EntityAssignSelect, EntityFilterSelect, EntitySelect, MarkdownEditor, ParamAssignInput, - ParamInputSelect, + ParamInput, + // ParamInputSelect, TimerInput, } from '../components'; +type NodeFormGroupType = { + groupName?: string; + helperText?: string; + children?: ControllerProps[]; +}; + /** * Form Item Props */ export type NodeFormDataProps = Record; const useNodeFormItems = (node?: WorkflowNode) => { - const { getIntlText } = useI18n(); + // const { getIntlText } = useI18n(); - const formItems = useMemo(() => { - if (!node) return []; - - const result: Partial[]>> = { + const formConfigs = useMemo(() => { + const result: Partial> = { trigger: [ { - name: 'debug', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, + // TODO: the name may come from api config + groupName: 'Arguments', + children: [ + { + name: 'entityConfigs', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], }, + ], + timer: [ { - name: 'debug2', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, + // TODO: the name may come from api config + groupName: 'Timer Setting', + children: [ + { + name: 'timer', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], }, ], - assigner: [], - select: [ + listener: [ { - name: 'entity', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, + groupName: 'Entity Listening Setting', + children: [ + { + name: 'entities', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], }, ], - listener: [ + ifelse: [ { - name: 'listener', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, + children: [ + { + name: 'when', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], }, ], code: [ { - name: 'input_vars', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, + groupName: 'Input Variables', + children: [ + { + name: 'input_vars', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], + }, + { + children: [ + { + name: 'code', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], + }, + { + groupName: 'Output Variables', + children: [ + { + name: 'output_vars', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], }, ], - ifelse: [ + service: [ + { + groupName: 'Service Setting', + children: [ + { + name: 'service', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + ], + }, + { + groupName: 'Input Variables', + helperText: 'Please select the service you want to call first.', + children: [], + }, + ], + assigner: [ + { + groupName: 'Assignment Setting', + children: [ + { + name: 'assignments', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], + }, + ], + select: [ + { + groupName: 'Entity Select Setting', + children: [ + { + name: 'entitySelect', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], + }, + ], + email: [ + { + groupName: 'Email Sending Source', + children: [ + { + name: 'emailType', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + { + name: 'emailApiKey', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + ], + }, + { + groupName: 'Email Content', + children: [ + { + name: 'emailRecipient', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + { + name: 'content', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], + }, + ], + webhook: [ { - name: 'condition', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, + groupName: 'Webhook Arguments', + children: [ + { + name: 'webhookUrl', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + { + name: 'secretKey', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + ], }, ], }; - return result[node.type!]; - }, [node]); + return result; + }, []); - return formItems; + return !node?.type ? [] : formConfigs[node.type] || []; }; export default useNodeFormItems; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 67bcd55e..d2561012 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -1,34 +1,37 @@ -import { useState, useCallback, useMemo, useLayoutEffect, useEffect } from 'react'; -import { - Panel, - useReactFlow, - type Node, - type ReactFlowProps, - type UseOnSelectionChangeOptions, -} from '@xyflow/react'; +import { useMemo, useLayoutEffect, useEffect, useRef } from 'react'; +import { Panel, useReactFlow } from '@xyflow/react'; import cls from 'classnames'; +import { isEqual } from 'lodash-es'; +import { useDebounceEffect } from 'ahooks'; import { Stack, IconButton, Divider } from '@mui/material'; -import { useForm, Controller, type SubmitHandler } from 'react-hook-form'; -// import { FormControl, InputLabel, Select, MenuItem } from '@mui/material'; +import { useForm, Controller } from 'react-hook-form'; import { useI18n } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; import useWorkflow from '../../hooks/useWorkflow'; -import { useCommonFormItems, useNodeFormItems, type CommonFormDataProps } from './hooks'; +import { + useCommonFormItems, + useNodeFormItems, + type CommonFormDataProps, + type NodeFormDataProps, +} from './hooks'; import useConfigPanelStore from './store'; import './style.less'; +type FormDataProps = CommonFormDataProps & NodeFormDataProps; + /** * Config Panel */ const ConfigPanel = () => { const { getIntlText } = useI18n(); + const { updateNode, updateNodeData } = useReactFlow(); // ---------- Handle Node-related logic ---------- - const { updateNode } = useReactFlow(); - const { selectedNode } = useWorkflow(); + const { getSelectedNode } = useWorkflow(); + const selectedNode = useMemo(() => getSelectedNode(), [getSelectedNode]); + const openPanel = !!selectedNode; const nodeConfig = useMemo(() => { - console.log({ selectedNode }); if (!selectedNode) return; return basicNodeConfigs[selectedNode.type as WorkflowNodeType]; @@ -38,14 +41,58 @@ const ConfigPanel = () => { const getEntityList = useConfigPanelStore(state => state.getEntityList); useLayoutEffect(() => { - if (!selectedNode) return; + if (!openPanel) return; getEntityList(undefined, true); - }, [selectedNode, getEntityList]); + }, [openPanel, getEntityList]); // ---------- Handle Form-related logic ---------- - const { control, formState, handleSubmit, reset } = useForm(); + const { control, setValue, watch, reset } = useForm(); const commonFormItems = useCommonFormItems(); - const nodeFormItems = useNodeFormItems(selectedNode); + const nodeFormGroups = useNodeFormItems(selectedNode); + const allFormData = watch(); + const preFormData = useRef(); + const formDataInit = useRef(false); + // Use ref value to avoid unnecessary re-renders + const latestFormData = useMemo(() => { + if (isEqual(preFormData.current, allFormData)) { + return preFormData.current; + } + + preFormData.current = allFormData; + return allFormData; + }, [allFormData]); + + // Backfill form data + useEffect(() => { + if (!selectedNode) { + formDataInit.current = false; + return; + } + const nodeData = selectedNode.data; + + reset(); + /** + * Since node form items are rendered dynamically, `SetTimeout` is used here to + * ensure that the initial data is filled in after the rendering is complete. + */ + setTimeout(() => { + Object.keys(nodeData).forEach(key => { + setValue(key, nodeData[key]); + }); + formDataInit.current = true; + }, 0); + }, [selectedNode, reset, setValue]); + + // Save node data + useDebounceEffect( + () => { + if (!openPanel || !formDataInit.current) return; + + updateNodeData(selectedNode.id, latestFormData || {}); + }, + [openPanel, selectedNode, latestFormData, updateNodeData], + { wait: 300 }, + ); return ( {
- {nodeFormItems?.map(props => ( - - {...props} - key={props.name} - control={control} - /> + {nodeFormGroups.map(({ groupName, children: formItems }, index) => ( + // eslint-disable-next-line react/no-array-index-key +
+ {!!groupName && ( +
{groupName}
+ )} +
+ {formItems?.map(props => ( + + {...props} + key={props.name} + control={control} + /> + ))} +
+
))}
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less index 17bda124..b694301b 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less @@ -56,5 +56,24 @@ .@{prefix}-divider { margin: @margin-xl 0; } + + .@{prefix}-node-form-group { + padding-bottom: @padding-xl; + border-bottom: 1px solid var(--border-color-base); + + &:last-child { + border-bottom: none; + } + + + .@{prefix}-node-form-group { + margin-top: @margin-xl; + } + + &-title { + margin-bottom: @margin-md; + font-weight: @font-weight-bold; + .text-size(@font-size-lg); + } + } } } diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts index 70a21ac9..ca613b59 100644 --- a/apps/web/src/pages/workflow/views/editor/constants.ts +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -8,6 +8,11 @@ export const MIN_ZOOM = 0.25; */ export const MAX_ZOOM = 2; +/** + * Parallel limit + */ +export const PARALLEL_LIMIT = 10; + /** * Parallel nesting layer limit */ diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json index 3c223375..d7ba5b3b 100644 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -9,7 +9,7 @@ "x": 0, "y": 0 }, - "type": "trigger" + "type": "timer" }, { "id": "2", @@ -92,6 +92,33 @@ "y": 200 }, "type": "select" + }, + { + "id": "6", + "data": {}, + "position": { + "x": 900, + "y": 0 + }, + "type": "assigner" + }, + { + "id": "7", + "data": {}, + "position": { + "x": 900, + "y": 100 + }, + "type": "email" + }, + { + "id": "8", + "data": {}, + "position": { + "x": 900, + "y": 200 + }, + "type": "webhook" } ], "edges": [ @@ -121,6 +148,24 @@ "target": "5", "type": "addable", "sourceHandle": "case-else" + }, + { + "id": "3-6", + "source": "3", + "target": "6", + "type": "addable" + }, + { + "id": "4-7", + "source": "4", + "target": "7", + "type": "addable" + }, + { + "id": "5-8", + "source": "5", + "target": "8", + "type": "addable" } ] } diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index 2822d67a..77e640b7 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -65,7 +65,7 @@ const useInteractions = () => { fitView, flowToScreenPosition, } = useReactFlow(); - const { checkNestedParallelLimit } = useWorkflow(); + const { checkParallelLimit, checkNestedParallelLimit } = useWorkflow(); const { width: bodyWidth, height: bodyHeight } = useSize(document.querySelector('body')) || {}; // Handle nodes connect @@ -218,6 +218,8 @@ const useInteractions = () => { }; const outgoers = getOutgoers(prevNode, nodes, edges); + if (!checkParallelLimit(prevNodeId!, prevNodeSourceHandle)) return; + if (!outgoers.length) { newNode.position = { x: @@ -270,6 +272,7 @@ const useInteractions = () => { setEdges, fitView, flowToScreenPosition, + checkParallelLimit, checkNestedParallelLimit, ], ); diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index d4df0053..176fd022 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -10,7 +10,7 @@ import { import { uniqBy } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; -import { PARALLEL_DEPTH_LIMIT } from '../constants'; +import { PARALLEL_LIMIT, PARALLEL_DEPTH_LIMIT } from '../constants'; import { getParallelInfo } from './utils'; const useWorkflow = () => { @@ -29,30 +29,33 @@ const useWorkflow = () => { return node; }, [nodes]); + // Use nodeId, nodeType to avoid frequent render triggers + const selectedNodeId = selectedNode?.id; + const selectedNodeType = selectedNode?.type; - // Check node connection cycle - const isValidConnection = useCallback( - connection => { - // we are using getNodes and getEdges helpers here - // to make sure we create isValidConnection function only once - const nodes = getNodes(); + // Check Parallel Limit + const checkParallelLimit = useCallback( + (nodeId: ApiKey, nodeHandle?: string | null) => { const edges = getEdges(); - const target = nodes.find(node => node.id === connection.target); - const hasCycle = (node: WorkflowNode, visited = new Set()) => { - if (visited.has(node.id)) return false; - - visited.add(node.id); + const connectedEdges = edges.filter( + edge => + edge.source === nodeId && + ((!nodeHandle && !edge.sourceHandle) || edge.sourceHandle === nodeHandle), + ); - for (const outgoer of getOutgoers(node, nodes, edges)) { - if (outgoer.id === connection.source) return true; - if (hasCycle(outgoer, visited)) return true; - } - }; + if (connectedEdges.length > PARALLEL_LIMIT - 1) { + toast.error({ + key: 'parallel-limit', + content: getIntlText('workflow.label.parallel_limit_tip', { + 1: PARALLEL_LIMIT, + }), + }); + return false; + } - if (target?.id === connection.source) return false; - return !hasCycle(target!); + return true; }, - [getNodes, getEdges], + [getEdges, getIntlText], ); // Check nested parallel limit @@ -60,7 +63,7 @@ const useWorkflow = () => { (nodes: WorkflowNode[], edges: WorkflowEdge[], parentNodeId?: ApiKey) => { const { parallelList, hasAbnormalEdges } = getParallelInfo(nodes, edges, parentNodeId); - console.log({ parallelList, hasAbnormalEdges }); + // console.log({ parallelList, hasAbnormalEdges }); if (hasAbnormalEdges) return false; const isGtLimit = parallelList.some(item => item.depth > PARALLEL_DEPTH_LIMIT); @@ -80,10 +83,39 @@ const useWorkflow = () => { [getIntlText], ); + // Check node connection cycle + const isValidConnection = useCallback( + connection => { + // we are using getNodes and getEdges helpers here + // to make sure we create isValidConnection function only once + const nodes = getNodes(); + const edges = getEdges(); + const target = nodes.find(node => node.id === connection.target); + const hasCycle = (node: WorkflowNode, visited = new Set()) => { + if (visited.has(node.id)) return false; + + visited.add(node.id); + + for (const outgoer of getOutgoers(node, nodes, edges)) { + if (outgoer.id === connection.source) return true; + if (hasCycle(outgoer, visited)) return true; + } + }; + + if (!checkParallelLimit(connection.source, connection.sourceHandle)) return false; + + if (target?.id === connection.source) return false; + return !hasCycle(target!); + }, + [getNodes, getEdges, checkParallelLimit], + ); + // Get the only selected node that is not dragging const getSelectedNode = useCallback(() => { - const selectedNodes = nodes.filter(item => item.selected); - + const nodes = getNodes(); + const selectedNodes = nodes.filter( + item => item.id === selectedNodeId && item.type === selectedNodeType, + ); const node = selectedNodes?.[0]; if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { @@ -91,7 +123,7 @@ const useWorkflow = () => { } return node; - }, [nodes]); + }, [selectedNodeId, selectedNodeType, getNodes]); const getIncomeNodes = useCallback( (currentNode?: WorkflowNode) => { @@ -125,8 +157,9 @@ const useWorkflow = () => { return { nodes, edges, - selectedNode, + // selectedNode, isValidConnection, + checkParallelLimit, checkNestedParallelLimit, getSelectedNode, getIncomeNodes, diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 51f75046..4c85aec8 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -161,5 +161,6 @@ "common.label.create": "Create", "common.label.test": "Test", "common.label.run": "Run", - "common.label.target": "Target" + "common.label.target": "Target", + "common.label.week": "Week" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 6b9a8625..8aa81e6e 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -38,6 +38,20 @@ "workflow.message.delete_tip": "Are you sure you want to delete the workflow? It cannot be restore after deletion.", "workflow.label.deletion": "Deletion", "workflow.editor.form_param_select_placeholder": "Enter or reference a variable", - "workflow.node.config_panel_lable_arguments": "Arguments", - "workflow.node.trigger_switch_label": "Conttext" + "workflow.node.trigger_switch_label": "Context", + "workflow.editor.form_param_timer_type": "Timer Type", + "workflow.editor.form_param_execution_time_queue": "Execution Time Queue", + "workflow.editor.form_param_execution_time": "Execution Time", + "workflow.editor.form_param_expire_time": "Expire Time", + "workflow.editor.form_param_timer_type_once": "Single Execution", + "workflow.editor.form_param_timer_type_cycle": "Cycle Execution", + "workflow.editor.form_param_timer_period_everyday": "Every Day", + "workflow.editor.form_param_timer_period_monday": "Monday", + "workflow.editor.form_param_timer_period_tuesday": "Tuesday", + "workflow.editor.form_param_timer_period_wednesday": "Wednesday", + "workflow.editor.form_param_timer_period_thursday": "Thursday", + "workflow.editor.form_param_timer_period_friday": "Friday", + "workflow.editor.form_param_timer_period_saturday": "Saturday", + "workflow.editor.form_param_timer_period_sunday": "Sunday", + "workflow.label.parallel_limit_tip": "并行分支限制为 {1} 个" } diff --git a/packages/shared/src/components/icons/components/code.tsx b/packages/shared/src/components/icons/components/code.tsx new file mode 100644 index 00000000..25f49daa --- /dev/null +++ b/packages/shared/src/components/icons/components/code.tsx @@ -0,0 +1,17 @@ +import { createSvgIcon } from '@mui/material/utils'; + +const CodeIcon = createSvgIcon( + + + + , + 'CodeIcon', +); + +export default CodeIcon; diff --git a/packages/shared/src/components/icons/components/index.ts b/packages/shared/src/components/icons/components/index.ts index b5d32afc..d9ede9b9 100644 --- a/packages/shared/src/components/icons/components/index.ts +++ b/packages/shared/src/components/icons/components/index.ts @@ -1,3 +1,4 @@ export { default as EntityIcon } from './entity'; export { default as EntityFilledIcon } from './entity-filled'; export { default as WorkflowIcon } from './workflow'; +export { default as CodeIcon } from './code'; diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index f68c4276..d8311ba0 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -92,6 +92,7 @@ export { SettingsOutlined as SettingsOutlinedIcon, KeyboardArrowUp as KeyboardArrowUpIcon, KeyboardArrowDown as KeyboardArrowDownIcon, + Sync as SyncIcon, } from '@mui/icons-material'; export * from './iot-icons'; diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index 72c01429..5e177711 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -94,6 +94,16 @@ declare type TriggerNodeDataType = BaseNodeDataType<{ }[]; }>; +declare type TimePeriodType = + | 'EVERYDAY' + | 'Monday' + | 'Tuesday' + | 'Wednesday' + | 'Thursday' + | 'Friday' + | 'Saturday' + | 'Sunday'; + /** * 定时器节点参数类型 */ @@ -104,26 +114,18 @@ declare type TimerNodeDataType = BaseNodeDataType<{ * @param CYCLE 周期执行 */ type: 'ONCE' | 'CYCLE'; - /** 首次执行时间 */ - firstExecutionTime?: number; + /** 执行时间 */ + executionTime?: number; /** 过期时间,默认 2035/01/01 00:00 */ expireTime?: number; /** 周期配置 */ settings?: { /** 执行周期 */ - period: - | 'EVERYDAY' - | 'Monday' - | 'Tuesday' - | 'Wednesday' - | 'Thursday' - | 'Friday' - | 'Saturday' - | 'Sunday'; + period?: TimePeriodType; /** * 执行时间,该数据为零点到所选时间点的毫秒数,默认 32400000(09:00) */ - time: number; + time?: number; }[]; }>; From cda353687b0b10fc448487d1fa9e4d9534984e4c Mon Sep 17 00:00:00 2001 From: jimco Date: Wed, 18 Dec 2024 10:19:26 +0800 Subject: [PATCH 038/172] feat: complete the ConfigPanel form --- .../components/code-editor/index.tsx | 18 ++++++++- .../components/conditions-input/index.tsx | 2 +- .../components/markdown-editor/index.tsx | 18 ++++++++- .../components/param-input-select/index.tsx | 6 +-- .../components/param-select/index.tsx | 6 +-- .../config-panel/hooks/useNodeFormItems.tsx | 14 +++++-- .../pages/workflow/views/editor/constants.ts | 5 +++ .../views/editor/hooks/useWorkflow.ts | 37 +++++++++++++++++-- .../src/pages/workflow/views/editor/index.tsx | 4 +- packages/locales/src/lang/en/workflow.json | 3 +- 10 files changed, 94 insertions(+), 19 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx index fbef3be9..63d5acfb 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -1,10 +1,24 @@ +import React from 'react'; +import { TextField, type TextFieldProps } from '@mui/material'; + +export type CodeEditorProps = TextFieldProps; + /** * Code Editor Component * * Note: Use in CodeNode, IfelseNode */ -const CodeEditor = () => { - return <>Code Editor; +const CodeEditor: React.FC = ({ ...props }) => { + return ( + + ); }; export default CodeEditor; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 120f7c00..ff6c26a7 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -73,7 +73,7 @@ const ConditionsInput = () => { 222 - + diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx index d45ab8ae..b74c7659 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx @@ -1,10 +1,24 @@ +import React from 'react'; +import { TextField, type TextFieldProps } from '@mui/material'; + +export type MarkdownEditorProps = TextFieldProps; + /** * Markdown Editor Component * * Note: Use in EmailNode */ -const MarkdownEditor = () => { - return <>Markdown Editor; +const MarkdownEditor: React.FC = ({ ...props }) => { + return ( + + ); }; export default MarkdownEditor; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx index 4d15f78f..ef56df7e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx @@ -68,7 +68,7 @@ const ParamInputSelect: React.FC = ({ ...props }) => { const { getIntlText } = useI18n(); - const { getIncomeNodes } = useWorkflow(); + const { getUpstreamNodes } = useWorkflow(); const containerRef = useRef(null); const [data, setData] = useControllableValue(props); const [inputValue, setInputValue] = useState(''); @@ -77,7 +77,7 @@ const ParamInputSelect: React.FC = ({ const { width: containerWidth } = useSize(containerRef) || {}; const [options, renderedOptions] = useMemo(() => { - const incomeNodes = getIncomeNodes(); + const incomeNodes = getUpstreamNodes(); const result = incomeNodes.reduce((acc, node) => { // TODO: get the correct nodes params demoOutputs.forEach(output => { @@ -128,7 +128,7 @@ const ParamInputSelect: React.FC = ({ }, [] as React.ReactNode[]); return [result, renderedOptions]; - }, [selectValue, getIncomeNodes, setData]); + }, [selectValue, getUpstreamNodes, setData]); useLayoutEffect(() => { // Direct input value diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx index 1b2dff9e..210f4fd4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -36,10 +36,10 @@ export type ParamSelectProps = SelectProps; */ const ParamSelect: React.FC = ({ label, required, disabled, ...props }) => { const { getIntlText } = useI18n(); - const { getIncomeNodes } = useWorkflow(); + const { getUpstreamNodes } = useWorkflow(); const renderOptions = useCallback(() => { - const incomeNodes = getIncomeNodes(); + const incomeNodes = getUpstreamNodes(); // TODO: get the correct nodes params const data: OptionItemType[] = incomeNodes.map(node => ({ nodeId: node.id, @@ -73,7 +73,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, ..
)), ]); - }, [getIncomeNodes]); + }, [getUpstreamNodes]); return (
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index cd173747..f37d8dac 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -101,8 +101,8 @@ const useNodeFormItems = (node?: WorkflowNode) => { children: [ { name: 'code', - render({ field: { onChange, value } }) { - return ; + render({ field: { onChange, value }, fieldState, formState }) { + return ; }, }, ], @@ -178,6 +178,8 @@ const useNodeFormItems = (node?: WorkflowNode) => { render({ field: { onChange, value } }) { return ( { render({ field: { onChange, value } }) { return ( { render({ field: { onChange, value } }) { return ( { { name: 'content', render({ field: { onChange, value } }) { - return ; + return ; }, }, ], @@ -233,6 +239,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { render({ field: { onChange, value } }) { return ( { render({ field: { onChange, value } }) { return ( item.category === 'entry') + .map(item => item.type); + const useWorkflow = () => { const { getNodes, getEdges } = useReactFlow(); const nodes = useNodes(); @@ -33,6 +38,26 @@ const useWorkflow = () => { const selectedNodeId = selectedNode?.id; const selectedNodeType = selectedNode?.type; + // Check entry node number limit + const checkEntryNodeNumberLimit = useCallback(() => { + const nodes = getNodes(); + const entryNodes = nodes.filter(node => + entryNodeTypes.includes(node.type as WorkflowNodeType), + ); + + if (entryNodes.length > 1) { + toast.error({ + key: 'entry-node-number-limit', + content: getIntlText('workflow.label.entry_node_number_limit_tip', { + 1: ENTRY_NODE_NUMBER_LIMIT, + }), + }); + return false; + } + + return true; + }, [getNodes, getIntlText]); + // Check Parallel Limit const checkParallelLimit = useCallback( (nodeId: ApiKey, nodeHandle?: string | null) => { @@ -125,7 +150,8 @@ const useWorkflow = () => { return node; }, [selectedNodeId, selectedNodeType, getNodes]); - const getIncomeNodes = useCallback( + // Get all upstream nodes of the current node + const getUpstreamNodes = useCallback( (currentNode?: WorkflowNode) => { currentNode = currentNode || getSelectedNode(); const getAllIncomers = ( @@ -154,6 +180,10 @@ const useWorkflow = () => { [nodes, edges, getSelectedNode], ); + // const checkFreeNodeLimit = useCallback(() => { + + // }, [getNodes, getUpstreamNodes]); + return { nodes, edges, @@ -161,8 +191,9 @@ const useWorkflow = () => { isValidConnection, checkParallelLimit, checkNestedParallelLimit, + checkEntryNodeNumberLimit, getSelectedNode, - getIncomeNodes, + getUpstreamNodes, }; }; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 27403132..523623db 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -46,7 +46,7 @@ const WorkflowEditor = () => { const { getIntlText } = useI18n(); const nodeTypes = useNodeTypes(); const { toObject } = useReactFlow(); - const { isValidConnection } = useWorkflow(); + const { isValidConnection, checkNestedParallelLimit } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); @@ -132,7 +132,9 @@ const WorkflowEditor = () => { const handleSave = () => { const data = toObject(); + if (!checkNestedParallelLimit(data.nodes, data.edges)) return; // TODO: check the workflow json is valid + console.log('workflow data', data); }; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 8aa81e6e..71aa7c14 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -53,5 +53,6 @@ "workflow.editor.form_param_timer_period_friday": "Friday", "workflow.editor.form_param_timer_period_saturday": "Saturday", "workflow.editor.form_param_timer_period_sunday": "Sunday", - "workflow.label.parallel_limit_tip": "并行分支限制为 {1} 个" + "workflow.label.parallel_limit_tip": "并行分支限制为 {1} 个", + "workflow.label.entry_node_number_limit_tip": "工作流中必须且仅允许有 {1} 个入口节点" } From 92bdf4673b912be84b8ec15388f442d99d4bb2dd Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 18 Dec 2024 11:13:23 +0800 Subject: [PATCH 039/172] feat: Run log popup adds scrolling loading logic --- .../log-modal/components/log-item/index.tsx | 8 +- .../log-modal/components/log-item/style.less | 2 +- .../components/log-modal/hooks/index.tsx | 2 + .../log-modal/hooks/useRenderList.tsx | 98 ++++++++++++++++ .../log-modal/hooks/useSourceData.tsx | 36 ++++++ .../workflow/components/log-modal/index.tsx | 111 ++++++++++-------- .../workflow/components/log-modal/style.less | 35 +++++- .../workflow/components/log-modal/types.d.ts | 22 +++- apps/web/src/services/http/workflow.ts | 20 ++-- packages/locales/src/lang/en/workflow.json | 3 +- 10 files changed, 263 insertions(+), 74 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/log-modal/hooks/index.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx index 26b103a2..55defc7c 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx @@ -1,6 +1,5 @@ import React, { useMemo } from 'react'; import cls from 'classnames'; -import { useTime } from '@milesight/shared/src/hooks'; import { Tooltip } from '@/components'; import { LogStatusMap } from '@/pages/workflow/config'; import type { LogItemProps } from '../../types'; @@ -12,8 +11,7 @@ export interface IProps { onClick?: (data: LogItemProps) => void; } export default React.memo(({ data, isActive, onClick }: IProps) => { - const { status, title, timestamp } = data || {}; - const { getTimeFormat } = useTime(); + const { status, title } = data || {}; const { className: statusClassName, icon } = useMemo(() => LogStatusMap[status], [status]); return ( @@ -28,14 +26,14 @@ export default React.memo(({ data, isActive, onClick }: IProps) => {

-

+ {/*

-

+

*/}
); diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less index acd69084..c8205db9 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less @@ -17,6 +17,7 @@ } .ms-log-status { + display: flex; margin-right: @margin-xxs; &__success { @@ -38,7 +39,6 @@ } .ms-log-title { - margin-bottom: @margin-xxs; font-weight: @font-weight-medium; .text-size(@font-size-md); } diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/index.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/index.tsx new file mode 100644 index 00000000..27fe449c --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/index.tsx @@ -0,0 +1,2 @@ +export { useSourceData } from './useSourceData'; +export { useRenderList } from './useRenderList'; diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx new file mode 100644 index 00000000..2567e5c7 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx @@ -0,0 +1,98 @@ +import { useCallback, useMemo, useState } from 'react'; +import { useInfiniteScroll, useVirtualList } from 'ahooks'; +import { useTime } from '@milesight/shared/src/hooks'; +import { generateUUID } from '@milesight/shared/src/utils/tools'; +import { awaitWrap } from '@/services/http'; +import type { + InfiniteScrollType, + LogItemProps, + LogListPageType, + LogRenderListType, + PaginationModel, +} from '../types'; + +interface IProps { + containerRef: React.RefObject; + listRef: React.RefObject; + getLogList: (pageInfo: PaginationModel) => Promise; +} +export const useRenderList = ({ getLogList, containerRef, listRef }: IProps) => { + const [paginationModel, setPaginationModel] = useState({ + page: 0, + pageSize: 20, + }); + const { getTimeFormat } = useTime(); + + /** get log List */ + const getRenderLogList = useCallback( + (logList: LogListPageType['content']): LogItemProps[] => { + return (logList || []).map(item => { + const { start_time: startTime, status, id } = item || {}; + return { + id, + status: status === 'ERROR' ? 'failed' : 'success', + title: startTime ? getTimeFormat(startTime, 'fullDateTimeSecondFormat') : '', + }; + }); + }, + [getTimeFormat], + ); + + /** Infinite Scroll */ + const { data, loading } = useInfiniteScroll( + async data => { + const [error, result] = await awaitWrap( + getLogList({ ...paginationModel, page: paginationModel.page + 1 }), + ); + + /** When an error is reported, the next page is not loaded */ + if (error) { + return { + ...(data || {}), + hasMore: false, + } as InfiniteScrollType; + } + + /** load success */ + const { page_number: pageNumber, page_size: pageSize, content, total } = result! || {}; + const hasMore = (pageNumber - 1) * pageSize + (content?.length || 0) < total; + setPaginationModel({ ...paginationModel, page: pageNumber, pageSize }); + + return { + list: getRenderLogList(content), + source: result, + hasMore, + }; + }, + { + target: containerRef, + isNoMore: result => !result?.hasMore, + }, + ); + + /** generate log render list */ + const renderLogList = useMemo(() => { + const { list = [], hasMore } = data || {}; + if (!hasMore) return list; + + const footer = { + id: generateUUID(), + $$isFooterNode: true, + } as unknown as LogRenderListType; + return list.concat(footer); + }, [data]); + + /** virtual list */ + const [virtualList] = useVirtualList(renderLogList, { + containerTarget: containerRef, + wrapperTarget: listRef, + itemHeight: 38, + overscan: 15, + }); + + return { + scrollItem: data, + virtualList, + getLogListLoading: loading, + }; +}; diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx new file mode 100644 index 00000000..16eff9c4 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx @@ -0,0 +1,36 @@ +import { useCallback } from 'react'; +import { delay, generateUUID, withPromiseResolvers } from '@milesight/shared/src/utils/tools'; +import type { PaginationModel, LogListPageType } from '../types'; + +export const useSourceData = () => { + // TODO + const generateList = useCallback((limit: number): LogListPageType['content'] => { + return Array.from({ length: limit }).map(() => ({ + id: generateUUID(), + status: Math.random() > 0.5 ? 'SUCCESS' : 'ERROR', + start_time: Date.now(), + })); + }, []); + // TODO + const getLogList = useCallback( + async (pageInfo: PaginationModel) => { + const { page, pageSize } = pageInfo || {}; + const { promise, resolve } = withPromiseResolvers(); + + await delay(1000); + + resolve({ + page_size: pageSize, + page_number: page, + content: generateList(pageSize), + total: 100, + }); + return promise; + }, + [generateList], + ); + + return { + getLogList, + }; +}; diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx index eb74a789..37d0b7d7 100644 --- a/apps/web/src/pages/workflow/components/log-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -1,11 +1,12 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useVirtualList } from 'ahooks'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { CircularProgress } from '@mui/material'; import { Modal, type ModalProps } from '@milesight/shared/src/components'; import { useI18n } from '@milesight/shared/src/hooks'; -import { Tooltip } from '@/components'; +import { Empty, Tooltip } from '@/components'; import { LogItem } from './components'; +import { useRenderList, useSourceData } from './hooks'; import ActionLog from '../action-log'; -import type { LogItemProps } from './types'; +import type { LogRenderListType } from './types'; import './style.less'; // TODO mock Data @@ -17,39 +18,30 @@ export default React.memo(({ visible, ...props }: IProps) => { const containerRef = useRef(null); const listRef = useRef(null); const { getIntlText } = useI18n(); - const [activeItem, setActiveItem] = useState(); + const [activeItem, setActiveItem] = useState(); - // TODO: mock data - const list: LogItemProps[] = useMemo(() => { - return Array.from({ length: 1000 }).map(() => ({ - key: Math.random().toString(36).substring(2, 9), - title: Math.random().toString(36).substring(2, 9), - status: Math.random() > 0.5 ? 'success' : 'failed', - timestamp: Date.now(), - })); - }, []); - - /** virtual list */ - const [virtualList, scrollTo] = useVirtualList(list, { - containerTarget: containerRef, - wrapperTarget: listRef, - itemHeight: 62, - overscan: 10, + const { getLogList } = useSourceData(); + const { scrollItem, virtualList, getLogListLoading } = useRenderList({ + containerRef, + listRef, + getLogList, }); /** When initializing, set the first as the default value */ useEffect(() => { - const [firstItem] = list || []; + if (activeItem) return; + const [firstItem] = scrollItem?.list || []; setActiveItem(firstItem); - scrollTo(0); - }, [list]); + }, [scrollItem, activeItem]); /** handle click left bar */ - const handleClick = useCallback((data: LogItemProps) => { + const handleClick = useCallback((data: LogRenderListType) => { setActiveItem(data); }, []); + const isLoading = !!getLogListLoading; + const isEmpty = !getLogListLoading && !scrollItem?.list?.length; return ( { {...props} >
-
-
- {virtualList.map(({ data }) => ( - - ))} -
-
-
-
- + {isLoading && ( +
+
-
- + )} + {isEmpty && ( +
+
-
+ )} + {!isLoading && !isEmpty && ( + <> +
+
+
+ {virtualList.map(({ data }) => { + if (data?.$$isFooterNode) { + return ( +
+ +
+ ); + } + return ( + + ); + })} +
+
+
+
+
+ +
+
+ +
+
+ + )}
); diff --git a/apps/web/src/pages/workflow/components/log-modal/style.less b/apps/web/src/pages/workflow/components/log-modal/style.less index 66ad09b5..b490c5a7 100644 --- a/apps/web/src/pages/workflow/components/log-modal/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/style.less @@ -15,21 +15,44 @@ display: flex; height: 100%; + .ms-log-loading, + .ms-log-empty { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + height: 100%; + } + .ms-log-left-bar { + display: flex; + flex-direction: column; width: 200px; - overflow: auto; background-color: var(--body-background); + + &__scroll { + width: 100%; + height: 100%; + overflow: auto; + } + + &__more { + display: flex; + align-items: center; + justify-content: center; + padding: @padding-xxs 0 @padding-sm 0; + } } .ms-log-right-bar { flex: 1; padding: @padding-lg; overflow-y: auto; - } - .ms-log-title { - margin-bottom: @margin-sm; - font-weight: @font-weight-medium; - .text-size(@font-size-xxl); + &__title { + margin-bottom: @margin-sm; + font-weight: @font-weight-medium; + .text-size(@font-size-xxl); + } } } diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts index 193498d1..8f4eb8e8 100644 --- a/apps/web/src/pages/workflow/components/log-modal/types.d.ts +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -1,10 +1,11 @@ +import { type WorkflowAPISchema } from '@/services/http'; import type { LogStatus } from '../../config'; export interface LogItemProps { /** * Key for Component render */ - key: string | number; + id: string | number; /** * Node status */ @@ -13,8 +14,19 @@ export interface LogItemProps { * Title */ title: string; - /** - * Timestamp - */ - timestamp: number; +} + +export type LogListPageType = WorkflowAPISchema['getLogList']['response']; + +export interface PaginationModel { + page: number; + pageSize: number; +} + +export type LogRenderListType = LogItemProps & { $$isFooterNode?: boolean }; + +export interface InfiniteScrollType { + list: LogRenderListType[]; + source: LogListPageType; + hasMore: boolean; } diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index cf4271ff..7f73bbfd 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -99,18 +99,20 @@ export interface WorkflowAPISchema extends APISchema { /** Get workflow log list */ getLogList: { - request: void | { + request: SearchRequestType & { // TODO: use workflow log status enum status?: string; }; - response: { - /** ID */ - id: ApiKey; - /** Start Time */ - start_time: number; - /** Running status */ - status: string; - }[]; + response: SearchResponseType< + { + /** ID */ + id: ApiKey; + /** Start Time */ + start_time: number; + /** Running status */ + status: 'SUCCESS' | 'ERROR'; + }[] + >; }; /** Get workflow log detail */ diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 0abd5654..05e1c9ed 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -40,5 +40,6 @@ "workflow.label.deletion": "Deletion", "workflow.editor.form_param_select_placeholder": "Enter or reference a variable", "workflow.label.branch": "Branch{1}", - "workflow.label.parallel": "Parallel{1}" + "workflow.label.parallel": "Parallel{1}", + "workflow.label.no_log_record": "No log is recorded" } From 0a6a117cf7929fa261b2ca2f89edc612c1edae65 Mon Sep 17 00:00:00 2001 From: wuzb Date: Wed, 18 Dec 2024 14:37:25 +0800 Subject: [PATCH 040/172] feat: workflow webhook add custom data form --- .../config-panel/hooks/useNodeFormItems.tsx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index f37d8dac..eaa176b5 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -239,6 +239,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { render({ field: { onChange, value } }) { return ( { }, ], }, + { + groupName: 'Custom Data', + children: [ + { + name: 'custom_data', + render({ field: { onChange, value } }) { + return ; + }, + }, + ], + }, ], }; From 7c8362358028b083283af87aee0007ae6dab78f7 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 18 Dec 2024 16:33:25 +0800 Subject: [PATCH 041/172] feat: The editor adds markdow format and support for custom commands --- apps/web/package.json | 2 + .../code-editor/components/editor/index.tsx | 108 +++++++++-------- .../code-editor/components/editor/style.less | 43 +++++++ .../code-editor/components/header/index.tsx | 78 +++++------- .../code-editor/components/header/style.less | 16 +-- .../code-editor/components/index.tsx | 1 + .../components/lang-select/index.tsx | 41 +++++++ .../components/lang-select/style.less | 14 +++ .../src/components/code-editor/constant.tsx | 8 ++ .../web/src/components/code-editor/editor.tsx | 49 ++++++++ .../components/code-editor/hooks/index.tsx | 1 + .../code-editor/hooks/useEditorCommand.tsx | 79 ++++++++++++ apps/web/src/components/code-editor/index.tsx | 47 ++------ .../web/src/components/code-editor/types.d.ts | 112 +++++++++++++++--- apps/web/src/components/index.ts | 12 +- pnpm-lock.yaml | 73 ++++++++++++ 16 files changed, 517 insertions(+), 167 deletions(-) create mode 100644 apps/web/src/components/code-editor/components/lang-select/index.tsx create mode 100644 apps/web/src/components/code-editor/components/lang-select/style.less create mode 100644 apps/web/src/components/code-editor/editor.tsx create mode 100644 apps/web/src/components/code-editor/hooks/useEditorCommand.tsx diff --git a/apps/web/package.json b/apps/web/package.json index 8db75875..1c7901f7 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -13,9 +13,11 @@ "ts-check": "tsc --noEmit" }, "dependencies": { + "@codemirror/commands": "^6.7.1", "@codemirror/lang-java": "^6.0.1", "@codemirror/lang-javascript": "^6.2.2", "@codemirror/lang-json": "^6.0.1", + "@codemirror/lang-markdown": "^6.3.1", "@codemirror/lang-python": "^6.1.6", "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.10.6", diff --git a/apps/web/src/components/code-editor/components/editor/index.tsx b/apps/web/src/components/code-editor/components/editor/index.tsx index 3cb26a60..e0ac6ecc 100644 --- a/apps/web/src/components/code-editor/components/editor/index.tsx +++ b/apps/web/src/components/code-editor/components/editor/index.tsx @@ -1,18 +1,19 @@ -import React, { useCallback, useMemo } from 'react'; +import React, { forwardRef, useCallback, useMemo } from 'react'; import cls from 'classnames'; -import CodeMirror from '@uiw/react-codemirror'; +import CodeMirror, { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; // support language import { yaml } from '@codemirror/lang-yaml'; import { json } from '@codemirror/lang-json'; import { java } from '@codemirror/lang-java'; import { python } from '@codemirror/lang-python'; import { javascript } from '@codemirror/lang-javascript'; +import { markdown } from '@codemirror/lang-markdown'; // extension language import { StreamLanguage } from '@codemirror/language'; import { groovy } from '@codemirror/legacy-modes/mode/groovy'; import { useEditorTheme } from '../../hooks'; -import { EditorContentProps, type EditorSupportLang } from '../../types'; +import type { EditorContentProps, EditorSupportLang } from '../../types'; import './style.less'; interface IProps extends EditorContentProps { @@ -20,8 +21,8 @@ interface IProps extends EditorContentProps { editorValue: string; setEditorValue: (value: string) => void; } -export default React.memo( - ({ +export const CodeEditorContent = forwardRef((props, ref) => { + const { showLineNumber = true, showFold = true, editable = true, @@ -31,52 +32,57 @@ export default React.memo( fontSize, setEditorValue, ...rest - }: IProps) => { - const { editorTheme } = useEditorTheme({ fontSize }); + } = props; + const { editorTheme } = useEditorTheme({ fontSize }); - /** editor input change callback */ - const onInputChange = useCallback( - (value: string) => { - setEditorValue(value); - }, - [setEditorValue], - ); + /** editor input change callback */ + const onInputChange = useCallback( + (value: string) => { + setEditorValue(value); + }, + [setEditorValue], + ); - /** Select the corresponding extension based on the language */ - const extensions = useMemo(() => { - switch (editorLang) { - case 'yaml': - return [yaml()]; - case 'json': - return [json()]; - case 'mvel': - // The mvel language is similar to java, which is used here for highlighting - return [java()]; - case 'python': - return [python()]; - case 'javascript': - return [javascript()]; - case 'groovy': - return [StreamLanguage.define(groovy)]; - default: - return []; - } - }, [editorLang]); + /** Select the corresponding extension based on the language */ + const extensions = useMemo(() => { + switch (editorLang) { + case 'yaml': + return [yaml()]; + case 'json': + return [json()]; + case 'mvel': + // The mvel language is similar to java, which is used here for highlighting + return [java()]; + case 'python': + return [python()]; + case 'javascript': + return [javascript()]; + case 'groovy': + return [StreamLanguage.define(groovy)]; + case 'markdown': + return [markdown()]; + case 'text': + default: + return []; + } + }, [editorLang]); - return ( - - ); - }, -); + return ( + + ); +}); + +export default React.memo(CodeEditorContent); diff --git a/apps/web/src/components/code-editor/components/editor/style.less b/apps/web/src/components/code-editor/components/editor/style.less index b0c77f07..b4e1ba1a 100644 --- a/apps/web/src/components/code-editor/components/editor/style.less +++ b/apps/web/src/components/code-editor/components/editor/style.less @@ -9,6 +9,49 @@ .cm-gutters { border: unset; user-select: none; + + &:hover { + .cm-gutterElement { + span { + &::after { + color: var(--text-color-tertiary); + transition: color 0.2s ease-in-out; + } + } + } + } + } + + .cm-foldGutter { + .cm-gutterElement { + position: relative; + + span { + color: transparent; + + &::after { + position: absolute; + top: 50%; + left: 0; + font-size: 12px; + line-height: 1; + color: transparent; + transform: translateY(-50%); + } + } + + span[title='Fold line'] { + &::after { + content: '∨'; + } + } + + span[title='Unfold line'] { + &::after { + content: '>'; + } + } + } } } } diff --git a/apps/web/src/components/code-editor/components/header/index.tsx b/apps/web/src/components/code-editor/components/header/index.tsx index 6acfded6..a9a85023 100644 --- a/apps/web/src/components/code-editor/components/header/index.tsx +++ b/apps/web/src/components/code-editor/components/header/index.tsx @@ -1,64 +1,46 @@ -import React, { useCallback, useMemo } from 'react'; -import { MenuItem, Select } from '@mui/material'; -import { type SelectInputProps } from '@mui/material/Select/SelectInput'; -import { ContentCopyIcon, ExpandMoreIcon } from '@milesight/shared/src/components'; +import React, { useCallback } from 'react'; +import { ContentCopyIcon } from '@milesight/shared/src/components'; import { useCopy } from '@milesight/shared/src/hooks'; -import { editorLangOptions } from '../../constant'; -import { type EditorSupportLang, EditorProps } from '../../types'; +import EditorSelect from '../lang-select'; +import type { EditorToolbarProps } from '../../types'; import './style.less'; -interface IProps extends Pick { - editorValue: string; - editorLang?: EditorSupportLang; - setEditorLang: (lang: EditorSupportLang) => void; -} export default React.memo( - ({ editorValue, editorLang, setEditorLang, header, supportLangs }: IProps) => { - const { icon, title } = header || {}; + ({ + editorValue, + editorLang, + setEditorLang, + icon, + title, + renderOptions, + }: EditorToolbarProps) => { const { handleCopy } = useCopy(); - /** select change callback */ - const handleChange = useCallback>['onChange']>( - e => { - const { value } = e.target; - setEditorLang(value as EditorSupportLang); - }, - [setEditorLang], - ); - /** copy button callback */ const handleCopyIcon = useCallback(() => { handleCopy?.(editorValue); }, [editorValue, handleCopy]); - /** custom select options */ - const customEditorLangOptions = useMemo(() => { - if (!supportLangs?.length) return editorLangOptions; - - return editorLangOptions.filter(item => supportLangs.includes(item.lang)); - }, [supportLangs]); - return (
- {title || ( - - )} - {icon || ( -
- -
- )} +
+ {title === void 0 ? ( + + ) : ( + title + )} +
+
+ {icon === void 0 ? ( + + ) : ( + icon + )} +
); }, diff --git a/apps/web/src/components/code-editor/components/header/style.less b/apps/web/src/components/code-editor/components/header/style.less index 4d4c7e00..0b1a8c43 100644 --- a/apps/web/src/components/code-editor/components/header/style.less +++ b/apps/web/src/components/code-editor/components/header/style.less @@ -6,27 +6,17 @@ background-color: var(--component-background-gray); border-bottom: @border-base; - .ms-header-select { - min-width: 100px; + &__title { color: var(--text-color-secondary); .text-size(@font-size-md); - - .MuiSelect-select.MuiInputBase-input { - padding: 0; - padding-right: @padding-xxl; - } - - .MuiOutlinedInput-notchedOutline { - border: unset; - } } - .ms-header-action { + &__operations { display: flex; align-items: center; justify-content: space-between; - .MuiSvgIcon-root { + .ms-header-copy { cursor: pointer; &:hover { diff --git a/apps/web/src/components/code-editor/components/index.tsx b/apps/web/src/components/code-editor/components/index.tsx index ff887c20..fcfa52d2 100644 --- a/apps/web/src/components/code-editor/components/index.tsx +++ b/apps/web/src/components/code-editor/components/index.tsx @@ -1,2 +1,3 @@ export { default as EditorHeader } from './header'; +export { default as EditorSelect } from './lang-select'; export { default as EditorComponent } from './editor'; diff --git a/apps/web/src/components/code-editor/components/lang-select/index.tsx b/apps/web/src/components/code-editor/components/lang-select/index.tsx new file mode 100644 index 00000000..09d25199 --- /dev/null +++ b/apps/web/src/components/code-editor/components/lang-select/index.tsx @@ -0,0 +1,41 @@ +import React, { useCallback } from 'react'; +import { MenuItem, Select } from '@mui/material'; +import { type SelectInputProps } from '@mui/material/Select/SelectInput'; +import { ExpandMoreIcon } from '@milesight/shared/src/components'; +import { editorLangOptions } from '../../constant'; +import type { EditorSupportLang, EditorSelectProps } from '../../types'; +import './style.less'; + +export default React.memo( + ({ editorLang, onEditorLangChange, renderOptions }: EditorSelectProps) => { + /** select change callback */ + const handleChange = useCallback>['onChange']>( + e => { + const { value } = e.target; + onEditorLangChange?.(value as EditorSupportLang, e); + }, + [onEditorLangChange], + ); + + return ( + + ); + }, +); diff --git a/apps/web/src/components/code-editor/components/lang-select/style.less b/apps/web/src/components/code-editor/components/lang-select/style.less new file mode 100644 index 00000000..cda2e480 --- /dev/null +++ b/apps/web/src/components/code-editor/components/lang-select/style.less @@ -0,0 +1,14 @@ +.ms-header-select { + min-width: 100px; + color: var(--text-color-secondary); + .text-size(@font-size-md); + + .MuiSelect-select.MuiInputBase-input { + padding: 0; + padding-right: @padding-xxl; + } + + .MuiOutlinedInput-notchedOutline { + border: unset; + } +} diff --git a/apps/web/src/components/code-editor/constant.tsx b/apps/web/src/components/code-editor/constant.tsx index c667b032..4871cab0 100644 --- a/apps/web/src/components/code-editor/constant.tsx +++ b/apps/web/src/components/code-editor/constant.tsx @@ -29,4 +29,12 @@ export const editorLangOptions: { lang: 'mvel', label: 'mvel', }, + { + lang: 'markdown', + label: 'Markdown', + }, + { + lang: 'text', + label: 'Text', + }, ]; diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx new file mode 100644 index 00000000..c4c86c16 --- /dev/null +++ b/apps/web/src/components/code-editor/editor.tsx @@ -0,0 +1,49 @@ +import React, { forwardRef, useImperativeHandle, useRef } from 'react'; +import { useControllableValue } from 'ahooks'; +import { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; +import { EditorComponent, EditorHeader } from './components'; +import { useEditorCommand } from './hooks'; +import type { EditorSupportLang, EditorProps, EditorHandlers } from './types'; +import './style.less'; + +export const CodeEditor = forwardRef((props, ref) => { + const { Header: CustomHeader, ...rest } = props; + const editorInstanceRef = useRef(null); + + const [editorLang, setEditorLang] = useControllableValue(props, { + defaultValuePropName: 'defaultEditorLang', + valuePropName: 'editorLang', + trigger: 'onLangChange', + }); + const [editorValue, setEditorValue] = useControllableValue(props, { + valuePropName: 'value', + trigger: 'onValueChange', + }); + + const { handlers } = useEditorCommand({ editorInstanceRef }); + /** Methods exposed to external components */ + useImperativeHandle(ref, () => handlers); + + const EditorHeaderComponent = CustomHeader === void 0 ? EditorHeader : CustomHeader; + return ( +
+ {CustomHeader !== null && ( + + )} + +
+ ); +}); + +export default React.memo(CodeEditor); diff --git a/apps/web/src/components/code-editor/hooks/index.tsx b/apps/web/src/components/code-editor/hooks/index.tsx index a0561be9..530fd8ab 100644 --- a/apps/web/src/components/code-editor/hooks/index.tsx +++ b/apps/web/src/components/code-editor/hooks/index.tsx @@ -1 +1,2 @@ export { useEditorTheme } from './useEditorTheme'; +export { useEditorCommand } from './useEditorCommand'; diff --git a/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx b/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx new file mode 100644 index 00000000..98f045e8 --- /dev/null +++ b/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx @@ -0,0 +1,79 @@ +import { useCallback, useMemo } from 'react'; +import { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; +import { undo as undoCommand, redo as redoCommand } from '@codemirror/commands'; +import type { EditorHandlers } from '../types'; + +interface IProps { + editorInstanceRef: React.RefObject; +} +export const useEditorCommand = ({ editorInstanceRef }: IProps) => { + /** Function to get the current EditorView instance */ + const getEditorView = useCallback(() => { + return editorInstanceRef.current?.view; + }, [editorInstanceRef]); + + /** Function to get the current EditorState instance */ + const getEditorState = useCallback(() => { + return editorInstanceRef.current?.state; + }, [editorInstanceRef]); + + /** Function to perform undo operation */ + const undo = useCallback(() => { + const editorView = getEditorView(); + if (!editorView) return; + + undoCommand(editorView); + }, [getEditorView]); + + /** Function to perform redo operation */ + const redo = useCallback(() => { + const editorView = getEditorView(); + if (!editorView) return; + + redoCommand(editorView); + }, [getEditorView]); + + /** Function to insert text at the current cursor position */ + const insert = useCallback( + async (text: string) => { + if (!text) return; + const editorState = getEditorState(); + const editorView = getEditorView(); + if (!editorState || !editorView) return; + + // insert text at the current cursor position + const { main } = editorView?.state?.selection || {}; + const { from, to } = main || {}; + const last = from + text.length; + editorView.dispatch({ + changes: { + from, + to, + insert: text, + }, + selection: { + anchor: last, + head: last, + }, + }); + + // Ensure the editor gains focus + editorView.focus(); + }, + [getEditorState, getEditorView], + ); + + const handlers = useMemo(() => { + return { + getEditorView, + getEditorState, + undo, + redo, + insert, + }; + }, [getEditorState, getEditorView, insert, redo, undo]); + + return { + handlers, + }; +}; diff --git a/apps/web/src/components/code-editor/index.tsx b/apps/web/src/components/code-editor/index.tsx index b1959bcc..51c4972c 100644 --- a/apps/web/src/components/code-editor/index.tsx +++ b/apps/web/src/components/code-editor/index.tsx @@ -1,37 +1,10 @@ -import React from 'react'; -import { useControllableValue } from 'ahooks'; -import { EditorComponent, EditorHeader } from './components'; -import type { EditorSupportLang, EditorProps } from './types'; -import './style.less'; - -export default React.memo((props: EditorProps) => { - const { header, supportLangs, ...rest } = props; - const [editorLang, setEditorLang] = useControllableValue(props, { - valuePropName: 'editorLang', - trigger: 'onLangChange', - }); - const [editorValue, setEditorValue] = useControllableValue(props, { - valuePropName: 'value', - trigger: 'onValueChange', - }); - - return ( -
- {header === null ? null : ( - - )} - -
- ); -}); +export { default as CodeEditor } from './editor'; +export { default as CodeEditorToolbar } from './components/header'; +export { default as CodeEditorSelect } from './components/lang-select'; +export type { + EditorProps, + EditorSupportLang, + EditorSelectProps, + EditorToolbarProps, + EditorHandlers, +} from './types'; diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts index 03043c42..b2b31fbd 100644 --- a/apps/web/src/components/code-editor/types.d.ts +++ b/apps/web/src/components/code-editor/types.d.ts @@ -1,10 +1,32 @@ -import { ReactCodeMirrorProps } from '@uiw/react-codemirror'; +import React from 'react'; +import type { ReactCodeMirrorProps, EditorView, EditorState } from '@uiw/react-codemirror'; +import type { SelectChangeEvent } from '@mui/material'; /** Supported languages for the code editor. */ -export type EditorSupportLang = 'groovy' | 'javascript' | 'python' | 'mvel' | 'json' | 'yaml'; +export type EditorSupportLang = + | 'groovy' + | 'javascript' + | 'python' + | 'mvel' + | 'json' + | 'yaml' + | 'markdown' + | 'text'; + +/** Props for the code editor content. */ +export interface EditorContentProps extends ReactCodeMirrorProps { + /** Whether to show line numbers in the editor. */ + showLineNumber?: boolean; + /** Whether to enable code folding in the editor. */ + showFold?: boolean; + /** The font size used in the editor. */ + fontSize?: number; +} /** Props for the code editor component. */ export interface EditorProps extends EditorContentProps { + /** Default editor language. */ + defaultEditorLang?: EditorSupportLang; /** The programming language used in the editor. */ editorLang?: EditorSupportLang; /** @@ -21,26 +43,84 @@ export interface EditorProps extends EditorContentProps { */ onValueChange?: (value: string) => void; - /** Props for the editor toolbar header. */ - header?: EditorToolbarProps | null; - /** Customize supported languages */ - supportLangs?: EditorSupportLang[]; + /** Custom editor toolbar header. */ + Header?: React.FC; +} + +/** Interface for editor language options. */ +export interface IEditorLangOption { + /** The language type. */ + lang: EditorSupportLang; + /** The label for the language. */ + label: string; } -/** Code Editor Header Props */ -export interface EditorToolbarProps { +/** Props for the editor language selection component. */ +export interface EditorSelectProps { + /** The programming language used in the editor. */ + editorLang?: EditorSupportLang; + /** + * Callback function triggered when the editor language changes. + * @param lang - The new language value. + * @param e - The selection change event. + */ + onEditorLangChange?: (lang: EditorSupportLang, e: SelectChangeEvent) => void; + /** + * Method to render language options. + * @param option - The language option. + * @returns The rendered node. + */ + renderOptions?: (option: IEditorLangOption) => React.ReactNode; +} + +/** Props for the code editor toolbar. */ +export interface EditorToolbarProps extends Pick { + /** The content value of the editor. */ + editorValue: string; + /** The programming language used in the editor. */ + editorLang?: EditorSupportLang; + /** + * Method to set the editor language. + * @param lang - The new language value. + */ + setEditorLang: (lang: EditorSupportLang) => void; /** The title displayed in the toolbar. */ title?: React.ReactNode; /** The icon displayed in the toolbar. */ icon?: React.ReactNode; + /** Interface for handling various editor operations. */ + editorHandlers: EditorHandlers; } -/** Props for the code editor content. */ -export interface EditorContentProps extends ReactCodeMirrorProps { - /** Whether to show line numbers in the editor. */ - showLineNumber?: boolean; - /** Whether to enable code folding in the editor. */ - showFold?: boolean; - /** The font size used in the editor. */ - fontSize?: number; +/** + * Interface for handling various editor operations. + */ +export interface EditorHandlers { + /** + * Returns the current EditorView instance. + * @returns {EditorView} The current EditorView instance. + */ + getEditorView: () => EditorView | void; + + /** + * Returns the current EditorState instance. + * @returns {EditorState} The current EditorState instance. + */ + getEditorState: () => EditorState | void; + + /** + * Undoes the last change in the editor. + */ + undo: () => void; + + /** + * Redoes the last undone change in the editor. + */ + redo: () => void; + + /** + * Inserts text at the current cursor position. + * @param text - The text to be inserted. + */ + insert: (text: string) => void; } diff --git a/apps/web/src/components/index.ts b/apps/web/src/components/index.ts index 2c2cf0b2..59ad05f1 100644 --- a/apps/web/src/components/index.ts +++ b/apps/web/src/components/index.ts @@ -8,5 +8,13 @@ export { default as Tooltip } from './tooltip'; export { default as DateRangePicker } from './date-range-picker'; export { default as RouteLoadingIndicator } from './route-loading-indicator'; export { default as Empty } from './empty'; -export { default as CodeEditor } from './code-editor'; -export type { EditorProps, EditorSupportLang } from './code-editor/types'; +export { + CodeEditor, + CodeEditorToolbar, + CodeEditorSelect, + type EditorProps, + type EditorSupportLang, + type EditorSelectProps, + type EditorToolbarProps, + type EditorHandlers, +} from './code-editor'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 39e3a8f2..ea9a33d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: apps/web: dependencies: + '@codemirror/commands': + specifier: ^6.7.1 + version: 6.7.1 '@codemirror/lang-java': specifier: ^6.0.1 version: 6.0.1 @@ -65,6 +68,9 @@ importers: '@codemirror/lang-json': specifier: ^6.0.1 version: 6.0.1 + '@codemirror/lang-markdown': + specifier: ^6.3.1 + version: 6.3.1 '@codemirror/lang-python': specifier: ^6.1.6 version: 6.1.6(@codemirror/view@6.35.3) @@ -707,6 +713,12 @@ packages: '@codemirror/commands@6.7.1': resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==} + '@codemirror/lang-css@6.3.1': + resolution: {integrity: sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==} + + '@codemirror/lang-html@6.4.9': + resolution: {integrity: sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==} + '@codemirror/lang-java@6.0.1': resolution: {integrity: sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==} @@ -716,6 +728,9 @@ packages: '@codemirror/lang-json@6.0.1': resolution: {integrity: sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==} + '@codemirror/lang-markdown@6.3.1': + resolution: {integrity: sha512-y3sSPuQjBKZQbQwe3ZJKrSW6Silyl9PnrU/Mf0m2OQgIlPoSYTtOvEL7xs94SVMkb8f4x+SQFnzXPdX4Wk2lsg==} + '@codemirror/lang-python@6.1.6': resolution: {integrity: sha512-ai+01WfZhWqM92UqjnvorkxosZ2aq2u28kHvr+N3gu012XqY2CThD67JPMHnGceRfXPDBmn1HnyqowdpF57bNg==} @@ -1105,9 +1120,15 @@ packages: '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} + '@lezer/css@1.1.9': + resolution: {integrity: sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==} + '@lezer/highlight@1.2.1': resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==} + '@lezer/html@1.3.10': + resolution: {integrity: sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==} + '@lezer/java@1.1.3': resolution: {integrity: sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==} @@ -1120,6 +1141,9 @@ packages: '@lezer/lr@1.4.2': resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + '@lezer/markdown@1.3.2': + resolution: {integrity: sha512-Wu7B6VnrKTbBEohqa63h5vxXjiC4pO5ZQJ/TDbhJxPQaaIoRD/6UVDhSDtVsCwVZV12vvN9KxuLL3ATMnlG0oQ==} + '@lezer/python@1.1.15': resolution: {integrity: sha512-aVQ43m2zk4FZYedCqL0KHPEUsqZOrmAvRhkhHlVPnDD1HODDyyQv5BRIuod4DadkgBEZd53vQOtXTonNbEgjrQ==} @@ -5106,6 +5130,28 @@ snapshots: '@codemirror/view': 6.35.3 '@lezer/common': 1.2.3 + '@codemirror/lang-css@6.3.1(@codemirror/view@6.35.3)': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@lezer/common': 1.2.3 + '@lezer/css': 1.1.9 + transitivePeerDependencies: + - '@codemirror/view' + + '@codemirror/lang-html@6.4.9': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/lang-css': 6.3.1(@codemirror/view@6.35.3) + '@codemirror/lang-javascript': 6.2.2 + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/common': 1.2.3 + '@lezer/css': 1.1.9 + '@lezer/html': 1.3.10 + '@codemirror/lang-java@6.0.1': dependencies: '@codemirror/language': 6.10.6 @@ -5126,6 +5172,16 @@ snapshots: '@codemirror/language': 6.10.6 '@lezer/json': 1.0.2 + '@codemirror/lang-markdown@6.3.1': + dependencies: + '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) + '@codemirror/lang-html': 6.4.9 + '@codemirror/language': 6.10.6 + '@codemirror/state': 6.5.0 + '@codemirror/view': 6.35.3 + '@lezer/common': 1.2.3 + '@lezer/markdown': 1.3.2 + '@codemirror/lang-python@6.1.6(@codemirror/view@6.35.3)': dependencies: '@codemirror/autocomplete': 6.18.3(@codemirror/language@6.10.6)(@codemirror/state@6.5.0)(@codemirror/view@6.35.3)(@lezer/common@1.2.3) @@ -5545,10 +5601,22 @@ snapshots: '@lezer/common@1.2.3': {} + '@lezer/css@1.1.9': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + '@lezer/highlight@1.2.1': dependencies: '@lezer/common': 1.2.3 + '@lezer/html@1.3.10': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + '@lezer/java@1.1.3': dependencies: '@lezer/common': 1.2.3 @@ -5571,6 +5639,11 @@ snapshots: dependencies: '@lezer/common': 1.2.3 + '@lezer/markdown@1.3.2': + dependencies: + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/python@1.1.15': dependencies: '@lezer/common': 1.2.3 From 379b69220499eee192d8b9f213ad86a5530bf0c8 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 18 Dec 2024 16:56:49 +0800 Subject: [PATCH 042/172] feat: add a code editor component to the form --- apps/web/src/components/code-editor/editor.tsx | 2 +- apps/web/src/components/code-editor/types.d.ts | 2 +- .../components/code-editor/index.tsx | 18 ++++++------------ .../components/code-editor/style.less | 4 ++++ 4 files changed, 12 insertions(+), 14 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/style.less diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index c4c86c16..903dd48b 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -17,7 +17,7 @@ export const CodeEditor = forwardRef((props, ref) = }); const [editorValue, setEditorValue] = useControllableValue(props, { valuePropName: 'value', - trigger: 'onValueChange', + trigger: 'onChange', }); const { handlers } = useEditorCommand({ editorInstanceRef }); diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts index b2b31fbd..f9cf3f0a 100644 --- a/apps/web/src/components/code-editor/types.d.ts +++ b/apps/web/src/components/code-editor/types.d.ts @@ -41,7 +41,7 @@ export interface EditorProps extends EditorContentProps { * Callback function triggered when the content value changes. * @param value - The new content value. */ - onValueChange?: (value: string) => void; + onChange?: (value: string) => void; /** Custom editor toolbar header. */ Header?: React.FC; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx index 63d5acfb..88248315 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -1,23 +1,17 @@ import React from 'react'; -import { TextField, type TextFieldProps } from '@mui/material'; - -export type CodeEditorProps = TextFieldProps; +import { CodeEditor as CodeMirror, type EditorProps } from '@/components'; +import './style.less'; /** * Code Editor Component * * Note: Use in CodeNode, IfelseNode */ -const CodeEditor: React.FC = ({ ...props }) => { +const CodeEditor: React.FC = ({ value, onChange }) => { return ( - +
+ +
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/style.less new file mode 100644 index 00000000..5db9b07c --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/style.less @@ -0,0 +1,4 @@ +.@{prefix}-code-editor-config { + width: 100%; + height: 240px; +} From 4467325f674e36380808edb8c07a4afb581a9c5d Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 18 Dec 2024 17:27:21 +0800 Subject: [PATCH 043/172] feat: Add defaultValue to the code editor --- apps/web/src/components/code-editor/editor.tsx | 3 ++- apps/web/src/components/code-editor/types.d.ts | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index 903dd48b..6b2d4324 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -16,6 +16,7 @@ export const CodeEditor = forwardRef((props, ref) = trigger: 'onLangChange', }); const [editorValue, setEditorValue] = useControllableValue(props, { + defaultValuePropName: 'defaultValue', valuePropName: 'value', trigger: 'onChange', }); @@ -24,7 +25,7 @@ export const CodeEditor = forwardRef((props, ref) = /** Methods exposed to external components */ useImperativeHandle(ref, () => handlers); - const EditorHeaderComponent = CustomHeader === void 0 ? EditorHeader : CustomHeader; + const EditorHeaderComponent = CustomHeader === void 0 ? EditorHeader : CustomHeader!; return (
{CustomHeader !== null && ( diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts index f9cf3f0a..584b3aaa 100644 --- a/apps/web/src/components/code-editor/types.d.ts +++ b/apps/web/src/components/code-editor/types.d.ts @@ -35,6 +35,8 @@ export interface EditorProps extends EditorContentProps { */ onLangChange?: (value: string) => void; + /** default value */ + defaultValue?: string; /** The content value of the editor. */ value?: string; /** @@ -44,7 +46,7 @@ export interface EditorProps extends EditorContentProps { onChange?: (value: string) => void; /** Custom editor toolbar header. */ - Header?: React.FC; + Header?: React.FC | null; } /** Interface for editor language options. */ From 31b5eb684d7fb1fa287f8626007fd73245120f8a Mon Sep 17 00:00:00 2001 From: jimco Date: Wed, 18 Dec 2024 19:54:23 +0800 Subject: [PATCH 044/172] feat: add ConditionsInput component --- .../components/conditions-input/index.tsx | 406 +++++++++++++++--- .../components/conditions-input/style.less | 81 +++- .../config-panel/hooks/useCommonFormItems.tsx | 4 +- .../config-panel/hooks/useNodeFormItems.tsx | 4 +- .../views/editor/components/topbar/style.less | 1 + .../workflow/views/editor/demo-data.json | 78 ++-- .../src/pages/workflow/views/editor/helper.ts | 9 + .../views/editor/hooks/useInteractions.tsx | 10 +- .../views/editor/hooks/useWorkflow.ts | 70 ++- .../src/pages/workflow/views/editor/index.tsx | 50 ++- .../pages/workflow/views/editor/style.less | 14 + packages/locales/src/lang/en/workflow.json | 16 +- packages/shared/types/workflow.d.ts | 32 +- 13 files changed, 613 insertions(+), 162 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index ff6c26a7..39ebf923 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -1,3 +1,7 @@ +import React, { useLayoutEffect, useRef } from 'react'; +import cls from 'classnames'; +import { isEqual, cloneDeep, merge } from 'lodash-es'; +import { useDynamicList, useControllableValue } from 'ahooks'; import { ToggleButtonGroup, ToggleButton, @@ -5,11 +9,10 @@ import { IconButton, Chip, Select, - FormControl, - InputLabel, MenuItem, TextField, } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; import { AddIcon, DeleteOutlineIcon, @@ -18,78 +21,361 @@ import { SyncIcon, KeyboardArrowDownIcon, } from '@milesight/shared/src/components'; +import { genUuid } from '../../../../helper'; import ParamSelect from '../param-select'; import './style.less'; +export type ConditionsInputValueType = NonNullable; + +type ConditionBlockValueType = ConditionsInputValueType['when'][number]; + +type ConditionValueType = ConditionBlockValueType['conditions'][number]; + +export type ConditionsInputProps = { + value?: ConditionsInputValueType; + defaultValue?: ConditionsInputValueType; + onChange?: (value: ConditionsInputValueType) => void; +}; + +const logicOperatorMap: Partial> = { + OR: { + labelIntlKey: 'workflow.label.logic_keyword_or', + }, + AND: { + labelIntlKey: 'workflow.label.logic_keyword_and', + }, +}; + +const conditionOperatorMap: Partial> = { + CONTAINS: { + labelIntlKey: 'workflow.label.condition_operator_contains', + }, + NOT_CONTAINS: { + labelIntlKey: 'workflow.label.condition_operator_not_contains', + }, + START_WITH: { + labelIntlKey: 'workflow.label.condition_operator_start_with', + }, + END_WITH: { + labelIntlKey: 'workflow.label.condition_operator_end_with', + }, + IS: { + labelIntlKey: 'workflow.label.condition_operator_is', + }, + IS_NOT: { + labelIntlKey: 'workflow.label.condition_operator_is_not', + }, + IS_EMPTY: { + labelIntlKey: 'workflow.label.condition_operator_is_empty', + }, + IS_NOT_EMPTY: { + labelIntlKey: 'workflow.label.condition_operator_is_not_empty', + }, +}; + +const genConditionValue = (): ConditionValueType => { + return { id: genUuid('subcondition') }; +}; + +const genConditionBlockValue = (): ConditionBlockValueType => { + return { + id: genUuid('condition'), + logicOperator: 'AND', + expressionType: 'condition', + conditions: [genConditionValue()], + }; +}; + +const DEFAULT_CONDITION_BLOCK_VALUE = genConditionBlockValue(); + +const MAX_CONDITIONS_NUMBER = 5; +const MAX_CONDITION_BLOCKS_NUMBER = 5; + /** * Conditions Input Component * * Note: use in IfelseNode */ -const ConditionsInput = () => { +const ConditionsInput: React.FC = props => { + const { getIntlText } = useI18n(); + const [data, setData] = useControllableValue(props); + // const otherwiseId = useRef(''); + const { + list: blockList, + remove: removeBlock, + getKey: getBlockKey, + insert: insertBlock, + replace: replaceBlock, + resetList: resetBlockList, + } = useDynamicList([DEFAULT_CONDITION_BLOCK_VALUE]); + + // console.log({ blockList }); + const handleExpTypeChange = (block: ConditionBlockValueType, blockIndex: number) => { + const { expressionType } = block; + const newExpType = expressionType === 'condition' ? 'mvel' : 'condition'; + + replaceBlock(blockIndex, { + ...block, + expressionType: newExpType, + conditions: [genConditionValue()], + }); + }; + + const handleLogicOperatorChange = (block: ConditionBlockValueType, blockIndex: number) => { + let { logicOperator } = block; + + logicOperator = logicOperator === 'AND' ? 'OR' : 'AND'; + replaceBlock(blockIndex, { ...block, logicOperator }); + }; + + const removeCondition = (index: number, block: ConditionBlockValueType, blockIndex: number) => { + const conditions = cloneDeep(block.conditions || []); + + conditions.splice(index, 1); + replaceBlock(blockIndex, { ...block, conditions }); + }; + + const insertCondition = ( + index: number, + condition: ConditionValueType, + block: ConditionBlockValueType, + blockIndex: number, + ) => { + const conditions = cloneDeep(block.conditions || []); + + conditions.splice(index, 0, condition); + replaceBlock(blockIndex, { ...block, conditions }); + }; + + const replaceCondition = ( + index: number, + condition: Partial, + block: ConditionBlockValueType, + blockIndex: number, + ) => { + const conditions = cloneDeep(block.conditions || []); + + conditions.splice(index, 1, merge(conditions[index], condition)); + replaceBlock(blockIndex, { ...block, conditions }); + }; + + const checkEmptyOperator = (condition: ConditionValueType) => { + const emptyOperators: WorkflowFilterOperator[] = ['IS_EMPTY', 'IS_NOT_EMPTY']; + const { expressionValue } = condition; + + return ( + typeof expressionValue !== 'string' && + emptyOperators.includes(expressionValue?.operator as WorkflowFilterOperator) + ); + }; + + useLayoutEffect(() => { + if (!data?.when?.length) return; + if (isEqual(data.when, blockList)) return; + resetBlockList(data.when); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [data, resetBlockList]); + + useLayoutEffect(() => { + setData(d => { + const otherwiseId = `${d?.otherwise?.id || ''}` || genUuid('condition'); + const result = { + when: blockList, + otherwise: { + id: otherwiseId, + }, + }; + return result; + }); + }, [blockList, setData]); + return (
-
-
-
IF
-
- - - - - - - - - - - -
-
-
-
- - AND - - } - /> -
-
-
-
- - + + + + + + + + {blockList.length > 1 && ( + removeBlock(blockIndex)}> + + + )}
-
- - - + {expressionType === 'mvel' ? ( +
+ + +
+ ) : ( +
+ {isMultipleConditions && ( +
+ + {getIntlText( + logicOperatorMap[logicOperator] + ?.labelIntlKey || '', + )} + + + } + onClick={() => + handleLogicOperatorChange(block, blockIndex) + } + /> +
+ )} + {conditions.map((condition, index) => { + const { expressionValue } = condition; + + if (typeof expressionValue === 'string') return null; + return ( +
+
+
+ + replaceCondition( + index, + { + expressionValue: { + key: e.target.value, + }, + }, + block, + blockIndex, + ) + } + /> + +
+ { + replaceCondition( + index, + { + expressionValue: { + value: e.target.value, + }, + }, + block, + blockIndex, + ); + }} + /> +
+ {isMultipleConditions && ( + + removeCondition(index, block, blockIndex) + } + > + + + )} +
+ ); + })} + +
+ )}
- -
-
-
); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less index be876751..fe03afc8 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/style.less @@ -1,4 +1,16 @@ .@{prefix}-conditions-input { + + &-add-btn.@{mui-prefix}ButtonBase-root { + font-weight: @font-weight-bold; + line-height: 24px; + color: var(--text-color-base); + border-color: var(--border-color-base); + + &:hover { + background-color: var(--component-background-gray); + } + } + &-item-topbar { display: flex; align-items: center; @@ -10,6 +22,32 @@ display: flex; gap: @margin-xs; align-items: center; + + .@{mui-prefix}ToggleButtonGroup-root { + background-color: var(--gray-color-2); + border: 2px solid var(--gray-color-2); + + .@{mui-prefix}ToggleButtonGroup-grouped { + border: none; + border-radius: @border-radius-base; + .text-size(@font-size-base); + + &::after { + display: block; + height: 1px; + margin-bottom: -1px; + overflow: hidden; + font-weight: @font-weight-bold; + visibility: hidden; + content: attr(aria-label); + } + + &.@{mui-prefix}-selected { + font-weight: @font-weight-bold; + background-color: var(--component-background); + } + } + } } .@{mui-prefix}ToggleButton-root { @@ -17,7 +55,7 @@ } } - &-item-content { + &-item-conditions { position: relative; margin-bottom: @margin-xl; border-bottom: 1px solid var(--border-color-base); @@ -39,7 +77,7 @@ width: 36px; border: 1px solid var(--border-color-base); border-right: none; - border-radius: @border-radius-base; + border-radius: @border-radius-base 0 0 @border-radius-base; .@{mui-prefix}Chip-root { position: absolute; @@ -53,6 +91,10 @@ transform: translate(-50%, -50%); .text-size(@font-size-sm); + &:hover { + background-color: var(--component-background-gray); + } + .@{mui-prefix}Chip-label { display: flex; align-items: center; @@ -67,6 +109,11 @@ .field-item { display: flex; gap: @margin-xs; + margin-bottom: @margin-sm; + + &:last-child { + margin-bottom: 0; + } .input-wrapper { flex: 1; @@ -76,13 +123,19 @@ > .@{prefix}-param-select { flex: 1; + width: 0; .@{mui-prefix}OutlinedInput-notchedOutline { border-radius: @border-radius-base 0 0 0; } + + .@{mui-prefix}InputBase-root .@{prefix}-param-select-item { + max-width: 120px; + } } > .@{mui-prefix}InputBase-root { flex: 1; + width: 0; margin-left: -1px; .@{mui-prefix}OutlinedInput-notchedOutline { @@ -99,6 +152,21 @@ border-radius: 0 0 @border-radius-base @border-radius-base; } } + + &.hidden-value-input { + > .select { + > .@{prefix}-param-select .@{mui-prefix}OutlinedInput-notchedOutline { + border-radius: @border-radius-base 0 0 @border-radius-base; + } + + > .@{mui-prefix}InputBase-root .@{mui-prefix}OutlinedInput-notchedOutline { + border-radius: 0 @border-radius-base @border-radius-base 0; + } + } + > .@{mui-prefix}FormControl-root { + display: none; + } + } } > .@{mui-prefix}ButtonBase-root { @@ -108,8 +176,15 @@ } } - .@{prefix}-conditions-input-item-add-btn { + .@{prefix}-conditions-input-add-btn { margin: @margin-sm 0 @margin-xl; } } + + &-item-mvel { + position: relative; + padding-bottom: @padding-sm; + margin-bottom: @margin-xl; + border-bottom: 1px solid var(--border-color-base); + } } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx index 8a88ef4a..a9c0938a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx @@ -27,7 +27,7 @@ const useCommonFormItems = () => { { return ( { { name: 'when', render({ field: { onChange, value } }) { - return ; + return ; }, }, ], @@ -101,7 +101,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { children: [ { name: 'code', - render({ field: { onChange, value }, fieldState, formState }) { + render({ field: { onChange, value } }) { return ; }, }, diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/style.less b/apps/web/src/pages/workflow/views/editor/components/topbar/style.less index bda7b445..1b4e74b4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/style.less @@ -1,6 +1,7 @@ .@{prefix}-workflow-topbar { padding: @padding-xs @padding-lg; background-color: var(--component-background); + border-bottom: 1px solid var(--border-color-base); .@{mui-prefix}Grid2-root > .@{mui-prefix}Grid2-root { display: flex; diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json index d7ba5b3b..c9baa1c8 100644 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -3,7 +3,7 @@ { "id": "1", "data": { - "label": "Hello" + "name": "Hello" }, "position": { "x": 0, @@ -14,45 +14,39 @@ { "id": "2", "data": { - "label": "World", - "cases": [ - { - "id": "case-1", - "conditions:": [ - { - "id": "condition-1", - "key": "var1", - "value": "a", - "operator": "CONTAINS" - }, - { - "id": "condition-2", - "key": "var2", - "value": "b", - "operator": "START_WITH" - } - ], - "logic": "AND" - }, - { - "id": "case-2", - "conditions:": [ - { - "id": "condition-3", - "key": "var4", - "value": "aa", - "operator": "CONTAINS" - }, - { - "id": "condition-4", - "key": "var5", - "value": "bb", - "operator": "START_WITH" - } - ], - "logic": "OR" + "name": "World", + "parameters": { + "when": [ + { + "id": "", + "logicOperator": "AND", + "conditions": [ + { + "expressionType": "mvel", + "expressionValue": "request.body['a.b.c'] != null" + } + ] + }, + { + "id": "", + "logicOperator": "OR", + "conditions": [ + { + "expressionType": "condition", + "expressionValue": { + "id": "", + "key": "", + "value": "", + "operator": "" + } + } + ] + } + ], + "otherwise": { + "id": "" } - ] + } }, "position": { "x": 300, @@ -63,7 +57,7 @@ { "id": "3", "data": { - "label": "Code" + "name": "Code" }, "position": { "x": 600, @@ -74,7 +68,7 @@ { "id": "4", "data": { - "label": "Service" + "name": "Service" }, "position": { "x": 600, @@ -85,7 +79,7 @@ { "id": "5", "data": { - "label": "Select" + "name": "Select" }, "position": { "x": 600, diff --git a/apps/web/src/pages/workflow/views/editor/helper.ts b/apps/web/src/pages/workflow/views/editor/helper.ts index 144d9c59..197b09fe 100644 --- a/apps/web/src/pages/workflow/views/editor/helper.ts +++ b/apps/web/src/pages/workflow/views/editor/helper.ts @@ -1,3 +1,4 @@ +import { genRandomString } from '@milesight/shared/src/utils/tools'; import { checkRequired, checkRangeLength, @@ -40,3 +41,11 @@ export const parseRefParamKey = (key: string) => { const [nodeType, nodeId, valueKey] = key.slice(1).split(PARAM_REFERENCE_DIVIDER); return { nodeType, nodeId, valueKey }; }; + +/** + * Generate Workflow Node, Edge or Condition uuid, format as `{node}:{8-bit random string}:{timestamp}` + * @param type node/edge + */ +export const genUuid = (type: 'node' | 'edge' | 'condition' | 'subcondition') => { + return `${type}:${genRandomString(8, { lowerCase: true })}:${Date.now()}`; +}; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index 77e640b7..fd6800ff 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -8,7 +8,7 @@ import { } from '@xyflow/react'; import { useSize } from 'ahooks'; import { cloneDeep, maxBy } from 'lodash-es'; -import { genRandomString } from '@milesight/shared/src/utils/tools'; +import { genUuid } from '../helper'; import { NODE_SPACING_X, NODE_SPACING_Y, @@ -43,14 +43,6 @@ type AddNodeFunc = ( closestNodePayload?: AddNodeClosestPayloadParam, ) => void; -/** - * Generate Workflow Node or Edge uuid, format as `{node}:{timestamp}:{8-bit random string}` - * @param type node/edge - */ -const genUuid = (type: 'node' | 'edge') => { - return `${type}:${Date.now()}:${genRandomString(8, { lowerCase: true })}`; -}; - /** * Workflow Interactions Hook */ diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 40f16f69..03beeea9 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -39,29 +39,32 @@ const useWorkflow = () => { const selectedNodeType = selectedNode?.type; // Check entry node number limit - const checkEntryNodeNumberLimit = useCallback(() => { - const nodes = getNodes(); - const entryNodes = nodes.filter(node => - entryNodeTypes.includes(node.type as WorkflowNodeType), - ); + const checkEntryNodeNumberLimit = useCallback( + (nodes?: WorkflowNode[]) => { + nodes = nodes || getNodes(); + const entryNodes = nodes.filter(node => + entryNodeTypes.includes(node.type as WorkflowNodeType), + ); - if (entryNodes.length > 1) { - toast.error({ - key: 'entry-node-number-limit', - content: getIntlText('workflow.label.entry_node_number_limit_tip', { - 1: ENTRY_NODE_NUMBER_LIMIT, - }), - }); - return false; - } + if (entryNodes.length !== ENTRY_NODE_NUMBER_LIMIT) { + toast.error({ + key: 'entry-node-number-limit', + content: getIntlText('workflow.label.entry_node_number_limit_tip', { + 1: ENTRY_NODE_NUMBER_LIMIT, + }), + }); + return false; + } - return true; - }, [getNodes, getIntlText]); + return true; + }, + [getNodes, getIntlText], + ); // Check Parallel Limit const checkParallelLimit = useCallback( - (nodeId: ApiKey, nodeHandle?: string | null) => { - const edges = getEdges(); + (nodeId: ApiKey, nodeHandle?: string | null, edges?: WorkflowEdge[]) => { + edges = edges || getEdges(); const connectedEdges = edges.filter( edge => edge.source === nodeId && @@ -180,18 +183,43 @@ const useWorkflow = () => { [nodes, edges, getSelectedNode], ); - // const checkFreeNodeLimit = useCallback(() => { + // Check if there is a node that is not connected to an entry node + const checkFreeNodeLimit = useCallback( + (nodes?: WorkflowNode[]) => { + nodes = nodes || getNodes(); + let result = false; + + result = nodes + .filter(node => !entryNodeTypes.includes(node.type as WorkflowNodeType)) + .some(node => { + const upstreamNodes = getUpstreamNodes(node); + const hasEntryNode = upstreamNodes.some(item => + entryNodeTypes.includes(item.type as WorkflowNodeType), + ); + + return !hasEntryNode; + }); + + if (result) { + toast.error({ + key: 'free-node-limit', + content: getIntlText('workflow.label.free_node_limit_tip'), + }); + } - // }, [getNodes, getUpstreamNodes]); + return result; + }, + [getNodes, getUpstreamNodes, getIntlText], + ); return { nodes, edges, - // selectedNode, isValidConnection, checkParallelLimit, checkNestedParallelLimit, checkEntryNodeNumberLimit, + checkFreeNodeLimit, getSelectedNode, getUpstreamNodes, }; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 523623db..42f3db94 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -46,11 +46,32 @@ const WorkflowEditor = () => { const { getIntlText } = useI18n(); const nodeTypes = useNodeTypes(); const { toObject } = useReactFlow(); - const { isValidConnection, checkNestedParallelLimit } = useWorkflow(); + const { + isValidConnection, + checkParallelLimit, + checkNestedParallelLimit, + checkEntryNodeNumberLimit, + checkFreeNodeLimit, + } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const checkWorkflowValid = useCallback(() => { + const { nodes, edges } = toObject(); + if (!checkEntryNodeNumberLimit(nodes)) return false; + if (checkFreeNodeLimit(nodes)) return false; + if (!checkNestedParallelLimit(nodes, edges)) return false; + if (nodes.some(node => !checkParallelLimit(node.id, undefined, edges))) return false; + + return true; + }, [ + toObject, + checkEntryNodeNumberLimit, + checkFreeNodeLimit, + checkNestedParallelLimit, + checkParallelLimit, + ]); // ---------- Fetch Data ---------- const [searchParams] = useSearchParams(); @@ -119,23 +140,25 @@ const WorkflowEditor = () => { const [designMode, setDesignMode] = useState('canvas'); const handleDesignModeChange = useCallback( (mode: DesignMode) => { - const data = toObject(); + if (!checkWorkflowValid()) return; - // TODO: check the workflow json data is valid + const data = toObject(); + // TODO: check the nodes json data is valid console.log('workflow data', data); + setDesignMode(mode); }, - [toObject], + [toObject, checkWorkflowValid], ); // ---------- Save Workflow ---------- const handleSave = () => { - const data = toObject(); + if (!checkWorkflowValid()) return; - if (!checkNestedParallelLimit(data.nodes, data.edges)) return; - // TODO: check the workflow json is valid + const { nodes, edges, viewport } = toObject(); - console.log('workflow data', data); + // TODO: check the nodes data is valid + console.log('workflow data', { nodes, edges, viewport }); }; return ( @@ -190,6 +213,17 @@ const WorkflowEditor = () => { + {designMode === 'advanced' && ( +
+
+ {JSON.stringify(toObject(), null, 4)} +
+
+ )}
diff --git a/apps/web/src/pages/workflow/views/editor/style.less b/apps/web/src/pages/workflow/views/editor/style.less index f7781279..6fa6d27f 100644 --- a/apps/web/src/pages/workflow/views/editor/style.less +++ b/apps/web/src/pages/workflow/views/editor/style.less @@ -1,4 +1,6 @@ .@{prefix}-view-wf_editor { + position: relative; + .@{prefix}-workflow { // ---------- Node style variable override ---------- --xy-node-border: none; @@ -94,4 +96,16 @@ margin-top: @margin-sm; } } + + .@{prefix}-workflow-advance { + position: absolute; + inset: 0; + z-index: 1000; + padding: @padding-md; + overflow: auto; + font-family: @font-family-code; + white-space: pre-wrap; + background-color: var(--component-background); + .text-size(@font-size-sm); + } } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 71aa7c14..35be3be3 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -54,5 +54,19 @@ "workflow.editor.form_param_timer_period_saturday": "Saturday", "workflow.editor.form_param_timer_period_sunday": "Sunday", "workflow.label.parallel_limit_tip": "并行分支限制为 {1} 个", - "workflow.label.entry_node_number_limit_tip": "工作流中必须且仅允许有 {1} 个入口节点" + "workflow.label.entry_node_number_limit_tip": "工作流中必须且仅允许有 {1} 个入口节点", + "workflow.label.logic_keyword_if": "IF", + "workflow.label.logic_keyword_elseif": "ELSEIF", + "workflow.label.logic_keyword_and": "AND", + "workflow.label.logic_keyword_or": "OR", + "workflow.editor.form_button_add_condition": "Add Condition", + "workflow.label.free_node_limit_tip": "工作流中不允许有游离节点", + "workflow.label.condition_operator_contains": "Contains", + "workflow.label.condition_operator_not_contains": "Not Contains", + "workflow.label.condition_operator_start_with": "Start With", + "workflow.label.condition_operator_end_with": "End With", + "workflow.label.condition_operator_is": "Is", + "workflow.label.condition_operator_is_not": "Is Not", + "workflow.label.condition_operator_is_empty": "Is Empty", + "workflow.label.condition_operator_is_not_empty": "Is Not Empty" } diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index 5e177711..d7cf9971 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -171,20 +171,24 @@ declare type WorkflowFilterOperator = declare type IfElseNodeDataType = BaseNodeDataType<{ when: { id: ApiKey; - [logic: WorkflowLogicOperator]: { - /** - * 表达式类型(默认 `condition`,且当前仅支持 `condition`) - * @param mvel mvel 表达式 - * @param condition 条件表达式 - */ - expressionType: 'mvel' | 'condition'; - /** 表达式值 */ - expressionValue: { - id: ApiKey; - key: ApiKey; - operator: WorkflowFilterOperator; - value?: string; - }; + logicOperator: WorkflowLogicOperator; + /** + * 表达式类型(默认 `condition`) + * @param mvel mvel 表达式 + * @param condition 条件表达式 + */ + expressionType: 'mvel' | 'condition'; + conditions: { + id: ApiKey; + expressionValue?: + | string + | { + key?: ApiKey; + operator?: WorkflowFilterOperator; + value?: string; + }; + /** 表达式备注 */ + expressionDescription?: string; }[]; }[]; otherwise: { From 619302bc5f5cb99c3137b66f8a403788d773ff52 Mon Sep 17 00:00:00 2001 From: Nian Date: Wed, 18 Dec 2024 09:27:58 +0800 Subject: [PATCH 045/172] fix: improve the trigger node configuration --- .../editor/components/config-panel/hooks/useNodeFormItems.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 72195293..4754cca5 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -11,9 +11,9 @@ import { EntitySelect, MarkdownEditor, ParamAssignInput, - ParamInput, // ParamInputSelect, TimerInput, + ParamInput, } from '../components'; type NodeFormGroupType = { From ef9a087cf2cdf7649009e5da28f36df28e0a14d3 Mon Sep 17 00:00:00 2001 From: Nian Date: Thu, 19 Dec 2024 09:20:52 +0800 Subject: [PATCH 046/172] feat: service call node --- .../config-panel/components/index.ts | 1 + .../components/service-entity-call/index.tsx | 131 ++++++++++++++++++ .../components/service-entity-call/style.less | 40 ++++++ .../config-panel/hooks/useNodeFormItems.tsx | 9 ++ packages/locales/src/lang/en/workflow.json | 5 +- 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 0d2c551a..2f4907c2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -16,3 +16,4 @@ export { type EntityFilterSelectValueType, } from './entity-filter-select'; export { default as ParamAssignInput, type ParamAssignInputValueType } from './param-assign-input'; +export { default as ServiceEntityCall } from './service-entity-call'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx new file mode 100644 index 00000000..fa2bfa29 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx @@ -0,0 +1,131 @@ +import { useLayoutEffect, useMemo } from 'react'; +import { useEntityApi } from '@/plugin/hooks'; +import { Divider } from '@mui/material'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; +import { useControllableValue, useDynamicList } from 'ahooks'; +import EntitySelect, { EntitySelectValueType } from '../entity-select'; +import ParamInputSelect, { type ParamInputSelectProps } from '../param-input-select'; +import './style.less'; +import useConfigPanelStore from '../../store'; + +type InputParamListType = { + key: ApiKey; + name: ApiKey; + type: ApiKey; + value: ParamInputSelectProps['value']; +}; +type ServiceEntityCallValueType = { + serviceValue?: EntitySelectValueType; + paramList?: InputParamListType[]; +}; +type ServiceEntityCallProps = { + required?: boolean; + disabled?: boolean; + value?: ServiceEntityCallValueType; + defaultValue?: any; + onChange?: (value: ServiceEntityCallValueType) => void; +}; +type EntityItem = { + entity_key: ApiKey; + entity_name: ApiKey; + entity_value_type: ApiKey; +}; + +const filterModel: { + type: EntityType; +} = { + type: 'SERVICE', +}; +const ServiceEntityCall: React.FC = ({ + required, + disabled, + defaultValue, + ...props +}) => { + const { getEntityList } = useConfigPanelStore(useStoreShallow(['getEntityList'])); + const { getIntlText } = useI18n(); + const { getEntityChildren } = useEntityApi(); + const [innerValue, setInnerValue] = useControllableValue(props, { + defaultValue: defaultValue || {}, + }); + const { list, replace, resetList } = useDynamicList( + innerValue?.paramList ?? [], + ); + const handlerChange = async (value: EntitySelectValueType) => { + const entityFilterList = await getEntityList({ keyword: value as string }); + if (entityFilterList?.length) { + const entityItem = entityFilterList[0]; + const { error, res } = await getEntityChildren({ + id: entityItem.entity_id, + }); + if (error) { + resetList([]); + } else { + resetList( + res.map((item: EntityItem) => { + return { + key: item.entity_key, + name: item.entity_name, + type: item.entity_value_type, + }; + }), + ); + } + } else { + resetList([]); + } + setInnerValue(pre => ({ ...pre, serviceValue: value })); + }; + const renderInputParam = useMemo(() => { + if (list.length) { + return ( +
+ + + {getIntlText('workflow.node.service_call_Input_title')} + + {list.map((item, index) => { + return ( +
+
+ {item.name} + {item.type} +
+ { + replace(index, { ...item, value: data }); + }} + /> +
+ ); + })} +
+ ); + } + return null; + }, [list]); + useLayoutEffect(() => { + setInnerValue(pre => ({ ...pre, paramList: list })); + }, list); + return ( +
+ + {getIntlText('workflow.node.service_call_service_title')} + + { + handlerChange(data); + }} + /> + {renderInputParam} +
+ ); +}; +export default ServiceEntityCall; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less new file mode 100644 index 00000000..7dc61375 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less @@ -0,0 +1,40 @@ +.@{prefix}-service-entity-call { + &-title { + font-size: @font-size-lg; + font-weight: @font-weight-medium; + line-height: 24px; + color: var(--gray-9); + } + + &-input { + display: block; + + &-divider { + margin: @margin-xl 0 !important; + } + + .param-item { + margin-top: @margin-md; + + &-title { + margin-bottom: @margin-xs; + } + + &-name { + font-size: @font-size-md; + font-weight: @font-weight-medium; + line-height: 22px; + color: #272e3b; + } + + &-type { + margin-left: @margin-xs; + font-size: @font-size-md; + font-style: normal; + line-height: 22px; + color: #6b7785; + } + } + } + +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 4754cca5..a1e130c1 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -14,6 +14,7 @@ import { // ParamInputSelect, TimerInput, ParamInput, + ServiceEntityCall, } from '../components'; type NodeFormGroupType = { @@ -275,6 +276,14 @@ const useNodeFormItems = (node?: WorkflowNode) => { ], }, ], + service: [ + { + name: 'service', + render({ field: { onChange, value }, fieldState: { error } }) { + return ; + }, + }, + ], }; return result; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 35be3be3..2e9e7f30 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -68,5 +68,8 @@ "workflow.label.condition_operator_is": "Is", "workflow.label.condition_operator_is_not": "Is Not", "workflow.label.condition_operator_is_empty": "Is Empty", - "workflow.label.condition_operator_is_not_empty": "Is Not Empty" + "workflow.label.condition_operator_is_not_empty": "Is Not Empty", + "workflow.node.config_panel_lable_arguments": "Arguments", + "workflow.node.service_call_service_title": "Service Setting", + "workflow.node.service_call_Input_title": "Input Variables" } From c16e5e552b442a877ad1e829e5eb742fc751b74a Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 17 Dec 2024 15:29:57 +0800 Subject: [PATCH 047/172] fix: file upload format restrictions & validate DSL file content --- .../import-modal/hook/useImportFormItems.tsx | 3 +- .../components/import-modal/index.tsx | 47 ++++++++++++++++--- apps/web/src/pages/workflow/index.tsx | 21 ++++----- packages/locales/src/lang/en/workflow.json | 3 +- 4 files changed, 51 insertions(+), 23 deletions(-) diff --git a/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx b/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx index 82d335ca..ce0fcd0e 100644 --- a/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx +++ b/apps/web/src/pages/workflow/components/import-modal/hook/useImportFormItems.tsx @@ -26,8 +26,7 @@ const useImportFormItems = () => { render({ field: { onChange, value }, fieldState: { error } }) { return ( { /** upload callback */ - onUpload?: (params: FormDataProps) => Promise | void; + onUpload?: (contains: WorkflowSchema) => Promise | void; } /** @@ -22,18 +23,50 @@ const ImportModal: React.FC = ({ visible, onCancel, onUpload, ...props }) const { control, formState, handleSubmit, reset } = useForm(); const onSubmit: SubmitHandler = async ({ ...params }) => { - if (onUpload) { - await onUpload(params); + const { res, contains } = await validateFile(params.file[0]); + if (res && contains) { + if (onUpload) { + await onUpload(contains); + } + // Clear the form data upon confirmation. + reset(); + } else { + toast.error(getIntlText('workflow.message.import_dsl_error')); + reset(); } - // Clear the form data upon confirmation. - reset(); }; const handleCancel = useMemoizedFn(() => { reset(); onCancel && onCancel(); }); - + const validateFile = (file: File): Promise<{ res: boolean; contains?: WorkflowSchema }> => { + return new Promise(resolve => { + if (file) { + const reader = new FileReader(); + reader.onload = event => { + try { + const result: WorkflowSchema = JSON.parse(event.target?.result as string); + if (result.nodes?.length) { + const nodeTypes = Object.values(basicNodeConfigs).map( + item => item.type, + ); + const isError = result.nodes.some( + item => !nodeTypes.includes(item.type as WorkflowNodeType), + ); + resolve({ res: !isError, contains: result }); + } + resolve({ res: false }); + } catch (error) { + resolve({ res: false }); + } + }; + reader.readAsText(file); + } else { + resolve({ res: false }); + } + }); + }; return ( { ); }, [getIntlText, handleDeleteConfirm, selectedIds]); const handlerImportModal = useCallback( - (isOpen: boolean, param?: ImportFormDataProps) => { - if (param) { - // TODO: Pass the DSL to the workflow. - // valid method to be add - const valid = true; - if (valid) { - navigate('/workflow/editor', { - state: { - file: param?.file?.[0] ?? null, - }, - }); - } + (isOpen: boolean, contains?: WorkflowSchema) => { + if (contains) { + // TODO: wid should be deleted + navigate('/workflow/editor?wid=12121', { + state: { + workflowSchema: contains, + }, + }); } setImportModal(isOpen); }, diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 2e9e7f30..c6c74a6e 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -71,5 +71,6 @@ "workflow.label.condition_operator_is_not_empty": "Is Not Empty", "workflow.node.config_panel_lable_arguments": "Arguments", "workflow.node.service_call_service_title": "Service Setting", - "workflow.node.service_call_Input_title": "Input Variables" + "workflow.node.service_call_Input_title": "Input Variables", + "workflow.message.import_dsl_error": "The DSL file contains errors. Please update the file and try again." } From 310bc95163bb150fb3afb4772db1a8924972fcbe Mon Sep 17 00:00:00 2001 From: Nian Date: Wed, 18 Dec 2024 09:59:39 +0800 Subject: [PATCH 048/172] refactor: adjust the DSL file validation return results --- .../components/import-modal/index.tsx | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/apps/web/src/pages/workflow/components/import-modal/index.tsx b/apps/web/src/pages/workflow/components/import-modal/index.tsx index 75a53783..b13d7352 100644 --- a/apps/web/src/pages/workflow/components/import-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/import-modal/index.tsx @@ -23,14 +23,18 @@ const ImportModal: React.FC = ({ visible, onCancel, onUpload, ...props }) const { control, formState, handleSubmit, reset } = useForm(); const onSubmit: SubmitHandler = async ({ ...params }) => { - const { res, contains } = await validateFile(params.file[0]); - if (res && contains) { - if (onUpload) { - await onUpload(contains); + try { + const res = await validateFile(params.file[0]); + if (res) { + if (onUpload) { + await onUpload(res); + } + // Clear the form data upon confirmation. + reset(); + } else { + throw new Error('error'); } - // Clear the form data upon confirmation. - reset(); - } else { + } catch (error) { toast.error(getIntlText('workflow.message.import_dsl_error')); reset(); } @@ -40,8 +44,8 @@ const ImportModal: React.FC = ({ visible, onCancel, onUpload, ...props }) reset(); onCancel && onCancel(); }); - const validateFile = (file: File): Promise<{ res: boolean; contains?: WorkflowSchema }> => { - return new Promise(resolve => { + const validateFile = (file: File): Promise => { + return new Promise((resolve, reject) => { if (file) { const reader = new FileReader(); reader.onload = event => { @@ -54,16 +58,18 @@ const ImportModal: React.FC = ({ visible, onCancel, onUpload, ...props }) const isError = result.nodes.some( item => !nodeTypes.includes(item.type as WorkflowNodeType), ); - resolve({ res: !isError, contains: result }); + if (!isError) { + resolve(result); + } } - resolve({ res: false }); + reject(); } catch (error) { - resolve({ res: false }); + reject(); } }; reader.readAsText(file); } else { - resolve({ res: false }); + reject(); } }); }; From d267a4f08eb07baf145a51869524376dd3a875a6 Mon Sep 17 00:00:00 2001 From: Nian Date: Wed, 18 Dec 2024 10:43:48 +0800 Subject: [PATCH 049/172] fix: remove defaultValue of param-input --- .../components/config-panel/components/param-input/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index 072cabd8..405a8258 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -57,9 +57,7 @@ const ParamInput: React.FC = ({ ...props }) => { const { getIntlText } = useI18n(); - const [innerValue, setInnerValue] = useControllableValue(props, { - defaultValue: defaultValue || [], - }); + const [innerValue, setInnerValue] = useControllableValue(props); const { list, remove, getKey, insert, replace } = useDynamicList( innerValue || [], ); From 2986b4424c622c265e9baa113a981cc77769ff70 Mon Sep 17 00:00:00 2001 From: Nian Date: Wed, 18 Dec 2024 11:00:45 +0800 Subject: [PATCH 050/172] fix: adjust the ServiceParamAssignInput component --- .../config-panel/components/index.ts | 2 +- .../components/service-entity-call/index.tsx | 131 ------------------ .../components/service-entity-call/style.less | 40 ------ .../service-param-assign-input/index.tsx | 104 ++++++++++++++ .../service-param-assign-input/style.less | 24 ++++ .../config-panel/hooks/useNodeFormItems.tsx | 36 +++-- packages/locales/src/lang/en/workflow.json | 2 - 7 files changed, 151 insertions(+), 188 deletions(-) delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 2f4907c2..418e09ba 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -16,4 +16,4 @@ export { type EntityFilterSelectValueType, } from './entity-filter-select'; export { default as ParamAssignInput, type ParamAssignInputValueType } from './param-assign-input'; -export { default as ServiceEntityCall } from './service-entity-call'; +export { default as ServiceParamAssignInput } from './service-param-assign-input'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx deleted file mode 100644 index fa2bfa29..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/index.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { useLayoutEffect, useMemo } from 'react'; -import { useEntityApi } from '@/plugin/hooks'; -import { Divider } from '@mui/material'; -import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; -import { useControllableValue, useDynamicList } from 'ahooks'; -import EntitySelect, { EntitySelectValueType } from '../entity-select'; -import ParamInputSelect, { type ParamInputSelectProps } from '../param-input-select'; -import './style.less'; -import useConfigPanelStore from '../../store'; - -type InputParamListType = { - key: ApiKey; - name: ApiKey; - type: ApiKey; - value: ParamInputSelectProps['value']; -}; -type ServiceEntityCallValueType = { - serviceValue?: EntitySelectValueType; - paramList?: InputParamListType[]; -}; -type ServiceEntityCallProps = { - required?: boolean; - disabled?: boolean; - value?: ServiceEntityCallValueType; - defaultValue?: any; - onChange?: (value: ServiceEntityCallValueType) => void; -}; -type EntityItem = { - entity_key: ApiKey; - entity_name: ApiKey; - entity_value_type: ApiKey; -}; - -const filterModel: { - type: EntityType; -} = { - type: 'SERVICE', -}; -const ServiceEntityCall: React.FC = ({ - required, - disabled, - defaultValue, - ...props -}) => { - const { getEntityList } = useConfigPanelStore(useStoreShallow(['getEntityList'])); - const { getIntlText } = useI18n(); - const { getEntityChildren } = useEntityApi(); - const [innerValue, setInnerValue] = useControllableValue(props, { - defaultValue: defaultValue || {}, - }); - const { list, replace, resetList } = useDynamicList( - innerValue?.paramList ?? [], - ); - const handlerChange = async (value: EntitySelectValueType) => { - const entityFilterList = await getEntityList({ keyword: value as string }); - if (entityFilterList?.length) { - const entityItem = entityFilterList[0]; - const { error, res } = await getEntityChildren({ - id: entityItem.entity_id, - }); - if (error) { - resetList([]); - } else { - resetList( - res.map((item: EntityItem) => { - return { - key: item.entity_key, - name: item.entity_name, - type: item.entity_value_type, - }; - }), - ); - } - } else { - resetList([]); - } - setInnerValue(pre => ({ ...pre, serviceValue: value })); - }; - const renderInputParam = useMemo(() => { - if (list.length) { - return ( -
- - - {getIntlText('workflow.node.service_call_Input_title')} - - {list.map((item, index) => { - return ( -
-
- {item.name} - {item.type} -
- { - replace(index, { ...item, value: data }); - }} - /> -
- ); - })} -
- ); - } - return null; - }, [list]); - useLayoutEffect(() => { - setInnerValue(pre => ({ ...pre, paramList: list })); - }, list); - return ( -
- - {getIntlText('workflow.node.service_call_service_title')} - - { - handlerChange(data); - }} - /> - {renderInputParam} -
- ); -}; -export default ServiceEntityCall; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less deleted file mode 100644 index 7dc61375..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-entity-call/style.less +++ /dev/null @@ -1,40 +0,0 @@ -.@{prefix}-service-entity-call { - &-title { - font-size: @font-size-lg; - font-weight: @font-weight-medium; - line-height: 24px; - color: var(--gray-9); - } - - &-input { - display: block; - - &-divider { - margin: @margin-xl 0 !important; - } - - .param-item { - margin-top: @margin-md; - - &-title { - margin-bottom: @margin-xs; - } - - &-name { - font-size: @font-size-md; - font-weight: @font-weight-medium; - line-height: 22px; - color: #272e3b; - } - - &-type { - margin-left: @margin-xs; - font-size: @font-size-md; - font-style: normal; - line-height: 22px; - color: #6b7785; - } - } - } - -} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx new file mode 100644 index 00000000..d7e3bad9 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -0,0 +1,104 @@ +import { useLayoutEffect, useMemo } from 'react'; +import { useEntityApi } from '@/plugin/hooks'; +import { useStoreShallow } from '@milesight/shared/src/hooks'; +import { useControllableValue, useDynamicList } from 'ahooks'; +import ParamInputSelect, { type ParamInputSelectProps } from '../param-input-select'; +import useConfigPanelStore from '../../store'; +import './style.less'; + +type InputParamListType = { + key: ApiKey; + name: ApiKey; + type: ApiKey; + value: ParamInputSelectProps['value']; +}; +type ServiceParamAssignInputValueType = InputParamListType[]; +type ServiceParamAssignInputProps = { + required?: boolean; + disabled?: boolean; + serviceKey?: ApiKey; + value?: ServiceParamAssignInputValueType; + defaultValue?: any; + onChange?: (value: ServiceParamAssignInputValueType) => void; +}; +type EntityItem = { + entity_key: ApiKey; + entity_name: ApiKey; + entity_value_type: ApiKey; +}; + +const ServiceParamAssignInput: React.FC = ({ + required, + disabled, + defaultValue, + serviceKey, + ...props +}) => { + const { getEntityList } = useConfigPanelStore(useStoreShallow(['getEntityList'])); + const { getEntityChildren } = useEntityApi(); + const [innerValue, setInnerValue] = + useControllableValue(props); + const { list, replace, resetList } = useDynamicList(innerValue); + const handlerChange = async (entityKey?: ApiKey) => { + if (entityKey) { + const entityFilterList = await getEntityList({ keyword: entityKey as string }); + if (entityFilterList?.length) { + const entityItem = entityFilterList[0]; + const { error, res } = await getEntityChildren({ + id: entityItem.entity_id, + }); + if (!error) { + resetList( + res.map((item: EntityItem) => { + const valueItem = innerValue?.find( + innerItem => innerItem.key === item.entity_key, + ); + return { + key: item.entity_key, + name: item.entity_name, + type: item.entity_value_type, + value: valueItem?.value ?? '', + }; + }), + ); + return; + } + } + } + resetList([]); + }; + const renderInputParam = useMemo(() => { + if (list.length) { + return ( +
+ {list.map((item, index) => { + return ( +
+
+ {item.name} + {item.type} +
+ { + replace(index, { ...item, value: data }); + }} + /> +
+ ); + })} +
+ ); + } + return null; + }, [list]); + useLayoutEffect(() => { + setInnerValue(list); + }, [list]); + useLayoutEffect(() => { + handlerChange(serviceKey); + }, [serviceKey]); + return
{renderInputParam}
; +}; +export default ServiceParamAssignInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/style.less new file mode 100644 index 00000000..cef67a52 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/style.less @@ -0,0 +1,24 @@ +.@{prefix}-service-param-assign-input { + .param-item { + margin-top: @margin-md; + + &-title { + margin-bottom: @margin-xs; + } + + &-name { + font-size: @font-size-md; + font-weight: @font-weight-medium; + line-height: 22px; + color: #272e3b; + } + + &-type { + margin-left: @margin-xs; + font-size: @font-size-md; + font-style: normal; + line-height: 22px; + color: #6b7785; + } + } +} \ No newline at end of file diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index a1e130c1..2a548260 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -1,4 +1,4 @@ -import { useMemo } from 'react'; +import { useMemo, useState } from 'react'; import { type ControllerProps } from 'react-hook-form'; import { TextField } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; @@ -14,7 +14,7 @@ import { // ParamInputSelect, TimerInput, ParamInput, - ServiceEntityCall, + ServiceParamAssignInput, } from '../components'; type NodeFormGroupType = { @@ -30,7 +30,7 @@ export type NodeFormDataProps = Record; const useNodeFormItems = (node?: WorkflowNode) => { // const { getIntlText } = useI18n(); - + const [serviceKey, setServiceKey] = useState(); const formConfigs = useMemo(() => { const result: Partial> = { trigger: [ @@ -131,7 +131,10 @@ const useNodeFormItems = (node?: WorkflowNode) => { { + setServiceKey(value); + onChange(value); + }} /> ); }, @@ -141,7 +144,20 @@ const useNodeFormItems = (node?: WorkflowNode) => { { groupName: 'Input Variables', helperText: 'Please select the service you want to call first.', - children: [], + children: [ + { + name: 'paramList', + render({ field: { onChange, value } }) { + return ( + + ); + }, + }, + ], }, ], assigner: [ @@ -276,18 +292,10 @@ const useNodeFormItems = (node?: WorkflowNode) => { ], }, ], - service: [ - { - name: 'service', - render({ field: { onChange, value }, fieldState: { error } }) { - return ; - }, - }, - ], }; return result; - }, []); + }, [serviceKey]); return !node?.type ? [] : formConfigs[node.type] || []; }; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index c6c74a6e..600b5933 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -70,7 +70,5 @@ "workflow.label.condition_operator_is_empty": "Is Empty", "workflow.label.condition_operator_is_not_empty": "Is Not Empty", "workflow.node.config_panel_lable_arguments": "Arguments", - "workflow.node.service_call_service_title": "Service Setting", - "workflow.node.service_call_Input_title": "Input Variables", "workflow.message.import_dsl_error": "The DSL file contains errors. Please update the file and try again." } From 527d313ab0faa8dbc90ab35c8a86dc3cb6b90c4d Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 19 Dec 2024 13:25:56 +0800 Subject: [PATCH 051/172] feat: Add run milliseconds to display in the action log component --- .../components/action-log/components/header/index.tsx | 11 +++++++++-- .../action-log/components/header/style.less | 3 ++- .../components/action-log/components/tree/index.tsx | 2 +- .../pages/workflow/components/action-log/index.tsx | 4 +++- .../pages/workflow/components/action-log/types.d.ts | 4 ++++ .../src/pages/workflow/components/log-modal/index.tsx | 2 +- packages/locales/src/lang/en/global.json | 3 ++- 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx index 9c40a549..6338df3d 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx @@ -1,5 +1,6 @@ import React, { useMemo } from 'react'; import cls from 'classnames'; +import { useI18n } from '@milesight/shared/src/hooks'; import { Tooltip } from '@/components'; import { basicNodeConfigs, LogStatusMap } from '@/pages/workflow/config'; import type { AccordionLog } from '../../types'; @@ -9,15 +10,18 @@ interface IProps { data: AccordionLog; } export default React.memo(({ data }: IProps) => { + const { getIntlText } = useI18n(); + /** Get the header render config */ - const { icon, iconBgColor, name, status } = useMemo(() => { - const { type, config, status, name } = data || {}; + const { icon, iconBgColor, name, status, timeCost } = useMemo(() => { + const { type, config, status, name, timeCost } = data || {}; const { icon, iconBgColor, labelIntlKey } = config || {}; const result = basicNodeConfigs[type]; return { status, name, + timeCost, icon: icon || result?.icon, iconBgColor: iconBgColor || result?.iconBgColor, labelIntlKey: labelIntlKey || result?.labelIntlKey, @@ -39,6 +43,9 @@ export default React.memo(({ data }: IProps) => { + {timeCost && ( +
{`${timeCost}${getIntlText('common.label.ms')}`}
+ )}
{statusIcon}
); diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/style.less b/apps/web/src/pages/workflow/components/action-log/components/header/style.less index a6da62e9..f507c631 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/style.less +++ b/apps/web/src/pages/workflow/components/action-log/components/header/style.less @@ -30,7 +30,8 @@ } } - .ms-header-status { + .ms-header-status, + .ms-header-ms { display: flex; align-items: center; margin-left: @margin-xxs; diff --git a/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx index 3e1375d0..1bb73164 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/tree/index.tsx @@ -11,7 +11,7 @@ interface IProps { } export default React.memo(({ header, children }: IProps) => { return ( - + } className="ms-log-tree__header"> {header} diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index 34c3a53d..a49f5907 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -44,12 +44,13 @@ export default function AccordionUsage({ traceData, workflowData }: IProps) { const nestNode = cloneDeep(node) as WorkflowNestNode; // TODO name const { id, type, $$name } = nestNode || {}; - const { status, input, output } = traceMap[id] || {}; + const { status, input, output, time_cost: timeCost } = traceMap[id] || {}; nestNode.attrs = { name: $$name, type: type!, status: status === 'SUCCESS' ? 'success' : 'failed', + timeCost, input, output, }; @@ -65,6 +66,7 @@ export default function AccordionUsage({ traceData, workflowData }: IProps) { }; /** Generate Tree Data */ const treeData = useMemo(() => { + // TODO Reconstructs the tree generation logic const { nodes, edges } = workflowData || {}; const nestNodes = (nodes || []).map(node => wrapperNode(node)); diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index 31e99d7f..7d9e5591 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -22,6 +22,10 @@ export interface AccordionLog { * Node status */ status: LogStatus; + /** + * Node time cost + */ + timeCost: number; /** * TODO Input */ diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx index 37d0b7d7..ca8de21b 100644 --- a/apps/web/src/pages/workflow/components/log-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -44,7 +44,7 @@ export default React.memo(({ visible, ...props }: IProps) => { const isEmpty = !getLogListLoading && !scrollItem?.list?.length; return ( Date: Thu, 19 Dec 2024 13:27:30 +0800 Subject: [PATCH 052/172] feat: Integrate custom entities --- .env | 6 +- apps/web/package.json | 1 + .../components/date-range-picker/index.tsx | 11 +- apps/web/src/constant.ts | 23 ++ .../components/add-from-workflow/index.tsx | 3 +- .../entity/components/add-modal/index.tsx | 82 ++++- .../entity/components/add-modal/useForm.tsx | 222 +++++++++++--- .../entity/components/custom-entity/index.tsx | 29 +- .../pages/entity/components/detail/index.tsx | 1 + .../pages/entity/components/entity/index.tsx | 2 +- .../entity/components/export-modal/index.tsx | 45 +++ .../web/src/pages/entity/hooks/useColumns.tsx | 11 +- apps/web/src/pages/entity/index.tsx | 260 +++------------- apps/web/src/pages/entity/style.less | 88 +++++- apps/web/src/services/http/entity.ts | 53 ++-- packages/locales/src/lang/en/entity.json | 15 +- packages/locales/src/lang/en/global.json | 5 +- packages/shared/src/components/form/index.tsx | 24 +- packages/shared/types/entity.d.ts | 4 +- pnpm-lock.yaml | 285 ++++++++++-------- 20 files changed, 728 insertions(+), 442 deletions(-) create mode 100644 apps/web/src/constant.ts create mode 100644 apps/web/src/pages/entity/components/export-modal/index.tsx diff --git a/.env b/.env index 1e21a680..2e83533c 100644 --- a/.env +++ b/.env @@ -5,9 +5,11 @@ WEB_API_ORIGIN=/ # Websocket Host 地址 WEB_WS_HOST=/ # 接口 API 代理地址 -WEB_API_PROXY=https://demo.beaver-iot.com/api/v1 +# WEB_API_PROXY=https://demo.beaver-iot.com/api/v1 +WEB_API_PROXY=http://192.168.43.48:9200 # WebSocket 代理地址 -WEB_SOCKET_PROXY=wss://demo.beaver-iot.com +# WEB_SOCKET_PROXY=wss://demo.beaver-iot.com +WEB_SOCKET_PROXY=ws://192.168.43.48:9200 # Oauth client ID OAUTH_CLIENT_ID=iab diff --git a/apps/web/package.json b/apps/web/package.json index 29704203..37021607 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -37,6 +37,7 @@ "react-intl-universal": "^2.11.1", "react-router": "^6.26.1", "react-router-dom": "^6.26.1", + "uuid": "^11.0.3", "zustand": "^4.5.5" }, "devDependencies": { diff --git a/apps/web/src/components/date-range-picker/index.tsx b/apps/web/src/components/date-range-picker/index.tsx index 0c7ec4ad..2b3e1fe2 100644 --- a/apps/web/src/components/date-range-picker/index.tsx +++ b/apps/web/src/components/date-range-picker/index.tsx @@ -2,21 +2,24 @@ import React, { useState } from 'react'; import { type Dayjs } from 'dayjs'; import { Box } from '@mui/material'; import { styled } from '@mui/material/styles'; -import { DatePicker, DatePickerProps } from '@mui/x-date-pickers/DatePicker'; +import { DateTimePicker, DateTimePickerProps } from '@mui/x-date-pickers/DateTimePicker'; export type DateRangePickerValueType = { start?: Dayjs | null; end?: Dayjs | null; }; +type ViewsType = 'year' | 'month' | 'day' | 'hours' | 'minutes'; + interface DateRangePickerProps - extends Omit, 'value' | 'label' | 'onChange'> { + extends Omit, 'value' | 'label' | 'onChange' | 'views'> { value?: DateRangePickerValueType | null; label?: { start?: React.ReactNode; end?: React.ReactNode; }; onChange?: (value: DateRangePickerValueType | null) => void; + views?: ViewsType[]; } const DateRangePickerStyled = styled('div')(() => ({ @@ -30,7 +33,7 @@ const DateRangePicker: React.FC = ({ label, value, onChang return ( - { @@ -47,7 +50,7 @@ const DateRangePicker: React.FC = ({ label, value, onChang {...props} /> - { diff --git a/apps/web/src/constant.ts b/apps/web/src/constant.ts new file mode 100644 index 00000000..1b78ccfc --- /dev/null +++ b/apps/web/src/constant.ts @@ -0,0 +1,23 @@ +// Entity Pattern +export enum ENTITY_AEECSS_MODE { + R = 'R', + RW = 'RW', + W = 'W', +} + +// Entity Type +export enum ENTITY_TYPE { + SERVICE = 'SERVICE', + PROPERTY = 'PROPERTY', + EVENT = 'EVENT', +} + +// Entity Value Type +export enum ENTITY_VALUE_TYPE { + STRING = 'STRING', + long = 'LONG', + BOOLEAN = 'BOOLEAN', + BINARY = 'BINARY', + OBJECT = 'OBJECT', + ENUM = 'ENUM', +} diff --git a/apps/web/src/pages/entity/components/add-from-workflow/index.tsx b/apps/web/src/pages/entity/components/add-from-workflow/index.tsx index 1ad08a98..68bf10cf 100644 --- a/apps/web/src/pages/entity/components/add-from-workflow/index.tsx +++ b/apps/web/src/pages/entity/components/add-from-workflow/index.tsx @@ -6,11 +6,12 @@ import { TableRowDataType } from '../../hooks/useColumns'; interface ConfigPluginProps { onCancel: () => void; onOk: (data: TableRowDataType) => void; + data?: TableRowDataType | null; } const AddEntity = (props: ConfigPluginProps) => { const { getIntlText } = useI18n(); - const { onOk, onCancel } = props; + const { onOk, onCancel, data } = props; const formRef = useRef(); // Initial form configuration diff --git a/apps/web/src/pages/entity/components/add-modal/index.tsx b/apps/web/src/pages/entity/components/add-modal/index.tsx index 433e2315..26eb3fa1 100644 --- a/apps/web/src/pages/entity/components/add-modal/index.tsx +++ b/apps/web/src/pages/entity/components/add-modal/index.tsx @@ -1,18 +1,22 @@ -import { useRef, useState, useEffect } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useI18n } from '@milesight/shared/src/hooks'; import { Modal, Form } from '@milesight/shared/src/components'; +import { entityAPI, awaitWrap, isRequestSuccess } from '@/services/http'; +import { ENTITY_TYPE } from '@/constant'; import { TableRowDataType } from '../../hooks/useColumns'; import useForm from './useForm'; -interface ConfigPluginProps { +interface IProps { onCancel: () => void; - onOk: (data: TableRowDataType) => void; + onOk: (data: Record) => void; + data?: TableRowDataType | null; } -const AddEntity = (props: ConfigPluginProps) => { +const AddEntity = (props: IProps) => { const { getIntlText } = useI18n(); - const { onOk, onCancel } = props; + const { onOk, onCancel, data } = props; const [formValues, setFormValues] = useState>({}); + const [defaultValues, setDefaultValues] = useState>(); const formRef = useRef(); const formValuesRef = useRef>({}); @@ -22,6 +26,8 @@ const AddEntity = (props: ConfigPluginProps) => { const formItems = useForm({ formValues, + data, + defaultValues, preFormValues: { ...formValuesRef.current }, setPreFormValues: setFormValuesRef, }); @@ -35,11 +41,73 @@ const AddEntity = (props: ConfigPluginProps) => { }; // Form submission - const handleSubmit = (values: TableRowDataType) => { + const handleSubmit = async (values: Record) => { + const resultValues: Record = { value_attribute: {} }; console.log(values); - // onOk(values); + Object.keys(values).forEach((key: string) => { + if (key.indexOf(`temp_${values.value_type}_key_`) > -1) { + if (!resultValues.value_attribute.enum) { + resultValues.value_attribute.enum = {}; + } + const keyIndex = Number(key.replace(`temp_${values.value_type}_key_`, '')) || 1; + if (values.value_type === 'BOOLEAN') { + const resultKey = keyIndex === 1; + resultValues.value_attribute.enum[`${resultKey}`] = + values[key.replace('key', 'value')]; + } else { + resultValues.value_attribute.enum[values[key]] = + values[key.replace('key', 'value')]; + } + } else if (['min', 'max', 'minLength', 'maxLength'].includes(key)) { + resultValues.value_attribute[key] = values[key]; + } else if (key.indexOf(`temp_${values.value_type}_value_`) === -1) { + resultValues[key] = values[key]; + } + }); + const fun = data?.entityId ? entityAPI.editCustomEntity : entityAPI.createCustomEntity; + const [err, res] = await awaitWrap( + fun({ + ...(resultValues as any), + type: ENTITY_TYPE.PROPERTY, + entityId: data?.entityId, + }), + ); + if (!err && isRequestSuccess(res)) { + onOk(resultValues); + } }; + useEffect(() => { + if (data) { + const resultFormValues: Record = { + name: data.entityName, + identifier: data.identifier, + access_mod: data.entityAccessMod, + value_type: data.entityValueType, + }; + if (data.entityValueAttribute?.enum) { + Object.keys(data.entityValueAttribute.enum).forEach( + (key: string, index: number) => { + resultFormValues[`temp_${data.entityValueType}_key_${index + 1}`] = key; + resultFormValues[`temp_${data.entityValueType}_value_${index + 1}`] = + data.entityValueAttribute.enum[key]; + }, + ); + } else if (data.entityValueAttribute?.min || data.entityValueAttribute?.min === 0) { + resultFormValues.min = data.entityValueAttribute.min; + resultFormValues.max = data.entityValueAttribute.max; + } else if ( + data.entityValueAttribute?.minLength || + data.entityValueAttribute?.minLength === 0 + ) { + resultFormValues.minLength = data.entityValueAttribute.minLength; + resultFormValues.maxLength = data.entityValueAttribute.maxLength; + } + setFormValues(resultFormValues); + setDefaultValues(resultFormValues); + } + }, [data]); + const handleChange = (values: any) => { setFormValues(values); }; diff --git a/apps/web/src/pages/entity/components/add-modal/useForm.tsx b/apps/web/src/pages/entity/components/add-modal/useForm.tsx index 064dce17..42b61b95 100644 --- a/apps/web/src/pages/entity/components/add-modal/useForm.tsx +++ b/apps/web/src/pages/entity/components/add-modal/useForm.tsx @@ -1,18 +1,23 @@ import { useEffect, useState, useRef } from 'react'; import { ToggleButtonGroup, ToggleButton, Button } from '@mui/material'; +import { v4 } from 'uuid'; import { useI18n } from '@milesight/shared/src/hooks'; import { UseFormItemsProps } from '@milesight/shared/src/components'; +import { ENTITY_AEECSS_MODE } from '@/constant'; +import { TableRowDataType } from '../../hooks/useColumns'; interface FormProps { preFormValues: Record; formValues: Record; setPreFormValues: (data: Record) => void; + defaultValues?: Record; + data?: TableRowDataType | null; } type ModeType = 'value' | 'enum'; const useForm = (props: FormProps) => { - const { preFormValues, formValues, setPreFormValues } = props; + const { preFormValues, formValues, setPreFormValues, defaultValues, data } = props; const { getIntlText } = useI18n(); const [formItems, setFormItems] = useState([]); const [mode, setMode] = useState('value'); @@ -22,8 +27,9 @@ const useForm = (props: FormProps) => { const initFormItems: UseFormItemsProps[] = [ { label: getIntlText('device.label.param_entity_name'), - name: 'entityName', + name: 'name', type: 'TextField', + defaultValue: defaultValues?.name, rules: { required: true, maxLength: { @@ -32,53 +38,78 @@ const useForm = (props: FormProps) => { }, }, }, + { + label: getIntlText('common.label.key'), + name: 'identifier', + type: 'TextField', + defaultValue: defaultValues?.identifier || v4().replace(/-/g, ''), + rules: { + required: true, + maxLength: { + value: 64, + message: '', + }, + pattern: { + value: /^[a-zA-Z0-9_]+$/, + message: 'Username can only contain letters, numbers, and underscores', + }, + }, + }, { label: getIntlText('entity.label.entity_type_of_access'), - name: 'entityAccessMod', + name: 'access_mod', type: 'Select', + defaultValue: defaultValues?.access_mod, rules: { required: true, }, props: { + disabled: !!defaultValues, options: [ { label: getIntlText('entity.label.entity_type_of_access_readonly'), - value: 'r', + value: ENTITY_AEECSS_MODE.R, }, { label: getIntlText('entity.label.entity_type_of_access_write'), - value: 'w', + value: ENTITY_AEECSS_MODE.W, + }, + { + label: getIntlText('entity.label.entity_type_of_access_read_and_write'), + value: ENTITY_AEECSS_MODE.RW, }, ], }, }, { label: getIntlText('common.label.data_type'), - name: 'entityValueType', + name: 'value_type', type: 'Select', + defaultValue: defaultValues?.value_type, rules: { required: true, }, props: { + disabled: !!defaultValues, componentProps: { fullWidth: true, }, options: [ { label: getIntlText('entity.label.entity_type_of_string'), - value: 'string', + value: 'STRING', }, { label: getIntlText('entity.label.entity_type_of_int'), - value: 'int', + value: 'LONG', }, { - label: getIntlText('entity.label.entity_type_of_boolean'), - value: 'boolean', + label: getIntlText('entity.label.entity_type_of_float'), + value: 'DOUBLE', }, { - label: getIntlText('entity.label.entity_type_of_enum'), - value: 'enum', + label: getIntlText('entity.label.entity_type_of_boolean'), + value: 'BOOLEAN', }, ], }, @@ -102,8 +133,10 @@ const useForm = (props: FormProps) => { className="entity-modal-button-group" fullWidth > - Set Value - Set Enumeration Items + {getIntlText('entity.label.set_value')} + + {getIntlText('entity.label.set_enumeration_items')} + ); }; @@ -115,19 +148,29 @@ const useForm = (props: FormProps) => { const initAddFormItem: UseFormItemsProps[] = [ { label: getIntlText('common.label.key'), - name: `key_${childrenLength - 1}`, + name: `temp_key_${formValues.value_type}_${childrenLength}`, type: 'TextField', + defaultValue: + defaultValues?.[`temp_key_${formValues.value_type}_${childrenLength}`], rules: { required: true, }, + props: { + disabled: !!defaultValues, + }, }, { label: getIntlText('common.label.value'), - name: `value_${childrenLength - 1}`, + name: `temp_value_${formValues.value_type}_${childrenLength}`, type: 'TextField', + defaultValue: + defaultValues?.[`temp_value_${formValues.value_type}_${childrenLength}`], rules: { required: true, }, + props: { + disabled: !!defaultValues, + }, }, ]; newFormItems[newFormItems.length - 1].children?.splice( @@ -152,25 +195,35 @@ const useForm = (props: FormProps) => { return [ { label: - formValues.entityValueType === 'int' - ? getIntlText('entity.label.entity_maximum_value') - : getIntlText('entity.label.entity_maximum_length'), - name: formValues.entityValueType === 'int' ? 'maxValue' : 'maxLength', + formValues.value_type === 'LONG' + ? getIntlText('entity.label.entity_minimum_value') + : getIntlText('entity.label.entity_minimum_length'), + name: formValues.value_type === 'LONG' ? 'min' : 'minLength', type: 'TextField', + defaultValue: + defaultValues?.[formValues.value_type === 'LONG' ? 'min' : 'minLength'], rules: { required: true, }, + props: { + disabled: !!defaultValues, + }, }, { label: - formValues.entityValueType === 'int' - ? getIntlText('entity.label.entity_minimum_value') - : getIntlText('entity.label.entity_minimum_length'), - name: formValues.entityValueType === 'int' ? 'minValue' : 'minLength', + formValues.value_type === 'LONG' + ? getIntlText('entity.label.entity_maximum_value') + : getIntlText('entity.label.entity_maximum_length'), + name: formValues.value_type === 'LONG' ? 'max' : 'maxLength', type: 'TextField', + defaultValue: + defaultValues?.[formValues.value_type === 'LONG' ? 'max' : 'maxLength'], rules: { required: true, }, + props: { + disabled: !!defaultValues, + }, }, ]; } @@ -182,19 +235,27 @@ const useForm = (props: FormProps) => { children: [ { label: getIntlText('common.label.key'), - name: 'key_1', + name: `temp_${formValues.value_type}_key_1`, type: 'TextField', + defaultValue: defaultValues?.[`${formValues.value_type}_key_1`], rules: { required: true, }, + props: { + disabled: !!defaultValues, + }, }, { label: getIntlText('common.label.value'), - name: 'value_1', + name: `temp_${formValues.value_type}_value_1`, type: 'TextField', + defaultValue: defaultValues?.[`${formValues.value_type}_value_1`], rules: { required: true, }, + props: { + disabled: !!defaultValues, + }, }, { label: '', @@ -211,8 +272,8 @@ const useForm = (props: FormProps) => { const getFormItems = (type: string): any[] => { switch (type) { - case 'int': - case 'string': + case 'LONG': + case 'STRING': return [ ...initFormItems, { @@ -224,30 +285,117 @@ const useForm = (props: FormProps) => { }, ...renderModeToForm(), ]; + case 'DOUBLE': + return [ + ...initFormItems, + { + label: getIntlText('entity.label.entity_minimum_value'), + name: 'min', + type: 'TextField', + defaultValue: defaultValues?.min, + rules: { + required: true, + }, + props: { + disabled: !!defaultValues, + }, + }, + { + label: getIntlText('entity.label.entity_maximum_value'), + name: 'max', + type: 'TextField', + defaultValue: defaultValues?.max, + rules: { + required: true, + }, + props: { + disabled: !!defaultValues, + }, + }, + ]; + case 'BOOLEAN': + return [ + ...initFormItems, + { + label: getIntlText('entity.label.entity_items'), + name: '', + type: '', + children: [ + { + label: getIntlText('common.label.key'), + name: `temp_${formValues.value_type}_key_1`, + type: 'TextField', + defaultValue: + defaultValues?.[`temp_${formValues.value_type}_key_1`], + props: { + disabled: true, + }, + }, + { + label: getIntlText('common.label.label'), + name: `temp_${formValues.value_type}_value_1`, + type: 'TextField', + defaultValue: + defaultValues?.[`temp_${formValues.value_type}_value_1`], + rules: { + required: true, + }, + props: { + disabled: !!defaultValues, + }, + }, + { + label: getIntlText('common.label.key'), + name: `temp_${formValues.value_type}_key_2`, + type: 'TextField', + defaultValue: + defaultValues?.[`temp_${formValues.value_type}_key_2`], + props: { + disabled: true, + }, + }, + { + label: getIntlText('common.label.label'), + name: `temp_${formValues.value_type}_value_2`, + type: 'TextField', + defaultValue: + defaultValues?.[`temp_${formValues.value_type}_value_2`], + rules: { + required: true, + }, + props: { + disabled: !!defaultValues, + }, + }, + ], + }, + ]; default: return initFormItems; } }; useEffect(() => { - if ( - preFormValues.entityValueType !== formValues.entityValueType || - !formValues.entityValueType - ) { - const newFormItem = getFormItems(formValues.entityValueType); + if (data && !defaultValues) { + return; + } + if (preFormValues.value_type !== formValues.value_type || !formValues.value_type) { + const newFormItem = getFormItems(formValues.value_type); setFormItems(newFormItem); setPreFormValues(formValues); } - }, [formValues]); + }, [formValues, data, defaultValues]); useEffect(() => { - const newFormItem = getFormItems(formValues.entityValueType); + if (data && !defaultValues) { + return; + } + const newFormItem = getFormItems(formValues.value_type); setFormItems(newFormItem); - }, [mode]); + }, [mode, data, defaultValues]); useEffect(() => { formItemsRef.current = [...formItems]; - console.log(formItemsRef.current); }, [formItems]); return formItems; diff --git a/apps/web/src/pages/entity/components/custom-entity/index.tsx b/apps/web/src/pages/entity/components/custom-entity/index.tsx index bd5490f1..e2732742 100644 --- a/apps/web/src/pages/entity/components/custom-entity/index.tsx +++ b/apps/web/src/pages/entity/components/custom-entity/index.tsx @@ -16,7 +16,6 @@ import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/servi import { useColumns, type UseColumnsProps, type TableRowDataType } from '../../hooks'; import AddModal from '../add-modal'; import AddFromWorkflow from '../add-from-workflow'; -import Detail from '../detail'; export default () => { const navigate = useNavigate(); @@ -47,10 +46,11 @@ export default () => { async () => { const { page, pageSize } = paginationModel; const [error, resp] = await awaitWrap( - entityAPI.getCustomEntityList({ + entityAPI.getList({ keyword, page_size: pageSize, page_number: page + 1, + customized: true, }), ); const data = getResponseData(resp); @@ -79,7 +79,7 @@ export default () => { }, onConfirm: async () => { const [error, resp] = await awaitWrap( - entityAPI.deleteEntities({ entity_id_list: idsToDelete }), + entityAPI.deleteEntities({ entity_ids: idsToDelete }), ); if (error || !isRequestSuccess(resp)) return; @@ -93,12 +93,14 @@ export default () => { [confirm, getIntlText, getList, selectedIds], ); - /** Details event related */ - const handleDetail = (data: TableRowDataType) => { - setDetail(data); + /** Details/Add event related */ + const handleAdd = (data?: TableRowDataType) => { + !!data && setDetail(data); + setModalOpen(true); }; - const handleDetailClose = () => { + const handleAddClose = () => { + setModalOpen(false); setDetail(null); }; @@ -133,8 +135,8 @@ export default () => { const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { switch (type) { - case 'detail': { - handleDetail(record); + case 'edit': { + handleAdd(record); break; } case 'delete': { @@ -183,23 +185,24 @@ export default () => { /> {modalOpen && ( setModalOpen(false)} + onCancel={handleAddClose} onOk={() => { getList(); - setModalOpen(false); + handleAddClose(); }} + data={detail} /> )} - {workflowModalOpen && ( + {workflowModalOpen && !!detail && ( setWorkflowModalOpen(false)} onOk={() => { getList(); setWorkflowModalOpen(false); }} + data={detail} /> )} - {!!detail && } { label={{ start: 'Start date', end: 'End date' }} onChange={changeFilter} value={time} + views={['year', 'month', 'day']} />
diff --git a/apps/web/src/pages/entity/components/entity/index.tsx b/apps/web/src/pages/entity/components/entity/index.tsx index fca429bd..1a08f523 100644 --- a/apps/web/src/pages/entity/components/entity/index.tsx +++ b/apps/web/src/pages/entity/components/entity/index.tsx @@ -75,7 +75,7 @@ export default () => { }, onConfirm: async () => { const [error, resp] = await awaitWrap( - entityAPI.deleteEntities({ entity_id_list: idsToDelete }), + entityAPI.deleteEntities({ entity_ids: idsToDelete }), ); if (error || !isRequestSuccess(resp)) return; diff --git a/apps/web/src/pages/entity/components/export-modal/index.tsx b/apps/web/src/pages/entity/components/export-modal/index.tsx new file mode 100644 index 00000000..6c110bd1 --- /dev/null +++ b/apps/web/src/pages/entity/components/export-modal/index.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Modal } from '@milesight/shared/src/components'; +import { DateRangePickerValueType } from '@/components/date-range-picker'; +import { DateRangePicker } from '@/components'; + +interface IProps { + onCancel: () => void; + onOk: (data: DateRangePickerValueType | null) => void; +} + +const ExportModal = (props: IProps) => { + const { getIntlText } = useI18n(); + const { onOk, onCancel } = props; + const [time, setTime] = useState(null); + + const handleClose = () => { + onCancel(); + }; + + const handleOk = () => { + onOk(time); + }; + + const changeTime = (values: DateRangePickerValueType | null) => { + setTime(values); + }; + + return ( + + + + ); +}; + +export default ExportModal; diff --git a/apps/web/src/pages/entity/hooks/useColumns.tsx b/apps/web/src/pages/entity/hooks/useColumns.tsx index 46ed682c..f1997efc 100644 --- a/apps/web/src/pages/entity/hooks/useColumns.tsx +++ b/apps/web/src/pages/entity/hooks/useColumns.tsx @@ -5,7 +5,7 @@ import { ListAltIcon, DeleteOutlineIcon } from '@milesight/shared/src/components import { Tooltip, type ColumnType } from '@/components'; import { type EntityAPISchema } from '@/services/http'; -type OperationType = 'detail' | 'delete'; +type OperationType = 'edit' | 'delete'; export type TableRowDataType = ObjectToCamelCase< EntityAPISchema['getList']['response']['content'][0] @@ -39,7 +39,7 @@ const useColumns = ({ onButtonClick }: UseColumnsPro ellipsis: true, }, { - field: 'entityId', + field: 'entityKey', headerName: getIntlText('device.label.param_entity_id'), flex: 1, minWidth: 150, @@ -71,12 +71,15 @@ const useColumns = ({ onButtonClick }: UseColumnsPro ellipsis: true, }, { - field: 'createdAt', + field: 'entityCreatedAt', headerName: getIntlText('common.label.create_time'), flex: 1, minWidth: 150, ellipsis: true, renderCell({ value }) { + if (!value) { + return; + } return getTimeFormat(value); }, }, @@ -95,7 +98,7 @@ const useColumns = ({ onButtonClick }: UseColumnsPro onButtonClick('detail', row)} + onClick={() => onButtonClick('edit', row)} > diff --git a/apps/web/src/pages/entity/index.tsx b/apps/web/src/pages/entity/index.tsx index 7f8e6543..ccbdb3ef 100644 --- a/apps/web/src/pages/entity/index.tsx +++ b/apps/web/src/pages/entity/index.tsx @@ -1,237 +1,53 @@ -import { useState, useMemo, useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { Button, Stack, Menu, MenuItem } from '@mui/material'; -import { useRequest } from 'ahooks'; +import { useMemo } from 'react'; +import { Tabs, Tab } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; -import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; -import { - AddIcon, - DeleteOutlineIcon, - NoteAddIcon, - CalculateIcon, - toast, -} from '@milesight/shared/src/components'; -import { Breadcrumbs, TablePro, useConfirm } from '@/components'; -import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; -import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; -import { AddModal } from './components'; +import { useRouteTab } from '@/hooks'; +import { Breadcrumbs, TabPanel } from '@/components'; +import CustomEntity from './components/custom-entity'; +import Entity from './components/entity'; import './style.less'; export default () => { - const navigate = useNavigate(); const { getIntlText } = useI18n(); - - // ---------- 列表数据相关 ---------- - const [keyword, setKeyword] = useState(); - const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); - const [selectedIds, setSelectedIds] = useState([]); - const [anchorEl, setAnchorEl] = useState(null); - const open = Boolean(anchorEl); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - const { - data: entityData, - loading, - run: getDeviceList, - } = useRequest( - async () => { - const { page, pageSize } = paginationModel; - const [error, resp] = await awaitWrap( - entityAPI.getList({ - keyword, - page_size: pageSize, - page_number: page + 1, - }), - ); - const data = getResponseData(resp); - - // console.log({ error, resp }); - if (error || !data || !isRequestSuccess(resp)) return; - - return objectToCamelCase(data); - }, - { - debounceWait: 300, - refreshDeps: [keyword, paginationModel], - }, - ); - - // ---------- 设备添加相关 ---------- - const [modalOpen, setModalOpen] = useState(false); - - // ---------- 数据删除相关 ---------- - const confirm = useConfirm(); - const handleDeleteConfirm = useCallback( - (ids?: ApiKey[]) => { - const idsToDelete = ids || [...selectedIds]; - - confirm({ - title: getIntlText('common.label.delete'), - description: getIntlText('device.message.delete_tip'), - confirmButtonText: getIntlText('common.label.delete'), - confirmButtonProps: { - color: 'error', - }, - onConfirm: async () => { - const [error, resp] = await awaitWrap( - entityAPI.deleteEntities({ entity_id_list: idsToDelete }), - ); - - // console.log({ error, resp }); - if (error || !isRequestSuccess(resp)) return; - - getDeviceList(); - setSelectedIds([]); - toast.success(getIntlText('common.message.delete_success')); - }, - }); - }, - [confirm, getIntlText, getDeviceList, selectedIds], - ); - - // ---------- Table 渲染相关 ---------- - const toolbarRender = useMemo(() => { - return ( - - - - - ); - }, [getIntlText, handleDeleteConfirm, selectedIds]); - - const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( - (type, record) => { - // console.log(type, record); - switch (type) { - case 'detail': { - navigate(`/device/detail/${record.entityId}`, { state: record }); - break; - } - case 'delete': { - handleDeleteConfirm([record.entityId]); - break; - } - default: { - break; - } - } - }, - [navigate, handleDeleteConfirm], - ); - const columns = useColumns({ onButtonClick: handleTableBtnClick }); - - const handleShowAddOnly = () => { - setModalOpen(true); - handleClose(); - }; + const tabs = useMemo(() => { + return [ + { + key: 'custom-entity', + label: getIntlText('entity.label.custom_entity'), + component: , + }, + { + key: 'entity-data', + label: getIntlText('device.detail.entity_data'), + component: , + }, + ]; + }, [getIntlText]); + const [tabKey, setTabKey] = useRouteTab(tabs[0].key); return (
-
- - checkboxSelection - loading={loading} - columns={columns} - getRowId={record => record.entityId} - rows={entityData?.content} - rowCount={entityData?.total || 0} - paginationModel={paginationModel} - rowSelectionModel={selectedIds} - // isRowSelectable={({ row }) => row.deletable} - toolbarRender={toolbarRender} - onPaginationModelChange={setPaginationModel} - onRowSelectionModelChange={setSelectedIds} - onRowDoubleClick={({ row }) => { - navigate(`/device/detail/${row.entityId}`, { state: row }); - }} - onSearch={setKeyword} - onRefreshButtonClick={getDeviceList} - /> +
+ setTabKey(value)} + > + {tabs.map(({ key, label }) => ( + + ))} + +
+
+ {tabs.map(({ key, component }) => ( + + {component} + + ))}
- {modalOpen && ( - setModalOpen(false)} - onOk={() => { - getDeviceList(); - setModalOpen(false); - }} - /> - )} - - -
- {getIntlText('entity.label.create_property')} -
-
- -
- - {getIntlText('entity.label.create_entity_only')} -
-
- -
- {getIntlText('entity.label.create_service')} -
-
- -
- - {getIntlText('entity.label.create_entity_from_workflow')} -
-
-
); }; diff --git a/apps/web/src/pages/entity/style.less b/apps/web/src/pages/entity/style.less index 100c64ad..eef4a7db 100644 --- a/apps/web/src/pages/entity/style.less +++ b/apps/web/src/pages/entity/style.less @@ -1,12 +1,45 @@ .@{prefix}-view-entity { - position: relative; - padding: @padding-lg; + display: flex; + flex-direction: column; + padding: 0; + + .topbar { + display: flex; + align-items: center; + + .@{prefix}-tabs { + flex: 1; + } + + .btn-wrap { + display: flex; + align-items: center; + height: 100%; + padding: 0 @padding-lg; + background-color: var(--component-background); + border-bottom: 1px solid var(--border-color-base); + } + } .@{prefix}-view__inner { display: flex; flex-direction: column; padding: @padding-lg; } + + .@{prefix}-tab-content { + flex: 1; + height: 0; + padding: @padding-lg; + margin: @margin-lg; + overflow-y: auto; + background-color: var(--component-background); + border-radius: @border-radius-base; + + .@{prefix}-tabpanel { + height: 100%; + } + } } .entity { @@ -30,4 +63,55 @@ &-modal-button-group { margin-bottom: @margin-xl; } + + &-main { + margin: @margin-lg; + background: var(--main-background); + } + + &-detail { + &-table-header { + display: flex; + align-items: center; + margin: @margin-xl 0 @margin-md; + + &-title { + flex: 1; + font-size: @font-size-lg; + font-weight: @font-weight-medium; + } + } + + &-columns { + &-header { + display: flex; + align-items: center; + + &-label { + flex: 1; + } + + &-icon { + color: var(--gray-color-7); + + &-active { + color: var(--primary-color-7); + } + } + } + } + + &-filter-popover { + padding: @padding-lg; + + &-footer { + display: flex; + margin-top: @margin-lg; + + &-reset { + flex: 1; + } + } + } + } } \ No newline at end of file diff --git a/apps/web/src/services/http/entity.ts b/apps/web/src/services/http/entity.ts index 9a448e18..59734dda 100644 --- a/apps/web/src/services/http/entity.ts +++ b/apps/web/src/services/http/entity.ts @@ -16,6 +16,8 @@ export interface EntityAPISchema extends APISchema { * 不包含子节点(在选择触发服务实体的时候,不能直接下发子实体/在更新属性实体时,不能只更新某个子实体) */ exclude_children?: boolean; + /** 是否是自定义实体 */ + customized?: boolean; }; response: SearchResponseType; }; @@ -136,30 +138,11 @@ export interface EntityAPISchema extends APISchema { /** 删除实体 */ deleteEntities: { request: { - entity_id_list: ApiKey[]; + entity_ids: ApiKey[]; }; response: unknown; }; - /** 获取自定义实体列表 */ - getCustomEntityList: { - request: SearchRequestType & { - /** 搜索关键字 */ - keyword?: string; - /** 实体类型 */ - entity_type?: EntitySchema['type']; - /** 实体值类型 */ - entity_value_type?: EntityValueDataType[]; - /** 实体属性(可读、可写、只读) */ - entity_access_mod?: EntityAccessMode[]; - /** - * 不包含子节点(在选择触发服务实体的时候,不能直接下发子实体/在更新属性实体时,不能只更新某个子实体) - */ - exclude_children?: boolean; - }; - response: SearchResponseType; - }; - /** 编辑实体 */ editEntity: { request: { @@ -168,6 +151,31 @@ export interface EntityAPISchema extends APISchema { }; response: unknown; }; + + /** 创建实体 */ + createCustomEntity: { + request: { + name: string; + access_mod: EntityAccessMode; + value_type: EntityValueDataType; + value_attribute: Record; + type: EntityType; + }; + response: unknown; + }; + + /** 编辑实体 */ + editCustomEntity: { + request: { + name: string; + access_mod: EntityAccessMode; + value_type: EntityValueDataType; + value_attribute: Record; + type: EntityType; + entityId: ApiKey; + }; + response: unknown; + }; } /** @@ -185,8 +193,9 @@ export default attachAPI(client, { callService: `POST ${API_PREFIX}/entity/service/call`, getEntityStatus: `GET ${API_PREFIX}/entity/:id/status`, getChildrenEntity: `GET ${API_PREFIX}/entity/:id/children`, - deleteEntities: `POST ${API_PREFIX}/entity/batch-delete`, - getCustomEntityList: `POST ${API_PREFIX}/entity/search`, + deleteEntities: `POST ${API_PREFIX}/entity/delete`, editEntity: `POST ${API_PREFIX}/entity/:id`, + createCustomEntity: `POST ${API_PREFIX}/entity`, + editCustomEntity: `PUT ${API_PREFIX}/entity/:entityId`, }, }); diff --git a/packages/locales/src/lang/en/entity.json b/packages/locales/src/lang/en/entity.json index ecbfb7a2..38f90cd5 100644 --- a/packages/locales/src/lang/en/entity.json +++ b/packages/locales/src/lang/en/entity.json @@ -8,5 +8,18 @@ "entity.label.entity_minimum_value": "Minimum Value", "entity.label.entity_maximum_length": "Maximum Length", "entity.label.entity_minimum_length": "Minimum length", - "entity.label.entity_items": "Items" + "entity.label.entity_items": "Items", + "entity.label.value_source": "Value Source", + "entity.label.custom_entity": "Custom Entity", + "entity.label.historical_data": "Historical Data", + "common.button.reset": "Reset", + "entity.label.entity_type_of_access_readonly": "R", + "entity.label.entity_type_of_access_read_and_write": "RW", + "entity.label.entity_type_of_access_write": "W", + "entity.label.entity_type_of_string": "STRING", + "entity.label.entity_type_of_int": "INT", + "entity.label.entity_type_of_boolean": "BOOLEAN", + "entity.label.entity_type_of_float": "FLOAT", + "entity.label.set_enumeration_items": "Set Enumeration Items", + "entity.label.set_value": "Set Value" } \ No newline at end of file diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 6c320d68..680e2805 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -152,5 +152,8 @@ "valid.input.value": "Input value: {0} only.", "valid.input.version": "The version number does not comply with the specification.", "valid.resp.at_least_one": "Please select at least one {1}.", - "common.label.create": "Create" + "common.label.create": "Create", + "common.label.export": "Export", + "common.label.update_time": "Update Time", + "common.label.label": "Label" } diff --git a/packages/shared/src/components/form/index.tsx b/packages/shared/src/components/form/index.tsx index ef5d0535..fd9e71a0 100644 --- a/packages/shared/src/components/form/index.tsx +++ b/packages/shared/src/components/form/index.tsx @@ -24,7 +24,7 @@ const Forms = (props: formProps, ref: any) => { const forms: FormItemsProps[] = useFormItems({ formItems }); const formValuesRef = useRef(); - // 监听所有表单字段的变化 + // Listen to changes in all form fields const formValues = watch(); useEffect(() => { @@ -50,15 +50,31 @@ const Forms = (props: formProps, ref: any) => { !!Object.keys(values)?.length ) { formValuesRef.current = { ...formValuesRef?.current, ...formValues }; - // 表单值变更回调 + // Form value change callback !!onChange && onChange({ ...formValuesRef?.current, ...formValues }); } }, [formValues]); const onSubmit: SubmitHandler = async (data: T) => { - const result = await trigger(); // 手动触发验证 + const result = await trigger(); // Manually trigger validation if (result) { - onOk(data); + // To filter out fields that are not currently in the form. + const resultData: Record = {}; + const keys: string[] = []; + forms.forEach((item: FormItemsProps) => { + keys.push(item.name); + if (item?.children?.length) { + item?.children?.forEach(subItem => { + keys.push(subItem.name); + }); + } + }); + Object.keys(data)?.forEach(key => { + if (keys.includes(key)) { + resultData[key] = data[key]; + } + }); + onOk(resultData as T); } else { console.error('Validation failed'); } diff --git a/packages/shared/types/entity.d.ts b/packages/shared/types/entity.d.ts index ce275e9c..fa2c939f 100644 --- a/packages/shared/types/entity.d.ts +++ b/packages/shared/types/entity.d.ts @@ -114,11 +114,13 @@ declare interface EntityData { /** 实体名称 */ entity_name: string; /** 实体值属性 */ - entity_value_attribute: string; + entity_value_attribute: Record; /** 实体值类型 */ entity_value_type: EntityValueDataType; /** 实体属性访问类型 */ entity_access_mod: EntityAccessMode; + /** 唯一表示 */ + identifier: string; } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 231d1e22..8791ef08 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,7 +61,7 @@ importers: version: 11.13.3(@types/react@18.3.5)(react@18.3.1) '@emotion/styled': specifier: ^11.13.0 - version: 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) + version: 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@milesight/locales': specifier: workspace:* version: link:../../packages/locales @@ -70,13 +70,13 @@ importers: version: link:../../packages/shared '@mui/material': specifier: ^6.1.0 - version: 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) + version: 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-data-grid': specifier: ^7.16.0 - version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) + version: 7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.18.0 - version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + version: 7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ahooks: specifier: ^3.8.1 version: 3.8.1(react@18.3.1) @@ -115,7 +115,7 @@ importers: version: 18.3.1(react@18.3.1) react-grid-layout: specifier: ^1.5.0 - version: 1.5.0(react-dom@18.3.1)(react@18.3.1) + version: 1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-hook-form: specifier: ^7.53.0 version: 7.53.0(react@18.3.1) @@ -127,7 +127,10 @@ importers: version: 6.26.1(react@18.3.1) react-router-dom: specifier: ^6.26.1 - version: 6.26.1(react-dom@18.3.1)(react@18.3.1) + version: 6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + uuid: + specifier: ^11.0.3 + version: 11.0.3 zustand: specifier: ^4.5.5 version: 4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1) @@ -161,7 +164,7 @@ importers: version: 1.3.5 '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.4.9) + version: 4.3.1(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) eslint: specifier: ^8.56.0 version: 8.57.0 @@ -182,19 +185,19 @@ importers: version: 5.4.9(@types/node@20.16.5)(less@4.2.0) vite-plugin-html-config: specifier: ^2.0.2 - version: 2.0.2(vite@5.4.9) + version: 2.0.2(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-imp: specifier: ^2.4.0 - version: 2.4.0(vite@5.4.9) + version: 2.4.0(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(vite@5.4.9) + version: 0.22.0(rollup@4.24.0)(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-progress: specifier: ^0.0.7 - version: 0.0.7(vite@5.4.9) + version: 0.0.7(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) vite-plugin-stylelint: specifier: ^5.3.1 - version: 5.3.1(postcss@8.4.47)(stylelint@15.11.0)(vite@5.4.9) + version: 5.3.1(postcss@8.4.47)(rollup@4.24.0)(stylelint@15.11.0(typescript@5.6.2))(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)) packages/locales: devDependencies: @@ -206,7 +209,7 @@ importers: version: link:../spec '@rollup/plugin-babel': specifier: ^6.0.3 - version: 6.0.4(@babel/core@7.25.2)(rollup@4.24.0) + version: 6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.24.0) '@rollup/plugin-dynamic-import-vars': specifier: ^2.0.5 version: 2.1.2(rollup@4.24.0) @@ -218,7 +221,7 @@ importers: version: 15.2.3(rollup@4.24.0) '@rollup/plugin-typescript': specifier: ^12.1.0 - version: 12.1.1(rollup@4.24.0)(typescript@5.6.2) + version: 12.1.1(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2) '@types/fs-extra': specifier: ^9.0.13 version: 9.0.13 @@ -297,7 +300,7 @@ importers: version: link:../spec '@rollup/plugin-babel': specifier: ^6.0.3 - version: 6.0.4(@babel/core@7.25.2)(rollup@4.24.0) + version: 6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.24.0) '@rollup/plugin-commonjs': specifier: ^28.0.1 version: 28.0.1(rollup@4.24.0) @@ -309,7 +312,7 @@ importers: version: 15.2.3(rollup@4.24.0) '@rollup/plugin-typescript': specifier: ^12.1.0 - version: 12.1.1(rollup@4.24.0)(typescript@5.6.2) + version: 12.1.1(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2) '@types/less': specifier: ^3.0.6 version: 3.0.6 @@ -333,13 +336,13 @@ importers: version: link:../locales '@mui/icons-material': specifier: ^6.0.2 - version: 6.0.2(@mui/material@6.1.0)(@types/react@18.3.5)(react@18.3.1) + version: 6.0.2(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/material': specifier: ^6.1.0 - version: 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) + version: 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@mui/x-date-pickers': specifier: ^7.18.0 - version: 7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1) + version: 7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ahooks: specifier: ^3.8.1 version: 3.8.1(react@18.3.1) @@ -384,13 +387,13 @@ importers: version: 2.11.1(react@18.3.1) react-resizable: specifier: ^3.0.5 - version: 3.0.5(react-dom@18.3.1)(react@18.3.1) + version: 3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-router: specifier: ^6.26.1 version: 6.26.1(react@18.3.1) react-router-dom: specifier: ^6.26.1 - version: 6.26.1(react-dom@18.3.1)(react@18.3.1) + version: 6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) validator: specifier: ^13.12.0 version: 13.12.0 @@ -439,13 +442,13 @@ importers: version: 1.10.4 '@typescript-eslint/eslint-plugin': specifier: ^8.4.0 - version: 8.5.0(@typescript-eslint/parser@8.5.0)(eslint@8.57.0)(typescript@5.6.2) + version: 8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2) '@typescript-eslint/parser': specifier: ^8.4.0 version: 8.5.0(eslint@8.57.0)(typescript@5.6.2) eslint-config-airbnb: specifier: ^19.0.4 - version: 19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0)(eslint-plugin-react-hooks@4.6.2)(eslint-plugin-react@7.35.2)(eslint@8.57.0) + version: 19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.35.2(eslint@8.57.0))(eslint@8.57.0) eslint-config-prettier: specifier: ^9.1.0 version: 9.1.0(eslint@8.57.0) @@ -454,19 +457,19 @@ importers: version: 1.1.2(eslint-plugin-import@2.30.0) eslint-import-resolver-typescript: specifier: ^3.6.3 - version: 3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0) + version: 3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0) eslint-plugin-check-file: specifier: ^2.8.0 version: 2.8.0(eslint@8.57.0) eslint-plugin-import: specifier: ^2.30.0 - version: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + version: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: specifier: ^6.10.0 version: 6.10.0(eslint@8.57.0) eslint-plugin-prettier: specifier: ^5.2.1 - version: 5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3) + version: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3) eslint-plugin-react: specifier: ^7.35.2 version: 7.35.2(eslint@8.57.0) @@ -493,16 +496,16 @@ importers: version: 15.11.0(typescript@5.6.2) stylelint-config-prettier: specifier: ^9.0.5 - version: 9.0.5(stylelint@15.11.0) + version: 9.0.5(stylelint@15.11.0(typescript@5.6.2)) stylelint-config-property-sort-order-smacss: specifier: ^10.0.0 - version: 10.0.0(stylelint@15.11.0) + version: 10.0.0(stylelint@15.11.0(typescript@5.6.2)) stylelint-config-standard: specifier: ^34.0.0 - version: 34.0.0(stylelint@15.11.0) + version: 34.0.0(stylelint@15.11.0(typescript@5.6.2)) stylelint-order: specifier: ^6.0.4 - version: 6.0.4(stylelint@15.11.0) + version: 6.0.4(stylelint@15.11.0(typescript@5.6.2)) devDependencies: eslint: specifier: ^8.56.0 @@ -4399,6 +4402,10 @@ packages: util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + uuid@11.0.3: + resolution: {integrity: sha512-d0z310fCWv5dJwnX1Y/MncBAqGMKEzlBb1AOf7z9K8ALnd0utBX/msg/fA0+sbyN1ihbMsLhrBlnl1ak7Wa0rg==} + hasBin: true + validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} @@ -4932,7 +4939,7 @@ snapshots: '@commitlint/types': 19.5.0 chalk: 5.3.0 cosmiconfig: 9.0.0(typescript@5.6.2) - cosmiconfig-typescript-loader: 5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0)(typescript@5.6.2) + cosmiconfig-typescript-loader: 5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -4989,7 +4996,7 @@ snapshots: '@csstools/css-tokenizer@2.4.1': {} - '@csstools/media-query-list-parser@2.1.13(@csstools/css-parser-algorithms@2.7.1)(@csstools/css-tokenizer@2.4.1)': + '@csstools/media-query-list-parser@2.1.13(@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1))(@csstools/css-tokenizer@2.4.1)': dependencies: '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-tokenizer': 2.4.1 @@ -5039,9 +5046,10 @@ snapshots: '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) '@emotion/utils': 1.4.0 '@emotion/weak-memoize': 0.4.0 - '@types/react': 18.3.5 hoist-non-react-statics: 3.3.2 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 transitivePeerDependencies: - supports-color @@ -5055,7 +5063,7 @@ snapshots: '@emotion/sheet@1.4.0': {} - '@emotion/styled@11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1)': + '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/babel-plugin': 11.12.0 @@ -5064,8 +5072,9 @@ snapshots: '@emotion/serialize': 1.3.1 '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) '@emotion/utils': 1.4.0 - '@types/react': 18.3.5 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 transitivePeerDependencies: - supports-color @@ -5242,24 +5251,22 @@ snapshots: '@mui/core-downloads-tracker@6.1.0': {} - '@mui/icons-material@6.0.2(@mui/material@6.1.0)(@types/react@18.3.5)(react@18.3.1)': + '@mui/icons-material@6.0.2(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) - '@types/react': 18.3.5 + '@mui/material': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 - '@mui/material@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1)': + '@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) '@mui/core-downloads-tracker': 6.1.0 - '@mui/system': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1) + '@mui/system': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/types': 7.2.16(@types/react@18.3.5) '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) '@popperjs/core': 2.11.8 - '@types/react': 18.3.5 '@types/react-transition-group': 4.4.11 clsx: 2.1.1 csstype: 3.1.3 @@ -5267,44 +5274,51 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) react-is: 18.3.1 - react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@types/react': 18.3.5 '@mui/private-theming@6.1.0(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@types/react': 18.3.5 prop-types: 15.8.1 react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 - '@mui/styled-engine@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1)': + '@mui/styled-engine@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@emotion/cache': 11.13.1 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) '@emotion/sheet': 1.4.0 - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) - '@mui/system@6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1)': + '@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) '@mui/private-theming': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@mui/styled-engine': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(react@18.3.1) + '@mui/styled-engine': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(react@18.3.1) '@mui/types': 7.2.16(@types/react@18.3.5) '@mui/utils': 6.1.0(@types/react@18.3.5)(react@18.3.1) - '@types/react': 18.3.5 clsx: 2.1.1 csstype: 3.1.3 prop-types: 15.8.1 react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + '@types/react': 18.3.5 '@mui/types@7.2.16(@types/react@18.3.5)': - dependencies: + optionalDependencies: '@types/react': 18.3.5 '@mui/utils@5.16.6(@types/react@18.3.5)(react@18.3.1)': @@ -5312,30 +5326,30 @@ snapshots: '@babel/runtime': 7.25.6 '@mui/types': 7.2.16(@types/react@18.3.5) '@types/prop-types': 15.7.12 - '@types/react': 18.3.5 clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 '@mui/utils@6.1.0(@types/react@18.3.5)(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 '@mui/types': 7.2.16(@types/react@18.3.5) '@types/prop-types': 15.7.12 - '@types/react': 18.3.5 clsx: 2.1.1 prop-types: 15.8.1 react: 18.3.1 react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.5 - '@mui/x-data-grid@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1)': + '@mui/x-data-grid@7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) - '@mui/system': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1) + '@mui/material': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) '@mui/x-internals': 7.18.0(@types/react@18.3.5)(react@18.3.1) clsx: 2.1.1 @@ -5343,26 +5357,30 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) reselect: 5.1.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) transitivePeerDependencies: - '@types/react' - '@mui/x-date-pickers@7.18.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@mui/material@6.1.0)(@mui/system@6.1.0)(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1)(react@18.3.1)': + '@mui/x-date-pickers@7.18.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@mui/material@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(date-fns@4.1.0)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@babel/runtime': 7.25.6 - '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) - '@emotion/styled': 11.13.0(@emotion/react@11.13.3)(@types/react@18.3.5)(react@18.3.1) - '@mui/material': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react-dom@18.3.1)(react@18.3.1) - '@mui/system': 6.1.0(@emotion/react@11.13.3)(@emotion/styled@11.13.0)(@types/react@18.3.5)(react@18.3.1) + '@mui/material': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.1.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) '@mui/utils': 5.16.6(@types/react@18.3.5)(react@18.3.1) '@mui/x-internals': 7.18.0(@types/react@18.3.5)(react@18.3.1) '@types/react-transition-group': 4.4.11 clsx: 2.1.1 - date-fns: 4.1.0 - dayjs: 1.11.13 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-transition-group: 4.4.5(react-dom@18.3.1)(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.5)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.5)(react@18.3.1))(@types/react@18.3.5)(react@18.3.1) + date-fns: 4.1.0 + dayjs: 1.11.13 transitivePeerDependencies: - '@types/react' @@ -5403,11 +5421,13 @@ snapshots: '@remix-run/router@1.19.1': {} - '@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(rollup@4.24.0)': + '@rollup/plugin-babel@6.0.4(@babel/core@7.25.2)(@types/babel__core@7.20.5)(rollup@4.24.0)': dependencies: '@babel/core': 7.25.2 '@babel/helper-module-imports': 7.24.7 '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + optionalDependencies: + '@types/babel__core': 7.20.5 rollup: 4.24.0 transitivePeerDependencies: - supports-color @@ -5421,6 +5441,7 @@ snapshots: is-reference: 1.2.1 magic-string: 0.30.11 picomatch: 4.0.2 + optionalDependencies: rollup: 4.24.0 '@rollup/plugin-dynamic-import-vars@2.1.2(rollup@4.24.0)': @@ -5430,17 +5451,21 @@ snapshots: estree-walker: 2.0.2 fast-glob: 3.3.2 magic-string: 0.30.11 + optionalDependencies: rollup: 4.24.0 - '@rollup/plugin-inject@5.0.5': + '@rollup/plugin-inject@5.0.5(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) estree-walker: 2.0.2 magic-string: 0.30.11 + optionalDependencies: + rollup: 4.24.0 '@rollup/plugin-json@6.1.0(rollup@4.24.0)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) + optionalDependencies: rollup: 4.24.0 '@rollup/plugin-node-resolve@15.2.3(rollup@4.24.0)': @@ -5451,20 +5476,24 @@ snapshots: is-builtin-module: 3.2.1 is-module: 1.0.0 resolve: 1.22.8 + optionalDependencies: rollup: 4.24.0 - '@rollup/plugin-typescript@12.1.1(rollup@4.24.0)(typescript@5.6.2)': + '@rollup/plugin-typescript@12.1.1(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2)': dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) resolve: 1.22.8 - rollup: 4.24.0 typescript: 5.6.2 + optionalDependencies: + rollup: 4.24.0 + tslib: 2.7.0 '@rollup/pluginutils@5.1.2(rollup@4.24.0)': dependencies: '@types/estree': 1.0.6 estree-walker: 2.0.2 picomatch: 2.3.1 + optionalDependencies: rollup: 4.24.0 '@rollup/rollup-android-arm-eabi@4.24.0': @@ -5625,7 +5654,7 @@ snapshots: '@types/validator@13.12.2': {} - '@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0)(eslint@8.57.0)(typescript@5.6.2)': + '@typescript-eslint/eslint-plugin@8.5.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint@8.57.0)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.11.0 '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) @@ -5638,6 +5667,7 @@ snapshots: ignore: 5.3.2 natural-compare: 1.4.0 ts-api-utils: 1.3.0(typescript@5.6.2) + optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: - supports-color @@ -5650,6 +5680,7 @@ snapshots: '@typescript-eslint/visitor-keys': 8.5.0 debug: 4.3.7 eslint: 8.57.0 + optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: - supports-color @@ -5665,6 +5696,7 @@ snapshots: '@typescript-eslint/utils': 8.5.0(eslint@8.57.0)(typescript@5.6.2) debug: 4.3.7 ts-api-utils: 1.3.0(typescript@5.6.2) + optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: - eslint @@ -5682,6 +5714,7 @@ snapshots: minimatch: 9.0.5 semver: 7.6.3 ts-api-utils: 1.3.0(typescript@5.6.2) + optionalDependencies: typescript: 5.6.2 transitivePeerDependencies: - supports-color @@ -5704,7 +5737,7 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.3.1(vite@5.4.9)': + '@vitejs/plugin-react@4.3.1(vite@5.4.9(@types/node@20.16.5)(less@4.2.0))': dependencies: '@babel/core': 7.25.2 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.25.2) @@ -6177,7 +6210,7 @@ snapshots: core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0)(typescript@5.6.2): + cosmiconfig-typescript-loader@5.0.0(@types/node@20.16.5)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2): dependencies: '@types/node': 20.16.5 cosmiconfig: 9.0.0(typescript@5.6.2) @@ -6198,6 +6231,7 @@ snapshots: js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 + optionalDependencies: typescript: 5.6.2 cosmiconfig@9.0.0(typescript@5.6.2): @@ -6206,6 +6240,7 @@ snapshots: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 + optionalDependencies: typescript: 5.6.2 create-ecdh@4.0.4: @@ -6627,16 +6662,16 @@ snapshots: dependencies: confusing-browser-globals: 1.0.11 eslint: 8.57.0 - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) object.assign: 4.1.5 object.entries: 1.1.8 semver: 6.3.1 - eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0)(eslint-plugin-react-hooks@4.6.2)(eslint-plugin-react@7.35.2)(eslint@8.57.0): + eslint-config-airbnb@19.0.4(eslint-plugin-import@2.30.0)(eslint-plugin-jsx-a11y@6.10.0(eslint@8.57.0))(eslint-plugin-react-hooks@4.6.2(eslint@8.57.0))(eslint-plugin-react@7.35.2(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 eslint-config-airbnb-base: 15.0.0(eslint-plugin-import@2.30.0)(eslint@8.57.0) - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.10.0(eslint@8.57.0) eslint-plugin-react: 7.35.2(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -6649,7 +6684,7 @@ snapshots: eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.30.0): dependencies: - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) eslint-import-resolver-node@0.3.9: dependencies: @@ -6659,31 +6694,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) - eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.8.0 is-bun-module: 1.2.1 is-glob: 4.0.3 + optionalDependencies: + eslint-plugin-import: 2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): + eslint-module-utils@2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.5.0)(eslint-plugin-import@2.30.0)(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-plugin-import@2.30.0)(eslint@8.57.0) transitivePeerDependencies: - supports-color @@ -6693,10 +6730,9 @@ snapshots: is-glob: 4.0.3 micromatch: 4.0.8 - eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): + eslint-plugin-import@2.30.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0): dependencies: '@rtsao/scc': 1.1.0 - '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -6705,7 +6741,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) + eslint-module-utils: 2.11.0(@typescript-eslint/parser@8.5.0(eslint@8.57.0)(typescript@5.6.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.1 is-glob: 4.0.3 @@ -6715,6 +6751,8 @@ snapshots: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.5.0(eslint@8.57.0)(typescript@5.6.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -6740,13 +6778,14 @@ snapshots: safe-regex-test: 1.0.3 string.prototype.includes: 2.0.0 - eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.3.3): + eslint-plugin-prettier@5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.0))(eslint@8.57.0)(prettier@3.3.3): dependencies: eslint: 8.57.0 - eslint-config-prettier: 9.1.0(eslint@8.57.0) prettier: 3.3.3 prettier-linter-helpers: 1.0.0 synckit: 0.9.1 + optionalDependencies: + eslint-config-prettier: 9.1.0(eslint@8.57.0) eslint-plugin-react-hooks@4.6.2(eslint@8.57.0): dependencies: @@ -6902,7 +6941,7 @@ snapshots: reusify: 1.0.4 fdir@6.4.2(picomatch@4.0.2): - dependencies: + optionalDependencies: picomatch: 4.0.2 figures@3.2.0: @@ -8165,12 +8204,13 @@ snapshots: dependencies: '@react-dnd/invariant': 4.0.2 '@react-dnd/shallowequal': 4.0.2 - '@types/node': 20.16.5 - '@types/react': 18.3.5 dnd-core: 16.0.1 fast-deep-equal: 3.1.3 hoist-non-react-statics: 3.3.2 react: 18.3.1 + optionalDependencies: + '@types/node': 20.16.5 + '@types/react': 18.3.5 react-dom@18.3.1(react@18.3.1): dependencies: @@ -8178,7 +8218,7 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 - react-draggable@4.4.6(react-dom@18.3.1)(react@18.3.1): + react-draggable@4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 1.2.1 prop-types: 15.8.1 @@ -8187,15 +8227,15 @@ snapshots: react-fast-compare@3.2.2: {} - react-grid-layout@1.5.0(react-dom@18.3.1)(react@18.3.1): + react-grid-layout@1.5.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: clsx: 2.1.1 fast-equals: 4.0.3 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) - react-resizable: 3.0.5(react-dom@18.3.1)(react@18.3.1) + react-draggable: 4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-resizable: 3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) resize-observer-polyfill: 1.5.1 react-hook-form@7.53.0(react@18.3.1): @@ -8218,15 +8258,15 @@ snapshots: react-refresh@0.14.2: {} - react-resizable@3.0.5(react-dom@18.3.1)(react@18.3.1): + react-resizable@3.0.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: prop-types: 15.8.1 react: 18.3.1 - react-draggable: 4.4.6(react-dom@18.3.1)(react@18.3.1) + react-draggable: 4.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - react-dom - react-router-dom@6.26.1(react-dom@18.3.1)(react@18.3.1): + react-router-dom@6.26.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@remix-run/router': 1.19.1 react: 18.3.1 @@ -8238,7 +8278,7 @@ snapshots: '@remix-run/router': 1.19.1 react: 18.3.1 - react-transition-group@4.4.5(react-dom@18.3.1)(react@18.3.1): + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.6 dom-helpers: 5.2.1 @@ -8665,26 +8705,26 @@ snapshots: style-search@0.1.0: {} - stylelint-config-prettier@9.0.5(stylelint@15.11.0): + stylelint-config-prettier@9.0.5(stylelint@15.11.0(typescript@5.6.2)): dependencies: stylelint: 15.11.0(typescript@5.6.2) - stylelint-config-property-sort-order-smacss@10.0.0(stylelint@15.11.0): + stylelint-config-property-sort-order-smacss@10.0.0(stylelint@15.11.0(typescript@5.6.2)): dependencies: css-property-sort-order-smacss: 2.2.0 stylelint: 15.11.0(typescript@5.6.2) - stylelint-order: 6.0.4(stylelint@15.11.0) + stylelint-order: 6.0.4(stylelint@15.11.0(typescript@5.6.2)) - stylelint-config-recommended@13.0.0(stylelint@15.11.0): + stylelint-config-recommended@13.0.0(stylelint@15.11.0(typescript@5.6.2)): dependencies: stylelint: 15.11.0(typescript@5.6.2) - stylelint-config-standard@34.0.0(stylelint@15.11.0): + stylelint-config-standard@34.0.0(stylelint@15.11.0(typescript@5.6.2)): dependencies: stylelint: 15.11.0(typescript@5.6.2) - stylelint-config-recommended: 13.0.0(stylelint@15.11.0) + stylelint-config-recommended: 13.0.0(stylelint@15.11.0(typescript@5.6.2)) - stylelint-order@6.0.4(stylelint@15.11.0): + stylelint-order@6.0.4(stylelint@15.11.0(typescript@5.6.2)): dependencies: postcss: 8.4.47 postcss-sorting: 8.0.2(postcss@8.4.47) @@ -8694,7 +8734,7 @@ snapshots: dependencies: '@csstools/css-parser-algorithms': 2.7.1(@csstools/css-tokenizer@2.4.1) '@csstools/css-tokenizer': 2.4.1 - '@csstools/media-query-list-parser': 2.1.13(@csstools/css-parser-algorithms@2.7.1)(@csstools/css-tokenizer@2.4.1) + '@csstools/media-query-list-parser': 2.1.13(@csstools/css-parser-algorithms@2.7.1(@csstools/css-tokenizer@2.4.1))(@csstools/css-tokenizer@2.4.1) '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) balanced-match: 2.0.0 colord: 2.9.3 @@ -8908,6 +8948,8 @@ snapshots: is-typed-array: 1.1.13 which-typed-array: 1.1.15 + uuid@11.0.3: {} + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 @@ -8915,11 +8957,11 @@ snapshots: validator@13.12.0: {} - vite-plugin-html-config@2.0.2(vite@5.4.9): + vite-plugin-html-config@2.0.2(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - vite-plugin-imp@2.4.0(vite@5.4.9): + vite-plugin-imp@2.4.0(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: '@babel/core': 7.25.2 '@babel/generator': 7.25.6 @@ -8932,41 +8974,43 @@ snapshots: transitivePeerDependencies: - supports-color - vite-plugin-node-polyfills@0.22.0(vite@5.4.9): + vite-plugin-node-polyfills@0.22.0(rollup@4.24.0)(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: - '@rollup/plugin-inject': 5.0.5 + '@rollup/plugin-inject': 5.0.5(rollup@4.24.0) node-stdlib-browser: 1.2.0 vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) transitivePeerDependencies: - rollup - vite-plugin-progress@0.0.7(vite@5.4.9): + vite-plugin-progress@0.0.7(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: picocolors: 1.1.0 progress: 2.0.3 rd: 2.0.1 vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) - vite-plugin-stylelint@5.3.1(postcss@8.4.47)(stylelint@15.11.0)(vite@5.4.9): + vite-plugin-stylelint@5.3.1(postcss@8.4.47)(rollup@4.24.0)(stylelint@15.11.0(typescript@5.6.2))(vite@5.4.9(@types/node@20.16.5)(less@4.2.0)): dependencies: '@rollup/pluginutils': 5.1.2(rollup@4.24.0) chokidar: 3.6.0 debug: 4.3.7 - postcss: 8.4.47 stylelint: 15.11.0(typescript@5.6.2) vite: 5.4.9(@types/node@20.16.5)(less@4.2.0) + optionalDependencies: + postcss: 8.4.47 + rollup: 4.24.0 transitivePeerDependencies: - supports-color vite@5.4.9(@types/node@20.16.5)(less@4.2.0): dependencies: - '@types/node': 20.16.5 esbuild: 0.23.1 - less: 4.2.0 postcss: 8.4.47 rollup: 4.24.0 optionalDependencies: + '@types/node': 20.16.5 fsevents: 2.3.3 + less: 4.2.0 vm-browserify@1.1.2: {} @@ -9087,7 +9131,8 @@ snapshots: zustand@4.5.5(@types/react@18.3.5)(immer@10.1.1)(react@18.3.1): dependencies: + use-sync-external-store: 1.2.2(react@18.3.1) + optionalDependencies: '@types/react': 18.3.5 immer: 10.1.1 react: 18.3.1 - use-sync-external-store: 1.2.2(react@18.3.1) From df09e135d195c3ac2586e5a588989b561fe58f66 Mon Sep 17 00:00:00 2001 From: wuzb Date: Thu, 19 Dec 2024 13:53:23 +0800 Subject: [PATCH 053/172] feat: workflow email notify node design phase 1 --- .../components/icon-entity-select/index.tsx | 10 +++ .../email-content/components/index.ts | 1 + .../components/email-content/index.tsx | 27 +++++++ .../email-content/style.module.less | 18 +++++ .../components/email-type-select/index.tsx | 75 +++++++++++++++++++ .../components/email-type-select/style.less | 3 + .../config-panel/components/index.ts | 2 + .../components/markdown-editor/index.tsx | 2 +- .../config-panel/hooks/useNodeFormItems.tsx | 31 +++++--- .../editor/components/config-panel/index.tsx | 29 +++++-- packages/locales/src/lang/en/global.json | 3 +- packages/locales/src/lang/en/workflow.json | 3 +- 12 files changed, 183 insertions(+), 21 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx new file mode 100644 index 00000000..fd105fff --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; + +/** + * Popover for entity selection and search based on icon + */ +const IconEntitySelect: React.FC = () => { + return
IconEntitySelect
; +}; + +export default IconEntitySelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts new file mode 100644 index 00000000..aebafde8 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts @@ -0,0 +1 @@ +export { default as IconEntitySelect } from './icon-entity-select'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx new file mode 100644 index 00000000..32ee7636 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import { useI18n } from '@milesight/shared/src/hooks'; +import { AddCircleIcon } from '@milesight/shared/src/components'; +import MarkdownEditor, { type MarkdownEditorProps } from '../markdown-editor'; + +import styles from './style.module.less'; + +/** + * Email Notify Node + * The Email Content Enter Component + */ +const EmailContent: React.FC = props => { + const { getIntlText } = useI18n(); + + return ( +
+
+
{getIntlText('common.label.content')}
+ +
+ +
+ ); +}; + +export default EmailContent; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less new file mode 100644 index 00000000..4d2c8326 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less @@ -0,0 +1,18 @@ +.email-content { + position: relative; + display: flex; + flex-direction: column; + border-radius: @border-radius-base; + + .header { + display: flex; + padding: @padding-xs @padding-sm; + background-color: var(--component-background-gray); + border-radius: @border-radius-base; + + .title { + flex: 1; + color: var(--text-color-secondary); + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx new file mode 100644 index 00000000..ceb49b61 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx @@ -0,0 +1,75 @@ +import React from 'react'; +import { useControllableValue } from 'ahooks'; + +import { Select, FormControl, InputLabel, MenuItem, type SelectProps } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { KeyboardArrowDownIcon } from '@milesight/shared/src/components'; + +import './style.less'; + +export enum EMAIL_TYPE { + SMTP = 'SMTP', + GMAIL = 'GMAIL', +} + +export const EmailTypeOptions = [ + { + label: 'SMTP', + value: EMAIL_TYPE.SMTP, + }, + { + label: 'Gmail', + value: EMAIL_TYPE.GMAIL, + }, +]; + +export type EmailTypeSelectProps = Omit< + SelectProps, + 'notched' | 'variant' | 'labelId' | 'IconComponent' | 'label' +>; + +/** + * Email Notify Node + * The Email Type Select Component + */ +const EmailTypeSelect: React.FC = props => { + const { required, disabled, value, onChange, ...restProps } = props; + + const { getIntlText } = useI18n(); + + const [state, setState] = useControllableValue( + { value: value || '', onChange }, + { + defaultValue: '', + }, + ); + + return ( +
+ + + {getIntlText('workflow.label.node_email_type')} + + + +
+ ); +}; + +export default EmailTypeSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less new file mode 100644 index 00000000..52fefb30 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less @@ -0,0 +1,3 @@ +.@{prefix}-workflow-email-notify-type { + margin-bottom: @margin-sm; +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 0d2c551a..2abe688b 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -16,3 +16,5 @@ export { type EntityFilterSelectValueType, } from './entity-filter-select'; export { default as ParamAssignInput, type ParamAssignInputValueType } from './param-assign-input'; +export { default as EmailTypeSelect, EMAIL_TYPE } from './email-type-select'; +export { default as EmailContent } from './email-content'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx index b74c7659..e63689b6 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/markdown-editor/index.tsx @@ -14,7 +14,7 @@ const MarkdownEditor: React.FC = ({ ...props }) => { {...props} multiline fullWidth - placeholder="Content" + placeholder="Please enter" rows={5} sx={{ margin: 0 }} /> diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index f37d8dac..310495a0 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -1,4 +1,6 @@ import { useMemo } from 'react'; +import { get } from 'lodash-es'; + import { type ControllerProps } from 'react-hook-form'; import { TextField } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; @@ -9,17 +11,24 @@ import { EntityAssignSelect, EntityFilterSelect, EntitySelect, - MarkdownEditor, ParamAssignInput, ParamInput, // ParamInputSelect, TimerInput, + EmailTypeSelect, + EMAIL_TYPE, + EmailContent, } from '../components'; type NodeFormGroupType = { groupName?: string; helperText?: string; - children?: ControllerProps[]; + children?: (ControllerProps & { + /** + * To Control whether the current component is rendered + */ + shouldRender?: (data: NodeFormDataProps) => boolean; + })[]; }; /** @@ -175,27 +184,25 @@ const useNodeFormItems = (node?: WorkflowNode) => { children: [ { name: 'emailType', + render({ field: { onChange, value } }) { return ( - + ); }, }, { name: 'emailApiKey', + shouldRender: data => { + return get(data, 'emailType') === EMAIL_TYPE.GMAIL; + }, render({ field: { onChange, value } }) { return ( @@ -212,9 +219,11 @@ const useNodeFormItems = (node?: WorkflowNode) => { render({ field: { onChange, value } }) { return ( @@ -224,7 +233,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { { name: 'content', render({ field: { onChange, value } }) { - return ; + return ; }, }, ], diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index d2561012..a6c60ffd 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -150,13 +150,28 @@ const ConfigPanel = () => {
{groupName}
)}
- {formItems?.map(props => ( - - {...props} - key={props.name} - control={control} - /> - ))} + {formItems?.map(props => { + const { shouldRender, ...restProps } = props; + + /** + * Whether render current form item + */ + if ( + shouldRender && + typeof shouldRender === 'function' && + !shouldRender(allFormData) + ) { + return null; + } + + return ( + + {...restProps} + key={restProps.name} + control={control} + /> + ); + })}
))} diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 4c85aec8..5b7fcfe5 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -162,5 +162,6 @@ "common.label.test": "Test", "common.label.run": "Run", "common.label.target": "Target", - "common.label.week": "Week" + "common.label.week": "Week", + "common.label.content": "Content" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 71aa7c14..8e85cdd0 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -54,5 +54,6 @@ "workflow.editor.form_param_timer_period_saturday": "Saturday", "workflow.editor.form_param_timer_period_sunday": "Sunday", "workflow.label.parallel_limit_tip": "并行分支限制为 {1} 个", - "workflow.label.entry_node_number_limit_tip": "工作流中必须且仅允许有 {1} 个入口节点" + "workflow.label.entry_node_number_limit_tip": "工作流中必须且仅允许有 {1} 个入口节点", + "workflow.label.node_email_type": "Email Type" } From da59b49f876bddbcd26bb8058e01c7d0c09c613c Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 19 Dec 2024 16:54:36 +0800 Subject: [PATCH 054/172] feat: add common editor header class --- apps/web/src/components/code-editor/constant.tsx | 3 +++ apps/web/src/components/code-editor/index.tsx | 2 ++ apps/web/src/components/index.ts | 1 + 3 files changed, 6 insertions(+) diff --git a/apps/web/src/components/code-editor/constant.tsx b/apps/web/src/components/code-editor/constant.tsx index 4871cab0..639ddc44 100644 --- a/apps/web/src/components/code-editor/constant.tsx +++ b/apps/web/src/components/code-editor/constant.tsx @@ -38,3 +38,6 @@ export const editorLangOptions: { label: 'Text', }, ]; + +/** Common editor header class name */ +export const COMMON_EDITOR_HEADER_CLASS = 'ms-code-editor-header'; diff --git a/apps/web/src/components/code-editor/index.tsx b/apps/web/src/components/code-editor/index.tsx index 51c4972c..d1849430 100644 --- a/apps/web/src/components/code-editor/index.tsx +++ b/apps/web/src/components/code-editor/index.tsx @@ -1,6 +1,8 @@ export { default as CodeEditor } from './editor'; export { default as CodeEditorToolbar } from './components/header'; export { default as CodeEditorSelect } from './components/lang-select'; + +export { COMMON_EDITOR_HEADER_CLASS } from './constant'; export type { EditorProps, EditorSupportLang, diff --git a/apps/web/src/components/index.ts b/apps/web/src/components/index.ts index 59ad05f1..c2a19aa4 100644 --- a/apps/web/src/components/index.ts +++ b/apps/web/src/components/index.ts @@ -12,6 +12,7 @@ export { CodeEditor, CodeEditorToolbar, CodeEditorSelect, + COMMON_EDITOR_HEADER_CLASS, type EditorProps, type EditorSupportLang, type EditorSelectProps, From 84b45c16fc65e9dbf221b81847a277f51c7ed487 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 19 Dec 2024 17:15:22 +0800 Subject: [PATCH 055/172] feat: Add usage documentation for the code editor component --- apps/web/src/components/code-editor/README.md | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 apps/web/src/components/code-editor/README.md diff --git a/apps/web/src/components/code-editor/README.md b/apps/web/src/components/code-editor/README.md new file mode 100644 index 00000000..33d81b6f --- /dev/null +++ b/apps/web/src/components/code-editor/README.md @@ -0,0 +1,105 @@ +## `CodeEditor` Component Usage + +### Default Usage +```jsx +const App = () => { + return ( + ; + ) +} +``` + +### Controlled Editor Language and Content +```jsx +const App = () => { + const [editorLang, setEditorLang] = useState('markdown'); + const [editorContent, setEditorContent] = useState('Hello World'); + + return ( + + ) +} +``` + +### Hide Certain Features +```jsx +const App = () => { + return ( + + ) +} +``` + +### Custom Header + +1. Simple adjustments, such as modifying only the title and icon +```jsx +import { CodeEditor, CodeEditorToolbar, type EditorToolbarProps } from '@/components'; +import { CheckCircleIcon } from '@milesight/shared/src/components'; + +const Header = (props: EditorToolbarProps) => { + return } title="xxxx" />; +}; +const App = () => { + return ( + + ) +} +``` + +2. Custom layout, but using some components like `CodeEditorSelect` +```jsx +import { CodeEditor, CodeEditorToolbar, type EditorToolbarProps } from '@/components'; + +const Header = (props: EditorToolbarProps) => { + return ( +
+ {/* Reuse the select component */} +

{item.label}

} /> +
Some styles you want
+
+ ); +}; +const App = () => { + return ( + + ) +} +``` + +3. Fully custom header component +```jsx +import { CodeEditor, COMMON_EDITOR_HEADER_CLASS, type EditorToolbarProps } from '@/components'; + +const Header = (props: EditorToolbarProps) => { + const { editorHandlers } = props; + const { redo, undo, insert } = editorHandlers || {}; + + return ( +
+
Content
+
+
undo
+
redo
+
insert('Value you want to insert')}>insert
+
+
+ ); +}; +const App = () => { + return ( + + ) +} +``` \ No newline at end of file From e76b749fb2bf1b7cd678e50dae084aa8e7676fd9 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 19 Dec 2024 17:58:21 +0800 Subject: [PATCH 056/172] feat: The select component in the editor is replaced with the public select component under shared --- .../components/lang-select/index.tsx | 19 ++++--------------- .../src/components/code-editor/constant.tsx | 18 +++++++++--------- .../web/src/components/code-editor/types.d.ts | 5 +++-- packages/shared/src/components/index.ts | 1 + .../shared/src/components/select/index.tsx | 10 +++++----- 5 files changed, 22 insertions(+), 31 deletions(-) diff --git a/apps/web/src/components/code-editor/components/lang-select/index.tsx b/apps/web/src/components/code-editor/components/lang-select/index.tsx index 09d25199..259fd3f2 100644 --- a/apps/web/src/components/code-editor/components/lang-select/index.tsx +++ b/apps/web/src/components/code-editor/components/lang-select/index.tsx @@ -1,7 +1,6 @@ import React, { useCallback } from 'react'; -import { MenuItem, Select } from '@mui/material'; import { type SelectInputProps } from '@mui/material/Select/SelectInput'; -import { ExpandMoreIcon } from '@milesight/shared/src/components'; +import { ExpandMoreIcon, Select } from '@milesight/shared/src/components'; import { editorLangOptions } from '../../constant'; import type { EditorSupportLang, EditorSelectProps } from '../../types'; import './style.less'; @@ -23,19 +22,9 @@ export default React.memo( value={editorLang} onChange={handleChange} IconComponent={ExpandMoreIcon} - > - {editorLangOptions.map(item => { - const { label, lang } = item || {}; - - return renderOptions ? ( - renderOptions(item) - ) : ( - - {label} - - ); - })} - + renderOptions={renderOptions} + options={editorLangOptions} + /> ); }, ); diff --git a/apps/web/src/components/code-editor/constant.tsx b/apps/web/src/components/code-editor/constant.tsx index 639ddc44..fd4efe8e 100644 --- a/apps/web/src/components/code-editor/constant.tsx +++ b/apps/web/src/components/code-editor/constant.tsx @@ -2,39 +2,39 @@ import type { EditorSupportLang } from './types'; /** Editor language options */ export const editorLangOptions: { - lang: EditorSupportLang; + value: EditorSupportLang; label: string; }[] = [ { - lang: 'javascript', + value: 'javascript', label: 'JavaScript', }, { - lang: 'python', + value: 'python', label: 'Python', }, { - lang: 'json', + value: 'json', label: 'JSON', }, { - lang: 'yaml', + value: 'yaml', label: 'YAML', }, { - lang: 'groovy', + value: 'groovy', label: 'Groovy', }, { - lang: 'mvel', + value: 'mvel', label: 'mvel', }, { - lang: 'markdown', + value: 'markdown', label: 'Markdown', }, { - lang: 'text', + value: 'text', label: 'Text', }, ]; diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts index 584b3aaa..0a18e01c 100644 --- a/apps/web/src/components/code-editor/types.d.ts +++ b/apps/web/src/components/code-editor/types.d.ts @@ -1,6 +1,7 @@ import React from 'react'; import type { ReactCodeMirrorProps, EditorView, EditorState } from '@uiw/react-codemirror'; import type { SelectChangeEvent } from '@mui/material'; +import { type SelectProps } from '@milesight/shared/src/components'; /** Supported languages for the code editor. */ export type EditorSupportLang = @@ -52,7 +53,7 @@ export interface EditorProps extends EditorContentProps { /** Interface for editor language options. */ export interface IEditorLangOption { /** The language type. */ - lang: EditorSupportLang; + value: EditorSupportLang; /** The label for the language. */ label: string; } @@ -72,7 +73,7 @@ export interface EditorSelectProps { * @param option - The language option. * @returns The rendered node. */ - renderOptions?: (option: IEditorLangOption) => React.ReactNode; + renderOptions?: SelectProps['renderOptions']; } /** Props for the code editor toolbar. */ diff --git a/packages/shared/src/components/index.ts b/packages/shared/src/components/index.ts index a51be7e9..babd2129 100644 --- a/packages/shared/src/components/index.ts +++ b/packages/shared/src/components/index.ts @@ -3,6 +3,7 @@ import { UseFormItemsProps } from './form/typings.d'; export { default as Logo } from './logo'; export { default as toast } from './toast'; export { default as Modal, type ModalProps } from './modal'; +export { default as Select, type SelectProps } from './select'; export { default as Form } from './form'; export * from './form/typings.d'; export type FormItemsType = UseFormItemsProps; diff --git a/packages/shared/src/components/select/index.tsx b/packages/shared/src/components/select/index.tsx index 73bcd896..c465f230 100644 --- a/packages/shared/src/components/select/index.tsx +++ b/packages/shared/src/components/select/index.tsx @@ -8,21 +8,21 @@ import { InputLabel, } from '@mui/material'; -type Props = { +type Props = { /** * 下拉选项 */ - options: OptionsProps[]; + options: OptionsProps[]; /** * 自定义下拉选项 * @returns 返回自定义下拉选项内容 */ - renderOptions?: (options: (OptionsProps & { description?: string })[]) => any[]; + renderOptions?: (options: (OptionsProps & { description?: string })[]) => any[]; }; -export type SelectProps = Props & MuiSelectProps; +export type SelectProps = Props & MuiSelectProps; -const Select = (props: SelectProps) => { +const Select = (props: SelectProps) => { const { options, renderOptions, style, label, ...rest } = props; // 转换下拉选项数据 From ff9f1dca6cbb5a05cf9c4296726cad12ccd7485f Mon Sep 17 00:00:00 2001 From: Lynn-Bing Date: Fri, 20 Dec 2024 11:18:33 +0800 Subject: [PATCH 057/172] feat: Optimize the logic and style of the custom entity addition dialog --- .../entity/components/add-modal/index.tsx | 23 ++-- .../entity/components/add-modal/useForm.tsx | 119 ++++++++++++++---- apps/web/src/pages/entity/style.less | 6 + packages/shared/src/components/form/index.tsx | 24 ++-- .../shared/src/components/form/style.less | 10 +- .../shared/src/components/form/typings.d.ts | 1 + 6 files changed, 135 insertions(+), 48 deletions(-) diff --git a/apps/web/src/pages/entity/components/add-modal/index.tsx b/apps/web/src/pages/entity/components/add-modal/index.tsx index 26eb3fa1..202a7ac5 100644 --- a/apps/web/src/pages/entity/components/add-modal/index.tsx +++ b/apps/web/src/pages/entity/components/add-modal/index.tsx @@ -43,21 +43,22 @@ const AddEntity = (props: IProps) => { // Form submission const handleSubmit = async (values: Record) => { const resultValues: Record = { value_attribute: {} }; - console.log(values); Object.keys(values).forEach((key: string) => { if (key.indexOf(`temp_${values.value_type}_key_`) > -1) { if (!resultValues.value_attribute.enum) { resultValues.value_attribute.enum = {}; } - const keyIndex = Number(key.replace(`temp_${values.value_type}_key_`, '')) || 1; - if (values.value_type === 'BOOLEAN') { - const resultKey = keyIndex === 1; - resultValues.value_attribute.enum[`${resultKey}`] = - values[key.replace('key', 'value')]; - } else { - resultValues.value_attribute.enum[values[key]] = - values[key.replace('key', 'value')]; + resultValues.value_attribute.enum[values[key]] = + values[key.replace('key', 'value')]; + } else if ( + key.indexOf(`temp_${values.value_type}_value_`) > -1 && + values.value_type === 'BOOLEAN' + ) { + if (!resultValues.value_attribute.enum) { + resultValues.value_attribute.enum = {}; } + const keyIndex = Number(key.replace(`temp_${values.value_type}_value_`, '')) || 1; + resultValues.value_attribute.enum[keyIndex === 1 ? 'true' : 'false'] = values[key]; } else if (['min', 'max', 'minLength', 'maxLength'].includes(key)) { resultValues.value_attribute[key] = values[key]; } else if (key.indexOf(`temp_${values.value_type}_value_`) === -1) { @@ -103,6 +104,10 @@ const AddEntity = (props: IProps) => { resultFormValues.minLength = data.entityValueAttribute.minLength; resultFormValues.maxLength = data.entityValueAttribute.maxLength; } + if (data?.entityKey) { + resultFormValues.identifier = + data.entityKey.split('.')[data.entityKey.split('.').length - 1]; + } setFormValues(resultFormValues); setDefaultValues(resultFormValues); } diff --git a/apps/web/src/pages/entity/components/add-modal/useForm.tsx b/apps/web/src/pages/entity/components/add-modal/useForm.tsx index 42b61b95..91f1283e 100644 --- a/apps/web/src/pages/entity/components/add-modal/useForm.tsx +++ b/apps/web/src/pages/entity/components/add-modal/useForm.tsx @@ -128,6 +128,7 @@ const useForm = (props: FormProps) => { { const addFormItem = () => { const newFormItems = [...formItemsRef.current]; const childrenLength = newFormItems?.[newFormItems.length - 1]?.children?.length || 0; + const length = (childrenLength - 1) / 2 + 1; const initAddFormItem: UseFormItemsProps[] = [ { label: getIntlText('common.label.key'), - name: `temp_key_${formValues.value_type}_${childrenLength}`, + name: `temp_${formValues.value_type}_key_${length}`, type: 'TextField', - defaultValue: - defaultValues?.[`temp_key_${formValues.value_type}_${childrenLength}`], + defaultValue: defaultValues?.[`temp_${formValues.value_type}_key_${length}`], rules: { required: true, }, @@ -161,10 +162,9 @@ const useForm = (props: FormProps) => { }, { label: getIntlText('common.label.value'), - name: `temp_value_${formValues.value_type}_${childrenLength}`, + name: `temp_${formValues.value_type}_value_${length}`, type: 'TextField', - defaultValue: - defaultValues?.[`temp_value_${formValues.value_type}_${childrenLength}`], + defaultValue: defaultValues?.[`temp_${formValues.value_type}_value_${length}`], rules: { required: true, }, @@ -183,7 +183,17 @@ const useForm = (props: FormProps) => { const addButton = () => { return ( - ); @@ -227,7 +237,7 @@ const useForm = (props: FormProps) => { }, ]; } - return [ + const childrenForms: any[] = [ { label: getIntlText('entity.label.entity_items'), name: '', @@ -237,7 +247,7 @@ const useForm = (props: FormProps) => { label: getIntlText('common.label.key'), name: `temp_${formValues.value_type}_key_1`, type: 'TextField', - defaultValue: defaultValues?.[`${formValues.value_type}_key_1`], + defaultValue: defaultValues?.[`temp_${formValues.value_type}_key_1`], rules: { required: true, }, @@ -249,7 +259,7 @@ const useForm = (props: FormProps) => { label: getIntlText('common.label.value'), name: `temp_${formValues.value_type}_value_1`, type: 'TextField', - defaultValue: defaultValues?.[`${formValues.value_type}_value_1`], + defaultValue: defaultValues?.[`temp_${formValues.value_type}_value_1`], rules: { required: true, }, @@ -257,17 +267,53 @@ const useForm = (props: FormProps) => { disabled: !!defaultValues, }, }, - { - label: '', - name: '', - type: '', - customRender: () => { - return addButton(); - }, - }, ], }, ]; + if (!defaultValues) { + childrenForms[0].children.push({ + label: '', + name: '', + type: '', + customRender: () => { + return addButton(); + }, + }); + } else if (defaultValues && data?.entityValueAttribute?.enum) { + const curEnum = data?.entityValueAttribute?.enum; + Object.keys(curEnum).forEach((key: string, index: number) => { + if (index > 0) { + const curForm = [ + { + label: getIntlText('common.label.key'), + name: `temp_${formValues.value_type}_key_${index + 1}`, + type: 'TextField', + defaultValue: key, + rules: { + required: true, + }, + props: { + disabled: !!defaultValues, + }, + }, + { + label: getIntlText('common.label.value'), + name: `temp_${formValues.value_type}_value_${index + 1}`, + type: 'TextField', + defaultValue: curEnum[key], + rules: { + required: true, + }, + props: { + disabled: !!defaultValues, + }, + }, + ]; + childrenForms[0].children.push(...curForm); + } + }); + } + return childrenForms; }; const getFormItems = (type: string): any[] => { @@ -325,16 +371,25 @@ const useForm = (props: FormProps) => { label: getIntlText('common.label.key'), name: `temp_${formValues.value_type}_key_1`, type: 'TextField', - defaultValue: - defaultValues?.[`temp_${formValues.value_type}_key_1`], - props: { - disabled: true, + col: 2, + // defaultValue: + // defaultValues?.[`temp_${formValues.value_type}_key_1`] || true, + // props: { + // disabled: true, + // }, + customRender: () => { + return ( +
+ True +
+ ); }, }, { label: getIntlText('common.label.label'), name: `temp_${formValues.value_type}_value_1`, type: 'TextField', + col: 10, defaultValue: defaultValues?.[`temp_${formValues.value_type}_value_1`], rules: { @@ -348,16 +403,25 @@ const useForm = (props: FormProps) => { label: getIntlText('common.label.key'), name: `temp_${formValues.value_type}_key_2`, type: 'TextField', - defaultValue: - defaultValues?.[`temp_${formValues.value_type}_key_2`], - props: { - disabled: true, + col: 2, + // defaultValue: + // defaultValues?.[`temp_${formValues.value_type}_key_2`] || false, + // props: { + // disabled: true, + // }, + customRender: () => { + return ( +
+ False +
+ ); }, }, { label: getIntlText('common.label.label'), name: `temp_${formValues.value_type}_value_2`, type: 'TextField', + col: 10, defaultValue: defaultValues?.[`temp_${formValues.value_type}_value_2`], rules: { @@ -379,6 +443,9 @@ const useForm = (props: FormProps) => { if (data && !defaultValues) { return; } + if (data?.entityValueAttribute?.enum) { + setMode('enum'); + } if (preFormValues.value_type !== formValues.value_type || !formValues.value_type) { const newFormItem = getFormItems(formValues.value_type); setFormItems(newFormItem); diff --git a/apps/web/src/pages/entity/style.less b/apps/web/src/pages/entity/style.less index eef4a7db..92d39316 100644 --- a/apps/web/src/pages/entity/style.less +++ b/apps/web/src/pages/entity/style.less @@ -64,6 +64,12 @@ margin-bottom: @margin-xl; } + &-add-modal-form-boolean-label { + display: flex; + align-items: center; + height: 40px; + } + &-main { margin: @margin-lg; background: var(--main-background); diff --git a/packages/shared/src/components/form/index.tsx b/packages/shared/src/components/form/index.tsx index fd9e71a0..0baabafb 100644 --- a/packages/shared/src/components/form/index.tsx +++ b/packages/shared/src/components/form/index.tsx @@ -108,22 +108,22 @@ const Forms = (props: formProps, ref: any) => { return (
{item?.label ?
{item?.label}
: null} - + {item?.children?.map((subItem: FormItemsProps) => { - if (subItem.customRender) { - return subItem.customRender(); - } - // const length = (item?.children?.length || 2) - 1; - // const size: number = 6; + const size: number = subItem.col || (subItem.customRender ? 12 : 6); return ( - +
- - key={subItem.name} - {...subItem} - control={control} - /> + {subItem.customRender ? ( + subItem.customRender() + ) : ( + + key={subItem.name} + {...subItem} + control={control} + /> + )}
); diff --git a/packages/shared/src/components/form/style.less b/packages/shared/src/components/form/style.less index 7c5ea6ce..23009e57 100644 --- a/packages/shared/src/components/form/style.less +++ b/packages/shared/src/components/form/style.less @@ -5,11 +5,15 @@ .MuiFormHelperText-root { margin-left: 0; } + + .MuiFormControl-root.MuiTextField-root { + margin: 0; + } } &-box { position: relative; - padding: @padding-md @padding-sm @padding-sm; + padding: @padding-md @padding-sm @padding-md; margin-bottom: @margin-md; border: 1px solid rgba(0,0,0, 0.23); border-radius: @border-radius-base; @@ -29,6 +33,10 @@ margin: 0; } } + + &-contain { + margin-bottom: -@margin-xl; + } } &-item { diff --git a/packages/shared/src/components/form/typings.d.ts b/packages/shared/src/components/form/typings.d.ts index 4b122665..d4f1c31e 100644 --- a/packages/shared/src/components/form/typings.d.ts +++ b/packages/shared/src/components/form/typings.d.ts @@ -39,6 +39,7 @@ export interface FormItemsProps { label?: string; defaultValue?: any; children?: FormItemsProps[]; // 一行多个子表单 + col?: number; // 布局列数 } export type UseFormItemsType = Omit; From 2eccd60a9733b8702e6ec107a1f461a0349fa2ca Mon Sep 17 00:00:00 2001 From: wuzb Date: Fri, 20 Dec 2024 13:19:54 +0800 Subject: [PATCH 058/172] feat: workflow email notify email config component design --- .../email-send-source/components/index.ts | 1 + .../components/smtp-form/index.tsx | 168 ++++++++++++++++++ .../components/smtp-form/style.module.less | 7 + .../components/email-send-source/index.tsx | 130 ++++++++++++++ .../components/email-send-source/style.less | 7 + .../components/email-type-select/index.tsx | 75 -------- .../components/email-type-select/style.less | 3 - .../config-panel/components/index.ts | 2 +- .../config-panel/hooks/useNodeFormItems.tsx | 29 +-- packages/locales/src/lang/en/workflow.json | 10 +- packages/shared/src/components/index.ts | 1 + .../shared/src/components/select/index.tsx | 9 +- 12 files changed, 335 insertions(+), 107 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/index.ts create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/style.module.less create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/style.less delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/index.ts new file mode 100644 index 00000000..af9a7425 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/index.ts @@ -0,0 +1 @@ +export { default as SmtpForm, defaultSmtpValue, type SmtpProps } from './smtp-form'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/index.tsx new file mode 100644 index 00000000..56d3c6f2 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/index.tsx @@ -0,0 +1,168 @@ +import React, { useMemo } from 'react'; +import { useControllableValue, useMemoizedFn } from 'ahooks'; +import { get } from 'lodash-es'; + +import { useI18n } from '@milesight/shared/src/hooks'; +import { TextField } from '@mui/material'; +import { KeyboardArrowDownIcon, MuiSelect } from '@milesight/shared/src/components'; + +import styles from './style.module.less'; + +export enum SmtpEncryptionMethod { + NONE = 'NONE', + STARTTLS = 'STARTTLS', +} + +export interface SmtpProps { + host?: string; + port?: string; + username?: string; + password?: string; + /** + * encryption method + */ + encryption?: SmtpEncryptionMethod; +} + +export interface SmtpFormProps { + value?: SmtpProps; + onChange: (val: SmtpProps) => void; +} + +export interface ItemProps { + name: keyof SmtpProps; + label: string; + type: 'TextField' | 'Select'; +} + +export const defaultSmtpValue: SmtpProps = { + host: '', + port: '587', + username: '', + password: '', + encryption: undefined, +}; + +/** + * email SMTP type form items + */ +const SmtpForm: React.FC = props => { + const { value, onChange } = props; + + const { getIntlText } = useI18n(); + + const [state, setState] = useControllableValue({ + value: value || defaultSmtpValue, + onChange, + }); + + const smtpEncryptionOptions = useMemo( + () => [ + { + value: SmtpEncryptionMethod.NONE, + label: getIntlText('workflow.email.label_smtp_config_encryption_none'), + }, + { + value: SmtpEncryptionMethod.STARTTLS, + label: getIntlText('workflow.email.label_smtp_config_encryption_start_tls'), + }, + ], + [getIntlText], + ); + + const setValue = useMemoizedFn((name: keyof SmtpProps, value: any) => { + setState({ + ...state, + [name]: value, + }); + }); + + const getValue = useMemoizedFn((name: keyof SmtpProps) => { + return get(state, name, get(defaultSmtpValue, name, '')); + }); + + const renderComponent = (item: ItemProps) => { + const { name, label, type } = item; + + switch (type) { + case 'TextField': + return ( + { + setValue(name, e.target.value); + }} + /> + ); + case 'Select': + return ( + { + setValue(name, e.target.value); + }} + /> + ); + default: + return null; + } + }; + + const formItems = useMemo((): ItemProps[] => { + return [ + { + label: getIntlText('workflow.email.label_smtp_config_service_host'), + name: 'host', + type: 'TextField', + }, + { + label: getIntlText('workflow.email.label_smtp_config_service_port'), + name: 'port', + type: 'TextField', + }, + { + label: getIntlText('workflow.email.label_smtp_config_username'), + name: 'username', + type: 'TextField', + }, + { + label: getIntlText('workflow.email.label_smtp_config_password'), + name: 'password', + type: 'TextField', + }, + { + label: getIntlText('workflow.email.label_smtp_config_encryption_method'), + name: 'encryption', + type: 'Select', + }, + ]; + }, [getIntlText]); + + return ( +
+ {formItems.map(f => ( +
+ {renderComponent(f)} +
+ ))} +
+ ); +}; + +export default SmtpForm; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/style.module.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/style.module.less new file mode 100644 index 00000000..ad0fb444 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/components/smtp-form/style.module.less @@ -0,0 +1,7 @@ +.smtp-form { + position: relative; + + .item { + margin-bottom: @margin-xxs; + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx new file mode 100644 index 00000000..6924b5c9 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx @@ -0,0 +1,130 @@ +import React from 'react'; +import { useControllableValue } from 'ahooks'; + +import { TextField, type SelectProps } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { KeyboardArrowDownIcon, MuiSelect } from '@milesight/shared/src/components'; + +import { SmtpForm, defaultSmtpValue, type SmtpProps } from './components'; + +import './style.less'; + +export enum EMAIL_TYPE { + SMTP = 'SMTP', + GMAIL = 'GMAIL', +} + +export const EmailTypeOptions = [ + { + label: 'SMTP', + value: EMAIL_TYPE.SMTP, + }, + { + label: 'Gmail', + value: EMAIL_TYPE.GMAIL, + }, +]; + +export type EmailTypeSelectProps = Omit< + SelectProps, + 'notched' | 'variant' | 'labelId' | 'IconComponent' | 'label' +>; + +/** + * Email Notify Node + * The Email Sending Source Component + */ +const EmailSendSource: React.FC = props => { + const { required, disabled, value, onChange, ...restProps } = props; + + const { getIntlText } = useI18n(); + + const [state, setState] = useControllableValue<{ + provider?: EMAIL_TYPE; + gmailConfig?: { + apiKey: string; + }; + smtpConfig?: SmtpProps; + }>({ + value: value || {}, + onChange, + }); + + const renderEmailTypeItems = () => { + const { provider, gmailConfig, smtpConfig } = state || {}; + + switch (provider) { + case EMAIL_TYPE.SMTP: + return ( + { + setState({ + provider: EMAIL_TYPE.SMTP, + smtpConfig: s, + }); + }} + /> + ); + case EMAIL_TYPE.GMAIL: + return ( + { + setState({ + provider: EMAIL_TYPE.GMAIL, + gmailConfig: { + apiKey: e.target.value, + }, + }); + }} + /> + ); + default: + null; + } + }; + + return ( +
+
+ { + const provider = e.target.value as EMAIL_TYPE; + setState({ + ...(provider === EMAIL_TYPE.GMAIL + ? { + gmailConfig: { + apiKey: '', + }, + } + : { smtpConfig: defaultSmtpValue }), + provider, + }); + }} + {...restProps} + /> +
+ + {renderEmailTypeItems()} +
+ ); +}; + +export default EmailSendSource; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/style.less new file mode 100644 index 00000000..6c60bd32 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/style.less @@ -0,0 +1,7 @@ +.@{prefix}-workflow-email-config { + position: relative; + + &__type { + margin-bottom: @margin-md; + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx deleted file mode 100644 index ceb49b61..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/index.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import React from 'react'; -import { useControllableValue } from 'ahooks'; - -import { Select, FormControl, InputLabel, MenuItem, type SelectProps } from '@mui/material'; -import { useI18n } from '@milesight/shared/src/hooks'; -import { KeyboardArrowDownIcon } from '@milesight/shared/src/components'; - -import './style.less'; - -export enum EMAIL_TYPE { - SMTP = 'SMTP', - GMAIL = 'GMAIL', -} - -export const EmailTypeOptions = [ - { - label: 'SMTP', - value: EMAIL_TYPE.SMTP, - }, - { - label: 'Gmail', - value: EMAIL_TYPE.GMAIL, - }, -]; - -export type EmailTypeSelectProps = Omit< - SelectProps, - 'notched' | 'variant' | 'labelId' | 'IconComponent' | 'label' ->; - -/** - * Email Notify Node - * The Email Type Select Component - */ -const EmailTypeSelect: React.FC = props => { - const { required, disabled, value, onChange, ...restProps } = props; - - const { getIntlText } = useI18n(); - - const [state, setState] = useControllableValue( - { value: value || '', onChange }, - { - defaultValue: '', - }, - ); - - return ( -
- - - {getIntlText('workflow.label.node_email_type')} - - - -
- ); -}; - -export default EmailTypeSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less deleted file mode 100644 index 52fefb30..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-type-select/style.less +++ /dev/null @@ -1,3 +0,0 @@ -.@{prefix}-workflow-email-notify-type { - margin-bottom: @margin-sm; -} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index c04eb9aa..d4267fb9 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -16,6 +16,6 @@ export { type EntityFilterSelectValueType, } from './entity-filter-select'; export { default as ParamAssignInput, type ParamAssignInputValueType } from './param-assign-input'; -export { default as EmailTypeSelect, EMAIL_TYPE } from './email-type-select'; +export { default as EmailSendSource, EMAIL_TYPE } from './email-send-source'; export { default as EmailContent } from './email-content'; export { default as ServiceParamAssignInput } from './service-param-assign-input'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index caa0aa39..8d996748 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -1,5 +1,4 @@ import { useMemo, useState } from 'react'; -import { get } from 'lodash-es'; import { type ControllerProps } from 'react-hook-form'; import { TextField } from '@mui/material'; @@ -14,11 +13,10 @@ import { ParamAssignInput, // ParamInputSelect, TimerInput, - EmailTypeSelect, - EMAIL_TYPE, EmailContent, ParamInput, ServiceParamAssignInput, + EmailSendSource, } from '../components'; type NodeFormGroupType = { @@ -200,29 +198,10 @@ const useNodeFormItems = (node?: WorkflowNode) => { groupName: 'Email Sending Source', children: [ { - name: 'emailType', - + name: 'config', render({ field: { onChange, value } }) { return ( - - ); - }, - }, - { - name: 'emailApiKey', - shouldRender: data => { - return get(data, 'emailType') === EMAIL_TYPE.GMAIL; - }, - render({ field: { onChange, value } }) { - return ( - + ); }, }, @@ -232,7 +211,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { groupName: 'Email Content', children: [ { - name: 'emailRecipient', + name: 'recipient', render({ field: { onChange, value } }) { return ( any[]; + /** + * Form control props + */ + formControlProps?: MuiFormControlProps; }; export type SelectProps = Props & MuiSelectProps; const Select = (props: SelectProps) => { - const { options, renderOptions, style, label, ...rest } = props; + const { options, renderOptions, style, label, formControlProps, ...rest } = props; // 转换下拉选项数据 const getMenuItems = useMemo(() => { @@ -45,7 +50,7 @@ const Select = (props: SelectProps) => { }, [options]); return ( - + {!!label && ( {label} From f5446fbf60d88fd7c29243f17cfda12ad6ed313f Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 20 Dec 2024 13:46:04 +0800 Subject: [PATCH 059/172] feat: complete ifelse node content render --- .../components/conditions-input/index.tsx | 75 +++--- .../components/param-input-select/index.tsx | 43 ++-- .../components/param-select/index.tsx | 37 +-- .../config-panel/hooks/useNodeFormItems.tsx | 4 +- .../editor/components/config-panel/index.tsx | 10 +- .../views/editor/components/handle/style.less | 3 +- .../editor/components/ifelse-node/index.tsx | 242 +++++++++++++++--- .../editor/components/ifelse-node/style.less | 57 +++++ .../pages/workflow/views/editor/constants.ts | 50 +++- .../workflow/views/editor/demo-data.json | 34 +-- .../src/pages/workflow/views/editor/helper.ts | 4 +- .../views/editor/hooks/useWorkflow.ts | 64 +++++ .../pages/workflow/views/editor/style.less | 4 +- packages/locales/src/lang/en/workflow.json | 6 +- packages/shared/src/config/const.ts | 13 +- packages/shared/src/utils/tools.ts | 11 + packages/shared/types/workflow.d.ts | 94 ++++--- 17 files changed, 538 insertions(+), 213 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/ifelse-node/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 39ebf923..487ca586 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -22,10 +22,11 @@ import { KeyboardArrowDownIcon, } from '@milesight/shared/src/components'; import { genUuid } from '../../../../helper'; +import { logicOperatorMap, conditionOperatorMap } from '../../../../constants'; import ParamSelect from '../param-select'; import './style.less'; -export type ConditionsInputValueType = NonNullable; +export type ConditionsInputValueType = NonNullable['settings']; type ConditionBlockValueType = ConditionsInputValueType['when'][number]; @@ -37,42 +38,6 @@ export type ConditionsInputProps = { onChange?: (value: ConditionsInputValueType) => void; }; -const logicOperatorMap: Partial> = { - OR: { - labelIntlKey: 'workflow.label.logic_keyword_or', - }, - AND: { - labelIntlKey: 'workflow.label.logic_keyword_and', - }, -}; - -const conditionOperatorMap: Partial> = { - CONTAINS: { - labelIntlKey: 'workflow.label.condition_operator_contains', - }, - NOT_CONTAINS: { - labelIntlKey: 'workflow.label.condition_operator_not_contains', - }, - START_WITH: { - labelIntlKey: 'workflow.label.condition_operator_start_with', - }, - END_WITH: { - labelIntlKey: 'workflow.label.condition_operator_end_with', - }, - IS: { - labelIntlKey: 'workflow.label.condition_operator_is', - }, - IS_NOT: { - labelIntlKey: 'workflow.label.condition_operator_is_not', - }, - IS_EMPTY: { - labelIntlKey: 'workflow.label.condition_operator_is_empty', - }, - IS_NOT_EMPTY: { - labelIntlKey: 'workflow.label.condition_operator_is_not_empty', - }, -}; - const genConditionValue = (): ConditionValueType => { return { id: genUuid('subcondition') }; }; @@ -225,8 +190,40 @@ const ConditionsInput: React.FC = props => {
{expressionType === 'mvel' ? (
- - + + replaceCondition( + 0, + { + expressionValue: e.target.value, + }, + block, + blockIndex, + ) + } + /> + + replaceCondition( + 0, + { + expressionDescription: e.target.value, + }, + block, + blockIndex, + ) + } + />
) : (
= ({ ...props }) => { const { getIntlText } = useI18n(); - const { getUpstreamNodes } = useWorkflow(); + const { getUpstreamNodeParams } = useWorkflow(); const containerRef = useRef(null); const [data, setData] = useControllableValue(props); const [inputValue, setInputValue] = useState(''); @@ -77,14 +77,13 @@ const ParamInputSelect: React.FC = ({ const { width: containerWidth } = useSize(containerRef) || {}; const [options, renderedOptions] = useMemo(() => { - const incomeNodes = getUpstreamNodes(); - const result = incomeNodes.reduce((acc, node) => { - // TODO: get the correct nodes params - demoOutputs.forEach(output => { + const [nodeParams] = getUpstreamNodeParams(); + const result = nodeParams?.reduce((acc, param) => { + param.outputs?.forEach(output => { acc.push({ - nodeId: node.id, - nodeName: node.data?.name, - nodeType: node.type, + nodeId: param.nodeId, + nodeName: param.nodeName, + nodeType: param.nodeType, valueName: output.name, valueType: output.type, valueKey: output.key, @@ -94,27 +93,25 @@ const ParamInputSelect: React.FC = ({ }, [] as OptionItemType[]); // TODO: render Empty component when the options is empty - const renderedOptions = incomeNodes.reduce((acc, node) => { + const renderedOptions = nodeParams?.reduce((acc, node) => { acc.push( - - {node.type} + + {node.nodeType} , ); - // TODO: get the correct nodes params - demoOutputs.forEach(output => { + node.outputs.forEach(output => { acc.push( { setAnchorEl(null); - setData({ - ref: genRefParamKey(node.type!, node.id, output.key), - }); + setData({ ref: output.key }); }} >
@@ -128,7 +125,7 @@ const ParamInputSelect: React.FC = ({ }, [] as React.ReactNode[]); return [result, renderedOptions]; - }, [selectValue, getUpstreamNodes, setData]); + }, [selectValue, getUpstreamNodeParams, setData]); useLayoutEffect(() => { // Direct input value @@ -141,12 +138,10 @@ const ParamInputSelect: React.FC = ({ // Reference to an entity if (data?.ref) { setInputValue(''); - const option = options.find( - item => genRefParamKey(item.nodeType!, item.nodeId, item.valueKey!) === data.ref, - ); + const option = options?.find(item => item.valueKey === data.ref); setSelectValue(val => { - if (val && genRefParamKey(val.nodeType!, val.nodeId, val.valueKey!) === data.ref) { + if (val && val.valueKey === data.ref) { return val; } return option; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx index 210f4fd4..7894b08b 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -16,17 +16,6 @@ import './style.less'; type ParamSelectValueType = string; -type OptionItemType = { - nodeId: ApiKey; - nodeName?: string; - nodeType?: WorkflowNodeType; - outputs: { - name: string; - type: string; - key: string; - }[]; -}; - export type ParamSelectProps = SelectProps; /** @@ -36,31 +25,13 @@ export type ParamSelectProps = SelectProps; */ const ParamSelect: React.FC = ({ label, required, disabled, ...props }) => { const { getIntlText } = useI18n(); - const { getUpstreamNodes } = useWorkflow(); + const { getUpstreamNodeParams } = useWorkflow(); const renderOptions = useCallback(() => { - const incomeNodes = getUpstreamNodes(); - // TODO: get the correct nodes params - const data: OptionItemType[] = incomeNodes.map(node => ({ - nodeId: node.id, - nodeName: node.data?.name, - nodeType: node.type, - outputs: [ - { - name: 'output112123123123123123123123131231231', - type: 'string', - key: `${node.type}.${node.id}.1132e3123132`, - }, - { - name: 'output22', - type: 'number', - key: `${node.type}.${node.id}.11eyu3123132`, - }, - ], - })); + const [data] = getUpstreamNodeParams(); // TODO: render Empty component when the options is empty - return data.map(item => [ + return data?.map(item => [ {item.nodeType} , @@ -73,7 +44,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. )), ]); - }, [getUpstreamNodes]); + }, [getUpstreamNodeParams]); return (
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 2a548260..0462e46f 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -78,7 +78,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { { children: [ { - name: 'when', + name: 'settings', render({ field: { onChange, value } }) { return ; }, @@ -297,7 +297,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { return result; }, [serviceKey]); - return !node?.type ? [] : formConfigs[node.type] || []; + return !node?.type ? [] : formConfigs[node.type as WorkflowNodeType] || []; }; export default useNodeFormItems; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index d2561012..02625238 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -68,7 +68,8 @@ const ConfigPanel = () => { formDataInit.current = false; return; } - const nodeData = selectedNode.data; + const { name, remark, parameters } = selectedNode.data || {}; + const data: Record = { name, remark, ...parameters }; reset(); /** @@ -76,8 +77,8 @@ const ConfigPanel = () => { * ensure that the initial data is filled in after the rendering is complete. */ setTimeout(() => { - Object.keys(nodeData).forEach(key => { - setValue(key, nodeData[key]); + Object.keys(data).forEach(key => { + setValue(key, data[key]); }); formDataInit.current = true; }, 0); @@ -87,8 +88,9 @@ const ConfigPanel = () => { useDebounceEffect( () => { if (!openPanel || !formDataInit.current) return; + const { name, remark, ...formData } = latestFormData || {}; - updateNodeData(selectedNode.id, latestFormData || {}); + updateNodeData(selectedNode.id, { name, remark, parameters: formData }); }, [openPanel, selectedNode, latestFormData, updateNodeData], { wait: 300 }, diff --git a/apps/web/src/pages/workflow/views/editor/components/handle/style.less b/apps/web/src/pages/workflow/views/editor/components/handle/style.less index 03fdf0a1..2f35a312 100644 --- a/apps/web/src/pages/workflow/views/editor/components/handle/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/handle/style.less @@ -1,5 +1,6 @@ .@{prefix}-workflow { & &-handle { + z-index: 10; width: 2px; min-width: auto; height: 16px; @@ -33,7 +34,7 @@ position: absolute; top: -12px; left: 0; - z-index: 1; + z-index: 10; min-width: 145px; padding: @padding-xs @padding-sm; font-size: @font-size-base; diff --git a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx index 82d88673..0128e317 100644 --- a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx @@ -1,20 +1,135 @@ -import React from 'react'; -import { Position, type Node, type NodeProps } from '@xyflow/react'; +import React, { useMemo, useEffect } from 'react'; +import { Position, useReactFlow, useUpdateNodeInternals, type NodeProps } from '@xyflow/react'; +import { isString } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; +import { Tooltip } from '@/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; +import { logicOperatorMap, conditionOperatorMap, DEFAULT_NODE_HEIGHT } from '../../constants'; +import useWorkflow from '../../hooks/useWorkflow'; import Handle from '../handle'; import NodeContainer from '../node-container'; +import './style.less'; -export type IfElseNode = Node; +export type IfElseNode = WorkflowNode<'ifelse'>; + +const NODE_HEADER_HEIGHT = 12 + 24 + 8; + +const CONDITION_TITLE_HEIGHT = 22; +const CONDITION_HEIGHT = 28; +const CONDITION_GAP = 4; +const CONDITION_BLOCK_GAP = 8; const nodeConfig = basicNodeConfigs.ifelse; +const DEFAULT_IF_SOURCE_HANDLE_ID = '$temp:if'; +const DEFAULT_ELSE_SOURCE_HANDLE_ID = '$temp:else'; + +const calculateHandleTop = (blockIndex: number, preConditionCount: number) => { + return ( + NODE_HEADER_HEIGHT + + blockIndex * CONDITION_BLOCK_GAP + + (blockIndex + 0.5) * CONDITION_TITLE_HEIGHT + + preConditionCount * (CONDITION_HEIGHT + CONDITION_GAP) + ); +}; + /** * IFELSE Node */ const IfElseNode: React.FC> = props => { + const { id: nodeId, selected } = props; const { getIntlText } = useI18n(); - console.log(props); + + // ---------- Render Handles ---------- + const { when, otherwise } = props.data.parameters?.settings || {}; + const whenList = useMemo( + () => + when || + ([{ id: DEFAULT_IF_SOURCE_HANDLE_ID, conditions: [] }] as unknown as NonNullable< + typeof when + >), + [when], + ); + const otherwiseItem = useMemo( + () => otherwise || { id: DEFAULT_ELSE_SOURCE_HANDLE_ID }, + [otherwise], + ); + const handles = useMemo(() => { + const result = [ + , + ]; + + whenList.forEach((block, index) => { + const preConditionCount = whenList.slice(0, index).reduce((acc, item) => { + const count = item.expressionType === 'mvel' ? 1 : item.conditions?.length || 1; + return acc + count; + }, 0); + + result.push( + , + ); + }); + + const preConditionCount = whenList.reduce((acc, item) => { + const count = item.expressionType === 'mvel' ? 1 : item.conditions?.length || 1; + return acc + count; + }, 0); + result.push( + , + ); + return result; + }, [whenList.length, otherwiseItem.id]); + + // ---------- Update Edges ---------- + const { getUpstreamNodeParams } = useWorkflow(); + const { getNode, getEdges, setEdges, updateEdge } = useReactFlow(); + const updateNodeInternals = useUpdateNodeInternals(); + const [, nodeParams] = getUpstreamNodeParams(getNode(props.id)); + + // Replace the temp handle id to real id + useEffect(() => { + if (!when?.length || !otherwise) return; + const edges = [...getEdges()]; + const tempIfEdge = edges.find( + edge => edge.source === nodeId && edge.sourceHandle === DEFAULT_IF_SOURCE_HANDLE_ID, + ); + const tempElseEdge = edges.find( + edge => edge.source === nodeId && edge.sourceHandle === DEFAULT_ELSE_SOURCE_HANDLE_ID, + ); + + if (!tempIfEdge || !tempElseEdge) return; + + updateEdge(tempIfEdge.id, { + sourceHandle: `${when[0].id || DEFAULT_IF_SOURCE_HANDLE_ID}`, + }); + updateEdge(tempElseEdge.id, { + sourceHandle: `${otherwise.id || DEFAULT_ELSE_SOURCE_HANDLE_ID}`, + }); + updateNodeInternals(nodeId); + }, [nodeId, when, otherwise, getEdges, updateEdge, updateNodeInternals]); + + // Update handles + useEffect(() => { + if (selected) return; + updateNodeInternals(nodeId); + }, [nodeId, selected, when, updateNodeInternals]); return ( > = props => { icon={nodeConfig.icon} iconBgColor={nodeConfig.iconBgColor} nodeProps={props} - handles={[ - , - // TODO: 根据条件动态渲染多个操作柄 - , - , - , - ]} + handles={handles} > - {/* TODO: render conditions detail... */} - render cases... +
+ {whenList?.map((block, blockIndex) => ( +
+
+ {blockIndex === 0 + ? getIntlText('workflow.label.logic_keyword_if') + : getIntlText('workflow.label.logic_keyword_elseif')} +
+
+ {!block.conditions.length && ( +
+ + {getIntlText('workflow.editor.valid.condition_not_setup')} + +
+ )} + {block.conditions?.map((condition, index) => { + const { expressionValue, expressionDescription } = condition; + const param = isString(expressionValue) + ? undefined + : nodeParams?.find( + param => param.valueKey === expressionValue?.key, + ); + const operatorText = + isString(expressionValue) || !expressionValue?.operator + ? undefined + : getIntlText( + conditionOperatorMap[expressionValue.operator] + ?.labelIntlKey || '', + ); + const logicOperatorText = getIntlText( + logicOperatorMap[block.logicOperator]?.labelIntlKey || '', + ); + const isEmpty = isString(expressionValue) + ? !expressionValue || !expressionDescription + : !expressionValue?.key || + !expressionValue?.operator || + !expressionValue?.value; + + return ( +
+ {isEmpty ? ( + + {getIntlText( + 'workflow.editor.valid.condition_not_setup', + )} + + ) : isString(expressionValue) ? ( + + ) : ( + <> + + {operatorText} + + {index === block.conditions.length && ( + + {logicOperatorText} + + )} + + + )} + {index !== block.conditions.length - 1 && ( + + {logicOperatorText} + + )} +
+ ); + })} +
+
+ ))} +
+
+ {getIntlText('workflow.label.logic_keyword_else')} +
+
+
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/style.less b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/style.less new file mode 100644 index 00000000..7875947f --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/style.less @@ -0,0 +1,57 @@ +.@{prefix}-condition-block { + margin-bottom: @margin-xs; + + &:last-child { + margin-bottom: 0; + } + + &-title { + font-weight: @font-weight-bold; + text-align: right; + .text-size(@font-size-base); + } + + .@{prefix}-condition-item { + position: relative; + display: flex; + gap: @margin-xxs; + padding: @padding-xxs @padding-xs; + margin-top: @margin-xxs; + background-color: var(--component-background-gray); + border-radius: @border-radius-base; + .text-size(@font-size-sm); + + .name { + max-width: 45px; + color: var(--text-color-main); + } + + .value { + max-width: 45px; + } + + .description { + max-width: 100%; + } + + .operator { + white-space: nowrap; + } + + .logic-operator { + position: absolute; + right: 8px; + bottom: -12px; + z-index: 1; + padding: @padding-xxs; + line-height: 1; + background-color: var(--component-background); + border: 1px solid var(--border-color-base); + border-radius: @border-radius-base; + } + + .placeholder { + color: var(--text-color-secondary); + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts index 19266e14..e34846c1 100644 --- a/apps/web/src/pages/workflow/views/editor/constants.ts +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -36,17 +36,17 @@ export const DEFAULT_NODE_WIDTH = 240; /** * The default node height */ -export const DEFAULT_NODE_HEIGHT = 50; +export const DEFAULT_NODE_HEIGHT = 48; /** * Node X-axis spacing */ -export const NODE_SPACING_X = 50; +export const NODE_SPACING_X = 48; /** * Node Y-axis spacing */ -export const NODE_SPACING_Y = 50; +export const NODE_SPACING_Y = 48; /** * Global parameter reference prefix @@ -57,3 +57,47 @@ export const PARAM_REFERENCE_PREFIX = '@'; * Global parameter reference key divider */ export const PARAM_REFERENCE_DIVIDER = '.'; + +/** + * Logic operator map + */ +export const logicOperatorMap: Partial> = { + OR: { + labelIntlKey: 'workflow.label.logic_keyword_or', + }, + AND: { + labelIntlKey: 'workflow.label.logic_keyword_and', + }, +}; + +/** + * Condition operator map + */ +export const conditionOperatorMap: Partial< + Record +> = { + CONTAINS: { + labelIntlKey: 'workflow.label.condition_operator_contains', + }, + NOT_CONTAINS: { + labelIntlKey: 'workflow.label.condition_operator_not_contains', + }, + START_WITH: { + labelIntlKey: 'workflow.label.condition_operator_start_with', + }, + END_WITH: { + labelIntlKey: 'workflow.label.condition_operator_end_with', + }, + IS: { + labelIntlKey: 'workflow.label.condition_operator_is', + }, + IS_NOT: { + labelIntlKey: 'workflow.label.condition_operator_is_not', + }, + IS_EMPTY: { + labelIntlKey: 'workflow.label.condition_operator_is_empty', + }, + IS_NOT_EMPTY: { + labelIntlKey: 'workflow.label.condition_operator_is_not_empty', + }, +}; diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json index c9baa1c8..f898717d 100644 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -14,39 +14,7 @@ { "id": "2", "data": { - "name": "World", - "parameters": { - "when": [ - { - "id": "", - "logicOperator": "AND", - "conditions": [ - { - "expressionType": "mvel", - "expressionValue": "request.body['a.b.c'] != null" - } - ] - }, - { - "id": "", - "logicOperator": "OR", - "conditions": [ - { - "expressionType": "condition", - "expressionValue": { - "id": "", - "key": "", - "value": "", - "operator": "" - } - } - ] - } - ], - "otherwise": { - "id": "" - } - } + "name": "World" }, "position": { "x": 300, diff --git a/apps/web/src/pages/workflow/views/editor/helper.ts b/apps/web/src/pages/workflow/views/editor/helper.ts index 197b09fe..e35491f9 100644 --- a/apps/web/src/pages/workflow/views/editor/helper.ts +++ b/apps/web/src/pages/workflow/views/editor/helper.ts @@ -46,6 +46,6 @@ export const parseRefParamKey = (key: string) => { * Generate Workflow Node, Edge or Condition uuid, format as `{node}:{8-bit random string}:{timestamp}` * @param type node/edge */ -export const genUuid = (type: 'node' | 'edge' | 'condition' | 'subcondition') => { - return `${type}:${genRandomString(8, { lowerCase: true })}:${Date.now()}`; +export const genUuid = (type: 'node' | 'edge' | 'condition' | 'subcondition' | 'temp') => { + return `${type}_${genRandomString(8, { lowerCase: true })}`; }; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 03beeea9..6e78af72 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -14,6 +14,26 @@ import { basicNodeConfigs } from '@/pages/workflow/config'; import { PARALLEL_LIMIT, PARALLEL_DEPTH_LIMIT, ENTRY_NODE_NUMBER_LIMIT } from '../constants'; import { getParallelInfo } from './utils'; +export type NodeParamType = { + nodeId: ApiKey; + nodeName?: string; + nodeType?: WorkflowNodeType; + outputs: { + name: string; + type: string; + key: string; + }[]; +}; + +export type FlattenNodeParamType = { + nodeId: ApiKey; + nodeName?: string; + nodeType?: WorkflowNodeType; + valueName: string; + valueType: string; + valueKey: string; +}; + const entryNodeTypes = Object.values(basicNodeConfigs) .filter(item => item.category === 'entry') .map(item => item.type); @@ -183,6 +203,49 @@ const useWorkflow = () => { [nodes, edges, getSelectedNode], ); + const getUpstreamNodeParams = useCallback( + (currentNode?: WorkflowNode): [NodeParamType[], FlattenNodeParamType[]] | [] => { + currentNode = currentNode || getSelectedNode(); + if (!currentNode) return []; + + const incomeNodes = getUpstreamNodes(currentNode); + // TODO: get the correct nodes params + const result: NodeParamType[] = incomeNodes.map(node => ({ + nodeId: node.id, + nodeName: node.data?.name, + nodeType: node.type as WorkflowNodeType, + outputs: [ + { + name: 'output112123123123123123123123131231231', + type: 'string', + key: `${node.type}.${node.id}.1132e3123132`, + }, + { + name: 'output22', + type: 'number', + key: `${node.type}.${node.id}.11eyu3123132`, + }, + ], + })); + const flattenResult = result.reduce((acc, item) => { + acc.push( + ...item.outputs.map(output => ({ + nodeId: item.nodeId, + nodeName: item.nodeName, + nodeType: item.nodeType, + valueName: output.name, + valueType: output.type, + valueKey: output.key, + })), + ); + return acc; + }, [] as FlattenNodeParamType[]); + + return [result, flattenResult]; + }, + [getSelectedNode, getUpstreamNodes], + ); + // Check if there is a node that is not connected to an entry node const checkFreeNodeLimit = useCallback( (nodes?: WorkflowNode[]) => { @@ -222,6 +285,7 @@ const useWorkflow = () => { checkFreeNodeLimit, getSelectedNode, getUpstreamNodes, + getUpstreamNodeParams, }; }; diff --git a/apps/web/src/pages/workflow/views/editor/style.less b/apps/web/src/pages/workflow/views/editor/style.less index 6fa6d27f..98e78351 100644 --- a/apps/web/src/pages/workflow/views/editor/style.less +++ b/apps/web/src/pages/workflow/views/editor/style.less @@ -39,7 +39,7 @@ } &-node { - padding: @padding-sm; + padding: (@padding-sm - 1) @padding-sm; background-color: var(--component-background); border: 1px solid var(--component-background); border-radius: var(--xy-node-border-radius); @@ -93,7 +93,7 @@ } &-node-body { - margin-top: @margin-sm; + margin-top: @margin-xs; } } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 600b5933..4f8b7225 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -56,7 +56,8 @@ "workflow.label.parallel_limit_tip": "并行分支限制为 {1} 个", "workflow.label.entry_node_number_limit_tip": "工作流中必须且仅允许有 {1} 个入口节点", "workflow.label.logic_keyword_if": "IF", - "workflow.label.logic_keyword_elseif": "ELSEIF", + "workflow.label.logic_keyword_else": "ELSE", + "workflow.label.logic_keyword_elseif": "ELIF", "workflow.label.logic_keyword_and": "AND", "workflow.label.logic_keyword_or": "OR", "workflow.editor.form_button_add_condition": "Add Condition", @@ -70,5 +71,6 @@ "workflow.label.condition_operator_is_empty": "Is Empty", "workflow.label.condition_operator_is_not_empty": "Is Not Empty", "workflow.node.config_panel_lable_arguments": "Arguments", - "workflow.message.import_dsl_error": "The DSL file contains errors. Please update the file and try again." + "workflow.message.import_dsl_error": "The DSL file contains errors. Please update the file and try again.", + "workflow.editor.valid.condition_not_setup": "Condition not setup" } diff --git a/packages/shared/src/config/const.ts b/packages/shared/src/config/const.ts index f3bb2ac2..7aa94837 100644 --- a/packages/shared/src/config/const.ts +++ b/packages/shared/src/config/const.ts @@ -1,5 +1,14 @@ -/** url 中标识当前 tab 的 key 参数 */ +/** + * The key parameter in the url that identifies the current tab + */ export const URL_TAB_SEARCH_KEY = 'tab'; -/** 刷新token主题 */ +/** + * Refresh token event topic + */ export const REFRESH_TOKEN_TOPIC = 'iot:token:refresh'; + +/** + * Private property prefix + */ +export const PRIVATE_PROPERTY_PREFIX = '$'; diff --git a/packages/shared/src/utils/tools.ts b/packages/shared/src/utils/tools.ts index 2a58ece7..f7127d41 100644 --- a/packages/shared/src/utils/tools.ts +++ b/packages/shared/src/utils/tools.ts @@ -6,6 +6,7 @@ import { stringify } from 'qs'; import axios, { type Canceler } from 'axios'; import { camelCase, isPlainObject } from 'lodash-es'; +import { PRIVATE_PROPERTY_PREFIX } from '../config'; /** * 判断是否为本地 IP 地址 @@ -688,3 +689,13 @@ export const withPromiseResolvers = () => { return { promise, resolve: resolve!, reject: reject! }; }; + +/** + * Check if a key is a frontend private property + */ +export const checkPrivateProperty = (key?: string) => { + if (!key) return false; + const regx = new RegExp(`^\\${PRIVATE_PROPERTY_PREFIX}`); + + return regx.test(key); +}; diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index d7cf9971..a662afae 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -67,7 +67,7 @@ declare type WorkflowNodeStatus = 'error' | 'success' | 'loading'; /** * 节点基础数据类型(以 $ 开头的均为前端私有属性) */ -declare type BaseNodeDataType> = { +declare type BaseNodeDataType = Record> = { /** 名称 */ name: string; /** 描述 */ @@ -169,30 +169,32 @@ declare type WorkflowFilterOperator = * 注意:实际节点渲染时需默认增加一个 else 分支 */ declare type IfElseNodeDataType = BaseNodeDataType<{ - when: { - id: ApiKey; - logicOperator: WorkflowLogicOperator; - /** - * 表达式类型(默认 `condition`) - * @param mvel mvel 表达式 - * @param condition 条件表达式 - */ - expressionType: 'mvel' | 'condition'; - conditions: { + settings: { + when: { id: ApiKey; - expressionValue?: - | string - | { - key?: ApiKey; - operator?: WorkflowFilterOperator; - value?: string; - }; - /** 表达式备注 */ - expressionDescription?: string; + logicOperator: WorkflowLogicOperator; + /** + * 表达式类型(默认 `condition`) + * @param mvel mvel 表达式 + * @param condition 条件表达式 + */ + expressionType: 'mvel' | 'condition'; + conditions: { + id: ApiKey; + expressionValue?: + | string + | { + key?: ApiKey; + operator?: WorkflowFilterOperator; + value?: string; + }; + /** 表达式备注 */ + expressionDescription?: string; + }[]; }[]; - }[]; - otherwise: { - id: ApiKey; + otherwise: { + id: ApiKey; + }; }; }>; @@ -316,18 +318,40 @@ declare type WebhookNodeDataType = BaseNodeDataType & { /** * 工作流节点模型 */ -declare type WorkflowNode = - | ReactFlowNode, 'trigger'> - | ReactFlowNode, 'timer'> - | ReactFlowNode, 'listener'> - | ReactFlowNode, 'ifelse'> - // | ReactFlowNode - | ReactFlowNode, 'code'> - | ReactFlowNode, 'service'> - | ReactFlowNode, 'assigner'> - | ReactFlowNode, 'select'> - | ReactFlowNode, 'email'> - | ReactFlowNode, 'webhook'>; +// declare type WorkflowNode = +// | ReactFlowNode, 'trigger'> +// | ReactFlowNode, 'timer'> +// | ReactFlowNode, 'listener'> +// | ReactFlowNode, 'ifelse'> +// // | ReactFlowNode +// | ReactFlowNode, 'code'> +// | ReactFlowNode, 'service'> +// | ReactFlowNode, 'assigner'> +// | ReactFlowNode, 'select'> +// | ReactFlowNode, 'email'> +// | ReactFlowNode, 'webhook'> +// | ReactFlowNode, any>; +declare type WorkflowNode = T extends 'trigger' + ? ReactFlowNode, 'trigger'> + : T extends 'timer' + ? ReactFlowNode, 'timer'> + : T extends 'listener' + ? ReactFlowNode, 'listener'> + : T extends 'ifelse' + ? ReactFlowNode, 'ifelse'> + : T extends 'code' + ? ReactFlowNode, 'code'> + : T extends 'service' + ? ReactFlowNode, 'service'> + : T extends 'assigner' + ? ReactFlowNode, 'assigner'> + : T extends 'select' + ? ReactFlowNode, 'select'> + : T extends 'email' + ? ReactFlowNode, 'email'> + : T extends 'webhook' + ? ReactFlowNode, 'webhook'> + : ReactFlowNode>; /** * 工作流边模型 From 70be653baebba6b8c9188a989b8de625041b099a Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 20 Dec 2024 14:01:17 +0800 Subject: [PATCH 060/172] chore: Limiting pnpm versions --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ab733633..f2b15afd 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "lint-staged": "lint-staged", "test": "echo \"Error: no test specified\" && exit 1", "prepare": "husky", - "preinstall": "node --import tsx ./scripts/only-allow.ts pnpm", + "preinstall": "node --import tsx ./scripts/only-allow.ts pnpm@9", "postinstall": "pnpm run build:pkgs" }, "devDependencies": { From 3552d62ea97b9307d89540de08ed049a579e2b07 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 20 Dec 2024 15:14:20 +0800 Subject: [PATCH 061/172] fix: rerender ifelse node handles when data change --- .../components/param-select/index.tsx | 2 +- .../editor/components/ifelse-node/index.tsx | 57 ++++++++++++------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx index 7894b08b..95ecdb82 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-select/index.tsx @@ -32,7 +32,7 @@ const ParamSelect: React.FC = ({ label, required, disabled, .. // TODO: render Empty component when the options is empty return data?.map(item => [ - + {item.nodeType} , item.outputs.map(output => ( diff --git a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx index 0128e317..982ecdbf 100644 --- a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx @@ -54,6 +54,10 @@ const IfElseNode: React.FC> = props => { () => otherwise || { id: DEFAULT_ELSE_SOURCE_HANDLE_ID }, [otherwise], ); + const conditionCount = whenList.reduce((acc, item) => { + const count = item.expressionType === 'mvel' ? 1 : item.conditions?.length || 1; + return acc + count; + }, 0); const handles = useMemo(() => { const result = [ > = props => { />, ); return result; - }, [whenList.length, otherwiseItem.id]); + }, [whenList.length, conditionCount, otherwiseItem.id]); // ---------- Update Edges ---------- const { getUpstreamNodeParams } = useWorkflow(); - const { getNode, getEdges, setEdges, updateEdge } = useReactFlow(); + const { getNode, getEdges, setEdges } = useReactFlow(); const updateNodeInternals = useUpdateNodeInternals(); const [, nodeParams] = getUpstreamNodeParams(getNode(props.id)); - // Replace the temp handle id to real id + // Replace the temp handle id to real id; Remove the useless edges; useEffect(() => { + if (!selected) { + updateNodeInternals(nodeId); + return; + } + if (!when?.length || !otherwise) return; - const edges = [...getEdges()]; - const tempIfEdge = edges.find( + let edges = [...getEdges()]; + const handleIds = when + .map(item => item.id) + .concat(otherwise.id, DEFAULT_IF_SOURCE_HANDLE_ID, DEFAULT_ELSE_SOURCE_HANDLE_ID); + + edges = edges.filter(edge => { + if (edge.source !== nodeId) return true; + if (handleIds.includes(edge.sourceHandle!)) return true; + return false; + }); + const tempIfEdgeIndex = edges.findIndex( edge => edge.source === nodeId && edge.sourceHandle === DEFAULT_IF_SOURCE_HANDLE_ID, ); - const tempElseEdge = edges.find( + const tempElseEdgeIndex = edges.findIndex( edge => edge.source === nodeId && edge.sourceHandle === DEFAULT_ELSE_SOURCE_HANDLE_ID, ); - if (!tempIfEdge || !tempElseEdge) return; + if (tempIfEdgeIndex >= 0) { + edges[tempIfEdgeIndex] = { + ...edges[tempIfEdgeIndex], + sourceHandle: `${when[0].id || DEFAULT_IF_SOURCE_HANDLE_ID}`, + }; + } - updateEdge(tempIfEdge.id, { - sourceHandle: `${when[0].id || DEFAULT_IF_SOURCE_HANDLE_ID}`, - }); - updateEdge(tempElseEdge.id, { - sourceHandle: `${otherwise.id || DEFAULT_ELSE_SOURCE_HANDLE_ID}`, - }); - updateNodeInternals(nodeId); - }, [nodeId, when, otherwise, getEdges, updateEdge, updateNodeInternals]); + if (tempElseEdgeIndex >= 0) { + edges[tempElseEdgeIndex] = { + ...edges[tempElseEdgeIndex], + sourceHandle: `${otherwise.id || DEFAULT_ELSE_SOURCE_HANDLE_ID}`, + }; + } - // Update handles - useEffect(() => { - if (selected) return; + setEdges(edges); updateNodeInternals(nodeId); - }, [nodeId, selected, when, updateNodeInternals]); + }, [nodeId, when, otherwise, selected, getEdges, setEdges, updateNodeInternals]); return ( Date: Fri, 20 Dec 2024 15:42:39 +0800 Subject: [PATCH 062/172] feat: Add nested data generation logic for action log --- .../components/action-log/hooks/index.tsx | 1 + .../action-log/hooks/useNestedData.tsx | 218 ++++++++++++++++++ .../workflow/components/action-log/index.tsx | 87 +------ .../workflow/components/action-log/types.d.ts | 33 +++ 4 files changed, 258 insertions(+), 81 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/action-log/hooks/index.tsx create mode 100644 apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/index.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/index.tsx new file mode 100644 index 00000000..2e96e1bc --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/hooks/index.tsx @@ -0,0 +1 @@ +export { useNestedData } from './useNestedData'; diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx new file mode 100644 index 00000000..7d6ae1f4 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -0,0 +1,218 @@ +import { useMemo } from 'react'; +import { getIncomers, getOutgoers } from '@xyflow/react'; +import { cloneDeep } from 'lodash-es'; +import { useMemoizedFn } from 'ahooks'; +import { generateUUID, objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { basicNodeConfigs } from '../../../config'; +import type { + ActionLogProps, + ParallelNodeResult, + WorkflowDataType, + WorkflowNestDataType, + WorkflowNestNode, + WorkflowTraceType, +} from '../types'; + +const entryNodeConfigs = Object.values(basicNodeConfigs).filter(node => node.category === 'entry'); +export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { + /** Generate trace Map */ + const traceMap = useMemo(() => { + return (traceData || []).reduce( + (acc, cur) => { + const { nodeId } = objectToCamelCase(cur || {}); + acc[nodeId] = cur; + return acc; + }, + {} as Record, + ); + }, [traceData]); + + /** Add required attributes */ + const wrapperNode = useMemoizedFn((node: WorkflowNode): WorkflowNestNode => { + const nestNode = cloneDeep(node) as WorkflowNestNode; + const { id, type, data } = nestNode || {}; + const { name } = data || {}; + const { status, input, output, time_cost: timeCost } = traceMap[id] || {}; + + nestNode.attrs = { + $$token: generateUUID(), + name: name || '', + type: type!, + status: status === 'SUCCESS' ? 'success' : 'failed', + timeCost, + input, + output, + }; + + return nestNode; + }); + + /** Determine whether it is the root node */ + const isRootNode = useMemoizedFn((node: WorkflowNestNode) => { + const { type } = node || {}; + return entryNodeConfigs.some(config => config.type === type); + }); + + /** Wrap workflow data */ + const wrapperWorkflowData = useMemoizedFn( + (workflowData: WorkflowDataType): WorkflowNestDataType => { + const { nodes } = workflowData || {}; + const nestNodes = (nodes || []).map(node => wrapperNode(node)); + + return { + ...(workflowData || {}), + nodes: nestNodes, + }; + }, + ); + + const workflowNestData = useMemo( + () => wrapperWorkflowData(workflowData), + [workflowData, wrapperWorkflowData], + ); + + /** Convert flat data to tree */ + const dataToTree = useMemoizedFn( + (cb?: (node: WorkflowNestNode) => void): WorkflowNestNode[] => { + const { nodes, edges } = workflowNestData || {}; + + const root: WorkflowNestNode[] = []; + (nodes || []).forEach(node => { + /** Get root node */ + const isRoot = isRootNode(node); + isRoot && root.push(node); + + // Callback during traversal + cb?.(node); + + const outgoers = getOutgoers(node, nodes, edges) as WorkflowNestNode[]; + /** Generate nested structure data */ + if (outgoers.length) { + node.children = [...(node.children || []), ...outgoers]; + } + }); + + return root; + }, + ); + + // Tree node data + const roots = useMemo(() => dataToTree(), [dataToTree]); + + // Tree structure related mappings + const { treeDepthMap, treeParentMap } = useMemo(() => { + const treeDepthMap: Map = new Map(); + const treeParentMap: Map = new Map(); + + const dfs = (node: WorkflowNestNode, parent: WorkflowNestNode | null, depth: number) => { + const { $$token } = node.attrs || {}; + + treeDepthMap.set($$token, depth); + treeParentMap.set($$token, parent || null); + + if (node.children) { + node.children.forEach(child => dfs(child, node, depth + 1)); + } + }; + roots.forEach(root => dfs(root, null, 0)); + return { treeDepthMap, treeParentMap }; + }, [roots]); + + /** Get the depth of a node */ + const getDepthByNode = useMemoizedFn((node: WorkflowNestNode): number => { + const { attrs } = node || {}; + const { $$token } = attrs || {}; + + return treeDepthMap.get($$token) || 0; + }); + + // Get the upstream node of a given node + const getParentNodeInTree = useMemoizedFn((node: WorkflowNestNode) => { + const { attrs } = node; + const { $$token } = attrs; + + return treeParentMap.get($$token); + }); + + /** Get available upstream nodes */ + const getOnceIncomeNode = useMemoizedFn((node: WorkflowNestNode): ParallelNodeResult | null => { + const { nodes, edges } = workflowNestData || {}; + + // Get parent nodes + const incomers = getIncomers(node, nodes, edges) as WorkflowNestNode[]; + if (!incomers?.length) return null; + + // Check if parent nodes have only one outgoing node + const isOnceIncomeNode = incomers.every(incomer => { + const outgoers = getOutgoers(incomer, nodes, edges) as WorkflowNestNode[]; + // Only one outgoing node + return outgoers.length === 1; + }); + if (!isOnceIncomeNode) return null; + + const usableIncomes = incomers.sort((a, b) => getDepthByNode(a) - getDepthByNode(b)); + const [usableIncome] = usableIncomes || []; + + return { + node, + incomers, + usableIncome, + }; + }); + + /** Get nodes that need to be promoted to the same level */ + const getParallelNodeList = useMemoizedFn((): ParallelNodeResult[] => { + const { nodes } = workflowNestData || {}; + const parallelNodeList: ParallelNodeResult[] = []; + + (nodes || []).forEach(node => { + const result = getOnceIncomeNode(node); + if (!result) return; + + parallelNodeList.push(result); + }); + + return parallelNodeList; + }); + + /** Cancel references */ + const cancelQuote = useMemoizedFn((parallelNodeList: ParallelNodeResult[]) => { + const { nodes, edges } = workflowNestData || {}; + + (parallelNodeList || []).forEach(({ node }) => { + const incomers = getIncomers(node, nodes, edges) as WorkflowNestNode[]; + // Remove references + (incomers || []).forEach(incomer => { + incomer.children = (incomer.children || []).filter(v => v.id !== node.id); + }); + }); + }); + + /** Connect new references */ + const connectQuote = useMemoizedFn((parallelNodeList: ParallelNodeResult[]) => { + (parallelNodeList || []).forEach(result => { + const { node, usableIncome } = result || {}; + const parentNode = getParentNodeInTree(usableIncome); + + if (parentNode) { + parentNode.children = (parentNode.children || []).concat(node); + return; + } + + roots.push(node); + }); + }); + + /** Generate tree data */ + const treeData = useMemo(() => { + const parallelNodeList = getParallelNodeList(); + const taskQueue = [cancelQuote, connectQuote]; + taskQueue.forEach(handler => handler(parallelNodeList)); + + return roots; + }, [cancelQuote, connectQuote, getParallelNodeList, roots]); + + return { + treeData, + }; +}; diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index a49f5907..baae1f34 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,90 +1,15 @@ -import { useCallback, useMemo } from 'react'; import { Fragment } from 'react/jsx-runtime'; -import { getOutgoers } from '@xyflow/react'; -import { cloneDeep, isNil } from 'lodash-es'; +import { isNil } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; -import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; import { AccordionCard, AccordionHeader, AccordionTree } from './components'; -import { basicNodeConfigs } from '../../config'; +import { useNestedData } from './hooks'; import { ALPHABET_LIST } from './constant'; -import type { AccordionLog, WorkflowDataType, WorkflowTraceType } from './types'; +import type { ActionLogProps, LevelStructType, WorkflowNestNode } from './types'; import './style.less'; -interface IProps { - traceData: WorkflowTraceType[]; - workflowData: WorkflowDataType; -} -type WorkflowNestNode = WorkflowNode & { - attrs: AccordionLog; - children?: WorkflowNestNode[]; -}; -type LevelStructType = { - label: string; - value: number; - parentValue?: number; -}; -const entryNodeConfigs = Object.values(basicNodeConfigs).filter(node => node.category === 'entry'); -export default function AccordionUsage({ traceData, workflowData }: IProps) { +export default function AccordionUsage({ traceData, workflowData }: ActionLogProps) { const { getIntlText } = useI18n(); - - /** generate trace Map */ - const traceMap = useMemo(() => { - return (traceData || []).reduce( - (acc, cur) => { - const { nodeId } = objectToCamelCase(cur || {}); - acc[nodeId] = cur; - return acc; - }, - {} as Record, - ); - }, [traceData]); - /** Add required attributes */ - const wrapperNode = useCallback( - (node: WorkflowNode): WorkflowNestNode => { - const nestNode = cloneDeep(node) as WorkflowNestNode; - // TODO name - const { id, type, $$name } = nestNode || {}; - const { status, input, output, time_cost: timeCost } = traceMap[id] || {}; - - nestNode.attrs = { - name: $$name, - type: type!, - status: status === 'SUCCESS' ? 'success' : 'failed', - timeCost, - input, - output, - }; - - return nestNode; - }, - [traceMap], - ); - /** Determine whether it is the root node */ - const isRootNode = (node: WorkflowNestNode) => { - const { type } = node || {}; - return entryNodeConfigs.some(config => config.type === type); - }; - /** Generate Tree Data */ - const treeData = useMemo(() => { - // TODO Reconstructs the tree generation logic - const { nodes, edges } = workflowData || {}; - const nestNodes = (nodes || []).map(node => wrapperNode(node)); - - const root: WorkflowNestNode[] = []; - (nestNodes || []).forEach(node => { - /** get Root Node */ - const isRoot = isRootNode(node); - isRoot && root.push(node); - - const outgoers = getOutgoers(node, nestNodes, edges) as WorkflowNestNode[]; - /** Generating nested struct data */ - if (outgoers.length) { - node.children = [...(node.children || []), ...outgoers]; - } - }); - - return root; - }, [workflowData, wrapperNode]); + const { treeData } = useNestedData({ workflowData, traceData }); /** recursive rendering */ const renderAccordion = (data: WorkflowNestNode, levelStruct: LevelStructType) => { @@ -103,7 +28,7 @@ export default function AccordionUsage({ traceData, workflowData }: IProps) { {/* // TODO */}
input
- {children && ( + {!!children?.length && ( {children.map((child, ind) => { const nextLevel = levelValue + 1; diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index 7d9e5591..16b3d206 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -6,6 +6,10 @@ import type { LogStatus } from '../../config'; * Action Log Type */ export interface AccordionLog { + /** + * Unique ID of web usage + */ + $$token: string; /** * Node Type */ @@ -63,3 +67,32 @@ export type WorkflowTraceType = WorkflowAPISchema['getLogDetail']['response'][nu /** Workflow Data Type */ export type WorkflowDataType = ReactFlowJsonObject; + +/** Workflow Custom Node Type */ +export type WorkflowNestNode = WorkflowNode & { + attrs: AccordionLog; + children?: WorkflowNestNode[]; +}; +/** Workflow Nested Data Type */ +export interface WorkflowNestDataType extends WorkflowDataType { + nodes: WorkflowNestNode[]; +} + +/** Parallel Node Result */ +export interface ParallelNodeResult { + node: WorkflowNestNode; + incomers: WorkflowNestNode[]; + usableIncome: WorkflowNestNode; +} +/** level struct type */ +export type LevelStructType = { + label: string; + value: number; + parentValue?: number; +}; + +/** action log props */ +export interface ActionLogProps { + traceData: WorkflowTraceType[]; + workflowData: WorkflowDataType; +} From 661fa584516850f39f3c0c924618e63219a12347 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 20 Dec 2024 16:24:33 +0800 Subject: [PATCH 063/172] feat: add MoreMenu component for ConfigPanel --- .../components/conditions-input/index.tsx | 2 +- .../config-panel/components/index.ts | 1 + .../components/more-menu/index.tsx | 55 +++++++++++++++++++ .../components/more-menu/style.less | 31 +++++++++++ .../components/param-input-select/index.tsx | 21 ++----- .../editor/components/config-panel/index.tsx | 7 +++ packages/locales/src/lang/en/workflow.json | 3 +- .../shared/src/components/icons/index.tsx | 1 + 8 files changed, 102 insertions(+), 19 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 487ca586..81017f08 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -1,6 +1,6 @@ import React, { useLayoutEffect, useRef } from 'react'; import cls from 'classnames'; -import { isEqual, cloneDeep, merge } from 'lodash-es'; +import { isEqual, cloneDeep, merge, isString } from 'lodash-es'; import { useDynamicList, useControllableValue } from 'ahooks'; import { ToggleButtonGroup, diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 418e09ba..5c3e21bf 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -17,3 +17,4 @@ export { } from './entity-filter-select'; export { default as ParamAssignInput, type ParamAssignInputValueType } from './param-assign-input'; export { default as ServiceParamAssignInput } from './service-param-assign-input'; +export { default as MoreMenu } from './more-menu'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx new file mode 100644 index 00000000..907fb348 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx @@ -0,0 +1,55 @@ +import { useState } from 'react'; +import { IconButton, Popover } from '@mui/material'; +import { useI18n, useCopy } from '@milesight/shared/src/hooks'; +import { MoreHorizIcon, ContentCopyIcon } from '@milesight/shared/src/components'; +import { Tooltip } from '@/components'; +import useWorkflow from '../../../../hooks/useWorkflow'; +import './style.less'; + +const MoreMenu = () => { + const { getIntlText } = useI18n(); + const { handleCopy } = useCopy(); + const { getSelectedNode } = useWorkflow(); + const [anchorEl, setAnchorEl] = useState(null); + const selectedNode = getSelectedNode(); + + return ( +
+ setAnchorEl(e.currentTarget)}> + + + { + setAnchorEl(null); + }} + anchorOrigin={{ + vertical: 'bottom', + horizontal: 'right', + }} + transformOrigin={{ + vertical: 'top', + horizontal: 'right', + }} + > +
+
+
+ {getIntlText('workflow.label.node_id')} +
+
+ + handleCopy(selectedNode?.id || '')}> + + +
+
+
+
+
+ ); +}; + +export default MoreMenu; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/style.less new file mode 100644 index 00000000..e202264f --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/style.less @@ -0,0 +1,31 @@ +.@{prefix}-config-panel-more-menu { + &-popover { + .@{mui-prefix}Paper-root { + border-radius: @border-radius-sm; + } + + .node-info-root { + width: 240px; + padding: @padding-sm @padding-md @padding-md; + } + + .node-info-item { + &-title { + color: var(--text-color-secondary); + .text-size(@font-size-base); + } + + &-content { + display: flex; + gap: @margin-xs; + margin-top: @margin-xs; + .text-size(@font-size-lg); + + .@{prefix}-tooltip { + flex: 1; + width: 0; + } + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx index 9a34d11a..6c147baf 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx @@ -41,25 +41,10 @@ export interface ParamInputSelectProps { onChange?: (value: ParamInputSelectValueType) => void; } -const demoOutputs = [ - { - name: 'output11', - type: 'string', - key: '1132e3123132', - }, - { - name: 'output22', - type: 'number', - key: '11eyu3123132', - }, -]; - /** * Param Input Select Component * * Note: This is a basic component., use in CodeNode, ServiceNode, EntityAssignmentNode - * - * TODO: render nodes params */ const ParamInputSelect: React.FC = ({ label, @@ -160,9 +145,11 @@ const ParamInputSelect: React.FC = ({ autoComplete="off" label={label || getIntlText('common.label.value')} required={required} - // disabled={!!selectValue} placeholder={ - selectValue ? '' : getIntlText('workflow.editor.form_param_select_placeholder') + selectValue + ? '' + : placeholder || + getIntlText('workflow.editor.form_param_select_placeholder') } slotProps={{ input: { diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 02625238..ba55ba39 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -16,6 +16,7 @@ import { type NodeFormDataProps, } from './hooks'; import useConfigPanelStore from './store'; +import { MoreMenu } from './components'; import './style.less'; type FormDataProps = CommonFormDataProps & NodeFormDataProps; @@ -121,6 +122,12 @@ const ConfigPanel = () => { )} + + { if (!selectedNode) return; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 4f8b7225..10f4092d 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -72,5 +72,6 @@ "workflow.label.condition_operator_is_not_empty": "Is Not Empty", "workflow.node.config_panel_lable_arguments": "Arguments", "workflow.message.import_dsl_error": "The DSL file contains errors. Please update the file and try again.", - "workflow.editor.valid.condition_not_setup": "Condition not setup" + "workflow.editor.valid.condition_not_setup": "Condition not setup", + "workflow.label.node_id": "Node ID" } diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index d8311ba0..f9541cab 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -93,6 +93,7 @@ export { KeyboardArrowUp as KeyboardArrowUpIcon, KeyboardArrowDown as KeyboardArrowDownIcon, Sync as SyncIcon, + MoreHoriz as MoreHorizIcon, } from '@mui/icons-material'; export * from './iot-icons'; From de1aa85c0c74d3efc7b338d479d39b5698b9d526 Mon Sep 17 00:00:00 2001 From: wuzb Date: Fri, 20 Dec 2024 18:51:54 +0800 Subject: [PATCH 064/172] feat: workflow email notify node content component --- .../components/content-header/index.tsx | 27 +++++++ .../content-header/style.module.less | 7 ++ .../components/icon-entity-select/index.tsx | 10 --- .../email-content/components/index.ts | 3 +- .../components/previous-node-select/index.tsx | 61 +++++++++++++++ .../components/email-content/index.tsx | 32 +++++--- .../email-content/style.module.less | 16 +--- .../components/email-send-source/index.tsx | 35 ++++----- .../components/upstream-node-list/index.tsx | 78 +++++++++++++++++++ .../components/upstream-node-list/style.less | 31 ++++++++ .../config-panel/hooks/useNodeFormItems.tsx | 4 +- 11 files changed, 247 insertions(+), 57 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/style.module.less delete mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/previous-node-select/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx new file mode 100644 index 00000000..4ef4d18b --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx @@ -0,0 +1,27 @@ +import React from 'react'; + +import { useI18n } from '@milesight/shared/src/hooks'; +import { COMMON_EDITOR_HEADER_CLASS, type EditorToolbarProps } from '@/components'; + +import PreviousNodeSelect from '../previous-node-select'; + +import styles from './style.module.less'; + +/** + * content header component + */ +const ContentHeader: React.FC = props => { + const { editorHandlers } = props; + const { insert } = editorHandlers || {}; + + const { getIntlText } = useI18n(); + + return ( +
+
{getIntlText('common.label.content')}
+ insert(nodeKey)} /> +
+ ); +}; + +export default ContentHeader; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/style.module.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/style.module.less new file mode 100644 index 00000000..639345c2 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/style.module.less @@ -0,0 +1,7 @@ +.content-header { + position: relative; + + .text { + color: var(--text-color-secondary); + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx deleted file mode 100644 index fd105fff..00000000 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/icon-entity-select/index.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react'; - -/** - * Popover for entity selection and search based on icon - */ -const IconEntitySelect: React.FC = () => { - return
IconEntitySelect
; -}; - -export default IconEntitySelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts index aebafde8..cceb9fb2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/index.ts @@ -1 +1,2 @@ -export { default as IconEntitySelect } from './icon-entity-select'; +export { default as PreviousNodeSelect } from './previous-node-select'; +export { default as ContentHeader } from './content-header'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/previous-node-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/previous-node-select/index.tsx new file mode 100644 index 00000000..62b19de8 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/previous-node-select/index.tsx @@ -0,0 +1,61 @@ +import React, { useState, useMemo } from 'react'; +import { useMemoizedFn } from 'ahooks'; + +import { IconButton, Popover } from '@mui/material'; +import { AddCircleIcon } from '@milesight/shared/src/components'; +import UpstreamNodeList from '../../../upstream-node-list'; + +export interface PreviousNodeSelectProps { + onSelect: (nodeKey: string) => void; +} + +/** + * Popover for node selection and search based on icon + */ +const PreviousNodeSelect: React.FC = props => { + const [anchorEl, setAnchorEl] = useState(null); + + const handleClick = useMemoizedFn((event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }); + + const handleClose = useMemoizedFn(() => { + setAnchorEl(null); + }); + + const open = useMemo(() => Boolean(anchorEl), [anchorEl]); + const id = useMemo(() => { + return open ? 'email-content-node-select-popover' : undefined; + }, [open]); + + return ( +
+ + + + + { + setAnchorEl(null); + props?.onSelect?.(node.valueKey); + }} + /> + +
+ ); +}; + +export default PreviousNodeSelect; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx index 32ee7636..035df81e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx @@ -1,25 +1,37 @@ import React from 'react'; +import { useControllableValue } from 'ahooks'; -import { useI18n } from '@milesight/shared/src/hooks'; -import { AddCircleIcon } from '@milesight/shared/src/components'; -import MarkdownEditor, { type MarkdownEditorProps } from '../markdown-editor'; +import { CodeEditor } from '@/components'; + +import { ContentHeader } from './components'; import styles from './style.module.less'; +export interface EmailContentProps { + value?: string; + onChange: (value: string) => void; +} + /** * Email Notify Node * The Email Content Enter Component */ -const EmailContent: React.FC = props => { - const { getIntlText } = useI18n(); +const EmailContent: React.FC = props => { + const { value, onChange } = props; + + const [content, setContent] = useControllableValue({ + value: value || '', + onChange, + }); return (
-
-
{getIntlText('common.label.content')}
- -
- +
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less index 4d2c8326..37f71cda 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/style.module.less @@ -1,18 +1,4 @@ .email-content { position: relative; - display: flex; - flex-direction: column; - border-radius: @border-radius-base; - - .header { - display: flex; - padding: @padding-xs @padding-sm; - background-color: var(--component-background-gray); - border-radius: @border-radius-base; - - .title { - flex: 1; - color: var(--text-color-secondary); - } - } + height: 280px; } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx index 6924b5c9..e5ba95c3 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useControllableValue } from 'ahooks'; -import { TextField, type SelectProps } from '@mui/material'; +import { TextField } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { KeyboardArrowDownIcon, MuiSelect } from '@milesight/shared/src/components'; @@ -25,27 +25,29 @@ export const EmailTypeOptions = [ }, ]; -export type EmailTypeSelectProps = Omit< - SelectProps, - 'notched' | 'variant' | 'labelId' | 'IconComponent' | 'label' ->; +export interface EmailConfigProps { + provider?: EMAIL_TYPE; + gmailConfig?: { + apiKey: string; + }; + smtpConfig?: SmtpProps; +} + +export type EmailSendSourceProps = { + value?: EmailConfigProps; + onChange: (val: EmailConfigProps) => void; +}; /** * Email Notify Node * The Email Sending Source Component */ -const EmailSendSource: React.FC = props => { - const { required, disabled, value, onChange, ...restProps } = props; +const EmailSendSource: React.FC = props => { + const { value, onChange } = props; const { getIntlText } = useI18n(); - const [state, setState] = useControllableValue<{ - provider?: EMAIL_TYPE; - gmailConfig?: { - apiKey: string; - }; - smtpConfig?: SmtpProps; - }>({ + const [state, setState] = useControllableValue({ value: value || {}, onChange, }); @@ -95,15 +97,13 @@ const EmailSendSource: React.FC = props => { { const provider = e.target.value as EMAIL_TYPE; @@ -118,7 +118,6 @@ const EmailSendSource: React.FC = props => { provider, }); }} - {...restProps} />
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx new file mode 100644 index 00000000..6d9f02a1 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx @@ -0,0 +1,78 @@ +import React, { useMemo } from 'react'; +import { useControllableValue } from 'ahooks'; + +import { MenuList, MenuItem, ListSubheader } from '@mui/material'; +import { Tooltip } from '@/components'; +import useWorkflow, { + type FlattenNodeParamType, +} from '@/pages/workflow/views/editor/hooks/useWorkflow'; + +import './style.less'; + +export interface UpstreamNodeListProps { + value?: FlattenNodeParamType; + onChange: (value: FlattenNodeParamType) => void; +} + +/** + * Upstream node list + */ +const UpstreamNodeList: React.FC = props => { + const { getUpstreamNodeParams } = useWorkflow(); + const [nodeParams] = getUpstreamNodeParams(); + + const [state, setState] = useControllableValue(props); + + const upstreamNodes = useMemo(() => { + return nodeParams?.reduce((acc, param) => { + param.outputs?.forEach(output => { + acc.push({ + nodeId: param.nodeId, + nodeName: param.nodeName, + nodeType: param.nodeType, + valueName: output.name, + valueType: output.type, + valueKey: output.key, + }); + }); + return acc; + }, [] as FlattenNodeParamType[]); + }, [nodeParams]); + + const renderedUpstreamNodes = useMemo(() => { + return nodeParams?.reduce((acc, node) => { + acc.push( + + {node.nodeType} + , + ); + + node.outputs.forEach(output => { + acc.push( + { + const node = upstreamNodes?.find(r => r.valueKey === output.key); + if (node) setState(node); + }} + > +
+ + {output.type} +
+
, + ); + }); + return acc; + }, [] as React.ReactNode[]); + }, [state, nodeParams, upstreamNodes, setState]); + + /** + * TODO: render Empty component when the options is empty + */ + return {renderedUpstreamNodes}; +}; + +export default UpstreamNodeList; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less new file mode 100644 index 00000000..329220b2 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less @@ -0,0 +1,31 @@ +.@{prefix}-upstream-node-list { + position: relative; + + &-option-groupname.@{mui-prefix}ListSubheader-root { + .text-size(@font-size-sm); + + margin-top: @margin-xs; + } + + &-option.@{mui-prefix}MenuItem-root { + padding: @padding-xs @padding-md; + + .@{prefix}-upstream-node-list-item { + display: flex; + .text-size(@font-size-base); + + .name { + max-width: 200px; + } + + .type { + display: inline-block; + padding: 0 @padding-xxs; + margin-left: @margin-md; + color: var(--text-color-tertiary); + background-color: var(--component-background-gray); + border-radius: @border-radius-base; + } + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 6f6d8789..b0de33a3 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -200,9 +200,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { { name: 'config', render({ field: { onChange, value } }) { - return ( - - ); + return ; }, }, ], From c1a11fe27f421108a5f8d149a914033000ace63a Mon Sep 17 00:00:00 2001 From: bineawu Date: Sat, 21 Dec 2024 15:58:14 +0800 Subject: [PATCH 065/172] feat: workflow email notify node content enter update string --- .../components/content-header/index.tsx | 7 +++- .../components/upstream-node-list/index.tsx | 42 ++++++++----------- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx index 4ef4d18b..56b4eaa6 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/components/content-header/index.tsx @@ -7,6 +7,11 @@ import PreviousNodeSelect from '../previous-node-select'; import styles from './style.module.less'; +/** insert string prefix */ +const PREFIX = '${'; +/** insert string suffix */ +const SUFFIX = '}'; + /** * content header component */ @@ -19,7 +24,7 @@ const ContentHeader: React.FC = props => { return (
{getIntlText('common.label.content')}
- insert(nodeKey)} /> + insert(`${PREFIX}${nodeKey}${SUFFIX}`)} />
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx index 6d9f02a1..550e17c5 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx @@ -1,8 +1,10 @@ import React, { useMemo } from 'react'; import { useControllableValue } from 'ahooks'; +import { isEmpty } from 'lodash-es'; +import { useI18n } from '@milesight/shared/src/hooks'; import { MenuList, MenuItem, ListSubheader } from '@mui/material'; -import { Tooltip } from '@/components'; +import { Tooltip, Empty } from '@/components'; import useWorkflow, { type FlattenNodeParamType, } from '@/pages/workflow/views/editor/hooks/useWorkflow'; @@ -19,28 +21,13 @@ export interface UpstreamNodeListProps { */ const UpstreamNodeList: React.FC = props => { const { getUpstreamNodeParams } = useWorkflow(); - const [nodeParams] = getUpstreamNodeParams(); + const [upstreamNodes, flattenUpstreamNodes] = getUpstreamNodeParams(); + const { getIntlText } = useI18n(); const [state, setState] = useControllableValue(props); - const upstreamNodes = useMemo(() => { - return nodeParams?.reduce((acc, param) => { - param.outputs?.forEach(output => { - acc.push({ - nodeId: param.nodeId, - nodeName: param.nodeName, - nodeType: param.nodeType, - valueName: output.name, - valueType: output.type, - valueKey: output.key, - }); - }); - return acc; - }, [] as FlattenNodeParamType[]); - }, [nodeParams]); - const renderedUpstreamNodes = useMemo(() => { - return nodeParams?.reduce((acc, node) => { + return upstreamNodes?.reduce((acc, node) => { acc.push( {node.nodeType} @@ -54,7 +41,7 @@ const UpstreamNodeList: React.FC = props => { key={output.key} selected={node.nodeId === state?.nodeId && output.key === state?.valueKey} onClick={() => { - const node = upstreamNodes?.find(r => r.valueKey === output.key); + const node = flattenUpstreamNodes?.find(r => r.valueKey === output.key); if (node) setState(node); }} > @@ -67,12 +54,17 @@ const UpstreamNodeList: React.FC = props => { }); return acc; }, [] as React.ReactNode[]); - }, [state, nodeParams, upstreamNodes, setState]); + }, [state, upstreamNodes, flattenUpstreamNodes, setState]); + + const renderList = () => { + if (isEmpty(upstreamNodes) || isEmpty(flattenUpstreamNodes)) { + return ; + } + + return {renderedUpstreamNodes}; + }; - /** - * TODO: render Empty component when the options is empty - */ - return {renderedUpstreamNodes}; + return renderList(); }; export default UpstreamNodeList; From e0d90de47d0326c6df41a20636c7f19e0efe9bdb Mon Sep 17 00:00:00 2001 From: jimco Date: Sat, 21 Dec 2024 16:07:08 +0800 Subject: [PATCH 066/172] feat: add TestDrawer component for ConfigPanel --- .../web/src/components/code-editor/editor.tsx | 3 +- .../web/src/components/code-editor/types.d.ts | 3 + .../config-panel/components/index.ts | 1 + .../components/more-menu/index.tsx | 4 +- .../components/test-drawer/index.tsx | 59 +++++++++++++++++++ .../components/test-drawer/style.less | 50 ++++++++++++++++ .../editor/components/config-panel/index.tsx | 12 ++-- .../editor/components/config-panel/style.less | 1 + packages/locales/src/lang/en/global.json | 4 +- 9 files changed, 128 insertions(+), 9 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx create mode 100644 apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index 6b2d4324..73479434 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -7,7 +7,7 @@ import type { EditorSupportLang, EditorProps, EditorHandlers } from './types'; import './style.less'; export const CodeEditor = forwardRef((props, ref) => { - const { Header: CustomHeader, ...rest } = props; + const { title, Header: CustomHeader, ...rest } = props; const editorInstanceRef = useRef(null); const [editorLang, setEditorLang] = useControllableValue(props, { @@ -30,6 +30,7 @@ export const CodeEditor = forwardRef((props, ref) =
{CustomHeader !== null && ( { ); }; -export default MoreMenu; +export default memo(MoreMenu); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx new file mode 100644 index 00000000..4b82657c --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import cls from 'classnames'; +import { Backdrop, Slide, IconButton, Button, Divider, Alert } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { CloseIcon, PlayArrowIcon, CheckCircleIcon } from '@milesight/shared/src/components'; +import { CodeEditor } from '@/components'; +import './style.less'; + +export interface TestDrawerProps { + title?: string; + open: boolean; + onClose: () => void; +} + +const TestDrawer: React.FC = ({ title, open, onClose }) => { + const { getIntlText } = useI18n(); + + return ( +
+ + +
e.stopPropagation()}> +
+
+ {title || 'Test xxx Node'} +
+ + + +
+
+
+ + +
+ +
+ }> + Success + + +
+
+
+
+
+
+ ); +}; + +export default React.memo(TestDrawer); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less new file mode 100644 index 00000000..edb9c9a9 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less @@ -0,0 +1,50 @@ +.@{prefix}-config-panel-test-drawer-root { + position: absolute; + inset: 0; + z-index: -1; + overflow: hidden; + border-radius: @border-radius-sm; + + &.open { + z-index: 10; + } + + .@{mui-prefix}Backdrop-root { + position: absolute; + align-items: end; + } +} + +.@{prefix}-config-panel-test-drawer { + width: 100%; + height: 90%; + background-color: var(--component-background); + + &-header { + display: flex; + justify-content: space-between; + padding: @padding-lg @padding-xl; + } + + &-title { + font-weight: @font-weight-bold; + .text-size(@font-size-xxl); + } + + &-body { + padding: 0 @padding-xl @padding-xl; + + .@{prefix}-code-editor { + height: 200px; + margin-bottom: @margin-xl; + } + + .@{mui-prefix}Divider-root { + margin: @margin-xl 0; + } + + .@{mui-prefix}Alert-root { + margin: @margin-xl 0; + } + } +} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index ba55ba39..6dc2f602 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -1,4 +1,4 @@ -import { useMemo, useLayoutEffect, useEffect, useRef } from 'react'; +import { useMemo, useLayoutEffect, useEffect, useRef, useState } from 'react'; import { Panel, useReactFlow } from '@xyflow/react'; import cls from 'classnames'; import { isEqual } from 'lodash-es'; @@ -16,7 +16,7 @@ import { type NodeFormDataProps, } from './hooks'; import useConfigPanelStore from './store'; -import { MoreMenu } from './components'; +import { MoreMenu, TestDrawer } from './components'; import './style.less'; type FormDataProps = CommonFormDataProps & NodeFormDataProps; @@ -97,6 +97,9 @@ const ConfigPanel = () => { { wait: 300 }, ); + // ---------- Show Test Drawer ---------- + const [drawerOpen, setDrawerOpen] = useState(false); + return ( { {nodeConfig?.testable && ( - console.log('execute testing or popup test panel')} - > + setDrawerOpen(true)}> )} @@ -171,6 +172,7 @@ const ConfigPanel = () => { ))}
+ setDrawerOpen(false)} />
); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less index b694301b..69594772 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less @@ -11,6 +11,7 @@ } .@{prefix}-workflow-panel-config { + position: relative; display: flex; flex-direction: column; height: 100%; diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 4c85aec8..a5b7f2a4 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -162,5 +162,7 @@ "common.label.test": "Test", "common.label.run": "Run", "common.label.target": "Target", - "common.label.week": "Week" + "common.label.week": "Week", + "common.label.input": "Input", + "common.label.output": "Output" } From a3d851bef8ecc36aa664b8e0a58a8d420499e97f Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 23 Dec 2024 09:04:09 +0800 Subject: [PATCH 067/172] feat: Optimize editor style, add backend node name mapping --- .../code-editor/components/editor/style.less | 46 +++++++++++-------- apps/web/src/pages/workflow/config.tsx | 14 ++++++ .../workflow/views/editor/demo-data.json | 6 +-- .../views/editor/hooks/useInteractions.tsx | 3 ++ .../src/pages/workflow/views/editor/index.tsx | 37 +++++++++++++-- .../pages/workflow/views/editor/style.less | 9 ++-- packages/shared/types/workflow.d.ts | 17 +++---- 7 files changed, 95 insertions(+), 37 deletions(-) diff --git a/apps/web/src/components/code-editor/components/editor/style.less b/apps/web/src/components/code-editor/components/editor/style.less index b4e1ba1a..d17ed0c1 100644 --- a/apps/web/src/components/code-editor/components/editor/style.less +++ b/apps/web/src/components/code-editor/components/editor/style.less @@ -13,10 +13,12 @@ &:hover { .cm-gutterElement { span { - &::after { - color: var(--text-color-tertiary); - transition: color 0.2s ease-in-out; - } + opacity: 1; + transition: color 0.2s ease-in-out; + // &::after { + // color: var(--text-color-tertiary); + // transition: color 0.2s ease-in-out; + // } } } } @@ -25,31 +27,37 @@ .cm-foldGutter { .cm-gutterElement { position: relative; + width: 12px; span { color: transparent; + opacity: 0; - &::after { - position: absolute; - top: 50%; - left: 0; - font-size: 12px; - line-height: 1; - color: transparent; - transform: translateY(-50%); - } + // &::after { + // position: absolute; + // top: 50%; + // left: 0; + // font-size: 12px; + // line-height: 1; + // color: transparent; + // transform: translateY(-50%); + // } } span[title='Fold line'] { - &::after { - content: '∨'; - } + .triangle(6px, { top: var(--text-color-tertiary) }); + + border-width: 6px 5px 0; } span[title='Unfold line'] { - &::after { - content: '>'; - } + .triangle(6px, { left: var(--text-color-tertiary) }); + + margin-left: 3px; + border-width: 5px 0 5px 6px; + // &::after { + // content: '>'; + // } } } } diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index f9f940aa..56c6c930 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -43,6 +43,10 @@ export type NodeConfigItemType = { * Node Type */ type: WorkflowNodeType; + /** + * Backend component name + */ + componentName: string; /** * Label i18n key */ @@ -75,6 +79,7 @@ export type NodeConfigItemType = { export const basicNodeConfigs: Record = { trigger: { type: 'trigger', + componentName: 'trigger', labelIntlKey: 'workflow.label.trigger_node_name', descIntlKey: 'workflow.label.trigger_node_desc', icon: , @@ -83,6 +88,7 @@ export const basicNodeConfigs: Record = { }, timer: { type: 'timer', + componentName: 'simpleTimer', labelIntlKey: 'workflow.label.timer_node_name', descIntlKey: 'workflow.label.timer_node_desc', icon: , @@ -91,6 +97,7 @@ export const basicNodeConfigs: Record = { }, listener: { type: 'listener', + componentName: 'eventListener', labelIntlKey: 'workflow.label.listener_node_name', descIntlKey: 'workflow.label.listener_node_desc', icon: , @@ -99,6 +106,7 @@ export const basicNodeConfigs: Record = { }, ifelse: { type: 'ifelse', + componentName: 'choice', labelIntlKey: 'workflow.label.ifelse_node_name', icon: , iconBgColor: '#F57C00', @@ -114,6 +122,7 @@ export const basicNodeConfigs: Record = { // }, code: { type: 'code', + componentName: 'code', labelIntlKey: 'workflow.label.code_node_name', icon: , iconBgColor: '#26A69A', @@ -122,6 +131,7 @@ export const basicNodeConfigs: Record = { }, assigner: { type: 'assigner', + componentName: 'variableAssigner', labelIntlKey: 'workflow.label.assigner_node_name', icon: , iconBgColor: '#26A69A', @@ -129,6 +139,7 @@ export const basicNodeConfigs: Record = { }, service: { type: 'service', + componentName: 'serviceInvocation', labelIntlKey: 'workflow.label.service_node_name', icon: , iconBgColor: '#26A69A', @@ -137,6 +148,7 @@ export const basicNodeConfigs: Record = { }, select: { type: 'select', + componentName: 'entitySelector', labelIntlKey: 'workflow.label.select_node_name', icon: , iconBgColor: '#26A69A', @@ -145,6 +157,7 @@ export const basicNodeConfigs: Record = { }, email: { type: 'email', + componentName: 'email', labelIntlKey: 'workflow.label.email_node_name', icon: , iconBgColor: '#7E57C2', @@ -153,6 +166,7 @@ export const basicNodeConfigs: Record = { }, webhook: { type: 'webhook', + componentName: 'webhook', labelIntlKey: 'workflow.label.webhook_node_name', icon: , iconBgColor: '#7E57C2', diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json index f898717d..5b85e525 100644 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -95,21 +95,21 @@ "source": "2", "target": "3", "type": "addable", - "sourceHandle": "case-1" + "sourceHandle": "$temp:if" }, { "id": "2-4", "source": "2", "target": "4", "type": "addable", - "sourceHandle": "case-2" + "sourceHandle": "$temp:else" }, { "id": "2-5", "source": "2", "target": "5", "type": "addable", - "sourceHandle": "case-else" + "sourceHandle": "$temp:else" }, { "id": "3-6", diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index fd6800ff..ed42b9ec 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -8,6 +8,7 @@ import { } from '@xyflow/react'; import { useSize } from 'ahooks'; import { cloneDeep, maxBy } from 'lodash-es'; +import { basicNodeConfigs } from '@/pages/workflow/config'; import { genUuid } from '../helper'; import { NODE_SPACING_X, @@ -115,9 +116,11 @@ const useInteractions = () => { const edges = cloneDeep(getEdges()); const prevNode = nodes.find(node => node.id === prevNodeId); const nextNode = nodes.find(node => node.id === nextNodeId); + const nodeConfig = basicNodeConfigs[nodeType] || {}; const newNode: WorkflowNode = { id: genUuid('node'), type: nodeType, + componentName: nodeConfig.componentName, position: position || { x: 0, y: 0, diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 42f3db94..d5b061a9 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -1,6 +1,7 @@ import { memo, useState, useCallback } from 'react'; import { useSearchParams } from 'react-router-dom'; import { useRequest } from 'ahooks'; +import { omitBy } from 'lodash-es'; import { ReactFlow, Background, @@ -12,8 +13,10 @@ import { type NodeChange, } from '@xyflow/react'; import { Button } from '@mui/material'; +import { checkPrivateProperty } from '@milesight/shared/src/utils/tools'; import { useI18n, useTheme } from '@milesight/shared/src/hooks'; import { CheckIcon } from '@milesight/shared/src/components'; +import { CodeEditor } from '@/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { MIN_ZOOM, MAX_ZOOM } from './constants'; import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; @@ -138,17 +141,36 @@ const WorkflowEditor = () => { // ---------- Design Mode Change ---------- const [designMode, setDesignMode] = useState('canvas'); + const [editorFlowData, setEditorFlowData] = useState(); const handleDesignModeChange = useCallback( (mode: DesignMode) => { if (!checkWorkflowValid()) return; + if (mode === 'advanced') { + const { nodes, edges } = toObject(); + const newNodes = nodes.map(node => { + const data = omitBy(node, (_, key) => checkPrivateProperty(key)); + return data; + }); + setEditorFlowData(JSON.stringify({ nodes: newNodes, edges }, null, 2)); + } else if (mode === 'canvas') { + // TODO: json validate, data validate + const { nodes, edges } = JSON.parse(editorFlowData || '{}') as Pick< + WorkflowSchema, + 'nodes' | 'edges' + >; + + console.log(123123123, { editorFlowData, nodes }); + setNodes(nodes); + setEdges(edges); + } const data = toObject(); // TODO: check the nodes json data is valid console.log('workflow data', data); setDesignMode(mode); }, - [toObject, checkWorkflowValid], + [editorFlowData, toObject, setEdges, setNodes, checkWorkflowValid], ); // ---------- Save Workflow ---------- @@ -215,13 +237,22 @@ const WorkflowEditor = () => { {designMode === 'advanced' && (
-
{JSON.stringify(toObject(), null, 4)} -
+
*/} + { + console.log(value); + setEditorFlowData(value); + }} + />
)}
diff --git a/apps/web/src/pages/workflow/views/editor/style.less b/apps/web/src/pages/workflow/views/editor/style.less index 98e78351..ceb3a1ae 100644 --- a/apps/web/src/pages/workflow/views/editor/style.less +++ b/apps/web/src/pages/workflow/views/editor/style.less @@ -101,11 +101,12 @@ position: absolute; inset: 0; z-index: 1000; - padding: @padding-md; overflow: auto; - font-family: @font-family-code; - white-space: pre-wrap; background-color: var(--component-background); - .text-size(@font-size-sm); + + .@{prefix}-code-editor { + background-color: var(--component-background); + border: none; + } } } diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index a662afae..a80766ad 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -4,7 +4,10 @@ declare type ReactFlowNode< D extends Record = Record, T extends string = string, -> = import('@xyflow/react').Node; +> = import('@xyflow/react').Node & { + /** Backend Node Type */ + componentName: string; +}; /** * ReactFlow 边模型 @@ -68,17 +71,15 @@ declare type WorkflowNodeStatus = 'error' | 'success' | 'loading'; * 节点基础数据类型(以 $ 开头的均为前端私有属性) */ declare type BaseNodeDataType = Record> = { - /** 名称 */ + /** Node Name */ name: string; - /** 描述 */ + /** Node Remark */ remark?: string; - /** 后端组件 ID */ - componentId?: string; - /** 状态 */ + /** Status */ $status?: WorkflowNodeStatus; - /** 错误信息 */ + /** Error Message */ $errMsg?: React.ReactNode; - /** 流程参数 */ + /** Flow Parameters */ parameters?: T; }; From e8fc1cedbd078b3b3d573533710cc1c8f675cf2e Mon Sep 17 00:00:00 2001 From: wuzb Date: Mon, 23 Dec 2024 09:46:38 +0800 Subject: [PATCH 068/172] feat: workflow email notify content upstream node empty --- .../config-panel/components/upstream-node-list/index.tsx | 9 ++++++++- .../components/upstream-node-list/style.less | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx index 550e17c5..0af0debe 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx @@ -58,7 +58,14 @@ const UpstreamNodeList: React.FC = props => { const renderList = () => { if (isEmpty(upstreamNodes) || isEmpty(flattenUpstreamNodes)) { - return ; + return ( + + ); } return {renderedUpstreamNodes}; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less index 329220b2..207c1f6e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/style.less @@ -28,4 +28,11 @@ } } } + + &__empty.@{prefix}-empty-small { + .@{prefix}-empty-img { + width: 96px; + height: 96px; + } + } } From 19dc2a2c55a8806caa1713ecdc0f4f600194f8d6 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 23 Dec 2024 10:29:16 +0800 Subject: [PATCH 069/172] feat: modify workflow api service --- .../src/pages/workflow/hooks/useColumns.tsx | 6 +- .../components/test-drawer/style.less | 1 + apps/web/src/services/http/workflow.ts | 224 ++++++++++-------- 3 files changed, 126 insertions(+), 105 deletions(-) diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index 417ea05e..05d809d1 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -7,11 +7,13 @@ import { type WorkflowAPISchema } from '@/services/http'; type OperationType = 'detail' | 'delete' | 'edit'; -export type TableRowDataType = ObjectToCamelCase; +export type TableRowDataType = ObjectToCamelCase< + WorkflowAPISchema['getList']['response']['content'][0] +>; export interface UseColumnsProps { /** - * 操作 Button 点击回调 + * Button Click Callback */ onButtonClick: (type: OperationType, record: T) => void; } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less index edb9c9a9..890b0726 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less @@ -19,6 +19,7 @@ width: 100%; height: 90%; background-color: var(--component-background); + border-radius: @border-radius-sm @border-radius-sm 0 0; &-header { display: flex; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index fb73ae5a..8c648939 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -1,42 +1,55 @@ import { client, attachAPI, API_PREFIX } from './client'; -export type WorkflowStatus = 'enabled' | 'disabled'; +export type FlowStatus = 'enable' | 'disable'; + +export type FlowRunningStatus = 'Error' | 'Success'; + +export type FlowNodeTraceInfo = { + /** Node ID */ + node_id: ApiKey; + /** Node Name */ + node_label: string; + /** Running status */ + status: FlowRunningStatus; + /** Start Time */ + start_time: number; + /** Cost Time */ + time_cost: number; + /** Input (JSON string) */ + input?: string; + /** Output (JSON string) */ + output?: string; + /** Message ID */ + message_id?: string; + /** Error Message */ + error_message?: string; +}; export interface WorkflowAPISchema extends APISchema { /** Get workflow list */ getList: { request: { name?: string; - status?: WorkflowStatus; - }; - response: { - /** ID */ - id: ApiKey; - /** Name */ - name: string; - /** Reamrk */ - description: string; - /** Create Time */ - created_at: number; - /** Update Time */ - updated_at: number; - /** Enabled */ - enabled: boolean; - /** User Email */ - user_email: string; - }; - }; - - /** Add a workflow */ - addFlow: { - request: { - name: string; - remark?: string; - }; - response: { - /** ID */ - id: ApiKey; + status?: FlowStatus; }; + response: SearchResponseType< + { + /** ID */ + id: ApiKey; + /** Name */ + name: string; + /** Remark */ + remark?: string; + /** Enabled */ + enabled: boolean; + /** Create Time */ + created_at: number; + /** Update Time */ + updated_at: number; + /** User Nickname */ + user_nickname: string; + }[] + >; }; /** Update a workflow */ @@ -52,10 +65,10 @@ export interface WorkflowAPISchema extends APISchema { }; }; - /** Delete a workflow */ - deleteFlow: { + /** Delete workflow */ + deleteFlows: { request: { - id: ApiKey[]; + workflow_id_list: ApiKey[]; }; response: { /** ID */ @@ -89,7 +102,7 @@ export interface WorkflowAPISchema extends APISchema { enableFlow: { request: { id: ApiKey; - status: WorkflowStatus; + status: FlowStatus; }; response: { /** ID */ @@ -100,17 +113,20 @@ export interface WorkflowAPISchema extends APISchema { /** Get workflow log list */ getLogList: { request: void | { - // TODO: use workflow log status enum - status?: string; + status?: FlowRunningStatus; }; - response: { - /** ID */ - id: ApiKey; - /** Start Time */ - start_time: number; - /** Running status */ - status: string; - }[]; + response: SearchResponseType< + { + /** ID */ + id: ApiKey; + /** Start Time */ + start_time: number; + /** Run Time (ms) */ + time_cost: number; + /** Running status */ + status: FlowRunningStatus; + }[] + >; }; /** Get workflow log detail */ @@ -119,25 +135,15 @@ export interface WorkflowAPISchema extends APISchema { id: ApiKey; }; response: { - /** Node ID */ - node_id: ApiKey; - /** Node Name */ - node_name: string; - /** Running status */ - status: string; - /** Cost Time */ - cost: number; - // TODO - input: Record; - // TODO - output: Record; - }[]; + trace_info: FlowNodeTraceInfo[]; + }; }; /** Get workflow Design */ getFlowDesign: { request: { id: ApiKey; + version?: string; }; response: { /** ID */ @@ -146,23 +152,20 @@ export interface WorkflowAPISchema extends APISchema { name: string; /** Remark */ remark?: string; - /** Created At */ - created_at: number; - /** Updated At */ - updated_at: number; /** Enabled */ enabled: boolean; - /** User Email */ - user_email: string; - /** Workflow DSL */ - dsl?: string; + /** Flow Version */ + version?: string; + /** Design Data (JSON string) */ + design_data: string; }; }; /** Check workflow Design */ checkFlowDesign: { request: { - dsl: string; + /** Flow Data (JSON string) */ + design_data: string; }; response: { /** ID */ @@ -173,49 +176,54 @@ export interface WorkflowAPISchema extends APISchema { /** Save workflow Design */ saveFlowDesign: { request: { - id: ApiKey; - dsl: string; + /** Flow ID (Empty means to create) */ + id?: ApiKey; + /** Flow Version */ + version?: string; + /** Flow Name */ + name: string; + /** Flow Remark */ + remark?: string; + /** Flow Design Data (JSON string) */ + design_data: string; }; response: { /** ID */ id: ApiKey; + /** Flow Version */ + version: string; }; }; - /** Run workflow */ - runFlow: { + /** Test workflow */ + testFlow: { request: { - // TODO: use workflow data type - dsl: any; + /** Input Parameters */ + input?: Record; + /** Flow Design Data (JSON string) */ + design_data: any; }; response: { - // TODO: use workflow log status enum - status: string; - trace_infos: { - node_id: ApiKey; - node_name: string; - status: string; - cost: number; - input: Record; - output: Record; - }[]; + /** Running Status */ + status: FlowRunningStatus; + /** Flow ID */ + flow_id: ApiKey; + /** Start Time */ + start_time: number; + /** Run Time */ + time_cost: number; + trace_infos: FlowNodeTraceInfo[]; }; }; - /** Run single node */ - runSingleNode: { + /** Test single node */ + testSingleNode: { request: { - node_config: Record; - input: Record; - }; - response: { - node_id: ApiKey; - node_name: string; - status: string; - const: number; input: Record; - output: Record; + /** Node Config (JSON string) */ + node_config: string; }; + response: FlowNodeTraceInfo; }; /** Get workflow nodes info */ @@ -224,8 +232,8 @@ export interface WorkflowAPISchema extends APISchema { response: Record< WorkflowNodeCategoryType, { - componentId: string; - componentName: string; + name: string; + title: string; }[] >; }; @@ -233,10 +241,20 @@ export interface WorkflowAPISchema extends APISchema { /** Get node form schema */ getNodeForm: { request: { - id: ApiKey; + /** The Backend Node Name */ + name: string; }; response: unknown; }; + + /** Get code langs */ + getCodeLangs: { + request: void; + response: { + code: string[]; + expression: string[]; + }; + }; } /** @@ -245,20 +263,20 @@ export interface WorkflowAPISchema extends APISchema { export default attachAPI(client, { apis: { getList: `POST ${API_PREFIX}/workflow/flows/search`, - addFlow: `POST ${API_PREFIX}/workflow/flows`, updateFlow: `PUT ${API_PREFIX}/workflow/flows/:id`, - deleteFlow: `DELETE ${API_PREFIX}/workflow/flows/:id`, + deleteFlows: `POST ${API_PREFIX}/workflow/flows/batch-delete`, importFlow: `POST ${API_PREFIX}/workflow/flows/import`, exportFlow: `GET ${API_PREFIX}/workflow/flows/:id/export`, enableFlow: `GET ${API_PREFIX}/workflow/flows/:id/:status`, getLogList: `POST ${API_PREFIX}/workflow/flows/:id/logs/search`, getLogDetail: `GET ${API_PREFIX}/workflow/flows/logs/:id`, - getFlowDesign: `GET ${API_PREFIX}/workflow/flows/:id/design`, - checkFlowDesign: `POST ${API_PREFIX}/workflow/flows/:id/design/validate`, - saveFlowDesign: `POST ${API_PREFIX}/workflow/flows/:id/design`, - runFlow: `POST ${API_PREFIX}/workflow/flows/:id/test`, - runSingleNode: `POST ${API_PREFIX}/workflow/flows/node/test`, + getFlowDesign: `GET ${API_PREFIX}/workflow/flows/:id/design?version=:version`, + checkFlowDesign: `POST ${API_PREFIX}/workflow/flows/design/validate`, + saveFlowDesign: `POST ${API_PREFIX}/workflow/flows/design`, + testFlow: `POST ${API_PREFIX}/workflow/flows/test`, + testSingleNode: `POST ${API_PREFIX}/workflow/flows/node/test`, getFlowNodes: `GET ${API_PREFIX}/workflow/components`, getNodeForm: `GET ${API_PREFIX}/workflow/components/:id`, + getCodeLangs: `GET ${API_PREFIX}/workflow/components/languages`, }, }); From 9ecfdef54b05e504364bdc56d1f79c6dc8f53eee Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 23 Dec 2024 10:43:29 +0800 Subject: [PATCH 070/172] feat: Adjust the branch display rules for the action log component --- .../workflow/components/action-log/index.tsx | 108 ++++++++++-------- .../workflow/components/action-log/types.d.ts | 6 - 2 files changed, 60 insertions(+), 54 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index baae1f34..ceea3013 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,65 +1,77 @@ import { Fragment } from 'react/jsx-runtime'; -import { isNil } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { AccordionCard, AccordionHeader, AccordionTree } from './components'; import { useNestedData } from './hooks'; import { ALPHABET_LIST } from './constant'; -import type { ActionLogProps, LevelStructType, WorkflowNestNode } from './types'; +import type { ActionLogProps, WorkflowNestNode } from './types'; import './style.less'; +/** + * get alphabet index + * @example getAlphabetIndex(0) => A + */ +const getAlphabetIndex = (index: number) => { + if (index < 0) return; + + if (index > 26) { + const first = Math.floor(index / 26); + const second = index % 26; + return `${ALPHABET_LIST[first - 1]}${ALPHABET_LIST[second]}`; + } + return ALPHABET_LIST[index]; +}; export default function AccordionUsage({ traceData, workflowData }: ActionLogProps) { const { getIntlText } = useI18n(); const { treeData } = useNestedData({ workflowData, traceData }); /** recursive rendering */ - const renderAccordion = (data: WorkflowNestNode, levelStruct: LevelStructType) => { - const { children, attrs, ...item } = data || {}; - /** get parallel text */ - const { label: levelLabel, value: levelValue, parentValue } = levelStruct || {}; - const parentText = !isNil(parentValue) ? `${ALPHABET_LIST[parentValue]}` : ''; - const parallelLabel = `${levelLabel}${parentText}`; - const parallelText = getIntlText('workflow.label.parallel', { - 1: `-${parallelLabel}`, - }); + const renderAccordion = (treeData: WorkflowNestNode[], level: number = 0) => { + // Existence of parallel branches + const parallelBranchCount = treeData.reduce((acc, cur) => { + if (cur.children?.length) acc++; + return acc; + }, 0); + const hasParallelBranch = parallelBranchCount > 1; + // current level + const currentLevel = level + 1; + // There are multiple parallel branches + let multiBranchInParallelIndex = -1; - return ( - <> - }> - {/* // TODO */} -
input
-
- {!!children?.length && ( - - {children.map((child, ind) => { - const nextLevel = levelValue + 1; - const branchLabel = ALPHABET_LIST[ind]; - const branchText = getIntlText('workflow.label.branch', { - 1: `-${parallelLabel}-${branchLabel}`, - }); + return treeData.map(data => { + const { children, attrs, ...item } = data || {}; - return ( - -
{branchText}
- {renderAccordion(child, { - label: `${nextLevel + 1}`, - value: nextLevel, - parentValue: children.length > 1 ? ind : void 0, - })} -
- ); - })} -
- )} - - ); - }; - return ( -
- {treeData.map((item, index) => ( + // If there are child nodes, increment the index + if ((children?.length || 0) > 1) { + multiBranchInParallelIndex++; + } + + const parallelLabel = hasParallelBranch + ? `${currentLevel}${getAlphabetIndex(multiBranchInParallelIndex)}` + : `${currentLevel}`; + + // Text to be displayed + const parallelText = getIntlText('workflow.label.parallel', { + 1: `-${parallelLabel}`, + }); + const branchText = getIntlText('workflow.label.branch', { + 1: `-${parallelLabel}-${getAlphabetIndex(multiBranchInParallelIndex)}`, + }); + return ( - {renderAccordion(item, { label: `${index + 1}`, value: index })} + }> + {/* // TODO */} +
input
+
+ {!!children?.length && ( + +
{branchText}
+ {renderAccordion(children, currentLevel)} +
+ )}
- ))} -
- ); + ); + }); + }; + + return
{renderAccordion(treeData)}
; } diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index 16b3d206..71534100 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -84,12 +84,6 @@ export interface ParallelNodeResult { incomers: WorkflowNestNode[]; usableIncome: WorkflowNestNode; } -/** level struct type */ -export type LevelStructType = { - label: string; - value: number; - parentValue?: number; -}; /** action log props */ export interface ActionLogProps { From 06eb36f01145e4baf95d54890552fe5c937fcc11 Mon Sep 17 00:00:00 2001 From: Lynn-Bing Date: Mon, 23 Dec 2024 11:09:47 +0800 Subject: [PATCH 071/172] feat: Integrated debugging and entity export --- .../entity/components/add-modal/useForm.tsx | 3 + .../entity/components/custom-entity/index.tsx | 2 +- .../pages/entity/components/detail/index.tsx | 105 +++++++++++------- .../pages/entity/components/entity/index.tsx | 104 +++++++++-------- apps/web/src/pages/entity/style.less | 15 +++ apps/web/src/services/http/entity.ts | 17 ++- packages/shared/src/utils/request/utils.ts | 22 ++++ packages/shared/src/utils/tools.ts | 6 + 8 files changed, 184 insertions(+), 90 deletions(-) diff --git a/apps/web/src/pages/entity/components/add-modal/useForm.tsx b/apps/web/src/pages/entity/components/add-modal/useForm.tsx index 91f1283e..d163a1e8 100644 --- a/apps/web/src/pages/entity/components/add-modal/useForm.tsx +++ b/apps/web/src/pages/entity/components/add-modal/useForm.tsx @@ -54,6 +54,9 @@ const useForm = (props: FormProps) => { message: 'Username can only contain letters, numbers, and underscores', }, }, + props: { + disabled: !!defaultValues, + }, }, { label: getIntlText('entity.label.entity_type_of_access'), diff --git a/apps/web/src/pages/entity/components/custom-entity/index.tsx b/apps/web/src/pages/entity/components/custom-entity/index.tsx index e2732742..9b6878d8 100644 --- a/apps/web/src/pages/entity/components/custom-entity/index.tsx +++ b/apps/web/src/pages/entity/components/custom-entity/index.tsx @@ -193,7 +193,7 @@ export default () => { data={detail} /> )} - {workflowModalOpen && !!detail && ( + {workflowModalOpen && ( setWorkflowModalOpen(false)} onOk={() => { diff --git a/apps/web/src/pages/entity/components/detail/index.tsx b/apps/web/src/pages/entity/components/detail/index.tsx index c91ee1d2..1fdf0ee4 100644 --- a/apps/web/src/pages/entity/components/detail/index.tsx +++ b/apps/web/src/pages/entity/components/detail/index.tsx @@ -24,6 +24,7 @@ export default (props: IProps) => { const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [anchorEl, setAnchorEl] = useState(null); const [isShowFilter, setIsShowFilter] = useState(false); + const [dataLoading, setDataLoading] = useState(true); // Store the selected time range and send it to the backend as a filter condition when the confirm button is clicked. const timeRef = useRef(null); @@ -33,6 +34,7 @@ export default (props: IProps) => { run: getList, } = useRequest( async () => { + setDataLoading(true); const { page, pageSize } = paginationModel; const [error, resp] = await awaitWrap( entityAPI.getHistory({ @@ -50,7 +52,7 @@ export default (props: IProps) => { const data = getResponseData(resp); if (error || !data || !isRequestSuccess(resp)) return; - + setDataLoading(false); return objectToCamelCase(data); }, { @@ -102,54 +104,71 @@ export default (props: IProps) => { onButtonClick: handleTableBtnClick, isShowFilter, }); - return ( - - -
-
- {getIntlText('entity.label.historical_data')} + +
+ +
+
+ {getIntlText('entity.label.historical_data')} +
-
- - loading={loading} - columns={columns} - getRowId={record => record.timestamp} - rows={entityData?.content} - rowCount={entityData?.total || 0} - paginationModel={paginationModel} - toolbarRender={false} - onPaginationModelChange={setPaginationModel} - onRefreshButtonClick={getList} - /> - -
- + + loading={loading} + columns={columns} + getRowId={record => record.timestamp} + rows={entityData?.content} + rowCount={entityData?.total || 0} + paginationModel={paginationModel} + toolbarRender={false} + onPaginationModelChange={setPaginationModel} + onRefreshButtonClick={getList} /> -
-
-
+ +
+ +
+
+ +
+
-
-
- + +
); }; diff --git a/apps/web/src/pages/entity/components/entity/index.tsx b/apps/web/src/pages/entity/components/entity/index.tsx index 1a08f523..c0d1e292 100644 --- a/apps/web/src/pages/entity/components/entity/index.tsx +++ b/apps/web/src/pages/entity/components/entity/index.tsx @@ -3,16 +3,26 @@ import { useNavigate } from 'react-router-dom'; import { Button, Stack } from '@mui/material'; import { useRequest } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; -import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { objectToCamelCase, xhrDownload } from '@milesight/shared/src/utils/tools'; +import { getCurrentComponentLang } from '@milesight/shared/src/services/i18n'; +import { getAuthorizationToken } from '@milesight/shared/src/utils/request/utils'; import { IosShareIcon, toast } from '@milesight/shared/src/components'; import { TablePro, useConfirm } from '@/components'; -import { entityAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import { DateRangePickerValueType } from '@/components/date-range-picker'; +import { + entityAPI, + awaitWrap, + getResponseData, + isRequestSuccess, + API_PREFIX, +} from '@/services/http'; import useColumns, { type UseColumnsProps, type TableRowDataType, } from '../../hooks/useEntityColumns'; import Detail from '../detail'; import EditEntity from '../edit-entity'; +import ExportModal from '../export-modal'; export default () => { const navigate = useNavigate(); @@ -21,19 +31,10 @@ export default () => { const [keyword, setKeyword] = useState(); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); - const [anchorEl, setAnchorEl] = useState(null); + const [exportVisible, setExportVisible] = useState(false); const [detail, setDetail] = useState(null); const [detailVisible, setDetailVisible] = useState(false); const [editVisible, setEditVisible] = useState(false); - const open = Boolean(anchorEl); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleClose = () => { - setAnchorEl(null); - }; const { data: entityData, @@ -61,33 +62,48 @@ export default () => { }, ); - const confirm = useConfirm(); - const handleExportConfirm = useCallback( - (ids?: ApiKey[]) => { - const idsToDelete = ids || [...selectedIds]; - - confirm({ - title: getIntlText('common.label.delete'), - description: getIntlText('device.message.delete_tip'), - confirmButtonText: getIntlText('common.label.delete'), - confirmButtonProps: { - color: 'error', - }, - onConfirm: async () => { - const [error, resp] = await awaitWrap( - entityAPI.deleteEntities({ entity_ids: idsToDelete }), - ); - - if (error || !isRequestSuccess(resp)) return; - - getList(); - setSelectedIds([]); - toast.success(getIntlText('common.message.operation_success')); - }, - }); - }, - [confirm, getIntlText, getList, selectedIds], - ); + const handleShowExport = () => { + if (!selectedIds?.length) { + toast.error( + getIntlText('valid.resp.at_least_one', { 1: getIntlText('common.label.entity') }), + ); + return; + } + setExportVisible(true); + }; + + const handleCloseExport = () => { + setExportVisible(false); + }; + + const handleExportConfirm = async (time: DateRangePickerValueType | null) => { + if (!selectedIds?.length) { + return; + } + let url = `${API_PREFIX}/entity/export?`; + selectedIds.forEach((id: ApiKey) => { + url += `&ids=${id}`; + }); + if (time?.start) { + url += `&start_timestamp=${time?.start.valueOf()}`; + } + if (time?.end) { + url += `&end_timestamp=${(time?.end.valueOf() || 0) + 86399000}`; + } + xhrDownload({ + assets: url, + fileName: 'entity.csv', + header: { + 'Accept-Language': getCurrentComponentLang(), + Authorization: getAuthorizationToken(), + }, + }).then(() => { + getList(); + setSelectedIds([]); + handleCloseExport(); + toast.success(getIntlText('common.message.operation_success')); + }); + }; /** Details event related */ const handleDetail = (data: TableRowDataType) => { @@ -113,7 +129,7 @@ export default () => { const handleEdit = async (name: string) => { const [error, resp] = await awaitWrap( - entityAPI.editEntity({ id: detail?.entityId || '', entity_name: name }), + entityAPI.editEntity({ name, id: detail?.entityId || '' }), ); if (error || !isRequestSuccess(resp)) return; @@ -130,11 +146,8 @@ export default () => { @@ -187,6 +200,9 @@ export default () => { {!!editVisible && !!detail && ( )} + {!!exportVisible && ( + + )}
); }; diff --git a/apps/web/src/pages/entity/style.less b/apps/web/src/pages/entity/style.less index 92d39316..a2c8f494 100644 --- a/apps/web/src/pages/entity/style.less +++ b/apps/web/src/pages/entity/style.less @@ -76,6 +76,21 @@ } &-detail { + &-contain { + display: flex; + flex-direction: column; + height: 100%; + } + + &-table { + flex: 1; + height: 0; + + & .@{prefix}-table-pro { + height: calc(100vh - 300px); + } + } + &-table-header { display: flex; align-items: center; diff --git a/apps/web/src/services/http/entity.ts b/apps/web/src/services/http/entity.ts index 59734dda..c9e0f405 100644 --- a/apps/web/src/services/http/entity.ts +++ b/apps/web/src/services/http/entity.ts @@ -147,7 +147,7 @@ export interface EntityAPISchema extends APISchema { editEntity: { request: { id: ApiKey; - entity_name: string; + name: string; }; response: unknown; }; @@ -176,6 +176,18 @@ export interface EntityAPISchema extends APISchema { }; response: unknown; }; + + /** 导出实体历史数据 */ + exportEntityHistory: { + request: { + ids: ApiKey[]; + /** 开始时间戳,单位 ms */ + start_timestamp?: number; + /** 结束时间戳,单位 ms */ + end_timestamp?: number; + }; + response: unknown; + }; } /** @@ -194,8 +206,9 @@ export default attachAPI(client, { getEntityStatus: `GET ${API_PREFIX}/entity/:id/status`, getChildrenEntity: `GET ${API_PREFIX}/entity/:id/children`, deleteEntities: `POST ${API_PREFIX}/entity/delete`, - editEntity: `POST ${API_PREFIX}/entity/:id`, + editEntity: `PUT ${API_PREFIX}/entity/:id`, createCustomEntity: `POST ${API_PREFIX}/entity`, editCustomEntity: `PUT ${API_PREFIX}/entity/:entityId`, + exportEntityHistory: `GET ${API_PREFIX}/entity/export`, }, }); diff --git a/packages/shared/src/utils/request/utils.ts b/packages/shared/src/utils/request/utils.ts index 52f67b4f..d6709066 100644 --- a/packages/shared/src/utils/request/utils.ts +++ b/packages/shared/src/utils/request/utils.ts @@ -2,6 +2,7 @@ * 常用请求处理工具 */ import type { AxiosResponse, AxiosError } from 'axios'; +import iotStorage, { TOKEN_CACHE_KEY } from '../storage'; /** * 判断 API 请求是否成功 @@ -53,3 +54,24 @@ export const awaitWrap = ( return [err, undefined]; }); }; + +export type TokenDataType = { + /** 鉴权 Token */ + access_token: string; + /** 刷新 Token */ + refresh_token: string; + /** + * 过期时间,单位 ms + * + * 注意:该值为前端过期时间,仅用于判断何时需刷新 token,实际 token 在后端可能还未过期 + */ + expires_in: number; +}; + +/** + * 获取接口Authorization Token + */ +export const getAuthorizationToken = () => { + const token = iotStorage.getItem(TOKEN_CACHE_KEY); + return token?.access_token ? `Bearer ${token?.access_token}` : ''; +}; diff --git a/packages/shared/src/utils/tools.ts b/packages/shared/src/utils/tools.ts index 2a58ece7..866cb9c1 100644 --- a/packages/shared/src/utils/tools.ts +++ b/packages/shared/src/utils/tools.ts @@ -349,6 +349,10 @@ interface DownloadOptions { * @param percent 下载进度百分比 */ onProgress?: (percent: number) => void; + /** + * 自定义请求头 + */ + header?: Record; } interface xhrDownloadResponse { /** @@ -376,6 +380,7 @@ export const xhrDownload = ({ assets, fileName, onProgress, + header, }: DownloadOptions): xhrDownloadResponse => { if (!assets) { throw new Error('assets is required'); @@ -390,6 +395,7 @@ export const xhrDownload = ({ // 利用axios下载文件 axios .request({ + headers: header, url: fileUrl, method: 'GET', responseType: 'blob', From 085a1b99a590bdfeb2ef2243b33f200f92801d40 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 23 Dec 2024 13:37:21 +0800 Subject: [PATCH 072/172] feat: Use the 'readerHeader' API to replace 'Header' usage --- apps/web/src/components/code-editor/README.md | 121 +++++++++++------- .../components/custom-header/index.tsx | 10 ++ .../code-editor/components/editor/style.less | 4 + .../code-editor/components/index.tsx | 1 + .../web/src/components/code-editor/editor.tsx | 18 ++- .../code-editor/hooks/useEditorCommand.tsx | 15 ++- .../web/src/components/code-editor/types.d.ts | 11 +- 7 files changed, 122 insertions(+), 58 deletions(-) create mode 100644 apps/web/src/components/code-editor/components/custom-header/index.tsx diff --git a/apps/web/src/components/code-editor/README.md b/apps/web/src/components/code-editor/README.md index 33d81b6f..5f341ad5 100644 --- a/apps/web/src/components/code-editor/README.md +++ b/apps/web/src/components/code-editor/README.md @@ -1,6 +1,6 @@ ## `CodeEditor` Component Usage -### Default Usage +### 1. Default Usage ```jsx const App = () => { return ( @@ -9,7 +9,7 @@ const App = () => { } ``` -### Controlled Editor Language and Content +### 2. Controlled Editor Language and Content ```jsx const App = () => { const [editorLang, setEditorLang] = useState('markdown'); @@ -26,80 +26,109 @@ const App = () => { } ``` -### Hide Certain Features +### 3. Hide Certain Features ```jsx const App = () => { return ( ) } ``` -### Custom Header +### 4. Read-Only/Edit Mode -1. Simple adjustments, such as modifying only the title and icon ```jsx -import { CodeEditor, CodeEditorToolbar, type EditorToolbarProps } from '@/components'; -import { CheckCircleIcon } from '@milesight/shared/src/components'; - -const Header = (props: EditorToolbarProps) => { - return } title="xxxx" />; -}; const App = () => { return ( - - ) -} + + ); +}; ``` -2. Custom layout, but using some components like `CodeEditorSelect` +### 5. Custom Header + +- Simple adjustments, such as modifying only the title and icon ```jsx +import { useCallback } from 'react'; +import { CheckCircleIcon } from '@milesight/shared/src/components'; import { CodeEditor, CodeEditorToolbar, type EditorToolbarProps } from '@/components'; -const Header = (props: EditorToolbarProps) => { - return ( -
- {/* Reuse the select component */} -

{item.label}

} /> -
Some styles you want
-
+const App = () => { + const renderHeader = useCallback( + (props: EditorToolbarProps) => ( + } title="xxxx" /> + ), + [], ); + + return ; }; +``` + +- Custom layout, but using some components like `CodeEditorSelect` +```jsx +import { useCallback } from 'react'; +import { + CodeEditor, + CodeEditorSelect, + COMMON_EDITOR_HEADER_CLASS, + type EditorToolbarProps, +} from '@/components'; + const App = () => { - return ( - - ) -} + const renderHeader = useCallback( + (props: EditorToolbarProps) => ( +
+ {/* Reuse the select component */} + + options.map(option =>

{option.label}

) + } + /> +
Some styles you want
+
+ ), + [], + ); + + return ; +}; ``` -3. Fully custom header component +- Fully custom header component ```jsx +import { useCallback } from 'react'; import { CodeEditor, COMMON_EDITOR_HEADER_CLASS, type EditorToolbarProps } from '@/components'; -const Header = (props: EditorToolbarProps) => { - const { editorHandlers } = props; - const { redo, undo, insert } = editorHandlers || {}; +const App = () => { + const renderHeader = useCallback((props: EditorToolbarProps) => { + const { editorHandlers } = props; + const { redo, undo, insert } = editorHandlers || {}; - return ( -
-
Content
-
-
undo
-
redo
-
insert('Value you want to insert')}>insert
+ return ( +
+
Content
+
+
undo
+
redo
+
insert('Value you want to insert')}>insert
+
-
- ); + ); + }, []); + + return ; }; -const App = () => { - return ( - - ) -} ``` \ No newline at end of file diff --git a/apps/web/src/components/code-editor/components/custom-header/index.tsx b/apps/web/src/components/code-editor/components/custom-header/index.tsx new file mode 100644 index 00000000..0efcc75c --- /dev/null +++ b/apps/web/src/components/code-editor/components/custom-header/index.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import EditorHeader from '../header'; +import type { EditorProps, EditorToolbarProps } from '../../types'; + +export default React.memo((props: EditorToolbarProps & Pick) => { + const { renderHeader, ...rest } = props; + + if (renderHeader) return renderHeader(rest); + return ; +}); diff --git a/apps/web/src/components/code-editor/components/editor/style.less b/apps/web/src/components/code-editor/components/editor/style.less index b4e1ba1a..0c5d896d 100644 --- a/apps/web/src/components/code-editor/components/editor/style.less +++ b/apps/web/src/components/code-editor/components/editor/style.less @@ -2,6 +2,10 @@ flex: 1; overflow: hidden; + .cm-scroller { + outline: none; + } + .cm-editor { height: 100%; outline: none; diff --git a/apps/web/src/components/code-editor/components/index.tsx b/apps/web/src/components/code-editor/components/index.tsx index fcfa52d2..cb8dbda0 100644 --- a/apps/web/src/components/code-editor/components/index.tsx +++ b/apps/web/src/components/code-editor/components/index.tsx @@ -1,3 +1,4 @@ export { default as EditorHeader } from './header'; export { default as EditorSelect } from './lang-select'; export { default as EditorComponent } from './editor'; +export { default as EditorHeaderComponent } from './custom-header'; diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index 6b2d4324..8caef5b6 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -1,13 +1,19 @@ import React, { forwardRef, useImperativeHandle, useRef } from 'react'; import { useControllableValue } from 'ahooks'; import { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; -import { EditorComponent, EditorHeader } from './components'; +import { EditorHeaderComponent, EditorComponent } from './components'; import { useEditorCommand } from './hooks'; import type { EditorSupportLang, EditorProps, EditorHandlers } from './types'; import './style.less'; export const CodeEditor = forwardRef((props, ref) => { - const { Header: CustomHeader, ...rest } = props; + const { + Header: CustomHeader, + readOnly = false, + editable = true, + renderHeader, + ...rest + } = props; const editorInstanceRef = useRef(null); const [editorLang, setEditorLang] = useControllableValue(props, { @@ -21,11 +27,10 @@ export const CodeEditor = forwardRef((props, ref) = trigger: 'onChange', }); - const { handlers } = useEditorCommand({ editorInstanceRef }); + const { handlers } = useEditorCommand({ editorInstanceRef, readOnly, editable }); /** Methods exposed to external components */ useImperativeHandle(ref, () => handlers); - const EditorHeaderComponent = CustomHeader === void 0 ? EditorHeader : CustomHeader!; return (
{CustomHeader !== null && ( @@ -34,6 +39,9 @@ export const CodeEditor = forwardRef((props, ref) = editorLang={editorLang} editorValue={editorValue} setEditorLang={setEditorLang} + readOnly={readOnly} + editable={editable} + renderHeader={renderHeader} /> )} ((props, ref) = editorLang={editorLang} editorValue={editorValue} setEditorValue={setEditorValue} + readOnly={readOnly} + editable={editable} />
); diff --git a/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx b/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx index 98f045e8..0258e7ee 100644 --- a/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx +++ b/apps/web/src/components/code-editor/hooks/useEditorCommand.tsx @@ -1,12 +1,12 @@ import { useCallback, useMemo } from 'react'; import { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; import { undo as undoCommand, redo as redoCommand } from '@codemirror/commands'; -import type { EditorHandlers } from '../types'; +import type { EditorHandlers, EditorProps } from '../types'; -interface IProps { +interface IProps extends Pick { editorInstanceRef: React.RefObject; } -export const useEditorCommand = ({ editorInstanceRef }: IProps) => { +export const useEditorCommand = ({ editorInstanceRef, readOnly, editable }: IProps) => { /** Function to get the current EditorView instance */ const getEditorView = useCallback(() => { return editorInstanceRef.current?.view; @@ -21,17 +21,19 @@ export const useEditorCommand = ({ editorInstanceRef }: IProps) => { const undo = useCallback(() => { const editorView = getEditorView(); if (!editorView) return; + if (readOnly || !editable) return; undoCommand(editorView); - }, [getEditorView]); + }, [editable, getEditorView, readOnly]); /** Function to perform redo operation */ const redo = useCallback(() => { const editorView = getEditorView(); if (!editorView) return; + if (readOnly || !editable) return; redoCommand(editorView); - }, [getEditorView]); + }, [editable, getEditorView, readOnly]); /** Function to insert text at the current cursor position */ const insert = useCallback( @@ -40,6 +42,7 @@ export const useEditorCommand = ({ editorInstanceRef }: IProps) => { const editorState = getEditorState(); const editorView = getEditorView(); if (!editorState || !editorView) return; + if (readOnly || !editable) return; // insert text at the current cursor position const { main } = editorView?.state?.selection || {}; @@ -60,7 +63,7 @@ export const useEditorCommand = ({ editorInstanceRef }: IProps) => { // Ensure the editor gains focus editorView.focus(); }, - [getEditorState, getEditorView], + [editable, getEditorState, getEditorView, readOnly], ); const handlers = useMemo(() => { diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts index 0a18e01c..5e2e51e3 100644 --- a/apps/web/src/components/code-editor/types.d.ts +++ b/apps/web/src/components/code-editor/types.d.ts @@ -46,8 +46,13 @@ export interface EditorProps extends EditorContentProps { */ onChange?: (value: string) => void; - /** Custom editor toolbar header. */ + /** + * Custom editor toolbar header. + * @deprecated This prop is deprecated and will be removed in a future version., please use the `renderHeader` prop instead. + */ Header?: React.FC | null; + /** Custom editor toolbar header. */ + renderHeader?: (props: EditorToolbarProps) => React.ReactNode; } /** Interface for editor language options. */ @@ -77,7 +82,9 @@ export interface EditorSelectProps { } /** Props for the code editor toolbar. */ -export interface EditorToolbarProps extends Pick { +export interface EditorToolbarProps + extends Pick, + Pick { /** The content value of the editor. */ editorValue: string; /** The programming language used in the editor. */ From 8bf33f6943b8f48270a601759b776701828c990e Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 23 Dec 2024 13:50:01 +0800 Subject: [PATCH 073/172] feat: The action log component adds an input/output editor --- .../components/code-editor/index.tsx | 22 +++++++++++++++++++ .../action-log/components/index.tsx | 1 + .../workflow/components/action-log/index.tsx | 20 ++++++++++++++--- .../workflow/components/action-log/style.less | 4 ++++ packages/locales/src/lang/en/global.json | 4 +++- 5 files changed, 47 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx diff --git a/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx new file mode 100644 index 00000000..25cd6701 --- /dev/null +++ b/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx @@ -0,0 +1,22 @@ +import React, { useCallback } from 'react'; +import { CodeEditor, CodeEditorToolbar, EditorToolbarProps } from '@/components'; + +interface IProps { + value: string; + title: React.ReactNode; +} +export default React.memo(({ title, value }: IProps) => { + const renderHeader = useCallback( + (props: EditorToolbarProps) => , + [title], + ); + return ( + + ); +}); diff --git a/apps/web/src/pages/workflow/components/action-log/components/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/index.tsx index 5f592928..60a3f8c8 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/index.tsx @@ -1,3 +1,4 @@ export { default as AccordionHeader } from './header'; export { default as AccordionCard } from './card'; export { default as AccordionTree } from './tree'; +export { default as ActionCodeEditor } from './code-editor'; diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index ceea3013..c47af656 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,6 +1,6 @@ import { Fragment } from 'react/jsx-runtime'; import { useI18n } from '@milesight/shared/src/hooks'; -import { AccordionCard, AccordionHeader, AccordionTree } from './components'; +import { AccordionCard, AccordionHeader, AccordionTree, ActionCodeEditor } from './components'; import { useNestedData } from './hooks'; import { ALPHABET_LIST } from './constant'; import type { ActionLogProps, WorkflowNestNode } from './types'; @@ -39,6 +39,7 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro return treeData.map(data => { const { children, attrs, ...item } = data || {}; + const { input, output } = attrs || {}; // If there are child nodes, increment the index if ((children?.length || 0) > 1) { @@ -56,11 +57,24 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro const branchText = getIntlText('workflow.label.branch', { 1: `-${parallelLabel}-${getAlphabetIndex(multiBranchInParallelIndex)}`, }); + return ( }> - {/* // TODO */} -
input
+ {input && ( +
+ +
+ )} + {output && ( + + )}
{!!children?.length && ( diff --git a/apps/web/src/pages/workflow/components/action-log/style.less b/apps/web/src/pages/workflow/components/action-log/style.less index e3e673cd..ae25df05 100644 --- a/apps/web/src/pages/workflow/components/action-log/style.less +++ b/apps/web/src/pages/workflow/components/action-log/style.less @@ -3,4 +3,8 @@ color: var(--text-color-secondary); .text-size(@font-size-sm); } + + &__input { + margin-bottom: @margin-sm; + } } diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index c6f40e22..90b47ef4 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -163,5 +163,7 @@ "common.label.run": "Run", "common.label.target": "Target", "common.label.ms": "ms", - "common.label.week": "Week" + "common.label.week": "Week", + "common.label.input": "Input", + "common.label.output": "Output" } From 1515d1275f113aae283a4c749045b009163522ad Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 23 Dec 2024 14:07:50 +0800 Subject: [PATCH 074/172] feat: The code editor component added support for the 'height' props --- .../code-editor/components/editor/index.tsx | 3 +++ .../code-editor/components/editor/style.less | 2 +- apps/web/src/components/code-editor/editor.tsx | 11 +++++++++-- .../action-log/components/code-editor/index.tsx | 4 ++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/apps/web/src/components/code-editor/components/editor/index.tsx b/apps/web/src/components/code-editor/components/editor/index.tsx index e0ac6ecc..dbb1a65a 100644 --- a/apps/web/src/components/code-editor/components/editor/index.tsx +++ b/apps/web/src/components/code-editor/components/editor/index.tsx @@ -21,6 +21,9 @@ interface IProps extends EditorContentProps { editorValue: string; setEditorValue: (value: string) => void; } +/** + * @docs https://github.com/uiwjs/react-codemirror + */ export const CodeEditorContent = forwardRef((props, ref) => { const { showLineNumber = true, diff --git a/apps/web/src/components/code-editor/components/editor/style.less b/apps/web/src/components/code-editor/components/editor/style.less index 0c5d896d..a017a03d 100644 --- a/apps/web/src/components/code-editor/components/editor/style.less +++ b/apps/web/src/components/code-editor/components/editor/style.less @@ -7,7 +7,7 @@ } .cm-editor { - height: 100%; + height: var(--code-mirror-height); outline: none; .cm-gutters { diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index 8caef5b6..12c964d0 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -1,4 +1,4 @@ -import React, { forwardRef, useImperativeHandle, useRef } from 'react'; +import React, { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'; import { useControllableValue } from 'ahooks'; import { type ReactCodeMirrorRef } from '@uiw/react-codemirror'; import { EditorHeaderComponent, EditorComponent } from './components'; @@ -11,6 +11,7 @@ export const CodeEditor = forwardRef((props, ref) = Header: CustomHeader, readOnly = false, editable = true, + height, renderHeader, ...rest } = props; @@ -31,8 +32,13 @@ export const CodeEditor = forwardRef((props, ref) = /** Methods exposed to external components */ useImperativeHandle(ref, () => handlers); + const cssVariable = useMemo(() => { + return { + '--code-mirror-height': height ?? '100%', + } as React.CSSProperties; + }, [height]); return ( -
+
{CustomHeader !== null && ( ((props, ref) = setEditorValue={setEditorValue} readOnly={readOnly} editable={editable} + height={height} />
); diff --git a/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx index 25cd6701..e48d8200 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/code-editor/index.tsx @@ -12,11 +12,11 @@ export default React.memo(({ title, value }: IProps) => { ); return ( ); }); From b02e33df4c1736bccf9d3ef957677685a4cff4c3 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 23 Dec 2024 16:28:53 +0800 Subject: [PATCH 075/172] feat: The action log component adds 'errMsg' text display --- apps/web/src/components/code-editor/editor.tsx | 2 ++ apps/web/src/components/code-editor/types.d.ts | 7 +++---- .../workflow/components/action-log/index.tsx | 11 ++++++++++- .../workflow/components/action-log/style.less | 15 +++++++++++++++ .../workflow/components/action-log/types.d.ts | 8 ++++++-- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index 69eda2a8..600f31ea 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -9,6 +9,7 @@ import './style.less'; export const CodeEditor = forwardRef((props, ref) => { const { title, + icon, Header: CustomHeader, readOnly = false, editable = true, @@ -43,6 +44,7 @@ export const CodeEditor = forwardRef((props, ref) = {CustomHeader !== null && ( { /** Default editor language. */ defaultEditorLang?: EditorSupportLang; /** The programming language used in the editor. */ diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index c47af656..b95c7079 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,5 +1,7 @@ import { Fragment } from 'react/jsx-runtime'; +import { Alert } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; +import { Tooltip } from '@/components'; import { AccordionCard, AccordionHeader, AccordionTree, ActionCodeEditor } from './components'; import { useNestedData } from './hooks'; import { ALPHABET_LIST } from './constant'; @@ -39,7 +41,7 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro return treeData.map(data => { const { children, attrs, ...item } = data || {}; - const { input, output } = attrs || {}; + const { input, output, errorMsg } = attrs || {}; // If there are child nodes, increment the index if ((children?.length || 0) > 1) { @@ -61,6 +63,13 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro return ( }> + {errorMsg && ( +
+ + + +
+ )} {input && (
; + input?: Record; /** * TODO Output */ - output: Record; + output?: Record; + /** + * error message + */ + errorMsg?: string; /** * Children */ From c8d41d856ff535270c616f26762426d91c25cada Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 23 Dec 2024 16:57:57 +0800 Subject: [PATCH 076/172] feat: Adjust some ts types in the action log --- .../action-log/hooks/useNestedData.tsx | 4 +-- .../workflow/components/action-log/style.less | 1 - .../workflow/components/action-log/types.d.ts | 25 +++++-------------- .../log-modal/components/log-item/index.tsx | 8 ------ .../log-modal/hooks/useRenderList.tsx | 2 +- .../log-modal/hooks/useSourceData.tsx | 3 ++- .../workflow/components/log-modal/types.d.ts | 4 +-- apps/web/src/pages/workflow/config.tsx | 23 +++++++++-------- 8 files changed, 25 insertions(+), 45 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx index 7d6ae1f4..40a23ac6 100644 --- a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -32,13 +32,13 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { const nestNode = cloneDeep(node) as WorkflowNestNode; const { id, type, data } = nestNode || {}; const { name } = data || {}; - const { status, input, output, time_cost: timeCost } = traceMap[id] || {}; + const { status, input, output, timeCost } = objectToCamelCase(traceMap[id] || {}); nestNode.attrs = { $$token: generateUUID(), name: name || '', type: type!, - status: status === 'SUCCESS' ? 'success' : 'failed', + status, timeCost, input, output, diff --git a/apps/web/src/pages/workflow/components/action-log/style.less b/apps/web/src/pages/workflow/components/action-log/style.less index a7760071..587c9c00 100644 --- a/apps/web/src/pages/workflow/components/action-log/style.less +++ b/apps/web/src/pages/workflow/components/action-log/style.less @@ -17,7 +17,6 @@ padding: 0 @padding-md; .MuiAlert-message { - // .text-size(@font-size-sm); } } diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index d474aa17..981be208 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -1,11 +1,14 @@ import { type ReactFlowJsonObject } from '@xyflow/react'; import { type WorkflowAPISchema } from '@/services/http'; -import type { LogStatus } from '../../config'; +import { type FlowNodeTraceInfo } from '@/services/http/workflow'; /** * Action Log Type */ -export interface AccordionLog { +export interface AccordionLog + extends ObjectToCamelCase< + Pick + > { /** * Unique ID of web usage */ @@ -22,22 +25,6 @@ export interface AccordionLog { * Custom header render config */ config?: CustomConfigItemType; - /** - * Node status - */ - status: LogStatus; - /** - * Node time cost - */ - timeCost: number; - /** - * TODO Input - */ - input?: Record; - /** - * TODO Output - */ - output?: Record; /** * error message */ @@ -67,7 +54,7 @@ export type CustomConfigItemType = { }; /** Workflow Trace Type */ -export type WorkflowTraceType = WorkflowAPISchema['getLogDetail']['response'][number]; +export type WorkflowTraceType = WorkflowAPISchema['getLogDetail']['response']['trace_info'][number]; /** Workflow Data Type */ export type WorkflowDataType = ReactFlowJsonObject; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx index 55defc7c..b6fd75cc 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/index.tsx @@ -26,14 +26,6 @@ export default React.memo(({ data, isActive, onClick }: IProps) => {

- {/*

- -

*/}
); diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx index 2567e5c7..c273cec9 100644 --- a/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx @@ -30,7 +30,7 @@ export const useRenderList = ({ getLogList, containerRef, listRef }: IProps) => const { start_time: startTime, status, id } = item || {}; return { id, - status: status === 'ERROR' ? 'failed' : 'success', + status, title: startTime ? getTimeFormat(startTime, 'fullDateTimeSecondFormat') : '', }; }); diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx index 16eff9c4..bea54f7b 100644 --- a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx @@ -7,8 +7,9 @@ export const useSourceData = () => { const generateList = useCallback((limit: number): LogListPageType['content'] => { return Array.from({ length: limit }).map(() => ({ id: generateUUID(), - status: Math.random() > 0.5 ? 'SUCCESS' : 'ERROR', + status: Math.random() > 0.5 ? 'Success' : 'Error', start_time: Date.now(), + time_cost: Math.floor(Math.random() * 1000), })); }, []); // TODO diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts index 8f4eb8e8..696a0328 100644 --- a/apps/web/src/pages/workflow/components/log-modal/types.d.ts +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -1,5 +1,5 @@ import { type WorkflowAPISchema } from '@/services/http'; -import type { LogStatus } from '../../config'; +import type { FlowRunningStatus } from '@/services/http/workflow'; export interface LogItemProps { /** @@ -9,7 +9,7 @@ export interface LogItemProps { /** * Node status */ - status: LogStatus; + status: FlowRunningStatus; /** * Title */ diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index b4802190..45276744 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -13,6 +13,7 @@ import { ErrorIcon, // FlagIcon, } from '@milesight/shared/src/components'; +import type { FlowRunningStatus } from '@/services/http/workflow'; type NodeCategoryConfigItemType = { /** Node Category i18n key */ @@ -177,17 +178,17 @@ export const basicNodeConfigs: Record = { }, }; -export type LogStatus = 'success' | 'failed'; /** * Status Render Map */ -export const LogStatusMap: Record = { - success: { - className: 'ms-log-status__success', - icon: , - }, - failed: { - className: 'ms-log-status__error', - icon: , - }, -}; +export const LogStatusMap: Record = + { + Success: { + className: 'ms-log-status__success', + icon: , + }, + Error: { + className: 'ms-log-status__error', + icon: , + }, + }; From c4b3acf2419e3fe66d34b103dc1629649e027a08 Mon Sep 17 00:00:00 2001 From: wuzb Date: Mon, 23 Dec 2024 17:43:19 +0800 Subject: [PATCH 077/172] feat: workflow email notify node remove gmail temporary --- .../components/email-content/index.tsx | 2 +- .../components/email-send-source/index.tsx | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx index 035df81e..e2bb234a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-content/index.tsx @@ -29,7 +29,7 @@ const EmailContent: React.FC = props => {
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx index e5ba95c3..644c26a4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/email-send-source/index.tsx @@ -19,10 +19,11 @@ export const EmailTypeOptions = [ label: 'SMTP', value: EMAIL_TYPE.SMTP, }, - { - label: 'Gmail', - value: EMAIL_TYPE.GMAIL, - }, + /** Temporary remove Gmail */ + // { + // label: 'Gmail', + // value: EMAIL_TYPE.GMAIL, + // }, ]; export interface EmailConfigProps { @@ -48,7 +49,10 @@ const EmailSendSource: React.FC = props => { const { getIntlText } = useI18n(); const [state, setState] = useControllableValue({ - value: value || {}, + value: value || { + provider: EMAIL_TYPE.SMTP, + smtpConfig: defaultSmtpValue, + }, onChange, }); From b0f1766cf39d5d5f3553d9292a36c6cc48122dae Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 23 Dec 2024 17:45:07 +0800 Subject: [PATCH 078/172] fix: rename the export type in workflow api service --- apps/web/src/services/http/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/services/http/index.ts b/apps/web/src/services/http/index.ts index 62fa5928..32a1d8b9 100644 --- a/apps/web/src/services/http/index.ts +++ b/apps/web/src/services/http/index.ts @@ -6,4 +6,4 @@ export { default as entityAPI, type EntityAPISchema } from './entity'; export { default as integrationAPI, type IntegrationAPISchema } from './integration'; export { default as globalAPI, type GlobalAPISchema } from './global'; export { default as dashboardAPI, type DashboardAPISchema } from './dashboard'; -export { default as workflowAPI, type WorkflowStatus, type WorkflowAPISchema } from './workflow'; +export { default as workflowAPI, type FlowStatus, type WorkflowAPISchema } from './workflow'; From 853bf2d502681efecd7bfbbdec598b2e148dd32a Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 23 Dec 2024 19:48:06 +0800 Subject: [PATCH 079/172] feat: modify workflow data based on api service --- .../components/confirm/confirm-provider.tsx | 2 +- apps/web/src/components/confirm/types.ts | 2 +- .../edit-modal/hook/useWorkflowFormItems.tsx | 2 +- .../components/conditions-input/index.tsx | 8 +- .../config-panel/hooks/useCommonFormItems.tsx | 4 +- .../config-panel/hooks/useNodeFormItems.tsx | 2 +- .../editor/components/config-panel/index.tsx | 9 +- .../views/editor/components/edge/style.less | 8 + .../editor/components/ifelse-node/index.tsx | 41 +++-- .../workflow/views/editor/components/index.ts | 2 +- .../editor/components/log-panel/index.tsx | 4 +- .../components/node-container/index.tsx | 9 +- .../components/node-container/style.less | 99 +++++++++++ .../editor/components/test-button/index.tsx | 16 +- .../components/test-button/log-list.tsx | 4 +- .../views/editor/components/topbar/index.tsx | 111 ++++++------ .../pages/workflow/views/editor/constants.ts | 5 + .../views/editor/hooks/useInteractions.tsx | 20 ++- .../views/editor/hooks/useWorkflow.ts | 10 +- .../src/pages/workflow/views/editor/index.tsx | 163 ++++++++++++------ .../views/editor/{log-store.ts => store.ts} | 44 +++-- .../pages/workflow/views/editor/style.less | 96 ----------- packages/locales/src/lang/en/global.json | 3 +- packages/locales/src/lang/en/workflow.json | 5 +- packages/shared/types/workflow.d.ts | 6 +- 25 files changed, 399 insertions(+), 276 deletions(-) rename apps/web/src/pages/workflow/views/editor/{log-store.ts => store.ts} (52%) diff --git a/apps/web/src/components/confirm/confirm-provider.tsx b/apps/web/src/components/confirm/confirm-provider.tsx index 3b11496f..7fcc173c 100644 --- a/apps/web/src/components/confirm/confirm-provider.tsx +++ b/apps/web/src/components/confirm/confirm-provider.tsx @@ -27,7 +27,7 @@ export const ConfirmProvider: React.FC = ({ children, ...globalOptions }) const confirm = React.useCallback( (confirmOptions?: ConfirmOptions) => { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const finalOptions = handleOverrideOptions(globalOptions, confirmOptions); setFinalOptions(finalOptions); setPromise({ resolve, reject }); diff --git a/apps/web/src/components/confirm/types.ts b/apps/web/src/components/confirm/types.ts index 100f631d..cd32b803 100644 --- a/apps/web/src/components/confirm/types.ts +++ b/apps/web/src/components/confirm/types.ts @@ -55,7 +55,7 @@ export type ConfirmOptions = GlobalOptions & { export type FinalOptions = Partial; -export type HandleConfirm = (options?: ConfirmOptions) => void; +export type HandleConfirm = (options?: ConfirmOptions) => void | Promise; export type DialogProps = { show: boolean; diff --git a/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx index 2fd911d5..1a5cb8fa 100644 --- a/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx +++ b/apps/web/src/pages/workflow/components/edit-modal/hook/useWorkflowFormItems.tsx @@ -8,7 +8,7 @@ import { checkRequired, checkMaxLength } from '@milesight/shared/src/utils/valid * type of dataSource */ export type FormDataProps = { - name: string; + name?: string; remark?: string; }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 81017f08..b303f25e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -1,6 +1,6 @@ -import React, { useLayoutEffect, useRef } from 'react'; +import React, { useLayoutEffect } from 'react'; import cls from 'classnames'; -import { isEqual, cloneDeep, merge, isString } from 'lodash-es'; +import { isEqual, cloneDeep, merge } from 'lodash-es'; import { useDynamicList, useControllableValue } from 'ahooks'; import { ToggleButtonGroup, @@ -26,7 +26,7 @@ import { logicOperatorMap, conditionOperatorMap } from '../../../../constants'; import ParamSelect from '../param-select'; import './style.less'; -export type ConditionsInputValueType = NonNullable['settings']; +export type ConditionsInputValueType = NonNullable['choice']; type ConditionBlockValueType = ConditionsInputValueType['when'][number]; @@ -378,4 +378,4 @@ const ConditionsInput: React.FC = props => { ); }; -export default ConditionsInput; +export default React.memo(ConditionsInput); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx index a9c0938a..f37bb562 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useCommonFormItems.tsx @@ -17,7 +17,7 @@ const useCommonFormItems = () => { result.push( { - name: 'name', + name: 'nodeName', rules: { validate: { checkRequired: checkRequired() }, }, @@ -38,7 +38,7 @@ const useCommonFormItems = () => { }, }, { - name: 'remark', + name: 'nodeRemark', defaultValue: '', render({ field: { onChange, value }, fieldState: { error } }) { return ( diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index b0de33a3..3054a07a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -85,7 +85,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { { children: [ { - name: 'settings', + name: 'choice', render({ field: { onChange, value } }) { return ; }, diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 416aba0c..a39643d2 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -69,8 +69,8 @@ const ConfigPanel = () => { formDataInit.current = false; return; } - const { name, remark, parameters } = selectedNode.data || {}; - const data: Record = { name, remark, ...parameters }; + const { nodeName, nodeRemark, parameters } = selectedNode.data || {}; + const data: Record = { nodeName, nodeRemark, ...parameters }; reset(); /** @@ -89,9 +89,10 @@ const ConfigPanel = () => { useDebounceEffect( () => { if (!openPanel || !formDataInit.current) return; - const { name, remark, ...formData } = latestFormData || {}; + const { nodeName, nodeRemark, ...formData } = latestFormData || {}; - updateNodeData(selectedNode.id, { name, remark, parameters: formData }); + // console.log({ formData }); + updateNodeData(selectedNode.id, { nodeName, nodeRemark, parameters: formData }); }, [openPanel, selectedNode, latestFormData, updateNodeData], { wait: 300 }, diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/style.less b/apps/web/src/pages/workflow/views/editor/components/edge/style.less index fae3c718..f7004211 100644 --- a/apps/web/src/pages/workflow/views/editor/components/edge/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/edge/style.less @@ -1,3 +1,11 @@ +.@{prefix}-workflow { + // ---------- Edge style variable override ---------- + --xy-edge-stroke: var(--gray-color-4); + --xy-edge-stroke-selected: var(--gray-color-5); + --xy-edge-stroke-width: 2; + --xy-connectionline-stroke-width: 2; +} + .@{prefix}-workflow-edge-label { display: flex; align-items: center; diff --git a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx index 982ecdbf..a2052b13 100644 --- a/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/ifelse-node/index.tsx @@ -41,7 +41,7 @@ const IfElseNode: React.FC> = props => { const { getIntlText } = useI18n(); // ---------- Render Handles ---------- - const { when, otherwise } = props.data.parameters?.settings || {}; + const { when, otherwise } = props.data.parameters?.choice || {}; const whenList = useMemo( () => when || @@ -125,26 +125,29 @@ const IfElseNode: React.FC> = props => { if (handleIds.includes(edge.sourceHandle!)) return true; return false; }); - const tempIfEdgeIndex = edges.findIndex( - edge => edge.source === nodeId && edge.sourceHandle === DEFAULT_IF_SOURCE_HANDLE_ID, - ); - const tempElseEdgeIndex = edges.findIndex( - edge => edge.source === nodeId && edge.sourceHandle === DEFAULT_ELSE_SOURCE_HANDLE_ID, - ); - if (tempIfEdgeIndex >= 0) { - edges[tempIfEdgeIndex] = { - ...edges[tempIfEdgeIndex], - sourceHandle: `${when[0].id || DEFAULT_IF_SOURCE_HANDLE_ID}`, - }; - } + edges.map(edge => { + let { sourceHandle } = edge; - if (tempElseEdgeIndex >= 0) { - edges[tempElseEdgeIndex] = { - ...edges[tempElseEdgeIndex], - sourceHandle: `${otherwise.id || DEFAULT_ELSE_SOURCE_HANDLE_ID}`, - }; - } + if (edge.source !== nodeId) return edge; + switch (edge.sourceHandle) { + case DEFAULT_IF_SOURCE_HANDLE_ID: { + sourceHandle = `${when[0].id || DEFAULT_IF_SOURCE_HANDLE_ID}`; + break; + } + case DEFAULT_ELSE_SOURCE_HANDLE_ID: { + sourceHandle = `${otherwise.id || DEFAULT_ELSE_SOURCE_HANDLE_ID}`; + break; + } + default: { + break; + } + } + + edge.sourceHandle = sourceHandle; + + return edge; + }); setEdges(edges); updateNodeInternals(nodeId); diff --git a/apps/web/src/pages/workflow/views/editor/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/index.ts index 719452e2..72402d08 100644 --- a/apps/web/src/pages/workflow/views/editor/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/index.ts @@ -1,4 +1,4 @@ -export { default as Topbar, type DesignMode } from './topbar'; +export { default as Topbar, type DesignMode, type TopbarProps } from './topbar'; export { default as Controls, type ControlsProps } from './controls'; export { default as ConfigPanel } from './config-panel'; diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index bca62241..8e652c38 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -6,7 +6,7 @@ import { useRequest } from 'ahooks'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; -import useLogStore from '../../log-store'; +import useFlowStore from '../../store'; import './style.less'; /** @@ -23,7 +23,7 @@ const LogPanel = () => { setOpenLogPanel, setLogDetail, setLogDetailLoading, - } = useLogStore( + } = useFlowStore( useStoreShallow([ 'openLogPanel', 'logPanelMode', diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index 9dcf1338..1ce24961 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -165,8 +165,7 @@ const NodeContainer: React.FC = ({ {handles?.map((handle, index) => {handle})}
@@ -195,7 +194,7 @@ const NodeContainer: React.FC = ({ {getIntlText('workflow.context_menu.title_change_node')} )} - {!entryNodeTypes.includes(nodeProps.type) && ( + {!entryNodeTypes.includes(nodeProps.type as WorkflowNodeType) && ( handleMenuItemClick(e, { type: 'delete' })}> {getIntlText('common.label.delete')} @@ -242,7 +241,9 @@ const NodeContainer: React.FC = ({ {title} {!!status && ( - {statusMap[status].icon} + + {statusMap[status].icon} + )}
{children &&
{children}
} diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/style.less b/apps/web/src/pages/workflow/views/editor/components/node-container/style.less index 3999b4f6..efa9c1b0 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/style.less @@ -1,3 +1,102 @@ +.@{prefix}-workflow { + // ---------- Node style variable override ---------- + --xy-node-border: none; + --xy-node-border-radius: @border-radius-sm; + --xy-node-boxshadow-selected: none; + + // ---------- Node Style ---------- + .react-flow__node { + width: 240px; + padding: 0; + border-radius: var(--xy-node-border-radius); + + &.selectable { + &:hover { + box-shadow: var(--xy-node-boxshadow-hover-default); + } + + &.selected { + .@{prefix}-workflow-node { + border: 1px solid var(--border-color-main); + box-shadow: 0 0 0 1px var(--border-color-main) inset; + + &.success, + &.error { + border-color: var(--border-color-main); + } + } + } + } + } + + &-node { + padding: (@padding-sm - 1) @padding-sm; + background-color: var(--component-background); + border: 1px solid var(--component-background); + border-radius: var(--xy-node-border-radius); + + &.success { + border-color: var(--border-color-green); + } + + &.error { + border-color: var(--border-color-red); + } + } + + &-node-header { + position: relative; + display: flex; + align-items: center; + + &:has(> .@{prefix}-workflow-node-status) { + padding-right: @padding-lg + @padding-xxs; + } + + .@{prefix}-workflow-node-icon { + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + margin-right: @margin-sm; + color: var(--white); + border-radius: @border-radius-sm; + + .@{mui-prefix}SvgIcon-root { + font-size: @font-size-lg; + } + } + + .@{prefix}-workflow-node-title { + .text-size(@font-size-base); + + font-weight: @font-weight-bold; + } + + .@{prefix}-workflow-node-status { + position: absolute; + top: 50%; + right: 0; + display: flex; + transform: translateY(-50%); + + &.error { + color: var(--text-color-error); + } + + &.success { + color: var(--text-color-success); + } + } + } + + &-node-body { + margin-top: @margin-xs; + } +} + +// ---------- Node ContextMenu ---------- .@{prefix}-workflow-node-contextmenu { .@{mui-prefix}Menu-list { width: 160px; diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx index d409b61b..8bab0fe6 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -7,7 +7,7 @@ import { PlayArrowIcon, HistoryIcon } from '@milesight/shared/src/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { TabPanel } from '@/components'; import LogList, { type LogType, type LogListProps } from './log-list'; -import useLogStore from '../../log-store'; +import useFlowStore from '../../store'; import './style.less'; export type TestButtonType = 'test' | 'history'; @@ -19,6 +19,7 @@ type LogTabItem = { }; interface Props { + disabled?: boolean; onClick?: () => void | Promise; } @@ -27,7 +28,7 @@ const DEFAULT_TAB_KEY: LogType = 'test'; /** * Test Button */ -const TestButton: React.FC = () => { +const TestButton: React.FC = ({ disabled }) => { const { getIntlText } = useI18n(); const nodes = useNodes(); const { @@ -39,7 +40,7 @@ const TestButton: React.FC = () => { setOpenLogPanel, setLogDetail, setLogDetailLoading, - } = useLogStore( + } = useFlowStore( useStoreShallow([ 'runLogs', 'testLogs', @@ -64,19 +65,20 @@ const TestButton: React.FC = () => { const log = { id: '1', start_time: Date.now(), - status: 'success', + time_cost: 500, + status: 'Success', }; const data = new Array(100).fill(0).map((_, index) => ({ ...log, id: index + 1, - status: Math.floor(Math.random() * 10) % 2 === 0 ? 'success' : 'error', + status: Math.floor(Math.random() * 10) % 2 === 0 ? 'Success' : 'Error', })); await new Promise(resolve => { setTimeout(resolve, 1000); }); - setRunLogs(data); + setRunLogs(data as LogListProps['data']); }, { manual: true, @@ -160,7 +162,7 @@ const TestButton: React.FC = () => { return ( <> - + -
- - setOpenEditModal(true)}> - - -
+ {(flowData?.name || loading === false) && ( +
+ + setOpenEditModal(true)}> + + +
+ )} = ({ ); }; -export default Topbar; +export default React.memo(Topbar); diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts index e34846c1..6d4ee82e 100644 --- a/apps/web/src/pages/workflow/views/editor/constants.ts +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -101,3 +101,8 @@ export const conditionOperatorMap: Partial< labelIntlKey: 'workflow.label.condition_operator_is_not_empty', }, }; + +/** + * The node property keys that cannot be modified ni advance mode + */ +export const FROZEN_NODE_PROPERTY_KEYS: readonly string[] = ['selected', 'dragging', 'measured']; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx index ed42b9ec..03d437eb 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx +++ b/apps/web/src/pages/workflow/views/editor/hooks/useInteractions.tsx @@ -8,6 +8,8 @@ import { } from '@xyflow/react'; import { useSize } from 'ahooks'; import { cloneDeep, maxBy } from 'lodash-es'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { useConfirm } from '@/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; import { genUuid } from '../helper'; import { @@ -60,6 +62,7 @@ const useInteractions = () => { } = useReactFlow(); const { checkParallelLimit, checkNestedParallelLimit } = useWorkflow(); const { width: bodyWidth, height: bodyHeight } = useSize(document.querySelector('body')) || {}; + const { getIntlText } = useI18n(); // Handle nodes connect const handleConnect = useCallback( @@ -75,6 +78,7 @@ const useInteractions = () => { ); // Check before node delete + const confirm = useConfirm(); const handleBeforeDelete = useCallback>( async ({ nodes }) => { const hasEntryNode = nodes.some( @@ -83,9 +87,23 @@ const useInteractions = () => { ); if (hasEntryNode) return false; + + if (nodes.length) { + let result = false; + await confirm({ + title: getIntlText('workflow.editor.node_delete_confirm_title'), + description: getIntlText('workflow.editor.node_delete_confirm_desc'), + onConfirm: async () => { + result = true; + }, + }); + + return result; + } + return true; }, - [], + [confirm, getIntlText], ); // Handle edge mouse enter diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 6e78af72..a1294196 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -112,7 +112,13 @@ const useWorkflow = () => { const { parallelList, hasAbnormalEdges } = getParallelInfo(nodes, edges, parentNodeId); // console.log({ parallelList, hasAbnormalEdges }); - if (hasAbnormalEdges) return false; + if (hasAbnormalEdges) { + toast.error({ + key: 'abnormal-edge', + content: getIntlText('workflow.label.abnormal_edge_tip'), + }); + return false; + } const isGtLimit = parallelList.some(item => item.depth > PARALLEL_DEPTH_LIMIT); @@ -212,7 +218,7 @@ const useWorkflow = () => { // TODO: get the correct nodes params const result: NodeParamType[] = incomeNodes.map(node => ({ nodeId: node.id, - nodeName: node.data?.name, + nodeName: node.data?.nodeName, nodeType: node.type as WorkflowNodeType, outputs: [ { diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index d5b061a9..a50564d3 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -11,14 +11,16 @@ import { useReactFlow, ReactFlowProvider, type NodeChange, + type EdgeChange, } from '@xyflow/react'; import { Button } from '@mui/material'; import { checkPrivateProperty } from '@milesight/shared/src/utils/tools'; -import { useI18n, useTheme } from '@milesight/shared/src/hooks'; -import { CheckIcon } from '@milesight/shared/src/components'; -import { CodeEditor } from '@/components'; +import { useI18n, useTheme, useStoreShallow, usePreventLeave } from '@milesight/shared/src/hooks'; +import { CheckIcon, toast } from '@milesight/shared/src/components'; +import { CodeEditor, useConfirm } from '@/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; -import { MIN_ZOOM, MAX_ZOOM } from './constants'; +import { MIN_ZOOM, MAX_ZOOM, FROZEN_NODE_PROPERTY_KEYS } from './constants'; +import useFlowStore from './store'; import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; import { Topbar, @@ -31,6 +33,7 @@ import { LogPanel, TestButton, type DesignMode, + type TopbarProps, } from './components'; import demoData from './demo-data.json'; @@ -76,42 +79,31 @@ const WorkflowEditor = () => { checkParallelLimit, ]); - // ---------- Fetch Data ---------- - const [searchParams] = useSearchParams(); - const wid = searchParams.get('wid'); - const { - loading, - data: flowData, - run: getFlowDesign, - } = useRequest( - async () => { - if (!wid) return; - // TODO: Call workflow detail API - // const [error, resp] = await awaitWrap(workflowAPI.getFlowDesign({ id: wid })); - - // if (error || !isRequestSuccess(resp)) return; - // const data = getResponseData(resp); - // console.log(data); - - await new Promise(resolve => { - setTimeout(resolve, 500); - }); - setNodes(demoData.nodes as WorkflowNode[]); - setEdges(demoData.edges as WorkflowEdge[]); + // ---------- Prevent Leave ---------- + const confirm = useConfirm(); + const [isPreventLeave, setIsPreventLeave] = useState(false); + const handleEdgesChange = useCallback( + (changes: EdgeChange[]) => { + if (changes.some(({ type }) => ['add', 'remove'].includes(type))) { + setIsPreventLeave(true); + } - return { id: 'xxx', name: 'Workflow Name', remark: 'Workflow Remark', enabled: false }; - }, - { - debounceWait: 300, - refreshDeps: [wid], + onEdgesChange(changes); }, + [onEdgesChange], ); + usePreventLeave({ isPreventLeave, confirm }); + // ---------- Show Helper Lines when node change ---------- const [helperLineHorizontal, setHelperLineHorizontal] = useState(undefined); const [helperLineVertical, setHelperLineVertical] = useState(undefined); const handleNodesChange = useCallback( (changes: NodeChange[]) => { + if (changes.some(({ type }) => ['add', 'remove', 'position'].includes(type))) { + setIsPreventLeave(true); + } + // reset the helper lines (clear existing lines, if any) setHelperLineHorizontal(undefined); setHelperLineVertical(undefined); @@ -139,6 +131,67 @@ const WorkflowEditor = () => { [nodes, onNodesChange], ); + // ---------- Fetch Nodes Config ---------- + const { setNodeConfigs } = useFlowStore(useStoreShallow(['setNodeConfigs'])); + const { loading: configLoading } = useRequest( + async () => { + const [error, resp] = await awaitWrap(workflowAPI.getFlowNodes()); + const data = getResponseData(resp); + + if (error || !data || !isRequestSuccess(resp)) return; + setNodeConfigs(data); + }, + { debounceWait: 300 }, + ); + + // ---------- Fetch Flow Data ---------- + const [searchParams] = useSearchParams(); + const wid = searchParams.get('wid'); + const [basicData, setBasicData] = useState(() => { + if (wid) return { id: wid }; + }); + const [flowDataLoading, setFlowDataLoading] = useState(); + const { + // loading, + // data: flowData, + run: getFlowDesign, + } = useRequest( + async () => { + if (!wid) return; + setFlowDataLoading(true); + // TODO: Call workflow detail API + // const [error, resp] = await awaitWrap(workflowAPI.getFlowDesign({ id: wid })); + + // if (error || !isRequestSuccess(resp)) return; + // const data = getResponseData(resp); + // console.log(data); + + await new Promise(resolve => { + setTimeout(resolve, 500); + }); + + setFlowDataLoading(false); + setNodes(demoData.nodes as WorkflowNode[]); + setEdges(demoData.edges as WorkflowEdge[]); + setBasicData({ + id: 'xxx', + name: 'Workflow Name', + remark: 'Workflow Remark', + enabled: false, + }); + + return { id: 'xxx', name: 'Workflow Name', remark: 'Workflow Remark', enabled: false }; + }, + { + debounceWait: 300, + refreshDeps: [wid], + }, + ); + const handleFlowDataChange = useCallback>(data => { + setBasicData(data); + setIsPreventLeave(true); + }, []); + // ---------- Design Mode Change ---------- const [designMode, setDesignMode] = useState('canvas'); const [editorFlowData, setEditorFlowData] = useState(); @@ -149,18 +202,26 @@ const WorkflowEditor = () => { if (mode === 'advanced') { const { nodes, edges } = toObject(); const newNodes = nodes.map(node => { - const data = omitBy(node, (_, key) => checkPrivateProperty(key)); - return data; + const result = omitBy(node, (_, key) => + FROZEN_NODE_PROPERTY_KEYS.includes(key), + ); + result.data = omitBy(node.data, (_, key) => checkPrivateProperty(key)); + return result; }); + setEditorFlowData(JSON.stringify({ nodes: newNodes, edges }, null, 2)); } else if (mode === 'canvas') { + let data: Pick; + // TODO: json validate, data validate - const { nodes, edges } = JSON.parse(editorFlowData || '{}') as Pick< - WorkflowSchema, - 'nodes' | 'edges' - >; + try { + data = JSON.parse(editorFlowData || '{}'); + } catch (e) { + toast.error({ content: getIntlText('common.message.json_format_error') }); + return; + } + const { nodes, edges } = data; - console.log(123123123, { editorFlowData, nodes }); setNodes(nodes); setEdges(edges); } @@ -170,7 +231,7 @@ const WorkflowEditor = () => { setDesignMode(mode); }, - [editorFlowData, toObject, setEdges, setNodes, checkWorkflowValid], + [editorFlowData, toObject, setEdges, setNodes, checkWorkflowValid, getIntlText], ); // ---------- Save Workflow ---------- @@ -186,11 +247,16 @@ const WorkflowEditor = () => { return (
, + , , + , ]} />
@@ -291,7 +342,7 @@ const WorkflowEditor = () => { onEdgeMouseEnter={handleEdgeMouseEnter} onEdgeMouseLeave={handleEdgeMouseLeave} > - + { /> - + {designMode === 'advanced' && (
diff --git a/apps/web/src/pages/workflow/views/editor/style.less b/apps/web/src/pages/workflow/views/editor/style.less index cf546449..b7358a31 100644 --- a/apps/web/src/pages/workflow/views/editor/style.less +++ b/apps/web/src/pages/workflow/views/editor/style.less @@ -1,6 +1,10 @@ .@{prefix}-view-wf_editor { position: relative; + .react-flow__background { + background-color: var(--component-background-gray); + } + .@{prefix}-workflow-advance { position: absolute; inset: 0; diff --git a/apps/web/src/pages/workflow/views/editor/typings.ts b/apps/web/src/pages/workflow/views/editor/typings.ts index 04c801dd..d3072738 100644 --- a/apps/web/src/pages/workflow/views/editor/typings.ts +++ b/apps/web/src/pages/workflow/views/editor/typings.ts @@ -1,6 +1,14 @@ import { type NodeConfigItemType } from '../../config'; -export type NodeFormItemValueType = 'string' | 'array' | 'object' | 'number' | 'boolean'; +export type NodeFormItemValueType = + | 'string' + | 'int' + | 'boolean' + | 'enum' + | 'array' + | 'map' + | 'duration' + | 'object'; export type NodeFormItemConfig = { name: string; @@ -11,10 +19,11 @@ export type NodeFormItemConfig = { defaultValue: string; description: string; index: number; - enum: string[]; - required: boolean; - secret: boolean; - autowired: boolean; + enum?: string[]; + required?: boolean; + secret?: boolean; + autowired?: boolean; + editable?: boolean; uiComponent?: string; // uiComponentTags: string; uiComponentGroup?: string; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 32d2b092..4b122185 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -184,6 +184,8 @@ export interface WorkflowAPISchema extends APISchema { name: string; /** Flow Remark */ remark?: string; + /** Enabled */ + enabled: boolean; /** Flow Design Data (JSON string) */ design_data: string; }; diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index ec5fb168..7e5c318d 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -167,5 +167,6 @@ "common.label.content": "Content", "common.label.input": "Input", "common.label.output": "Output", - "common.message.json_format_error": "JSON 格式错误" + "common.message.json_format_error": "JSON 格式错误", + "common.label.tip": "Tip" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index d6cbe85b..73133ceb 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -92,5 +92,7 @@ "workflow.editor.node_delete_confirm_desc": "确定删除此节点?", "workflow.email.label_content_recipients": "Recipients", "workflow.email.label_content_recipients_tip": "Multiple recipients need to use ';' separate", - "workflow.editor.form_logic_operator_switch_tip": "Click to switch to {1}" + "workflow.editor.form_logic_operator_switch_tip": "Click to switch to {1}", + "workflow.editor.editor_auto_create_service_entity_tip": "系统会自动根据工作流名称创建一个自定义服务实体,你可以在自定义实体列表中查看该实体。", + "workflow.label.node_min_number_limit_tip": "工作流中至少需要有 {1} 个节点" } diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index a8971d2b..43abb213 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -97,18 +97,30 @@ declare type TriggerNodeDataType = BaseNodeDataType<{ declare type TimePeriodType = | 'EVERYDAY' - | 'Monday' - | 'Tuesday' - | 'Wednesday' - | 'Thursday' - | 'Friday' - | 'Saturday' - | 'Sunday'; + | 'MONDAY' + | 'TUESDAY' + | 'WEDNESDAY' + | 'THURSDAY' + | 'FRIDAY' + | 'SATURDAY' + | 'SUNDAY'; /** * 定时器节点参数类型 */ declare type TimerNodeDataType = BaseNodeDataType<{ + timerSettings: { + type: 'ONCE' | 'SCHEDULE'; + timezone: string; + executionEpochSecond?: number; + rules?: { + hour?: number; + minute?: number; + daysOfWeek?: TimePeriodType[]; + }[]; + expirationEpochSecond?: number; + }; + /** * 执行类型 * @param ONCE 单次执行 From 1a9ef54fb074437f3edbe3bee7ca510c4c0ee85b Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 10:18:03 +0800 Subject: [PATCH 088/172] feat: add node data validators --- .../workflow/views/editor/demo-data.json | 4 +- .../views/editor/hooks/useValidate.ts | 260 ++++++++++++++++++ .../workflow/views/editor/hooks/utils.ts | 2 +- .../src/pages/workflow/views/editor/index.tsx | 6 +- packages/locales/src/lang/en/workflow.json | 7 +- packages/shared/src/utils/validators/index.ts | 2 + packages/shared/types/workflow.d.ts | 116 ++------ 7 files changed, 302 insertions(+), 95 deletions(-) create mode 100644 apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json index 5b4bfc44..ecfd76f0 100644 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -34,7 +34,7 @@ "y": 0 }, "type": "code", - "componentName": "simpleTimer" + "componentName": "code" }, { "id": "4", @@ -93,7 +93,7 @@ "y": 200 }, "type": "webhook", - "componentName": "simpleTimer" + "componentName": "webhook" } ], "edges": [ diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts new file mode 100644 index 00000000..5ed58202 --- /dev/null +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -0,0 +1,260 @@ +import { useMemo, useCallback } from 'react'; +import { useReactFlow } from '@xyflow/react'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { toast } from '@milesight/shared/src/components'; +import { + isEmpty, + isRangeValue, + isMinLength, + isMaxLength, + checkMaxLength, +} from '@milesight/shared/src/utils/validators'; +import useFlowStore from '../store'; + +type NodeDataValidator = (value?: T, fieldName?: string) => string | boolean | undefined; + +type CheckOptions = { + /** + * When a rule fails validation, should the validation of the remaining rules be stopped + */ + validateFirst?: boolean; +}; + +enum ErrorIntlKey { + required = 'workflow.valid.required', + rangeLength = 'workflow.valid.range_length', + minLength = 'workflow.valid.min_length', + maxLength = 'workflow.valid.max_length', +} + +const useValidate = () => { + const { getIntlText } = useI18n(); + const { getNodes, getEdges } = useReactFlow(); + const nodeConfigs = useFlowStore(state => state.nodeConfigs); + + const dataValidators = useMemo(() => { + const checkRequired: NodeDataValidator = (value?: any, fieldName?: string) => { + if (!isEmpty(value)) { + return true; + } + const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); + return message; + }; + const genMaxLengthValidator = (maxLength: number): NodeDataValidator => { + return (value, fieldName) => { + if (value && !isMaxLength(value, maxLength)) { + const message = getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + return message; + } + return true; + }; + }; + const result: Record> = { + nodeName: { + checkRequired, + checkRangeLength(value) { + if (!isRangeValue(value, 1, 50)) return true; + + const message = getIntlText(ErrorIntlKey.rangeLength, { + 1: getIntlText('common.label.name'), + }); + return message; + }, + }, + nodeRemark: { + checkRangeLength(value) { + if (!isEmpty(value) || !isRangeValue(value, 1, 1000)) return true; + + const message = getIntlText(ErrorIntlKey.rangeLength, { + 1: getIntlText('common.label.remark'), + }); + return message; + }, + }, + // Check listener.entities, select.entities + entities: { + checkRequired( + value: NonNullable['entities'], + fieldName?: string, + ) { + if (value?.length && value.some(item => !isEmpty(item))) { + return true; + } + const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); + return message; + }, + }, + // Check code.inputArguments and webhook.inputArguments + inputArguments: { + checkMaxLength() { + return true; + }, + }, + 'trigger.entityConfigs': { + checkRequired( + value?: NonNullable['entityConfigs'], + fieldName?: string, + ) { + if ( + value?.length && + value.every(item => Object.values(item).every(it => !!it)) + ) { + return true; + } + const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); + return message; + }, + // checkType(value, fieldName) { + // return true; + // }, + }, + 'timer.timerSettings': { + checkRequired( + value?: NonNullable['timerSettings'], + fieldName?: string, + ) { + switch (value?.type) { + case 'ONCE': + if (value.timezone && value.executionEpochSecond) { + return true; + } + break; + case 'SCHEDULE': + if ( + value.timezone && + value.expirationEpochSecond && + value.rules?.length && + value.rules.every( + rule => + !isEmpty(rule.hour) && + !isEmpty(rule.minute) && + rule.daysOfWeek?.length, + ) + ) { + return true; + } + break; + default: + break; + } + + const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); + return message; + }, + }, + 'ifelse.choice': {}, + 'code.expression': { + checkRequired, + }, + 'code.Payload': {}, + 'service.serviceInvocationSetting': {}, + 'assigner.exchangePayload': {}, + 'email.emailConfig': { + checkRequired(value: NonNullable, fieldName) { + return true; + }, + checkMaxLength(value: NonNullable, fieldName) { + return true; + }, + }, + 'email.subject': { + checkRequired, + checkMaxLength: genMaxLengthValidator(500), + }, + 'email.recipients': { + checkRequired, + }, + 'email.content': { + checkRequired, + checkMaxLength: genMaxLengthValidator(5000), + }, + 'webhook.webhookUrl': { + checkRequired, + }, + 'webhook.secretKey': { + // checkRequired, + }, + }; + return result; + }, [getIntlText]); + + const checkNodeId = () => {}; + + const checkNodeType = () => {}; + + const checkEdgeType = () => {}; + + const checkNodesData = useCallback( + (nodes?: WorkflowNode[], options?: CheckOptions) => { + nodes = nodes || getNodes(); + const result: Record = {}; + + for (let i = 0; i < nodes.length; i++) { + const { id, type, data } = nodes[i]; + const config = nodeConfigs[type as WorkflowNodeType]; + const { nodeName, nodeRemark, parameters } = data || {}; + let tempResult = result[id]; + + if (options?.validateFirst && tempResult?.errMsgs.length) { + toast.error({ key: 'node-validate', content: tempResult.errMsgs[0] }); + return result; + } + + if (!tempResult) { + tempResult = { type: type as WorkflowNodeType, errMsgs: [] }; + result[id] = tempResult; + } + + Object.values(dataValidators.nodeName).forEach(validator => { + const result = validator(nodeName, getIntlText('common.label.name')); + if (result && result !== true) { + tempResult.errMsgs.push(result); + } + }); + + Object.values(dataValidators.nodeRemark).forEach(validator => { + const result = validator(nodeRemark, getIntlText('common.label.remark')); + if (result && result !== true) { + tempResult.errMsgs.push(result); + } + }); + + if (!parameters || !Object.keys(parameters).length) { + tempResult.errMsgs.push( + getIntlText('workflow.valid.parameter_required', { + 1: getIntlText(config.labelIntlKey), + }), + ); + return; + } + + Object.entries(parameters).forEach(([key, value]) => { + const validKey = `${type}.${key}`; + const validators = dataValidators[validKey] || dataValidators[key] || {}; + + Object.values(validators).forEach(validator => { + const result = validator(value, key); + if (result && result !== true) { + tempResult.errMsgs.push(result); + } + }); + }); + } + + return Object.values(result).some(item => item.errMsgs.length) ? result : true; + }, + [dataValidators, getIntlText, getNodes, nodeConfigs], + ); + + return { + checkNodeId, + checkNodeType, + checkEdgeType, + checkNodesData, + }; +}; + +export default useValidate; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/utils.ts b/apps/web/src/pages/workflow/views/editor/hooks/utils.ts index f02a8fad..9e44122f 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/utils.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/utils.ts @@ -44,7 +44,7 @@ export const getParallelInfo = ( startNode = nodes.find(node => node.id === (parentNode.data as any).start_node_id); } else { - startNode = nodes.find(node => entryNodeTypes.includes(node.type!)); + startNode = nodes.find(node => entryNodeTypes.includes(node.type as WorkflowNodeType)); } if (!startNode) throw new Error('Start node not found'); diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 2c72635f..a49c6601 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -207,8 +207,12 @@ const WorkflowEditor = () => { result.data = omitBy(node.data, (_, key) => checkPrivateProperty(key)); return result; }); + const newEdges = edges.map(edge => { + edge.data = omitBy(edge.data, (_, key) => checkPrivateProperty(key)); + return edge; + }); - setEditorFlowData(JSON.stringify({ nodes: newNodes, edges }, null, 2)); + setEditorFlowData(JSON.stringify({ nodes: newNodes, edges: newEdges }, null, 2)); } else if (mode === 'canvas') { let data: Pick; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 73133ceb..4742bf1d 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -94,5 +94,10 @@ "workflow.email.label_content_recipients_tip": "Multiple recipients need to use ';' separate", "workflow.editor.form_logic_operator_switch_tip": "Click to switch to {1}", "workflow.editor.editor_auto_create_service_entity_tip": "系统会自动根据工作流名称创建一个自定义服务实体,你可以在自定义实体列表中查看该实体。", - "workflow.label.node_min_number_limit_tip": "工作流中至少需要有 {1} 个节点" + "workflow.label.node_min_number_limit_tip": "工作流中至少需要有 {1} 个节点", + "workflow.valid.required": "The {1} field is required.", + "workflow.valid.range_length": "The length of the {1} field value should be greater than or equal to {0} and less than or equal to {1}.", + "workflow.valid.node_parameters_required": "The {1} node parameters is required.", + "workflow.valid.min_length": "The {1} field length of the value you entered should be more than or equal to {0}.", + "workflow.valid.max_length": "The {1} field length of the value you entered cannot exceed {2} digits." } diff --git a/packages/shared/src/utils/validators/index.ts b/packages/shared/src/utils/validators/index.ts index 8ce97b6e..faac338f 100644 --- a/packages/shared/src/utils/validators/index.ts +++ b/packages/shared/src/utils/validators/index.ts @@ -20,8 +20,10 @@ import { } from './validator'; import type { Validate, TValidator } from './typings'; +export * from './asserts'; // 导出所有的单条 validator export * from './validator'; +export { getErrorMessage, EErrorMessages }; export type { Validate, TValidator }; export type TChecker = () => Record>; diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index 43abb213..bd6b9759 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -89,9 +89,8 @@ declare type BaseNodeDataType = Record; @@ -120,48 +119,13 @@ declare type TimerNodeDataType = BaseNodeDataType<{ }[]; expirationEpochSecond?: number; }; - - /** - * 执行类型 - * @param ONCE 单次执行 - * @param CYCLE 周期执行 - */ - type: 'ONCE' | 'CYCLE'; - /** 执行时间 */ - executionTime?: number; - /** 过期时间,默认 2035/01/01 00:00 */ - expireTime?: number; - /** 周期配置 */ - settings?: { - /** 执行周期 */ - period?: TimePeriodType; - /** - * 执行时间,该数据为零点到所选时间点的毫秒数,默认 32400000(09:00) - */ - time?: number; - }[]; }>; /** * 事件监听节点参数类型 */ declare type ListenerNodeDataType = BaseNodeDataType<{ - /** - * 监听类型 - * @param change 实体数据变更 - * @param call 服务调用 - * @param report 事件上报 - */ - type: 'change' | 'call' | 'report'; - /** 监听目标 */ - target: ApiKey; - /** 输出参数 */ - // outputs?: { - // key: ApiKey; - // name: string; - // type: EntityType; - // value: any; - // }[]; + entities: ApiKey[]; }>; declare type WorkflowLogicOperator = 'AND' | 'OR'; @@ -232,9 +196,9 @@ declare type CodeNodeDataType = BaseNodeDataType<{ /** 代码内容 */ expression: string; /** 输入参数 */ - inputArguments: Record; + inputArguments: { key: string; value: string }[]; /** 输出参数 */ - outputArguments: { + Payload: { name: ApiKey; type: EntityValueDataType; }[]; @@ -244,20 +208,12 @@ declare type CodeNodeDataType = BaseNodeDataType<{ * 服务节点参数类型 */ declare type ServiceNodeDataType = BaseNodeDataType<{ - /** 服务实体 Key */ + /** Service Entity Key */ serviceEntity: ApiKey; /** - * 输入参数 - * - * TODO: 该参数后端设计与需求输入限制表不符,待确认 + * Input variables of service entity */ serviceParams: Record; - // inputs: { - // name: ApiKey; - // type: EntityValueDataType; - // value: any; - // source: ApiKey; - // }[]; }>; /** @@ -271,61 +227,41 @@ declare type AssignerNodeDataType = BaseNodeDataType<{ * 实体选择节点参数类型 */ declare type SelectNodeDataType = BaseNodeDataType<{ - settings: { - /** - * 监听类型 - * @param change 实体数据变更 - * @param call 服务调用 - * @param report 事件上报 - */ - type: 'change' | 'call' | 'report'; - /** 监听目标 */ - target: ApiKey; - }[]; + entities: ApiKey[]; }>; /** * 邮件节点参数类型 */ declare type EmailNodeDataType = BaseNodeDataType & { - /** 邮箱类型 */ - type: 'gmail'; - /** 邮箱 API Key */ - apiKey: ApiKey; - /** 邮箱 */ - email: string | string[]; - /** 邮件内容 */ + emailConfig: { + provider: 'SMTP' | 'google'; + smtpConfig: { + host: string; + port: number; + encryption: 'STARTTLS' | 'NONE'; + username: string; + password: string; + }; + }; + subject: string; + recipients: string[]; content: string; - /** 输出参数 */ - // outputs: { - // name: ApiKey; - // type: EntityValueDataType; - // value: any; - // }[]; }; /** * Webhook 节点参数类型 */ declare type WebhookNodeDataType = BaseNodeDataType & { - /** 推送数据(来源于上个节点) */ - data: ApiKey[]; - /** 自定义数据 */ - customData: { + /** Custom Data */ + inputArguments?: { key: ApiKey; - type: EntityValueDataType; - value: any; + value: string; }[]; /** Webhook URL */ - url: string; - /** Webhook 密钥 */ - secret: string; - /** 输出参数 */ - outputs: { - name: ApiKey; - type: EntityValueDataType; - value: any; - }[]; + webhookUrl: string; + /** Webhook Secret */ + secretKey?: string; }; /** From 94ce77fb4d5c5aaebbf1b63c84f940d1c3f9f964 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 26 Dec 2024 10:56:44 +0800 Subject: [PATCH 089/172] feat: Code nodes for workflows adjust editor data types --- .../web/src/components/code-editor/types.d.ts | 2 +- .../components/code-editor/index.tsx | 52 ++++++++++++++++--- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/web/src/components/code-editor/types.d.ts b/apps/web/src/components/code-editor/types.d.ts index 583c5990..87fc9762 100644 --- a/apps/web/src/components/code-editor/types.d.ts +++ b/apps/web/src/components/code-editor/types.d.ts @@ -37,7 +37,7 @@ export interface EditorProps * Callback function triggered when the language changes. * @param value - The new language value. */ - onLangChange?: (value: string) => void; + onLangChange?: (value: EditorSupportLang) => void; /** default value */ defaultValue?: string; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx index e5b45ebd..3e7d631f 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -1,19 +1,59 @@ -import React from 'react'; -import { CodeEditor as CodeMirror, type EditorProps } from '@/components'; +import React, { useCallback } from 'react'; +import { CodeEditor as CodeMirror, type EditorSupportLang } from '@/components'; import './style.less'; +interface CodeEditorData { + language: EditorSupportLang; + expression: string; +} +export interface IProps { + value: CodeEditorData; + onChange: (value: CodeEditorData) => void; +} +const DEFAULT_LANGUAGE = 'javascript'; /** * Code Editor Component * * Note: Use in CodeNode, IfelseNode */ -const CodeEditor: React.FC = ({ value, onChange }) => { +const CodeEditor: React.FC = ({ value, onChange }) => { + const { language = DEFAULT_LANGUAGE, expression } = value || {}; + + /** Actual form change callbacks */ + const handleChange = useCallback( + (data: Partial) => { + const { language = DEFAULT_LANGUAGE, expression = '' } = data; + + const result = { + language: language || value?.language, + expression: expression || value?.expression, + }; + onChange?.(result); + }, + [value, onChange], + ); + + /** Callback function triggered when the language changes. */ + const handleEditorLangChange = useCallback( + (language: EditorSupportLang) => { + handleChange?.({ language }); + }, + [handleChange], + ); + /** Callback function triggered when the content value changes. */ + const handleEditorValueChange = useCallback( + (expression: string) => { + handleChange?.({ expression }); + }, + [handleChange], + ); return ( ); }; From 2ab019aaf6174d97be9aae00b4540dfbc12b1960 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 14:09:30 +0800 Subject: [PATCH 090/172] feat: modify the prop type of ActionLog component --- .../components/action-log/hooks/useNestedData.tsx | 6 +++--- .../pages/workflow/components/action-log/types.d.ts | 10 ++++++++-- apps/web/src/pages/workflow/components/index.ts | 1 + 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx index 40a23ac6..1f192d85 100644 --- a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -31,15 +31,15 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { const wrapperNode = useMemoizedFn((node: WorkflowNode): WorkflowNestNode => { const nestNode = cloneDeep(node) as WorkflowNestNode; const { id, type, data } = nestNode || {}; - const { name } = data || {}; + const { nodeName } = data || {}; const { status, input, output, timeCost } = objectToCamelCase(traceMap[id] || {}); nestNode.attrs = { $$token: generateUUID(), - name: name || '', + name: nodeName || '', type: type!, status, - timeCost, + timeCost: timeCost!, input, output, }; diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index 981be208..322a763c 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -54,10 +54,16 @@ export type CustomConfigItemType = { }; /** Workflow Trace Type */ -export type WorkflowTraceType = WorkflowAPISchema['getLogDetail']['response']['trace_info'][number]; +export type WorkflowTraceType = PartialOptional< + WorkflowAPISchema['getLogDetail']['response']['trace_info'][number], + 'start_time' | 'time_cost' +>; /** Workflow Data Type */ -export type WorkflowDataType = ReactFlowJsonObject; +export type WorkflowDataType = PartialOptional< + ReactFlowJsonObject, + 'viewport' +>; /** Workflow Custom Node Type */ export type WorkflowNestNode = WorkflowNode & { diff --git a/apps/web/src/pages/workflow/components/index.ts b/apps/web/src/pages/workflow/components/index.ts index 83e1e198..57df9468 100644 --- a/apps/web/src/pages/workflow/components/index.ts +++ b/apps/web/src/pages/workflow/components/index.ts @@ -1,3 +1,4 @@ export { default as EditModal, type Props as EditModalProps } from './edit-modal'; export { default as ImportModal } from './import-modal'; export { default as LogModal } from './log-modal'; +export { default as ActionLog } from './action-log'; From 4f139711ff333348fa3daa5aaef19a29d8d808fa Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 14:10:34 +0800 Subject: [PATCH 091/172] feat: add node data check --- apps/web/src/pages/workflow/config.tsx | 25 +++--- .../editor/components/log-panel/index.tsx | 13 ++- .../components/node-container/index.tsx | 16 ++-- .../workflow/views/editor/hooks/index.ts | 5 ++ .../views/editor/hooks/useValidate.ts | 80 +++++++++++++++---- .../views/editor/hooks/useWorkflow.ts | 32 +++++++- .../src/pages/workflow/views/editor/index.tsx | 45 +++++++++-- .../src/pages/workflow/views/editor/store.ts | 32 ++++++-- apps/web/src/services/http/index.ts | 7 +- packages/locales/src/lang/en/workflow.json | 3 +- packages/shared/types/workflow.d.ts | 10 +-- 11 files changed, 213 insertions(+), 55 deletions(-) diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index be039c15..1f5666cf 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -13,7 +13,6 @@ import { ErrorIcon, // FlagIcon, } from '@milesight/shared/src/components'; -import type { FlowRunningStatus } from '@/services/http/workflow'; type NodeCategoryConfigItemType = { /** Node Category i18n key */ @@ -175,14 +174,16 @@ export const basicNodeConfigs: Record = { /** * Status Render Map */ -export const LogStatusMap: Record = - { - Success: { - className: 'ms-log-status__success', - icon: , - }, - Error: { - className: 'ms-log-status__error', - icon: , - }, - }; +export const LogStatusMap: Record< + WorkflowNodeStatus, + { className: string; icon: React.ReactNode } +> = { + SUCCESS: { + className: 'ms-log-status__success', + icon: , + }, + ERROR: { + className: 'ms-log-status__error', + icon: , + }, +}; diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index 8e652c38..6e4313c4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -5,7 +5,9 @@ import cls from 'classnames'; import { useRequest } from 'ahooks'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; +import { ActionLog } from '@/pages/workflow/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import useWorkflow from '../../hooks/useWorkflow'; import useFlowStore from '../../store'; import './style.less'; @@ -14,6 +16,8 @@ import './style.less'; */ const LogPanel = () => { const { getIntlText } = useI18n(); + const nodes = useNodes(); + const { toObject } = useReactFlow(); const { openLogPanel, logPanelMode, @@ -35,8 +39,7 @@ const LogPanel = () => { 'setLogDetailLoading', ]), ); - const nodes = useNodes(); - const { toObject } = useReactFlow(); + const { updateNodesStatus } = useWorkflow(); const title = useMemo(() => { switch (logPanelMode) { case 'testRun': @@ -55,6 +58,7 @@ const LogPanel = () => { const handleClose = () => { // TODO: remove node `$status` prop and close the panel setOpenLogPanel(false); + updateNodesStatus(null); }; // ---------- Run Test ---------- @@ -92,6 +96,7 @@ const LogPanel = () => { runFlowTest(); }, [logPanelMode, showTestInput, runFlowTest]); + console.log({ logDetail }); return ( {
)}
- Render LogDetail... + {!!logDetail?.length && ( + + )}
diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index 33be38ef..d6a6b540 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -50,18 +50,18 @@ export type NodeContainerProps = { * Node Status Map */ const statusMap: Record< - WorkflowNodeStatus, + WorkflowNodeStatus | 'LOADING', { icon: React.ReactNode; } > = { - error: { + ERROR: { icon: , }, - success: { + SUCCESS: { icon: , }, - loading: { + LOADING: { icon: , }, }; @@ -206,7 +206,7 @@ const NodeContainer: React.FC = ({ {handles?.map((handle, index) => {handle})}
@@ -282,7 +282,11 @@ const NodeContainer: React.FC = ({ {title} {!!status && ( - + {statusMap[status].icon} )} diff --git a/apps/web/src/pages/workflow/views/editor/hooks/index.ts b/apps/web/src/pages/workflow/views/editor/hooks/index.ts index a8392564..0b70b853 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/index.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/index.ts @@ -1,3 +1,8 @@ export { default as useNodeTypes } from './useNodeTypes'; export { default as useInteractions } from './useInteractions'; export { default as useWorkflow } from './useWorkflow'; +export { + default as useValidate, + NODE_VALIDATE_TOAST_KEY, + type NodesDataValidResult, +} from './useValidate'; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 5ed58202..3f38f121 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -7,7 +7,7 @@ import { isRangeValue, isMinLength, isMaxLength, - checkMaxLength, + isURL, } from '@milesight/shared/src/utils/validators'; import useFlowStore from '../store'; @@ -20,6 +20,17 @@ type CheckOptions = { validateFirst?: boolean; }; +export type NodesDataValidResult = Record< + string, + { + type: WorkflowNodeType; + label?: string; + name?: string; + status: WorkflowNodeStatus; + errMsgs: string[]; + } +>; + enum ErrorIntlKey { required = 'workflow.valid.required', rangeLength = 'workflow.valid.range_length', @@ -27,6 +38,8 @@ enum ErrorIntlKey { maxLength = 'workflow.valid.max_length', } +export const NODE_VALIDATE_TOAST_KEY = 'node-validate'; + const useValidate = () => { const { getIntlText } = useI18n(); const { getNodes, getEdges } = useReactFlow(); @@ -89,7 +102,10 @@ const useValidate = () => { }, // Check code.inputArguments and webhook.inputArguments inputArguments: { - checkMaxLength() { + checkMaxLength( + value: NonNullable['inputArguments'], + fieldName, + ) { return true; }, }, @@ -145,18 +161,38 @@ const useValidate = () => { return message; }, }, - 'ifelse.choice': {}, + 'ifelse.choice': { + checkRequired( + value: NonNullable['choice'], + fieldName, + ) { + return true; + }, + }, 'code.expression': { - checkRequired, + // checkRequired() {}, + }, + 'code.Payload': { + checkMaxLength( + value: NonNullable['Payload'], + fieldName, + ) { + return true; + }, }, - 'code.Payload': {}, 'service.serviceInvocationSetting': {}, 'assigner.exchangePayload': {}, 'email.emailConfig': { - checkRequired(value: NonNullable, fieldName) { + checkRequired( + value: NonNullable['emailConfig'], + fieldName, + ) { return true; }, - checkMaxLength(value: NonNullable, fieldName) { + checkMaxLength( + value: NonNullable['emailConfig'], + fieldName, + ) { return true; }, }, @@ -173,10 +209,11 @@ const useValidate = () => { }, 'webhook.webhookUrl': { checkRequired, + checkUrl(value: string, fieldName) { + return true; + }, }, - 'webhook.secretKey': { - // checkRequired, - }, + // 'webhook.secretKey': {}, }; return result; }, [getIntlText]); @@ -190,7 +227,7 @@ const useValidate = () => { const checkNodesData = useCallback( (nodes?: WorkflowNode[], options?: CheckOptions) => { nodes = nodes || getNodes(); - const result: Record = {}; + const result: NodesDataValidResult = {}; for (let i = 0; i < nodes.length; i++) { const { id, type, data } = nodes[i]; @@ -204,7 +241,13 @@ const useValidate = () => { } if (!tempResult) { - tempResult = { type: type as WorkflowNodeType, errMsgs: [] }; + tempResult = { + type: type as WorkflowNodeType, + label: getIntlText(config.labelIntlKey), + name: nodeName, + status: 'SUCCESS', + errMsgs: [], + }; result[id] = tempResult; } @@ -222,13 +265,14 @@ const useValidate = () => { } }); + // console.log({ id, type, parameters }); if (!parameters || !Object.keys(parameters).length) { tempResult.errMsgs.push( getIntlText('workflow.valid.parameter_required', { - 1: getIntlText(config.labelIntlKey), + 1: `${getIntlText(config.labelIntlKey)} (ID: ${id})`, }), ); - return; + continue; } Object.entries(parameters).forEach(([key, value]) => { @@ -244,6 +288,14 @@ const useValidate = () => { }); } + Object.entries(result).forEach(([id, data]) => { + if (!data.errMsgs.length) { + delete result[id]; + } else { + data.status = 'ERROR'; + } + }); + return Object.values(result).some(item => item.errMsgs.length) ? result : true; }, [dataValidators, getIntlText, getNodes, nodeConfigs], diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 97cb1810..f92a344a 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -7,7 +7,7 @@ import { getOutgoers, type IsValidConnection, } from '@xyflow/react'; -import { uniqBy } from 'lodash-es'; +import { uniqBy, omit, cloneDeep } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; @@ -44,7 +44,7 @@ const entryNodeTypes = Object.values(basicNodeConfigs) .map(item => item.type); const useWorkflow = () => { - const { getNodes, getEdges } = useReactFlow(); + const { getNodes, getEdges, setNodes } = useReactFlow(); const nodes = useNodes(); const edges = useEdges(); const { getIntlText } = useI18n(); @@ -297,6 +297,33 @@ const useWorkflow = () => { [getNodes, getUpstreamNodes, getIntlText], ); + // Update node status + const updateNodesStatus = useCallback( + (data: Record | null, nodes?: WorkflowNode[]) => { + nodes = cloneDeep(nodes || getNodes()); + + if (!data) { + nodes.forEach(node => { + node.data = omit(node.data, ['$status', '$errMsg']); + }); + } else { + nodes.forEach(node => { + let status: WorkflowNodeStatus = data[node.id]; + if (!status) { + status = 'SUCCESS'; + } + node.data = { + ...node.data, + $status: status, + }; + }); + } + + setNodes(nodes); + }, + [getNodes, setNodes], + ); + return { nodes, edges, @@ -308,6 +335,7 @@ const useWorkflow = () => { getSelectedNode, getUpstreamNodes, getUpstreamNodeParams, + updateNodesStatus, }; }; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index a49c6601..ef9bd3c8 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -20,7 +20,13 @@ import { CodeEditor, useConfirm } from '@/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { MIN_ZOOM, MAX_ZOOM, FROZEN_NODE_PROPERTY_KEYS } from './constants'; import useFlowStore from './store'; -import { useNodeTypes, useInteractions, useWorkflow } from './hooks'; +import { + useNodeTypes, + useInteractions, + useWorkflow, + useValidate, + NODE_VALIDATE_TOAST_KEY, +} from './hooks'; import { Topbar, Controls, @@ -56,11 +62,13 @@ const WorkflowEditor = () => { checkNestedParallelLimit, checkNodeNumberLimit, checkFreeNodeLimit, + updateNodesStatus, } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const { checkNodesData } = useValidate(); const checkWorkflowValid = useCallback(() => { const { nodes, edges } = toObject(); if (!checkNodeNumberLimit(nodes)) return false; @@ -130,7 +138,9 @@ const WorkflowEditor = () => { ); // ---------- Fetch Nodes Config ---------- - const { setNodeConfigs } = useFlowStore(useStoreShallow(['setNodeConfigs'])); + const { setNodeConfigs, setNodesDataValidResult } = useFlowStore( + useStoreShallow(['setNodeConfigs', 'setNodesDataValidResult']), + ); const { loading: nodeConfigLoading } = useRequest( async () => { const [error, resp] = await awaitWrap(workflowAPI.getFlowNodes()); @@ -243,12 +253,35 @@ const WorkflowEditor = () => { const [saveLoading, setSaveLoading] = useState(false); const handleSave = async () => { if (!checkWorkflowValid()) return; + const dataCheckResult = checkNodesData(); + + console.log({ dataCheckResult }); + if (dataCheckResult !== true) { + if (designMode === 'canvas') { + const statusData = Object.entries(dataCheckResult).reduce( + (acc, [id, item]) => { + acc[id] = item.status; + return acc; + }, + {} as NonNullable[0]>, + ); + // TODO: show validate panel + setNodesDataValidResult(dataCheckResult); + updateNodesStatus(statusData, nodes); + } else { + const errItem = Object.values(dataCheckResult).find(item => item.errMsgs.length); + + toast.error({ key: NODE_VALIDATE_TOAST_KEY, content: errItem?.errMsgs[0] }); + } + return; + } + if (!basicData) { // TODO: data validate return; } - const { nodes, edges, viewport } = toObject(); + // const { nodes, edges, viewport } = toObject(); const hasTriggerNode = nodes.find(node => node.type === 'trigger'); // If has a trigger node and it is the first time to create, show tip @@ -270,14 +303,14 @@ const WorkflowEditor = () => { // TODO: referenced warning confirm ? // TODO: check the nodes data is valid - console.log('workflow data', { nodes, edges, viewport }); + console.log('workflow data', { nodes, edges }); setSaveLoading(true); const [error, resp] = await awaitWrap( workflowAPI.saveFlowDesign({ name: basicData.name!, remark: basicData.remark!, enabled: basicData.enabled!, - design_data: JSON.stringify({ nodes, edges, viewport }), + design_data: JSON.stringify({ nodes, edges }), }), ); @@ -288,7 +321,7 @@ const WorkflowEditor = () => { console.log(data); toast.success(getIntlText('common.message.operation_success')); - // navigate('/workflow'); + navigate('/workflow'); }; return ( diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index c8f2c59b..5341ae52 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -1,7 +1,8 @@ import { create } from 'zustand'; import { immer } from 'zustand/middleware/immer'; -import { type WorkflowAPISchema } from '@/services/http'; +import { type WorkflowAPISchema, type FlowNodeTraceInfo } from '@/services/http'; import { basicNodeConfigs } from '../../config'; +import type { NodesDataValidResult } from './hooks'; import type { NodeConfigItem } from './typings'; interface FlowStore { @@ -33,7 +34,7 @@ interface FlowStore { */ runLogs?: WorkflowAPISchema['getLogList']['response']['content']; - logDetail?: WorkflowAPISchema['getLogDetail']['response']; + logDetail?: PartialOptional[]; logDetailLoading?: boolean; @@ -50,6 +51,8 @@ interface FlowStore { setLogDetail: (detail?: FlowStore['logDetail']) => void; setLogDetailLoading: (loading: FlowStore['logDetailLoading']) => void; + + setNodesDataValidResult: (data?: NodesDataValidResult) => void; } const useFlowStore = create( @@ -61,19 +64,19 @@ const useFlowStore = create( id: '1', start_time: 1733809691235, time_cost: 1000, - status: 'Success', + status: 'SUCCESS', }, { id: '2', start_time: 1733809691235, time_cost: 1000, - status: 'Error', + status: 'ERROR', }, { id: '3', start_time: 1733809691235, time_cost: 1000, - status: 'Success', + status: 'ERROR', }, ], @@ -115,6 +118,25 @@ const useFlowStore = create( setRunLogs: runLogs => set({ runLogs }), setLogDetail: detail => set({ logDetail: detail }), setLogDetailLoading: loading => set({ logDetailLoading: loading }), + setNodesDataValidResult(data) { + if (!data) { + set({ logPanelMode: undefined, logDetail: undefined }); + return; + } + console.log(data); + const logDetail = Object.entries(data).map(([id, { type, name, label, errMsgs }]) => { + const result: NonNullable[0] = { + node_id: id, + node_label: label!, + status: 'ERROR', + error_message: errMsgs[0], + }; + return result; + }); + + console.log(logDetail); + set({ openLogPanel: true, logPanelMode: 'feVerify', logDetail }); + }, })), ); diff --git a/apps/web/src/services/http/index.ts b/apps/web/src/services/http/index.ts index 32a1d8b9..55125a26 100644 --- a/apps/web/src/services/http/index.ts +++ b/apps/web/src/services/http/index.ts @@ -6,4 +6,9 @@ export { default as entityAPI, type EntityAPISchema } from './entity'; export { default as integrationAPI, type IntegrationAPISchema } from './integration'; export { default as globalAPI, type GlobalAPISchema } from './global'; export { default as dashboardAPI, type DashboardAPISchema } from './dashboard'; -export { default as workflowAPI, type FlowStatus, type WorkflowAPISchema } from './workflow'; +export { + default as workflowAPI, + type FlowStatus, + type WorkflowAPISchema, + type FlowNodeTraceInfo, +} from './workflow'; diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 4742bf1d..2058841d 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -99,5 +99,6 @@ "workflow.valid.range_length": "The length of the {1} field value should be greater than or equal to {0} and less than or equal to {1}.", "workflow.valid.node_parameters_required": "The {1} node parameters is required.", "workflow.valid.min_length": "The {1} field length of the value you entered should be more than or equal to {0}.", - "workflow.valid.max_length": "The {1} field length of the value you entered cannot exceed {2} digits." + "workflow.valid.max_length": "The {1} field length of the value you entered cannot exceed {2} digits.", + "workflow.valid.parameter_required": "The {1} Node parameter is required." } diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index bd6b9759..881a04ab 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -65,7 +65,7 @@ declare type WorkflowNodeCategoryType = 'entry' | 'control' | 'action' | 'extern /** * Node Status Type */ -declare type WorkflowNodeStatus = 'error' | 'success' | 'loading'; +declare type WorkflowNodeStatus = 'ERROR' | 'SUCCESS'; /** * 节点基础数据类型(以 $ 开头的均为前端私有属性) @@ -233,7 +233,7 @@ declare type SelectNodeDataType = BaseNodeDataType<{ /** * 邮件节点参数类型 */ -declare type EmailNodeDataType = BaseNodeDataType & { +declare type EmailNodeDataType = BaseNodeDataType<{ emailConfig: { provider: 'SMTP' | 'google'; smtpConfig: { @@ -247,12 +247,12 @@ declare type EmailNodeDataType = BaseNodeDataType & { subject: string; recipients: string[]; content: string; -}; +}>; /** * Webhook 节点参数类型 */ -declare type WebhookNodeDataType = BaseNodeDataType & { +declare type WebhookNodeDataType = BaseNodeDataType<{ /** Custom Data */ inputArguments?: { key: ApiKey; @@ -262,7 +262,7 @@ declare type WebhookNodeDataType = BaseNodeDataType & { webhookUrl: string; /** Webhook Secret */ secretKey?: string; -}; +}>; /** * 工作流节点模型 From 37083956b63b5ccd020184d63543b8feb2dd323a Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 26 Dec 2024 14:11:00 +0800 Subject: [PATCH 092/172] feat: Compatible when the action log component does not have an expansion item --- .../action-log/components/card/index.tsx | 14 +- .../action-log/components/card/style.less | 11 ++ .../action-log/components/header/index.tsx | 8 +- .../action-log/hooks/useNestedData.tsx | 9 +- .../workflow/components/action-log/index.tsx | 18 +-- .../workflow/components/action-log/style.less | 11 +- .../workflow/components/action-log/types.d.ts | 2 +- .../workflow/components/log-modal/trace.json | 129 +++++------------- .../components/code-editor/index.tsx | 4 +- 9 files changed, 93 insertions(+), 113 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx index c567a285..1c23180e 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/card/index.tsx @@ -1,4 +1,5 @@ import React from 'react'; +import cls from 'classnames'; import Accordion from '@mui/material/Accordion'; import AccordionSummary from '@mui/material/AccordionSummary'; import AccordionDetails from '@mui/material/AccordionDetails'; @@ -10,15 +11,26 @@ interface IProps { children: React.ReactNode; } export default React.memo(({ header, children }: IProps) => { + const hasChildren = React.Children.toArray(children).length > 0; + return ( } className="ms-log-accordion__header" > {header} - {children} + {hasChildren && ( + + {children} + + )} ); }); diff --git a/apps/web/src/pages/workflow/components/action-log/components/card/style.less b/apps/web/src/pages/workflow/components/action-log/components/card/style.less index dbabf51e..c3ef37f4 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/card/style.less +++ b/apps/web/src/pages/workflow/components/action-log/components/card/style.less @@ -48,6 +48,17 @@ } } + & &__icon { + &--hidden { + width: 0; + margin-left: @margin-sm; + + .MuiSvgIcon-root { + display: none; + } + } + } + & &__content { padding-top: 0; } diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx index 6338df3d..c4a05cd7 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx @@ -13,7 +13,13 @@ export default React.memo(({ data }: IProps) => { const { getIntlText } = useI18n(); /** Get the header render config */ - const { icon, iconBgColor, name, status, timeCost } = useMemo(() => { + const { + icon, + iconBgColor, + name, + status = 'Success', + timeCost, + } = useMemo(() => { const { type, config, status, name, timeCost } = data || {}; const { icon, iconBgColor, labelIntlKey } = config || {}; const result = basicNodeConfigs[type]; diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx index 40a23ac6..e05e1d33 100644 --- a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -31,17 +31,20 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { const wrapperNode = useMemoizedFn((node: WorkflowNode): WorkflowNestNode => { const nestNode = cloneDeep(node) as WorkflowNestNode; const { id, type, data } = nestNode || {}; - const { name } = data || {}; - const { status, input, output, timeCost } = objectToCamelCase(traceMap[id] || {}); + const { nodeName } = data || {}; + const { status, input, output, timeCost, errorMessage } = objectToCamelCase( + traceMap[id] || {}, + ); nestNode.attrs = { $$token: generateUUID(), - name: name || '', + name: nodeName || '', type: type!, status, timeCost, input, output, + errorMessage, }; return nestNode; diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index b95c7079..93cf0139 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -41,7 +41,7 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro return treeData.map(data => { const { children, attrs, ...item } = data || {}; - const { input, output, errorMsg } = attrs || {}; + const { input, output, errorMessage } = attrs || {}; // If there are child nodes, increment the index if ((children?.length || 0) > 1) { @@ -63,26 +63,28 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro return ( }> - {errorMsg && ( + {errorMessage && (
- +
)} {input && (
)} {output && ( - +
+ +
)}
{!!children?.length && ( diff --git a/apps/web/src/pages/workflow/components/action-log/style.less b/apps/web/src/pages/workflow/components/action-log/style.less index 587c9c00..9e7287f4 100644 --- a/apps/web/src/pages/workflow/components/action-log/style.less +++ b/apps/web/src/pages/workflow/components/action-log/style.less @@ -6,13 +6,16 @@ .text-size(@font-size-sm); } - &__input { - margin-bottom: @margin-sm; + &__input, + &__output { + margin-top: @margin-sm; + + &:first-child { + margin-top: 0; + } } &__alert { - margin-bottom: @margin-sm; - .MuiPaper-root { padding: 0 @padding-md; diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index 981be208..29315472 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -28,7 +28,7 @@ export interface AccordionLog /** * error message */ - errorMsg?: string; + errorMessage?: string; /** * Children */ diff --git a/apps/web/src/pages/workflow/components/log-modal/trace.json b/apps/web/src/pages/workflow/components/log-modal/trace.json index 8ca54ca2..1e1fc013 100644 --- a/apps/web/src/pages/workflow/components/log-modal/trace.json +++ b/apps/web/src/pages/workflow/components/log-modal/trace.json @@ -4,183 +4,126 @@ "message_id": "msg_001", "node_id": "node:1734403663609:tqhoyisd", "node_label": "Trigger Node", - "status": "SUCCESS", + "status": "Error", "time_cost": 100, "start_time": 1672531200000, - "input": { - "trigger": "start" - }, - "output": { - "result": "initiated" - } + "error_message": "xxxx" }, { "message_id": "msg_002", "node_id": "node:1734403696051:mmijxipj", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 150, - "start_time": 1672531300000, - "input": { - "param1": "value1" - }, - "output": { - "result": "processed" - } + "start_time": 1672531300000 }, { "message_id": "msg_003", "node_id": "node:1734403741746:cvlebfnk", "node_label": "Code Node", - "status": "ERROR", + "status": "Error", "time_cost": 200, "start_time": 1672531400000, - "input": { - "param1": "value2" - }, - "output": { - "error": "runtime error" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_004", "node_id": "node:1734403754076:tunkfwyp", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 120, "start_time": 1672531500000, - "input": { - "param1": "value3" - }, - "output": { - "result": "completed" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_005", "node_id": "node:1734403760131:nxrtmtqt", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 110, "start_time": 1672531600000, - "input": { - "param1": "value4" - }, - "output": { - "result": "validated" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_006", "node_id": "node:1734403762609:tojsahsk", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 130, "start_time": 1672531700000, - "input": { - "param1": "value5" - }, - "output": { - "result": "executed" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_007", "node_id": "node:1734403764786:iwvgqkbl", "node_label": "Code Node", - "status": "ERROR", + "status": "Error", "time_cost": 180, "start_time": 1672531800000, - "input": { - "param1": "value6" - }, - "output": { - "error": "timeout error" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_008", "node_id": "node:1734403768531:pioadrla", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 90, "start_time": 1672531900000, - "input": { - "param1": "value7" - }, - "output": { - "result": "success" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_009", "node_id": "node:1734403779597:krdbgvbs", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 140, "start_time": 1672532000000, - "input": { - "param1": "value8" - }, - "output": { - "result": "processed" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_010", "node_id": "node:1734403781283:dwnkqolz", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 160, "start_time": 1672532100000, - "input": { - "param1": "value9" - }, - "output": { - "result": "executed" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_011", "node_id": "node:1734403783794:wjhlqruk", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 110, "start_time": 1672532200000, - "input": { - "param1": "value10" - }, - "output": { - "result": "completed" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_012", "node_id": "node:1734403795500:xceqicsa", "node_label": "Code Node", - "status": "ERROR", + "status": "Error", "time_cost": 170, "start_time": 1672532300000, - "input": { - "param1": "value11" - }, - "output": { - "error": "network error" - } + "input": "{\"param1\": \"value1\"}", + "output": "{\"result\": \"processed\"}" }, { "message_id": "msg_013", "node_id": "node:1734403803395:tcmtlast", "node_label": "Code Node", - "status": "SUCCESS", + "status": "Success", "time_cost": 100, - "start_time": 1672532400000, - "input": { - "param1": "value12" - }, - "output": { - "result": "finalized" - } + "start_time": 1672532400000 } ] } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx index 3e7d631f..f6f47bde 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -25,8 +25,8 @@ const CodeEditor: React.FC = ({ value, onChange }) => { const { language = DEFAULT_LANGUAGE, expression = '' } = data; const result = { - language: language || value?.language, - expression: expression || value?.expression, + language: language ?? value?.language, + expression: expression ?? value?.expression, }; onChange?.(result); }, From 93680655fda3a2d038d4f7cea276ce209e5ee562 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 14:33:12 +0800 Subject: [PATCH 093/172] fix: fix workflow service status error --- apps/web/src/services/http/workflow.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 4b122185..ad2a52be 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -2,15 +2,13 @@ import { client, attachAPI, API_PREFIX } from './client'; export type FlowStatus = 'enable' | 'disable'; -export type FlowRunningStatus = 'Error' | 'Success'; - export type FlowNodeTraceInfo = { /** Node ID */ node_id: ApiKey; /** Node Name */ node_label: string; /** Running status */ - status: FlowRunningStatus; + status: WorkflowNodeStatus; /** Start Time */ start_time: number; /** Cost Time */ @@ -113,7 +111,7 @@ export interface WorkflowAPISchema extends APISchema { /** Get workflow log list */ getLogList: { request: void | { - status?: FlowRunningStatus; + status?: WorkflowNodeStatus; }; response: SearchResponseType< { @@ -124,7 +122,7 @@ export interface WorkflowAPISchema extends APISchema { /** Run Time (ms) */ time_cost: number; /** Running status */ - status: FlowRunningStatus; + status: WorkflowNodeStatus; }[] >; }; @@ -207,7 +205,7 @@ export interface WorkflowAPISchema extends APISchema { }; response: { /** Running Status */ - status: FlowRunningStatus; + status: WorkflowNodeStatus; /** Flow ID */ flow_id: ApiKey; /** Start Time */ From 2f53a283b0205675a741e30be26eaabd14d69d62 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 26 Dec 2024 15:04:36 +0800 Subject: [PATCH 094/172] feat: The loading state of the action log component is added --- .../action-log/components/header/index.tsx | 2 +- .../action-log/components/header/style.less | 4 +++ .../action-log/hooks/useNestedData.tsx | 4 ++- .../workflow/components/action-log/index.tsx | 6 ++-- .../log-modal/components/log-item/style.less | 7 +++++ .../log-modal/hooks/useSourceData.tsx | 2 +- .../workflow/components/log-modal/style.less | 2 +- .../workflow/components/log-modal/trace.json | 28 +++++++++---------- .../workflow/components/log-modal/types.d.ts | 3 +- apps/web/src/pages/workflow/config.tsx | 7 ++++- 10 files changed, 41 insertions(+), 24 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx index c4a05cd7..7488f243 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx @@ -17,7 +17,7 @@ export default React.memo(({ data }: IProps) => { icon, iconBgColor, name, - status = 'Success', + status = 'SUCCESS', timeCost, } = useMemo(() => { const { type, config, status, name, timeCost } = data || {}; diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/style.less b/apps/web/src/pages/workflow/components/action-log/components/header/style.less index f507c631..1800dfa3 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/style.less +++ b/apps/web/src/pages/workflow/components/action-log/components/header/style.less @@ -45,5 +45,9 @@ &__error { color: var(--icon-color-error); } + + &__loading { + padding: 0 2px; + } } } diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx index 9b855aa4..29ea3618 100644 --- a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -69,9 +69,11 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { }, ); + // when `workflowData` or `traceData` change, re-render the tree const workflowNestData = useMemo( () => wrapperWorkflowData(workflowData), - [workflowData, wrapperWorkflowData], + // eslint-disable-next-line react-hooks/exhaustive-deps + [workflowData, traceData, wrapperWorkflowData], ); /** Convert flat data to tree */ diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index 93cf0139..8bef0385 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -40,8 +40,8 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro let multiBranchInParallelIndex = -1; return treeData.map(data => { - const { children, attrs, ...item } = data || {}; - const { input, output, errorMessage } = attrs || {}; + const { children, attrs } = data || {}; + const { input, output, errorMessage, $$token } = attrs || {}; // If there are child nodes, increment the index if ((children?.length || 0) > 1) { @@ -61,7 +61,7 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro }); return ( - + }> {errorMessage && (
diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less index c8205db9..7d657404 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-item/style.less @@ -27,6 +27,13 @@ &__error { color: var(--icon-color-error); } + + &__loading { + display: flex; + align-items: center; + padding: 0 2px; + margin-top: -2px; + } } .ms-log-content { diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx index bea54f7b..48ccb726 100644 --- a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx @@ -7,7 +7,7 @@ export const useSourceData = () => { const generateList = useCallback((limit: number): LogListPageType['content'] => { return Array.from({ length: limit }).map(() => ({ id: generateUUID(), - status: Math.random() > 0.5 ? 'Success' : 'Error', + status: Math.random() > 0.5 ? 'SUCCESS' : 'ERROR', start_time: Date.now(), time_cost: Math.floor(Math.random() * 1000), })); diff --git a/apps/web/src/pages/workflow/components/log-modal/style.less b/apps/web/src/pages/workflow/components/log-modal/style.less index b490c5a7..5f269715 100644 --- a/apps/web/src/pages/workflow/components/log-modal/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/style.less @@ -27,7 +27,7 @@ .ms-log-left-bar { display: flex; flex-direction: column; - width: 200px; + width: 210px; background-color: var(--body-background); &__scroll { diff --git a/apps/web/src/pages/workflow/components/log-modal/trace.json b/apps/web/src/pages/workflow/components/log-modal/trace.json index 1e1fc013..0b608881 100644 --- a/apps/web/src/pages/workflow/components/log-modal/trace.json +++ b/apps/web/src/pages/workflow/components/log-modal/trace.json @@ -4,16 +4,16 @@ "message_id": "msg_001", "node_id": "node:1734403663609:tqhoyisd", "node_label": "Trigger Node", - "status": "Error", + "status": "ERROR", "time_cost": 100, "start_time": 1672531200000, - "error_message": "xxxx" + "ERROR_message": "xxxx" }, { "message_id": "msg_002", "node_id": "node:1734403696051:mmijxipj", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 150, "start_time": 1672531300000 }, @@ -21,7 +21,7 @@ "message_id": "msg_003", "node_id": "node:1734403741746:cvlebfnk", "node_label": "Code Node", - "status": "Error", + "status": "ERROR", "time_cost": 200, "start_time": 1672531400000, "input": "{\"param1\": \"value1\"}", @@ -31,7 +31,7 @@ "message_id": "msg_004", "node_id": "node:1734403754076:tunkfwyp", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 120, "start_time": 1672531500000, "input": "{\"param1\": \"value1\"}", @@ -41,7 +41,7 @@ "message_id": "msg_005", "node_id": "node:1734403760131:nxrtmtqt", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 110, "start_time": 1672531600000, "input": "{\"param1\": \"value1\"}", @@ -51,7 +51,7 @@ "message_id": "msg_006", "node_id": "node:1734403762609:tojsahsk", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 130, "start_time": 1672531700000, "input": "{\"param1\": \"value1\"}", @@ -61,7 +61,7 @@ "message_id": "msg_007", "node_id": "node:1734403764786:iwvgqkbl", "node_label": "Code Node", - "status": "Error", + "status": "ERROR", "time_cost": 180, "start_time": 1672531800000, "input": "{\"param1\": \"value1\"}", @@ -71,7 +71,7 @@ "message_id": "msg_008", "node_id": "node:1734403768531:pioadrla", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 90, "start_time": 1672531900000, "input": "{\"param1\": \"value1\"}", @@ -81,7 +81,7 @@ "message_id": "msg_009", "node_id": "node:1734403779597:krdbgvbs", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 140, "start_time": 1672532000000, "input": "{\"param1\": \"value1\"}", @@ -91,7 +91,7 @@ "message_id": "msg_010", "node_id": "node:1734403781283:dwnkqolz", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 160, "start_time": 1672532100000, "input": "{\"param1\": \"value1\"}", @@ -101,7 +101,7 @@ "message_id": "msg_011", "node_id": "node:1734403783794:wjhlqruk", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 110, "start_time": 1672532200000, "input": "{\"param1\": \"value1\"}", @@ -111,7 +111,7 @@ "message_id": "msg_012", "node_id": "node:1734403795500:xceqicsa", "node_label": "Code Node", - "status": "Error", + "status": "ERROR", "time_cost": 170, "start_time": 1672532300000, "input": "{\"param1\": \"value1\"}", @@ -121,7 +121,7 @@ "message_id": "msg_013", "node_id": "node:1734403803395:tcmtlast", "node_label": "Code Node", - "status": "Success", + "status": "SUCCESS", "time_cost": 100, "start_time": 1672532400000 } diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts index 696a0328..df39ede7 100644 --- a/apps/web/src/pages/workflow/components/log-modal/types.d.ts +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -1,5 +1,4 @@ import { type WorkflowAPISchema } from '@/services/http'; -import type { FlowRunningStatus } from '@/services/http/workflow'; export interface LogItemProps { /** @@ -9,7 +8,7 @@ export interface LogItemProps { /** * Node status */ - status: FlowRunningStatus; + status: WorkflowNodeStatus; /** * Title */ diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index 1f5666cf..ac6b1630 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -1,3 +1,4 @@ +import { CircularProgress } from '@mui/material'; import { SettingsEthernetIcon, EntityIcon, @@ -175,7 +176,7 @@ export const basicNodeConfigs: Record = { * Status Render Map */ export const LogStatusMap: Record< - WorkflowNodeStatus, + WorkflowNodeStatus | 'LOADING', { className: string; icon: React.ReactNode } > = { SUCCESS: { @@ -186,4 +187,8 @@ export const LogStatusMap: Record< className: 'ms-log-status__error', icon: , }, + LOADING: { + className: 'ms-log-status__loading', + icon: , + }, }; From 9fe148e628ea61b9eb87b1262d9588824e9da830 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 26 Dec 2024 15:31:55 +0800 Subject: [PATCH 095/172] fix: Fixed an issue where the action log component would not rerender --- .../action-log/hooks/useNestedData.tsx | 184 ++++++++++-------- .../workflow/components/action-log/index.tsx | 6 +- 2 files changed, 105 insertions(+), 85 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx index 29ea3618..680e8c20 100644 --- a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -1,7 +1,6 @@ -import { useMemo } from 'react'; +import { useCallback, useMemo } from 'react'; import { getIncomers, getOutgoers } from '@xyflow/react'; import { cloneDeep } from 'lodash-es'; -import { useMemoizedFn } from 'ahooks'; import { generateUUID, objectToCamelCase } from '@milesight/shared/src/utils/tools'; import { basicNodeConfigs } from '../../../config'; import type { @@ -28,36 +27,39 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { }, [traceData]); /** Add required attributes */ - const wrapperNode = useMemoizedFn((node: WorkflowNode): WorkflowNestNode => { - const nestNode = cloneDeep(node) as WorkflowNestNode; - const { id, type, data } = nestNode || {}; - const { nodeName } = data || {}; - const { status, input, output, timeCost, errorMessage } = objectToCamelCase( - traceMap[id] || {}, - ); - - nestNode.attrs = { - $$token: generateUUID(), - name: nodeName || '', - type: type!, - status, - timeCost: timeCost!, - input, - output, - errorMessage, - }; + const wrapperNode = useCallback( + (node: WorkflowNode): WorkflowNestNode => { + const nestNode = cloneDeep(node) as WorkflowNestNode; + const { id, type, data } = nestNode || {}; + const { nodeName } = data || {}; + const { status, input, output, timeCost, errorMessage } = objectToCamelCase( + traceMap[id] || {}, + ); + + nestNode.attrs = { + $$token: generateUUID(), + name: nodeName || '', + type: type!, + status, + timeCost: timeCost!, + input, + output, + errorMessage, + }; - return nestNode; - }); + return nestNode; + }, + [traceMap], + ); /** Determine whether it is the root node */ - const isRootNode = useMemoizedFn((node: WorkflowNestNode) => { + const isRootNode = useCallback((node: WorkflowNestNode) => { const { type } = node || {}; return entryNodeConfigs.some(config => config.type === type); - }); + }, []); /** Wrap workflow data */ - const wrapperWorkflowData = useMemoizedFn( + const wrapperWorkflowData = useCallback( (workflowData: WorkflowDataType): WorkflowNestDataType => { const { nodes } = workflowData || {}; const nestNodes = (nodes || []).map(node => wrapperNode(node)); @@ -67,17 +69,17 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { nodes: nestNodes, }; }, + [wrapperNode], ); // when `workflowData` or `traceData` change, re-render the tree const workflowNestData = useMemo( () => wrapperWorkflowData(workflowData), - // eslint-disable-next-line react-hooks/exhaustive-deps - [workflowData, traceData, wrapperWorkflowData], + [workflowData, wrapperWorkflowData], ); /** Convert flat data to tree */ - const dataToTree = useMemoizedFn( + const dataToTree = useCallback( (cb?: (node: WorkflowNestNode) => void): WorkflowNestNode[] => { const { nodes, edges } = workflowNestData || {}; @@ -99,6 +101,7 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { return root; }, + [isRootNode, workflowNestData], ); // Tree node data @@ -124,49 +127,58 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { }, [roots]); /** Get the depth of a node */ - const getDepthByNode = useMemoizedFn((node: WorkflowNestNode): number => { - const { attrs } = node || {}; - const { $$token } = attrs || {}; + const getDepthByNode = useCallback( + (node: WorkflowNestNode): number => { + const { attrs } = node || {}; + const { $$token } = attrs || {}; - return treeDepthMap.get($$token) || 0; - }); + return treeDepthMap.get($$token) || 0; + }, + [treeDepthMap], + ); // Get the upstream node of a given node - const getParentNodeInTree = useMemoizedFn((node: WorkflowNestNode) => { - const { attrs } = node; - const { $$token } = attrs; + const getParentNodeInTree = useCallback( + (node: WorkflowNestNode) => { + const { attrs } = node; + const { $$token } = attrs; - return treeParentMap.get($$token); - }); + return treeParentMap.get($$token); + }, + [treeParentMap], + ); /** Get available upstream nodes */ - const getOnceIncomeNode = useMemoizedFn((node: WorkflowNestNode): ParallelNodeResult | null => { - const { nodes, edges } = workflowNestData || {}; - - // Get parent nodes - const incomers = getIncomers(node, nodes, edges) as WorkflowNestNode[]; - if (!incomers?.length) return null; - - // Check if parent nodes have only one outgoing node - const isOnceIncomeNode = incomers.every(incomer => { - const outgoers = getOutgoers(incomer, nodes, edges) as WorkflowNestNode[]; - // Only one outgoing node - return outgoers.length === 1; - }); - if (!isOnceIncomeNode) return null; + const getOnceIncomeNode = useCallback( + (node: WorkflowNestNode): ParallelNodeResult | null => { + const { nodes, edges } = workflowNestData || {}; - const usableIncomes = incomers.sort((a, b) => getDepthByNode(a) - getDepthByNode(b)); - const [usableIncome] = usableIncomes || []; + // Get parent nodes + const incomers = getIncomers(node, nodes, edges) as WorkflowNestNode[]; + if (!incomers?.length) return null; - return { - node, - incomers, - usableIncome, - }; - }); + // Check if parent nodes have only one outgoing node + const isOnceIncomeNode = incomers.every(incomer => { + const outgoers = getOutgoers(incomer, nodes, edges) as WorkflowNestNode[]; + // Only one outgoing node + return outgoers.length === 1; + }); + if (!isOnceIncomeNode) return null; + + const usableIncomes = incomers.sort((a, b) => getDepthByNode(a) - getDepthByNode(b)); + const [usableIncome] = usableIncomes || []; + + return { + node, + incomers, + usableIncome, + }; + }, + [getDepthByNode, workflowNestData], + ); /** Get nodes that need to be promoted to the same level */ - const getParallelNodeList = useMemoizedFn((): ParallelNodeResult[] => { + const getParallelNodeList = useCallback((): ParallelNodeResult[] => { const { nodes } = workflowNestData || {}; const parallelNodeList: ParallelNodeResult[] = []; @@ -178,35 +190,41 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { }); return parallelNodeList; - }); + }, [getOnceIncomeNode, workflowNestData]); /** Cancel references */ - const cancelQuote = useMemoizedFn((parallelNodeList: ParallelNodeResult[]) => { - const { nodes, edges } = workflowNestData || {}; + const cancelQuote = useCallback( + (parallelNodeList: ParallelNodeResult[]) => { + const { nodes, edges } = workflowNestData || {}; - (parallelNodeList || []).forEach(({ node }) => { - const incomers = getIncomers(node, nodes, edges) as WorkflowNestNode[]; - // Remove references - (incomers || []).forEach(incomer => { - incomer.children = (incomer.children || []).filter(v => v.id !== node.id); + (parallelNodeList || []).forEach(({ node }) => { + const incomers = getIncomers(node, nodes, edges) as WorkflowNestNode[]; + // Remove references + (incomers || []).forEach(incomer => { + incomer.children = (incomer.children || []).filter(v => v.id !== node.id); + }); }); - }); - }); + }, + [workflowNestData], + ); /** Connect new references */ - const connectQuote = useMemoizedFn((parallelNodeList: ParallelNodeResult[]) => { - (parallelNodeList || []).forEach(result => { - const { node, usableIncome } = result || {}; - const parentNode = getParentNodeInTree(usableIncome); - - if (parentNode) { - parentNode.children = (parentNode.children || []).concat(node); - return; - } + const connectQuote = useCallback( + (parallelNodeList: ParallelNodeResult[]) => { + (parallelNodeList || []).forEach(result => { + const { node, usableIncome } = result || {}; + const parentNode = getParentNodeInTree(usableIncome); + + if (parentNode) { + parentNode.children = (parentNode.children || []).concat(node); + return; + } - roots.push(node); - }); - }); + roots.push(node); + }); + }, + [getParentNodeInTree, roots], + ); /** Generate tree data */ const treeData = useMemo(() => { diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index 8bef0385..d5dbf0f5 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,3 +1,4 @@ +import React from 'react'; import { Fragment } from 'react/jsx-runtime'; import { Alert } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; @@ -22,7 +23,8 @@ const getAlphabetIndex = (index: number) => { } return ALPHABET_LIST[index]; }; -export default function AccordionUsage({ traceData, workflowData }: ActionLogProps) { + +export default React.memo(({ traceData, workflowData }: ActionLogProps) => { const { getIntlText } = useI18n(); const { treeData } = useNestedData({ workflowData, traceData }); @@ -99,4 +101,4 @@ export default function AccordionUsage({ traceData, workflowData }: ActionLogPro }; return
{renderAccordion(treeData)}
; -} +}); From 76fda6fdae3c4ea1ece707494bdc7653cee49238 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 17:51:52 +0800 Subject: [PATCH 096/172] feat: add validators for workflow data --- .../editor/components/log-panel/index.tsx | 19 +- .../workflow/views/editor/demo-data.json | 14 +- .../workflow/views/editor/hooks/index.ts | 1 + .../views/editor/hooks/useValidate.ts | 257 +++++++++++++++++- .../views/editor/hooks/useWorkflow.ts | 5 +- .../src/pages/workflow/views/editor/index.tsx | 106 ++++++-- .../src/pages/workflow/views/editor/store.ts | 4 +- packages/locales/src/lang/en/workflow.json | 8 +- 8 files changed, 352 insertions(+), 62 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index 6e4313c4..ed9946dc 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -1,6 +1,6 @@ import { useState, useMemo, useEffect } from 'react'; import { Stack, IconButton, Button, TextField, CircularProgress } from '@mui/material'; -import { Panel, useNodes, useReactFlow } from '@xyflow/react'; +import { Panel, useReactFlow } from '@xyflow/react'; import cls from 'classnames'; import { useRequest } from 'ahooks'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; @@ -16,8 +16,7 @@ import './style.less'; */ const LogPanel = () => { const { getIntlText } = useI18n(); - const nodes = useNodes(); - const { toObject } = useReactFlow(); + const { getNodes, toObject } = useReactFlow(); const { openLogPanel, logPanelMode, @@ -40,6 +39,10 @@ const LogPanel = () => { ]), ); const { updateNodesStatus } = useWorkflow(); + const flowData = useMemo(() => { + if (!openLogPanel) return; + return toObject(); + }, [openLogPanel, toObject]); const title = useMemo(() => { switch (logPanelMode) { case 'testRun': @@ -64,11 +67,12 @@ const LogPanel = () => { // ---------- Run Test ---------- const [entryInput, setEntryInput] = useState(''); const showTestInput = useMemo(() => { - if (logPanelMode !== 'testRun') return false; + if (!flowData || logPanelMode !== 'testRun') return false; + const nodes = getNodes(); const hasTriggerNode = nodes.find(node => node.type === 'trigger'); return !!hasTriggerNode; - }, [nodes, logPanelMode]); + }, [flowData, logPanelMode, getNodes]); const { run: runFlowTest } = useRequest( async () => { if (logPanelMode !== 'testRun') return; @@ -96,7 +100,6 @@ const LogPanel = () => { runFlowTest(); }, [logPanelMode, showTestInput, runFlowTest]); - console.log({ logDetail }); return ( {
)}
- {!!logDetail?.length && ( - + {!!logDetail?.length && flowData && ( + )}
diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json index ecfd76f0..50a22b15 100644 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ b/apps/web/src/pages/workflow/views/editor/demo-data.json @@ -98,46 +98,46 @@ ], "edges": [ { - "id": "1-2", + "id": "1_2", "source": "1", "target": "2", "type": "addable" }, { - "id": "2-3", + "id": "2_3", "source": "2", "target": "3", "type": "addable", "sourceHandle": "$temp:if" }, { - "id": "2-4", + "id": "2_4", "source": "2", "target": "4", "type": "addable", "sourceHandle": "$temp:else" }, { - "id": "2-5", + "id": "2_5", "source": "2", "target": "5", "type": "addable", "sourceHandle": "$temp:else" }, { - "id": "3-6", + "id": "3_6", "source": "3", "target": "6", "type": "addable" }, { - "id": "4-7", + "id": "4_7", "source": "4", "target": "7", "type": "addable" }, { - "id": "5-8", + "id": "5_8", "source": "5", "target": "8", "type": "addable" diff --git a/apps/web/src/pages/workflow/views/editor/hooks/index.ts b/apps/web/src/pages/workflow/views/editor/hooks/index.ts index 0b70b853..867975bc 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/index.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/index.ts @@ -4,5 +4,6 @@ export { default as useWorkflow } from './useWorkflow'; export { default as useValidate, NODE_VALIDATE_TOAST_KEY, + EDGE_VALIDATE_TOAST_KEY, type NodesDataValidResult, } from './useValidate'; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 3f38f121..5d028402 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -1,5 +1,6 @@ import { useMemo, useCallback } from 'react'; import { useReactFlow } from '@xyflow/react'; +import { uniqBy } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { @@ -8,7 +9,9 @@ import { isMinLength, isMaxLength, isURL, + isMatches, } from '@milesight/shared/src/utils/validators'; +import { EDGE_TYPE_ADDABLE } from '../constants'; import useFlowStore from '../store'; type NodeDataValidator = (value?: T, fieldName?: string) => string | boolean | undefined; @@ -39,6 +42,10 @@ enum ErrorIntlKey { } export const NODE_VALIDATE_TOAST_KEY = 'node-validate'; +export const EDGE_VALIDATE_TOAST_KEY = 'edge-validate'; + +// node and edge id regex +const ID_PATTERN = /^(?!_)[a-zA-Z0-9_]+$/; const useValidate = () => { const { getIntlText } = useI18n(); @@ -218,12 +225,232 @@ const useValidate = () => { return result; }, [getIntlText]); - const checkNodeId = () => {}; + /** + * Check Nodes ID + * 1. ID is unique + * 2. ID Cannot start with `_` + * 3. ID strings can only contain letters (case insensitive), numbers, and underscores + */ + const checkNodesId = useCallback( + (nodes?: WorkflowNode[], options?: CheckOptions) => { + nodes = nodes || getNodes(); + const result: NodesDataValidResult = {}; + const nodesMap = new Map(); + + for (let i = 0; i < nodes.length; i++) { + const { id, type, data } = nodes[i]; + const nodeName = data?.nodeName; + const nodeType = type as WorkflowNodeType; + const nodeLabel = !nodeConfigs[nodeType] + ? undefined + : getIntlText(nodeConfigs[nodeType]?.labelIntlKey); + const errMsgs: string[] = []; + + if (nodesMap.has(id)) { + const [node1, node2] = nodes.filter(item => item.id === id); + errMsgs.push( + getIntlText('workflow.valid.node_id_duplicated', { + 1: + node1.data?.nodeName || + `${getIntlText(nodeConfigs[node1.type as WorkflowNodeType].labelIntlKey)}(${id})`, + 2: + node2.data?.nodeName || + `${getIntlText(nodeConfigs[node2.type as WorkflowNodeType].labelIntlKey)}(${id})`, + }), + ); + } else { + nodesMap.set(id, true); + } + + if (!isMatches(id, ID_PATTERN)) { + errMsgs.push( + getIntlText('workflow.valid.invalid_node_id', { + 1: nodeName || `${nodeLabel}(${id})`, + }), + ); + } + + if (errMsgs.length) { + result[id] = { + type: type as WorkflowNodeType, + label: nodeLabel, + name: nodeName, + status: 'ERROR', + errMsgs, + }; + } + + if (!options?.validateFirst || !errMsgs.length) continue; + toast.error({ + key: NODE_VALIDATE_TOAST_KEY, + content: errMsgs[0], + }); + return result; + } + + return Object.values(result).some(item => item.errMsgs.length) ? result : undefined; + }, + [nodeConfigs, getNodes, getIntlText], + ); + + // Check Nodes Type + const checkNodesType = useCallback( + (nodes?: WorkflowNode[], options?: CheckOptions) => { + nodes = nodes || getNodes(); + const result: NodesDataValidResult = {}; + + for (let i = 0; i < nodes.length; i++) { + const { id, type, data, componentName } = nodes[i]; + const nodeType = type as WorkflowNodeType; + + if (nodeConfigs[nodeType] && nodeConfigs[nodeType].componentName === componentName) + continue; + + result[id] = { + type: nodeType, + status: 'ERROR', + errMsgs: [ + getIntlText('workflow.valid.invalid_node_type', { + 1: data.nodeName || id, + }), + ], + }; + + if (!options?.validateFirst) continue; + toast.error({ + key: NODE_VALIDATE_TOAST_KEY, + content: result[id].errMsgs[0], + }); + return result; + } + + return Object.values(result).some(item => item.errMsgs.length) ? result : undefined; + }, + [nodeConfigs, getNodes, getIntlText], + ); + + // Check Edges ID, the rule is same with node ID + const checkEdgesId = useCallback( + (edges?: WorkflowEdge[], nodes?: WorkflowNode[], options?: CheckOptions) => { + edges = edges || getEdges(); + nodes = nodes || getNodes(); + const result: Record< + string, + { id: string; status: WorkflowNodeStatus; errMsgs: string[] } + > = {}; + const edgesMap = new Map(); - const checkNodeType = () => {}; + for (let i = 0; i < edges.length; i++) { + const { id, source, target } = edges[i]; - const checkEdgeType = () => {}; + const sourceNode = nodes.find(node => node.id === source); + const targetNode = nodes.find(node => node.id === target); + const errMsgs: string[] = []; + + if (edgesMap.has(id)) { + errMsgs.push( + getIntlText('workflow.valid.edge_id_duplicated', { + 1: + sourceNode?.data?.nodeName || + `${getIntlText(nodeConfigs[sourceNode?.type as WorkflowNodeType]?.labelIntlKey || '')}(${id})`, + 2: + targetNode?.data?.nodeName || + `${getIntlText(nodeConfigs[targetNode?.type as WorkflowNodeType]?.labelIntlKey || '')}(${id})`, + }), + ); + } else { + edgesMap.set(id, true); + } + + if (!isMatches(id, ID_PATTERN)) { + errMsgs.push( + getIntlText('workflow.valid.invalid_edge_id', { + 1: sourceNode?.data.nodeName || source, + 2: targetNode?.data.nodeName || target, + }), + ); + } + + if (errMsgs.length) { + result[id] = { + id, + status: 'ERROR', + errMsgs: [ + getIntlText('workflow.valid.invalid_edge_id', { + 1: sourceNode?.data.nodeName || source, + 2: targetNode?.data.nodeName || target, + }), + ], + }; + } + + if (!options?.validateFirst || !errMsgs.length) continue; + toast.error({ + key: EDGE_VALIDATE_TOAST_KEY, + content: errMsgs[0], + }); + return result; + } + const errors = Object.values(result); + const isSuccess = !errors.some(item => item.errMsgs.length); + + if (isSuccess) return; + + toast.error({ key: EDGE_VALIDATE_TOAST_KEY, content: errors[0].errMsgs[0] }); + return result; + }, + [nodeConfigs, getEdges, getNodes, getIntlText], + ); + + // Check Edges Type + const checkEdgesType = useCallback( + (edges?: WorkflowEdge[], nodes?: WorkflowNode[], options?: CheckOptions) => { + edges = edges || getEdges(); + nodes = nodes || getNodes(); + const result: Record< + string, + { id: string; status: WorkflowNodeStatus; errMsgs: string[] } + > = {}; + + for (let i = 0; i < edges.length; i++) { + const { id, type, source, target } = edges[i]; + + if (type === EDGE_TYPE_ADDABLE) continue; + const sourceNode = nodes.find(node => node.id === source); + const targetNode = nodes.find(node => node.id === target); + + result[id] = { + id, + status: 'ERROR', + errMsgs: [ + getIntlText('workflow.valid.invalid_edge_type', { + 1: sourceNode?.data.nodeName || source, + 2: targetNode?.data.nodeName || target, + }), + ], + }; + + if (!options?.validateFirst || !result[id].errMsgs.length) continue; + toast.error({ + key: EDGE_VALIDATE_TOAST_KEY, + content: result[id].errMsgs[0], + }); + return result; + } + + const errors = Object.values(result); + const isSuccess = !errors.some(item => item.errMsgs.length); + + if (isSuccess) return; + + toast.error({ key: EDGE_VALIDATE_TOAST_KEY, content: errors[0].errMsgs[0] }); + return result; + }, + [getEdges, getNodes, getIntlText], + ); + + // Check Nodes Data const checkNodesData = useCallback( (nodes?: WorkflowNode[], options?: CheckOptions) => { nodes = nodes || getNodes(); @@ -231,7 +458,8 @@ const useValidate = () => { for (let i = 0; i < nodes.length; i++) { const { id, type, data } = nodes[i]; - const config = nodeConfigs[type as WorkflowNodeType]; + const nodeType = type as WorkflowNodeType; + const config = nodeConfigs[nodeType]; const { nodeName, nodeRemark, parameters } = data || {}; let tempResult = result[id]; @@ -242,8 +470,8 @@ const useValidate = () => { if (!tempResult) { tempResult = { - type: type as WorkflowNodeType, - label: getIntlText(config.labelIntlKey), + type: nodeType, + label: config ? getIntlText(config.labelIntlKey) : undefined, name: nodeName, status: 'SUCCESS', errMsgs: [], @@ -251,6 +479,7 @@ const useValidate = () => { result[id] = tempResult; } + // Node name check Object.values(dataValidators.nodeName).forEach(validator => { const result = validator(nodeName, getIntlText('common.label.name')); if (result && result !== true) { @@ -258,6 +487,7 @@ const useValidate = () => { } }); + // Node remark check Object.values(dataValidators.nodeRemark).forEach(validator => { const result = validator(nodeRemark, getIntlText('common.label.remark')); if (result && result !== true) { @@ -266,15 +496,17 @@ const useValidate = () => { }); // console.log({ id, type, parameters }); + // Node parameters check if (!parameters || !Object.keys(parameters).length) { tempResult.errMsgs.push( getIntlText('workflow.valid.parameter_required', { - 1: `${getIntlText(config.labelIntlKey)} (ID: ${id})`, + 1: nodeName || `${getIntlText(config.labelIntlKey)} (ID: ${id})`, }), ); continue; } + // Node parameters data check Object.entries(parameters).forEach(([key, value]) => { const validKey = `${type}.${key}`; const validators = dataValidators[validKey] || dataValidators[key] || {}; @@ -296,15 +528,16 @@ const useValidate = () => { } }); - return Object.values(result).some(item => item.errMsgs.length) ? result : true; + return Object.values(result).some(item => item.errMsgs.length) ? result : undefined; }, - [dataValidators, getIntlText, getNodes, nodeConfigs], + [dataValidators, nodeConfigs, getIntlText, getNodes], ); return { - checkNodeId, - checkNodeType, - checkEdgeType, + checkNodesId, + checkNodesType, + checkEdgesId, + checkEdgesType, checkNodesData, }; }; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index f92a344a..bc01b241 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -44,7 +44,7 @@ const entryNodeTypes = Object.values(basicNodeConfigs) .map(item => item.type); const useWorkflow = () => { - const { getNodes, getEdges, setNodes } = useReactFlow(); + const { getNodes, getEdges, setNodes, fitView } = useReactFlow(); const nodes = useNodes(); const edges = useEdges(); const { getIntlText } = useI18n(); @@ -317,11 +317,12 @@ const useWorkflow = () => { $status: status, }; }); + fitView({ duration: 300 }); } setNodes(nodes); }, - [getNodes, setNodes], + [getNodes, setNodes, fitView], ); return { diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index ef9bd3c8..3fcc716d 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -1,7 +1,7 @@ import { memo, useState, useCallback } from 'react'; import { useSearchParams, useNavigate } from 'react-router-dom'; import { useRequest } from 'ahooks'; -import { omitBy } from 'lodash-es'; +import { omitBy, merge, isEmpty } from 'lodash-es'; import { ReactFlow, Background, @@ -26,6 +26,7 @@ import { useWorkflow, useValidate, NODE_VALIDATE_TOAST_KEY, + EDGE_VALIDATE_TOAST_KEY, } from './hooks'; import { Topbar, @@ -68,7 +69,8 @@ const WorkflowEditor = () => { useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); - const { checkNodesData } = useValidate(); + const { checkNodesId, checkNodesType, checkNodesData, checkEdgesId, checkEdgesType } = + useValidate(); const checkWorkflowValid = useCallback(() => { const { nodes, edges } = toObject(); if (!checkNodeNumberLimit(nodes)) return false; @@ -226,26 +228,42 @@ const WorkflowEditor = () => { } else if (mode === 'canvas') { let data: Pick; - // TODO: json validate, data validate try { data = JSON.parse(editorFlowData || '{}'); } catch (e) { + console.warn(e); toast.error({ content: getIntlText('common.message.json_format_error') }); return; } const { nodes, edges } = data; + if ( + checkNodesId(nodes, { validateFirst: true }) || + checkNodesType(nodes, { validateFirst: true }) || + checkEdgesId(edges, nodes, { validateFirst: true }) || + checkEdgesType(edges, nodes, { validateFirst: true }) + ) { + return; + } + setNodes(nodes); setEdges(edges); } - const data = toObject(); - - // TODO: check the nodes json data is valid - console.log('workflow data', data); setDesignMode(mode); }, - [editorFlowData, toObject, setEdges, setNodes, checkWorkflowValid, getIntlText], + [ + editorFlowData, + checkWorkflowValid, + toObject, + checkNodesId, + checkNodesType, + checkEdgesId, + checkEdgesType, + setNodes, + setEdges, + getIntlText, + ], ); // ---------- Save Workflow ---------- @@ -253,35 +271,65 @@ const WorkflowEditor = () => { const [saveLoading, setSaveLoading] = useState(false); const handleSave = async () => { if (!checkWorkflowValid()) return; - const dataCheckResult = checkNodesData(); - - console.log({ dataCheckResult }); - if (dataCheckResult !== true) { - if (designMode === 'canvas') { - const statusData = Object.entries(dataCheckResult).reduce( - (acc, [id, item]) => { - acc[id] = item.status; - return acc; - }, - {} as NonNullable[0]>, - ); - // TODO: show validate panel - setNodesDataValidResult(dataCheckResult); - updateNodesStatus(statusData, nodes); - } else { - const errItem = Object.values(dataCheckResult).find(item => item.errMsgs.length); - - toast.error({ key: NODE_VALIDATE_TOAST_KEY, content: errItem?.errMsgs[0] }); + const flowData = toObject(); + const isAdvanceMode = designMode === 'advanced'; + + if (isAdvanceMode) { + let jsonData: Pick; + + try { + jsonData = JSON.parse(editorFlowData || '{}'); + } catch (e) { + console.warn(e); + toast.error({ content: getIntlText('common.message.json_format_error') }); + return; } + + flowData.nodes = jsonData.nodes; + flowData.edges = jsonData.edges; + } + + const { nodes, edges, viewport } = flowData; + + const edgesCheckResult = merge( + checkEdgesId(edges, nodes, { validateFirst: isAdvanceMode }), + checkEdgesType(edges, nodes, { validateFirst: isAdvanceMode }), + ); + console.log({ edgesCheckResult }); + if (!isEmpty(edgesCheckResult)) { + if (isAdvanceMode) return; + const errItem = Object.values(edgesCheckResult).find(item => item.errMsgs.length); + toast.error({ key: EDGE_VALIDATE_TOAST_KEY, content: errItem?.errMsgs[0] }); + return; + } + + const nodesCheckResult = merge( + checkNodesId(nodes, { validateFirst: isAdvanceMode }), + checkNodesType(nodes, { validateFirst: isAdvanceMode }), + checkNodesData(nodes, { validateFirst: isAdvanceMode }), + ); + console.log({ nodesCheckResult }); + if (!isEmpty(nodesCheckResult)) { + const statusData = Object.entries(nodesCheckResult).reduce( + (acc, [id, item]) => { + acc[id] = item.status; + return acc; + }, + {} as NonNullable[0]>, + ); + + setNodesDataValidResult(nodesCheckResult); + updateNodesStatus(statusData, nodes); return; } + updateNodesStatus(null); + setNodesDataValidResult(null); if (!basicData) { // TODO: data validate return; } - // const { nodes, edges, viewport } = toObject(); const hasTriggerNode = nodes.find(node => node.type === 'trigger'); // If has a trigger node and it is the first time to create, show tip @@ -302,8 +350,6 @@ const WorkflowEditor = () => { // TODO: referenced warning confirm ? - // TODO: check the nodes data is valid - console.log('workflow data', { nodes, edges }); setSaveLoading(true); const [error, resp] = await awaitWrap( workflowAPI.saveFlowDesign({ diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index 5341ae52..5b3949ba 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -52,7 +52,7 @@ interface FlowStore { setLogDetailLoading: (loading: FlowStore['logDetailLoading']) => void; - setNodesDataValidResult: (data?: NodesDataValidResult) => void; + setNodesDataValidResult: (data: NodesDataValidResult | null) => void; } const useFlowStore = create( @@ -120,7 +120,7 @@ const useFlowStore = create( setLogDetailLoading: loading => set({ logDetailLoading: loading }), setNodesDataValidResult(data) { if (!data) { - set({ logPanelMode: undefined, logDetail: undefined }); + set({ openLogPanel: false, logPanelMode: undefined, logDetail: undefined }); return; } console.log(data); diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 2058841d..196d3422 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -100,5 +100,11 @@ "workflow.valid.node_parameters_required": "The {1} node parameters is required.", "workflow.valid.min_length": "The {1} field length of the value you entered should be more than or equal to {0}.", "workflow.valid.max_length": "The {1} field length of the value you entered cannot exceed {2} digits.", - "workflow.valid.parameter_required": "The {1} Node parameter is required." + "workflow.valid.parameter_required": "The {1} Node parameter is required.", + "workflow.valid.invalid_node_id": "The {1} Node ID is invalid.", + "workflow.valid.invalid_edge_id": "The Edge (connecting node {1} and {2}) ID is invalid.", + "workflow.valid.invalid_node_type": "The {1} Node type is invalid.", + "workflow.valid.invalid_edge_type": "The Edge (connecting node {1} and {2}) Type is invalid.", + "workflow.valid.node_id_duplicated": "{1} and {2} Node ID is duplicated.", + "workflow.valid.edge_id_duplicated": "The Edge (connecting node {1} and {2}) ID is duplicated." } From 2fe9bb9231591c4e62440bcc5ee32f44948d21c9 Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 24 Dec 2024 09:26:09 +0800 Subject: [PATCH 097/172] fix: change the default value type --- .../components/service-param-assign-input/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx index d7e3bad9..0824fd36 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -18,7 +18,7 @@ type ServiceParamAssignInputProps = { disabled?: boolean; serviceKey?: ApiKey; value?: ServiceParamAssignInputValueType; - defaultValue?: any; + defaultValue?: ServiceParamAssignInputValueType; onChange?: (value: ServiceParamAssignInputValueType) => void; }; type EntityItem = { From 6034ff90b83e7b2313af98855000db66752836ba Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 24 Dec 2024 16:35:56 +0800 Subject: [PATCH 098/172] fix: update the data type options for the parameter input component --- .../components/config-panel/components/param-input/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index 405a8258..28548f07 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -44,7 +44,7 @@ const DEFAULT_EMPTY_VALUE: ParamInputValueType = { arg: '', type: '', }; -const typeOptions = ['STRING', 'LONG', 'DOUBLE', 'BOOLEAN', 'BINARY']; +const typeOptions = ['INT', 'FLOAT', 'BOOLEAN', 'STRING']; const ParamInput: React.FC = ({ required, From 1471b25308386de85079a93164102a5e1c10838d Mon Sep 17 00:00:00 2001 From: Nian Date: Thu, 26 Dec 2024 09:30:46 +0800 Subject: [PATCH 099/172] refactor: refactor the service invocation parameter settings component --- .../config-panel/components/index.ts | 2 +- .../components/param-input/index.tsx | 11 +- .../service-param-assign-input/index.tsx | 141 +++++++++++++----- .../config-panel/hooks/useNodeFormItems.tsx | 4 +- packages/locales/src/lang/en/workflow.json | 4 +- 5 files changed, 114 insertions(+), 48 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts index 573b4380..52326c0a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/index.ts @@ -9,7 +9,7 @@ export { export { default as MarkdownEditor } from './markdown-editor'; export { default as ParamInput } from './param-input'; export { default as ParamSelect } from './param-select'; -export { default as ParamInputSelect } from './param-input-select'; +export { default as ParamInputSelect, type ParamInputSelectProps } from './param-input-select'; export { default as TimerInput } from './timer-input'; export { default as EntityMultipleSelect, diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index 28548f07..d7baf6e4 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -21,6 +21,7 @@ import { useDynamicList, useControllableValue } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; import './style.less'; +import { isEqual } from 'lodash-es'; export type ParamInputValueType = { arg: string; @@ -58,10 +59,12 @@ const ParamInput: React.FC = ({ }) => { const { getIntlText } = useI18n(); const [innerValue, setInnerValue] = useControllableValue(props); - const { list, remove, getKey, insert, replace } = useDynamicList( - innerValue || [], - ); - + const { list, remove, getKey, insert, replace, resetList } = + useDynamicList(innerValue || []); + useLayoutEffect(() => { + if (isEqual(innerValue, list)) return; + resetList(innerValue || []); + }, [innerValue, resetList]); useLayoutEffect(() => { setInnerValue?.(list); }, [list, setInnerValue]); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx index 0824fd36..980c3906 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -1,9 +1,13 @@ -import { useLayoutEffect, useMemo } from 'react'; +import { useCallback, useLayoutEffect, useMemo } from 'react'; import { useEntityApi } from '@/plugin/hooks'; -import { useStoreShallow } from '@milesight/shared/src/hooks'; -import { useControllableValue, useDynamicList } from 'ahooks'; -import ParamInputSelect, { type ParamInputSelectProps } from '../param-input-select'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; +import { useControllableValue, useDebounceFn, useDynamicList } from 'ahooks'; +import { Divider, IconButton, Tooltip } from '@mui/material'; +import { HelpIcon } from '@milesight/shared/src/components'; +import { isEqual } from 'lodash-es'; import useConfigPanelStore from '../../store'; +import ParamInputSelect, { ParamInputSelectProps } from '../param-input-select'; +import EntitySelect from '../entity-select'; import './style.less'; type InputParamListType = { @@ -12,11 +16,18 @@ type InputParamListType = { type: ApiKey; value: ParamInputSelectProps['value']; }; -type ServiceParamAssignInputValueType = InputParamListType[]; +type ServiceParamsValueType = { + [key: string]: string; +}; +type ServiceParamAssignInputValueType = { + serviceEntity?: ApiKey; + paramList?: InputParamListType[]; + serviceParams: ServiceParamsValueType; +}; type ServiceParamAssignInputProps = { required?: boolean; disabled?: boolean; - serviceKey?: ApiKey; + helperText?: string; value?: ServiceParamAssignInputValueType; defaultValue?: ServiceParamAssignInputValueType; onChange?: (value: ServiceParamAssignInputValueType) => void; @@ -31,42 +42,61 @@ const ServiceParamAssignInput: React.FC = ({ required, disabled, defaultValue, - serviceKey, ...props }) => { + const { getIntlText } = useI18n(); + const { helperText = getIntlText('workflow.node.service_helptext') } = props; const { getEntityList } = useConfigPanelStore(useStoreShallow(['getEntityList'])); const { getEntityChildren } = useEntityApi(); const [innerValue, setInnerValue] = useControllableValue(props); - const { list, replace, resetList } = useDynamicList(innerValue); - const handlerChange = async (entityKey?: ApiKey) => { - if (entityKey) { - const entityFilterList = await getEntityList({ keyword: entityKey as string }); - if (entityFilterList?.length) { - const entityItem = entityFilterList[0]; - const { error, res } = await getEntityChildren({ - id: entityItem.entity_id, - }); - if (!error) { - resetList( - res.map((item: EntityItem) => { - const valueItem = innerValue?.find( - innerItem => innerItem.key === item.entity_key, - ); - return { - key: item.entity_key, - name: item.entity_name, - type: item.entity_value_type, - value: valueItem?.value ?? '', - }; - }), - ); - return; + const { list, replace, resetList } = useDynamicList([]); + const { run: handlerChange, cancel: cancelHandlerChange } = useDebounceFn( + async (serviceEntity?: ApiKey) => { + setInnerValue(pre => ({ ...pre, serviceEntity })); + if (serviceEntity) { + const entityFilterList = await getEntityList({ keyword: serviceEntity as string }); + if (entityFilterList?.length) { + const entityItem = entityFilterList[0]; + const { error, res } = await getEntityChildren({ + id: entityItem.entity_id, + }); + if (!error) { + resetList( + res.map((item: EntityItem) => { + const reg = /#{([^}]+)}/g; + const valueItem = + innerValue?.serviceParams?.[item.entity_key as string]; + let paramValue: ParamInputSelectProps['value'] = { + value: valueItem, + }; + if (valueItem) { + const match = reg.exec(valueItem); + if (match) { + const ref = match[1]; + if (ref) { + paramValue = { ref }; + } + } + } + return { + key: item.entity_key, + name: item.entity_name, + type: item.entity_value_type, + value: valueItem ? paramValue : undefined, + }; + }), + ); + return; + } } } - } - resetList([]); - }; + if (list.length) { + resetList([]); + } + }, + { wait: 100 }, + ); const renderInputParam = useMemo(() => { if (list.length) { return ( @@ -93,12 +123,45 @@ const ServiceParamAssignInput: React.FC = ({ } return null; }, [list]); + const transformParams = useCallback( + (paramList: InputParamListType[]): ServiceParamsValueType => { + const res: ServiceParamsValueType = {}; + paramList.forEach(item => { + const { value = '', ref } = item.value || {}; + res[item.key] = ref ? `#{${ref}}` : value; + }); + return res; + }, + [], + ); useLayoutEffect(() => { - setInnerValue(list); - }, [list]); + const serviceParams = innerValue?.serviceParams ?? {}; + if (isEqual(serviceParams, transformParams(list))) return; + handlerChange(innerValue?.serviceEntity); + }, [innerValue]); useLayoutEffect(() => { - handlerChange(serviceKey); - }, [serviceKey]); - return
{renderInputParam}
; + setInnerValue(pre => ({ ...pre, serviceParams: transformParams(list) })); + }, [list]); + return ( +
+ + +
+ {getIntlText('workflow.node.input_variables')} + {helperText && !innerValue?.serviceEntity && ( + + + + + + )} +
+ {renderInputParam} +
+ ); }; export default ServiceParamAssignInput; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 51312c51..b420d40f 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -177,11 +177,9 @@ const useNodeFormItems = (node?: WorkflowNode) => { break; } case 'serviceEntitySetting': { - // TODO: use the correct component instead formItem.render = ({ field: { onChange, value } }) => { return ( - diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 196d3422..aa774b24 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -106,5 +106,7 @@ "workflow.valid.invalid_node_type": "The {1} Node type is invalid.", "workflow.valid.invalid_edge_type": "The Edge (connecting node {1} and {2}) Type is invalid.", "workflow.valid.node_id_duplicated": "{1} and {2} Node ID is duplicated.", - "workflow.valid.edge_id_duplicated": "The Edge (connecting node {1} and {2}) ID is duplicated." + "workflow.valid.edge_id_duplicated": "The Edge (connecting node {1} and {2}) ID is duplicated.", + "workflow.node.service_helptext": "Please select the service you want to call first.", + "workflow.node.input_variables": "Input Variables" } From 579b285f86d7e2915a180ba52a41f390dd63d9c3 Mon Sep 17 00:00:00 2001 From: Nian Date: Thu, 26 Dec 2024 11:44:15 +0800 Subject: [PATCH 100/172] fix: integrate the API for the workflow list page --- .../web/src/pages/auth/views/useFormItems.tsx | 2 + .../src/pages/workflow/hooks/useColumns.tsx | 18 ++++-- apps/web/src/pages/workflow/index.tsx | 64 ++++++++++++++----- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/apps/web/src/pages/auth/views/useFormItems.tsx b/apps/web/src/pages/auth/views/useFormItems.tsx index 5e1f2395..2b10db17 100644 --- a/apps/web/src/pages/auth/views/useFormItems.tsx +++ b/apps/web/src/pages/auth/views/useFormItems.tsx @@ -45,6 +45,7 @@ const useFormItems = ({ mode = 'login' }: UseFormItemsProps) => { const items: ControllerProps[] = [ { name: 'email', + defaultValue: 'xiejc@milesight.com', rules: { validate: { checkRequired: checkRequired(), @@ -106,6 +107,7 @@ const useFormItems = ({ mode = 'login' }: UseFormItemsProps) => { }, { name: 'password', + defaultValue: 'Milesight@2024', rules: { validate: { checkRequired: checkRequired(), diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index 05d809d1..4daf576b 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -1,11 +1,11 @@ import { useMemo } from 'react'; -import { Stack, IconButton } from '@mui/material'; +import { Stack, IconButton, Switch } from '@mui/material'; import { useI18n, useTime } from '@milesight/shared/src/hooks'; import { ListAltIcon, DeleteOutlineIcon, EditIcon } from '@milesight/shared/src/components'; import { Tooltip, type ColumnType } from '@/components'; -import { type WorkflowAPISchema } from '@/services/http'; +import { workflowAPI, type WorkflowAPISchema } from '@/services/http'; -type OperationType = 'detail' | 'delete' | 'edit'; +type OperationType = 'detail' | 'delete' | 'edit' | 'enable'; export type TableRowDataType = ObjectToCamelCase< WorkflowAPISchema['getList']['response']['content'][0] @@ -61,11 +61,19 @@ const useColumns = ({ onButtonClick }: UseColumnsPro }, }, { - field: 'status', + field: 'enabled', headerName: getIntlText('common.label.enable_status'), - ellipsis: true, + // ellipsis: true, flex: 2, minWidth: 200, + renderCell({ row }) { + return ( + onButtonClick('enable', row)} + /> + ); + }, }, { field: '$operation', diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index f034efa3..c57e6094 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -12,7 +12,7 @@ import { toast, } from '@milesight/shared/src/components'; import { Breadcrumbs, TablePro, useConfirm } from '@/components'; -import { awaitWrap, isRequestSuccess, workflowAPI } from '@/services/http'; +import { awaitWrap, getResponseData, isRequestSuccess, workflowAPI } from '@/services/http'; import { type FormDataProps as ImportFormDataProps } from '@/pages/workflow/components/import-modal/hook/useImportFormItems'; import { ImportModal, LogModal } from '@/pages/workflow/components'; import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; @@ -32,23 +32,24 @@ const Workflow = () => { data: workflowList, loading, run: getWorkflowList, + mutate: updateWorkworkflowList, } = useRequest( async () => { // const { page, pageSize } = paginationModel; - // const [error, resp] = await awaitWrap( - // deviceAPI.getList({ - // name: keyword, - // page_size: pageSize, - // page_number: page + 1, - // }), - // ); - // const data = getResponseData(resp); + const [error, resp] = await awaitWrap( + workflowAPI.getList({ + name: keyword || '', + // page_size: pageSize, + // page_number: page + 1, + }), + ); + const data = getResponseData(resp); - // // console.log({ error, resp }); - // if (error || !data || !isRequestSuccess(resp)) return; + // console.log({ error, resp }); + if (error || !data || !isRequestSuccess(resp)) return; - // return objectToCamelCase(data); - return {}; + console.log('data ? ', data); + return objectToCamelCase(data); }, { debounceWait: 300, @@ -154,12 +155,16 @@ const Workflow = () => { handleDeleteConfirm([record.id]); break; } + case 'enable': { + handleSwitchChange(record); + break; + } default: { break; } } }, - [navigate, handleDeleteConfirm], + [workflowList, navigate, handleDeleteConfirm], ); const columns = useColumns({ onButtonClick: handleTableBtnClick }); const handleCloseLogModal = useCallback(() => setLogModalVisible(false), []); @@ -169,6 +174,33 @@ const Workflow = () => { }, [columns], ); + const handleSwitchChange = useCallback( + async (row: TableRowDataType) => { + if (!workflowList?.content) { + return; + } + const { enabled } = row; + const [error, res] = await awaitWrap( + workflowAPI.enableFlow({ + id: row.id, + status: enabled ? 'disable' : 'enable', + }), + ); + updateWorkworkflowList({ + ...workflowList, + content: workflowList?.content.map(item => + item.id === row.id + ? { + ...item, + enabled: + error || !isRequestSuccess(res) ? item.enabled : !item.enabled, + } + : item, + ), + }); + }, + [workflowList], + ); return (
@@ -178,8 +210,8 @@ const Workflow = () => { checkboxSelection loading={loading} columns={columns} - // rows={deviceData?.content} - // rowCount={workflowList?.total || 0} + rows={workflowList?.content} + rowCount={workflowList?.total || 0} paginationModel={paginationModel} rowSelectionModel={selectedIds} isRowSelectable={isRowSelectable} From 931645f6462045e060aa7fdcd4eb82b3af1f5ae9 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 19:30:27 +0800 Subject: [PATCH 101/172] fix: fix data coverage issues caused by verification --- .../workflow/views/editor/demo-data.json | 146 ------------------ .../views/editor/hooks/useWorkflow.ts | 20 ++- .../src/pages/workflow/views/editor/index.tsx | 103 ++++++------ 3 files changed, 64 insertions(+), 205 deletions(-) delete mode 100644 apps/web/src/pages/workflow/views/editor/demo-data.json diff --git a/apps/web/src/pages/workflow/views/editor/demo-data.json b/apps/web/src/pages/workflow/views/editor/demo-data.json deleted file mode 100644 index 50a22b15..00000000 --- a/apps/web/src/pages/workflow/views/editor/demo-data.json +++ /dev/null @@ -1,146 +0,0 @@ -{ - "nodes": [ - { - "id": "1", - "data": { - "nodeName": "Hello" - }, - "position": { - "x": 0, - "y": 0 - }, - "type": "trigger", - "componentName": "trigger" - }, - { - "id": "2", - "data": { - "nodeName": "World" - }, - "position": { - "x": 300, - "y": 100 - }, - "type": "ifelse", - "componentName": "choice" - }, - { - "id": "3", - "data": { - "nodeName": "Code" - }, - "position": { - "x": 600, - "y": 0 - }, - "type": "code", - "componentName": "code" - }, - { - "id": "4", - "data": { - "nodeName": "Service" - }, - "position": { - "x": 600, - "y": 100 - }, - "type": "service", - "componentName": "serviceInvocation" - }, - { - "id": "5", - "data": { - "nodeName": "Select" - }, - "position": { - "x": 600, - "y": 200 - }, - "type": "select", - "componentName": "entitySelector" - }, - { - "id": "6", - "data": {}, - "position": { - "x": 900, - "y": 0 - }, - "type": "assigner", - "componentName": "entityAssigner" - }, - { - "id": "7", - "data": { - "nodeName": "Email" - }, - "position": { - "x": 900, - "y": 100 - }, - "type": "email", - "componentName": "email" - }, - { - "id": "8", - "data": { - "nodeName": "Webhook", - "nodeRemark": "This a Webhook node" - }, - "position": { - "x": 900, - "y": 200 - }, - "type": "webhook", - "componentName": "webhook" - } - ], - "edges": [ - { - "id": "1_2", - "source": "1", - "target": "2", - "type": "addable" - }, - { - "id": "2_3", - "source": "2", - "target": "3", - "type": "addable", - "sourceHandle": "$temp:if" - }, - { - "id": "2_4", - "source": "2", - "target": "4", - "type": "addable", - "sourceHandle": "$temp:else" - }, - { - "id": "2_5", - "source": "2", - "target": "5", - "type": "addable", - "sourceHandle": "$temp:else" - }, - { - "id": "3_6", - "source": "3", - "target": "6", - "type": "addable" - }, - { - "id": "4_7", - "source": "4", - "target": "7", - "type": "addable" - }, - { - "id": "5_8", - "source": "5", - "target": "8", - "type": "addable" - } - ] -} diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index bc01b241..30cf79aa 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -197,8 +197,11 @@ const useWorkflow = () => { // Get all upstream nodes of the current node const getUpstreamNodes = useCallback( - (currentNode?: WorkflowNode) => { + (currentNode?: WorkflowNode, nodes?: WorkflowNode[], edges?: WorkflowEdge[]) => { + nodes = nodes || getNodes(); + edges = edges || getEdges(); currentNode = currentNode || getSelectedNode(); + const getAllIncomers = ( node: WorkflowNode, data: Record = {}, @@ -222,7 +225,7 @@ const useWorkflow = () => { return getAllIncomers(currentNode!); }, - [nodes, edges, getSelectedNode], + [getNodes, getEdges, getSelectedNode], ); const getUpstreamNodeParams = useCallback( @@ -230,7 +233,7 @@ const useWorkflow = () => { currentNode = currentNode || getSelectedNode(); if (!currentNode) return []; - const incomeNodes = getUpstreamNodes(currentNode); + const incomeNodes = getUpstreamNodes(currentNode, nodes, edges); // TODO: get the correct nodes params const result: NodeParamType[] = incomeNodes.map(node => ({ nodeId: node.id, @@ -265,19 +268,20 @@ const useWorkflow = () => { return [result, flattenResult]; }, - [getSelectedNode, getUpstreamNodes], + [nodes, edges, getSelectedNode, getUpstreamNodes], ); // Check if there is a node that is not connected to an entry node const checkFreeNodeLimit = useCallback( - (nodes?: WorkflowNode[]) => { + (nodes?: WorkflowNode[], edges?: WorkflowEdge[]) => { nodes = nodes || getNodes(); + edges = edges || getEdges(); let result = false; result = nodes .filter(node => !entryNodeTypes.includes(node.type as WorkflowNodeType)) .some(node => { - const upstreamNodes = getUpstreamNodes(node); + const upstreamNodes = getUpstreamNodes(node, nodes, edges); const hasEntryNode = upstreamNodes.some(item => entryNodeTypes.includes(item.type as WorkflowNodeType), ); @@ -299,8 +303,8 @@ const useWorkflow = () => { // Update node status const updateNodesStatus = useCallback( - (data: Record | null, nodes?: WorkflowNode[]) => { - nodes = cloneDeep(nodes || getNodes()); + (data: Record | null) => { + const nodes = cloneDeep(getNodes()); if (!data) { nodes.forEach(node => { diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 3fcc716d..5e06dd22 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -1,7 +1,7 @@ import { memo, useState, useCallback } from 'react'; import { useSearchParams, useNavigate } from 'react-router-dom'; import { useRequest } from 'ahooks'; -import { omitBy, merge, isEmpty } from 'lodash-es'; +import { omitBy, merge, isEmpty, cloneDeep } from 'lodash-es'; import { ReactFlow, Background, @@ -41,7 +41,6 @@ import { type DesignMode, type TopbarProps, } from './components'; -import demoData from './demo-data.json'; import '@xyflow/react/dist/style.css'; import './style.less'; @@ -71,21 +70,17 @@ const WorkflowEditor = () => { const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { checkNodesId, checkNodesType, checkNodesData, checkEdgesId, checkEdgesType } = useValidate(); - const checkWorkflowValid = useCallback(() => { - const { nodes, edges } = toObject(); - if (!checkNodeNumberLimit(nodes)) return false; - if (checkFreeNodeLimit(nodes)) return false; - if (!checkNestedParallelLimit(nodes, edges)) return false; - if (nodes.some(node => !checkParallelLimit(node.id, undefined, edges))) return false; - - return true; - }, [ - toObject, - checkNodeNumberLimit, - checkFreeNodeLimit, - checkNestedParallelLimit, - checkParallelLimit, - ]); + const checkWorkflowValid = useCallback( + (nodes: WorkflowNode[], edges: WorkflowEdge[]) => { + if (!checkNodeNumberLimit(nodes)) return false; + if (checkFreeNodeLimit(nodes, edges)) return false; + if (!checkNestedParallelLimit(nodes, edges)) return false; + if (nodes.some(node => !checkParallelLimit(node.id, undefined, edges))) return false; + + return true; + }, + [checkNodeNumberLimit, checkFreeNodeLimit, checkNestedParallelLimit, checkParallelLimit], + ); const confirm = useConfirm(); // ---------- Prevent Leave ---------- @@ -162,36 +157,33 @@ const WorkflowEditor = () => { if (wid) return { id: wid }; }); const [flowDataLoading, setFlowDataLoading] = useState(); - const { - // loading, - // data: flowData, - run: getFlowDesign, - } = useRequest( + + useRequest( async () => { if (!wid) return; setFlowDataLoading(true); - // TODO: Call workflow detail API const [error, resp] = await awaitWrap(workflowAPI.getFlowDesign({ id: wid, version })); - // await new Promise(resolve => { - // setTimeout(resolve, 500); - // }); setFlowDataLoading(false); - // if (error || !isRequestSuccess(resp)) return; - // const data = getResponseData(resp); - // console.log(data); + if (error || !isRequestSuccess(resp)) return; + const data = getResponseData(resp); + const { design_data: designData, ...basicData } = data || {}; + let flowData: Pick; - setFlowDataLoading(false); - setNodes(demoData.nodes as WorkflowNode[]); - setEdges(demoData.edges as WorkflowEdge[]); - setBasicData({ - id: 'xxx', - name: 'Workflow Name', - remark: 'Workflow Remark', - enabled: false, - }); + console.log(data); + try { + flowData = JSON.parse(designData || '{}'); + } catch (e) { + console.warn(e); + toast.error({ content: getIntlText('common.message.json_format_error') }); + return; + } + + setNodes(flowData?.nodes); + setEdges(flowData?.edges); + setBasicData(basicData); - return { id: 'xxx', name: 'Workflow Name', remark: 'Workflow Remark', enabled: false }; + return data; }, { debounceWait: 300, @@ -208,22 +200,22 @@ const WorkflowEditor = () => { const [editorFlowData, setEditorFlowData] = useState(); const handleDesignModeChange = useCallback( (mode: DesignMode) => { - if (!checkWorkflowValid()) return; - if (mode === 'advanced') { - const { nodes, edges } = toObject(); + const { nodes, edges } = cloneDeep(toObject()); const newNodes = nodes.map(node => { const result = omitBy(node, (_, key) => FROZEN_NODE_PROPERTY_KEYS.includes(key), ); result.data = omitBy(node.data, (_, key) => checkPrivateProperty(key)); - return result; + return result as WorkflowNode; }); const newEdges = edges.map(edge => { edge.data = omitBy(edge.data, (_, key) => checkPrivateProperty(key)); return edge; }); + if (!checkWorkflowValid(newNodes, newEdges)) return; + setEditorFlowData(JSON.stringify({ nodes: newNodes, edges: newEdges }, null, 2)); } else if (mode === 'canvas') { let data: Pick; @@ -237,6 +229,7 @@ const WorkflowEditor = () => { } const { nodes, edges } = data; + if (!checkWorkflowValid(nodes, edges)) return; if ( checkNodesId(nodes, { validateFirst: true }) || checkNodesType(nodes, { validateFirst: true }) || @@ -270,8 +263,7 @@ const WorkflowEditor = () => { const navigate = useNavigate(); const [saveLoading, setSaveLoading] = useState(false); const handleSave = async () => { - if (!checkWorkflowValid()) return; - const flowData = toObject(); + const flowData = cloneDeep(toObject()); const isAdvanceMode = designMode === 'advanced'; if (isAdvanceMode) { @@ -291,6 +283,9 @@ const WorkflowEditor = () => { const { nodes, edges, viewport } = flowData; + console.log({ nodes, edges }); + if (!checkWorkflowValid(nodes, edges)) return; + const edgesCheckResult = merge( checkEdgesId(edges, nodes, { validateFirst: isAdvanceMode }), checkEdgesType(edges, nodes, { validateFirst: isAdvanceMode }), @@ -310,6 +305,7 @@ const WorkflowEditor = () => { ); console.log({ nodesCheckResult }); if (!isEmpty(nodesCheckResult)) { + if (isAdvanceMode) return; const statusData = Object.entries(nodesCheckResult).reduce( (acc, [id, item]) => { acc[id] = item.status; @@ -319,16 +315,13 @@ const WorkflowEditor = () => { ); setNodesDataValidResult(nodesCheckResult); - updateNodesStatus(statusData, nodes); + updateNodesStatus(statusData); return; } updateNodesStatus(null); setNodesDataValidResult(null); - if (!basicData) { - // TODO: data validate - return; - } + if (!basicData?.name) return; const hasTriggerNode = nodes.find(node => node.type === 'trigger'); @@ -348,6 +341,14 @@ const WorkflowEditor = () => { if (!proceed) return; } + // remove private property + nodes.forEach(node => { + node.data = omitBy(node.data, (_, key) => checkPrivateProperty(key)); + }); + edges.forEach(edge => { + edge.data = omitBy(edge.data, (_, key) => checkPrivateProperty(key)); + }); + // TODO: referenced warning confirm ? setSaveLoading(true); @@ -356,7 +357,7 @@ const WorkflowEditor = () => { name: basicData.name!, remark: basicData.remark!, enabled: basicData.enabled!, - design_data: JSON.stringify({ nodes, edges }), + design_data: JSON.stringify({ nodes, edges, viewport }), }), ); From eb6d51fb07303c597bccb13995ba7c9ba85c3b8b Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 19:35:17 +0800 Subject: [PATCH 102/172] fix: fix navigate prevent error on save success --- apps/web/src/pages/workflow/views/editor/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 5e06dd22..41f2bcd5 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -368,7 +368,8 @@ const WorkflowEditor = () => { console.log(data); toast.success(getIntlText('common.message.operation_success')); - navigate('/workflow'); + setIsPreventLeave(false); + setTimeout(() => navigate('/workflow'), 0); }; return ( From 1d6985a66b9fa624f34585950e1ce8598b0c2251 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 26 Dec 2024 19:42:34 +0800 Subject: [PATCH 103/172] fix: add id, version params when is in editing --- apps/web/src/pages/workflow/views/editor/index.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 41f2bcd5..23081a61 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -354,6 +354,8 @@ const WorkflowEditor = () => { setSaveLoading(true); const [error, resp] = await awaitWrap( workflowAPI.saveFlowDesign({ + id: wid || undefined, + version, name: basicData.name!, remark: basicData.remark!, enabled: basicData.enabled!, From a22ae5535e1c94a1a537d9172c5060107719702c Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 26 Dec 2024 20:20:33 +0800 Subject: [PATCH 104/172] feat: Log modal component docking interface --- .../components/log-modal/components/index.tsx | 1 + .../components/log-right-bar/hooks/index.tsx | 1 + .../log-right-bar/hooks/useLogDetail.tsx | 78 ++++ .../components/log-right-bar/index.tsx | 44 +++ .../components/log-right-bar/style.less | 11 + .../log-modal/hooks/useSourceData.tsx | 35 +- .../workflow/components/log-modal/index.tsx | 32 +- .../workflow/components/log-modal/style.less | 12 - .../workflow/components/log-modal/trace.json | 129 ------- .../workflow/components/log-modal/types.d.ts | 4 + .../components/log-modal/workflow.json | 348 ------------------ apps/web/src/pages/workflow/index.tsx | 13 +- apps/web/src/services/http/workflow.ts | 9 +- 13 files changed, 177 insertions(+), 540 deletions(-) create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/index.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/index.tsx create mode 100644 apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/style.less delete mode 100644 apps/web/src/pages/workflow/components/log-modal/trace.json delete mode 100644 apps/web/src/pages/workflow/components/log-modal/workflow.json diff --git a/apps/web/src/pages/workflow/components/log-modal/components/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/index.tsx index 890cfb94..7f8c797e 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/components/index.tsx @@ -1 +1,2 @@ export { default as LogItem } from './log-item'; +export { default as LogRightBar } from './log-right-bar'; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/index.tsx new file mode 100644 index 00000000..65b2187b --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/index.tsx @@ -0,0 +1 @@ +export { useLogDetail } from './useLogDetail'; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx new file mode 100644 index 00000000..e40adeea --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx @@ -0,0 +1,78 @@ +import { useEffect, useState } from 'react'; +import { useRequest } from 'ahooks'; +import { awaitWrap, getResponseData, workflowAPI, type FlowNodeTraceInfo } from '@/services/http'; +import type { LogRenderListType, WorkflowData } from '../../../types'; +import type { WorkflowDataType } from '../../../../action-log/types'; + +export const useLogDetail = ({ + activeItem, + data, +}: { + activeItem?: LogRenderListType; + data: WorkflowData; +}) => { + const [loading, setLoading] = useState(false); + const [actionLogResult, setActionLogResult] = useState< + [WorkflowDataType | void, FlowNodeTraceInfo[]] + >([void 0, []]); + const [workflowData, traceData] = actionLogResult || []; + + // Interface to obtain log details + const { runAsync: getLogDetail } = useRequest( + async () => { + if (!activeItem) return []; + + const { id } = activeItem || {}; + const [error, resp] = await awaitWrap(workflowAPI.getLogDetail({ id })); + if (error) return []; + + return getResponseData(resp)?.trace_info || []; + }, + { manual: true }, + ); + // Interface to obtain the design of the workflow + const { runAsync: getFlowDesign } = useRequest( + async () => { + if (!activeItem) return; + + const { id } = data || {}; + const resp = await workflowAPI.getFlowDesign({ + id, + // TODO Get the specified version + version: '', + }); + + const designData = getResponseData(resp)?.design_data; + if (!designData) return; + + try { + return JSON.parse(designData) as WorkflowDataType; + } catch (e) { + // eslint-disable-next-line no-console + console.error(e); + } + }, + { manual: true }, + ); + + /** get data function */ + const getList = async () => { + setLoading(true); + const [_, result] = await awaitWrap(Promise.all([getFlowDesign(), getLogDetail()])); + setLoading(false); + + const [workflowData, traceData] = result || []; + setActionLogResult([workflowData, traceData || []]); + }; + useEffect(() => { + if (!activeItem) return; + + getList(); + }, [activeItem]); + + return { + actionLoading: loading, + workflowData, + traceData, + }; +}; diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/index.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/index.tsx new file mode 100644 index 00000000..8771d7cb --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/index.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import { CircularProgress } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { Empty, Tooltip } from '@/components'; +import { useLogDetail } from './hooks'; +import ActionLog from '../../../action-log'; +import type { LogRenderListType, WorkflowData } from '../../types'; +import './style.less'; + +export interface IProps { + data: WorkflowData; + activeItem?: LogRenderListType; +} +export default React.memo(({ activeItem, data }: IProps) => { + const { getIntlText } = useI18n(); + const { actionLoading, traceData, workflowData } = useLogDetail({ activeItem, data }); + + const isLoading = !!actionLoading; + const isEmpty = !actionLoading && !workflowData; + return ( +
+ {isLoading && ( +
+ +
+ )} + {isEmpty && ( +
+ +
+ )} + {!isLoading && !isEmpty && ( + <> +
+ +
+
+ +
+ + )} +
+ ); +}); diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/style.less b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/style.less new file mode 100644 index 00000000..9e564891 --- /dev/null +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/style.less @@ -0,0 +1,11 @@ +.ms-log-right-bar { + flex: 1; + padding: @padding-lg; + overflow-y: auto; + + &__title { + margin-bottom: @margin-sm; + font-weight: @font-weight-medium; + .text-size(@font-size-xxl); + } +} diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx index 48ccb726..7541bc2b 100644 --- a/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useSourceData.tsx @@ -1,34 +1,21 @@ -import { useCallback } from 'react'; -import { delay, generateUUID, withPromiseResolvers } from '@milesight/shared/src/utils/tools'; -import type { PaginationModel, LogListPageType } from '../types'; +import { useRequest } from 'ahooks'; +import { getResponseData, workflowAPI } from '@/services/http'; +import type { PaginationModel, WorkflowData } from '../types'; -export const useSourceData = () => { - // TODO - const generateList = useCallback((limit: number): LogListPageType['content'] => { - return Array.from({ length: limit }).map(() => ({ - id: generateUUID(), - status: Math.random() > 0.5 ? 'SUCCESS' : 'ERROR', - start_time: Date.now(), - time_cost: Math.floor(Math.random() * 1000), - })); - }, []); - // TODO - const getLogList = useCallback( +export const useSourceData = ({ data }: { data: WorkflowData }) => { + const { runAsync: getLogList } = useRequest( async (pageInfo: PaginationModel) => { + const { id } = data || {}; const { page, pageSize } = pageInfo || {}; - const { promise, resolve } = withPromiseResolvers(); - await delay(1000); - - resolve({ - page_size: pageSize, + const resp = await workflowAPI.getLogList({ + id, page_number: page, - content: generateList(pageSize), - total: 100, + page_size: pageSize, }); - return promise; + return getResponseData(resp)!; }, - [generateList], + { manual: true }, ); return { diff --git a/apps/web/src/pages/workflow/components/log-modal/index.tsx b/apps/web/src/pages/workflow/components/log-modal/index.tsx index ca8de21b..ac3d229a 100644 --- a/apps/web/src/pages/workflow/components/log-modal/index.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/index.tsx @@ -2,25 +2,22 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { CircularProgress } from '@mui/material'; import { Modal, type ModalProps } from '@milesight/shared/src/components'; import { useI18n } from '@milesight/shared/src/hooks'; -import { Empty, Tooltip } from '@/components'; -import { LogItem } from './components'; +import { Empty } from '@/components'; +import { LogItem, LogRightBar } from './components'; import { useRenderList, useSourceData } from './hooks'; -import ActionLog from '../action-log'; -import type { LogRenderListType } from './types'; +import type { LogRenderListType, WorkflowData } from './types'; import './style.less'; -// TODO mock Data -import traceData from './trace.json'; -import workflowData from './workflow.json'; - -export type IProps = ModalProps; -export default React.memo(({ visible, ...props }: IProps) => { +export interface IProps extends ModalProps { + data: WorkflowData; +} +export default React.memo(({ visible, data, ...props }: IProps) => { const containerRef = useRef(null); const listRef = useRef(null); const { getIntlText } = useI18n(); const [activeItem, setActiveItem] = useState(); - const { getLogList } = useSourceData(); + const { getLogList } = useSourceData({ data }); const { scrollItem, virtualList, getLogListLoading } = useRenderList({ containerRef, listRef, @@ -88,18 +85,7 @@ export default React.memo(({ visible, ...props }: IProps) => {
-
-
- -
-
- -
-
+ )} diff --git a/apps/web/src/pages/workflow/components/log-modal/style.less b/apps/web/src/pages/workflow/components/log-modal/style.less index 5f269715..fc798dcf 100644 --- a/apps/web/src/pages/workflow/components/log-modal/style.less +++ b/apps/web/src/pages/workflow/components/log-modal/style.less @@ -43,16 +43,4 @@ padding: @padding-xxs 0 @padding-sm 0; } } - - .ms-log-right-bar { - flex: 1; - padding: @padding-lg; - overflow-y: auto; - - &__title { - margin-bottom: @margin-sm; - font-weight: @font-weight-medium; - .text-size(@font-size-xxl); - } - } } diff --git a/apps/web/src/pages/workflow/components/log-modal/trace.json b/apps/web/src/pages/workflow/components/log-modal/trace.json deleted file mode 100644 index 0b608881..00000000 --- a/apps/web/src/pages/workflow/components/log-modal/trace.json +++ /dev/null @@ -1,129 +0,0 @@ -{ - "traceInfo": [ - { - "message_id": "msg_001", - "node_id": "node:1734403663609:tqhoyisd", - "node_label": "Trigger Node", - "status": "ERROR", - "time_cost": 100, - "start_time": 1672531200000, - "ERROR_message": "xxxx" - }, - { - "message_id": "msg_002", - "node_id": "node:1734403696051:mmijxipj", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 150, - "start_time": 1672531300000 - }, - { - "message_id": "msg_003", - "node_id": "node:1734403741746:cvlebfnk", - "node_label": "Code Node", - "status": "ERROR", - "time_cost": 200, - "start_time": 1672531400000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_004", - "node_id": "node:1734403754076:tunkfwyp", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 120, - "start_time": 1672531500000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_005", - "node_id": "node:1734403760131:nxrtmtqt", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 110, - "start_time": 1672531600000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_006", - "node_id": "node:1734403762609:tojsahsk", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 130, - "start_time": 1672531700000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_007", - "node_id": "node:1734403764786:iwvgqkbl", - "node_label": "Code Node", - "status": "ERROR", - "time_cost": 180, - "start_time": 1672531800000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_008", - "node_id": "node:1734403768531:pioadrla", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 90, - "start_time": 1672531900000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_009", - "node_id": "node:1734403779597:krdbgvbs", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 140, - "start_time": 1672532000000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_010", - "node_id": "node:1734403781283:dwnkqolz", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 160, - "start_time": 1672532100000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_011", - "node_id": "node:1734403783794:wjhlqruk", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 110, - "start_time": 1672532200000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_012", - "node_id": "node:1734403795500:xceqicsa", - "node_label": "Code Node", - "status": "ERROR", - "time_cost": 170, - "start_time": 1672532300000, - "input": "{\"param1\": \"value1\"}", - "output": "{\"result\": \"processed\"}" - }, - { - "message_id": "msg_013", - "node_id": "node:1734403803395:tcmtlast", - "node_label": "Code Node", - "status": "SUCCESS", - "time_cost": 100, - "start_time": 1672532400000 - } - ] -} diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts index df39ede7..438ff383 100644 --- a/apps/web/src/pages/workflow/components/log-modal/types.d.ts +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -29,3 +29,7 @@ export interface InfiniteScrollType { source: LogListPageType; hasMore: boolean; } + +export type WorkflowData = ObjectToCamelCase< + WorkflowAPISchema['getList']['response']['content'][number] +>; diff --git a/apps/web/src/pages/workflow/components/log-modal/workflow.json b/apps/web/src/pages/workflow/components/log-modal/workflow.json deleted file mode 100644 index 3389f8dd..00000000 --- a/apps/web/src/pages/workflow/components/log-modal/workflow.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "nodes": [ - { - "id": "node:1734403663609:tqhoyisd", - "type": "trigger", - "position": { - "x": 0, - "y": 0 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "zIndex": 0, - "selected": false, - "$$name": "START" - }, - { - "id": "node:1734403696051:mmijxipj", - "type": "code", - "position": { - "x": 290, - "y": 0 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "zIndex": 0, - "$$name": "CODE 1" - }, - { - "id": "node:1734403741746:cvlebfnk", - "type": "code", - "position": { - "x": 580, - "y": 0 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "zIndex": 0, - "selected": false, - "$$name": "CODE 4" - }, - { - "id": "node:1734403754076:tunkfwyp", - "type": "code", - "position": { - "x": 580, - "y": 100 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "zIndex": 0, - "selected": false, - "$$name": "CODE 2" - }, - { - "id": "node:1734403760131:nxrtmtqt", - "type": "code", - "position": { - "x": 870, - "y": 100 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "zIndex": 0, - "$$name": "CODE 6" - }, - { - "id": "node:1734403762609:tojsahsk", - "type": "code", - "position": { - "x": 870, - "y": 200 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "zIndex": 0, - "$$name": "CODE 12" - }, - { - "id": "node:1734403764786:iwvgqkbl", - "type": "code", - "position": { - "x": 870, - "y": 300 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "zIndex": 0, - "$$name": "代码 6" - }, - { - "id": "node:1734403768531:pioadrla", - "type": "code", - "position": { - "x": 1293, - "y": -35.00000000000001 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "dragging": false, - "$$name": "CODE 7" - }, - { - "id": "node:1734403779597:krdbgvbs", - "type": "code", - "position": { - "x": 1176, - "y": 150 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "dragging": false, - "$$name": "代码 9" - }, - { - "id": "node:1734403781283:dwnkqolz", - "type": "code", - "position": { - "x": 1176, - "y": 226.99999999999997 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "zIndex": 0, - "dragging": false, - "$$name": "代码 11" - }, - { - "id": "node:1734403783794:wjhlqruk", - "type": "code", - "position": { - "x": 1176, - "y": 300 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "dragging": false, - "zIndex": 0, - "$$name": "代码 7" - }, - { - "id": "node:1734403795500:xceqicsa", - "type": "code", - "position": { - "x": 1176, - "y": 400 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "zIndex": 0, - "selected": false, - "dragging": false, - "$$name": "代码 8" - }, - { - "id": "node:1734403803395:tcmtlast", - "type": "code", - "position": { - "x": 1466, - "y": 400 - }, - "data": {}, - "measured": { - "width": 240, - "height": 50 - }, - "selected": false, - "$$name": "代码 12" - } - ], - "edges": [ - { - "id": "edge:1734403696051:haikqpgy", - "type": "addable", - "source": "node:1734403663609:tqhoyisd", - "target": "node:1734403696051:mmijxipj", - "data": { - "$hovering": false - }, - "selected": false, - "$$name": "start" - }, - { - "id": "edge:1734403741746:dhqkxsqr", - "type": "addable", - "source": "node:1734403696051:mmijxipj", - "target": "node:1734403741746:cvlebfnk", - "data": { - "$hovering": false - }, - "selected": false, - "$$name": "CODE 1" - }, - { - "id": "edge:1734403754076:gbofxrfa", - "type": "addable", - "source": "node:1734403696051:mmijxipj", - "target": "node:1734403754076:tunkfwyp", - "selected": false, - "$$name": "CODE 4" - }, - { - "id": "edge:1734403760131:lojptzjc", - "type": "addable", - "source": "node:1734403754076:tunkfwyp", - "target": "node:1734403760131:nxrtmtqt", - "data": { - "$hovering": false - }, - "selected": false, - "$$name": "CODE 2" - }, - { - "id": "edge:1734403762609:psbmmpcl", - "type": "addable", - "source": "node:1734403754076:tunkfwyp", - "target": "node:1734403762609:tojsahsk", - "data": { - "$hovering": false - }, - "selected": false, - "$$name": "CODE 6" - }, - { - "id": "edge:1734403764786:siwnxxoz", - "type": "addable", - "source": "node:1734403754076:tunkfwyp", - "target": "node:1734403764786:iwvgqkbl", - "selected": false, - "$$name": "CODE 12" - }, - { - "id": "edge:1734403768531:rzwgnydv", - "type": "addable", - "source": "node:1734403741746:cvlebfnk", - "target": "node:1734403768531:pioadrla", - "selected": false, - "$$name": "代码 6" - }, - { - "source": "node:1734403760131:nxrtmtqt", - "sourceHandle": null, - "target": "node:1734403768531:pioadrla", - "targetHandle": null, - "id": "edge:1734403776080:vlmahrru", - "type": "addable", - "data": { - "$hovering": false - }, - "selected": false, - "$$name": "CODE 7" - }, - { - "id": "edge:1734403779597:qjaweigh", - "type": "addable", - "source": "node:1734403762609:tojsahsk", - "target": "node:1734403779597:krdbgvbs", - "data": { - "$hovering": false - }, - "selected": false, - "$$name": "代码 9" - }, - { - "id": "edge:1734403781283:wiremvxr", - "type": "addable", - "source": "node:1734403762609:tojsahsk", - "target": "node:1734403781283:dwnkqolz", - "selected": false, - "$$name": "代码 11" - }, - { - "id": "edge:1734403783794:yztayqju", - "type": "addable", - "source": "node:1734403764786:iwvgqkbl", - "target": "node:1734403783794:wjhlqruk", - "selected": false, - "data": { - "$hovering": false - }, - "$$name": "代码 7" - }, - { - "id": "edge:1734403795500:lflujxjh", - "type": "addable", - "source": "node:1734403764786:iwvgqkbl", - "target": "node:1734403795500:xceqicsa", - "selected": false, - "$$name": "代码 8" - }, - { - "id": "edge:1734403803395:uwqdlnzp", - "type": "addable", - "source": "node:1734403795500:xceqicsa", - "target": "node:1734403803395:tcmtlast", - "selected": false, - "$$name": "代码 12" - } - ], - "viewport": { - "x": 119.37734719140894, - "y": 263.2629915858977, - "zoom": 0.49999999999999983 - } -} diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index c57e6094..79a766a5 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -28,6 +28,7 @@ const Workflow = () => { const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); const [selectedIds, setSelectedIds] = useState([]); const [logModalVisible, setLogModalVisible] = useState(false); + const [workflowItem, setWorkflowItem] = useState(); const { data: workflowList, loading, @@ -138,6 +139,12 @@ const Workflow = () => { [navigate], ); + /** Wake up log pop-up window */ + const handleLog = useCallback((record: TableRowDataType) => { + setLogModalVisible(true); + setWorkflowItem(record); + }, []); + const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); @@ -227,7 +234,11 @@ const Workflow = () => { {logModalVisible && ( - + )} Date: Thu, 26 Dec 2024 20:43:59 +0800 Subject: [PATCH 105/172] fix: adjust the workflow list page --- .../src/pages/workflow/hooks/useColumns.tsx | 112 ++++++++++++++---- apps/web/src/pages/workflow/index.tsx | 4 + apps/web/src/pages/workflow/style.less | 17 +++ packages/locales/src/lang/en/global.json | 4 +- .../shared/src/components/icons/index.tsx | 2 + 5 files changed, 115 insertions(+), 24 deletions(-) diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index 4daf576b..192d6cc8 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -1,11 +1,17 @@ -import { useMemo } from 'react'; -import { Stack, IconButton, Switch } from '@mui/material'; +import React, { useCallback, useMemo, useState } from 'react'; +import { Stack, IconButton, Switch, Menu, MenuItem } from '@mui/material'; import { useI18n, useTime } from '@milesight/shared/src/hooks'; -import { ListAltIcon, DeleteOutlineIcon, EditIcon } from '@milesight/shared/src/components'; +import { + ListAltIcon, + DeleteOutlineIcon, + EditIcon, + MoreVertIcon, + IosShareIcon, +} from '@milesight/shared/src/components'; import { Tooltip, type ColumnType } from '@/components'; import { workflowAPI, type WorkflowAPISchema } from '@/services/http'; -type OperationType = 'detail' | 'delete' | 'edit' | 'enable'; +type OperationType = 'detail' | 'delete' | 'edit' | 'enable' | 'export'; export type TableRowDataType = ObjectToCamelCase< WorkflowAPISchema['getList']['response']['content'][0] @@ -21,7 +27,22 @@ export interface UseColumnsProps { const useColumns = ({ onButtonClick }: UseColumnsProps) => { const { getIntlText } = useI18n(); const { getTimeFormat } = useTime(); - + const [anchorEl, setAnchorEl] = useState(null); + const [popoverId, setPopoverId] = useState(''); + const handlerPopoverClose = useCallback( + (e: React.MouseEvent, id: string) => { + setPopoverId(''); + setAnchorEl(null); + }, + [popoverId, anchorEl], + ); + const handlerPopoverOpen = useCallback( + (e: React.MouseEvent, id: string) => { + setPopoverId(id); + setAnchorEl(e.currentTarget); + }, + [popoverId, anchorEl], + ); const columns: ColumnType[] = useMemo(() => { return [ { @@ -60,11 +81,18 @@ const useColumns = ({ onButtonClick }: UseColumnsPro return getTimeFormat(value); }, }, + { + field: 'userNickname', + headerName: getIntlText('common.label.creator'), + flex: 1, + minWidth: 150, + ellipsis: true, + }, { field: 'enabled', headerName: getIntlText('common.label.enable_status'), // ellipsis: true, - flex: 2, + flex: 1, minWidth: 200, renderCell({ row }) { return ( @@ -78,7 +106,7 @@ const useColumns = ({ onButtonClick }: UseColumnsPro { field: '$operation', headerName: getIntlText('common.label.operation'), - flex: 2, + flex: 1, minWidth: 100, renderCell({ row }) { return ( @@ -103,27 +131,65 @@ const useColumns = ({ onButtonClick }: UseColumnsPro - - onButtonClick('delete', row)} - > - - - + handlerPopoverOpen(e, row.id as string)} + > + + + + onButtonClick('export', row)}> + + + + {getIntlText('common.label.export')} + + + + onButtonClick('delete', row)}> + + + + {getIntlText('common.label.delete')} + + + + ); }, }, ]; - }, [getIntlText, getTimeFormat, onButtonClick]); + }, [getIntlText, getTimeFormat, onButtonClick, popoverId, anchorEl]); return columns; }; diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 79a766a5..04a8bf52 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -166,6 +166,10 @@ const Workflow = () => { handleSwitchChange(record); break; } + case 'export': { + // handleSwitchChange(record); + break; + } default: { break; } diff --git a/apps/web/src/pages/workflow/style.less b/apps/web/src/pages/workflow/style.less index 5f26f5f7..52d6c2bc 100644 --- a/apps/web/src/pages/workflow/style.less +++ b/apps/web/src/pages/workflow/style.less @@ -14,4 +14,21 @@ &.modal-waringIcon { fill: var(--orange-6); } +} +.@{prefix}-workflow-list-more-menu { + .MuiList-root { + width: 120px; + height: auto; + + .MuiMenuItem-root { + display: flex; + align-items: center; + justify-content: center; + } + } + + &-item-text { + margin-left: @margin-xs; + } + } \ No newline at end of file diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 7e5c318d..a837a2f0 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -168,5 +168,7 @@ "common.label.input": "Input", "common.label.output": "Output", "common.message.json_format_error": "JSON 格式错误", - "common.label.tip": "Tip" + "common.label.tip": "Tip", + "common.label.export": "Export", + "common.label.creator": "Creator" } diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 7ce3846a..81e58849 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -97,6 +97,8 @@ export { Sync as SyncIcon, MoreHoriz as MoreHorizIcon, Help as HelpIcon, + MoreVert as MoreVertIcon, + IosShare as IosShareIcon, } from '@mui/icons-material'; export * from './iot-icons'; From 0663e85164753c00f00b73af23ee7327e4f52599 Mon Sep 17 00:00:00 2001 From: Nian Date: Thu, 26 Dec 2024 21:11:54 +0800 Subject: [PATCH 106/172] fix: workflow edit & export --- apps/web/src/pages/workflow/index.tsx | 43 ++++++++++++++++++++++++-- apps/web/src/services/http/workflow.ts | 2 ++ 2 files changed, 42 insertions(+), 3 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 04a8bf52..8991f0d8 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -12,7 +12,13 @@ import { toast, } from '@milesight/shared/src/components'; import { Breadcrumbs, TablePro, useConfirm } from '@/components'; -import { awaitWrap, getResponseData, isRequestSuccess, workflowAPI } from '@/services/http'; +import { + WorkflowAPISchema, + awaitWrap, + getResponseData, + isRequestSuccess, + workflowAPI, +} from '@/services/http'; import { type FormDataProps as ImportFormDataProps } from '@/pages/workflow/components/import-modal/hook/useImportFormItems'; import { ImportModal, LogModal } from '@/pages/workflow/components'; import { useColumns, type UseColumnsProps, type TableRowDataType } from './hooks'; @@ -144,13 +150,44 @@ const Workflow = () => { setLogModalVisible(true); setWorkflowItem(record); }, []); + const exportJsonFile = useCallback( + (workflowItem: WorkflowAPISchema['getFlowDesign']['response']) => { + const { name, design_data: designData } = workflowItem; + const blob = new Blob([JSON.stringify(JSON.parse(designData), null, 4)], { + type: 'application/json', + }); + const fileName = `${name}.json`; + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = fileName; + document.body.appendChild(a); + a.click(); + window.URL.revokeObjectURL(url); + document.body.removeChild(a); + }, + [workflowList], + ); + const handleExportWorkFlow = useCallback( + async (record: TableRowDataType) => { + const [error, resp] = await awaitWrap( + workflowAPI.getFlowDesign({ + id: record.id, + version: record.version, + }), + ); + if (error || !isRequestSuccess(resp)) return; + exportJsonFile(getResponseData(resp) as WorkflowAPISchema['getFlowDesign']['response']); + }, + [workflowList], + ); const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); switch (type) { case 'edit': { - navigate(`/workflow/editor?wid=${record.id}`); + navigate(`/workflow/editor?wid=${record.id}&version=${record.version}`); break; } case 'detail': { @@ -167,7 +204,7 @@ const Workflow = () => { break; } case 'export': { - // handleSwitchChange(record); + handleExportWorkFlow(record); break; } default: { diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 69ecad93..f68df177 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -46,6 +46,8 @@ export interface WorkflowAPISchema extends APISchema { updated_at: number; /** User Nickname */ user_nickname: string; + /** workflow version */ + version: string; }[] >; }; From 31e05979f5b4a5f457d2d942403f6f394797d172 Mon Sep 17 00:00:00 2001 From: Nian Date: Fri, 27 Dec 2024 09:20:37 +0800 Subject: [PATCH 107/172] fix: workflow list pagination --- apps/web/src/pages/workflow/index.tsx | 10 +++++----- apps/web/src/services/http/workflow.ts | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 8991f0d8..92ff95af 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -42,12 +42,12 @@ const Workflow = () => { mutate: updateWorkworkflowList, } = useRequest( async () => { - // const { page, pageSize } = paginationModel; + const { page, pageSize } = paginationModel; const [error, resp] = await awaitWrap( workflowAPI.getList({ name: keyword || '', - // page_size: pageSize, - // page_number: page + 1, + page_size: pageSize, + page_number: page + 1, }), ); const data = getResponseData(resp); @@ -173,7 +173,7 @@ const Workflow = () => { const [error, resp] = await awaitWrap( workflowAPI.getFlowDesign({ id: record.id, - version: record.version, + version: record?.version ?? '', }), ); @@ -187,7 +187,7 @@ const Workflow = () => { // console.log(type, record); switch (type) { case 'edit': { - navigate(`/workflow/editor?wid=${record.id}&version=${record.version}`); + navigate(`/workflow/editor?wid=${record.id}&version=${record?.version ?? ''}`); break; } case 'detail': { diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index f68df177..58972577 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -29,6 +29,10 @@ export interface WorkflowAPISchema extends APISchema { request: { name?: string; status?: FlowStatus; + /** data per page */ + page_size?: number | null; + /** pagination page numbers */ + page_number?: number | null; }; response: SearchResponseType< { From b9e91ca1daed0e228380c33020759c0f8b63f85c Mon Sep 17 00:00:00 2001 From: Nian Date: Fri, 27 Dec 2024 09:30:00 +0800 Subject: [PATCH 108/172] refactor: restore the default value --- apps/web/src/pages/auth/views/useFormItems.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/web/src/pages/auth/views/useFormItems.tsx b/apps/web/src/pages/auth/views/useFormItems.tsx index 2b10db17..5e1f2395 100644 --- a/apps/web/src/pages/auth/views/useFormItems.tsx +++ b/apps/web/src/pages/auth/views/useFormItems.tsx @@ -45,7 +45,6 @@ const useFormItems = ({ mode = 'login' }: UseFormItemsProps) => { const items: ControllerProps[] = [ { name: 'email', - defaultValue: 'xiejc@milesight.com', rules: { validate: { checkRequired: checkRequired(), @@ -107,7 +106,6 @@ const useFormItems = ({ mode = 'login' }: UseFormItemsProps) => { }, { name: 'password', - defaultValue: 'Milesight@2024', rules: { validate: { checkRequired: checkRequired(), From 7ba5a8b8d081e2b068af4691addf85ea71fb9cfc Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 27 Dec 2024 14:14:19 +0800 Subject: [PATCH 109/172] feat: Component adjustment, adapting to backend data structures --- .../components/entity-assign-select/index.tsx | 43 ++++--- .../components/entity-select/index.tsx | 14 +-- .../components/param-assign-input/index.tsx | 47 ++++--- .../components/param-input-select/index.tsx | 65 +++++----- .../components/param-input/index.tsx | 25 ++-- .../service-param-assign-input/index.tsx | 6 +- .../config-panel/hooks/useNodeFormItems.tsx | 2 +- .../editor/components/log-panel/style.less | 2 + .../components/node-container/index.tsx | 7 +- .../editor/components/test-button/index.tsx | 56 ++++----- .../components/test-button/log-list.tsx | 32 ++--- .../editor/components/test-button/style.less | 6 +- .../pages/workflow/views/editor/constants.ts | 5 + .../src/pages/workflow/views/editor/helper.ts | 16 +-- .../views/editor/hooks/useValidate.ts | 42 ++++--- .../views/editor/hooks/useWorkflow.ts | 7 +- .../src/pages/workflow/views/editor/index.tsx | 5 +- apps/web/src/services/http/workflow.ts | 10 +- packages/shared/types/workflow.d.ts | 115 ++++++++---------- 19 files changed, 258 insertions(+), 247 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx index ff61da52..6f432d89 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx @@ -9,12 +9,9 @@ import ParamInputSelect from '../param-input-select'; import './style.less'; export type EntityAssignInputValueType = - | undefined - | { - entityKey?: ApiKey; - ref?: string; - value?: string; - }; + | NonNullable['exchangePayload'] + | undefined; +export type EntityAssignInputInnerValueType = [string, string] | undefined; export interface EntityAssignSelectProps { label?: string[]; @@ -22,12 +19,20 @@ export interface EntityAssignSelectProps { multiple?: boolean; error?: boolean; helperText?: React.ReactNode; - value?: EntityAssignInputValueType[]; - defaultValue?: EntityAssignInputValueType[]; - onChange?: (value: EntityAssignInputValueType[]) => void; + value?: EntityAssignInputValueType; + defaultValue?: EntityAssignInputValueType; + onChange?: (value: EntityAssignInputValueType) => void; } const MAX_VALUE_LENGTH = 10; +const arrayToObject = (arr: EntityAssignInputInnerValueType[]) => { + const result: EntityAssignInputValueType = {}; + arr?.forEach(item => { + if (!item) return; + result[item[0]] = item[1]; + }); + return result; +}; /** * Entity Assignment Input Component @@ -41,18 +46,18 @@ const EntityAssignSelect: React.FC = ({ ...props }) => { const { getIntlText } = useI18n(); - const [data, setData] = useControllableValue(props); + const [data, setData] = useControllableValue(props); const { list, remove, getKey, insert, replace, resetList } = - useDynamicList(data || []); + useDynamicList(Object.entries(data || {})); useLayoutEffect(() => { - if (isEqual(data, list)) return; - resetList(data || [{}]); + if (isEqual(data, arrayToObject(list))) return; + resetList(Object.entries(data || {})); // eslint-disable-next-line react-hooks/exhaustive-deps }, [data, resetList]); useLayoutEffect(() => { - setData?.(list || [{}]); + setData?.(arrayToObject(list)); }, [list, setData]); return ( @@ -60,15 +65,15 @@ const EntityAssignSelect: React.FC = ({ {list.map((item, index) => (
{ - replace(index, { ...item, entityKey: value }); + replace(index, [`${value || ''}`, item?.[1] || '']); }} /> { - replace(index, { entityKey: item?.entityKey, ...data }); + replace(index, [item?.[0] || '', data || '']); }} /> remove(index)}> @@ -85,7 +90,7 @@ const EntityAssignSelect: React.FC = ({ disabled={list.length >= MAX_VALUE_LENGTH} onClick={() => { if (list.length >= MAX_VALUE_LENGTH) return; - insert(list.length, {}); + insert(list.length, ['', '']); }} > {getIntlText('common.label.add')} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx index fc74daf1..d5034ab1 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-select/index.tsx @@ -29,7 +29,7 @@ export type EntitySelectOptionType = { EntityAPISchema['getList']['response']['content'][number], 'entity_value_attribute' > & { - entity_value_attribute: EntityValueAttributeType; + entity_value_attribute: string; }; }; @@ -140,22 +140,12 @@ const EntitySelect: React.FC = ({ const options = useMemo(() => { const list = searchedEntityList || filteredEntityList || []; const result: EntitySelectOptionType[] = list.map(item => { - const entityValueAttribute = (() => { - try { - return JSON.parse(item.entity_value_attribute); - } catch (e) { - return item.entity_value_attribute; - } - })(); return { label: item.entity_name, value: item.entity_key, valueType: item.entity_value_type, description: [item.device_name, item.integration_name].filter(Boolean).join(', '), - rawData: { - ...item, - entity_value_attribute: entityValueAttribute as EntityValueAttributeType, - }, + rawData: item, }; }); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx index b7720cd1..81a9749e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx @@ -1,4 +1,4 @@ -import React, { useLayoutEffect } from 'react'; +import React, { useCallback, useLayoutEffect } from 'react'; import { Button, IconButton, TextField } from '@mui/material'; import { isEqual } from 'lodash-es'; import { useDynamicList, useControllableValue } from 'ahooks'; @@ -8,12 +8,10 @@ import ParamInputSelect from '../param-input-select'; import './style.less'; export type ParamAssignInputValueType = - | undefined - | { - key?: string; - ref?: string; - value?: string; - }; + | NonNullable['inputArguments'] + | undefined; + +export type ParamAssignInputInnerValueType = [string, string] | undefined; export interface ParamAssignInputProps { label?: string[]; @@ -21,13 +19,22 @@ export interface ParamAssignInputProps { multiple?: boolean; error?: boolean; helperText?: React.ReactNode; - value?: ParamAssignInputValueType[]; - defaultValue?: ParamAssignInputValueType[]; - onChange?: (value: ParamAssignInputValueType[]) => void; + value?: ParamAssignInputValueType; + defaultValue?: ParamAssignInputValueType; + onChange?: (value: ParamAssignInputValueType) => void; } const MAX_VALUE_LENGTH = 10; +const arrayToObject = (arr: ParamAssignInputInnerValueType[]) => { + const result: ParamAssignInputValueType = {}; + arr?.forEach(item => { + if (!item) return; + result[item[0]] = item[1]; + }); + return result; +}; + /** * Param Assignment Input Component * @@ -40,18 +47,18 @@ const ParamAssignInput: React.FC = ({ ...props }) => { const { getIntlText } = useI18n(); - const [data, setData] = useControllableValue(props); + const [data, setData] = useControllableValue(props); const { list, remove, getKey, insert, replace, resetList } = - useDynamicList(data); + useDynamicList(Object.entries(data || {})); useLayoutEffect(() => { - if (isEqual(data, list)) return; - resetList(data || []); + if (isEqual(data, arrayToObject(list))) return; + resetList(Object.entries(data || {})); // eslint-disable-next-line react-hooks/exhaustive-deps }, [data, resetList]); useLayoutEffect(() => { - setData?.(list); + setData?.(arrayToObject(list)); }, [list, setData]); return ( @@ -62,15 +69,15 @@ const ParamAssignInput: React.FC = ({ autoComplete="off" label={label?.[0] || getIntlText('common.label.name')} required={required} - value={item?.key || ''} - onChange={e => replace(index, { ...item, key: e.target.value })} + value={item?.[0] || ''} + onChange={e => replace(index, [e.target.value, item?.[1] || ''])} /> { - replace(index, { key: item?.key, ...data }); + replace(index, [item?.[0] || '', data || '']); }} /> remove(index)}> @@ -87,7 +94,7 @@ const ParamAssignInput: React.FC = ({ disabled={list.length >= MAX_VALUE_LENGTH} onClick={() => { if (list.length >= MAX_VALUE_LENGTH) return; - insert(list.length, {}); + insert(list.length, ['', '']); }} > {getIntlText('common.label.add')} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx index dd043018..1423fe67 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx @@ -1,18 +1,14 @@ -import { useMemo, useLayoutEffect, useState, useRef } from 'react'; +import { useMemo, useLayoutEffect, useState, useRef, useCallback } from 'react'; import { useControllableValue, useSize } from 'ahooks'; import { TextField, Menu, MenuItem, IconButton, Chip, ListSubheader } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { SettingsOutlinedIcon } from '@milesight/shared/src/components'; import { Tooltip } from '@/components'; import useWorkflow from '@/pages/workflow/views/editor/hooks/useWorkflow'; +import { isRefParamKey } from '@/pages/workflow/views/editor/helper'; import './style.less'; -type ParamInputSelectValueType = - | undefined - | { - ref?: string; - value?: string; - }; +type ParamInputSelectValueType = string | undefined; type OptionItemType = { nodeId: ApiKey; @@ -95,7 +91,7 @@ const ParamInputSelect: React.FC = ({ } onClick={() => { setAnchorEl(null); - setData({ ref: output.key }); + setData(output.key); }} >
@@ -111,30 +107,45 @@ const ParamInputSelect: React.FC = ({ return [result, renderedOptions]; }, [selectValue, getUpstreamNodeParams, setData]); + const handleInputChange = useCallback>( + e => { + const { value } = e.target; + + // input value + if (!isRefParamKey(value)) { + setData(value); + return; + } + + // Reference to an entity + setInputValue(''); + const option = options?.find(item => item.valueKey === value); + + if (!option) return; + setData(value); + setSelectValue(option); + }, + [options, setData], + ); + useLayoutEffect(() => { // Direct input value - if (data?.value) { - setInputValue(data.value); + if (!isRefParamKey(data)) { + setInputValue(data || ''); setSelectValue(undefined); return; } // Reference to an entity - if (data?.ref) { - setInputValue(''); - const option = options?.find(item => item.valueKey === data.ref); - - setSelectValue(val => { - if (val && val.valueKey === data.ref) { - return val; - } - return option; - }); - return; - } - setInputValue(''); - setSelectValue(undefined); + const option = options?.find(item => item.valueKey === data); + + setSelectValue(val => { + if (val && val.valueKey === data) { + return val; + } + return option; + }); }, [data, options]); return ( @@ -161,9 +172,7 @@ const ParamInputSelect: React.FC = ({ }, }} value={inputValue} - onChange={e => { - setData({ value: e.target.value }); - }} + onChange={handleInputChange} /> {!!selectValue && ( = ({ {selectValue.valueType} } - onDelete={() => setData({ ref: undefined })} + onDelete={() => setData(undefined)} /> )} ['entityConfigs'][0] & { + context?: boolean; }; export interface ParamInputProps { @@ -42,7 +41,7 @@ export interface ParamInputProps { } const DEFAULT_EMPTY_VALUE: ParamInputValueType = { - arg: '', + name: '', type: '', }; const typeOptions = ['INT', 'FLOAT', 'BOOLEAN', 'STRING']; @@ -69,7 +68,7 @@ const ParamInput: React.FC = ({ setInnerValue?.(list); }, [list, setInnerValue]); - const handlerChange = ( + const handleChange = ( index: number, rowItem: ParamInputValueType, key: string, @@ -91,9 +90,9 @@ const ParamInput: React.FC = ({ - handlerChange(index, item, 'arg', e.target.value as string) + handleChange(index, item, 'name', e.target.value as string) } /> @@ -107,7 +106,7 @@ const ParamInput: React.FC = ({ label={typeSelectProps?.label || getIntlText('common.label.type')} value={item.type} onChange={e => - handlerChange(index, item, 'type', e.target.value as string) + handleChange(index, item, 'type', e.target.value as string) } > {typeOptions.map(item => ( @@ -124,12 +123,12 @@ const ParamInput: React.FC = ({ disabled={disabled} > - handlerChange( + handleChange( index, item, - 'isCheck', + 'context', e.target.checked as boolean, ) } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx index 980c3906..da1216e9 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -1,10 +1,10 @@ import { useCallback, useLayoutEffect, useMemo } from 'react'; -import { useEntityApi } from '@/plugin/hooks'; -import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; +import { isEqual } from 'lodash-es'; import { useControllableValue, useDebounceFn, useDynamicList } from 'ahooks'; import { Divider, IconButton, Tooltip } from '@mui/material'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { HelpIcon } from '@milesight/shared/src/components'; -import { isEqual } from 'lodash-es'; +import { useEntityApi } from '@/plugin/hooks'; import useConfigPanelStore from '../../store'; import ParamInputSelect, { ParamInputSelectProps } from '../param-input-select'; import EntitySelect from '../entity-select'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index b420d40f..571eeeaf 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -53,7 +53,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { const formConfigs = useMemo(() => { if (!Object.keys(nodeConfigs).length) return {}; - // console.log({ nodeConfigs }); + console.log({ nodeConfigs }); const result: Partial> = {}; Object.entries(nodeConfigs).forEach(([nodeType, nodeConfig]) => { const { properties = {}, outputProperties = {} } = nodeConfig.schema || {}; diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less index ea7b98b9..be913687 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less @@ -15,6 +15,8 @@ } .@{prefix}-workflow-panel-log { + display: flex; + flex-direction: column; height: 100%; background-color: var(--component-background); border-radius: @border-radius-sm; diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index d6a6b540..a234c83a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -6,6 +6,7 @@ import { Menu, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { CheckCircleIcon, ErrorIcon, LoopIcon } from '@milesight/shared/src/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; +import useFlowStore from '../../store'; import Handle from '../handle'; import './style.less'; @@ -137,7 +138,8 @@ const NodeContainer: React.FC = ({ /** * Menu Item click callback */ - const { updateNode, deleteElements } = useReactFlow(); + const { updateNode, deleteElements } = useReactFlow(); + const nodeConfigs = useFlowStore(state => state.nodeConfigs); const handleMenuItemClick = useCallback( async ( e: React.MouseEvent, @@ -155,8 +157,11 @@ const NodeContainer: React.FC = ({ switch (type) { case 'change': { + if (!targetNodeType) return; + const nodeConfig = nodeConfigs[targetNodeType]; updateNode(nodeId, { type: targetNodeType, + componentName: nodeConfig.componentName, data: {}, }); break; diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx index 8bab0fe6..2092974c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -1,4 +1,5 @@ import { useCallback, useMemo, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { ButtonGroup, Button, Popover, Tabs, Tab, CircularProgress } from '@mui/material'; import { useRequest } from 'ahooks'; import { useNodes } from '@xyflow/react'; @@ -31,6 +32,8 @@ const DEFAULT_TAB_KEY: LogType = 'test'; const TestButton: React.FC = ({ disabled }) => { const { getIntlText } = useI18n(); const nodes = useNodes(); + const [searchParams] = useSearchParams(); + const wid = searchParams.get('wid'); const { runLogs, testLogs, @@ -53,36 +56,24 @@ const TestButton: React.FC = ({ disabled }) => { ]), ); + // ---------- Fetch Run Log List ---------- const { loading, run: getRunLogList } = useRequest( async () => { - // const [error, resp] = await awaitWrap(workflowAPI.getLogList()); - // if (error || !isRequestSuccess(resp)) { - // return; - // } - // const data = getResponseData(resp); - - // TODO: get log list - const log = { - id: '1', - start_time: Date.now(), - time_cost: 500, - status: 'Success', - }; - const data = new Array(100).fill(0).map((_, index) => ({ - ...log, - id: index + 1, - status: Math.floor(Math.random() * 10) % 2 === 0 ? 'Success' : 'Error', - })); - - await new Promise(resolve => { - setTimeout(resolve, 1000); - }); + if (!wid) return; + const [error, resp] = await awaitWrap( + workflowAPI.getLogList({ id: wid, page_size: 999, page_number: 1 }), + ); + + // console.log({ error, resp }); + if (error || !isRequestSuccess(resp)) return; + const data = getResponseData(resp); - setRunLogs(data as LogListProps['data']); + setRunLogs(data?.content); }, { manual: true, debounceWait: 300, + refreshDeps: [wid], }, ); @@ -125,15 +116,15 @@ const TestButton: React.FC = ({ disabled }) => { if (error || !isRequestSuccess(resp)) return; const data = getResponseData(resp); - setLogDetail(data); + setLogDetail(data?.trace_info); }, [setLogDetail, setLogDetailLoading, setLogPanelMode, setOpenLogPanel], ); // ---------- Tab ---------- const [tabKey, setTabKey] = useState(DEFAULT_TAB_KEY); - const tabs = useMemo(() => { - return [ + const tabs = useMemo(() => { + const result: LogTabItem[] = [ { key: 'test', label: getIntlText('workflow.editor.logs_popover_title_test'), @@ -145,7 +136,10 @@ const TestButton: React.FC = ({ disabled }) => { /> ), }, - { + ]; + + if (wid) { + result.push({ key: 'run', label: getIntlText('workflow.editor.logs_popover_title_run'), component: ( @@ -156,9 +150,11 @@ const TestButton: React.FC = ({ disabled }) => { onSelect={record => handleLogItemClick('run', record)} /> ), - }, - ]; - }, [runLogs, testLogs, loading, getIntlText, handleLogItemClick]); + }); + } + + return result; + }, [wid, runLogs, testLogs, loading, getIntlText, handleLogItemClick]); return ( <> diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx index 037ece66..ddb07001 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx @@ -20,16 +20,16 @@ export interface LogListProps { const LogList: React.FC = ({ type, data = [], loading, onSelect }) => { const { getIntlText } = useI18n(); const { getTimeFormat } = useTime(); - const titlePrefix = useMemo(() => { - switch (type) { - case 'test': - return getIntlText('workflow.editor.log_title_test'); - case 'run': - return getIntlText('workflow.editor.log_title_run'); - default: - return ''; - } - }, [type, getIntlText]); + // const titlePrefix = useMemo(() => { + // switch (type) { + // case 'test': + // return getIntlText('workflow.editor.log_title_test'); + // case 'run': + // return getIntlText('workflow.editor.log_title_run'); + // default: + // return ''; + // } + // }, [type, getIntlText]); // ---------- Virtual List ---------- const containerRef = useRef(null); @@ -37,11 +37,11 @@ const LogList: React.FC = ({ type, data = [], loading, onSelect }) const [virtualList] = useVirtualList(data, { containerTarget: containerRef, wrapperTarget: wrapperRef, - itemHeight: 58, + itemHeight: 38, overscan: 10, }); - // TODO: Infinite Scroll Loading + // TODO: Infinite Scroll Loading ? return (
@@ -58,7 +58,7 @@ const LogList: React.FC = ({ type, data = [], loading, onSelect }) }} >
- {record.status === 'Success' ? ( + {record.status === 'SUCCESS' ? ( ) : ( @@ -66,11 +66,11 @@ const LogList: React.FC = ({ type, data = [], loading, onSelect })
- {`${titlePrefix}#${record.id}`} -
-
{getTimeFormat(record.start_time)}
+ {/*
+ {getTimeFormat(record.start_time)} +
*/}
)) diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/style.less b/apps/web/src/pages/workflow/views/editor/components/test-button/style.less index d1ac3a18..eaeff898 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/style.less @@ -46,6 +46,7 @@ .@{prefix}-workflow-com-log-list { position: relative; height: 248px; + padding-top: @padding-xs; overflow: auto; .@{prefix}-empty { @@ -76,8 +77,9 @@ opacity: .8; } - &:first-child { - margin-top: @margin-xs; + &__left { + display: flex; + align-items: center; } &__title { diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts index f953e14c..34caa761 100644 --- a/apps/web/src/pages/workflow/views/editor/constants.ts +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -58,6 +58,11 @@ export const NODE_SPACING_Y = 48; */ export const PARAM_REFERENCE_PREFIX = '@'; +/** + * Global parameter reference pattern + */ +export const PARAM_REFERENCE_PATTERN = /^#\{([^{}]+)\}$/; + /** * Global parameter reference key divider */ diff --git a/apps/web/src/pages/workflow/views/editor/helper.ts b/apps/web/src/pages/workflow/views/editor/helper.ts index e35491f9..2759628c 100644 --- a/apps/web/src/pages/workflow/views/editor/helper.ts +++ b/apps/web/src/pages/workflow/views/editor/helper.ts @@ -4,7 +4,7 @@ import { checkRangeLength, type Validate, } from '@milesight/shared/src/utils/validators'; -import { PARAM_REFERENCE_PREFIX, PARAM_REFERENCE_DIVIDER } from './constants'; +import { PARAM_REFERENCE_PATTERN, PARAM_REFERENCE_DIVIDER } from './constants'; /** * Node Data Validators Config @@ -23,23 +23,23 @@ export const validatorsConfig: Record> = { * Generate Reference Param Key */ export const genRefParamKey = (nodeType: WorkflowNodeType, nodeId: ApiKey, valueKey: ApiKey) => { - return `${PARAM_REFERENCE_PREFIX}${[nodeType, nodeId, valueKey].join(PARAM_REFERENCE_DIVIDER)}`; + return `#{${[nodeType, nodeId, valueKey].join(PARAM_REFERENCE_DIVIDER)}}`; }; /** * Check if the value is a reference param key */ -export const isRefParamKey = (key: string) => { - return key.startsWith(PARAM_REFERENCE_PREFIX); +export const isRefParamKey = (key?: string) => { + return key && PARAM_REFERENCE_PATTERN.test(key); }; /** * Parse the reference param key */ -export const parseRefParamKey = (key: string) => { - if (!isRefParamKey(key)) return {}; - const [nodeType, nodeId, valueKey] = key.slice(1).split(PARAM_REFERENCE_DIVIDER); - return { nodeType, nodeId, valueKey }; +export const parseRefParamKey = (key?: string) => { + if (!key || !isRefParamKey(key)) return; + const matches = key.match(PARAM_REFERENCE_PATTERN); + return matches?.[1]; }; /** diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 5d028402..28ecae2c 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -463,10 +463,14 @@ const useValidate = () => { const { nodeName, nodeRemark, parameters } = data || {}; let tempResult = result[id]; - if (options?.validateFirst && tempResult?.errMsgs.length) { - toast.error({ key: 'node-validate', content: tempResult.errMsgs[0] }); - return result; - } + // if ( + // options?.validateFirst && + // Object.values(result).some(item => item.errMsgs.length) + // ) { + // const errItem = Object.values(result).find(item => item.errMsgs.length); + // toast.error({ key: 'node-validate', content: errItem?.errMsgs[0] }); + // return result; + // } if (!tempResult) { tempResult = { @@ -503,21 +507,25 @@ const useValidate = () => { 1: nodeName || `${getIntlText(config.labelIntlKey)} (ID: ${id})`, }), ); - continue; + } else { + // Node parameters data check + Object.entries(parameters).forEach(([key, value]) => { + const validKey = `${type}.${key}`; + const validators = dataValidators[validKey] || dataValidators[key] || {}; + + Object.values(validators).forEach(validator => { + const result = validator(value, key); + if (result && result !== true) { + tempResult.errMsgs.push(result); + } + }); + }); } - // Node parameters data check - Object.entries(parameters).forEach(([key, value]) => { - const validKey = `${type}.${key}`; - const validators = dataValidators[validKey] || dataValidators[key] || {}; - - Object.values(validators).forEach(validator => { - const result = validator(value, key); - if (result && result !== true) { - tempResult.errMsgs.push(result); - } - }); - }); + if (options?.validateFirst && tempResult.errMsgs.length) { + toast.error({ key: 'node-validate', content: tempResult.errMsgs[0] }); + return result; + } } Object.entries(result).forEach(([id, data]) => { diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 30cf79aa..e624b7af 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -17,6 +17,7 @@ import { NODE_MIN_NUMBER_LIMIT, ENTRY_NODE_NUMBER_LIMIT, } from '../constants'; +import { genRefParamKey } from '../helper'; import { getParallelInfo } from './utils'; export type NodeParamType = { @@ -243,12 +244,12 @@ const useWorkflow = () => { { name: 'output112123123123123123123123131231231', type: 'string', - key: `${node.type}.${node.id}.1132e3123132`, + key: genRefParamKey(node.type as WorkflowNodeType, node.id, '1132e3123132'), }, { name: 'output22', type: 'number', - key: `${node.type}.${node.id}.11eyu3123132`, + key: genRefParamKey(node.type as WorkflowNodeType, node.id, '11eyu3123132'), }, ], })); @@ -298,7 +299,7 @@ const useWorkflow = () => { return result; }, - [getNodes, getUpstreamNodes, getIntlText], + [getNodes, getEdges, getUpstreamNodes, getIntlText], ); // Update node status diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 23081a61..678f6a7a 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -135,8 +135,8 @@ const WorkflowEditor = () => { ); // ---------- Fetch Nodes Config ---------- - const { setNodeConfigs, setNodesDataValidResult } = useFlowStore( - useStoreShallow(['setNodeConfigs', 'setNodesDataValidResult']), + const { setOpenLogPanel, setNodeConfigs, setNodesDataValidResult } = useFlowStore( + useStoreShallow(['setOpenLogPanel', 'setNodeConfigs', 'setNodesDataValidResult']), ); const { loading: nodeConfigLoading } = useRequest( async () => { @@ -241,6 +241,7 @@ const WorkflowEditor = () => { setNodes(nodes); setEdges(edges); + setOpenLogPanel(false); } setDesignMode(mode); diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 58972577..8e2b2489 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -116,12 +116,10 @@ export interface WorkflowAPISchema extends APISchema { /** Get workflow log list */ getLogList: { - request: - | void - | ({ - id: ApiKey; - status?: WorkflowNodeStatus; - } & SearchRequestType); + request: SearchRequestType & { + id: ApiKey; + status?: WorkflowNodeStatus; + }; response: SearchResponseType< { /** ID */ diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index 881a04ab..b0cc97ab 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -1,5 +1,5 @@ /** - * ReactFlow 节点模型 + * ReactFlow Node Model */ declare type ReactFlowNode< D extends Record = Record, @@ -10,7 +10,7 @@ declare type ReactFlowNode< }; /** - * ReactFlow 边模型 + * ReactFlow Edge Model */ declare type ReactFlowEdge< D extends Record = Record, @@ -18,22 +18,22 @@ declare type ReactFlowEdge< > = import('@xyflow/react').Edge; /** - * ReactFlow 视口模型 + * ReactFlow Viewport Model */ declare type ReactFlowViewport = import('@xyflow/react').Viewport; /** - * 节点类型 - * @param trigger 触发器节点 - * @param timer 定时节点 - * @param listener 监听器节点 - * @param ifelse 条件节点 - * @param code 代码节点 - * @param service 实体服务调用节点 - * @param assigner 实体赋值节点 - * @param select 实体选择节点 - * @param email 邮件节点 - * @param webhook webhook 节点 + * Node Type + * @param trigger Trigger Node + * @param timer Timer Node + * @param listener Listener Node + * @param ifelse IfElse Node + * @param code Code Node + * @param service Service Node + * @param assigner Assigner Node + * @param select Select Node + * @param email Email Node + * @param webhook Webhook Node */ declare type WorkflowNodeType = | 'trigger' @@ -48,17 +48,17 @@ declare type WorkflowNodeType = | 'webhook'; /** - * 边类型 - * @param addable 可添加节点的边 + * Edge Type + * @param addable Edges to which nodes can be added */ declare type WorkflowEdgeType = 'addable'; /** - * 节点分类 - * @param entry 入口节点 - * @param control 控制节点 - * @param action 动作节点 - * @param external 外部节点 + * Node Category + * @param entry Entry Node + * @param control Control Node + * @param action Action Node + * @param external External Node */ declare type WorkflowNodeCategoryType = 'entry' | 'control' | 'action' | 'external'; @@ -68,7 +68,7 @@ declare type WorkflowNodeCategoryType = 'entry' | 'control' | 'action' | 'extern declare type WorkflowNodeStatus = 'ERROR' | 'SUCCESS'; /** - * 节点基础数据类型(以 $ 开头的均为前端私有属性) + * Node Base Data Model(Properties that begin with `$` are private to the frontend) */ declare type BaseNodeDataType = Record> = { /** Node Name */ @@ -84,12 +84,12 @@ declare type BaseNodeDataType = Record; @@ -105,7 +105,7 @@ declare type TimePeriodType = | 'SUNDAY'; /** - * 定时器节点参数类型 + * Timer Node Parameters */ declare type TimerNodeDataType = BaseNodeDataType<{ timerSettings: { @@ -122,7 +122,7 @@ declare type TimerNodeDataType = BaseNodeDataType<{ }>; /** - * 事件监听节点参数类型 + * Event Listener Node Parameters */ declare type ListenerNodeDataType = BaseNodeDataType<{ entities: ApiKey[]; @@ -141,9 +141,7 @@ declare type WorkflowFilterOperator = | 'IS_NOT_EMPTY'; /** - * 条件节点参数类型 - * - * 注意:实际节点渲染时需默认增加一个 else 分支 + * IfElse Node Parameters */ declare type IfElseNodeDataType = BaseNodeDataType<{ choice: { @@ -176,10 +174,10 @@ declare type IfElseNodeDataType = BaseNodeDataType<{ }>; /** - * 结束节点参数类型 + * End Node Parameters */ // declare type EndNodeDataType = BaseNodeDataType<{ -// /** 输出参数 */ +// /** Outputs */ // outputs: { // key: ApiKey; // type: EntityValueDataType; @@ -188,16 +186,17 @@ declare type IfElseNodeDataType = BaseNodeDataType<{ // }>; /** - * 代码节点参数类型 + * Code Node Parameters */ declare type CodeNodeDataType = BaseNodeDataType<{ - /** 代码语言 */ - language: string; - /** 代码内容 */ - expression: string; - /** 输入参数 */ - inputArguments: { key: string; value: string }[]; - /** 输出参数 */ + /** Code Content */ + expression: { + language: string; + expression: string; + }; + /** Input Arguments */ + inputArguments: Record; + /** Output */ Payload: { name: ApiKey; type: EntityValueDataType; @@ -205,7 +204,7 @@ declare type CodeNodeDataType = BaseNodeDataType<{ }>; /** - * 服务节点参数类型 + * Service Node Parameters */ declare type ServiceNodeDataType = BaseNodeDataType<{ /** Service Entity Key */ @@ -217,21 +216,21 @@ declare type ServiceNodeDataType = BaseNodeDataType<{ }>; /** - * 赋值节点参数类型 + * Entity Assigner Node Parameters */ declare type AssignerNodeDataType = BaseNodeDataType<{ exchangePayload: Record; }>; /** - * 实体选择节点参数类型 + * Entity Selector Node Parameters */ declare type SelectNodeDataType = BaseNodeDataType<{ entities: ApiKey[]; }>; /** - * 邮件节点参数类型 + * Email Node Parameters */ declare type EmailNodeDataType = BaseNodeDataType<{ emailConfig: { @@ -250,14 +249,11 @@ declare type EmailNodeDataType = BaseNodeDataType<{ }>; /** - * Webhook 节点参数类型 + * Webhook Node Parameters */ declare type WebhookNodeDataType = BaseNodeDataType<{ /** Custom Data */ - inputArguments?: { - key: ApiKey; - value: string; - }[]; + inputArguments?: Record; /** Webhook URL */ webhookUrl: string; /** Webhook Secret */ @@ -265,21 +261,8 @@ declare type WebhookNodeDataType = BaseNodeDataType<{ }>; /** - * 工作流节点模型 + * Workflow Node Model */ -// declare type WorkflowNode = -// | ReactFlowNode, 'trigger'> -// | ReactFlowNode, 'timer'> -// | ReactFlowNode, 'listener'> -// | ReactFlowNode, 'ifelse'> -// // | ReactFlowNode -// | ReactFlowNode, 'code'> -// | ReactFlowNode, 'service'> -// | ReactFlowNode, 'assigner'> -// | ReactFlowNode, 'select'> -// | ReactFlowNode, 'email'> -// | ReactFlowNode, 'webhook'> -// | ReactFlowNode, any>; declare type WorkflowNode = T extends 'trigger' ? ReactFlowNode, 'trigger'> : T extends 'timer' @@ -303,18 +286,18 @@ declare type WorkflowNode = : ReactFlowNode>; /** - * 工作流边模型 + * Workflow Edge Model */ declare type WorkflowEdge = ReactFlowEdge< { - /** 标识鼠标是否 hover */ + /** mouse hover mark */ $hovering?: boolean; }, WorkflowEdgeType >; /** - * 工作流数据模型 + * Workflow Schema */ declare type WorkflowSchema = { /** 版本号 */ From f5c044ac78cc2087c0e999bda2547d5325c3c5cb Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 27 Dec 2024 14:43:02 +0800 Subject: [PATCH 110/172] fix: remove version param form the url query --- .../views/editor/components/topbar/index.tsx | 9 ++------- .../src/pages/workflow/views/editor/index.tsx | 16 +++++++--------- apps/web/src/services/http/workflow.ts | 2 +- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx index 7539655d..e4110f83 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx @@ -4,6 +4,7 @@ import { isEqual } from 'lodash-es'; import { Button, IconButton, Grid2, Switch, ToggleButtonGroup, ToggleButton } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { ArrowBackIcon, EditIcon } from '@milesight/shared/src/components'; +import { WorkflowAPISchema } from '@/services/http'; import { Tooltip } from '@/components'; import { EditModal, type EditModalProps } from '@/pages/workflow/components'; import './style.less'; @@ -21,12 +22,7 @@ export interface TopbarProps { disabled?: boolean; /** Workflow Detail Data */ - data?: { - id?: ApiKey; - name?: string; - remark?: string; - enabled?: boolean; - }; + data?: Partial>; /** Default Workflow Design Mode */ mode?: DesignMode; @@ -58,7 +54,6 @@ const Topbar: React.FC = ({ }) => { const navigate = useNavigate(); const { getIntlText } = useI18n(); - // const isEdit = !!data?.id; // ---------- Workflow Name/Remark/Status Edit ---------- const [flowData, setFlowData] = useState(); diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 678f6a7a..a66c5077 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -157,6 +157,10 @@ const WorkflowEditor = () => { if (wid) return { id: wid }; }); const [flowDataLoading, setFlowDataLoading] = useState(); + const handleFlowDataChange = useCallback>(data => { + setBasicData(data); + setIsPreventLeave(true); + }, []); useRequest( async () => { @@ -190,10 +194,6 @@ const WorkflowEditor = () => { refreshDeps: [wid, version], }, ); - const handleFlowDataChange = useCallback>(data => { - setBasicData(data); - setIsPreventLeave(true); - }, []); // ---------- Design Mode Change ---------- const [designMode, setDesignMode] = useState('canvas'); @@ -256,6 +256,7 @@ const WorkflowEditor = () => { checkEdgesType, setNodes, setEdges, + setOpenLogPanel, getIntlText, ], ); @@ -355,11 +356,8 @@ const WorkflowEditor = () => { setSaveLoading(true); const [error, resp] = await awaitWrap( workflowAPI.saveFlowDesign({ - id: wid || undefined, - version, - name: basicData.name!, - remark: basicData.remark!, - enabled: basicData.enabled!, + ...basicData, + name: basicData.name, design_data: JSON.stringify({ nodes, edges, viewport }), }), ); diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 8e2b2489..b97d9803 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -190,7 +190,7 @@ export interface WorkflowAPISchema extends APISchema { /** Flow Remark */ remark?: string; /** Enabled */ - enabled: boolean; + enabled?: boolean; /** Flow Design Data (JSON string) */ design_data: string; }; From 6f6f1bafe6d7a22b09d6852adb2bc285f035d7e0 Mon Sep 17 00:00:00 2001 From: Nian Date: Fri, 27 Dec 2024 14:45:30 +0800 Subject: [PATCH 111/172] fix: adjust workflow list export & service call node --- apps/web/src/pages/workflow/index.tsx | 9 ++----- .../service-param-assign-input/index.tsx | 25 +++++-------------- 2 files changed, 8 insertions(+), 26 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 92ff95af..baf428e2 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -170,12 +170,7 @@ const Workflow = () => { ); const handleExportWorkFlow = useCallback( async (record: TableRowDataType) => { - const [error, resp] = await awaitWrap( - workflowAPI.getFlowDesign({ - id: record.id, - version: record?.version ?? '', - }), - ); + const [error, resp] = await awaitWrap(workflowAPI.getFlowDesign({ id: record.id })); if (error || !isRequestSuccess(resp)) return; exportJsonFile(getResponseData(resp) as WorkflowAPISchema['getFlowDesign']['response']); @@ -187,7 +182,7 @@ const Workflow = () => { // console.log(type, record); switch (type) { case 'edit': { - navigate(`/workflow/editor?wid=${record.id}&version=${record?.version ?? ''}`); + navigate(`/workflow/editor?wid=${record.id}`); break; } case 'detail': { diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx index da1216e9..b717e110 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -17,7 +17,7 @@ type InputParamListType = { value: ParamInputSelectProps['value']; }; type ServiceParamsValueType = { - [key: string]: string; + [key: string]: string | undefined; }; type ServiceParamAssignInputValueType = { serviceEntity?: ApiKey; @@ -51,7 +51,7 @@ const ServiceParamAssignInput: React.FC = ({ const [innerValue, setInnerValue] = useControllableValue(props); const { list, replace, resetList } = useDynamicList([]); - const { run: handlerChange, cancel: cancelHandlerChange } = useDebounceFn( + const { run: handlerChange } = useDebounceFn( async (serviceEntity?: ApiKey) => { setInnerValue(pre => ({ ...pre, serviceEntity })); if (serviceEntity) { @@ -64,26 +64,13 @@ const ServiceParamAssignInput: React.FC = ({ if (!error) { resetList( res.map((item: EntityItem) => { - const reg = /#{([^}]+)}/g; - const valueItem = + const valueItem: ParamInputSelectProps['value'] = innerValue?.serviceParams?.[item.entity_key as string]; - let paramValue: ParamInputSelectProps['value'] = { - value: valueItem, - }; - if (valueItem) { - const match = reg.exec(valueItem); - if (match) { - const ref = match[1]; - if (ref) { - paramValue = { ref }; - } - } - } return { key: item.entity_key, name: item.entity_name, type: item.entity_value_type, - value: valueItem ? paramValue : undefined, + value: valueItem || undefined, }; }), ); @@ -125,10 +112,10 @@ const ServiceParamAssignInput: React.FC = ({ }, [list]); const transformParams = useCallback( (paramList: InputParamListType[]): ServiceParamsValueType => { + console.log('💯 ~ paramList:', paramList); const res: ServiceParamsValueType = {}; paramList.forEach(item => { - const { value = '', ref } = item.value || {}; - res[item.key] = ref ? `#{${ref}}` : value; + res[item.key] = item.value; }); return res; }, From b7e17a6b50a53d0ce9ddc7aad42796278ba30856 Mon Sep 17 00:00:00 2001 From: Nian Date: Fri, 27 Dec 2024 14:46:56 +0800 Subject: [PATCH 112/172] fix: adjust workflow list api --- .../components/service-param-assign-input/index.tsx | 1 - apps/web/src/services/http/workflow.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx index b717e110..f4cc406c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -112,7 +112,6 @@ const ServiceParamAssignInput: React.FC = ({ }, [list]); const transformParams = useCallback( (paramList: InputParamListType[]): ServiceParamsValueType => { - console.log('💯 ~ paramList:', paramList); const res: ServiceParamsValueType = {}; paramList.forEach(item => { res[item.key] = item.value; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index b97d9803..19ef4370 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -50,8 +50,6 @@ export interface WorkflowAPISchema extends APISchema { updated_at: number; /** User Nickname */ user_nickname: string; - /** workflow version */ - version: string; }[] >; }; From 4442aa51335a447f369aeb4fb26496583a2f04d7 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Dec 2024 17:07:20 +0800 Subject: [PATCH 113/172] feat: add new entity select component --- .../src/components/code-editor/constant.tsx | 2 +- .../components/entity-list/index.tsx | 51 ++++++++ .../components/entity-menu-popper/index.tsx | 26 ++++ .../components/entity-menu-popper/style.less | 7 + .../components/entity-menu/index.tsx | 40 ++++++ .../components/entity-menu/style.less | 31 +++++ .../components/entity-option/index.tsx | 48 +++++++ .../components/entity-option/style.less | 35 +++++ .../components/entity-paper/index.tsx | 40 ++++++ .../entity-select/components/index.tsx | 5 + .../src/components/entity-select/constant.ts | 12 ++ .../src/components/entity-select/context.ts | 4 + .../components/entity-select/entitySelect.tsx | 117 +++++++++++++++++ .../src/components/entity-select/helper.ts | 10 ++ .../components/entity-select/hooks/index.tsx | 3 + .../entity-select/hooks/useContextValue.tsx | 44 +++++++ .../entity-select/hooks/useOptions.tsx | 120 ++++++++++++++++++ .../entity-select/hooks/useSelectValue.tsx | 52 ++++++++ .../src/components/entity-select/index.tsx | 41 ++++++ .../src/components/entity-select/types.d.ts | 76 +++++++++++ apps/web/src/components/index.ts | 1 + apps/web/src/stores/entity.ts | 16 +++ apps/web/src/stores/index.ts | 1 + packages/locales/src/lang/en/global.json | 3 +- 24 files changed, 783 insertions(+), 2 deletions(-) create mode 100644 apps/web/src/components/entity-select/components/entity-list/index.tsx create mode 100644 apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx create mode 100644 apps/web/src/components/entity-select/components/entity-menu-popper/style.less create mode 100644 apps/web/src/components/entity-select/components/entity-menu/index.tsx create mode 100644 apps/web/src/components/entity-select/components/entity-menu/style.less create mode 100644 apps/web/src/components/entity-select/components/entity-option/index.tsx create mode 100644 apps/web/src/components/entity-select/components/entity-option/style.less create mode 100644 apps/web/src/components/entity-select/components/entity-paper/index.tsx create mode 100644 apps/web/src/components/entity-select/components/index.tsx create mode 100644 apps/web/src/components/entity-select/constant.ts create mode 100644 apps/web/src/components/entity-select/context.ts create mode 100644 apps/web/src/components/entity-select/entitySelect.tsx create mode 100644 apps/web/src/components/entity-select/helper.ts create mode 100644 apps/web/src/components/entity-select/hooks/index.tsx create mode 100644 apps/web/src/components/entity-select/hooks/useContextValue.tsx create mode 100644 apps/web/src/components/entity-select/hooks/useOptions.tsx create mode 100644 apps/web/src/components/entity-select/hooks/useSelectValue.tsx create mode 100644 apps/web/src/components/entity-select/index.tsx create mode 100644 apps/web/src/components/entity-select/types.d.ts create mode 100644 apps/web/src/stores/entity.ts diff --git a/apps/web/src/components/code-editor/constant.tsx b/apps/web/src/components/code-editor/constant.tsx index 68a85450..ca281d27 100644 --- a/apps/web/src/components/code-editor/constant.tsx +++ b/apps/web/src/components/code-editor/constant.tsx @@ -27,7 +27,7 @@ export const editorLangOptions: { }, { value: 'mvel', - label: 'mvel', + label: 'Mvel', }, { value: 'markdown', diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx new file mode 100644 index 00000000..cda5b731 --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -0,0 +1,51 @@ +import React, { useCallback, useContext, useEffect, useState } from 'react'; +import EntityMenuPopper from '../entity-menu-popper'; +import { EntityContext } from '../../context'; +import EntityOption from '../entity-option'; +import EntityMenu from '../entity-menu'; +import type { EntitySelectOption } from '../../types'; + +interface IProps { + children: React.ReactNode; +} +export default React.memo(({ children: _children, ...props }: IProps) => { + const { tabType, options, selectedEntityMap } = useContext(EntityContext); + + const [menuList, setMenuList] = useState([]); + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const open = Boolean(menuAnchorEl); + + /** When clicked, the pop-up window opens */ + const handleClick = useCallback((event: React.MouseEvent, option: EntitySelectOption) => { + setMenuAnchorEl(event.currentTarget as HTMLDivElement); + + const { children } = option || {}; + setMenuList(children || []); + }, []); + + useEffect(() => { + // When the tab changes, the pop-up window is closed + if (tabType !== 'device') { + setMenuAnchorEl(null); + } + }, [tabType]); + return ( + <> +
+ {(options || []).map(option => { + const { value } = option || {}; + const selected = selectedEntityMap.has(value); + + return tabType === 'entity' ? ( + + ) : ( + + ); + })} +
+ {tabType === 'device' && ( + + )} + + ); +}); diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx new file mode 100644 index 00000000..a3ee195d --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx @@ -0,0 +1,26 @@ +import React, { useContext } from 'react'; +import { Paper, Popper, PopperProps } from '@mui/material'; +import EntityOption from '../entity-option'; +import { EntityContext } from '../../context'; +import type { EntitySelectOption } from '../../types'; +import './style.less'; + +export interface IProps extends PopperProps { + menuList: EntitySelectOption[]; +} +export default React.memo(({ menuList, ...props }: IProps) => { + const { selectedEntityMap } = useContext(EntityContext); + + return ( + + + {(menuList || []).map(menu => { + const { value } = menu || {}; + const selected = selectedEntityMap.has(value); + + return ; + })} + + + ); +}); diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/style.less b/apps/web/src/components/entity-select/components/entity-menu-popper/style.less new file mode 100644 index 00000000..bbb164a6 --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/style.less @@ -0,0 +1,7 @@ +.ms-entity-menu-popper { + .ms-entity-menu-paper { + width: 360px; + max-height: 320px; + overflow: auto; + } +} diff --git a/apps/web/src/components/entity-select/components/entity-menu/index.tsx b/apps/web/src/components/entity-select/components/entity-menu/index.tsx new file mode 100644 index 00000000..92e38ccf --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-menu/index.tsx @@ -0,0 +1,40 @@ +import React, { useCallback } from 'react'; +import cls from 'classnames'; +import { ArrowForwardIosIcon } from '@milesight/shared/src/components'; +import Tooltip from '@/components/tooltip'; +import type { EntitySelectOption } from '../../types'; +import './style.less'; + +interface IProps { + option: EntitySelectOption; + selected?: boolean; + onClick?: (e: React.MouseEvent, option: EntitySelectOption) => void; +} +export default React.memo((props: IProps) => { + const { option, selected, onClick } = props; + const { label } = option || {}; + + /** When an entity item is selected/canceled */ + const handleClick = useCallback( + (e: React.MouseEvent) => { + onClick?.(e, option); + }, + [option, onClick], + ); + return ( +
+
+ +
+
+ +
+
+ ); +}); diff --git a/apps/web/src/components/entity-select/components/entity-menu/style.less b/apps/web/src/components/entity-select/components/entity-menu/style.less new file mode 100644 index 00000000..5aaa3953 --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-menu/style.less @@ -0,0 +1,31 @@ +.ms-entity-menu { + display: flex; + align-items: center; + justify-content: space-between; + padding: @padding-xs @padding-md; + cursor: pointer; + + &--active { + background-color: var(--component-background-gray); + } + + &:hover { + background-color: var(--component-background-gray); + } + + &__title { + overflow: hidden; + font-weight: @font-weight-regular; + .text-size(@font-size-md); + } + + &__icon { + padding: @padding-xxs; + color: var(--icon-color-gray-secondary); + + .MuiSvgIcon-root { + display: block; + font-size: @font-size-1; + } + } +} diff --git a/apps/web/src/components/entity-select/components/entity-option/index.tsx b/apps/web/src/components/entity-select/components/entity-option/index.tsx new file mode 100644 index 00000000..132767d9 --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-option/index.tsx @@ -0,0 +1,48 @@ +import React, { useCallback, useContext } from 'react'; +import cls from 'classnames'; +import { CheckIcon } from '@milesight/shared/src/components'; +import Tooltip from '@/components/tooltip'; +import { EntityContext } from '../../context'; +import type { EntitySelectOption } from '../../types'; +import './style.less'; + +interface IProps { + option: EntitySelectOption; + selected?: boolean; +} +export default React.memo((props: IProps) => { + const { onEntityChange } = useContext(EntityContext); + const { option, selected } = props; + const { label, description } = option || {}; + + /** When an entity item is selected/canceled */ + const handleClick = useCallback(() => { + onEntityChange(option); + }, [option, onEntityChange]); + + /** when mouse down, prevent default behavior */ + const handleMouseDown = useCallback((event: React.MouseEvent) => { + event.preventDefault(); + }, []); + return ( +
+
+
+ +
+ {description && ( +
+ +
+ )} +
+ {selected && } +
+ ); +}); diff --git a/apps/web/src/components/entity-select/components/entity-option/style.less b/apps/web/src/components/entity-select/components/entity-option/style.less new file mode 100644 index 00000000..e405fd71 --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-option/style.less @@ -0,0 +1,35 @@ +.ms-entity-content { + display: flex; + align-items: center; + justify-content: space-between; + padding: @padding-xs @padding-md; + cursor: pointer; + + &:hover { + background-color: var(--component-background-gray); + } + + &--active { + font-weight: @font-weight-medium; + color: var(--text-color-main); + background-color: var(--purple-1); + } + + .ms-entity-option { + display: flex; + flex-direction: column; + justify-content: center; + height: 42px; + overflow: hidden; + + &__title { + font-weight: @font-weight-regular; + .text-size(@font-size-md); + } + + &__description { + color: var(--text-color-tertiary); + .text-size(@font-size-sm); + } + } +} diff --git a/apps/web/src/components/entity-select/components/entity-paper/index.tsx b/apps/web/src/components/entity-select/components/entity-paper/index.tsx new file mode 100644 index 00000000..78a288ba --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-paper/index.tsx @@ -0,0 +1,40 @@ +import React, { useCallback, useContext } from 'react'; +import { Paper, PaperProps, Tab, Tabs } from '@mui/material'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { EntityContext } from '../../context'; +import { TabOptions } from '../../constant'; +import type { TabType } from '../../types'; + +export default React.memo(({ children, ...props }: PaperProps) => { + const { tabType, setTabType } = useContext(EntityContext); + const { getIntlText } = useI18n(); + + /** handle tab change */ + const handleTabChange = useCallback( + (_event: React.SyntheticEvent, value: TabType) => { + setTabType(value); + }, + [setTabType], + ); + /** when tab change, prevent default behavior */ + const handleMouseDown = useCallback((event: React.MouseEvent) => { + event.preventDefault(); + }, []); + return ( +
+ + + {TabOptions.map(({ label, value }) => ( + + ))} + + {children} + +
+ ); +}); diff --git a/apps/web/src/components/entity-select/components/index.tsx b/apps/web/src/components/entity-select/components/index.tsx new file mode 100644 index 00000000..48476171 --- /dev/null +++ b/apps/web/src/components/entity-select/components/index.tsx @@ -0,0 +1,5 @@ +export { default as EntityPaper } from './entity-paper'; +export { default as EntityList } from './entity-list'; +export { default as EntityOption } from './entity-option'; +export { default as EntityMenu } from './entity-menu'; +export { default as EntityMenuPopper } from './entity-menu-popper'; diff --git a/apps/web/src/components/entity-select/constant.ts b/apps/web/src/components/entity-select/constant.ts new file mode 100644 index 00000000..20c90bf9 --- /dev/null +++ b/apps/web/src/components/entity-select/constant.ts @@ -0,0 +1,12 @@ +import type { TabType } from './types'; + +export const TabOptions: { label: string; value: TabType }[] = [ + { + label: 'common.label.entity', + value: 'entity', + }, + { + label: 'common.label.by_device', + value: 'device', + }, +]; diff --git a/apps/web/src/components/entity-select/context.ts b/apps/web/src/components/entity-select/context.ts new file mode 100644 index 00000000..0908c1fd --- /dev/null +++ b/apps/web/src/components/entity-select/context.ts @@ -0,0 +1,4 @@ +import { createContext } from 'react'; +import type { EntitySelectContext } from './types'; + +export const EntityContext = createContext({} as EntitySelectContext); diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx new file mode 100644 index 00000000..40d7e273 --- /dev/null +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -0,0 +1,117 @@ +import React, { useState, useContext, useCallback, useMemo } from 'react'; +import { Autocomplete, TextField, AutocompleteProps, Chip } from '@mui/material'; +import Tooltip from '@/components/tooltip'; +import { EntityList, EntityPaper } from './components'; +import { EntityContext } from './context'; +import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; + +/** + * EntitySelect Component + */ +const EntitySelect = < + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, + Props extends EntitySelectProps = EntitySelectProps< + Value, + Multiple, + DisableClearable + >, + EntityAutocompleteProps extends Required< + AutocompleteProps + > = Required>, +>( + props: Props, +) => { + const { title, required, value, multiple, onChange, ...rest } = props; + const { options } = useContext>( + EntityContext as unknown as React.Context>, + ); + + // State to manage the open/close status of the select menu + const [anchorEl, setAnchorEl] = useState(null); + const open = Boolean(anchorEl); + + /** + * Handles opening of the select menu. + */ + const handleSelectOpen = useCallback['onOpen']>(event => { + setAnchorEl(event.currentTarget as HTMLDivElement); + }, []); + + /** + * Handles closing of the select menu. + */ + const handleSelectClose = useCallback['onClose']>(() => { + setAnchorEl(null); + }, []); + + /** + * Handles the change event when an option is selected or cleared. + */ + const handleChange = useCallback( + (_event, value) => { + onChange?.(value); + }, + [onChange], + ); + + /** + * Renders the input component for the Autocomplete. + */ + const renderInput = useCallback( + params => , + [required, title], + ); + + /** + * Renders the tags for the selected values when multiple selection is enabled. + */ + const renderTags = useCallback( + (value, getTagProps) => { + if (!multiple) return; + + return value.map((option, index) => { + const { key, ...tagProps } = getTagProps({ index }); + + return ( + + + + ); + }); + }, + [multiple], + ); + + /** + * Gets the display text for the input box based on the selected option. + */ + const getOptionLabel = useCallback( + option => option?.label || '', + [], + ); + + // Memoize custom slots and slot props to optimize rendering + const slots = useMemo(() => ({ paper: EntityPaper }), []); + const slotProps = useMemo(() => ({ listbox: { component: EntityList } }), []); + + return ( + + {...rest} + open={open} + value={value} + options={options} + multiple={multiple} + onChange={handleChange} + onOpen={handleSelectOpen} + onClose={handleSelectClose} + getOptionLabel={getOptionLabel} + renderTags={renderTags} + renderInput={renderInput} + slots={slots} + slotProps={slotProps} + /> + ); +}; +export default React.memo(EntitySelect) as unknown as typeof EntitySelect; diff --git a/apps/web/src/components/entity-select/helper.ts b/apps/web/src/components/entity-select/helper.ts new file mode 100644 index 00000000..94fb5087 --- /dev/null +++ b/apps/web/src/components/entity-select/helper.ts @@ -0,0 +1,10 @@ +/** + * Safe format json method + */ +export const safeJsonParse = (str: string) => { + try { + return JSON.parse(str); + } catch (e) { + return str; + } +}; diff --git a/apps/web/src/components/entity-select/hooks/index.tsx b/apps/web/src/components/entity-select/hooks/index.tsx new file mode 100644 index 00000000..9745c477 --- /dev/null +++ b/apps/web/src/components/entity-select/hooks/index.tsx @@ -0,0 +1,3 @@ +export { useOptions } from './useOptions'; +export { useSelectValue } from './useSelectValue'; +export { useContextValue } from './useContextValue'; diff --git a/apps/web/src/components/entity-select/hooks/useContextValue.tsx b/apps/web/src/components/entity-select/hooks/useContextValue.tsx new file mode 100644 index 00000000..ef9f896b --- /dev/null +++ b/apps/web/src/components/entity-select/hooks/useContextValue.tsx @@ -0,0 +1,44 @@ +import { useMemo, useState } from 'react'; +import { useOptions } from './useOptions'; +import { useSelectValue } from './useSelectValue'; +import type { + TabType, + EntitySelectContext, + EntitySelectProps, + EntitySelectValueType, +} from '../types'; + +export const useContextValue = < + V extends EntitySelectValueType = EntitySelectValueType, + M extends boolean | undefined = false, + D extends boolean | undefined = false, +>( + props: Pick, 'value' | 'multiple' | 'onChange'>, +) => { + const { value, multiple, onChange } = props; + + const [tabType, setTabType] = useState('entity'); + const { options } = useOptions({ tabType }); + const { selectedEntityMap, onEntityChange } = useSelectValue({ + value, + multiple, + onChange, + }); + + /** context value */ + const contextValue = useMemo(() => { + const result: EntitySelectContext = { + options: (options as V[]) || [], + tabType, + setTabType, + selectedEntityMap, + onEntityChange, + }; + + return result; + }, [onEntityChange, options, selectedEntityMap, tabType]); + + return { + contextValue, + }; +}; diff --git a/apps/web/src/components/entity-select/hooks/useOptions.tsx b/apps/web/src/components/entity-select/hooks/useOptions.tsx new file mode 100644 index 00000000..761e3d5f --- /dev/null +++ b/apps/web/src/components/entity-select/hooks/useOptions.tsx @@ -0,0 +1,120 @@ +import { useCallback, useMemo } from 'react'; +import { useShallow } from 'zustand/react/shallow'; +import { useI18n } from '@milesight/shared/src/hooks'; +import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { useEntityStore } from '@/stores'; +import { safeJsonParse } from '../helper'; +import type { EntitySelectOption, TabType } from '../types'; + +interface IProps { + tabType: TabType; +} + +export const useOptions = ({ tabType }: IProps) => { + const { getIntlText } = useI18n(); + const { entityList } = useEntityStore(useShallow(state => ({ entityList: state.entityList }))); + + /** Get description text */ + const getDescription = useCallback( + (entityType: string, entityKey: string, deviceName?: string) => { + const comma = getIntlText('common.symbol.comma'); + // Extract meaningful part of the entity key + const entityKeyText = entityKey.split('.').slice(3).join('.'); + + // Join entity type, entity key, and device name with commas + return [entityType, entityKeyText, deviceName].filter(Boolean).join(`${comma} `); + }, + [getIntlText], + ); + + /** Get value of the option based on entity data */ + const getOptionValue = useCallback( + (entityData: ObjectToCamelCase) => { + const { + deviceName, + entityId, + entityName, + entityType, + entityKey, + entityValueType, + entityValueAttribute: entityValueAttributeString, + } = entityData || {}; + + // Parse the entity value attribute from JSON string + const entityValueAttribute = safeJsonParse( + entityValueAttributeString, + ) as EntityValueAttributeType; + + // Create an entity item for the select option + const entityItem: EntitySelectOption = { + value: entityId, + label: entityName, + valueType: entityValueType, + description: getDescription(entityType, entityKey, deviceName), + rawData: { + ...entityData, + entityValueAttribute, + }, + }; + return entityItem; + }, + [getDescription], + ); + + /** Get entity drop-down options and device drop-down options */ + const { entityOptions, deviceOptions } = useMemo(() => { + const { entityOptions, deviceMap } = entityList.reduce<{ + deviceMap: Map; + entityOptions: EntitySelectOption[]; + }>( + (prev, entity) => { + const { entityOptions, deviceMap } = prev; + + // Convert entity data to camel case + const entityData = objectToCamelCase(entity || {}); + const { deviceName, entityType, entityKey } = entityData || {}; + const entityItem = getOptionValue(entityData); + entityOptions.push(entityItem); + + // Create or update device group + let deviceGroup = deviceMap.get(deviceName); + if (!deviceGroup) { + deviceGroup = { + value: deviceName, + label: deviceName, + children: [], + }; + } + deviceGroup.children?.push({ + ...entityItem, + description: getDescription(entityType, entityKey), + }); + deviceMap.set(deviceName, deviceGroup); + + return { + deviceMap, + entityOptions, + }; + }, + { + deviceMap: new Map(), + entityOptions: [], + }, + ); + + return { + entityOptions, + deviceOptions: Array.from(deviceMap.values()), + }; + }, [entityList, getDescription, getOptionValue]); + + /** Get the corresponding drop-down rendering options based on `tabType` */ + const options = useMemo( + () => (tabType === 'entity' ? entityOptions : deviceOptions), + [deviceOptions, entityOptions, tabType], + ); + + return { + options, + }; +}; diff --git a/apps/web/src/components/entity-select/hooks/useSelectValue.tsx b/apps/web/src/components/entity-select/hooks/useSelectValue.tsx new file mode 100644 index 00000000..27b6683a --- /dev/null +++ b/apps/web/src/components/entity-select/hooks/useSelectValue.tsx @@ -0,0 +1,52 @@ +import { useCallback, useMemo } from 'react'; +import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from '../types'; + +export const useSelectValue = < + V extends EntitySelectValueType = EntitySelectValueType, + M extends boolean | undefined = false, + D extends boolean | undefined = false, +>( + props: Pick, 'value' | 'multiple' | 'onChange'>, +) => { + const { value, multiple, onChange } = props; + + /** selected Value to entity map */ + const selectedEntityMap = useMemo(() => { + if (!value) return new Map(); + + const valueList: V[] = Array.isArray(value) ? value : ([value] as unknown as V[]); + return valueList.reduce>((acc, curr) => { + acc.set(curr.value, curr); + return acc; + }, new Map()); + }, [value]); + + /** Select/cancel entity selection callback */ + const onEntityChange = useCallback( + selectedItem => { + const { value } = selectedItem || {}; + if (!value) return; + + if (!multiple) { + // single select + onChange?.(selectedItem); + return; + } + + // multiple select + if (selectedEntityMap.has(value)) { + selectedEntityMap.delete(value); + } else { + selectedEntityMap.set(value, selectedItem!); + } + const onMultipleChange = onChange as EntitySelectProps['onChange']; + onMultipleChange?.(Array.from(selectedEntityMap.values())); + }, + [multiple, onChange, selectedEntityMap], + ); + + return { + selectedEntityMap, + onEntityChange, + }; +}; diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx new file mode 100644 index 00000000..5fd705f1 --- /dev/null +++ b/apps/web/src/components/entity-select/index.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useControllableValue } from 'ahooks'; +import EntitySelect from './entitySelect'; +import { useContextValue } from './hooks'; +import { EntityContext } from './context'; +import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; + +const EntitySelectApp = < + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, + Props extends EntitySelectProps = EntitySelectProps< + Value, + Multiple, + DisableClearable + >, +>( + props: Props, +) => { + const { multiple } = props; + const [value, onChange] = useControllableValue['value']>(props); + const { contextValue } = useContextValue({ + value, + multiple, + onChange, + }); + + return ( + + + {...props} + multiple={multiple} + value={value} + onChange={onChange} + /> + + ); +}; +export default React.memo(EntitySelectApp) as unknown as typeof EntitySelectApp; + +export type { EntitySelectOption } from './types'; diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts new file mode 100644 index 00000000..4cb5aa94 --- /dev/null +++ b/apps/web/src/components/entity-select/types.d.ts @@ -0,0 +1,76 @@ +import type { AutocompleteProps, TextFieldProps } from '@mui/material'; + +/** Define the possible tab types */ +export type TabType = 'entity' | 'device'; + +/** Define the possible value types for entities */ +export type EntityValueType = ApiKey; + +/** + * Represents an option in the EntitySelect component. + */ +export interface EntitySelectOption { + /** The value of the option */ + value: T; + /** The display label of the option */ + label: string; + /** Optional value type */ + valueType?: string; + /** Optional description of the option */ + description?: string; + /** Additional raw data associated with the option */ + rawData?: ObjectToCamelCase> & { + entityValueAttribute: EntityValueAttributeType; + }; + /** Optional children options for hierarchical selection */ + children?: EntitySelectOption[]; +} + +export type EntitySelectValueType = EntitySelectOption; + +/** + * Type to represent the value of the EntitySelect component based on its configuration. + */ +export type EntitySelectValue = Multiple extends true + ? Array + : DisableClearable extends true + ? NonNullable + : Value | null; + +/** + * Props for the EntitySelect component. + */ +export interface EntitySelectProps< + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, +> extends Pick, + Omit< + AutocompleteProps, + 'renderInput' | 'options' + > { + /** Whether multiple selection is enabled */ + multiple?: Multiple; + /** The current value of the select */ + value?: EntitySelectValue; + /** Callback function when the value changes */ + onChange?: (value: EntitySelectValue) => void; + /** Whether the clear button is disabled */ + disableClearable?: DisableClearable; +} + +/** + * Context for the EntitySelect component. + */ +export interface EntitySelectContext { + /** Available options for selection */ + options: V[]; + /** The current tab type */ + tabType: TabType; + /** Function to set the tab type */ + setTabType: (tabType: TabType) => void; + /** The map of selected entities */ + selectedEntityMap: Map; + /** Callback function when an entity is selected or changed */ + onEntityChange: (selectedItem: EntitySelectValue) => void; +} diff --git a/apps/web/src/components/index.ts b/apps/web/src/components/index.ts index c2a19aa4..81057fe3 100644 --- a/apps/web/src/components/index.ts +++ b/apps/web/src/components/index.ts @@ -8,6 +8,7 @@ export { default as Tooltip } from './tooltip'; export { default as DateRangePicker } from './date-range-picker'; export { default as RouteLoadingIndicator } from './route-loading-indicator'; export { default as Empty } from './empty'; +export { default as EntitySelect } from './entity-select'; export { CodeEditor, CodeEditorToolbar, diff --git a/apps/web/src/stores/entity.ts b/apps/web/src/stores/entity.ts new file mode 100644 index 00000000..0184ee05 --- /dev/null +++ b/apps/web/src/stores/entity.ts @@ -0,0 +1,16 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import { type EntityAPISchema } from '@/services/http'; + +type EntityData = EntityAPISchema['getList']['response']['content'][number]; +interface EntityStore { + entityList: EntityData[]; + setEntityList: (entityList: EntityData[]) => void; +} + +export default create( + immer(set => ({ + entityList: [], + setEntityList: entityList => set({ entityList }), + })), +); diff --git a/apps/web/src/stores/index.ts b/apps/web/src/stores/index.ts index 452e4526..abe6cf39 100644 --- a/apps/web/src/stores/index.ts +++ b/apps/web/src/stores/index.ts @@ -1 +1,2 @@ export { default as useUserStore } from './user'; +export { default as useEntityStore } from './entity'; diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 7e5c318d..29ac9f14 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -168,5 +168,6 @@ "common.label.input": "Input", "common.label.output": "Output", "common.message.json_format_error": "JSON 格式错误", - "common.label.tip": "Tip" + "common.label.tip": "Tip", + "common.label.by_device": "By device" } From 0caf24b0604bd62e71edf4bb80ad82bd3c83a0ec Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Fri, 27 Dec 2024 17:26:53 +0800 Subject: [PATCH 114/172] feat: Adds virtual scrolling to the physical drop-down component --- .../components/entity-list/index.tsx | 40 +++++++++++-------- .../components/entity-menu/style.less | 2 + .../components/entity-paper/index.tsx | 4 +- .../src/components/entity-select/index.tsx | 2 +- 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx index cda5b731..8e076d0f 100644 --- a/apps/web/src/components/entity-select/components/entity-list/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -1,4 +1,5 @@ -import React, { useCallback, useContext, useEffect, useState } from 'react'; +import React, { useCallback, useContext, useRef, useState } from 'react'; +import { useVirtualList } from '@milesight/shared/src/hooks'; import EntityMenuPopper from '../entity-menu-popper'; import { EntityContext } from '../../context'; import EntityOption from '../entity-option'; @@ -10,6 +11,8 @@ interface IProps { } export default React.memo(({ children: _children, ...props }: IProps) => { const { tabType, options, selectedEntityMap } = useContext(EntityContext); + const containerRef = useRef(null); + const listRef = useRef(null); const [menuList, setMenuList] = useState([]); const [menuAnchorEl, setMenuAnchorEl] = useState(null); @@ -23,25 +26,28 @@ export default React.memo(({ children: _children, ...props }: IProps) => { setMenuList(children || []); }, []); - useEffect(() => { - // When the tab changes, the pop-up window is closed - if (tabType !== 'device') { - setMenuAnchorEl(null); - } - }, [tabType]); + /** virtual list */ + const [virtualList] = useVirtualList(options, { + containerTarget: containerRef, + wrapperTarget: listRef, + itemHeight: tabType === 'entity' ? 58 : 38, + overscan: 10, + }); return ( <> -
- {(options || []).map(option => { - const { value } = option || {}; - const selected = selectedEntityMap.has(value); +
+
+ {(virtualList || []).map(({ data: option }) => { + const { value } = option || {}; + const selected = selectedEntityMap.has(value); - return tabType === 'entity' ? ( - - ) : ( - - ); - })} + return tabType === 'entity' ? ( + + ) : ( + + ); + })} +
{tabType === 'device' && ( diff --git a/apps/web/src/components/entity-select/components/entity-menu/style.less b/apps/web/src/components/entity-select/components/entity-menu/style.less index 5aaa3953..e2a64cd5 100644 --- a/apps/web/src/components/entity-select/components/entity-menu/style.less +++ b/apps/web/src/components/entity-select/components/entity-menu/style.less @@ -14,6 +14,8 @@ } &__title { + width: 100%; + height: 22px; overflow: hidden; font-weight: @font-weight-regular; .text-size(@font-size-md); diff --git a/apps/web/src/components/entity-select/components/entity-paper/index.tsx b/apps/web/src/components/entity-select/components/entity-paper/index.tsx index 78a288ba..19df1a6f 100644 --- a/apps/web/src/components/entity-select/components/entity-paper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-paper/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext } from 'react'; +import React, { Fragment, useCallback, useContext } from 'react'; import { Paper, PaperProps, Tab, Tabs } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { EntityContext } from '../../context'; @@ -33,7 +33,7 @@ export default React.memo(({ children, ...props }: PaperProps) => { ))} - {children} + {children}
); diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index 5fd705f1..6ab54818 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -38,4 +38,4 @@ const EntitySelectApp = < }; export default React.memo(EntitySelectApp) as unknown as typeof EntitySelectApp; -export type { EntitySelectOption } from './types'; +export type { EntitySelectOption, EntitySelectValueType, EntitySelectProps } from './types'; From 32e4d0ea9bbc76cb31db362b9f765ed9bc26d642 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 27 Dec 2024 17:49:51 +0800 Subject: [PATCH 115/172] feat: complete the single node test --- apps/web/src/pages/workflow/config.tsx | 9 + .../components/test-drawer/index.tsx | 211 +++++++++++++++--- .../components/test-drawer/style.less | 23 ++ .../editor/components/config-panel/index.tsx | 11 +- apps/web/src/services/http/workflow.ts | 2 +- packages/locales/src/lang/en/global.json | 4 +- packages/locales/src/lang/en/workflow.json | 3 +- 7 files changed, 232 insertions(+), 31 deletions(-) diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index ac6b1630..ccc6e0c4 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -74,6 +74,10 @@ export type NodeConfigItemType = { * Independent testing enabled */ testable?: boolean; + /** + * The keys that can be used in test input + */ + testInputKeys?: string[]; }; /** @@ -129,6 +133,7 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#26A69A', category: 'action', + testInputKeys: ['inputArguments'], }, assigner: { type: 'assigner', @@ -137,6 +142,7 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#26A69A', category: 'action', + testInputKeys: ['exchangePayload'], }, service: { type: 'service', @@ -145,6 +151,7 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#26A69A', category: 'action', + testInputKeys: ['serviceInvocationSetting.serviceParams'], }, select: { type: 'select', @@ -153,6 +160,7 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#26A69A', category: 'action', + testInputKeys: ['entities'], }, email: { type: 'email', @@ -169,6 +177,7 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#7E57C2', category: 'external', + testInputKeys: ['inputArguments'], }, }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx index 4b82657c..0d69de34 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx @@ -1,54 +1,211 @@ -import React from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import cls from 'classnames'; -import { Backdrop, Slide, IconButton, Button, Divider, Alert } from '@mui/material'; +import { get } from 'lodash-es'; +import { useRequest } from 'ahooks'; +import { + Backdrop, + Slide, + IconButton, + Button, + Divider, + Alert, + CircularProgress, +} from '@mui/material'; +import { useReactFlow } from '@xyflow/react'; import { useI18n } from '@milesight/shared/src/hooks'; -import { CloseIcon, PlayArrowIcon, CheckCircleIcon } from '@milesight/shared/src/components'; -import { CodeEditor } from '@/components'; +import { genRandomString } from '@milesight/shared/src/utils/tools'; +import { CloseIcon, PlayArrowIcon, CheckCircleIcon, toast } from '@milesight/shared/src/components'; +import { CodeEditor, Tooltip } from '@/components'; +import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; +import useFlowStore from '../../../../store'; +import { isRefParamKey } from '../../../../helper'; import './style.less'; export interface TestDrawerProps { - title?: string; + node?: WorkflowNode; open: boolean; onClose: () => void; } -const TestDrawer: React.FC = ({ title, open, onClose }) => { +const statusDefaultMsgKey: Record = { + ERROR: 'common.label.error', + SUCCESS: 'common.label.success', +}; + +const TestDrawer: React.FC = ({ node, open, onClose }) => { const { getIntlText } = useI18n(); + const { getNode } = useReactFlow(); + + // ---------- Basic Node Info ---------- + const nodeId = node?.id; + const nodeConfigs = useFlowStore(state => state.nodeConfigs); + const nodeConfig = useMemo(() => { + if (!node) return; + return nodeConfigs[node.type as WorkflowNodeType]; + }, [node, nodeConfigs]); + const title = useMemo(() => { + let tit = node?.data.nodeName; + if (!tit) { + tit = nodeConfig?.labelIntlKey ? getIntlText(nodeConfig.labelIntlKey) : ''; + } + + return getIntlText('workflow.editor.config_panel_test_title', { 1: tit }); + }, [node, nodeConfig, getIntlText]); + + // ---------- Generate Demo Data ---------- + const [inputData, setInputData] = useState(''); + const genDemoData = useCallback(() => { + if (!open || !nodeId || !nodeConfig) return; + // Get the latest node data + const node = getNode(nodeId); + const { parameters } = node?.data || {}; + const result: Record = {}; + const inputArgs = get(parameters, nodeConfig.testInputKeys || []); + + if (!inputArgs) return result; + + // TODO: Generate different type data based on reference key type ? + switch (nodeConfig.type) { + case 'code': + case 'service': + case 'assigner': + case 'webhook': { + Object.entries(inputArgs).forEach(([key, value]) => { + if (!key) return; + result[key] = + value && !isRefParamKey(value as string) + ? value + : genRandomString(8, { lowerCase: true }); + }); + break; + } + case 'select': { + inputArgs.forEach((key: string) => { + result[key] = genRandomString(8, { lowerCase: true }); + }); + break; + } + default: { + break; + } + } + + return result; + }, [open, nodeId, nodeConfig, getNode]); + + // ---------- Run Test ---------- + const hasInput = nodeConfig?.testable && !!nodeConfig.testInputKeys?.length; + const { + loading, + data: testResult, + run: testSingleNode, + } = useRequest( + async (value?: string) => { + if (!open || !nodeId) return; + let input: Record; + + try { + input = !value ? undefined : JSON.parse(value || '{}'); + } catch (e) { + toast.error({ content: getIntlText('common.message.json_format_error') }); + return; + } + + const node = getNode(nodeId); + const [error, resp] = await awaitWrap( + workflowAPI.testSingleNode({ input, node_config: JSON.stringify(node) }), + ); + + if (error || !isRequestSuccess(resp)) return; + return getResponseData(resp); + }, + { + manual: true, + debounceWait: 300, + refreshDeps: [open, nodeId], + }, + ); + + useEffect(() => { + if (hasInput) { + setInputData(JSON.stringify(genDemoData(), null, 2)); + return; + } + + testSingleNode(); + }, [hasInput, genDemoData, testSingleNode]); + + // Clear Data when panel closed + useEffect(() => { + if (open) return; + setInputData(''); + testSingleNode(); + }, [open, testSingleNode]); return ( -
+
e.stopPropagation()}>
- {title || 'Test xxx Node'} +
-
- - -
- -
- }> - Success - - -
+ {hasInput && ( +
+ + +
+ )} + {testResult && ( + <> + {hasInput && } +
+ } + > + {testResult.error_message || + getIntlText( + statusDefaultMsgKey[testResult.status] || '', + )} + + {testResult.output && ( + + )} +
+ + )}
+ {loading && }
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less index 890b0726..c0fbe280 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/style.less @@ -9,6 +9,15 @@ z-index: 10; } + &.loading { + .@{prefix}-config-panel-test-drawer { + &-body { + opacity: .5; + pointer-events: none; + } + } + } + .@{mui-prefix}Backdrop-root { position: absolute; align-items: end; @@ -16,6 +25,9 @@ } .@{prefix}-config-panel-test-drawer { + position: relative; + display: flex; + flex-direction: column; width: 100%; height: 90%; background-color: var(--component-background); @@ -28,11 +40,15 @@ } &-title { + flex: 1; + width: 0; font-weight: @font-weight-bold; .text-size(@font-size-xxl); } &-body { + flex: 1; + height: 0; padding: 0 @padding-xl @padding-xl; .@{prefix}-code-editor { @@ -48,4 +64,11 @@ margin: @margin-xl 0; } } + + .@{mui-prefix}CircularProgress-root { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } } diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 3166b321..35b25c51 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -101,6 +101,11 @@ const ConfigPanel = () => { // ---------- Show Test Drawer ---------- const [drawerOpen, setDrawerOpen] = useState(false); + useEffect(() => setDrawerOpen(false), [selectedNode]); + useEffect(() => { + if (drawerOpen) return; + setDrawerOpen(false); + }, [drawerOpen]); return ( { )}
- setDrawerOpen(false)} /> + setDrawerOpen(false)} + />
); diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 19ef4370..ece2b821 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -224,7 +224,7 @@ export interface WorkflowAPISchema extends APISchema { /** Test single node */ testSingleNode: { request: { - input: Record; + input?: Record; /** Node Config (JSON string) */ node_config: string; }; diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index a837a2f0..75a06f5f 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -170,5 +170,7 @@ "common.message.json_format_error": "JSON 格式错误", "common.label.tip": "Tip", "common.label.export": "Export", - "common.label.creator": "Creator" + "common.label.creator": "Creator", + "common.label.error": "Error", + "common.label.success": "Success" } diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index aa774b24..5e041c27 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -108,5 +108,6 @@ "workflow.valid.node_id_duplicated": "{1} and {2} Node ID is duplicated.", "workflow.valid.edge_id_duplicated": "The Edge (connecting node {1} and {2}) ID is duplicated.", "workflow.node.service_helptext": "Please select the service you want to call first.", - "workflow.node.input_variables": "Input Variables" + "workflow.node.input_variables": "Input Variables", + "workflow.editor.config_panel_test_title": "Test {1}" } From de68b788d078136eb116be948fff928843fc4e19 Mon Sep 17 00:00:00 2001 From: jimco Date: Fri, 27 Dec 2024 19:12:59 +0800 Subject: [PATCH 116/172] feat: complete workflow test --- .../components/param-input/index.tsx | 4 +- .../editor/components/log-panel/index.tsx | 159 ++++++++++++++---- .../editor/components/log-panel/style.less | 19 +++ .../src/pages/workflow/views/editor/store.ts | 28 +-- packages/shared/types/workflow.d.ts | 9 +- 5 files changed, 161 insertions(+), 58 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index 01aec60f..f7b61187 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -42,9 +42,9 @@ export interface ParamInputProps { const DEFAULT_EMPTY_VALUE: ParamInputValueType = { name: '', - type: '', + type: '' as WorkflowParamValueType, }; -const typeOptions = ['INT', 'FLOAT', 'BOOLEAN', 'STRING']; +const typeOptions: WorkflowParamValueType[] = ['INT', 'FLOAT', 'BOOLEAN', 'STRING']; const ParamInput: React.FC = ({ required, diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index ed9946dc..54eff4ab 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -1,10 +1,12 @@ -import { useState, useMemo, useEffect } from 'react'; -import { Stack, IconButton, Button, TextField, CircularProgress } from '@mui/material'; +import { useState, useMemo, useEffect, useCallback } from 'react'; +import { Stack, IconButton, Button, CircularProgress } from '@mui/material'; import { Panel, useReactFlow } from '@xyflow/react'; import cls from 'classnames'; import { useRequest } from 'ahooks'; +import { genRandomString } from '@milesight/shared/src/utils/tools'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; -import { CloseIcon, PlayArrowIcon } from '@milesight/shared/src/components'; +import { CloseIcon, PlayArrowIcon, toast } from '@milesight/shared/src/components'; +import { CodeEditor } from '@/components'; import { ActionLog } from '@/pages/workflow/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import useWorkflow from '../../hooks/useWorkflow'; @@ -22,7 +24,7 @@ const LogPanel = () => { logPanelMode, logDetail, logDetailLoading, - testLogs, + addTestLog, setOpenLogPanel, setLogDetail, setLogDetailLoading, @@ -32,7 +34,7 @@ const LogPanel = () => { 'logPanelMode', 'logDetail', 'logDetailLoading', - 'testLogs', + 'addTestLog', 'setOpenLogPanel', 'setLogDetail', 'setLogDetailLoading', @@ -57,53 +59,142 @@ const LogPanel = () => { return ''; } }, [logPanelMode, getIntlText]); + const isTestRunMode = logPanelMode === 'testRun'; - const handleClose = () => { - // TODO: remove node `$status` prop and close the panel + const handleClose = useCallback(() => { setOpenLogPanel(false); + setEntryInput(''); + setLogDetail(undefined); + setLogDetailLoading(false); updateNodesStatus(null); - }; + }, [setLogDetail, setLogDetailLoading, setOpenLogPanel, updateNodesStatus]); // ---------- Run Test ---------- const [entryInput, setEntryInput] = useState(''); - const showTestInput = useMemo(() => { - if (!flowData || logPanelMode !== 'testRun') return false; + const hasInput = useMemo(() => { + if (!openLogPanel || !flowData || !isTestRunMode) return false; const nodes = getNodes(); - const hasTriggerNode = nodes.find(node => node.type === 'trigger'); - return !!hasTriggerNode; - }, [flowData, logPanelMode, getNodes]); + return !!nodes.find(({ type }) => type === 'trigger' || type === 'listener'); + }, [openLogPanel, flowData, isTestRunMode, getNodes]); + + const genDemoData = useCallback(() => { + if (!openLogPanel || !isTestRunMode) return; + const nodes = getNodes(); + const entryNode = nodes.find(({ type }) => type === 'trigger' || type === 'listener'); + const { parameters } = entryNode?.data || {}; + const result: Record = {}; + + switch (entryNode?.type) { + case 'trigger': { + const configs = parameters?.entityConfigs as NonNullable< + TriggerNodeDataType['parameters'] + >['entityConfigs']; + + if (!configs?.length) return result; + configs.forEach(({ name, type }) => { + if (!name) return; + let value: any = genRandomString(8, { lowerCase: true }); + + switch (type) { + case 'BOOLEAN': { + value = Math.random() > 0.5; + break; + } + case 'INT': + case 'FLOAT': { + value = Math.floor(Math.random() * 100); + break; + } + default: { + break; + } + } + result[name] = value; + }); + break; + } + case 'listener': { + const inputArgs = parameters?.entities; + + if (!inputArgs) return result; + inputArgs.forEach((key: string) => { + result[key] = genRandomString(8, { lowerCase: true }); + }); + break; + } + default: { + break; + } + } + + return result; + }, [openLogPanel, isTestRunMode, getNodes]); + const { run: runFlowTest } = useRequest( - async () => { - if (logPanelMode !== 'testRun') return; + async (value?: string) => { + if (!openLogPanel || !isTestRunMode) return; setLogDetailLoading(true); - const dsl = toObject(); - // TODO: insert entryInput value into dsl - const [error, resp] = await awaitWrap(workflowAPI.runFlow({ dsl })); + + let input: Record; + try { + input = !value ? undefined : JSON.parse(value || '{}'); + } catch (e) { + toast.error({ content: getIntlText('common.message.json_format_error') }); + return; + } + const designData = toObject(); + const [error, resp] = await awaitWrap( + workflowAPI.testFlow({ input, design_data: JSON.stringify(designData) }), + ); setLogDetailLoading(false); if (error || !isRequestSuccess(resp)) return; const data = getResponseData(resp); + const nodeStatus = + data?.trace_infos.reduce( + (result: Record, { node_id: nodeId, status }) => { + result[nodeId] = status; + return result; + }, + {}, + ) || null; + addTestLog({ id: genRandomString(8, { lowerCase: true }), ...data! }); setLogDetail(data?.trace_infos); + updateNodesStatus(nodeStatus); }, { manual: true, debounceWait: 300, - refreshDeps: [logPanelMode, entryInput, toObject], + refreshDeps: [openLogPanel, isTestRunMode, entryInput, toObject], }, ); // Auto run flow test when there is not trigger node in workflow useEffect(() => { - if (logPanelMode !== 'testRun' || showTestInput) return; + if (!openLogPanel || !isTestRunMode) return; + if (hasInput) { + setEntryInput(JSON.stringify(genDemoData(), null, 2)); + return; + } + runFlowTest(); - }, [logPanelMode, showTestInput, runFlowTest]); + }, [openLogPanel, isTestRunMode, hasInput, genDemoData, runFlowTest]); + + // Clear Data when panel mode change + useEffect(() => { + if (!logPanelMode) return; + handleClose(); + }, [logPanelMode, handleClose]); return (
@@ -117,14 +208,13 @@ const LogPanel = () => {
- {showTestInput && ( + {hasInput && (
- {/* TODO: Replace TextField with JSON Editor */} - setEntryInput(e.target.value)} +
)} -
- {!!logDetail?.length && flowData && ( + {!!logDetail?.length && flowData && ( +
- )} -
+
+ )}
+ {logDetailLoading && }
); diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less index be913687..2b43ba3e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less @@ -9,6 +9,13 @@ display: none; } + &.loading { + .@{prefix}-workflow-panel-config-body { + opacity: .5; + pointer-events: none; + } + } + &:not(.hidden) ~ .@{prefix}-workflow-panel-config-root { right: 445px - @padding-xs; } @@ -23,10 +30,22 @@ box-shadow: @shadow-2; .input-area { + .@{prefix}-code-editor { + height: 200px; + margin-bottom: @margin-xl; + } + ~ .log-detail-area { padding-top: @padding-xl; margin-top: @margin-xl; border-top: 1px solid var(--border-color-base); } } + + .@{mui-prefix}CircularProgress-root { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } } diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index 5b3949ba..34b7a2b1 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -45,6 +45,7 @@ interface FlowStore { setOpenLogPanel: (open: FlowStore['openLogPanel']) => void; setTestLogs: (testLogs: FlowStore['testLogs']) => void; + addTestLog: (log?: NonNullable[0]) => void; setRunLogs: (runLogs: FlowStore['runLogs']) => void; @@ -59,26 +60,7 @@ const useFlowStore = create( immer(set => ({ nodeConfigs: basicNodeConfigs, - testLogs: [ - { - id: '1', - start_time: 1733809691235, - time_cost: 1000, - status: 'SUCCESS', - }, - { - id: '2', - start_time: 1733809691235, - time_cost: 1000, - status: 'ERROR', - }, - { - id: '3', - start_time: 1733809691235, - time_cost: 1000, - status: 'ERROR', - }, - ], + testLogs: [], setNodeConfigs: nodeConfigs => { const configs = Object.entries(nodeConfigs).reduce((acc, [cat, configs]) => { @@ -115,6 +97,12 @@ const useFlowStore = create( setLogPanelMode: logPanelMode => set({ logPanelMode }), setOpenLogPanel: open => set({ openLogPanel: open }), setTestLogs: testLogs => set({ testLogs }), + addTestLog: log => { + set(state => { + if (!log) return; + state.testLogs?.unshift(log); + }); + }, setRunLogs: runLogs => set({ runLogs }), setLogDetail: detail => set({ logDetail: detail }), setLogDetailLoading: loading => set({ logDetailLoading: loading }), diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index b0cc97ab..eb7fb8fa 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -67,6 +67,11 @@ declare type WorkflowNodeCategoryType = 'entry' | 'control' | 'action' | 'extern */ declare type WorkflowNodeStatus = 'ERROR' | 'SUCCESS'; +/** + * Node Param Value Type + */ +declare type WorkflowParamValueType = 'INT' | 'FLOAT' | 'BOOLEAN' | 'STRING'; + /** * Node Base Data Model(Properties that begin with `$` are private to the frontend) */ @@ -90,7 +95,7 @@ declare type TriggerNodeDataType = BaseNodeDataType<{ /** Entity Definition */ entityConfigs: { name: string; - type: string; + type: WorkflowParamValueType; }[]; }>; @@ -283,7 +288,7 @@ declare type WorkflowNode = ? ReactFlowNode, 'email'> : T extends 'webhook' ? ReactFlowNode, 'webhook'> - : ReactFlowNode>; + : ReactFlowNode, WorkflowNodeType>; /** * Workflow Edge Model From 320891e2e9ba08ce7f93098da688f156589efab1 Mon Sep 17 00:00:00 2001 From: jimco Date: Sat, 28 Dec 2024 18:43:40 +0800 Subject: [PATCH 117/172] feat: complete the data import and param reference --- apps/web/src/pages/workflow/config.tsx | 15 ++- apps/web/src/pages/workflow/index.tsx | 6 +- .../components/param-assign-input/index.tsx | 2 +- .../components/param-input-select/index.tsx | 86 ++++------------ .../components/test-drawer/index.tsx | 63 ++++++------ .../components/upstream-node-list/index.tsx | 2 +- .../pages/workflow/views/editor/constants.ts | 4 +- .../src/pages/workflow/views/editor/helper.ts | 14 ++- .../views/editor/hooks/useWorkflow.ts | 99 ++++++++++++++----- .../src/pages/workflow/views/editor/index.tsx | 16 ++- packages/shared/types/workflow.d.ts | 15 ++- 11 files changed, 186 insertions(+), 136 deletions(-) diff --git a/apps/web/src/pages/workflow/config.tsx b/apps/web/src/pages/workflow/config.tsx index ccc6e0c4..cb5b4100 100644 --- a/apps/web/src/pages/workflow/config.tsx +++ b/apps/web/src/pages/workflow/config.tsx @@ -71,13 +71,17 @@ export type NodeConfigItemType = { */ category: WorkflowNodeCategoryType; /** - * Independent testing enabled + * Enable independent testing */ testable?: boolean; /** * The keys that can be used in test input */ testInputKeys?: string[]; + /** + * The keys that can be referenced in downstream node + */ + outputKeys?: string[]; }; /** @@ -92,6 +96,8 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#3491FA', category: 'entry', + testInputKeys: ['entityConfigs'], + outputKeys: ['entityConfigs'], }, timer: { type: 'timer', @@ -110,6 +116,8 @@ export const basicNodeConfigs: Record = { icon: , iconBgColor: '#3491FA', category: 'entry', + testInputKeys: ['entities'], + outputKeys: ['entities'], }, ifelse: { type: 'ifelse', @@ -134,6 +142,7 @@ export const basicNodeConfigs: Record = { iconBgColor: '#26A69A', category: 'action', testInputKeys: ['inputArguments'], + outputKeys: ['Payload'], }, assigner: { type: 'assigner', @@ -143,6 +152,7 @@ export const basicNodeConfigs: Record = { iconBgColor: '#26A69A', category: 'action', testInputKeys: ['exchangePayload'], + outputKeys: ['exchangePayload'], }, service: { type: 'service', @@ -152,6 +162,7 @@ export const basicNodeConfigs: Record = { iconBgColor: '#26A69A', category: 'action', testInputKeys: ['serviceInvocationSetting.serviceParams'], + outputKeys: ['serviceInvocationSetting.serviceParams'], }, select: { type: 'select', @@ -161,6 +172,7 @@ export const basicNodeConfigs: Record = { iconBgColor: '#26A69A', category: 'action', testInputKeys: ['entities'], + outputKeys: ['entities'], }, email: { type: 'email', @@ -178,6 +190,7 @@ export const basicNodeConfigs: Record = { iconBgColor: '#7E57C2', category: 'external', testInputKeys: ['inputArguments'], + outputKeys: ['inputArguments'], }, }; diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index baf428e2..7dbfbc25 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -39,7 +39,7 @@ const Workflow = () => { data: workflowList, loading, run: getWorkflowList, - mutate: updateWorkworkflowList, + mutate: updateWorkflowList, } = useRequest( async () => { const { page, pageSize } = paginationModel; @@ -134,7 +134,7 @@ const Workflow = () => { (isOpen: boolean, contains?: WorkflowSchema) => { if (contains) { // TODO: wid should be deleted - navigate('/workflow/editor?wid=12121', { + navigate('/workflow/editor', { state: { workflowSchema: contains, }, @@ -229,7 +229,7 @@ const Workflow = () => { status: enabled ? 'disable' : 'enable', }), ); - updateWorkworkflowList({ + updateWorkflowList({ ...workflowList, content: workflowList?.content.map(item => item.id === row.id diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx index 81a9749e..35141a40 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useLayoutEffect } from 'react'; +import React, { useLayoutEffect } from 'react'; import { Button, IconButton, TextField } from '@mui/material'; import { isEqual } from 'lodash-es'; import { useDynamicList, useControllableValue } from 'ahooks'; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx index 1423fe67..72837fba 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input-select/index.tsx @@ -1,24 +1,18 @@ -import { useMemo, useLayoutEffect, useState, useRef, useCallback } from 'react'; +import { useLayoutEffect, useState, useRef, useCallback } from 'react'; import { useControllableValue, useSize } from 'ahooks'; -import { TextField, Menu, MenuItem, IconButton, Chip, ListSubheader } from '@mui/material'; +import { TextField, IconButton, Chip, Popover } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { SettingsOutlinedIcon } from '@milesight/shared/src/components'; import { Tooltip } from '@/components'; -import useWorkflow from '@/pages/workflow/views/editor/hooks/useWorkflow'; +import useWorkflow, { + type FlattenNodeParamType, +} from '@/pages/workflow/views/editor/hooks/useWorkflow'; import { isRefParamKey } from '@/pages/workflow/views/editor/helper'; +import UpstreamNodeList from '../upstream-node-list'; import './style.less'; type ParamInputSelectValueType = string | undefined; -type OptionItemType = { - nodeId: ApiKey; - nodeName?: string; - nodeType?: WorkflowNodeType; - valueName?: string; - valueType?: string; - valueKey?: string; -}; - export interface ParamInputSelectProps { label?: string; @@ -49,64 +43,14 @@ const ParamInputSelect: React.FC = ({ }) => { const { getIntlText } = useI18n(); const { getUpstreamNodeParams } = useWorkflow(); + const [, options] = getUpstreamNodeParams(); const containerRef = useRef(null); const [data, setData] = useControllableValue(props); const [inputValue, setInputValue] = useState(''); - const [selectValue, setSelectValue] = useState(); + const [selectValue, setSelectValue] = useState(); const [anchorEl, setAnchorEl] = useState(null); const { width: containerWidth } = useSize(containerRef) || {}; - const [options, renderedOptions] = useMemo(() => { - const [nodeParams] = getUpstreamNodeParams(); - const result = nodeParams?.reduce((acc, param) => { - param.outputs?.forEach(output => { - acc.push({ - nodeId: param.nodeId, - nodeName: param.nodeName, - nodeType: param.nodeType, - valueName: output.name, - valueType: output.type, - valueKey: output.key, - }); - }); - return acc; - }, [] as OptionItemType[]); - - // TODO: render Empty component when the options is empty - const renderedOptions = nodeParams?.reduce((acc, node) => { - acc.push( - - {node.nodeType} - , - ); - - node.outputs.forEach(output => { - acc.push( - { - setAnchorEl(null); - setData(output.key); - }} - > -
- - {output.type} -
-
, - ); - }); - return acc; - }, [] as React.ReactNode[]); - - return [result, renderedOptions]; - }, [selectValue, getUpstreamNodeParams, setData]); - const handleInputChange = useCallback>( e => { const { value } = e.target; @@ -114,6 +58,7 @@ const ParamInputSelect: React.FC = ({ // input value if (!isRefParamKey(value)) { setData(value); + setInputValue(value); return; } @@ -186,10 +131,11 @@ const ParamInputSelect: React.FC = ({ onDelete={() => setData(undefined)} /> )} - setAnchorEl(null)} anchorOrigin={{ vertical: 'bottom', horizontal: 'right', @@ -198,7 +144,6 @@ const ParamInputSelect: React.FC = ({ vertical: 'top', horizontal: 'right', }} - onClose={() => setAnchorEl(null)} sx={{ '& .MuiList-root': { width: containerWidth, @@ -207,8 +152,13 @@ const ParamInputSelect: React.FC = ({ }, }} > - {renderedOptions} - + { + setAnchorEl(null); + setData(node.valueKey); + }} + /> +
); }; diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx index 0d69de34..efbd2e3e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import cls from 'classnames'; -import { get } from 'lodash-es'; +import { pick, isEmpty } from 'lodash-es'; import { useRequest } from 'ahooks'; import { Backdrop, @@ -60,35 +60,40 @@ const TestDrawer: React.FC = ({ node, open, onClose }) => { const node = getNode(nodeId); const { parameters } = node?.data || {}; const result: Record = {}; - const inputArgs = get(parameters, nodeConfig.testInputKeys || []); - - if (!inputArgs) return result; - - // TODO: Generate different type data based on reference key type ? - switch (nodeConfig.type) { - case 'code': - case 'service': - case 'assigner': - case 'webhook': { - Object.entries(inputArgs).forEach(([key, value]) => { - if (!key) return; - result[key] = - value && !isRefParamKey(value as string) - ? value - : genRandomString(8, { lowerCase: true }); - }); - break; + const inputArgs = pick(parameters, nodeConfig.testInputKeys || []); + + if (isEmpty(inputArgs)) return result; + + // Use different traversal methods for different param + Object.entries(inputArgs).forEach(([param, data]) => { + // TODO: Generate different type data based on reference key type ? + switch (param) { + case 'entities': { + data.forEach((key: string) => { + result[key] = genRandomString(8, { lowerCase: true }); + }); + break; + } + case 'inputArguments': + case 'serviceInvocationSetting': + case 'exchangePayload': { + if (param === 'serviceInvocationSetting') { + data = data.serviceParams; + } + Object.entries(data).forEach(([key, value]) => { + if (!key) return; + result[key] = + value && !isRefParamKey(value as string) + ? value + : genRandomString(8, { lowerCase: true }); + }); + break; + } + default: { + break; + } } - case 'select': { - inputArgs.forEach((key: string) => { - result[key] = genRandomString(8, { lowerCase: true }); - }); - break; - } - default: { - break; - } - } + }); return result; }, [open, nodeId, nodeConfig, getNode]); diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx index 0af0debe..6e807713 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/upstream-node-list/index.tsx @@ -30,7 +30,7 @@ const UpstreamNodeList: React.FC = props => { return upstreamNodes?.reduce((acc, node) => { acc.push( - {node.nodeType} + {`${node.nodeLabel} (${node.nodeName || node.nodeId})`} , ); diff --git a/apps/web/src/pages/workflow/views/editor/constants.ts b/apps/web/src/pages/workflow/views/editor/constants.ts index 34caa761..a3931050 100644 --- a/apps/web/src/pages/workflow/views/editor/constants.ts +++ b/apps/web/src/pages/workflow/views/editor/constants.ts @@ -98,10 +98,10 @@ export const conditionOperatorMap: Partial< END_WITH: { labelIntlKey: 'workflow.label.condition_operator_end_with', }, - IS: { + EQ: { labelIntlKey: 'workflow.label.condition_operator_is', }, - IS_NOT: { + NE: { labelIntlKey: 'workflow.label.condition_operator_is_not', }, IS_EMPTY: { diff --git a/apps/web/src/pages/workflow/views/editor/helper.ts b/apps/web/src/pages/workflow/views/editor/helper.ts index 2759628c..f1f650ea 100644 --- a/apps/web/src/pages/workflow/views/editor/helper.ts +++ b/apps/web/src/pages/workflow/views/editor/helper.ts @@ -22,8 +22,8 @@ export const validatorsConfig: Record> = { /** * Generate Reference Param Key */ -export const genRefParamKey = (nodeType: WorkflowNodeType, nodeId: ApiKey, valueKey: ApiKey) => { - return `#{${[nodeType, nodeId, valueKey].join(PARAM_REFERENCE_DIVIDER)}}`; +export const genRefParamKey = (nodeId: ApiKey, valueKey: ApiKey) => { + return `#{properties.${nodeId}['${valueKey}']}`; }; /** @@ -38,8 +38,14 @@ export const isRefParamKey = (key?: string) => { */ export const parseRefParamKey = (key?: string) => { if (!key || !isRefParamKey(key)) return; - const matches = key.match(PARAM_REFERENCE_PATTERN); - return matches?.[1]; + const matches = key.match(/^#\{properties\.([^'[\]]+)\['([^']+)'\]\}$/); + + if (!matches) return; + const [, nodeId, valueKey] = matches; + return { + nodeId, + valueKey, + }; }; /** diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index e624b7af..5398164e 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -7,26 +7,28 @@ import { getOutgoers, type IsValidConnection, } from '@xyflow/react'; -import { uniqBy, omit, cloneDeep } from 'lodash-es'; +import { uniqBy, omit, cloneDeep, pick, isEmpty, isObject } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; +import useFlowStore from '../store'; import { PARALLEL_LIMIT, PARALLEL_DEPTH_LIMIT, NODE_MIN_NUMBER_LIMIT, ENTRY_NODE_NUMBER_LIMIT, } from '../constants'; -import { genRefParamKey } from '../helper'; +import { genRefParamKey, isRefParamKey } from '../helper'; import { getParallelInfo } from './utils'; export type NodeParamType = { nodeId: ApiKey; nodeName?: string; nodeType?: WorkflowNodeType; + nodeLabel?: string; outputs: { name: string; - type: string; + type?: string; key: string; }[]; }; @@ -36,7 +38,7 @@ export type FlattenNodeParamType = { nodeName?: string; nodeType?: WorkflowNodeType; valueName: string; - valueType: string; + valueType?: string; valueKey: string; }; @@ -49,6 +51,7 @@ const useWorkflow = () => { const nodes = useNodes(); const edges = useEdges(); const { getIntlText } = useI18n(); + const nodeConfigs = useFlowStore(state => state.nodeConfigs); const selectedNode = useMemo(() => { const selectedNodes = nodes.filter(item => item.selected); @@ -235,24 +238,74 @@ const useWorkflow = () => { if (!currentNode) return []; const incomeNodes = getUpstreamNodes(currentNode, nodes, edges); - // TODO: get the correct nodes params - const result: NodeParamType[] = incomeNodes.map(node => ({ - nodeId: node.id, - nodeName: node.data?.nodeName, - nodeType: node.type as WorkflowNodeType, - outputs: [ - { - name: 'output112123123123123123123123131231231', - type: 'string', - key: genRefParamKey(node.type as WorkflowNodeType, node.id, '1132e3123132'), - }, - { - name: 'output22', - type: 'number', - key: genRefParamKey(node.type as WorkflowNodeType, node.id, '11eyu3123132'), - }, - ], - })); + const result = incomeNodes + .map(({ id, type, data }) => { + const { nodeName, parameters } = data || {}; + const config = nodeConfigs[type!]; + const outputKeys = config?.outputKeys; + const paramData: NodeParamType = { + nodeId: id, + nodeName, + nodeType: type, + nodeLabel: config?.labelIntlKey ? getIntlText(config.labelIntlKey) : '', + outputs: [], + }; + + if (isEmpty(parameters) || !outputKeys?.length) return; + const outputArgs = pick(parameters, outputKeys); + + Object.entries(outputArgs).forEach(([param, data]) => { + switch (param) { + case 'entityConfigs': + case 'Payload': { + if (!Array.isArray(data)) return; + data.forEach((item: Record) => { + paramData.outputs.push({ + name: item?.name, + type: item?.type, + key: genRefParamKey(id, item.name), + }); + }); + break; + } + case 'entities': { + if (!Array.isArray(data)) return; + data.forEach(item => { + // TODO: get the entity value type + paramData.outputs.push({ + name: item, + // type: ??, + key: genRefParamKey(id, item), + }); + }); + break; + } + case 'inputArguments': + case 'exchangePayload': + case 'serviceInvocationSetting': { + if (param === 'serviceInvocationSetting') { + data = data.serviceParams; + } + if (!isObject(data)) return; + Object.entries(data).forEach(([key, value]) => { + if (isRefParamKey(value)) return; + // TODO: get the entity value type + paramData.outputs.push({ + name: key, + // type: ??, + key: genRefParamKey(id, key), + }); + }); + break; + } + default: { + break; + } + } + }); + return paramData; + }) + .filter(item => !!item); const flattenResult = result.reduce((acc, item) => { acc.push( ...item.outputs.map(output => ({ @@ -269,7 +322,7 @@ const useWorkflow = () => { return [result, flattenResult]; }, - [nodes, edges, getSelectedNode, getUpstreamNodes], + [nodes, edges, nodeConfigs, getSelectedNode, getUpstreamNodes, getIntlText], ); // Check if there is a node that is not connected to an entry node diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index a66c5077..aa41bb65 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -1,5 +1,5 @@ -import { memo, useState, useCallback } from 'react'; -import { useSearchParams, useNavigate } from 'react-router-dom'; +import { memo, useState, useCallback, useEffect } from 'react'; +import { useSearchParams, useNavigate, useLocation } from 'react-router-dom'; import { useRequest } from 'ahooks'; import { omitBy, merge, isEmpty, cloneDeep } from 'lodash-es'; import { @@ -195,6 +195,18 @@ const WorkflowEditor = () => { }, ); + // ---------- Handle Import Data ---------- + const { state } = useLocation(); + const importedData = state?.workflowSchema as WorkflowSchema | undefined; + + useEffect(() => { + if (wid || !importedData) return; + const { nodes, edges, viewport } = importedData; + + setNodes(nodes); + setEdges(edges); + }, [wid, importedData, setNodes, setEdges]); + // ---------- Design Mode Change ---------- const [designMode, setDesignMode] = useState('canvas'); const [editorFlowData, setEditorFlowData] = useState(); diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index eb7fb8fa..bfd4e44b 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -135,13 +135,24 @@ declare type ListenerNodeDataType = BaseNodeDataType<{ declare type WorkflowLogicOperator = 'AND' | 'OR'; +/** + * Filter Operator used in the Condition Expression + * @param CONTAINS contains + * @param NOT_CONTAINS not contains + * @param START_WITH start witch + * @param END_WITH end witch + * @param EQ equal + * @param NE not equal + * @param IS_EMPTY is empty + * @param IS_NOT_EMPTY is not empty + */ declare type WorkflowFilterOperator = | 'CONTAINS' | 'NOT_CONTAINS' | 'START_WITH' | 'END_WITH' - | 'IS' - | 'IS_NOT' + | 'EQ' + | 'NE' | 'IS_EMPTY' | 'IS_NOT_EMPTY'; From fd56cd90a4c52e68ca448f1c97d63813723436f2 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 10:27:37 +0800 Subject: [PATCH 118/172] feat: add workflow data validators and optimize the overall process --- .../config-panel/hooks/useNodeFormItems.tsx | 2 +- .../workflow/views/editor/components/index.ts | 2 +- .../editor/components/log-panel/index.tsx | 64 +++++- .../components/node-container/index.tsx | 2 +- .../editor/components/test-button/index.tsx | 7 +- .../views/editor/components/topbar/index.tsx | 6 +- .../workflow/views/editor/hooks/useRun.ts | 10 - .../views/editor/hooks/useValidate.ts | 208 ++++++++++++++++-- .../views/editor/hooks/useWorkflow.ts | 14 ++ .../src/pages/workflow/views/editor/index.tsx | 65 ++---- .../src/pages/workflow/views/editor/store.ts | 4 +- .../pages/workflow/views/editor/typings.ts | 5 + packages/locales/src/lang/en/workflow.json | 4 +- packages/shared/types/workflow.d.ts | 12 +- 14 files changed, 307 insertions(+), 98 deletions(-) delete mode 100644 apps/web/src/pages/workflow/views/editor/hooks/useRun.ts diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx index 571eeeaf..b420d40f 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/hooks/useNodeFormItems.tsx @@ -53,7 +53,7 @@ const useNodeFormItems = (node?: WorkflowNode) => { const formConfigs = useMemo(() => { if (!Object.keys(nodeConfigs).length) return {}; - console.log({ nodeConfigs }); + // console.log({ nodeConfigs }); const result: Partial> = {}; Object.entries(nodeConfigs).forEach(([nodeType, nodeConfig]) => { const { properties = {}, outputProperties = {} } = nodeConfig.schema || {}; diff --git a/apps/web/src/pages/workflow/views/editor/components/index.ts b/apps/web/src/pages/workflow/views/editor/components/index.ts index 72402d08..a821a9ef 100644 --- a/apps/web/src/pages/workflow/views/editor/components/index.ts +++ b/apps/web/src/pages/workflow/views/editor/components/index.ts @@ -1,4 +1,4 @@ -export { default as Topbar, type DesignMode, type TopbarProps } from './topbar'; +export { default as Topbar, type TopbarProps } from './topbar'; export { default as Controls, type ControlsProps } from './controls'; export { default as ConfigPanel } from './config-panel'; diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index 54eff4ab..ff24315e 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -1,8 +1,9 @@ -import { useState, useMemo, useEffect, useCallback } from 'react'; +import React, { useState, useMemo, useEffect, useCallback, useRef } from 'react'; import { Stack, IconButton, Button, CircularProgress } from '@mui/material'; import { Panel, useReactFlow } from '@xyflow/react'; import cls from 'classnames'; import { useRequest } from 'ahooks'; +import { merge, isEmpty } from 'lodash-es'; import { genRandomString } from '@milesight/shared/src/utils/tools'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon, toast } from '@milesight/shared/src/components'; @@ -10,15 +11,21 @@ import { CodeEditor } from '@/components'; import { ActionLog } from '@/pages/workflow/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import useWorkflow from '../../hooks/useWorkflow'; +import useValidate from '../../hooks/useValidate'; import useFlowStore from '../../store'; +import { type DesignMode } from '../../typings'; import './style.less'; +export interface LogPanelProps { + designMode?: DesignMode; +} + /** * Log Detail Panel */ -const LogPanel = () => { +const LogPanel: React.FC = ({ designMode }) => { const { getIntlText } = useI18n(); - const { getNodes, toObject } = useReactFlow(); + const { getNodes, getEdges, toObject } = useReactFlow(); const { openLogPanel, logPanelMode, @@ -28,6 +35,7 @@ const LogPanel = () => { setOpenLogPanel, setLogDetail, setLogDetailLoading, + setNodesDataValidResult, } = useFlowStore( useStoreShallow([ 'openLogPanel', @@ -38,9 +46,10 @@ const LogPanel = () => { 'setOpenLogPanel', 'setLogDetail', 'setLogDetailLoading', + 'setNodesDataValidResult', ]), ); - const { updateNodesStatus } = useWorkflow(); + const { updateNodesStatus, checkWorkflowValid } = useWorkflow(); const flowData = useMemo(() => { if (!openLogPanel) return; return toObject(); @@ -60,6 +69,7 @@ const LogPanel = () => { } }, [logPanelMode, getIntlText]); const isTestRunMode = logPanelMode === 'testRun'; + const isAdvanceMode = designMode === 'advanced'; const handleClose = useCallback(() => { setOpenLogPanel(false); @@ -70,6 +80,8 @@ const LogPanel = () => { }, [setLogDetail, setLogDetailLoading, setOpenLogPanel, updateNodesStatus]); // ---------- Run Test ---------- + const { checkNodesId, checkNodesType, checkNodesData, checkEdgesId, checkEdgesType } = + useValidate(); const [entryInput, setEntryInput] = useState(''); const hasInput = useMemo(() => { if (!openLogPanel || !flowData || !isTestRunMode) return false; @@ -134,6 +146,40 @@ const LogPanel = () => { const { run: runFlowTest } = useRequest( async (value?: string) => { if (!openLogPanel || !isTestRunMode) return; + + // workflow verification + const nodes = getNodes(); + const edges = getEdges(); + if (!checkWorkflowValid(nodes, edges)) return; + + const edgesCheckResult = merge( + checkEdgesId(edges, nodes, { validateFirst: true }), + checkEdgesType(edges, nodes, { validateFirst: true }), + ); + // console.log({ edgesCheckResult }); + if (!isEmpty(edgesCheckResult)) return; + + const nodesCheckResult = merge( + checkNodesId(nodes, { validateFirst: isAdvanceMode }), + checkNodesType(nodes, { validateFirst: isAdvanceMode }), + checkNodesData(nodes, { validateFirst: isAdvanceMode }), + ); + // console.log({ nodesCheckResult }); + if (!isEmpty(nodesCheckResult)) { + if (isAdvanceMode) return; + const statusData = Object.entries(nodesCheckResult).reduce( + (acc, [id, item]) => { + acc[id] = item.status; + return acc; + }, + {} as NonNullable[0]>, + ); + + setNodesDataValidResult(nodesCheckResult); + updateNodesStatus(statusData); + return; + } + setLogDetailLoading(true); let input: Record; @@ -179,14 +225,14 @@ const LogPanel = () => { return; } + setEntryInput(''); runFlowTest(); }, [openLogPanel, isTestRunMode, hasInput, genDemoData, runFlowTest]); - // Clear Data when panel mode change + // Clear Loading Status when panel mode change useEffect(() => { - if (!logPanelMode) return; - handleClose(); - }, [logPanelMode, handleClose]); + setLogDetailLoading(false); + }, [logPanelMode, setLogDetailLoading]); return ( { ); }; -export default LogPanel; +export default React.memo(LogPanel); diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index a234c83a..6d67c80c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -175,7 +175,7 @@ const NodeContainer: React.FC = ({ } } }, - [nodeId, updateNode, deleteElements], + [nodeId, nodeConfigs, updateNode, deleteElements], ); const menuItems = useMemo(() => { diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx index 2092974c..4e4441f6 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -9,6 +9,7 @@ import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/ser import { TabPanel } from '@/components'; import LogList, { type LogType, type LogListProps } from './log-list'; import useFlowStore from '../../store'; +import useWorkflow from '../../hooks/useWorkflow'; import './style.less'; export type TestButtonType = 'test' | 'history'; @@ -55,6 +56,7 @@ const TestButton: React.FC = ({ disabled }) => { 'setLogDetailLoading', ]), ); + const { updateNodesStatus } = useWorkflow(); // ---------- Fetch Run Log List ---------- const { loading, run: getRunLogList } = useRequest( @@ -83,6 +85,8 @@ const TestButton: React.FC = ({ disabled }) => { if (type === 'test') { setOpenLogPanel(true); setLogPanelMode('testRun'); + setLogDetail(undefined); + updateNodesStatus(null); return; } @@ -94,6 +98,7 @@ const TestButton: React.FC = ({ disabled }) => { async (type: LogType, record: Parameters>[0]) => { setAnchorEl(null); setOpenLogPanel(true); + updateNodesStatus(null); switch (type) { case 'test': { setLogPanelMode('testLog'); @@ -118,7 +123,7 @@ const TestButton: React.FC = ({ disabled }) => { setLogDetail(data?.trace_info); }, - [setLogDetail, setLogDetailLoading, setLogPanelMode, setOpenLogPanel], + [setLogDetail, setLogDetailLoading, setLogPanelMode, setOpenLogPanel, updateNodesStatus], ); // ---------- Tab ---------- diff --git a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx index e4110f83..c7de1aa8 100644 --- a/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/topbar/index.tsx @@ -7,13 +7,9 @@ import { ArrowBackIcon, EditIcon } from '@milesight/shared/src/components'; import { WorkflowAPISchema } from '@/services/http'; import { Tooltip } from '@/components'; import { EditModal, type EditModalProps } from '@/pages/workflow/components'; +import { type DesignMode } from '../../typings'; import './style.less'; -/** - * Workflow Design Mode - */ -export type DesignMode = 'canvas' | 'advanced'; - export interface TopbarProps { /* Is Data Loading */ loading?: boolean; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useRun.ts b/apps/web/src/pages/workflow/views/editor/hooks/useRun.ts deleted file mode 100644 index 6e63b500..00000000 --- a/apps/web/src/pages/workflow/views/editor/hooks/useRun.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { type Node } from '@xyflow/react'; - -/** - * Nodes Test Run - */ -const useRun = (nodes: Node | Node[]) => { - // TODO: 数据校验,实时渲染各节点状态,调用运行日志等,待实现 -}; - -export default useRun; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 28ecae2c..4693e49a 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -1,18 +1,18 @@ import { useMemo, useCallback } from 'react'; import { useReactFlow } from '@xyflow/react'; -import { uniqBy } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { isEmpty, isRangeValue, - isMinLength, isMaxLength, isURL, isMatches, + isEmail, } from '@milesight/shared/src/utils/validators'; import { EDGE_TYPE_ADDABLE } from '../constants'; import useFlowStore from '../store'; +import { isRefParamKey } from '../helper'; type NodeDataValidator = (value?: T, fieldName?: string) => string | boolean | undefined; @@ -39,6 +39,8 @@ enum ErrorIntlKey { rangeLength = 'workflow.valid.range_length', minLength = 'workflow.valid.min_length', maxLength = 'workflow.valid.max_length', + url = 'workflow.valid.invalid_url', + email = 'workflow.valid.invalid_email', } export const NODE_VALIDATE_TOAST_KEY = 'node-validate'; @@ -113,6 +115,20 @@ const useValidate = () => { value: NonNullable['inputArguments'], fieldName, ) { + if (value && Object.keys(value).length) { + const maxLength = 50; + const hasOverLength = Object.keys(value).some(key => { + if (key && !isMaxLength(key, maxLength)) return true; + return false; + }); + + if (!hasOverLength) return true; + return getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + } + return true; }, }, @@ -173,33 +189,178 @@ const useValidate = () => { value: NonNullable['choice'], fieldName, ) { + const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); + const { when } = value || {}; + + if (!when.length) return message; + const hasEmptyCondition = when.some(({ expressionType, conditions }) => { + switch (expressionType) { + case 'mvel': { + const { expressionValue, expressionDescription } = conditions[0]; + return !expressionValue || !expressionDescription; + } + case 'condition': { + const hasEmpty = conditions.some(({ expressionValue }) => { + if (typeof expressionValue === 'string') return true; + const { key, operator, value } = expressionValue || {}; + + return !key || !operator || !value; + }); + + return hasEmpty; + } + default: { + return true; + } + } + }); + + if (hasEmptyCondition) return message; return true; }, }, 'code.expression': { - // checkRequired() {}, + checkMaxLength(value: string, fieldName) { + const maxLength = 2000; + if (value && value.length > maxLength) { + return getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + } + return true; + }, }, 'code.Payload': { checkMaxLength( value: NonNullable['Payload'], fieldName, ) { + if (value.length) { + const maxLength = 50; + const hasOverLength = value.some(item => { + if (item.name && !isMaxLength(`${item.name}`, maxLength)) return true; + return false; + }); + + if (!hasOverLength) return true; + return getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + } + + return true; + }, + }, + 'service.serviceInvocationSetting': { + checkRequired( + value: NonNullable< + ServiceNodeDataType['parameters'] + >['serviceInvocationSetting'], + fieldName, + ) { + if (value?.serviceEntity) return true; + return getIntlText(ErrorIntlKey.required, { 1: fieldName }); + }, + checkMaxLength( + value: NonNullable< + ServiceNodeDataType['parameters'] + >['serviceInvocationSetting'], + fieldName, + ) { + if (value?.serviceParams && Object.keys(value.serviceParams).length) { + const maxLength = 1000; + const hasOverLength = Object.values(value.serviceParams).some(val => { + if (val && !isRefParamKey(val) && !isMaxLength(val, maxLength)) { + return true; + } + return false; + }); + + if (!hasOverLength) return true; + return getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + } + return true; + }, + }, + 'assigner.exchangePayload': { + checkRequired( + value: NonNullable['exchangePayload'], + fieldName, + ) { + if ( + !value || + !Object.keys(value).filter(Boolean).length || + !Object.values(value).filter(Boolean).length + ) { + return getIntlText(ErrorIntlKey.required, { 1: fieldName }); + } + + return true; + }, + checkMaxLength( + value: NonNullable['exchangePayload'], + fieldName, + ) { + if (value && Object.values(value).filter(Boolean).length) { + const maxLength = 1000; + const hasOverLength = Object.values(value).some(val => { + if (val && !isRefParamKey(val) && !isMaxLength(val, maxLength)) { + return true; + } + return false; + }); + + if (!hasOverLength) return true; + return getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + } return true; }, }, - 'service.serviceInvocationSetting': {}, - 'assigner.exchangePayload': {}, 'email.emailConfig': { checkRequired( value: NonNullable['emailConfig'], fieldName, ) { + const { provider, smtpConfig } = value || {}; + if ( + !provider || + !smtpConfig || + !Object.values(smtpConfig).filter(Boolean).length + ) { + return getIntlText(ErrorIntlKey.required, { 1: fieldName }); + } return true; }, checkMaxLength( value: NonNullable['emailConfig'], fieldName, ) { + const { smtpConfig } = value || {}; + + if (smtpConfig && Object.values(smtpConfig).filter(Boolean).length) { + const maxLength = 50; + const hasOverLength = Object.entries(smtpConfig).some(([key, val]) => { + if (key !== 'encryption' && val && !isMaxLength(`${val}`, maxLength)) { + return true; + } + return false; + }); + + if (!hasOverLength) return true; + return getIntlText(ErrorIntlKey.maxLength, { + 1: fieldName, + 2: maxLength, + }); + } + return true; }, }, @@ -208,15 +369,39 @@ const useValidate = () => { checkMaxLength: genMaxLengthValidator(500), }, 'email.recipients': { - checkRequired, + checkRequired( + value: NonNullable['recipients'], + fieldName, + ) { + if (!value || !value.filter(Boolean).length) { + return getIntlText(ErrorIntlKey.required, { 1: fieldName }); + } + return true; + }, + checkEmail( + value: NonNullable['recipients'], + fieldName, + ) { + if (value && value.filter(Boolean).length) { + const hasInvalidEmail = value.some(val => !isEmail(val)); + + if (!hasInvalidEmail) return true; + return getIntlText(ErrorIntlKey.email, { 1: fieldName }); + } + return true; + }, }, 'email.content': { checkRequired, - checkMaxLength: genMaxLengthValidator(5000), + checkMaxLength: genMaxLengthValidator(10000), }, 'webhook.webhookUrl': { checkRequired, checkUrl(value: string, fieldName) { + if (value && !isURL(value)) { + return getIntlText(ErrorIntlKey.url, { 1: fieldName }); + } + return true; }, }, @@ -463,15 +648,6 @@ const useValidate = () => { const { nodeName, nodeRemark, parameters } = data || {}; let tempResult = result[id]; - // if ( - // options?.validateFirst && - // Object.values(result).some(item => item.errMsgs.length) - // ) { - // const errItem = Object.values(result).find(item => item.errMsgs.length); - // toast.error({ key: 'node-validate', content: errItem?.errMsgs[0] }); - // return result; - // } - if (!tempResult) { tempResult = { type: nodeType, diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 5398164e..6737d26a 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -355,6 +355,19 @@ const useWorkflow = () => { [getNodes, getEdges, getUpstreamNodes, getIntlText], ); + // Check if the workflow nodes&edges is valid + const checkWorkflowValid = useCallback( + (nodes: WorkflowNode[], edges: WorkflowEdge[]) => { + if (!checkNodeNumberLimit(nodes)) return false; + if (checkFreeNodeLimit(nodes, edges)) return false; + if (!checkNestedParallelLimit(nodes, edges)) return false; + if (nodes.some(node => !checkParallelLimit(node.id, undefined, edges))) return false; + + return true; + }, + [checkNodeNumberLimit, checkFreeNodeLimit, checkNestedParallelLimit, checkParallelLimit], + ); + // Update node status const updateNodesStatus = useCallback( (data: Record | null) => { @@ -391,6 +404,7 @@ const useWorkflow = () => { checkNestedParallelLimit, checkNodeNumberLimit, checkFreeNodeLimit, + checkWorkflowValid, getSelectedNode, getUpstreamNodes, getUpstreamNodeParams, diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index aa41bb65..b3935e58 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -15,19 +15,12 @@ import { } from '@xyflow/react'; import { checkPrivateProperty } from '@milesight/shared/src/utils/tools'; import { useI18n, useStoreShallow, usePreventLeave } from '@milesight/shared/src/hooks'; -import { CheckIcon, InfoIcon, LoadingButton, toast } from '@milesight/shared/src/components'; +import { InfoIcon, LoadingButton, toast } from '@milesight/shared/src/components'; import { CodeEditor, useConfirm } from '@/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import { MIN_ZOOM, MAX_ZOOM, FROZEN_NODE_PROPERTY_KEYS } from './constants'; import useFlowStore from './store'; -import { - useNodeTypes, - useInteractions, - useWorkflow, - useValidate, - NODE_VALIDATE_TOAST_KEY, - EDGE_VALIDATE_TOAST_KEY, -} from './hooks'; +import { useNodeTypes, useInteractions, useWorkflow, useValidate } from './hooks'; import { Topbar, Controls, @@ -38,9 +31,9 @@ import { EntryPanel, LogPanel, TestButton, - type DesignMode, type TopbarProps, } from './components'; +import { type DesignMode } from './typings'; import '@xyflow/react/dist/style.css'; import './style.less'; @@ -56,31 +49,13 @@ const WorkflowEditor = () => { const { getIntlText } = useI18n(); const nodeTypes = useNodeTypes(); const { toObject } = useReactFlow(); - const { - isValidConnection, - checkParallelLimit, - checkNestedParallelLimit, - checkNodeNumberLimit, - checkFreeNodeLimit, - updateNodesStatus, - } = useWorkflow(); + const { isValidConnection, checkWorkflowValid, updateNodesStatus } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); const [nodes, setNodes, onNodesChange] = useNodesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { checkNodesId, checkNodesType, checkNodesData, checkEdgesId, checkEdgesType } = useValidate(); - const checkWorkflowValid = useCallback( - (nodes: WorkflowNode[], edges: WorkflowEdge[]) => { - if (!checkNodeNumberLimit(nodes)) return false; - if (checkFreeNodeLimit(nodes, edges)) return false; - if (!checkNestedParallelLimit(nodes, edges)) return false; - if (nodes.some(node => !checkParallelLimit(node.id, undefined, edges))) return false; - - return true; - }, - [checkNodeNumberLimit, checkFreeNodeLimit, checkNestedParallelLimit, checkParallelLimit], - ); const confirm = useConfirm(); // ---------- Prevent Leave ---------- @@ -174,7 +149,7 @@ const WorkflowEditor = () => { const { design_data: designData, ...basicData } = data || {}; let flowData: Pick; - console.log(data); + // console.log(data); try { flowData = JSON.parse(designData || '{}'); } catch (e) { @@ -297,27 +272,22 @@ const WorkflowEditor = () => { const { nodes, edges, viewport } = flowData; - console.log({ nodes, edges }); + // console.log({ nodes, edges }); if (!checkWorkflowValid(nodes, edges)) return; const edgesCheckResult = merge( - checkEdgesId(edges, nodes, { validateFirst: isAdvanceMode }), - checkEdgesType(edges, nodes, { validateFirst: isAdvanceMode }), + checkEdgesId(edges, nodes, { validateFirst: true }), + checkEdgesType(edges, nodes, { validateFirst: true }), ); - console.log({ edgesCheckResult }); - if (!isEmpty(edgesCheckResult)) { - if (isAdvanceMode) return; - const errItem = Object.values(edgesCheckResult).find(item => item.errMsgs.length); - toast.error({ key: EDGE_VALIDATE_TOAST_KEY, content: errItem?.errMsgs[0] }); - return; - } + // console.log({ edgesCheckResult }); + if (!isEmpty(edgesCheckResult)) return; const nodesCheckResult = merge( checkNodesId(nodes, { validateFirst: isAdvanceMode }), checkNodesType(nodes, { validateFirst: isAdvanceMode }), checkNodesData(nodes, { validateFirst: isAdvanceMode }), ); - console.log({ nodesCheckResult }); + // console.log({ nodesCheckResult }); if (!isEmpty(nodesCheckResult)) { if (isAdvanceMode) return; const statusData = Object.entries(nodesCheckResult).reduce( @@ -355,11 +325,16 @@ const WorkflowEditor = () => { if (!proceed) return; } - // remove private property nodes.forEach(node => { + // remove interactive property + delete node.selected; + delete node.dragging; + + // remove private property node.data = omitBy(node.data, (_, key) => checkPrivateProperty(key)); }); edges.forEach(edge => { + delete edge.selected; edge.data = omitBy(edge.data, (_, key) => checkPrivateProperty(key)); }); @@ -374,12 +349,12 @@ const WorkflowEditor = () => { }), ); - console.log({ error, resp }); + // console.log({ error, resp }); setSaveLoading(false); if (error || !isRequestSuccess(resp)) return; const data = getResponseData(resp); - console.log(data); + // console.log(data); toast.success(getIntlText('common.message.operation_success')); setIsPreventLeave(false); setTimeout(() => navigate('/workflow'), 0); @@ -446,7 +421,7 @@ const WorkflowEditor = () => { horizontal={helperLineHorizontal} vertical={helperLineVertical} /> - + diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index 34b7a2b1..1a08a784 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -111,7 +111,7 @@ const useFlowStore = create( set({ openLogPanel: false, logPanelMode: undefined, logDetail: undefined }); return; } - console.log(data); + // console.log(data); const logDetail = Object.entries(data).map(([id, { type, name, label, errMsgs }]) => { const result: NonNullable[0] = { node_id: id, @@ -122,7 +122,7 @@ const useFlowStore = create( return result; }); - console.log(logDetail); + // console.log(logDetail); set({ openLogPanel: true, logPanelMode: 'feVerify', logDetail }); }, })), diff --git a/apps/web/src/pages/workflow/views/editor/typings.ts b/apps/web/src/pages/workflow/views/editor/typings.ts index d3072738..237b9b7d 100644 --- a/apps/web/src/pages/workflow/views/editor/typings.ts +++ b/apps/web/src/pages/workflow/views/editor/typings.ts @@ -1,5 +1,10 @@ import { type NodeConfigItemType } from '../../config'; +/** + * Workflow Design Mode + */ +export type DesignMode = 'canvas' | 'advanced'; + export type NodeFormItemValueType = | 'string' | 'int' diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 5e041c27..7790a2fe 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -109,5 +109,7 @@ "workflow.valid.edge_id_duplicated": "The Edge (connecting node {1} and {2}) ID is duplicated.", "workflow.node.service_helptext": "Please select the service you want to call first.", "workflow.node.input_variables": "Input Variables", - "workflow.editor.config_panel_test_title": "Test {1}" + "workflow.editor.config_panel_test_title": "Test {1}", + "workflow.valid.invalid_url": "The {1} field value is not a valid URL.", + "workflow.valid.invalid_email": "The {1} field value is not a valid email." } diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index bfd4e44b..62552187 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -223,12 +223,12 @@ declare type CodeNodeDataType = BaseNodeDataType<{ * Service Node Parameters */ declare type ServiceNodeDataType = BaseNodeDataType<{ - /** Service Entity Key */ - serviceEntity: ApiKey; - /** - * Input variables of service entity - */ - serviceParams: Record; + serviceInvocationSetting: { + /** Service Entity Key */ + serviceEntity: string; + /** Input variables of service entity */ + serviceParams?: Record; + }; }>; /** From cef2b06358ef67e5e4ec627cb456051bf40c2de8 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 10:31:40 +0800 Subject: [PATCH 119/172] feat: optimize the overall process --- .../editor/components/log-panel/index.tsx | 14 ++++-- .../editor/components/log-panel/style.less | 15 +++++++ .../editor/components/node-menu/index.tsx | 43 +++++++++++-------- .../editor/components/test-button/index.tsx | 6 +-- .../components/test-button/log-list.tsx | 23 +++------- .../views/editor/hooks/useValidate.ts | 18 +++++++- .../src/pages/workflow/views/editor/index.tsx | 22 ++++++++-- 7 files changed, 94 insertions(+), 47 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index ff24315e..405428f6 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -7,7 +7,7 @@ import { merge, isEmpty } from 'lodash-es'; import { genRandomString } from '@milesight/shared/src/utils/tools'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon, toast } from '@milesight/shared/src/components'; -import { CodeEditor } from '@/components'; +import { CodeEditor, Empty } from '@/components'; import { ActionLog } from '@/pages/workflow/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import useWorkflow from '../../hooks/useWorkflow'; @@ -32,6 +32,7 @@ const LogPanel: React.FC = ({ designMode }) => { logDetail, logDetailLoading, addTestLog, + setTestLogs, setOpenLogPanel, setLogDetail, setLogDetailLoading, @@ -43,6 +44,7 @@ const LogPanel: React.FC = ({ designMode }) => { 'logDetail', 'logDetailLoading', 'addTestLog', + 'setTestLogs', 'setOpenLogPanel', 'setLogDetail', 'setLogDetailLoading', @@ -253,7 +255,7 @@ const LogPanel: React.FC = ({ designMode }) => { -
+
{hasInput && (
= ({ designMode }) => {
)} - {!!logDetail?.length && flowData && ( + {!!logDetail?.length && flowData ? (
+ ) : ( + !hasInput && ( +
+ +
+ ) )}
{logDetailLoading && } diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less index 2b43ba3e..ab85aeeb 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/style.less @@ -29,6 +29,14 @@ border-radius: @border-radius-sm; box-shadow: @shadow-2; + &-body { + display: flex; + flex: 1; + flex-direction: column; + padding: 0 @padding-xl @padding-lg; + overflow: auto; + } + .input-area { .@{prefix}-code-editor { height: 200px; @@ -42,6 +50,13 @@ } } + .empty-area { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + } + .@{mui-prefix}CircularProgress-root { position: absolute; top: 50%; diff --git a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx index a60bc32c..99f61a5d 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-menu/index.tsx @@ -1,6 +1,7 @@ import { useState, useMemo, useLayoutEffect } from 'react'; import { useReactFlow } from '@xyflow/react'; import { Menu, MenuItem, type MenuProps } from '@mui/material'; +import { useDebounceFn } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; import { nodeCategoryConfigs, @@ -73,24 +74,24 @@ const NodeMenu = ({ // ---------- Menu Item Click ---------- const { screenToFlowPosition } = useReactFlow(); const { addNode } = useInteractions(); - const handleClick = ( - e: React.MouseEvent, - type: WorkflowNodeType, - ) => { - e.stopPropagation(); + const { run: handleClick } = useDebounceFn( + (e: React.MouseEvent, type: WorkflowNodeType) => { + let position: { x: number; y: number } | undefined; + if (!prevNodeId && !nextNodeId) { + position = screenToFlowPosition({ x: e.clientX - 20, y: e.clientY - 20 }); + } - let position: { x: number; y: number } | undefined; - if (!prevNodeId && !nextNodeId) { - position = screenToFlowPosition({ x: e.clientX - 20, y: e.clientY - 20 }); - } - - addNode( - { nodeType: type, position }, - { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle }, - ); - onItemClick?.(type); - handleInnerClose({}, 'backdropClick'); - }; + addNode( + { nodeType: type, position }, + { prevNodeId, prevNodeSourceHandle, nextNodeId, nextNodeTargetHandle }, + ); + onItemClick?.(type); + handleInnerClose({}, 'backdropClick'); + }, + { + wait: 300, + }, + ); return ( ( - handleClick(e, menu.type)}> + { + e.stopPropagation(); + handleClick(e, menu.type); + }} + > {menu.icon} diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx index 4e4441f6..186abcb1 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -135,8 +135,7 @@ const TestButton: React.FC = ({ disabled }) => { label: getIntlText('workflow.editor.logs_popover_title_test'), component: ( handleLogItemClick('test', record)} /> ), @@ -149,8 +148,7 @@ const TestButton: React.FC = ({ disabled }) => { label: getIntlText('workflow.editor.logs_popover_title_run'), component: ( handleLogItemClick('run', record)} /> diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx index ddb07001..8a4c1739 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx @@ -1,5 +1,5 @@ -import { useMemo, useRef } from 'react'; -import { useI18n, useTime, useVirtualList } from '@milesight/shared/src/hooks'; +import { useRef } from 'react'; +import { useTime, useVirtualList } from '@milesight/shared/src/hooks'; import { ErrorIcon, CheckCircleIcon } from '@milesight/shared/src/components'; import { type WorkflowAPISchema } from '@/services/http'; import { Empty } from '@/components'; @@ -8,28 +8,17 @@ import './style.less'; export type LogType = 'test' | 'run'; export interface LogListProps { - type: LogType; + // type: LogType; - data?: WorkflowAPISchema['getLogList']['response']['content']; + data: WorkflowAPISchema['getLogList']['response']['content']; loading?: boolean; onSelect?: (record: NonNullable[number]) => void; } -const LogList: React.FC = ({ type, data = [], loading, onSelect }) => { - const { getIntlText } = useI18n(); +const LogList: React.FC = ({ data, loading, onSelect }) => { const { getTimeFormat } = useTime(); - // const titlePrefix = useMemo(() => { - // switch (type) { - // case 'test': - // return getIntlText('workflow.editor.log_title_test'); - // case 'run': - // return getIntlText('workflow.editor.log_title_run'); - // default: - // return ''; - // } - // }, [type, getIntlText]); // ---------- Virtual List ---------- const containerRef = useRef(null); @@ -41,8 +30,6 @@ const LogList: React.FC = ({ type, data = [], loading, onSelect }) overscan: 10, }); - // TODO: Infinite Scroll Loading ? - return (
diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 4693e49a..496af42a 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -74,6 +74,8 @@ const useValidate = () => { return true; }; }; + + // Note: The `checkRequired` name is fixed and cannot be modified const result: Record> = { nodeName: { checkRequired, @@ -192,7 +194,7 @@ const useValidate = () => { const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); const { when } = value || {}; - if (!when.length) return message; + if (!when?.length) return message; const hasEmptyCondition = when.some(({ expressionType, conditions }) => { switch (expressionType) { case 'mvel': { @@ -220,6 +222,7 @@ const useValidate = () => { }, }, 'code.expression': { + checkRequired, checkMaxLength(value: string, fieldName) { const maxLength = 2000; if (value && value.length > maxLength) { @@ -684,6 +687,19 @@ const useValidate = () => { }), ); } else { + const nodeCheckers = Object.keys(dataValidators).filter(key => + key.startsWith(`${type}.`), + ); + nodeCheckers?.forEach(checker => { + const key = checker.replace(`${type}.`, ''); + const checkRequired = dataValidators[key]?.checkRequired; + + if (parameters[key] || !checkRequired) return; + const result = checkRequired(parameters[key], key); + if (result && result !== true) { + tempResult.errMsgs.push(result); + } + }); // Node parameters data check Object.entries(parameters).forEach(([key, value]) => { const validKey = `${type}.${key}`; diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index b3935e58..85ff235c 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -110,9 +110,16 @@ const WorkflowEditor = () => { ); // ---------- Fetch Nodes Config ---------- - const { setOpenLogPanel, setNodeConfigs, setNodesDataValidResult } = useFlowStore( - useStoreShallow(['setOpenLogPanel', 'setNodeConfigs', 'setNodesDataValidResult']), - ); + const { setOpenLogPanel, setNodeConfigs, setTestLogs, setLogDetail, setNodesDataValidResult } = + useFlowStore( + useStoreShallow([ + 'setOpenLogPanel', + 'setNodeConfigs', + 'setTestLogs', + 'setLogDetail', + 'setNodesDataValidResult', + ]), + ); const { loading: nodeConfigLoading } = useRequest( async () => { const [error, resp] = await awaitWrap(workflowAPI.getFlowNodes()); @@ -360,6 +367,15 @@ const WorkflowEditor = () => { setTimeout(() => navigate('/workflow'), 0); }; + useEffect(() => { + return () => { + setOpenLogPanel(false); + setIsPreventLeave(false); + setTestLogs(undefined); + setLogDetail(undefined); + }; + }, []); + return (
Date: Mon, 30 Dec 2024 10:43:47 +0800 Subject: [PATCH 120/172] feat: The entity select component increases the maxCount props and dynamic menu height --- .../components/entity-menu-popper/index.tsx | 38 +++++++++++++++---- .../components/entity-menu-popper/style.less | 9 ++++- .../components/entity-option/index.tsx | 8 +++- .../components/entity-option/style.less | 5 +++ .../src/components/entity-select/constant.ts | 3 ++ .../entity-select/hooks/useContextValue.tsx | 7 ++-- .../src/components/entity-select/index.tsx | 8 +++- .../src/components/entity-select/types.d.ts | 12 ++++++ 8 files changed, 74 insertions(+), 16 deletions(-) diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx index a3ee195d..ed1d5c5e 100644 --- a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx @@ -1,5 +1,6 @@ -import React, { useContext } from 'react'; +import React, { useContext, useMemo, useRef } from 'react'; import { Paper, Popper, PopperProps } from '@mui/material'; +import { useVirtualList } from '@milesight/shared/src/hooks'; import EntityOption from '../entity-option'; import { EntityContext } from '../../context'; import type { EntitySelectOption } from '../../types'; @@ -9,17 +10,40 @@ export interface IProps extends PopperProps { menuList: EntitySelectOption[]; } export default React.memo(({ menuList, ...props }: IProps) => { - const { selectedEntityMap } = useContext(EntityContext); + const { selectedEntityMap, maxCount } = useContext(EntityContext); + const containerRef = useRef(null); + const listRef = useRef(null); + // /** virtual list */ + // const [virtualList] = useVirtualList(menuList, { + // containerTarget: containerRef, + // wrapperTarget: listRef, + // itemHeight: 58, + // overscan: 10, + // }); + const selectedCount = useMemo(() => selectedEntityMap.size, [selectedEntityMap]); return ( - {(menuList || []).map(menu => { - const { value } = menu || {}; - const selected = selectedEntityMap.has(value); +
+
+ {(menuList || []).map(menu => { + const { value } = menu || {}; + const selected = selectedEntityMap.has(value); + const disabled = + maxCount && selectedCount >= maxCount ? !selected : false; - return ; - })} + return ( + + ); + })} +
+
); diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/style.less b/apps/web/src/components/entity-select/components/entity-menu-popper/style.less index bbb164a6..4fcf24cf 100644 --- a/apps/web/src/components/entity-select/components/entity-menu-popper/style.less +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/style.less @@ -1,7 +1,12 @@ .ms-entity-menu-popper { .ms-entity-menu-paper { width: 360px; - max-height: 320px; - overflow: auto; + overflow: hidden; + + &__list { + position: relative; + max-height: 40vh; + overflow: auto; + } } } diff --git a/apps/web/src/components/entity-select/components/entity-option/index.tsx b/apps/web/src/components/entity-select/components/entity-option/index.tsx index 132767d9..39fc308e 100644 --- a/apps/web/src/components/entity-select/components/entity-option/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-option/index.tsx @@ -9,16 +9,19 @@ import './style.less'; interface IProps { option: EntitySelectOption; selected?: boolean; + disabled?: boolean; } export default React.memo((props: IProps) => { const { onEntityChange } = useContext(EntityContext); - const { option, selected } = props; + const { option, selected, disabled } = props; const { label, description } = option || {}; /** When an entity item is selected/canceled */ const handleClick = useCallback(() => { + if (disabled) return; + onEntityChange(option); - }, [option, onEntityChange]); + }, [disabled, onEntityChange, option]); /** when mouse down, prevent default behavior */ const handleMouseDown = useCallback((event: React.MouseEvent) => { @@ -28,6 +31,7 @@ export default React.memo((props: IProps) => {
( - props: Pick, 'value' | 'multiple' | 'onChange'>, + props: Pick, 'value' | 'multiple' | 'onChange' | 'maxCount'>, ) => { - const { value, multiple, onChange } = props; + const { value, multiple, maxCount, onChange } = props; const [tabType, setTabType] = useState('entity'); const { options } = useOptions({ tabType }); @@ -28,6 +28,7 @@ export const useContextValue = < /** context value */ const contextValue = useMemo(() => { const result: EntitySelectContext = { + maxCount, options: (options as V[]) || [], tabType, setTabType, @@ -36,7 +37,7 @@ export const useContextValue = < }; return result; - }, [onEntityChange, options, selectedEntityMap, tabType]); + }, [maxCount, onEntityChange, options, selectedEntityMap, tabType]); return { contextValue, diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index 6ab54818..02ed68d3 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -3,6 +3,7 @@ import { useControllableValue } from 'ahooks'; import EntitySelect from './entitySelect'; import { useContextValue } from './hooks'; import { EntityContext } from './context'; +import { DEFAULT_MAX_COUNT } from './constant'; import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; const EntitySelectApp = < @@ -15,12 +16,15 @@ const EntitySelectApp = < DisableClearable >, >( - props: Props, + props: EntitySelectProps, ) => { - const { multiple } = props; + const { multiple, maxCount: _maxCount } = props; + const maxCount = multiple ? (_maxCount! ?? DEFAULT_MAX_COUNT) : void 0; + const [value, onChange] = useControllableValue['value']>(props); const { contextValue } = useContextValue({ value, + maxCount, multiple, onChange, }); diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index 4cb5aa94..4186323c 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -57,12 +57,24 @@ export interface EntitySelectProps< onChange?: (value: EntitySelectValue) => void; /** Whether the clear button is disabled */ disableClearable?: DisableClearable; + /** + * maximum number of items that can be selected + * @default 5 + * @description This prop is only used when `multiple` is true + */ + maxCount?: Multiple extends true ? number : never; } /** * Context for the EntitySelect component. */ export interface EntitySelectContext { + /** + * maximum number of items that can be selected + * @default 5 + * @description This prop is only used when `multiple` is true + */ + maxCount?: number; /** Available options for selection */ options: V[]; /** The current tab type */ From 0d91b3a6101eb09fc62173e37c218727d4811def Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 10:51:13 +0800 Subject: [PATCH 121/172] fix: entity api service params error fixed --- apps/web/src/plugin/hooks/useEntitySelectOptions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/plugin/hooks/useEntitySelectOptions.ts b/apps/web/src/plugin/hooks/useEntitySelectOptions.ts index 8bd6c10a..9e63376a 100644 --- a/apps/web/src/plugin/hooks/useEntitySelectOptions.ts +++ b/apps/web/src/plugin/hooks/useEntitySelectOptions.ts @@ -48,7 +48,7 @@ export function useEntitySelectOptions( const [error, resp] = await awaitWrap( entityAPI.getList({ keyword, - entity_type: entityType, + entity_type: entityType ? [entityType] : undefined, entity_value_type: entityValueTypes, entity_access_mod: entityAccessMods, exclude_children: entityExcludeChildren, From 70d08094d043e47addb65590877b02dbefec6ea0 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 11:22:03 +0800 Subject: [PATCH 122/172] fix: icon and route jump adjustment --- apps/web/src/pages/workflow/hooks/useColumns.tsx | 9 +++++---- apps/web/src/pages/workflow/index.tsx | 8 ++++---- packages/locales/src/lang/en/global.json | 3 ++- packages/shared/src/components/icons/index.tsx | 1 + 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index 192d6cc8..4bc0f221 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -7,11 +7,12 @@ import { EditIcon, MoreVertIcon, IosShareIcon, + EventNoteIcon, } from '@milesight/shared/src/components'; import { Tooltip, type ColumnType } from '@/components'; import { workflowAPI, type WorkflowAPISchema } from '@/services/http'; -type OperationType = 'detail' | 'delete' | 'edit' | 'enable' | 'export'; +type OperationType = 'log' | 'delete' | 'edit' | 'enable' | 'export'; export type TableRowDataType = ObjectToCamelCase< WorkflowAPISchema['getList']['response']['content'][0] @@ -123,12 +124,12 @@ const useColumns = ({ onButtonClick }: UseColumnsPro - + onButtonClick('detail', row)} + onClick={() => onButtonClick('log', row)} > - + { navigate(`/workflow/editor?wid=${record.id}`); break; } - case 'detail': { - // TODO: workflow id 传参 - navigate('/workflow/editor'); + case 'log': { + // TODO: Popup Log Modal + console.log('popup log modal', record); break; } case 'delete': { @@ -262,7 +262,7 @@ const Workflow = () => { onPaginationModelChange={setPaginationModel} onRowSelectionModelChange={setSelectedIds} onRowDoubleClick={({ row }) => { - navigate(`/device/detail/${row.id}`, { state: row }); + navigate(`/workflow/editor?wid=${row.id}`); }} onSearch={setKeyword} onRefreshButtonClick={getWorkflowList} diff --git a/packages/locales/src/lang/en/global.json b/packages/locales/src/lang/en/global.json index 75a06f5f..867517d5 100644 --- a/packages/locales/src/lang/en/global.json +++ b/packages/locales/src/lang/en/global.json @@ -172,5 +172,6 @@ "common.label.export": "Export", "common.label.creator": "Creator", "common.label.error": "Error", - "common.label.success": "Success" + "common.label.success": "Success", + "common.label.log": "Log" } diff --git a/packages/shared/src/components/icons/index.tsx b/packages/shared/src/components/icons/index.tsx index 81e58849..0ab2fdb2 100644 --- a/packages/shared/src/components/icons/index.tsx +++ b/packages/shared/src/components/icons/index.tsx @@ -99,6 +99,7 @@ export { Help as HelpIcon, MoreVert as MoreVertIcon, IosShare as IosShareIcon, + EventNote as EventNoteIcon, } from '@mui/icons-material'; export * from './iot-icons'; From da1492f1b6ccee4d5d22483613499d948f3b8478 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 30 Dec 2024 11:29:13 +0800 Subject: [PATCH 123/172] feat: Added the log modal entry --- apps/web/src/pages/workflow/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 16160d1b..03ff8ae1 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -186,8 +186,8 @@ const Workflow = () => { break; } case 'log': { - // TODO: Popup Log Modal - console.log('popup log modal', record); + setWorkflowItem(record); + setLogModalVisible(true); break; } case 'delete': { From 0467ad59dfd9e9616d4d58a47834a1a5d99c60cf Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 11:53:23 +0800 Subject: [PATCH 124/172] feat: optimize ui and enhance code robust --- .../components/test-drawer/index.tsx | 31 +++++++++++++++---- .../src/pages/workflow/views/editor/index.tsx | 4 +-- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx index efbd2e3e..4f9022bd 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx @@ -14,7 +14,13 @@ import { import { useReactFlow } from '@xyflow/react'; import { useI18n } from '@milesight/shared/src/hooks'; import { genRandomString } from '@milesight/shared/src/utils/tools'; -import { CloseIcon, PlayArrowIcon, CheckCircleIcon, toast } from '@milesight/shared/src/components'; +import { + CloseIcon, + PlayArrowIcon, + CheckCircleIcon, + ErrorIcon, + toast, +} from '@milesight/shared/src/components'; import { CodeEditor, Tooltip } from '@/components'; import { workflowAPI, awaitWrap, getResponseData, isRequestSuccess } from '@/services/http'; import useFlowStore from '../../../../store'; @@ -27,9 +33,21 @@ export interface TestDrawerProps { onClose: () => void; } -const statusDefaultMsgKey: Record = { - ERROR: 'common.label.error', - SUCCESS: 'common.label.success', +const statusDefaultMsgKey: Record< + WorkflowNodeStatus, + { + icon: React.ReactNode; + intlKey: string; + } +> = { + ERROR: { + icon: , + intlKey: 'common.label.error', + }, + SUCCESS: { + icon: , + intlKey: 'common.label.success', + }, }; const TestDrawer: React.FC = ({ node, open, onClose }) => { @@ -190,11 +208,12 @@ const TestDrawer: React.FC = ({ node, open, onClose }) => { ? 'success' : 'error' } - icon={} + icon={statusDefaultMsgKey[testResult.status]?.icon} > {testResult.error_message || getIntlText( - statusDefaultMsgKey[testResult.status] || '', + statusDefaultMsgKey[testResult.status] + ?.intlKey || '', )} {testResult.output && ( diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 85ff235c..415e8577 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -390,7 +390,7 @@ const WorkflowEditor = () => { key="test-button" disabled={ designMode === 'advanced' || - !nodes.length || + !nodes?.length || nodeConfigLoading || saveLoading } @@ -398,7 +398,7 @@ const WorkflowEditor = () => { } onClick={handleSave} From 82405550a84d5babd1282ed684b218e6ecf3036f Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 11:58:43 +0800 Subject: [PATCH 125/172] fix: remove the basic data change to prevent route jump --- apps/web/src/pages/workflow/views/editor/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index 415e8577..a1e12f0c 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -141,7 +141,7 @@ const WorkflowEditor = () => { const [flowDataLoading, setFlowDataLoading] = useState(); const handleFlowDataChange = useCallback>(data => { setBasicData(data); - setIsPreventLeave(true); + // setIsPreventLeave(true); }, []); useRequest( @@ -359,9 +359,15 @@ const WorkflowEditor = () => { // console.log({ error, resp }); setSaveLoading(false); if (error || !isRequestSuccess(resp)) return; - const data = getResponseData(resp); + const respData = getResponseData(resp); // console.log(data); + setBasicData(data => { + const result = data || {}; + result.version = respData?.version ? respData.version : result.version; + + return result; + }); toast.success(getIntlText('common.message.operation_success')); setIsPreventLeave(false); setTimeout(() => navigate('/workflow'), 0); From 8ae953d134a2bcc568bf058609bf692cc2eff452 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 13:29:54 +0800 Subject: [PATCH 126/172] fix: temporarily ignore entity type errors --- apps/web/src/plugin/hooks/useEntitySelectOptions.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/plugin/hooks/useEntitySelectOptions.ts b/apps/web/src/plugin/hooks/useEntitySelectOptions.ts index 9e63376a..50f1eeb0 100644 --- a/apps/web/src/plugin/hooks/useEntitySelectOptions.ts +++ b/apps/web/src/plugin/hooks/useEntitySelectOptions.ts @@ -48,7 +48,8 @@ export function useEntitySelectOptions( const [error, resp] = await awaitWrap( entityAPI.getList({ keyword, - entity_type: entityType ? [entityType] : undefined, + // @ts-ignore TODO: prop type is error, should be EntityType[] + entity_type: entityType, entity_value_type: entityValueTypes, entity_access_mod: entityAccessMods, exclude_children: entityExcludeChildren, From b4c83464141eee06d085993dc22ebdec8caf0b75 Mon Sep 17 00:00:00 2001 From: Nian Date: Mon, 30 Dec 2024 14:49:08 +0800 Subject: [PATCH 127/172] fix: adjust workflow delete & export api --- apps/web/src/pages/workflow/index.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 03ff8ae1..8a1d736e 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -78,7 +78,7 @@ const Workflow = () => { description: getIntlText('workflow.message.delete_tip'), onConfirm: async () => { const [error, resp] = await awaitWrap( - workflowAPI.deleteFlow({ id: idsToDelete }), + workflowAPI.deleteFlows({ workflow_id_list: idsToDelete }), ); // console.log({ error, resp }); @@ -170,7 +170,9 @@ const Workflow = () => { ); const handleExportWorkFlow = useCallback( async (record: TableRowDataType) => { - const [error, resp] = await awaitWrap(workflowAPI.getFlowDesign({ id: record.id })); + const [error, resp] = await awaitWrap( + workflowAPI.getFlowDesign({ id: record.id, version: '' }), + ); if (error || !isRequestSuccess(resp)) return; exportJsonFile(getResponseData(resp) as WorkflowAPISchema['getFlowDesign']['response']); From 680673a1051bbf521af62d843b2466322c6d92f6 Mon Sep 17 00:00:00 2001 From: Nian Date: Mon, 30 Dec 2024 15:18:14 +0800 Subject: [PATCH 128/172] fix: change param value type enum --- .../components/param-input/index.tsx | 26 +++++++++++++++---- .../editor/components/log-panel/index.tsx | 4 +-- packages/shared/types/workflow.d.ts | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index f7b61187..819dac52 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -42,10 +42,26 @@ export interface ParamInputProps { const DEFAULT_EMPTY_VALUE: ParamInputValueType = { name: '', - type: '' as WorkflowParamValueType, + type: '' as EntityValueDataType, }; -const typeOptions: WorkflowParamValueType[] = ['INT', 'FLOAT', 'BOOLEAN', 'STRING']; - +const typeOptions = [ + { + label: 'INT', + value: 'LONG', + }, + { + label: 'FLOAT', + value: 'DOUBLE', + }, + { + label: 'BOOLEAN', + value: 'BOOLEAN', + }, + { + label: 'STRING', + value: 'STRING', + }, +]; const ParamInput: React.FC = ({ required, disabled, @@ -110,8 +126,8 @@ const ParamInput: React.FC = ({ } > {typeOptions.map(item => ( - - {item} + + {item.label} ))} diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index 405428f6..7a01714a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -115,8 +115,8 @@ const LogPanel: React.FC = ({ designMode }) => { value = Math.random() > 0.5; break; } - case 'INT': - case 'FLOAT': { + case 'LONG': + case 'DOUBLE': { value = Math.floor(Math.random() * 100); break; } diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index 62552187..4c5e4a8f 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -95,7 +95,7 @@ declare type TriggerNodeDataType = BaseNodeDataType<{ /** Entity Definition */ entityConfigs: { name: string; - type: WorkflowParamValueType; + type: EntityValueDataType; }[]; }>; From 338da7865b8592896f387bbf2ba74ced8d5dc06d Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Mon, 30 Dec 2024 15:22:20 +0800 Subject: [PATCH 129/172] feat: entity select component Added the interface search function --- .../components/entity-list/index.tsx | 63 ++++++++++++++++-- .../components/entity-select/entitySelect.tsx | 58 ++++++++++++++--- .../components/entity-select/hooks/index.tsx | 1 + .../entity-select/hooks/useContextValue.tsx | 8 ++- .../entity-select/hooks/useOptions.tsx | 9 +-- .../entity-select/hooks/useSourceData.tsx | 65 +++++++++++++++++++ .../src/components/entity-select/index.tsx | 25 ++++++- .../src/components/entity-select/types.d.ts | 15 ++++- apps/web/src/stores/entity.ts | 16 ----- apps/web/src/stores/index.ts | 1 - 10 files changed, 217 insertions(+), 44 deletions(-) create mode 100644 apps/web/src/components/entity-select/hooks/useSourceData.tsx delete mode 100644 apps/web/src/stores/entity.ts diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx index 8e076d0f..7f6ce48a 100644 --- a/apps/web/src/components/entity-select/components/entity-list/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useContext, useRef, useState } from 'react'; +import React, { useCallback, useContext, useMemo, useRef, useState } from 'react'; import { useVirtualList } from '@milesight/shared/src/hooks'; import EntityMenuPopper from '../entity-menu-popper'; import { EntityContext } from '../../context'; @@ -10,7 +10,7 @@ interface IProps { children: React.ReactNode; } export default React.memo(({ children: _children, ...props }: IProps) => { - const { tabType, options, selectedEntityMap } = useContext(EntityContext); + const { tabType, options, selectedEntityMap, maxCount } = useContext(EntityContext); const containerRef = useRef(null); const listRef = useRef(null); @@ -33,6 +33,47 @@ export default React.memo(({ children: _children, ...props }: IProps) => { itemHeight: tabType === 'entity' ? 58 : 38, overscan: 10, }); + const selectedCount = useMemo(() => selectedEntityMap.size, [selectedEntityMap]); + + // get scroll bar width + const scrollbarWidth = useMemo(() => { + if (!menuAnchorEl) return 0; + + const listNode = menuAnchorEl?.parentElement?.parentElement; + const popNode = listNode?.parentElement; + if (!listNode || !popNode) return 0; + + const { scrollHeight, clientHeight, clientWidth } = listNode || {}; + const hasScroll = scrollHeight > clientHeight; + if (!hasScroll) return 0; + + const { clientWidth: popClientWidth } = popNode || {}; + return popClientWidth - clientWidth || 0; + }, [menuAnchorEl]); + // Define popper modifiers + const modifiers = useMemo( + () => [ + { + name: 'offset', + options: { + offset: [0, scrollbarWidth], + }, + }, + { + name: 'preventOverflow', + options: { + altBoundary: true, + tether: false, + }, + }, + { + name: 'flip', + enabled: false, + }, + ], + [scrollbarWidth], + ); + return ( <>
@@ -40,17 +81,29 @@ export default React.memo(({ children: _children, ...props }: IProps) => { {(virtualList || []).map(({ data: option }) => { const { value } = option || {}; const selected = selectedEntityMap.has(value); + const disabled = maxCount && selectedCount >= maxCount ? !selected : false; return tabType === 'entity' ? ( - + ) : ( ); })}
- {tabType === 'device' && ( - + {tabType === 'device' && !!menuList.length && ( + )} ); diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx index 40d7e273..c30c9145 100644 --- a/apps/web/src/components/entity-select/entitySelect.tsx +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -23,7 +23,19 @@ const EntitySelect = < >( props: Props, ) => { - const { title, required, value, multiple, onChange, ...rest } = props; + const { + label, + required, + value, + multiple, + loading, + onChange, + onOpen, + onClose, + onInputChange, + onSearch, + ...rest + } = props; const { options } = useContext>( EntityContext as unknown as React.Context>, ); @@ -35,16 +47,24 @@ const EntitySelect = < /** * Handles opening of the select menu. */ - const handleSelectOpen = useCallback['onOpen']>(event => { - setAnchorEl(event.currentTarget as HTMLDivElement); - }, []); + const handleSelectOpen = useCallback['onOpen']>( + event => { + setAnchorEl(event.currentTarget as HTMLDivElement); + onOpen?.(event); + }, + [onOpen], + ); /** * Handles closing of the select menu. */ - const handleSelectClose = useCallback['onClose']>(() => { - setAnchorEl(null); - }, []); + const handleSelectClose = useCallback['onClose']>( + (...params) => { + setAnchorEl(null); + onClose?.(...params); + }, + [onClose], + ); /** * Handles the change event when an option is selected or cleared. @@ -56,12 +76,24 @@ const EntitySelect = < [onChange], ); + /** + * Handles the input change event. + */ + const handleInputChange = useCallback( + (event, value, reason) => { + onSearch?.(reason === 'input' ? value : ''); + + onInputChange?.(event, value, reason); + }, + [onInputChange, onSearch], + ); + /** * Renders the input component for the Autocomplete. */ const renderInput = useCallback( - params => , - [required, title], + params => , + [required, label], ); /** @@ -92,6 +124,11 @@ const EntitySelect = < [], ); + const filterOptions = useCallback( + options => options, + [], + ); + // Memoize custom slots and slot props to optimize rendering const slots = useMemo(() => ({ paper: EntityPaper }), []); const slotProps = useMemo(() => ({ listbox: { component: EntityList } }), []); @@ -106,11 +143,14 @@ const EntitySelect = < onChange={handleChange} onOpen={handleSelectOpen} onClose={handleSelectClose} + onInputChange={handleInputChange} getOptionLabel={getOptionLabel} renderTags={renderTags} renderInput={renderInput} slots={slots} slotProps={slotProps} + loading={loading} + filterOptions={filterOptions} /> ); }; diff --git a/apps/web/src/components/entity-select/hooks/index.tsx b/apps/web/src/components/entity-select/hooks/index.tsx index 9745c477..62336a6f 100644 --- a/apps/web/src/components/entity-select/hooks/index.tsx +++ b/apps/web/src/components/entity-select/hooks/index.tsx @@ -1,3 +1,4 @@ export { useOptions } from './useOptions'; export { useSelectValue } from './useSelectValue'; export { useContextValue } from './useContextValue'; +export { useSourceData } from './useSourceData'; diff --git a/apps/web/src/components/entity-select/hooks/useContextValue.tsx b/apps/web/src/components/entity-select/hooks/useContextValue.tsx index a093e098..8ed7bcf5 100644 --- a/apps/web/src/components/entity-select/hooks/useContextValue.tsx +++ b/apps/web/src/components/entity-select/hooks/useContextValue.tsx @@ -13,12 +13,14 @@ export const useContextValue = < M extends boolean | undefined = false, D extends boolean | undefined = false, >( - props: Pick, 'value' | 'multiple' | 'onChange' | 'maxCount'>, + props: Pick, 'value' | 'multiple' | 'onChange' | 'maxCount'> & { + entityList: EntityData[]; + }, ) => { - const { value, multiple, maxCount, onChange } = props; + const { value, multiple, maxCount, entityList, onChange } = props; const [tabType, setTabType] = useState('entity'); - const { options } = useOptions({ tabType }); + const { options } = useOptions({ tabType, entityList }); const { selectedEntityMap, onEntityChange } = useSelectValue({ value, multiple, diff --git a/apps/web/src/components/entity-select/hooks/useOptions.tsx b/apps/web/src/components/entity-select/hooks/useOptions.tsx index 761e3d5f..ebea5fa1 100644 --- a/apps/web/src/components/entity-select/hooks/useOptions.tsx +++ b/apps/web/src/components/entity-select/hooks/useOptions.tsx @@ -1,18 +1,15 @@ import { useCallback, useMemo } from 'react'; -import { useShallow } from 'zustand/react/shallow'; import { useI18n } from '@milesight/shared/src/hooks'; import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; -import { useEntityStore } from '@/stores'; import { safeJsonParse } from '../helper'; import type { EntitySelectOption, TabType } from '../types'; interface IProps { tabType: TabType; + entityList: EntityData[]; } - -export const useOptions = ({ tabType }: IProps) => { +export const useOptions = ({ tabType, entityList }: IProps) => { const { getIntlText } = useI18n(); - const { entityList } = useEntityStore(useShallow(state => ({ entityList: state.entityList }))); /** Get description text */ const getDescription = useCallback( @@ -63,7 +60,7 @@ export const useOptions = ({ tabType }: IProps) => { /** Get entity drop-down options and device drop-down options */ const { entityOptions, deviceOptions } = useMemo(() => { - const { entityOptions, deviceMap } = entityList.reduce<{ + const { entityOptions, deviceMap } = (entityList || []).reduce<{ deviceMap: Map; entityOptions: EntitySelectOption[]; }>( diff --git a/apps/web/src/components/entity-select/hooks/useSourceData.tsx b/apps/web/src/components/entity-select/hooks/useSourceData.tsx new file mode 100644 index 00000000..8a1cb0aa --- /dev/null +++ b/apps/web/src/components/entity-select/hooks/useSourceData.tsx @@ -0,0 +1,65 @@ +import { useCallback, useMemo } from 'react'; +import { useRequest } from 'ahooks'; +import { awaitWrap, entityAPI, getResponseData, isRequestSuccess } from '@/services/http'; +import type { EntitySelectProps } from '../types'; + +export const useSourceData = ( + props: Pick< + EntitySelectProps, + 'entityType' | 'entityAccessMod' | 'excludeChildren' | 'entityValueType' + >, +) => { + const { entityType, entityAccessMod, excludeChildren, entityValueType } = props; + const { + run: getEntityList, + data: entityList, + loading, + } = useRequest( + async (keyword?: string) => { + const [error, resp] = await awaitWrap( + entityAPI.getList({ + page_number: 1, + page_size: 9999, + entity_type: entityType, + entity_access_mod: entityAccessMod, + exclude_children: excludeChildren, + entity_value_type: entityValueType, + keyword, + }), + ); + if (error || !isRequestSuccess(resp)) return; + + const data = getResponseData(resp)!; + return data?.content || []; + }, + { + refreshDeps: [ + entityType, + entityType, + entityAccessMod, + excludeChildren, + entityValueType, + ], + debounceWait: 300, + }, + ); + + /** search entity list by keyword */ + const onSearch = useCallback( + async (keyword: string) => { + if (!keyword) { + getEntityList(); + return; + } + + getEntityList(keyword); + }, + [getEntityList], + ); + + return { + entityList: useMemo(() => entityList || [], [entityList]), + loading, + onSearch, + }; +}; diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index 02ed68d3..59b241ee 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { useControllableValue } from 'ahooks'; import EntitySelect from './entitySelect'; -import { useContextValue } from './hooks'; +import { useContextValue, useSourceData } from './hooks'; import { EntityContext } from './context'; import { DEFAULT_MAX_COUNT } from './constant'; import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; @@ -18,15 +18,34 @@ const EntitySelectApp = < >( props: EntitySelectProps, ) => { - const { multiple, maxCount: _maxCount } = props; + const { + multiple, + maxCount: _maxCount, + loading, + entityType, + entityValueType, + entityAccessMod, + excludeChildren, + } = props; const maxCount = multiple ? (_maxCount! ?? DEFAULT_MAX_COUNT) : void 0; + const { + entityList, + onSearch, + loading: sourceLoading, + } = useSourceData({ + entityType, + entityValueType, + entityAccessMod, + excludeChildren, + }); const [value, onChange] = useControllableValue['value']>(props); const { contextValue } = useContextValue({ value, maxCount, multiple, onChange, + entityList, }); return ( @@ -36,6 +55,8 @@ const EntitySelectApp = < multiple={multiple} value={value} onChange={onChange} + loading={loading || sourceLoading} + onSearch={onSearch} /> ); diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index 4186323c..d12fa729 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -1,4 +1,5 @@ import type { AutocompleteProps, TextFieldProps } from '@mui/material'; +import type { EntityAPISchema } from '@/services/http'; /** Define the possible tab types */ export type TabType = 'entity' | 'device'; @@ -37,6 +38,11 @@ export type EntitySelectValue = Multiple exte ? NonNullable : Value | null; +/** Interface filter parameter */ +type FilterParameters = Omit< + ObjectToCamelCase, + 'pageSize' | 'pageNumber' | 'keyword' +>; /** * Props for the EntitySelect component. */ @@ -44,11 +50,12 @@ export interface EntitySelectProps< Value extends EntitySelectValueType = EntitySelectValueType, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, -> extends Pick, +> extends Pick, Omit< AutocompleteProps, 'renderInput' | 'options' - > { + >, + FilterParameters { /** Whether multiple selection is enabled */ multiple?: Multiple; /** The current value of the select */ @@ -63,6 +70,10 @@ export interface EntitySelectProps< * @description This prop is only used when `multiple` is true */ maxCount?: Multiple extends true ? number : never; + /** + * Callback function when the search input changes + */ + onSearch?: (value: string) => void; } /** diff --git a/apps/web/src/stores/entity.ts b/apps/web/src/stores/entity.ts deleted file mode 100644 index 0184ee05..00000000 --- a/apps/web/src/stores/entity.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { create } from 'zustand'; -import { immer } from 'zustand/middleware/immer'; -import { type EntityAPISchema } from '@/services/http'; - -type EntityData = EntityAPISchema['getList']['response']['content'][number]; -interface EntityStore { - entityList: EntityData[]; - setEntityList: (entityList: EntityData[]) => void; -} - -export default create( - immer(set => ({ - entityList: [], - setEntityList: entityList => set({ entityList }), - })), -); diff --git a/apps/web/src/stores/index.ts b/apps/web/src/stores/index.ts index abe6cf39..452e4526 100644 --- a/apps/web/src/stores/index.ts +++ b/apps/web/src/stores/index.ts @@ -1,2 +1 @@ export { default as useUserStore } from './user'; -export { default as useEntityStore } from './entity'; From 739d967586bf57ceaa0cf1dddc3a085afc22f84c Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 15:49:48 +0800 Subject: [PATCH 130/172] feat: performance optimization to avoid crashing when dragging nodes --- .../components/more-menu/index.tsx | 5 +- .../editor/components/config-panel/index.tsx | 8 +-- .../views/editor/hooks/useWorkflow.ts | 67 +++++-------------- .../src/pages/workflow/views/editor/index.tsx | 51 ++++++++++---- .../src/pages/workflow/views/editor/store.ts | 6 ++ 5 files changed, 68 insertions(+), 69 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx index ff42a20e..8697fc37 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/more-menu/index.tsx @@ -3,15 +3,14 @@ import { IconButton, Popover } from '@mui/material'; import { useI18n, useCopy } from '@milesight/shared/src/hooks'; import { MoreHorizIcon, ContentCopyIcon } from '@milesight/shared/src/components'; import { Tooltip } from '@/components'; -import useWorkflow from '../../../../hooks/useWorkflow'; +import useFlowStore from '../../../../store'; import './style.less'; const MoreMenu = () => { const { getIntlText } = useI18n(); const { handleCopy } = useCopy(); - const { getSelectedNode } = useWorkflow(); + const selectedNode = useFlowStore(state => state.selectedNode); const [anchorEl, setAnchorEl] = useState(null); - const selectedNode = getSelectedNode(); return (
diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 35b25c51..28bc09d0 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -5,7 +5,7 @@ import { isEqual } from 'lodash-es'; import { useDebounceEffect } from 'ahooks'; import { Stack, IconButton, Divider, Tooltip } from '@mui/material'; import { useForm, Controller } from 'react-hook-form'; -import { useI18n } from '@milesight/shared/src/hooks'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon, HelpIcon } from '@milesight/shared/src/components'; import useFlowStore from '../../store'; import useWorkflow from '../../hooks/useWorkflow'; @@ -29,9 +29,9 @@ const ConfigPanel = () => { const { updateNode, updateNodeData } = useReactFlow(); // ---------- Handle Node-related logic ---------- - const { getSelectedNode } = useWorkflow(); - const selectedNode = useMemo(() => getSelectedNode(), [getSelectedNode]); - const nodeConfigs = useFlowStore(state => state.nodeConfigs); + const { selectedNode, nodeConfigs } = useFlowStore( + useStoreShallow(['selectedNode', 'nodeConfigs']), + ); const openPanel = !!selectedNode; const nodeConfig = useMemo(() => { if (!selectedNode) return; diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 6737d26a..28b2f718 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -1,14 +1,7 @@ -import { useMemo, useCallback } from 'react'; -import { - useReactFlow, - useNodes, - useEdges, - getIncomers, - getOutgoers, - type IsValidConnection, -} from '@xyflow/react'; +import { useCallback } from 'react'; +import { useReactFlow, getIncomers, getOutgoers, type IsValidConnection } from '@xyflow/react'; import { uniqBy, omit, cloneDeep, pick, isEmpty, isObject } from 'lodash-es'; -import { useI18n } from '@milesight/shared/src/hooks'; +import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; import useFlowStore from '../store'; @@ -48,24 +41,10 @@ const entryNodeTypes = Object.values(basicNodeConfigs) const useWorkflow = () => { const { getNodes, getEdges, setNodes, fitView } = useReactFlow(); - const nodes = useNodes(); - const edges = useEdges(); const { getIntlText } = useI18n(); - const nodeConfigs = useFlowStore(state => state.nodeConfigs); - - const selectedNode = useMemo(() => { - const selectedNodes = nodes.filter(item => item.selected); - const node = selectedNodes?.[0]; - - if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { - return; - } - - return node; - }, [nodes]); - // Use nodeId, nodeType to avoid frequent render triggers - const selectedNodeId = selectedNode?.id; - const selectedNodeType = selectedNode?.type; + const { selectedNode, nodeConfigs } = useFlowStore( + useStoreShallow(['selectedNode', 'nodeConfigs']), + ); // Check node number limit const checkNodeNumberLimit = useCallback( @@ -184,27 +163,12 @@ const useWorkflow = () => { [getNodes, getEdges, checkParallelLimit], ); - // Get the only selected node that is not dragging - const getSelectedNode = useCallback(() => { - const nodes = getNodes(); - const selectedNodes = nodes.filter( - item => item.id === selectedNodeId && item.type === selectedNodeType, - ); - const node = selectedNodes?.[0]; - - if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { - return; - } - - return node; - }, [selectedNodeId, selectedNodeType, getNodes]); - // Get all upstream nodes of the current node const getUpstreamNodes = useCallback( (currentNode?: WorkflowNode, nodes?: WorkflowNode[], edges?: WorkflowEdge[]) => { nodes = nodes || getNodes(); edges = edges || getEdges(); - currentNode = currentNode || getSelectedNode(); + currentNode = currentNode || selectedNode; const getAllIncomers = ( node: WorkflowNode, @@ -229,12 +193,18 @@ const useWorkflow = () => { return getAllIncomers(currentNode!); }, - [getNodes, getEdges, getSelectedNode], + [getNodes, getEdges, selectedNode], ); const getUpstreamNodeParams = useCallback( - (currentNode?: WorkflowNode): [NodeParamType[], FlattenNodeParamType[]] | [] => { - currentNode = currentNode || getSelectedNode(); + ( + currentNode?: WorkflowNode, + nodes?: WorkflowNode[], + edges?: WorkflowEdge[], + ): [NodeParamType[], FlattenNodeParamType[]] | [] => { + nodes = nodes || getNodes(); + edges = edges || getEdges(); + currentNode = currentNode || selectedNode; if (!currentNode) return []; const incomeNodes = getUpstreamNodes(currentNode, nodes, edges); @@ -322,7 +292,7 @@ const useWorkflow = () => { return [result, flattenResult]; }, - [nodes, edges, nodeConfigs, getSelectedNode, getUpstreamNodes, getIntlText], + [nodeConfigs, selectedNode, getNodes, getEdges, getUpstreamNodes, getIntlText], ); // Check if there is a node that is not connected to an entry node @@ -397,15 +367,12 @@ const useWorkflow = () => { ); return { - nodes, - edges, isValidConnection, checkParallelLimit, checkNestedParallelLimit, checkNodeNumberLimit, checkFreeNodeLimit, checkWorkflowValid, - getSelectedNode, getUpstreamNodes, getUpstreamNodeParams, updateNodesStatus, diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index a1e12f0c..e4d830e8 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -49,14 +49,50 @@ const WorkflowEditor = () => { const { getIntlText } = useI18n(); const nodeTypes = useNodeTypes(); const { toObject } = useReactFlow(); + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { isValidConnection, checkWorkflowValid, updateNodesStatus } = useWorkflow(); const { handleConnect, handleBeforeDelete, handleEdgeMouseEnter, handleEdgeMouseLeave } = useInteractions(); - const [nodes, setNodes, onNodesChange] = useNodesState([]); - const [edges, setEdges, onEdgesChange] = useEdgesState([]); const { checkNodesId, checkNodesType, checkNodesData, checkEdgesId, checkEdgesType } = useValidate(); const confirm = useConfirm(); + const { + setSelectedNode, + setOpenLogPanel, + setNodeConfigs, + setTestLogs, + setLogDetail, + setNodesDataValidResult, + } = useFlowStore( + useStoreShallow([ + 'setSelectedNode', + 'setOpenLogPanel', + 'setNodeConfigs', + 'setTestLogs', + 'setLogDetail', + 'setNodesDataValidResult', + ]), + ); + + // ---------- Store selected node ---------- + const selectedNode = (() => { + const selectedNodes = nodes.filter(item => item.selected); + const node = selectedNodes?.[0]; + + if (selectedNodes.length > 1 || !node || !node.selected || node.dragging) { + return; + } + + return node; + })(); + const selectedNodeId = selectedNode?.id; + const selectedNodeType = selectedNode?.type; + + // Only the nodeId/nodeType changed, we update the selected node + useEffect(() => { + setSelectedNode(selectedNode); + }, [selectedNodeId, selectedNodeType]); // ---------- Prevent Leave ---------- const [isPreventLeave, setIsPreventLeave] = useState(false); @@ -110,16 +146,7 @@ const WorkflowEditor = () => { ); // ---------- Fetch Nodes Config ---------- - const { setOpenLogPanel, setNodeConfigs, setTestLogs, setLogDetail, setNodesDataValidResult } = - useFlowStore( - useStoreShallow([ - 'setOpenLogPanel', - 'setNodeConfigs', - 'setTestLogs', - 'setLogDetail', - 'setNodesDataValidResult', - ]), - ); + const { loading: nodeConfigLoading } = useRequest( async () => { const [error, resp] = await awaitWrap(workflowAPI.getFlowNodes()); diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index 1a08a784..e2448b90 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -6,6 +6,8 @@ import type { NodesDataValidResult } from './hooks'; import type { NodeConfigItem } from './typings'; interface FlowStore { + selectedNode?: WorkflowNode; + /** Workflow Node Configs */ nodeConfigs: Record; @@ -38,6 +40,8 @@ interface FlowStore { logDetailLoading?: boolean; + setSelectedNode: (node?: FlowStore['selectedNode']) => void; + setNodeConfigs: (nodeConfigs: WorkflowAPISchema['getFlowNodes']['response']) => void; setLogPanelMode: (logPanelMode: FlowStore['logPanelMode']) => void; @@ -62,6 +66,8 @@ const useFlowStore = create( testLogs: [], + setSelectedNode: node => set({ selectedNode: node }), + setNodeConfigs: nodeConfigs => { const configs = Object.entries(nodeConfigs).reduce((acc, [cat, configs]) => { const result = configs.map(config => { From c6f335c12ee98c572852d2b7bb1a25b4f120aa16 Mon Sep 17 00:00:00 2001 From: Nian Date: Mon, 30 Dec 2024 16:33:25 +0800 Subject: [PATCH 131/172] fix: modify the logic for disabling deletion --- apps/web/src/pages/workflow/hooks/useColumns.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index 4bc0f221..77850c52 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -155,7 +155,6 @@ const useColumns = ({ onButtonClick }: UseColumnsPro > onButtonClick('export', row)}> ({ onButtonClick }: UseColumnsPro - onButtonClick('delete', row)}> + onButtonClick('delete', row)} + > Date: Mon, 30 Dec 2024 16:59:29 +0800 Subject: [PATCH 132/172] feat: the edge start/end points to the handler center position --- .../editor/components/config-panel/index.tsx | 1 - .../views/editor/components/edge/index.tsx | 4 +-- .../views/editor/components/handle/style.less | 25 ++++++++++++++++--- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 28bc09d0..2f3067de 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -8,7 +8,6 @@ import { useForm, Controller } from 'react-hook-form'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon, HelpIcon } from '@milesight/shared/src/components'; import useFlowStore from '../../store'; -import useWorkflow from '../../hooks/useWorkflow'; import { useCommonFormItems, useNodeFormItems, diff --git a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx index c0502a4e..18a8c598 100644 --- a/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/edge/index.tsx @@ -31,10 +31,10 @@ const AddableEdge = ({ const edges = useEdges(); const edge = edges.find(edge => edge.id === id); const [edgePath, labelX, labelY] = getBezierPath({ - sourceX, + sourceX: sourceX - 8, sourceY, sourcePosition, - targetX, + targetX: targetX + 8, targetY, targetPosition, }); diff --git a/apps/web/src/pages/workflow/views/editor/components/handle/style.less b/apps/web/src/pages/workflow/views/editor/components/handle/style.less index 2f35a312..c688ed5a 100644 --- a/apps/web/src/pages/workflow/views/editor/components/handle/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/handle/style.less @@ -1,14 +1,25 @@ .@{prefix}-workflow { & &-handle { z-index: 10; - width: 2px; + width: 16px; min-width: auto; height: 16px; color: var(--primary-color-base); - background-color: var(--primary-color-base); + background-color: transparent; border: none; border-radius: 0; + &::after { + position: absolute; + top: 1px; + bottom: 1px; + left: 50%; + width: 2px; + margin-left: -1px; + background-color: var(--primary-color-base); + content: ''; + } + .@{mui-prefix}SvgIcon-root { display: none; font-size: @font-size-lg; @@ -60,11 +71,14 @@ .react-flow__node { .@{prefix}-workflow-handle.is-menu-open { - width: 16px; background-color: var(--component-background); border-radius: 16px; cursor: pointer; + &::after { + display: none; + } + .@{mui-prefix}SvgIcon-root { display: block; } @@ -75,11 +89,14 @@ .react-flow__node.selected { .@{prefix}-workflow-handle.source, .@{prefix}-workflow-handle.target.target-enable-add { - width: 16px; background-color: var(--component-background); border-radius: 16px; cursor: pointer; + &::after { + display: none; + } + .@{mui-prefix}SvgIcon-root { display: block; } From 548006340aed3e6b2175941cbf960066f28e69a8 Mon Sep 17 00:00:00 2001 From: jimco Date: Mon, 30 Dec 2024 17:30:35 +0800 Subject: [PATCH 133/172] feat: add identify prop for trigger node data --- .../config-panel/components/param-input/index.tsx | 7 ++++++- .../src/pages/workflow/views/editor/hooks/useWorkflow.ts | 9 ++++++++- packages/shared/types/workflow.d.ts | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx index 819dac52..191f6e42 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-input/index.tsx @@ -18,6 +18,7 @@ import { } from '@mui/material'; import { useDynamicList, useControllableValue } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; +import { genRandomString } from '@milesight/shared/src/utils/tools'; import { DeleteOutlineIcon, AddIcon } from '@milesight/shared/src/components'; import './style.less'; import { isEqual } from 'lodash-es'; @@ -41,6 +42,7 @@ export interface ParamInputProps { } const DEFAULT_EMPTY_VALUE: ParamInputValueType = { + identify: '', name: '', type: '' as EntityValueDataType, }; @@ -97,7 +99,10 @@ const ParamInput: React.FC = ({ }, [list]); const handlerAdd = () => { if (disabledAdd) return; - insert(list.length, DEFAULT_EMPTY_VALUE); + insert(list.length, { + ...DEFAULT_EMPTY_VALUE, + identify: `param_${genRandomString(8, { lowerCase: true })}`, + }); }; return (
diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts index 28b2f718..be9ddb7c 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useWorkflow.ts @@ -226,18 +226,24 @@ const useWorkflow = () => { Object.entries(outputArgs).forEach(([param, data]) => { switch (param) { + // Data Type: { identify?: string; name: string; type: string }[] case 'entityConfigs': case 'Payload': { if (!Array.isArray(data)) return; + // TODO: The key may use `identity` to replace `name` ? data.forEach((item: Record) => { paramData.outputs.push({ name: item?.name, type: item?.type, - key: genRefParamKey(id, item.name), + key: + param === 'entityConfigs' + ? genRefParamKey(id, item.name) + : genRefParamKey(id, item.identify), }); }); break; } + // Data Type: string[] case 'entities': { if (!Array.isArray(data)) return; data.forEach(item => { @@ -250,6 +256,7 @@ const useWorkflow = () => { }); break; } + // Data Type: Record case 'inputArguments': case 'exchangePayload': case 'serviceInvocationSetting': { diff --git a/packages/shared/types/workflow.d.ts b/packages/shared/types/workflow.d.ts index 4c5e4a8f..b116829e 100644 --- a/packages/shared/types/workflow.d.ts +++ b/packages/shared/types/workflow.d.ts @@ -94,6 +94,7 @@ declare type BaseNodeDataType = Record Date: Mon, 30 Dec 2024 20:02:57 +0800 Subject: [PATCH 134/172] feat: Hide the submenu as the entity select component scrolls --- .../components/entity-list/index.tsx | 33 +++++++++++-------- .../components/entity-menu-popper/index.tsx | 32 ++++++++++-------- .../components/entity-menu-popper/style.less | 4 ++- .../components/entity-menu/index.tsx | 6 ++-- .../components/entity-select/entitySelect.tsx | 14 ++++++-- .../src/components/entity-select/types.d.ts | 2 +- 6 files changed, 56 insertions(+), 35 deletions(-) diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx index 7f6ce48a..55458696 100644 --- a/apps/web/src/components/entity-select/components/entity-list/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -1,4 +1,5 @@ -import React, { useCallback, useContext, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { useMemoizedFn } from 'ahooks'; import { useVirtualList } from '@milesight/shared/src/hooks'; import EntityMenuPopper from '../entity-menu-popper'; import { EntityContext } from '../../context'; @@ -50,6 +51,23 @@ export default React.memo(({ children: _children, ...props }: IProps) => { const { clientWidth: popClientWidth } = popNode || {}; return popClientWidth - clientWidth || 0; }, [menuAnchorEl]); + + // when scroll, clear menu list + const handleScroll = useMemoizedFn(() => { + if (!menuList?.length) return; + + setMenuList([]); + }); + useEffect(() => { + const node = containerRef.current; + if (!node) return; + + node.addEventListener('scroll', handleScroll); + return () => { + node.removeEventListener('scroll', handleScroll); + }; + }, [handleScroll]); + // Define popper modifiers const modifiers = useMemo( () => [ @@ -59,21 +77,9 @@ export default React.memo(({ children: _children, ...props }: IProps) => { offset: [0, scrollbarWidth], }, }, - { - name: 'preventOverflow', - options: { - altBoundary: true, - tether: false, - }, - }, - { - name: 'flip', - enabled: false, - }, ], [scrollbarWidth], ); - return ( <>
@@ -102,7 +108,6 @@ export default React.memo(({ children: _children, ...props }: IProps) => { anchorEl={menuAnchorEl} menuList={menuList} modifiers={modifiers} - disablePortal /> )} diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx index ed1d5c5e..673b1c3b 100644 --- a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx @@ -1,6 +1,6 @@ -import React, { useContext, useMemo, useRef } from 'react'; +import React, { useContext, useEffect, useMemo, useRef } from 'react'; import { Paper, Popper, PopperProps } from '@mui/material'; -import { useVirtualList } from '@milesight/shared/src/hooks'; +import { useVirtualList } from 'ahooks'; import EntityOption from '../entity-option'; import { EntityContext } from '../../context'; import type { EntitySelectOption } from '../../types'; @@ -14,20 +14,27 @@ export default React.memo(({ menuList, ...props }: IProps) => { const containerRef = useRef(null); const listRef = useRef(null); - // /** virtual list */ - // const [virtualList] = useVirtualList(menuList, { - // containerTarget: containerRef, - // wrapperTarget: listRef, - // itemHeight: 58, - // overscan: 10, - // }); + /** virtual list */ + const [virtualList, scrollTo] = useVirtualList(menuList, { + containerTarget: containerRef, + wrapperTarget: listRef, + itemHeight: 58, + overscan: 10, + }); + useEffect(() => { + scrollTo(0); + }, [menuList]); const selectedCount = useMemo(() => selectedEntityMap.size, [selectedEntityMap]); return ( - + -
+
- {(menuList || []).map(menu => { + {(virtualList || []).map(({ data: menu }) => { const { value } = menu || {}; const selected = selectedEntityMap.has(value); const disabled = @@ -35,7 +42,6 @@ export default React.memo(({ menuList, ...props }: IProps) => { return ( { const { option, selected, onClick } = props; - const { label } = option || {}; + const { label, children } = option || {}; /** When an entity item is selected/canceled */ const handleClick = useCallback( @@ -32,9 +32,7 @@ export default React.memo((props: IProps) => {
-
- -
+
{!!children && }
); }); diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx index c30c9145..67e56f7f 100644 --- a/apps/web/src/components/entity-select/entitySelect.tsx +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -34,6 +34,8 @@ const EntitySelect = < onClose, onInputChange, onSearch, + error, + helperText, ...rest } = props; const { options } = useContext>( @@ -92,8 +94,16 @@ const EntitySelect = < * Renders the input component for the Autocomplete. */ const renderInput = useCallback( - params => , - [required, label], + params => ( + + ), + [error, helperText, label, required], ); /** diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index d12fa729..f12724d1 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -50,7 +50,7 @@ export interface EntitySelectProps< Value extends EntitySelectValueType = EntitySelectValueType, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, -> extends Pick, +> extends Pick, Omit< AutocompleteProps, 'renderInput' | 'options' From c6deaf46147b1fda549690531926a5e2496f0b00 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Tue, 31 Dec 2024 10:25:18 +0800 Subject: [PATCH 135/172] feat: Entity drop-down Component Adds Device list to the selected effect --- .../components/entity-list/index.tsx | 53 ++++++++++++------- .../components/entity-menu-popper/index.tsx | 6 ++- .../components/entity-menu/style.less | 12 +++-- .../components/entity-option/style.less | 4 ++ .../src/components/entity-select/constant.ts | 3 -- .../entity-select/hooks/useContextValue.tsx | 18 +++++-- .../entity-select/hooks/useSelectValue.tsx | 17 ++++++ .../src/components/entity-select/index.tsx | 5 +- .../src/components/entity-select/types.d.ts | 8 ++- apps/web/src/components/index.ts | 6 ++- 10 files changed, 95 insertions(+), 37 deletions(-) diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx index 55458696..f72e62c1 100644 --- a/apps/web/src/components/entity-select/components/entity-list/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -11,7 +11,8 @@ interface IProps { children: React.ReactNode; } export default React.memo(({ children: _children, ...props }: IProps) => { - const { tabType, options, selectedEntityMap, maxCount } = useContext(EntityContext); + const { tabType, options, selectedEntityMap, selectedDeviceMap, maxCount } = + useContext(EntityContext); const containerRef = useRef(null); const listRef = useRef(null); @@ -23,8 +24,10 @@ export default React.memo(({ children: _children, ...props }: IProps) => { const handleClick = useCallback((event: React.MouseEvent, option: EntitySelectOption) => { setMenuAnchorEl(event.currentTarget as HTMLDivElement); - const { children } = option || {}; - setMenuList(children || []); + setTimeout(() => { + const { children } = option || {}; + setMenuList(children || []); + }, 0); }, []); /** virtual list */ @@ -86,30 +89,42 @@ export default React.memo(({ children: _children, ...props }: IProps) => {
{(virtualList || []).map(({ data: option }) => { const { value } = option || {}; - const selected = selectedEntityMap.has(value); - const disabled = maxCount && selectedCount >= maxCount ? !selected : false; - return tabType === 'entity' ? ( - = maxCount ? !selected : false; + + return ( + + ); + } + + // Drop down the device entity + const hasSelectedDevice = selectedDeviceMap.has((value as string) || ''); + return ( + - ) : ( - ); })}
- {tabType === 'device' && !!menuList.length && ( - - )} + ); }); diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx index 673b1c3b..4da456b4 100644 --- a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx @@ -9,6 +9,7 @@ import './style.less'; export interface IProps extends PopperProps { menuList: EntitySelectOption[]; } +const LINE_HEIGHT = 58; export default React.memo(({ menuList, ...props }: IProps) => { const { selectedEntityMap, maxCount } = useContext(EntityContext); const containerRef = useRef(null); @@ -18,12 +19,13 @@ export default React.memo(({ menuList, ...props }: IProps) => { const [virtualList, scrollTo] = useVirtualList(menuList, { containerTarget: containerRef, wrapperTarget: listRef, - itemHeight: 58, + itemHeight: LINE_HEIGHT, overscan: 10, }); useEffect(() => { scrollTo(0); }, [menuList]); + const selectedCount = useMemo(() => selectedEntityMap.size, [selectedEntityMap]); return ( @@ -31,7 +33,7 @@ export default React.memo(({ menuList, ...props }: IProps) => {
{(virtualList || []).map(({ data: menu }) => { diff --git a/apps/web/src/components/entity-select/components/entity-menu/style.less b/apps/web/src/components/entity-select/components/entity-menu/style.less index e2a64cd5..28e2b2b5 100644 --- a/apps/web/src/components/entity-select/components/entity-menu/style.less +++ b/apps/web/src/components/entity-select/components/entity-menu/style.less @@ -5,12 +5,18 @@ padding: @padding-xs @padding-md; cursor: pointer; - &--active { + &:hover { background-color: var(--component-background-gray); } - &:hover { - background-color: var(--component-background-gray); + &--active { + font-weight: @font-weight-medium; + color: var(--text-color-main); + background-color: var(--purple-1); + + &:hover { + background-color: var(--purple-1); + } } &__title { diff --git a/apps/web/src/components/entity-select/components/entity-option/style.less b/apps/web/src/components/entity-select/components/entity-option/style.less index e238a09d..be382839 100644 --- a/apps/web/src/components/entity-select/components/entity-option/style.less +++ b/apps/web/src/components/entity-select/components/entity-option/style.less @@ -13,6 +13,10 @@ font-weight: @font-weight-medium; color: var(--text-color-main); background-color: var(--purple-1); + + &:hover { + background-color: var(--purple-1); + } } &--disabled { diff --git a/apps/web/src/components/entity-select/constant.ts b/apps/web/src/components/entity-select/constant.ts index 3f2ca21a..20c90bf9 100644 --- a/apps/web/src/components/entity-select/constant.ts +++ b/apps/web/src/components/entity-select/constant.ts @@ -10,6 +10,3 @@ export const TabOptions: { label: string; value: TabType }[] = [ value: 'device', }, ]; - -// default max count of selected entities -export const DEFAULT_MAX_COUNT = 5; diff --git a/apps/web/src/components/entity-select/hooks/useContextValue.tsx b/apps/web/src/components/entity-select/hooks/useContextValue.tsx index 8ed7bcf5..401c2a6f 100644 --- a/apps/web/src/components/entity-select/hooks/useContextValue.tsx +++ b/apps/web/src/components/entity-select/hooks/useContextValue.tsx @@ -13,15 +13,22 @@ export const useContextValue = < M extends boolean | undefined = false, D extends boolean | undefined = false, >( - props: Pick, 'value' | 'multiple' | 'onChange' | 'maxCount'> & { + props: Pick< + EntitySelectProps, + 'value' | 'multiple' | 'onChange' | 'maxCount' | 'filterOption' + > & { entityList: EntityData[]; }, ) => { - const { value, multiple, maxCount, entityList, onChange } = props; + const { value, multiple, maxCount, entityList, onChange, filterOption } = props; const [tabType, setTabType] = useState('entity'); - const { options } = useOptions({ tabType, entityList }); - const { selectedEntityMap, onEntityChange } = useSelectValue({ + const { options: _options } = useOptions({ tabType, entityList }); + const options = useMemo( + () => (filterOption ? filterOption(_options) : _options), + [_options, filterOption], + ); + const { selectedEntityMap, selectedDeviceMap, onEntityChange } = useSelectValue({ value, multiple, onChange, @@ -35,11 +42,12 @@ export const useContextValue = < tabType, setTabType, selectedEntityMap, + selectedDeviceMap, onEntityChange, }; return result; - }, [maxCount, onEntityChange, options, selectedEntityMap, tabType]); + }, [maxCount, onEntityChange, options, selectedDeviceMap, selectedEntityMap, tabType]); return { contextValue, diff --git a/apps/web/src/components/entity-select/hooks/useSelectValue.tsx b/apps/web/src/components/entity-select/hooks/useSelectValue.tsx index 27b6683a..fd7c7ccd 100644 --- a/apps/web/src/components/entity-select/hooks/useSelectValue.tsx +++ b/apps/web/src/components/entity-select/hooks/useSelectValue.tsx @@ -21,6 +21,22 @@ export const useSelectValue = < }, new Map()); }, [value]); + /** selected device to entity map */ + const selectedDeviceMap = useMemo(() => { + if (!value) return new Map(); + + const valueList: V[] = Array.isArray(value) ? value : ([value] as unknown as V[]); + return valueList.reduce>((acc, curr) => { + const { rawData } = curr || {}; + const { deviceName = '' } = rawData || {}; + + const deviceList = acc.get(deviceName) || []; + deviceList.push(curr); + acc.set(deviceName, deviceList); + return acc; + }, new Map()); + }, [value]); + /** Select/cancel entity selection callback */ const onEntityChange = useCallback( selectedItem => { @@ -47,6 +63,7 @@ export const useSelectValue = < return { selectedEntityMap, + selectedDeviceMap, onEntityChange, }; }; diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index 59b241ee..28bc62c2 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -3,7 +3,6 @@ import { useControllableValue } from 'ahooks'; import EntitySelect from './entitySelect'; import { useContextValue, useSourceData } from './hooks'; import { EntityContext } from './context'; -import { DEFAULT_MAX_COUNT } from './constant'; import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; const EntitySelectApp = < @@ -26,8 +25,9 @@ const EntitySelectApp = < entityValueType, entityAccessMod, excludeChildren, + filterOption, } = props; - const maxCount = multiple ? (_maxCount! ?? DEFAULT_MAX_COUNT) : void 0; + const maxCount = multiple ? _maxCount : void 0; const { entityList, @@ -46,6 +46,7 @@ const EntitySelectApp = < multiple, onChange, entityList, + filterOption, }); return ( diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index f12724d1..3c7fae1e 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -66,7 +66,6 @@ export interface EntitySelectProps< disableClearable?: DisableClearable; /** * maximum number of items that can be selected - * @default 5 * @description This prop is only used when `multiple` is true */ maxCount?: Multiple extends true ? number : never; @@ -74,6 +73,10 @@ export interface EntitySelectProps< * Callback function when the search input changes */ onSearch?: (value: string) => void; + /** + * Callback function to filter options + */ + filterOption?: (options: EntitySelectOption[]) => boolean; } /** @@ -82,7 +85,6 @@ export interface EntitySelectProps< export interface EntitySelectContext { /** * maximum number of items that can be selected - * @default 5 * @description This prop is only used when `multiple` is true */ maxCount?: number; @@ -94,6 +96,8 @@ export interface EntitySelectContext void; /** The map of selected entities */ selectedEntityMap: Map; + /** The map of selected devices */ + selectedDeviceMap: Map; /** Callback function when an entity is selected or changed */ onEntityChange: (selectedItem: EntitySelectValue) => void; } diff --git a/apps/web/src/components/index.ts b/apps/web/src/components/index.ts index 81057fe3..e4eabaf5 100644 --- a/apps/web/src/components/index.ts +++ b/apps/web/src/components/index.ts @@ -8,7 +8,11 @@ export { default as Tooltip } from './tooltip'; export { default as DateRangePicker } from './date-range-picker'; export { default as RouteLoadingIndicator } from './route-loading-indicator'; export { default as Empty } from './empty'; -export { default as EntitySelect } from './entity-select'; +export { + default as EntitySelect, + type EntitySelectProps, + type EntitySelectValueType, +} from './entity-select'; export { CodeEditor, CodeEditorToolbar, From f7039c2ea2a5d0df990c6a582db9487785b1a3d2 Mon Sep 17 00:00:00 2001 From: jimco Date: Tue, 31 Dec 2024 10:34:26 +0800 Subject: [PATCH 136/172] feat: real time rendering of node names on nodes and config panels --- .../components/conditions-input/index.tsx | 5 +++- .../entity-multiple-select/index.tsx | 2 +- .../components/param-assign-input/index.tsx | 2 +- .../components/param-input/index.tsx | 2 +- .../components/timer-input/index.tsx | 2 +- .../editor/components/config-panel/index.tsx | 23 +++++++++++++++---- .../editor/components/config-panel/style.less | 11 ++++++--- .../components/node-container/index.tsx | 12 +++++++--- .../components/node-container/style.less | 6 +++-- 9 files changed, 48 insertions(+), 17 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx index 5d18f415..ad5f6c5c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/conditions-input/index.tsx @@ -166,7 +166,10 @@ const ConditionsInput: React.FC = props => { : getIntlText(logicOperatorMap.OR?.labelIntlKey || ''); return ( -
+
{blockIndex === 0 diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-multiple-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-multiple-select/index.tsx index 55901028..e35bbd0c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-multiple-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-multiple-select/index.tsx @@ -59,7 +59,7 @@ const EntityMultipleSelect: React.FC = ({ return (
{list.map((item, index) => ( -
+
{/* {typeSelectProps?.label || getIntlText('common.label.type')} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx index 35141a40..c8e8b15f 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/param-assign-input/index.tsx @@ -64,7 +64,7 @@ const ParamAssignInput: React.FC = ({ return (
{list.map((item, index) => ( -
+
= ({ return (
{list.map((item, index) => ( -
+
= ({ required, ...props }) => { {getIntlText('workflow.editor.form_param_execution_time_queue')} {list.map((item, index) => ( -
+
{getIntlText('workflow.editor.form_param_timer_type')} diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index 2f3067de..e3d1a021 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -3,10 +3,11 @@ import { Panel, useReactFlow } from '@xyflow/react'; import cls from 'classnames'; import { isEqual } from 'lodash-es'; import { useDebounceEffect } from 'ahooks'; -import { Stack, IconButton, Divider, Tooltip } from '@mui/material'; +import { Stack, IconButton, Divider } from '@mui/material'; import { useForm, Controller } from 'react-hook-form'; import { useI18n, useStoreShallow } from '@milesight/shared/src/hooks'; import { CloseIcon, PlayArrowIcon, HelpIcon } from '@milesight/shared/src/components'; +import { Tooltip } from '@/components'; import useFlowStore from '../../store'; import { useCommonFormItems, @@ -115,13 +116,27 @@ const ConfigPanel = () => { >
- + {nodeConfig?.icon} - {!!nodeConfig?.labelIntlKey && ( + + {/* {!!nodeConfig?.labelIntlKey && ( {getIntlText(nodeConfig.labelIntlKey)} - )} + )} */} {nodeConfig?.testable && ( diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less index 73e08e94..b732ba7c 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/style.less @@ -37,9 +37,10 @@ } .title { - .text-size(@font-size-xxl); - + flex: 1; + width: 0; font-weight: @font-weight-bold; + .text-size(@font-size-xxl); } } @@ -78,7 +79,11 @@ font-weight: @font-weight-bold; .text-size(@font-size-lg); - .@{prefix}ButtonBase-root { + .@{prefix}-tooltip { + display: flex; + } + + .@{mui-prefix}ButtonBase-root { padding: @padding-xxs; } } diff --git a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx index 6d67c80c..af9dfd98 100644 --- a/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/node-container/index.tsx @@ -5,6 +5,7 @@ import cls from 'classnames'; import { Menu, MenuItem } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; import { CheckCircleIcon, ErrorIcon, LoopIcon } from '@milesight/shared/src/components'; +import { Tooltip } from '@/components'; import { basicNodeConfigs } from '@/pages/workflow/config'; import useFlowStore from '../../store'; import Handle from '../handle'; @@ -87,7 +88,8 @@ const NodeContainer: React.FC = ({ children, }) => { const { getIntlText } = useI18n(); - const status = nodeProps?.data?.$status as WorkflowNodeStatus; + const status = nodeProps?.data?.$status; + const nodeName = nodeProps?.data?.nodeName; // ---------- ContextMenu ---------- const [searchParams] = useSearchParams(); @@ -211,7 +213,7 @@ const NodeContainer: React.FC = ({ {handles?.map((handle, index) => {handle})}
@@ -285,7 +287,11 @@ const NodeContainer: React.FC = ({ > {icon} - {title} + {!!status && ( Date: Tue, 31 Dec 2024 13:07:49 +0800 Subject: [PATCH 137/172] feat: add placeholder and popperWidth prop --- .../components/entity-popper/index.tsx | 13 +++++++++++++ .../components/entity-select/components/index.tsx | 1 + .../src/components/entity-select/entitySelect.tsx | 8 +++++--- .../entity-select/hooks/useContextValue.tsx | 15 ++++++++++++--- apps/web/src/components/entity-select/index.tsx | 2 ++ apps/web/src/components/entity-select/types.d.ts | 10 ++++++++-- 6 files changed, 41 insertions(+), 8 deletions(-) create mode 100644 apps/web/src/components/entity-select/components/entity-popper/index.tsx diff --git a/apps/web/src/components/entity-select/components/entity-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-popper/index.tsx new file mode 100644 index 00000000..51541b63 --- /dev/null +++ b/apps/web/src/components/entity-select/components/entity-popper/index.tsx @@ -0,0 +1,13 @@ +import React, { useContext } from 'react'; +import cls from 'classnames'; +import { Popper, PopperProps } from '@mui/material'; +import { EntityContext } from '../../context'; + +export default React.memo(({ className, style, ...props }: PopperProps) => { + const { popperWidth } = useContext(EntityContext); + const newStyle = popperWidth ? { ...style, width: popperWidth } : style; + + return ( + + ); +}); diff --git a/apps/web/src/components/entity-select/components/index.tsx b/apps/web/src/components/entity-select/components/index.tsx index 48476171..b3ae51f0 100644 --- a/apps/web/src/components/entity-select/components/index.tsx +++ b/apps/web/src/components/entity-select/components/index.tsx @@ -3,3 +3,4 @@ export { default as EntityList } from './entity-list'; export { default as EntityOption } from './entity-option'; export { default as EntityMenu } from './entity-menu'; export { default as EntityMenuPopper } from './entity-menu-popper'; +export { default as EntityPopper } from './entity-popper'; diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx index 67e56f7f..0b3215c4 100644 --- a/apps/web/src/components/entity-select/entitySelect.tsx +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -1,7 +1,7 @@ import React, { useState, useContext, useCallback, useMemo } from 'react'; import { Autocomplete, TextField, AutocompleteProps, Chip } from '@mui/material'; import Tooltip from '@/components/tooltip'; -import { EntityList, EntityPaper } from './components'; +import { EntityList, EntityPaper, EntityPopper } from './components'; import { EntityContext } from './context'; import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; @@ -36,6 +36,7 @@ const EntitySelect = < onSearch, error, helperText, + placeholder, ...rest } = props; const { options } = useContext>( @@ -101,9 +102,10 @@ const EntitySelect = < helperText={helperText} label={label} required={required} + placeholder={placeholder} /> ), - [error, helperText, label, required], + [error, helperText, label, required, placeholder], ); /** @@ -140,7 +142,7 @@ const EntitySelect = < ); // Memoize custom slots and slot props to optimize rendering - const slots = useMemo(() => ({ paper: EntityPaper }), []); + const slots = useMemo(() => ({ paper: EntityPaper, popper: EntityPopper }), []); const slotProps = useMemo(() => ({ listbox: { component: EntityList } }), []); return ( diff --git a/apps/web/src/components/entity-select/hooks/useContextValue.tsx b/apps/web/src/components/entity-select/hooks/useContextValue.tsx index 401c2a6f..3fda8127 100644 --- a/apps/web/src/components/entity-select/hooks/useContextValue.tsx +++ b/apps/web/src/components/entity-select/hooks/useContextValue.tsx @@ -15,12 +15,12 @@ export const useContextValue = < >( props: Pick< EntitySelectProps, - 'value' | 'multiple' | 'onChange' | 'maxCount' | 'filterOption' + 'value' | 'multiple' | 'onChange' | 'maxCount' | 'filterOption' | 'popperWidth' > & { entityList: EntityData[]; }, ) => { - const { value, multiple, maxCount, entityList, onChange, filterOption } = props; + const { value, multiple, maxCount, entityList, popperWidth, onChange, filterOption } = props; const [tabType, setTabType] = useState('entity'); const { options: _options } = useOptions({ tabType, entityList }); @@ -37,6 +37,7 @@ export const useContextValue = < /** context value */ const contextValue = useMemo(() => { const result: EntitySelectContext = { + popperWidth, maxCount, options: (options as V[]) || [], tabType, @@ -47,7 +48,15 @@ export const useContextValue = < }; return result; - }, [maxCount, onEntityChange, options, selectedDeviceMap, selectedEntityMap, tabType]); + }, [ + popperWidth, + maxCount, + onEntityChange, + options, + selectedDeviceMap, + selectedEntityMap, + tabType, + ]); return { contextValue, diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index 28bc62c2..aefce7a4 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -25,6 +25,7 @@ const EntitySelectApp = < entityValueType, entityAccessMod, excludeChildren, + popperWidth, filterOption, } = props; const maxCount = multiple ? _maxCount : void 0; @@ -47,6 +48,7 @@ const EntitySelectApp = < onChange, entityList, filterOption, + popperWidth, }); return ( diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index 3c7fae1e..bb8200ed 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -50,7 +50,7 @@ export interface EntitySelectProps< Value extends EntitySelectValueType = EntitySelectValueType, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, -> extends Pick, +> extends Pick, Omit< AutocompleteProps, 'renderInput' | 'options' @@ -76,13 +76,19 @@ export interface EntitySelectProps< /** * Callback function to filter options */ - filterOption?: (options: EntitySelectOption[]) => boolean; + filterOption?: (options: EntitySelectOption[]) => EntitySelectOption[]; + /** + * custom popper width + */ + popperWidth?: number; } /** * Context for the EntitySelect component. */ export interface EntitySelectContext { + /** custom popper width */ + popperWidth?: number; /** * maximum number of items that can be selected * @description This prop is only used when `multiple` is true From 0c07cee9a693587b0327ea4d198b653b13c2d801 Mon Sep 17 00:00:00 2001 From: Nian Date: Tue, 31 Dec 2024 14:26:08 +0800 Subject: [PATCH 138/172] refactor: optimize frequent data updates --- .../service-param-assign-input/index.tsx | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx index f4cc406c..10ee5bea 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/service-param-assign-input/index.tsx @@ -1,4 +1,4 @@ -import { useCallback, useLayoutEffect, useMemo } from 'react'; +import { useCallback, useLayoutEffect, useMemo, useRef } from 'react'; import { isEqual } from 'lodash-es'; import { useControllableValue, useDebounceFn, useDynamicList } from 'ahooks'; import { Divider, IconButton, Tooltip } from '@mui/material'; @@ -51,7 +51,9 @@ const ServiceParamAssignInput: React.FC = ({ const [innerValue, setInnerValue] = useControllableValue(props); const { list, replace, resetList } = useDynamicList([]); - const { run: handlerChange } = useDebounceFn( + const preValueRef = useRef(); + + const handlerChange = useCallback( async (serviceEntity?: ApiKey) => { setInnerValue(pre => ({ ...pre, serviceEntity })); if (serviceEntity) { @@ -82,7 +84,7 @@ const ServiceParamAssignInput: React.FC = ({ resetList([]); } }, - { wait: 100 }, + [innerValue], ); const renderInputParam = useMemo(() => { if (list.length) { @@ -121,12 +123,16 @@ const ServiceParamAssignInput: React.FC = ({ [], ); useLayoutEffect(() => { - const serviceParams = innerValue?.serviceParams ?? {}; - if (isEqual(serviceParams, transformParams(list))) return; + if (isEqual(preValueRef.current, innerValue)) return; + preValueRef.current = innerValue; handlerChange(innerValue?.serviceEntity); }, [innerValue]); useLayoutEffect(() => { - setInnerValue(pre => ({ ...pre, serviceParams: transformParams(list) })); + setInnerValue(pre => { + const newValue = { ...pre, serviceParams: transformParams(list) }; + preValueRef.current = newValue; + return newValue; + }); }, [list]); return (
From 100132979c2a9bdcee8d5ea5e79d430552c347b0 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Tue, 31 Dec 2024 14:32:41 +0800 Subject: [PATCH 139/172] feat: Use pass-through props instead of react context --- .../components/entity-list/index.tsx | 38 ++++++++++---- .../components/entity-menu-popper/index.tsx | 25 ++++++--- .../components/entity-option/index.tsx | 10 ++-- .../components/entity-paper/index.tsx | 9 ++-- .../components/entity-popper/index.tsx | 8 +-- .../src/components/entity-select/context.ts | 4 -- .../components/entity-select/entitySelect.tsx | 52 +++++++++++++------ .../src/components/entity-select/index.tsx | 31 ++++++----- .../src/components/entity-select/types.d.ts | 20 +++++++ 9 files changed, 132 insertions(+), 65 deletions(-) delete mode 100644 apps/web/src/components/entity-select/context.ts diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx index f72e62c1..514f8073 100644 --- a/apps/web/src/components/entity-select/components/entity-list/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -1,18 +1,34 @@ -import React, { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useMemoizedFn } from 'ahooks'; import { useVirtualList } from '@milesight/shared/src/hooks'; import EntityMenuPopper from '../entity-menu-popper'; -import { EntityContext } from '../../context'; import EntityOption from '../entity-option'; import EntityMenu from '../entity-menu'; -import type { EntitySelectOption } from '../../types'; - -interface IProps { +import type { EntitySelectInnerProps, EntitySelectOption } from '../../types'; + +interface IProps + extends Pick< + EntitySelectInnerProps, + | 'tabType' + | 'options' + | 'maxCount' + | 'selectedEntityMap' + | 'selectedDeviceMap' + | 'onEntityChange' + > { children: React.ReactNode; } -export default React.memo(({ children: _children, ...props }: IProps) => { - const { tabType, options, selectedEntityMap, selectedDeviceMap, maxCount } = - useContext(EntityContext); +export default React.memo((props: IProps) => { + const { + children: _children, + tabType, + options, + selectedEntityMap, + selectedDeviceMap, + maxCount, + onEntityChange, + ...rest + } = props; const containerRef = useRef(null); const listRef = useRef(null); @@ -85,7 +101,7 @@ export default React.memo(({ children: _children, ...props }: IProps) => { ); return ( <> -
+
{(virtualList || []).map(({ data: option }) => { const { value } = option || {}; @@ -102,6 +118,7 @@ export default React.memo(({ children: _children, ...props }: IProps) => { option={option} selected={selected} disabled={disabled} + onEntityChange={onEntityChange} /> ); } @@ -124,6 +141,9 @@ export default React.memo(({ children: _children, ...props }: IProps) => { anchorEl={menuAnchorEl} menuList={menuList} modifiers={modifiers} + maxCount={maxCount} + selectedEntityMap={selectedEntityMap} + onEntityChange={onEntityChange} /> ); diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx index 4da456b4..63081688 100644 --- a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx @@ -1,17 +1,26 @@ -import React, { useContext, useEffect, useMemo, useRef } from 'react'; +import React, { useEffect, useMemo, useRef } from 'react'; import { Paper, Popper, PopperProps } from '@mui/material'; import { useVirtualList } from 'ahooks'; import EntityOption from '../entity-option'; -import { EntityContext } from '../../context'; -import type { EntitySelectOption } from '../../types'; +import type { EntitySelectInnerProps, EntitySelectOption } from '../../types'; import './style.less'; -export interface IProps extends PopperProps { +interface IProps + extends PopperProps, + Pick< + EntitySelectInnerProps, + | 'tabType' + | 'options' + | 'maxCount' + | 'selectedEntityMap' + | 'selectedDeviceMap' + | 'onEntityChange' + > { menuList: EntitySelectOption[]; } const LINE_HEIGHT = 58; -export default React.memo(({ menuList, ...props }: IProps) => { - const { selectedEntityMap, maxCount } = useContext(EntityContext); +export default React.memo((props: IProps) => { + const { menuList, maxCount, selectedEntityMap, onEntityChange, ...rest } = props; const containerRef = useRef(null); const listRef = useRef(null); @@ -24,11 +33,12 @@ export default React.memo(({ menuList, ...props }: IProps) => { }); useEffect(() => { scrollTo(0); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [menuList]); const selectedCount = useMemo(() => selectedEntityMap.size, [selectedEntityMap]); return ( - +
{ option={menu} selected={selected} disabled={disabled} + onEntityChange={onEntityChange} /> ); })} diff --git a/apps/web/src/components/entity-select/components/entity-option/index.tsx b/apps/web/src/components/entity-select/components/entity-option/index.tsx index 39fc308e..bc939f73 100644 --- a/apps/web/src/components/entity-select/components/entity-option/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-option/index.tsx @@ -1,19 +1,17 @@ -import React, { useCallback, useContext } from 'react'; +import React, { useCallback } from 'react'; import cls from 'classnames'; import { CheckIcon } from '@milesight/shared/src/components'; import Tooltip from '@/components/tooltip'; -import { EntityContext } from '../../context'; -import type { EntitySelectOption } from '../../types'; +import type { EntitySelectInnerProps, EntitySelectOption } from '../../types'; import './style.less'; -interface IProps { +interface IProps extends Pick { option: EntitySelectOption; selected?: boolean; disabled?: boolean; } export default React.memo((props: IProps) => { - const { onEntityChange } = useContext(EntityContext); - const { option, selected, disabled } = props; + const { option, selected, disabled, onEntityChange } = props; const { label, description } = option || {}; /** When an entity item is selected/canceled */ diff --git a/apps/web/src/components/entity-select/components/entity-paper/index.tsx b/apps/web/src/components/entity-select/components/entity-paper/index.tsx index 19df1a6f..fbebf8b7 100644 --- a/apps/web/src/components/entity-select/components/entity-paper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-paper/index.tsx @@ -1,12 +1,11 @@ -import React, { Fragment, useCallback, useContext } from 'react'; +import React, { Fragment, useCallback } from 'react'; import { Paper, PaperProps, Tab, Tabs } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; -import { EntityContext } from '../../context'; import { TabOptions } from '../../constant'; -import type { TabType } from '../../types'; +import type { EntitySelectInnerProps, TabType } from '../../types'; -export default React.memo(({ children, ...props }: PaperProps) => { - const { tabType, setTabType } = useContext(EntityContext); +type IProps = PaperProps & Pick; +export default React.memo(({ children, tabType, setTabType, ...props }: IProps) => { const { getIntlText } = useI18n(); /** handle tab change */ diff --git a/apps/web/src/components/entity-select/components/entity-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-popper/index.tsx index 51541b63..01cce7c9 100644 --- a/apps/web/src/components/entity-select/components/entity-popper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-popper/index.tsx @@ -1,10 +1,10 @@ -import React, { useContext } from 'react'; +import React from 'react'; import cls from 'classnames'; import { Popper, PopperProps } from '@mui/material'; -import { EntityContext } from '../../context'; +import type { EntitySelectInnerProps } from '../../types'; -export default React.memo(({ className, style, ...props }: PopperProps) => { - const { popperWidth } = useContext(EntityContext); +type IProps = PopperProps & Pick; +export default React.memo(({ className, style, popperWidth, ...props }: IProps) => { const newStyle = popperWidth ? { ...style, width: popperWidth } : style; return ( diff --git a/apps/web/src/components/entity-select/context.ts b/apps/web/src/components/entity-select/context.ts deleted file mode 100644 index 0908c1fd..00000000 --- a/apps/web/src/components/entity-select/context.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createContext } from 'react'; -import type { EntitySelectContext } from './types'; - -export const EntityContext = createContext({} as EntitySelectContext); diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx index 0b3215c4..d1a2b0f4 100644 --- a/apps/web/src/components/entity-select/entitySelect.tsx +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -1,9 +1,8 @@ -import React, { useState, useContext, useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; import { Autocomplete, TextField, AutocompleteProps, Chip } from '@mui/material'; import Tooltip from '@/components/tooltip'; import { EntityList, EntityPaper, EntityPopper } from './components'; -import { EntityContext } from './context'; -import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; +import type { EntitySelectInnerProps, EntitySelectValueType } from './types'; /** * EntitySelect Component @@ -12,16 +11,11 @@ const EntitySelect = < Value extends EntitySelectValueType = EntitySelectValueType, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, - Props extends EntitySelectProps = EntitySelectProps< - Value, - Multiple, - DisableClearable - >, EntityAutocompleteProps extends Required< AutocompleteProps > = Required>, >( - props: Props, + props: EntitySelectInnerProps, ) => { const { label, @@ -37,11 +31,16 @@ const EntitySelect = < error, helperText, placeholder, + tabType, + setTabType, + popperWidth, + options, + selectedEntityMap, + selectedDeviceMap, + maxCount, + onEntityChange, ...rest } = props; - const { options } = useContext>( - EntityContext as unknown as React.Context>, - ); // State to manage the open/close status of the select menu const [anchorEl, setAnchorEl] = useState(null); @@ -142,9 +141,31 @@ const EntitySelect = < ); // Memoize custom slots and slot props to optimize rendering - const slots = useMemo(() => ({ paper: EntityPaper, popper: EntityPopper }), []); - const slotProps = useMemo(() => ({ listbox: { component: EntityList } }), []); - + const slotProps = useMemo( + () => ({ + listbox: { + component: EntityList, + tabType, + options, + maxCount, + selectedEntityMap, + selectedDeviceMap, + onEntityChange, + }, + paper: { component: EntityPaper, tabType, setTabType }, + popper: { component: EntityPopper, popperWidth }, + }), + [ + popperWidth, + setTabType, + tabType, + options, + selectedEntityMap, + selectedDeviceMap, + maxCount, + onEntityChange, + ], + ); return ( {...rest} @@ -159,7 +180,6 @@ const EntitySelect = < getOptionLabel={getOptionLabel} renderTags={renderTags} renderInput={renderInput} - slots={slots} slotProps={slotProps} loading={loading} filterOptions={filterOptions} diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index aefce7a4..8da14b0e 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -2,8 +2,7 @@ import React from 'react'; import { useControllableValue } from 'ahooks'; import EntitySelect from './entitySelect'; import { useContextValue, useSourceData } from './hooks'; -import { EntityContext } from './context'; -import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from './types'; +import type { EntitySelectProps, EntitySelectValueType } from './types'; const EntitySelectApp = < Value extends EntitySelectValueType = EntitySelectValueType, @@ -25,7 +24,6 @@ const EntitySelectApp = < entityValueType, entityAccessMod, excludeChildren, - popperWidth, filterOption, } = props; const maxCount = multiple ? _maxCount : void 0; @@ -48,20 +46,25 @@ const EntitySelectApp = < onChange, entityList, filterOption, - popperWidth, }); + const { tabType, setTabType, selectedDeviceMap, selectedEntityMap, onEntityChange, options } = + contextValue || {}; return ( - - - {...props} - multiple={multiple} - value={value} - onChange={onChange} - loading={loading || sourceLoading} - onSearch={onSearch} - /> - + + {...props} + multiple={multiple} + value={value} + onChange={onChange} + loading={loading || sourceLoading} + onSearch={onSearch} + tabType={tabType} + setTabType={setTabType} + onEntityChange={onEntityChange} + options={options} + selectedDeviceMap={selectedDeviceMap} + selectedEntityMap={selectedEntityMap} + /> ); }; export default React.memo(EntitySelectApp) as unknown as typeof EntitySelectApp; diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index bb8200ed..f4446f0d 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -83,8 +83,28 @@ export interface EntitySelectProps< popperWidth?: number; } +export interface EntitySelectInnerProps< + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, +> extends EntitySelectProps { + /** The current tab type */ + tabType: TabType; + /** Function to set the tab type */ + setTabType: (tabType: TabType) => void; + /** Available options for selection */ + options: Value[]; + /** The map of selected entities */ + selectedEntityMap: Map; + /** The map of selected devices */ + selectedDeviceMap: Map; + /** Callback function when an entity is selected or changed */ + onEntityChange: (selectedItem: EntitySelectValue) => void; +} + /** * Context for the EntitySelect component. + * @deprecated This type is deprecated and will be removed in the future. */ export interface EntitySelectContext { /** custom popper width */ From 5792e099b1f6b4ddcdeef755223f6c32fe5f4099 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Tue, 31 Dec 2024 14:57:12 +0800 Subject: [PATCH 140/172] feat: Resolve editor component css variable conflicts and Format the data in the action log component --- .../code-editor/components/editor/style.less | 4 +++ .../web/src/components/code-editor/editor.tsx | 4 ++- .../code-editor/hooks/useCssVariable.tsx | 19 ++++++++---- .../action-log/components/header/index.tsx | 3 +- .../workflow/components/action-log/index.tsx | 29 +++++++++++++++++-- 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/code-editor/components/editor/style.less b/apps/web/src/components/code-editor/components/editor/style.less index e9e7b954..fb593e22 100644 --- a/apps/web/src/components/code-editor/components/editor/style.less +++ b/apps/web/src/components/code-editor/components/editor/style.less @@ -6,6 +6,10 @@ outline: none; } + .cm-content { + padding-right: @padding-xs; + } + .cm-editor { outline: none; diff --git a/apps/web/src/components/code-editor/editor.tsx b/apps/web/src/components/code-editor/editor.tsx index 263e4bbd..2b7d2be4 100644 --- a/apps/web/src/components/code-editor/editor.tsx +++ b/apps/web/src/components/code-editor/editor.tsx @@ -21,10 +21,12 @@ export const CodeEditor = forwardRef((props, ref) = fontSize, ...rest } = props; + const editorRef = useRef(null); const editorInstanceRef = useRef(null); const { handleBlur, handleFocus, themeBgColor } = useCssVariable({ onBlur, onFocus, + editorRef, }); const { editorTheme } = useEditorTheme({ fontSize }); @@ -44,7 +46,7 @@ export const CodeEditor = forwardRef((props, ref) = useImperativeHandle(ref, () => handlers); return ( -
+
{CustomHeader !== null && ( ; -export const useCssVariable = ({ onFocus, onBlur }: IProps) => { - const updateCssVariable = useCallback((themeColor: string) => { - const root = document.documentElement; - root.style.setProperty(THEME_MAIN_BG_COLOR, themeColor); - }, []); +type IProps = Pick & { + editorRef: React.RefObject; +}; +export const useCssVariable = ({ onFocus, onBlur, editorRef }: IProps) => { + const updateCssVariable = useCallback( + (themeColor: string) => { + const root = editorRef.current; + if (!root) return; + + root.style.setProperty(THEME_MAIN_BG_COLOR, themeColor); + }, + [editorRef], + ); const handleFocus: Required['onFocus'] = useCallback( async (...params) => { diff --git a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx index 7488f243..0c6835de 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/components/header/index.tsx @@ -1,4 +1,5 @@ import React, { useMemo } from 'react'; +import { isNil } from 'lodash-es'; import cls from 'classnames'; import { useI18n } from '@milesight/shared/src/hooks'; import { Tooltip } from '@/components'; @@ -49,7 +50,7 @@ export default React.memo(({ data }: IProps) => {
- {timeCost && ( + {!isNil(timeCost) && (
{`${timeCost}${getIntlText('common.label.ms')}`}
)}
{statusIcon}
diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index d5dbf0f5..bb7d9f94 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Fragment } from 'react/jsx-runtime'; import { Alert } from '@mui/material'; import { useI18n } from '@milesight/shared/src/hooks'; @@ -9,6 +9,14 @@ import { ALPHABET_LIST } from './constant'; import type { ActionLogProps, WorkflowNestNode } from './types'; import './style.less'; +function safeJsonParse(str: string) { + try { + const result = JSON.parse(str); + return JSON.stringify(result, null, 2); + } catch (e) { + return str; + } +} /** * get alphabet index * @example getAlphabetIndex(0) => A @@ -23,11 +31,26 @@ const getAlphabetIndex = (index: number) => { } return ALPHABET_LIST[index]; }; - export default React.memo(({ traceData, workflowData }: ActionLogProps) => { const { getIntlText } = useI18n(); const { treeData } = useNestedData({ workflowData, traceData }); + const renderTreeData = useMemo(() => { + return treeData.map(data => { + const { attrs } = data || {}; + const { input, output } = attrs || {}; + + return { + ...(data || {}), + attrs: { + ...attrs, + input: safeJsonParse(input!), + output: safeJsonParse(output!), + }, + }; + }); + }, [treeData]); + /** recursive rendering */ const renderAccordion = (treeData: WorkflowNestNode[], level: number = 0) => { // Existence of parallel branches @@ -100,5 +123,5 @@ export default React.memo(({ traceData, workflowData }: ActionLogProps) => { }); }; - return
{renderAccordion(treeData)}
; + return
{renderAccordion(renderTreeData)}
; }); From c564579f11f2c81842fbf6d0688a71053a0f14a4 Mon Sep 17 00:00:00 2001 From: jimco Date: Tue, 31 Dec 2024 17:53:09 +0800 Subject: [PATCH 141/172] feat: prevent navigate jump when node form data change --- .../components/test-drawer/index.tsx | 16 ++++++++-- .../editor/components/log-panel/index.tsx | 6 +++- .../views/editor/hooks/useValidate.ts | 29 ++++++++++-------- .../src/pages/workflow/views/editor/index.tsx | 30 +++++++++++++++---- 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx index 4f9022bd..e7d913b3 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/test-drawer/index.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react'; import cls from 'classnames'; -import { pick, isEmpty } from 'lodash-es'; +import { pick, isEmpty, isObject } from 'lodash-es'; import { useRequest } from 'ahooks'; import { Backdrop, @@ -50,6 +50,18 @@ const statusDefaultMsgKey: Record< }, }; +const safeJSONStringify = (value?: string, space: number = 2) => { + if (!value) return ''; + let result = ''; + try { + result = JSON.stringify(JSON.parse(value), null, space); + } catch (e) { + result = value; + } + + return result; +}; + const TestDrawer: React.FC = ({ node, open, onClose }) => { const { getIntlText } = useI18n(); const { getNode } = useReactFlow(); @@ -222,7 +234,7 @@ const TestDrawer: React.FC = ({ node, open, onClose }) => { editable={false} editorLang="json" title={getIntlText('common.label.output')} - value={testResult.output} + value={safeJSONStringify(testResult.output)} /> )}
diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index 7a01714a..351656d7 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -246,7 +246,11 @@ const LogPanel: React.FC = ({ designMode }) => { >
- + {title} diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 496af42a..0f6037e0 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -75,6 +75,19 @@ const useValidate = () => { }; }; + const entitiesChecker: Record = { + checkRequired( + value: NonNullable['entities'], + fieldName?: string, + ) { + if (value?.length && value.some(item => !isEmpty(item))) { + return true; + } + const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); + return message; + }, + }; + // Note: The `checkRequired` name is fixed and cannot be modified const result: Record> = { nodeName: { @@ -98,19 +111,6 @@ const useValidate = () => { return message; }, }, - // Check listener.entities, select.entities - entities: { - checkRequired( - value: NonNullable['entities'], - fieldName?: string, - ) { - if (value?.length && value.some(item => !isEmpty(item))) { - return true; - } - const message = getIntlText(ErrorIntlKey.required, { 1: fieldName }); - return message; - }, - }, // Check code.inputArguments and webhook.inputArguments inputArguments: { checkMaxLength( @@ -134,6 +134,9 @@ const useValidate = () => { return true; }, }, + // Check listener.entities, select.entities + 'listener.entities': entitiesChecker, + 'select.entities': entitiesChecker, 'trigger.entityConfigs': { checkRequired( value?: NonNullable['entityConfigs'], diff --git a/apps/web/src/pages/workflow/views/editor/index.tsx b/apps/web/src/pages/workflow/views/editor/index.tsx index e4d830e8..79cb72dc 100644 --- a/apps/web/src/pages/workflow/views/editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/index.tsx @@ -1,7 +1,7 @@ import { memo, useState, useCallback, useEffect } from 'react'; import { useSearchParams, useNavigate, useLocation } from 'react-router-dom'; import { useRequest } from 'ahooks'; -import { omitBy, merge, isEmpty, cloneDeep } from 'lodash-es'; +import { omitBy, merge, isEmpty, isEqual, cloneDeep } from 'lodash-es'; import { ReactFlow, Background, @@ -114,9 +114,30 @@ const WorkflowEditor = () => { const [helperLineVertical, setHelperLineVertical] = useState(undefined); const handleNodesChange = useCallback( (changes: NodeChange[]) => { - if (changes.some(({ type }) => ['add', 'remove', 'position'].includes(type))) { - setIsPreventLeave(true); - } + const isPreventLeave = changes.some(change => { + switch (change.type) { + case 'add': + case 'remove': + case 'position': { + return true; + } + case 'replace': { + const { id: nodeId, data: nodeData } = change.item; + const { nodeName, nodeRemark } = nodeData; + const node = nodes.find(item => item.id === nodeId); + const isEq = + node?.data?.nodeName === nodeName && + node?.data?.nodeRemark === nodeRemark && + isEqual(node?.data.parameters, nodeData.parameters); + + return !isEq; + } + default: { + return false; + } + } + }); + if (isPreventLeave) setIsPreventLeave(true); // reset the helper lines (clear existing lines, if any) setHelperLineHorizontal(undefined); @@ -146,7 +167,6 @@ const WorkflowEditor = () => { ); // ---------- Fetch Nodes Config ---------- - const { loading: nodeConfigLoading } = useRequest( async () => { const [error, resp] = await awaitWrap(workflowAPI.getFlowNodes()); From 5f88eed5fe12756014aee6b730b7ce7cc6ad737d Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 1 Jan 2025 22:42:48 +0800 Subject: [PATCH 142/172] feat: Entity select component supports custom 'value' types --- .../components/entity-menu-popper/index.tsx | 10 +-- .../components/entity-select/entitySelect.tsx | 47 +++++++---- .../components/entity-select/hooks/index.tsx | 1 - .../entity-select/hooks/useContextValue.tsx | 64 --------------- .../entity-select/hooks/useOptions.tsx | 67 ++++++++++++---- .../entity-select/hooks/useSelectValue.tsx | 78 +++++++++++++------ .../src/components/entity-select/index.tsx | 31 ++++---- .../src/components/entity-select/types.d.ts | 69 ++++++++-------- 8 files changed, 192 insertions(+), 175 deletions(-) delete mode 100644 apps/web/src/components/entity-select/hooks/useContextValue.tsx diff --git a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx index 63081688..dc3e0fdb 100644 --- a/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-menu-popper/index.tsx @@ -7,15 +7,7 @@ import './style.less'; interface IProps extends PopperProps, - Pick< - EntitySelectInnerProps, - | 'tabType' - | 'options' - | 'maxCount' - | 'selectedEntityMap' - | 'selectedDeviceMap' - | 'onEntityChange' - > { + Pick { menuList: EntitySelectOption[]; } const LINE_HEIGHT = 58; diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx index d1a2b0f4..503b8563 100644 --- a/apps/web/src/components/entity-select/entitySelect.tsx +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -1,8 +1,8 @@ import React, { useState, useCallback, useMemo } from 'react'; -import { Autocomplete, TextField, AutocompleteProps, Chip } from '@mui/material'; -import Tooltip from '@/components/tooltip'; +import { Autocomplete, TextField, AutocompleteProps, Chip, Tooltip } from '@mui/material'; import { EntityList, EntityPaper, EntityPopper } from './components'; -import type { EntitySelectInnerProps, EntitySelectValueType } from './types'; +import { useSelectValue } from './hooks'; +import type { EntitySelectComponentProps, EntitySelectValueType } from './types'; /** * EntitySelect Component @@ -15,7 +15,7 @@ const EntitySelect = < AutocompleteProps > = Required>, >( - props: EntitySelectInnerProps, + props: EntitySelectComponentProps, ) => { const { label, @@ -31,14 +31,13 @@ const EntitySelect = < error, helperText, placeholder, + popperWidth, + maxCount, tabType, setTabType, - popperWidth, options, - selectedEntityMap, - selectedDeviceMap, - maxCount, - onEntityChange, + entityOptionMap, + getOptionValue, ...rest } = props; @@ -46,6 +45,18 @@ const EntitySelect = < const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); + const { selectedEntityMap, selectedDeviceMap, onEntityChange } = useSelectValue< + Value, + Multiple, + DisableClearable + >({ + value, + multiple, + onChange, + entityOptionMap, + getOptionValue, + }); + /** * Handles opening of the select menu. */ @@ -114,7 +125,10 @@ const EntitySelect = < (value, getTagProps) => { if (!multiple) return; - return value.map((option, index) => { + const valueList = value + .map(v => entityOptionMap.get(getOptionValue(v))!) + .filter(Boolean); + return valueList.map((option, index) => { const { key, ...tagProps } = getTagProps({ index }); return ( @@ -124,15 +138,20 @@ const EntitySelect = < ); }); }, - [multiple], + [entityOptionMap, getOptionValue, multiple], ); /** * Gets the display text for the input box based on the selected option. */ const getOptionLabel = useCallback( - option => option?.label || '', - [], + option => { + const value = getOptionValue(option); + + const currentOption = entityOptionMap.get(value); + return currentOption?.label || ''; + }, + [entityOptionMap, getOptionValue], ); const filterOptions = useCallback( @@ -171,7 +190,7 @@ const EntitySelect = < {...rest} open={open} value={value} - options={options} + options={options as Value[]} multiple={multiple} onChange={handleChange} onOpen={handleSelectOpen} diff --git a/apps/web/src/components/entity-select/hooks/index.tsx b/apps/web/src/components/entity-select/hooks/index.tsx index 62336a6f..4ff73198 100644 --- a/apps/web/src/components/entity-select/hooks/index.tsx +++ b/apps/web/src/components/entity-select/hooks/index.tsx @@ -1,4 +1,3 @@ export { useOptions } from './useOptions'; export { useSelectValue } from './useSelectValue'; -export { useContextValue } from './useContextValue'; export { useSourceData } from './useSourceData'; diff --git a/apps/web/src/components/entity-select/hooks/useContextValue.tsx b/apps/web/src/components/entity-select/hooks/useContextValue.tsx deleted file mode 100644 index 3fda8127..00000000 --- a/apps/web/src/components/entity-select/hooks/useContextValue.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import { useMemo, useState } from 'react'; -import { useOptions } from './useOptions'; -import { useSelectValue } from './useSelectValue'; -import type { - TabType, - EntitySelectContext, - EntitySelectProps, - EntitySelectValueType, -} from '../types'; - -export const useContextValue = < - V extends EntitySelectValueType = EntitySelectValueType, - M extends boolean | undefined = false, - D extends boolean | undefined = false, ->( - props: Pick< - EntitySelectProps, - 'value' | 'multiple' | 'onChange' | 'maxCount' | 'filterOption' | 'popperWidth' - > & { - entityList: EntityData[]; - }, -) => { - const { value, multiple, maxCount, entityList, popperWidth, onChange, filterOption } = props; - - const [tabType, setTabType] = useState('entity'); - const { options: _options } = useOptions({ tabType, entityList }); - const options = useMemo( - () => (filterOption ? filterOption(_options) : _options), - [_options, filterOption], - ); - const { selectedEntityMap, selectedDeviceMap, onEntityChange } = useSelectValue({ - value, - multiple, - onChange, - }); - - /** context value */ - const contextValue = useMemo(() => { - const result: EntitySelectContext = { - popperWidth, - maxCount, - options: (options as V[]) || [], - tabType, - setTabType, - selectedEntityMap, - selectedDeviceMap, - onEntityChange, - }; - - return result; - }, [ - popperWidth, - maxCount, - onEntityChange, - options, - selectedDeviceMap, - selectedEntityMap, - tabType, - ]); - - return { - contextValue, - }; -}; diff --git a/apps/web/src/components/entity-select/hooks/useOptions.tsx b/apps/web/src/components/entity-select/hooks/useOptions.tsx index ebea5fa1..102ed109 100644 --- a/apps/web/src/components/entity-select/hooks/useOptions.tsx +++ b/apps/web/src/components/entity-select/hooks/useOptions.tsx @@ -2,13 +2,32 @@ import { useCallback, useMemo } from 'react'; import { useI18n } from '@milesight/shared/src/hooks'; import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; import { safeJsonParse } from '../helper'; -import type { EntitySelectOption, TabType } from '../types'; +import type { + EntitySelectComponentProps, + EntitySelectOption, + EntitySelectValueType, + EntityValueType, + TabType, +} from '../types'; -interface IProps { +interface IProps< + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, +> { tabType: TabType; entityList: EntityData[]; + filterOption?: EntitySelectComponentProps['filterOption']; } -export const useOptions = ({ tabType, entityList }: IProps) => { +export const useOptions = < + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, +>({ + tabType, + entityList, + filterOption, +}: IProps) => { const { getIntlText } = useI18n(); /** Get description text */ @@ -43,7 +62,7 @@ export const useOptions = ({ tabType, entityList }: IProps) => { ) as EntityValueAttributeType; // Create an entity item for the select option - const entityItem: EntitySelectOption = { + const entityItem: EntitySelectOption = { value: entityId, label: entityName, valueType: entityValueType, @@ -58,20 +77,28 @@ export const useOptions = ({ tabType, entityList }: IProps) => { [getDescription], ); + const optionList = useMemo(() => { + const result = (entityList || []).map(entity => { + // Convert entity data to camel case + const entityData = objectToCamelCase(entity || {}); + + return getOptionValue(entityData); + }); + return filterOption ? filterOption(result) : result; + }, [entityList, filterOption, getOptionValue]); + /** Get entity drop-down options and device drop-down options */ const { entityOptions, deviceOptions } = useMemo(() => { - const { entityOptions, deviceMap } = (entityList || []).reduce<{ - deviceMap: Map; - entityOptions: EntitySelectOption[]; + const { entityOptions, deviceMap } = (optionList || []).reduce<{ + deviceMap: Map>; + entityOptions: EntitySelectOption[]; }>( (prev, entity) => { const { entityOptions, deviceMap } = prev; - // Convert entity data to camel case - const entityData = objectToCamelCase(entity || {}); - const { deviceName, entityType, entityKey } = entityData || {}; - const entityItem = getOptionValue(entityData); - entityOptions.push(entityItem); + const { rawData } = entity || {}; + const { deviceName, entityType, entityKey } = rawData! || {}; + entityOptions.push(entity); // Create or update device group let deviceGroup = deviceMap.get(deviceName); @@ -83,7 +110,7 @@ export const useOptions = ({ tabType, entityList }: IProps) => { }; } deviceGroup.children?.push({ - ...entityItem, + ...entity, description: getDescription(entityType, entityKey), }); deviceMap.set(deviceName, deviceGroup); @@ -94,7 +121,7 @@ export const useOptions = ({ tabType, entityList }: IProps) => { }; }, { - deviceMap: new Map(), + deviceMap: new Map>(), entityOptions: [], }, ); @@ -103,7 +130,7 @@ export const useOptions = ({ tabType, entityList }: IProps) => { entityOptions, deviceOptions: Array.from(deviceMap.values()), }; - }, [entityList, getDescription, getOptionValue]); + }, [optionList, getDescription]); /** Get the corresponding drop-down rendering options based on `tabType` */ const options = useMemo( @@ -111,7 +138,17 @@ export const useOptions = ({ tabType, entityList }: IProps) => { [deviceOptions, entityOptions, tabType], ); + const entityOptionMap = useMemo(() => { + return (optionList || []).reduce((acc, option) => { + const { value } = option; + + acc.set(value, option); + return acc; + }, new Map>()); + }, [optionList]); + return { options, + entityOptionMap, }; }; diff --git a/apps/web/src/components/entity-select/hooks/useSelectValue.tsx b/apps/web/src/components/entity-select/hooks/useSelectValue.tsx index fd7c7ccd..5c3b17be 100644 --- a/apps/web/src/components/entity-select/hooks/useSelectValue.tsx +++ b/apps/web/src/components/entity-select/hooks/useSelectValue.tsx @@ -1,59 +1,93 @@ import { useCallback, useMemo } from 'react'; -import type { EntitySelectContext, EntitySelectProps, EntitySelectValueType } from '../types'; +import type { + EntitySelectInnerProps, + EntitySelectComponentProps, + EntitySelectValueType, + SelectedParameterType, + EntitySelectOption, + EntitySelectProps, + EntityValueType, +} from '../types'; +interface IProps< + V extends EntitySelectValueType = EntitySelectValueType, + M extends boolean | undefined = false, + D extends boolean | undefined = false, +> extends Pick< + EntitySelectComponentProps, + 'value' | 'multiple' | 'onChange' | 'getOptionValue' + > { + entityOptionMap: Map>; +} export const useSelectValue = < V extends EntitySelectValueType = EntitySelectValueType, M extends boolean | undefined = false, D extends boolean | undefined = false, >( - props: Pick, 'value' | 'multiple' | 'onChange'>, -) => { - const { value, multiple, onChange } = props; + props: IProps, +): SelectedParameterType => { + const { entityOptionMap, value, multiple, onChange, getOptionValue } = props; + + const valueList = useMemo(() => { + const valueList = (Array.isArray(value) ? value : [value]) as unknown as V[]; + + return valueList.map(getOptionValue); + }, [getOptionValue, value]); /** selected Value to entity map */ const selectedEntityMap = useMemo(() => { - if (!value) return new Map(); + if (!valueList?.length) + return new Map>(); - const valueList: V[] = Array.isArray(value) ? value : ([value] as unknown as V[]); - return valueList.reduce>((acc, curr) => { - acc.set(curr.value, curr); - return acc; - }, new Map()); - }, [value]); + return valueList.reduce>>( + (acc, curr) => { + const option = entityOptionMap.get(curr); + if (!option) return acc; + + acc.set(curr, option); + return acc; + }, + new Map(), + ); + }, [entityOptionMap, valueList]); /** selected device to entity map */ const selectedDeviceMap = useMemo(() => { - if (!value) return new Map(); + if (!valueList?.length) return new Map[]>(); + + return valueList.reduce[]>>((acc, curr) => { + const option = entityOptionMap.get(curr); + if (!option) return acc; - const valueList: V[] = Array.isArray(value) ? value : ([value] as unknown as V[]); - return valueList.reduce>((acc, curr) => { - const { rawData } = curr || {}; + const { rawData } = option || {}; const { deviceName = '' } = rawData || {}; const deviceList = acc.get(deviceName) || []; - deviceList.push(curr); + deviceList.push(option); acc.set(deviceName, deviceList); return acc; }, new Map()); - }, [value]); + }, [entityOptionMap, valueList]); /** Select/cancel entity selection callback */ - const onEntityChange = useCallback( + const onEntityChange = useCallback['onEntityChange']>( selectedItem => { const { value } = selectedItem || {}; if (!value) return; if (!multiple) { + const onSingleChange = onChange as EntitySelectProps['onChange']; // single select - onChange?.(selectedItem); + onSingleChange?.(selectedItem); return; } + const realValue = value; // multiple select - if (selectedEntityMap.has(value)) { - selectedEntityMap.delete(value); + if (selectedEntityMap.has(realValue)) { + selectedEntityMap.delete(realValue); } else { - selectedEntityMap.set(value, selectedItem!); + selectedEntityMap.set(realValue, selectedItem!); } const onMultipleChange = onChange as EntitySelectProps['onChange']; onMultipleChange?.(Array.from(selectedEntityMap.values())); diff --git a/apps/web/src/components/entity-select/index.tsx b/apps/web/src/components/entity-select/index.tsx index 8da14b0e..269ffbb9 100644 --- a/apps/web/src/components/entity-select/index.tsx +++ b/apps/web/src/components/entity-select/index.tsx @@ -1,8 +1,8 @@ -import React from 'react'; +import React, { useMemo, useState } from 'react'; import { useControllableValue } from 'ahooks'; import EntitySelect from './entitySelect'; -import { useContextValue, useSourceData } from './hooks'; -import type { EntitySelectProps, EntitySelectValueType } from './types'; +import { useOptions, useSourceData } from './hooks'; +import type { EntitySelectProps, EntitySelectValueType, TabType } from './types'; const EntitySelectApp = < Value extends EntitySelectValueType = EntitySelectValueType, @@ -24,9 +24,15 @@ const EntitySelectApp = < entityValueType, entityAccessMod, excludeChildren, + getOptionValue: _getOptionValue, filterOption, } = props; - const maxCount = multiple ? _maxCount : void 0; + + const maxCount = useMemo(() => (multiple ? _maxCount : void 0), [_maxCount, multiple]); + const getOptionValue = useMemo( + () => _getOptionValue || ((value: EntitySelectValueType) => value), + [_getOptionValue], + ); const { entityList, @@ -39,16 +45,13 @@ const EntitySelectApp = < excludeChildren, }); const [value, onChange] = useControllableValue['value']>(props); - const { contextValue } = useContextValue({ - value, - maxCount, - multiple, - onChange, + + const [tabType, setTabType] = useState('entity'); + const { options, entityOptionMap } = useOptions({ + tabType, entityList, filterOption, }); - const { tabType, setTabType, selectedDeviceMap, selectedEntityMap, onEntityChange, options } = - contextValue || {}; return ( @@ -58,12 +61,12 @@ const EntitySelectApp = < onChange={onChange} loading={loading || sourceLoading} onSearch={onSearch} + maxCount={maxCount} + getOptionValue={getOptionValue} tabType={tabType} setTabType={setTabType} - onEntityChange={onEntityChange} options={options} - selectedDeviceMap={selectedDeviceMap} - selectedEntityMap={selectedEntityMap} + entityOptionMap={entityOptionMap} /> ); }; diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index f4446f0d..b741827d 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -27,7 +27,11 @@ export interface EntitySelectOption children?: EntitySelectOption[]; } -export type EntitySelectValueType = EntitySelectOption; +/** + * For now, no restrictions will be imposed. + * First, specify the `ElementSelectValueType` as any to facilitate type restrictions on the `value` in the future + */ +export type EntitySelectValueType = any; /** * Type to represent the value of the EntitySelect component based on its configuration. @@ -61,7 +65,9 @@ export interface EntitySelectProps< /** The current value of the select */ value?: EntitySelectValue; /** Callback function when the value changes */ - onChange?: (value: EntitySelectValue) => void; + onChange?: ( + value: EntitySelectValue, Multiple, DisableClearable>, + ) => void; /** Whether the clear button is disabled */ disableClearable?: DisableClearable; /** @@ -76,54 +82,45 @@ export interface EntitySelectProps< /** * Callback function to filter options */ - filterOption?: (options: EntitySelectOption[]) => EntitySelectOption[]; + filterOption?: ( + options: EntitySelectOption[], + ) => EntitySelectOption[]; + /** + * Get the unique value of the current value + */ + getOptionValue?: (option: Value) => EntityValueType; /** * custom popper width */ popperWidth?: number; } -export interface EntitySelectInnerProps< +export interface EntitySelectComponentProps< Value extends EntitySelectValueType = EntitySelectValueType, Multiple extends boolean | undefined = false, DisableClearable extends boolean | undefined = false, > extends EntitySelectProps { - /** The current tab type */ tabType: TabType; - /** Function to set the tab type */ - setTabType: (tabType: TabType) => void; - /** Available options for selection */ - options: Value[]; - /** The map of selected entities */ - selectedEntityMap: Map; - /** The map of selected devices */ - selectedDeviceMap: Map; - /** Callback function when an entity is selected or changed */ - onEntityChange: (selectedItem: EntitySelectValue) => void; + setTabType: (value: TabType) => void; + options: EntitySelectOption[]; + entityOptionMap: Map>; + onChange: (value: EntitySelectValue) => void; + getOptionValue: Required< + EntitySelectProps + >['getOptionValue']; } -/** - * Context for the EntitySelect component. - * @deprecated This type is deprecated and will be removed in the future. - */ -export interface EntitySelectContext { - /** custom popper width */ - popperWidth?: number; - /** - * maximum number of items that can be selected - * @description This prop is only used when `multiple` is true - */ - maxCount?: number; - /** Available options for selection */ - options: V[]; - /** The current tab type */ - tabType: TabType; - /** Function to set the tab type */ - setTabType: (tabType: TabType) => void; +export interface SelectedParameterType { /** The map of selected entities */ - selectedEntityMap: Map; + selectedEntityMap: Map>; /** The map of selected devices */ - selectedDeviceMap: Map; + selectedDeviceMap: Map[]>; /** Callback function when an entity is selected or changed */ - onEntityChange: (selectedItem: EntitySelectValue) => void; + onEntityChange: (selectedItem: EntitySelectOption) => void; } + +export type EntitySelectInnerProps< + Value extends EntitySelectValueType = EntitySelectValueType, + Multiple extends boolean | undefined = false, + DisableClearable extends boolean | undefined = false, +> = SelectedParameterType & EntitySelectComponentProps; From e362d65b4df1f937e8a74a919eef442996b55d63 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Wed, 1 Jan 2025 23:47:26 +0800 Subject: [PATCH 143/172] feat: use entity store to cache data --- .../components/entity-select/entitySelect.tsx | 5 +- .../entity-select/hooks/useSourceData.tsx | 80 +++++++++---------- .../web/src/components/entity-select/store.ts | 75 +++++++++++++++++ 3 files changed, 116 insertions(+), 44 deletions(-) create mode 100644 apps/web/src/components/entity-select/store.ts diff --git a/apps/web/src/components/entity-select/entitySelect.tsx b/apps/web/src/components/entity-select/entitySelect.tsx index 503b8563..3c837830 100644 --- a/apps/web/src/components/entity-select/entitySelect.tsx +++ b/apps/web/src/components/entity-select/entitySelect.tsx @@ -94,7 +94,10 @@ const EntitySelect = < */ const handleInputChange = useCallback( (event, value, reason) => { - onSearch?.(reason === 'input' ? value : ''); + if (reason === 'input') { + onSearch?.(value); + return; + } onInputChange?.(event, value, reason); }, diff --git a/apps/web/src/components/entity-select/hooks/useSourceData.tsx b/apps/web/src/components/entity-select/hooks/useSourceData.tsx index 8a1cb0aa..8237c3af 100644 --- a/apps/web/src/components/entity-select/hooks/useSourceData.tsx +++ b/apps/web/src/components/entity-select/hooks/useSourceData.tsx @@ -1,6 +1,7 @@ -import { useCallback, useMemo } from 'react'; -import { useRequest } from 'ahooks'; -import { awaitWrap, entityAPI, getResponseData, isRequestSuccess } from '@/services/http'; +import { useCallback, useEffect, useState } from 'react'; +import { useShallow } from 'zustand/react/shallow'; +import { useMemoizedFn, useRequest } from 'ahooks'; +import useEntityStore from '../store'; import type { EntitySelectProps } from '../types'; export const useSourceData = ( @@ -10,56 +11,49 @@ export const useSourceData = ( >, ) => { const { entityType, entityAccessMod, excludeChildren, entityValueType } = props; - const { - run: getEntityList, - data: entityList, - loading, - } = useRequest( - async (keyword?: string) => { - const [error, resp] = await awaitWrap( - entityAPI.getList({ - page_number: 1, - page_size: 9999, - entity_type: entityType, - entity_access_mod: entityAccessMod, - exclude_children: excludeChildren, - entity_value_type: entityValueType, - keyword, - }), - ); - if (error || !isRequestSuccess(resp)) return; + const [keyword, setKeyword] = useState(''); + const { entityList, getEntityList, entityLoading, initEntityList, status } = useEntityStore( + useShallow(state => ({ + entityList: state.entityList, + entityLoading: state.entityLoading, + getEntityList: state.getEntityList, + initEntityList: state.initEntityList, + status: state.status, + })), + ); - const data = getResponseData(resp)!; - return data?.content || []; - }, - { - refreshDeps: [ - entityType, + const init = useMemoizedFn(() => { + initEntityList({ entityType, entityAccessMod, excludeChildren, entityValueType }); + }); + useEffect(() => { + if (status !== 'ready') return; + + init(); + }, [init, initEntityList, status]); + + const { data: searchEntityList, loading: searchLoading } = useRequest( + async () => { + if (!keyword) return; + + const params = { + keyword, entityType, entityAccessMod, excludeChildren, entityValueType, - ], - debounceWait: 300, + }; + return getEntityList(params); }, + { refreshDeps: [keyword], debounceWait: 300 }, ); - /** search entity list by keyword */ - const onSearch = useCallback( - async (keyword: string) => { - if (!keyword) { - getEntityList(); - return; - } - - getEntityList(keyword); - }, - [getEntityList], - ); + const onSearch = useCallback((keyword: string) => { + setKeyword(keyword); + }, []); return { - entityList: useMemo(() => entityList || [], [entityList]), - loading, + entityList: keyword ? searchEntityList : entityList, + loading: keyword ? searchLoading : entityLoading, onSearch, }; }; diff --git a/apps/web/src/components/entity-select/store.ts b/apps/web/src/components/entity-select/store.ts new file mode 100644 index 00000000..fdafecd6 --- /dev/null +++ b/apps/web/src/components/entity-select/store.ts @@ -0,0 +1,75 @@ +import { create } from 'zustand'; +import { immer } from 'zustand/middleware/immer'; +import { + entityAPI, + awaitWrap, + getResponseData, + isRequestSuccess, + type EntityAPISchema, +} from '@/services/http'; + +type EntityFilterParams = Omit< + ObjectToCamelCase, + 'pageSize' | 'pageNumber' +>; +interface EntityStore { + status: 'ready' | 'loading' | 'finish'; + + entityList: EntityAPISchema['getList']['response']['content']; + + entityLoading: boolean; + + getEntityList: (params?: EntityFilterParams) => Promise; + + initEntityList: (params?: EntityFilterParams) => void; +} + +export default create( + immer((set, get) => ({ + entityList: [], + + status: 'ready', + + entityLoading: false, + + initEntityList: async params => { + set({ entityLoading: true, status: 'loading' }); + + const entityList = await get().getEntityList(params); + + set({ entityList, entityLoading: false, status: 'finish' }); + }, + + getEntityList: async params => { + const { + keyword, + entityType: type, + entityAccessMod: accessMode, + excludeChildren, + entityValueType: valueType, + } = params || {}; + + const entityType = type && (Array.isArray(type) ? type : [type]); + const entityValueType = + valueType && (Array.isArray(valueType) ? valueType : [valueType]); + const entityAccessMode = + accessMode && (Array.isArray(accessMode) ? accessMode : [accessMode]); + const [error, resp] = await awaitWrap( + entityAPI.getList({ + keyword, + entity_type: entityType, + entity_value_type: entityValueType, + entity_access_mod: entityAccessMode, + exclude_children: excludeChildren, + page_number: 1, + page_size: 99999, + }), + ); + + if (error || !isRequestSuccess(resp)) return []; + const data = getResponseData(resp); + + return data?.content || []; + }, + })), +); From b516bacf8de78601091642d60992870c527f103a Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 01:03:40 +0800 Subject: [PATCH 144/172] feat: Adjust entity select popper position calculation --- .../components/entity-list/index.tsx | 29 +++++++------------ .../entity-select/hooks/useSourceData.tsx | 4 +-- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/apps/web/src/components/entity-select/components/entity-list/index.tsx b/apps/web/src/components/entity-select/components/entity-list/index.tsx index 514f8073..bae8a79c 100644 --- a/apps/web/src/components/entity-select/components/entity-list/index.tsx +++ b/apps/web/src/components/entity-select/components/entity-list/index.tsx @@ -32,13 +32,20 @@ export default React.memo((props: IProps) => { const containerRef = useRef(null); const listRef = useRef(null); + const [popperDiff, setPopperDiff] = useState([0, 0]); const [menuList, setMenuList] = useState([]); const [menuAnchorEl, setMenuAnchorEl] = useState(null); const open = Boolean(menuAnchorEl); /** When clicked, the pop-up window opens */ const handleClick = useCallback((event: React.MouseEvent, option: EntitySelectOption) => { - setMenuAnchorEl(event.currentTarget as HTMLDivElement); + const containerNode = containerRef.current!; + const popperNode = event.currentTarget; + + const diffH = + popperNode.getBoundingClientRect().top - containerNode.getBoundingClientRect().top; + setPopperDiff([diffH, 0]); + setMenuAnchorEl(containerNode); setTimeout(() => { const { children } = option || {}; @@ -55,22 +62,6 @@ export default React.memo((props: IProps) => { }); const selectedCount = useMemo(() => selectedEntityMap.size, [selectedEntityMap]); - // get scroll bar width - const scrollbarWidth = useMemo(() => { - if (!menuAnchorEl) return 0; - - const listNode = menuAnchorEl?.parentElement?.parentElement; - const popNode = listNode?.parentElement; - if (!listNode || !popNode) return 0; - - const { scrollHeight, clientHeight, clientWidth } = listNode || {}; - const hasScroll = scrollHeight > clientHeight; - if (!hasScroll) return 0; - - const { clientWidth: popClientWidth } = popNode || {}; - return popClientWidth - clientWidth || 0; - }, [menuAnchorEl]); - // when scroll, clear menu list const handleScroll = useMemoizedFn(() => { if (!menuList?.length) return; @@ -93,11 +84,11 @@ export default React.memo((props: IProps) => { { name: 'offset', options: { - offset: [0, scrollbarWidth], + offset: popperDiff, }, }, ], - [scrollbarWidth], + [popperDiff], ); return ( <> diff --git a/apps/web/src/components/entity-select/hooks/useSourceData.tsx b/apps/web/src/components/entity-select/hooks/useSourceData.tsx index 8237c3af..229f749a 100644 --- a/apps/web/src/components/entity-select/hooks/useSourceData.tsx +++ b/apps/web/src/components/entity-select/hooks/useSourceData.tsx @@ -33,7 +33,7 @@ export const useSourceData = ( const { data: searchEntityList, loading: searchLoading } = useRequest( async () => { - if (!keyword) return; + if (!keyword) return []; const params = { keyword, @@ -52,7 +52,7 @@ export const useSourceData = ( }, []); return { - entityList: keyword ? searchEntityList : entityList, + entityList: keyword ? searchEntityList! : entityList, loading: keyword ? searchLoading : entityLoading, onSearch, }; From 0fdb57bd1153f337d9dc5db6791b81e9f238e19d Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 2 Jan 2025 09:37:38 +0800 Subject: [PATCH 145/172] fix: reset node form data when the node is blur --- .../workflow/views/editor/components/config-panel/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx index e3d1a021..1ee389de 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/index.tsx @@ -67,6 +67,7 @@ const ConfigPanel = () => { // Backfill form data useEffect(() => { if (!selectedNode) { + reset(); formDataInit.current = false; return; } From 376e63cfdf1d48ea97c0159b56cfe240a4eeb6ef Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 11:06:48 +0800 Subject: [PATCH 146/172] feat: apply entity select component --- .../entity-select/hooks/useSourceData.tsx | 38 ++- .../src/components/entity-select/types.d.ts | 4 +- .../components/entity-assign-select/index.tsx | 1 + .../components/entity-select/index.tsx | 227 +++--------------- .../plugin/components/entity-select/index.tsx | 126 +++------- .../components/entity-select/style.less | 6 - .../components/multi-entity-select/index.tsx | 150 +++--------- .../components/multi-entity-select/style.less | 12 - 8 files changed, 141 insertions(+), 423 deletions(-) delete mode 100644 apps/web/src/plugin/components/entity-select/style.less delete mode 100644 apps/web/src/plugin/components/multi-entity-select/style.less diff --git a/apps/web/src/components/entity-select/hooks/useSourceData.tsx b/apps/web/src/components/entity-select/hooks/useSourceData.tsx index 229f749a..f9673e33 100644 --- a/apps/web/src/components/entity-select/hooks/useSourceData.tsx +++ b/apps/web/src/components/entity-select/hooks/useSourceData.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { useShallow } from 'zustand/react/shallow'; import { useMemoizedFn, useRequest } from 'ahooks'; import useEntityStore from '../store'; @@ -23,7 +23,7 @@ export const useSourceData = ( ); const init = useMemoizedFn(() => { - initEntityList({ entityType, entityAccessMod, excludeChildren, entityValueType }); + initEntityList(); }); useEffect(() => { if (status !== 'ready') return; @@ -51,9 +51,39 @@ export const useSourceData = ( setKeyword(keyword); }, []); + const hasFilterParams = useMemo( + () => !!(entityType || entityAccessMod || excludeChildren || entityValueType), + [entityType, entityAccessMod, excludeChildren, entityValueType], + ); + const { data: sourceEntityList, loading: sourceSearchLoading } = useRequest( + async () => { + if (!hasFilterParams) return; + + const params = { + entityType, + entityAccessMod, + excludeChildren, + entityValueType, + }; + return getEntityList(params); + }, + { + refreshDeps: [entityType, entityAccessMod, excludeChildren, entityValueType], + }, + ); + + const fetchEntityList = useMemo(() => { + if (!keyword) return hasFilterParams ? sourceEntityList : entityList; + return searchEntityList; + }, [entityList, hasFilterParams, keyword, searchEntityList, sourceEntityList]); + const fetchLoading = useMemo(() => { + if (!keyword) return hasFilterParams ? sourceSearchLoading : entityLoading; + return searchLoading; + }, [entityLoading, hasFilterParams, keyword, searchLoading, sourceSearchLoading]); + return { - entityList: keyword ? searchEntityList! : entityList, - loading: keyword ? searchLoading : entityLoading, + entityList: fetchEntityList, + loading: fetchLoading, onSearch, }; }; diff --git a/apps/web/src/components/entity-select/types.d.ts b/apps/web/src/components/entity-select/types.d.ts index b741827d..aee792a1 100644 --- a/apps/web/src/components/entity-select/types.d.ts +++ b/apps/web/src/components/entity-select/types.d.ts @@ -43,9 +43,9 @@ export type EntitySelectValue = Multiple exte : Value | null; /** Interface filter parameter */ -type FilterParameters = Omit< +type FilterParameters = Pick< ObjectToCamelCase, - 'pageSize' | 'pageNumber' | 'keyword' + 'entityType' | 'entityAccessMod' | 'excludeChildren' | 'entityValueType' >; /** * Props for the EntitySelect component. diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx index 6f432d89..e2300ea9 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/entity-assign-select/index.tsx @@ -69,6 +69,7 @@ const EntityAssignSelect: React.FC = ({ onChange={value => { replace(index, [`${value || ''}`, item?.[1] || '']); }} + popperWidth={400} /> ; +export interface EntitySelectProps + extends Omit, 'onChange'> { /** * API Filter Model */ @@ -54,194 +42,43 @@ export interface EntitySelectProps { onChange?: (value: EntitySelectValueType) => void; } -/** - * Virtual List Component - */ -const Listbox = forwardRef>( - ({ children, ...props }, ref) => { - const containerRef = useRef(null); - const wrapperRef = useRef(null); - const list = useMemo(() => { - const result: React.ReactElement[] = []; - (children as React.ReactElement[]).forEach( - ( - item: React.ReactElement & { - children?: React.ReactElement[]; - }, - ) => { - result.push(item); - result.push(...(item.children || [])); - }, - ); - - return result; - }, [children]); - const [virtualList] = useVirtualList(list, { - containerTarget: containerRef, - wrapperTarget: wrapperRef, - itemHeight: 58, - overscan: 5, - }); - - return ( -
-
-
- {virtualList.map(({ data }) => data)} -
-
-
- ); - }, -); - /** * Entity Select Component * * Note: This is a basic component, use in EntityListeningNode, ServiceNode, EntitySelectNode */ -const EntitySelect: React.FC = ({ - label, - required, - disabled, - placeholder, - filterModel, - ...props -}) => { - const { getIntlText } = useI18n(); +const EntitySelect: React.FC = ({ filterModel, ...props }) => { const [value, setValue] = useControllableValue(props); - // ---------- Entity List Data ---------- - const { entityList, getEntityList } = useConfigPanelStore( - useStoreShallow(['entityList', 'getEntityList']), - ); - const filteredEntityList = useMemo(() => { - if (!filterModel) return entityList; - - return entityList?.filter(entity => { - let { type, valueType, accessMode } = filterModel; - - type = type && (Array.isArray(type) ? type : [type]); - valueType = valueType && (Array.isArray(valueType) ? valueType : [valueType]); - accessMode = accessMode && (Array.isArray(accessMode) ? accessMode : [accessMode]); - - return ( - (!type || type.includes(entity.entity_type)) && - (!valueType || valueType.includes(entity.entity_value_type)) && - (!accessMode || accessMode.includes(entity.entity_access_mod)) - ); - }); - }, [entityList, filterModel]); - - // ---------- Search Entity Data ---------- - const [searchedEntityList, setSearchedEntityList] = useState(); - - // ---------- Autocomplete Render ---------- - const options = useMemo(() => { - const list = searchedEntityList || filteredEntityList || []; - const result: EntitySelectOptionType[] = list.map(item => { - return { - label: item.entity_name, - value: item.entity_key, - valueType: item.entity_value_type, - description: [item.device_name, item.integration_name].filter(Boolean).join(', '), - rawData: item, - }; - }); - - return result; - }, [filteredEntityList, searchedEntityList]); - - const renderInput = useCallback( - (params: AutocompleteRenderInputParams) => { - return ( - - ); + const handleChange = useCallback['onChange']>( + option => { + setValue(option?.value); }, - [label, required, placeholder, getIntlText], + [setValue], ); - const renderOption = useCallback< - NonNullable< - AutocompleteProps< - EntitySelectOptionType, - undefined, - undefined, - undefined - >['renderOption'] - > - >((optionProps, option) => { - const { label, value, description } = option || {}; - - return ( - -
-
- -
-
- -
-
-
- ); - }, []); - - const { run: handleInputChange } = useDebounceFn< - NonNullable< - AutocompleteProps< - EntitySelectOptionType, - undefined, - undefined, - undefined - >['onInputChange'] - > - >( - async (_, keyword, reason) => { - if (keyword && reason === 'input') { - const list = await getEntityList({ ...filterModel, keyword }); - setSearchedEntityList(list); - return; - } + const filterModelValue = useMemo(() => { + const { type, valueType, accessMode, excludeChildren } = filterModel || {}; - setSearchedEntityList(undefined); - }, - { wait: 300 }, - ); + const filterType = type && (Array.isArray(type) ? type : [type]); + const filterValueType = valueType && (Array.isArray(valueType) ? valueType : [valueType]); + const filterAccessMode = + accessMode && (Array.isArray(accessMode) ? accessMode : [accessMode]); - // ---------- Autocomplete Inner Value ---------- - const [innerValue, setInnerValue] = useState(null); - useLayoutEffect(() => { - const option = options.find(item => item.value === value); - setInnerValue(option || null); - }, [value, options]); + return { + type: filterType, + valueType: filterValueType, + accessMode: filterAccessMode, + excludeChildren, + }; + }, [filterModel]); return ( - - value={innerValue} - disabled={disabled} - options={options} - renderInput={renderInput} - renderOption={renderOption} - filterOptions={x => x} - isOptionEqualToValue={(option, currentVal) => option.value === currentVal.value} - slotProps={{ - listbox: { component: Listbox }, - popper: { - sx: { minWidth: 300 }, - placement: 'bottom-end', - }, - }} - popupIcon={} - onInputChange={handleInputChange} - onChange={(_, data) => { - setValue(data?.value); - }} + ); }; diff --git a/apps/web/src/plugin/components/entity-select/index.tsx b/apps/web/src/plugin/components/entity-select/index.tsx index 6f380db8..25bc7278 100644 --- a/apps/web/src/plugin/components/entity-select/index.tsx +++ b/apps/web/src/plugin/components/entity-select/index.tsx @@ -1,106 +1,50 @@ -import { useMemo } from 'react'; -import { MenuItem, Autocomplete, TextField, Tooltip } from '@mui/material'; -import type { AutocompleteProps } from '@mui/material'; -import { useI18n } from '@milesight/shared/src/hooks'; -import { useEntitySelectOptions } from '../../hooks'; - -import './style.less'; - -type EntitySelectProps = AutocompleteProps & - EntitySelectCommonProps; - +import React, { useCallback, useMemo } from 'react'; +import { EntitySelect, type EntitySelectProps } from '@/components'; +import { filterEntityMap } from '@/plugin/utils'; + +type SingleEntitySelectProps = EntitySelectProps; +interface IProps extends SingleEntitySelectProps { + entityValueTypes: SingleEntitySelectProps['entityValueType']; + entityAccessMods: SingleEntitySelectProps['entityAccessMod']; + entityExcludeChildren: SingleEntitySelectProps['excludeChildren']; + customFilterEntity: keyof typeof filterEntityMap; +} /** - * 实体选择下拉框组件(单选) + * Entity Select drop-down component (single option) */ -const EntitySelect = (props: EntitySelectProps) => { +export default React.memo((props: IProps) => { const { - value, - onChange, entityType, + entityValueType, entityValueTypes, + entityAccessMod, entityAccessMods, entityExcludeChildren, customFilterEntity, ...restProps } = props; - const { getIntlText } = useI18n(); - - /** - * 动态从服务器获取 options - */ - const { - loading, - getEntityOptions, - options = [], - } = useEntitySelectOptions({ - entityType, - entityValueTypes, - entityAccessMods, - entityExcludeChildren, - customFilterEntity, - }); - - const renderOption: EntitySelectProps['renderOption'] = (optionProps, option) => { - const { label, value, description } = option || {}; - - return ( - -
-
- {label} -
-
- {description} -
-
-
- ); - }; - - const renderTooltipTitle = useMemo(() => { - return value?.description || undefined; - }, [value]); - + const filterOption = useMemo( + () => + Reflect.get( + filterEntityMap, + customFilterEntity, + ) as SingleEntitySelectProps['filterOption'], + [customFilterEntity], + ); + const getOptionValue = useCallback['getOptionValue']>( + option => option?.value, + [], + ); return ( - onChange(option)} - options={options} - renderInput={params => ( - - - {(restProps as any).error.message} -
- ) : ( - '' - ) - } - label={getIntlText('common.label.entity')} - /> - - )} - renderOption={renderOption} - getOptionLabel={option => option?.label || ''} - loading={loading} - filterOptions={options => options} - onInputChange={(_, keyword, reason) => { - if (reason !== 'input') { - getEntityOptions(); - return; - } - - getEntityOptions(keyword); - }} - isOptionEqualToValue={(option, currentVal) => option.value === currentVal.value} /> ); -}; - -export default EntitySelect; +}); diff --git a/apps/web/src/plugin/components/entity-select/style.less b/apps/web/src/plugin/components/entity-select/style.less deleted file mode 100644 index fe68ae6c..00000000 --- a/apps/web/src/plugin/components/entity-select/style.less +++ /dev/null @@ -1,6 +0,0 @@ -.ms-entity-select-item { - &__description { - color: var(--text-color-tertiary); - .text-size(@font-size-md); - } -} diff --git a/apps/web/src/plugin/components/multi-entity-select/index.tsx b/apps/web/src/plugin/components/multi-entity-select/index.tsx index bbc9f25e..9db7e507 100644 --- a/apps/web/src/plugin/components/multi-entity-select/index.tsx +++ b/apps/web/src/plugin/components/multi-entity-select/index.tsx @@ -1,129 +1,53 @@ -import { Autocomplete, TextField, Checkbox, Tooltip, Chip } from '@mui/material'; - -import type { AutocompleteProps } from '@mui/material'; - -import { useI18n } from '@milesight/shared/src/hooks'; -import { useEntitySelectOptions } from '../../hooks'; - -import './style.less'; - -type EntitySelectProps = AutocompleteProps & - EntitySelectCommonProps; - +import React, { useCallback, useMemo } from 'react'; +import { EntitySelect, type EntitySelectProps } from '@/components'; +import { filterEntityMap } from '@/plugin/utils'; + +type MultipleEntitySelectProps = EntitySelectProps; +interface IProps extends MultipleEntitySelectProps { + entityValueTypes: MultipleEntitySelectProps['entityValueType']; + entityAccessMods: MultipleEntitySelectProps['entityAccessMod']; + entityExcludeChildren: MultipleEntitySelectProps['excludeChildren']; + customFilterEntity: keyof typeof filterEntityMap; +} /** - * 实体选择下拉框组件(多选) + * Entity Select drop-down components (multiple selections) */ -const MultiEntitySelect = (props: EntitySelectProps) => { +export default React.memo((props: IProps) => { const { - value, - onChange, entityType, + entityValueType, entityValueTypes, + entityAccessMod, entityAccessMods, entityExcludeChildren, - customFilterEntity, - /** - * 默认最大可选择 5 个 - */ maxCount = 5, + customFilterEntity, ...restProps } = props; - const { getIntlText } = useI18n(); - - /** - * 动态从服务器获取 options - */ - const { - loading, - getEntityOptions, - options = [], - } = useEntitySelectOptions({ - entityType, - entityValueTypes, - entityAccessMods, - entityExcludeChildren, - customFilterEntity, - }); - - const renderOption: EntitySelectProps['renderOption'] = (optionProps, option, { selected }) => { - const { label, value, description } = option || {}; + const filterOption = useMemo( + () => + Reflect.get( + filterEntityMap, + customFilterEntity, + ) as MultipleEntitySelectProps['filterOption'], + [customFilterEntity], + ); - return ( -
  • -
    - -
    -
    - {label} -
    -
    - {description} -
    -
    -
    -
  • - ); - }; + const getOptionValue = useCallback< + Required>['getOptionValue'] + >(option => option?.value, []); return ( - onChange(option)} - options={options} - getOptionDisabled={option => { - const currentValue = value || []; - /** - * 默认实体最多只能选择 5 个 - */ - if (currentValue.length < maxCount) { - return false; - } - - return currentValue.every(e => e.value !== option.value); - }} - renderInput={params => ( - - {(restProps as any).error.message} -
    - ) : ( - '' - ) - } - /> - )} - renderOption={renderOption} - getOptionLabel={option => option?.label || ''} - loading={loading} - filterOptions={options => options} - onInputChange={(_, keyword, reason) => { - if (reason !== 'input') { - getEntityOptions(); - return; - } - - getEntityOptions(keyword); - }} - isOptionEqualToValue={(option, currentVal) => option.value === currentVal.value} - renderTags={(value, getTagProps) => - value.map((option, index) => { - const { key, ...tagProps } = getTagProps({ index }); - return ( - - - - ); - }) - } + maxCount={maxCount} + entityType={entityType} + entityValueType={entityValueTypes || entityValueType} + entityAccessMod={entityAccessMods || entityAccessMod} + excludeChildren={entityExcludeChildren} + filterOption={filterOption} + getOptionValue={getOptionValue} + {...restProps} /> ); -}; - -export default MultiEntitySelect; +}); diff --git a/apps/web/src/plugin/components/multi-entity-select/style.less b/apps/web/src/plugin/components/multi-entity-select/style.less deleted file mode 100644 index e35d6dea..00000000 --- a/apps/web/src/plugin/components/multi-entity-select/style.less +++ /dev/null @@ -1,12 +0,0 @@ -.ms-multi-entity-select { - display: flex; - align-items: flex-start; - justify-content: flex-start; -} - -.ms-entity-select-item { - &__description { - color: var(--text-color-tertiary); - .text-size(@font-size-md); - } -} From 01af9efa3f3d4c48a55070aaf3917a1b8a2e860b Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 11:12:57 +0800 Subject: [PATCH 147/172] feat: code editor component remove the default value of onChange --- .../components/config-panel/components/code-editor/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx index f6f47bde..de053234 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -22,7 +22,7 @@ const CodeEditor: React.FC = ({ value, onChange }) => { /** Actual form change callbacks */ const handleChange = useCallback( (data: Partial) => { - const { language = DEFAULT_LANGUAGE, expression = '' } = data; + const { language, expression = '' } = data; const result = { language: language ?? value?.language, From a32262e3cd4de33d74e769e8959ebd183131c33f Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 11:24:08 +0800 Subject: [PATCH 148/172] feat: Added version query workflow interface in log popup --- .../log-modal/components/log-right-bar/hooks/useLogDetail.tsx | 3 +-- .../workflow/components/log-modal/hooks/useRenderList.tsx | 3 ++- apps/web/src/pages/workflow/components/log-modal/types.d.ts | 4 ++++ apps/web/src/services/http/workflow.ts | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx index e40adeea..0b99c9c0 100644 --- a/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/components/log-right-bar/hooks/useLogDetail.tsx @@ -38,8 +38,7 @@ export const useLogDetail = ({ const { id } = data || {}; const resp = await workflowAPI.getFlowDesign({ id, - // TODO Get the specified version - version: '', + version: activeItem.version, }); const designData = getResponseData(resp)?.design_data; diff --git a/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx b/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx index c273cec9..b0cdef36 100644 --- a/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx +++ b/apps/web/src/pages/workflow/components/log-modal/hooks/useRenderList.tsx @@ -27,11 +27,12 @@ export const useRenderList = ({ getLogList, containerRef, listRef }: IProps) => const getRenderLogList = useCallback( (logList: LogListPageType['content']): LogItemProps[] => { return (logList || []).map(item => { - const { start_time: startTime, status, id } = item || {}; + const { start_time: startTime, status, id, version } = item || {}; return { id, status, title: startTime ? getTimeFormat(startTime, 'fullDateTimeSecondFormat') : '', + version, }; }); }, diff --git a/apps/web/src/pages/workflow/components/log-modal/types.d.ts b/apps/web/src/pages/workflow/components/log-modal/types.d.ts index 438ff383..8a39fcc5 100644 --- a/apps/web/src/pages/workflow/components/log-modal/types.d.ts +++ b/apps/web/src/pages/workflow/components/log-modal/types.d.ts @@ -13,6 +13,10 @@ export interface LogItemProps { * Title */ title: string; + /** + * Version + */ + version: string; } export type LogListPageType = WorkflowAPISchema['getLogList']['response']; diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index ece2b821..7ea1d025 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -128,6 +128,8 @@ export interface WorkflowAPISchema extends APISchema { time_cost: number; /** Running status */ status: WorkflowNodeStatus; + /** Version */ + version: string; }[] >; }; From 4b8a188c68e32ec9cfee0f1e58f66b765e0e833b Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 2 Jan 2025 11:42:01 +0800 Subject: [PATCH 149/172] feat: optimize workflow list and add status filter --- .../src/pages/workflow/hooks/useColumns.tsx | 88 ++++---- apps/web/src/pages/workflow/index.tsx | 209 ++++++++---------- .../editor/components/config-panel/index.tsx | 8 +- packages/locales/src/lang/en/workflow.json | 4 +- 4 files changed, 145 insertions(+), 164 deletions(-) diff --git a/apps/web/src/pages/workflow/hooks/useColumns.tsx b/apps/web/src/pages/workflow/hooks/useColumns.tsx index 77850c52..ddf8891a 100644 --- a/apps/web/src/pages/workflow/hooks/useColumns.tsx +++ b/apps/web/src/pages/workflow/hooks/useColumns.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useMemo, useState } from 'react'; import { Stack, IconButton, Switch, Menu, MenuItem } from '@mui/material'; import { useI18n, useTime } from '@milesight/shared/src/hooks'; import { - ListAltIcon, DeleteOutlineIcon, EditIcon, MoreVertIcon, @@ -10,7 +9,7 @@ import { EventNoteIcon, } from '@milesight/shared/src/components'; import { Tooltip, type ColumnType } from '@/components'; -import { workflowAPI, type WorkflowAPISchema } from '@/services/http'; +import { type WorkflowAPISchema } from '@/services/http'; type OperationType = 'log' | 'delete' | 'edit' | 'enable' | 'export'; @@ -30,19 +29,16 @@ const useColumns = ({ onButtonClick }: UseColumnsPro const { getTimeFormat } = useTime(); const [anchorEl, setAnchorEl] = useState(null); const [popoverId, setPopoverId] = useState(''); - const handlerPopoverClose = useCallback( - (e: React.MouseEvent, id: string) => { - setPopoverId(''); - setAnchorEl(null); - }, - [popoverId, anchorEl], - ); + const handlerPopoverClose = useCallback(() => { + setPopoverId(''); + setAnchorEl(null); + }, []); const handlerPopoverOpen = useCallback( (e: React.MouseEvent, id: string) => { setPopoverId(id); setAnchorEl(e.currentTarget); }, - [popoverId, anchorEl], + [], ); const columns: ColumnType[] = useMemo(() => { return [ @@ -86,15 +82,19 @@ const useColumns = ({ onButtonClick }: UseColumnsPro field: 'userNickname', headerName: getIntlText('common.label.creator'), flex: 1, - minWidth: 150, + minWidth: 100, ellipsis: true, }, { field: 'enabled', headerName: getIntlText('common.label.enable_status'), - // ellipsis: true, + // align: 'left', + headerAlign: 'left', + type: 'boolean', + filterable: true, + disableColumnMenu: false, flex: 1, - minWidth: 200, + minWidth: 150, renderCell({ row }) { return ( ({ onButtonClick }: UseColumnsPro field: '$operation', headerName: getIntlText('common.label.operation'), flex: 1, - minWidth: 100, + minWidth: 118, renderCell({ row }) { return ( ({ onButtonClick }: UseColumnsPro }} onClose={handlerPopoverClose} > - onButtonClick('export', row)}> - - - - {getIntlText('common.label.export')} - - + { + handlerPopoverClose(); + onButtonClick('export', row); + }} + sx={{ color: 'text.secondary' }} + > + + + {getIntlText('common.label.export')} + onButtonClick('delete', row)} + onClick={() => { + handlerPopoverClose(); + onButtonClick('delete', row); + }} + sx={{ + color: 'text.secondary', + '&:hover': { color: 'error.light' }, + }} > - - - - {getIntlText('common.label.delete')} - - + + + {getIntlText('common.label.delete')} +
    @@ -191,7 +187,15 @@ const useColumns = ({ onButtonClick }: UseColumnsPro }, }, ]; - }, [getIntlText, getTimeFormat, onButtonClick, popoverId, anchorEl]); + }, [ + popoverId, + anchorEl, + getIntlText, + getTimeFormat, + onButtonClick, + handlerPopoverOpen, + handlerPopoverClose, + ]); return columns; }; diff --git a/apps/web/src/pages/workflow/index.tsx b/apps/web/src/pages/workflow/index.tsx index 8a1d736e..c6abb414 100644 --- a/apps/web/src/pages/workflow/index.tsx +++ b/apps/web/src/pages/workflow/index.tsx @@ -3,7 +3,7 @@ import { useNavigate } from 'react-router-dom'; import { Button, Stack } from '@mui/material'; import { useRequest } from 'ahooks'; import { useI18n } from '@milesight/shared/src/hooks'; -import { objectToCamelCase } from '@milesight/shared/src/utils/tools'; +import { objectToCamelCase, linkDownload } from '@milesight/shared/src/utils/tools'; import { AddIcon, DeleteOutlineIcon, @@ -28,7 +28,7 @@ const Workflow = () => { const navigate = useNavigate(); const { getIntlText } = useI18n(); - // ---------- 列表数据相关逻辑 ---------- + // ---------- Fetch Workflow List ---------- const [keyword, setKeyword] = useState(); const [importModal, setImportModal] = useState(false); const [paginationModel, setPaginationModel] = useState({ page: 0, pageSize: 10 }); @@ -64,17 +64,14 @@ const Workflow = () => { }, ); - // ---------- 数据删除相关逻辑 ---------- + // ---------- Delete Flow ---------- const confirm = useConfirm(); - const warnIcon = useMemo(() => { - return ; - }, []); const handleDeleteConfirm = useCallback( (ids?: ApiKey[]) => { const idsToDelete = ids || [...selectedIds]; confirm({ title: getIntlText('workflow.label.deletion'), - icon: warnIcon, + icon: , description: getIntlText('workflow.message.delete_tip'), onConfirm: async () => { const [error, resp] = await awaitWrap( @@ -94,91 +91,51 @@ const Workflow = () => { }, [confirm, getIntlText, getWorkflowList, selectedIds], ); - const handlerAddModal = () => { - navigate('/workflow/editor'); - }; - // ---------- Table 渲染相关 ---------- - const toolbarRender = useMemo(() => { - return ( - - - - - + + // ---------- Workflow Row Data Interaction ---------- + const handleExportWorkFlow = useCallback(async (record: TableRowDataType) => { + const [error, resp] = await awaitWrap( + workflowAPI.getFlowDesign({ id: record.id, version: '' }), ); - }, [getIntlText, handleDeleteConfirm, selectedIds]); - const handlerImportModal = useCallback( - (isOpen: boolean, contains?: WorkflowSchema) => { - if (contains) { - // TODO: wid should be deleted - navigate('/workflow/editor', { - state: { - workflowSchema: contains, - }, - }); - } - setImportModal(isOpen); - }, - [navigate], - ); - /** Wake up log pop-up window */ - const handleLog = useCallback((record: TableRowDataType) => { - setLogModalVisible(true); - setWorkflowItem(record); + if (error || !isRequestSuccess(resp)) return; + // exportJsonFile(getResponseData(resp) as WorkflowAPISchema['getFlowDesign']['response']); + const { name, design_data: designData = '{}' } = getResponseData(resp) || {}; + const blob = new Blob([JSON.stringify(JSON.parse(designData), null, 4)], { + type: 'application/json', + }); + + linkDownload(blob, `${name}.json`); }, []); - const exportJsonFile = useCallback( - (workflowItem: WorkflowAPISchema['getFlowDesign']['response']) => { - const { name, design_data: designData } = workflowItem; - const blob = new Blob([JSON.stringify(JSON.parse(designData), null, 4)], { - type: 'application/json', + + const handleSwitchChange = useCallback( + async (row: TableRowDataType) => { + if (!workflowList?.content) { + return; + } + const { enabled } = row; + const [error, res] = await awaitWrap( + workflowAPI.enableFlow({ + id: row.id, + status: enabled ? 'disable' : 'enable', + }), + ); + updateWorkflowList({ + ...workflowList, + content: workflowList?.content.map(item => + item.id === row.id + ? { + ...item, + enabled: + error || !isRequestSuccess(res) ? item.enabled : !item.enabled, + } + : item, + ), }); - const fileName = `${name}.json`; - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = fileName; - document.body.appendChild(a); - a.click(); - window.URL.revokeObjectURL(url); - document.body.removeChild(a); }, [workflowList], ); - const handleExportWorkFlow = useCallback( - async (record: TableRowDataType) => { - const [error, resp] = await awaitWrap( - workflowAPI.getFlowDesign({ id: record.id, version: '' }), - ); - if (error || !isRequestSuccess(resp)) return; - exportJsonFile(getResponseData(resp) as WorkflowAPISchema['getFlowDesign']['response']); - }, - [workflowList], - ); const handleTableBtnClick: UseColumnsProps['onButtonClick'] = useCallback( (type, record) => { // console.log(type, record); @@ -209,43 +166,61 @@ const Workflow = () => { } } }, - [workflowList, navigate, handleDeleteConfirm], + [navigate, handleDeleteConfirm, handleExportWorkFlow, handleSwitchChange], ); + + // ---------- Table Render ---------- const columns = useColumns({ onButtonClick: handleTableBtnClick }); - const handleCloseLogModal = useCallback(() => setLogModalVisible(false), []); - const isRowSelectable = useCallback( - ({ row }: { row: TableRowDataType }) => { - return !row.enabled; - }, - [columns], - ); - const handleSwitchChange = useCallback( - async (row: TableRowDataType) => { - if (!workflowList?.content) { - return; + const isRowSelectable = useCallback(({ row }: { row: TableRowDataType }) => { + return !row.enabled; + }, []); + const handlerImportModal = useCallback( + (isOpen: boolean, contains?: WorkflowSchema) => { + if (contains) { + // TODO: wid should be deleted + navigate('/workflow/editor', { + state: { + workflowSchema: contains, + }, + }); } - const { enabled } = row; - const [error, res] = await awaitWrap( - workflowAPI.enableFlow({ - id: row.id, - status: enabled ? 'disable' : 'enable', - }), - ); - updateWorkflowList({ - ...workflowList, - content: workflowList?.content.map(item => - item.id === row.id - ? { - ...item, - enabled: - error || !isRequestSuccess(res) ? item.enabled : !item.enabled, - } - : item, - ), - }); + setImportModal(isOpen); }, - [workflowList], + [navigate], ); + const toolbarRender = useMemo(() => { + return ( + + + + + + ); + }, [getIntlText, navigate, handleDeleteConfirm, handlerImportModal, selectedIds]); + return (
    @@ -274,8 +249,8 @@ const Workflow = () => { {logModalVisible && ( setLogModalVisible(false)} /> )} { {nodeConfig?.testable && ( - setDrawerOpen(true)}> - - + + setDrawerOpen(true)}> + + + )} Date: Thu, 2 Jan 2025 11:44:34 +0800 Subject: [PATCH 150/172] fix: adjustment the test logs type --- apps/web/src/pages/workflow/views/editor/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index e2448b90..4bd70116 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -29,7 +29,7 @@ interface FlowStore { /** * Test Log List */ - testLogs?: WorkflowAPISchema['getLogList']['response']['content']; + testLogs?: Omit[]; /** * Run Log List From 02e829e1ded187ae82bc75533fcd351ad552e7fc Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 2 Jan 2025 13:06:13 +0800 Subject: [PATCH 151/172] fix: adjustment the test logs type --- .../workflow/views/editor/components/test-button/log-list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx index 8a4c1739..71322bed 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx @@ -10,7 +10,7 @@ export type LogType = 'test' | 'run'; export interface LogListProps { // type: LogType; - data: WorkflowAPISchema['getLogList']['response']['content']; + data: Omit[]; loading?: boolean; From c9c3a10f7615b381b3332b293621f56fa2be6730 Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 14:09:39 +0800 Subject: [PATCH 152/172] fix: Fixed text loss caused by switching languages in the code editor --- .../components/config-panel/components/code-editor/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx index de053234..4fa7f3ff 100644 --- a/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/config-panel/components/code-editor/index.tsx @@ -22,7 +22,7 @@ const CodeEditor: React.FC = ({ value, onChange }) => { /** Actual form change callbacks */ const handleChange = useCallback( (data: Partial) => { - const { language, expression = '' } = data; + const { language, expression } = data; const result = { language: language ?? value?.language, From d7e817a19388d5eee11b2eda47373f918a83fbf9 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 2 Jan 2025 14:36:25 +0800 Subject: [PATCH 153/172] fix: fixed text copy failed error caused by tabindex attribute --- .../code-editor/components/header/index.tsx | 17 ++++++++++------- packages/shared/src/hooks/useCopy.tsx | 4 ++-- packages/shared/src/utils/clipboard.ts | 9 ++++++--- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/apps/web/src/components/code-editor/components/header/index.tsx b/apps/web/src/components/code-editor/components/header/index.tsx index 3b42b46f..224eca0e 100644 --- a/apps/web/src/components/code-editor/components/header/index.tsx +++ b/apps/web/src/components/code-editor/components/header/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React from 'react'; import { ContentCopyIcon } from '@milesight/shared/src/components'; import { useCopy } from '@milesight/shared/src/hooks'; import EditorSelect from '../lang-select'; @@ -17,11 +17,6 @@ export default React.memo( }: EditorToolbarProps) => { const { handleCopy } = useCopy(); - /** copy button callback */ - const handleCopyIcon = useCallback(() => { - handleCopy?.(editorValue); - }, [editorValue, handleCopy]); - return (
    @@ -37,7 +32,15 @@ export default React.memo(
    {icon === void 0 ? ( - + + handleCopy( + editorValue, + (e.target as HTMLElement).parentElement || undefined, + ) + } + /> ) : ( icon )} diff --git a/packages/shared/src/hooks/useCopy.tsx b/packages/shared/src/hooks/useCopy.tsx index 99a577ee..cbd1cd70 100644 --- a/packages/shared/src/hooks/useCopy.tsx +++ b/packages/shared/src/hooks/useCopy.tsx @@ -9,9 +9,9 @@ import { copyText } from '../utils/clipboard'; const useCopy = () => { const { getIntlText } = useI18n(); const handleCopy = useCallback( - async (text: string) => { + async (text: string, container?: HTMLElement) => { if (!text) return; - const res = await copyText(text); + const res = await copyText(text, container); res && toast.success({ key: 'copy', diff --git a/packages/shared/src/utils/clipboard.ts b/packages/shared/src/utils/clipboard.ts index 296c1d68..4685860b 100644 --- a/packages/shared/src/utils/clipboard.ts +++ b/packages/shared/src/utils/clipboard.ts @@ -11,7 +11,10 @@ const copyErrorMessage = 'Failed to copy value to clipboard. Unknown type.'; * @param content 待复制的文本 * @returns `Promise` 返回复制操作的结果,成功 `true`,失败 `false` */ -export const copyText = (content: string): Promise => { +export const copyText = ( + content: string, + container: HTMLElement = document.body, +): Promise => { if (typeof content !== 'string') { try { content = JSON.stringify(content); @@ -29,7 +32,7 @@ export const copyText = (content: string): Promise => { textarea.setAttribute('readonly', ''); textarea.style.cssText = cssText; - document.body.appendChild(textarea); + container.appendChild(textarea); if (isIOS()) { const { readOnly, contentEditable: editable } = textarea; @@ -60,7 +63,7 @@ export const copyText = (content: string): Promise => { cb(false); } - document.body.removeChild(textarea); + container.removeChild(textarea); }; if (!isFallback) { From 70979161459c3831c6b24b81db30b0eca794011a Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 15:24:39 +0800 Subject: [PATCH 154/172] fix: Optimize the rounded corners of the action log component --- .../workflow/components/action-log/components/card/style.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/web/src/pages/workflow/components/action-log/components/card/style.less b/apps/web/src/pages/workflow/components/action-log/components/card/style.less index c3ef37f4..52080c2c 100644 --- a/apps/web/src/pages/workflow/components/action-log/components/card/style.less +++ b/apps/web/src/pages/workflow/components/action-log/components/card/style.less @@ -17,6 +17,10 @@ margin-top: 0; margin-bottom: @margin-xxs; } + + &.MuiPaper-elevation { + border-radius: @border-radius-sm; + } } & &__header.MuiButtonBase-root { From 344a26dd1e8db9c7e948174114e6017d4c99368b Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 16:41:50 +0800 Subject: [PATCH 155/172] feat: Adjust the branch header display of the action log component --- .../action-log/hooks/useNestedData.tsx | 13 +++++++++++-- .../workflow/components/action-log/index.tsx | 16 ++++++++++------ .../workflow/components/action-log/types.d.ts | 4 ++++ packages/locales/src/lang/en/workflow.json | 4 ++-- 4 files changed, 27 insertions(+), 10 deletions(-) diff --git a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx index 680e8c20..bc2c10b6 100644 --- a/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx +++ b/apps/web/src/pages/workflow/components/action-log/hooks/useNestedData.tsx @@ -177,20 +177,29 @@ export const useNestedData = ({ traceData, workflowData }: ActionLogProps) => { [getDepthByNode, workflowNestData], ); + /** Mark whether it is a parallel branch */ + const signParallelNode = useCallback((node: WorkflowNestNode) => { + node.attrs.$$isParallelBranch = true; + }, []); + /** Get nodes that need to be promoted to the same level */ const getParallelNodeList = useCallback((): ParallelNodeResult[] => { const { nodes } = workflowNestData || {}; const parallelNodeList: ParallelNodeResult[] = []; - (nodes || []).forEach(node => { + (nodes || []).forEach((node, index) => { + if (index === 0) { + signParallelNode(node); + } const result = getOnceIncomeNode(node); if (!result) return; + signParallelNode(node); parallelNodeList.push(result); }); return parallelNodeList; - }, [getOnceIncomeNode, workflowNestData]); + }, [getOnceIncomeNode, signParallelNode, workflowNestData]); /** Cancel references */ const cancelQuote = useCallback( diff --git a/apps/web/src/pages/workflow/components/action-log/index.tsx b/apps/web/src/pages/workflow/components/action-log/index.tsx index bb7d9f94..f3585042 100644 --- a/apps/web/src/pages/workflow/components/action-log/index.tsx +++ b/apps/web/src/pages/workflow/components/action-log/index.tsx @@ -52,7 +52,11 @@ export default React.memo(({ traceData, workflowData }: ActionLogProps) => { }, [treeData]); /** recursive rendering */ - const renderAccordion = (treeData: WorkflowNestNode[], level: number = 0) => { + const renderAccordion = ( + treeData: WorkflowNestNode[], + level: number = 0, + parallelTitle = '', + ) => { // Existence of parallel branches const parallelBranchCount = treeData.reduce((acc, cur) => { if (cur.children?.length) acc++; @@ -64,9 +68,9 @@ export default React.memo(({ traceData, workflowData }: ActionLogProps) => { // There are multiple parallel branches let multiBranchInParallelIndex = -1; - return treeData.map(data => { + return treeData.map((data, index) => { const { children, attrs } = data || {}; - const { input, output, errorMessage, $$token } = attrs || {}; + const { input, output, errorMessage, $$token, $$isParallelBranch } = attrs || {}; // If there are child nodes, increment the index if ((children?.length || 0) > 1) { @@ -82,11 +86,12 @@ export default React.memo(({ traceData, workflowData }: ActionLogProps) => { 1: `-${parallelLabel}`, }); const branchText = getIntlText('workflow.label.branch', { - 1: `-${parallelLabel}-${getAlphabetIndex(multiBranchInParallelIndex)}`, + 1: `-${parallelTitle}-${getAlphabetIndex(index)}`, }); return ( + {!$$isParallelBranch &&
    {branchText}
    } }> {errorMessage && (
    @@ -114,8 +119,7 @@ export default React.memo(({ traceData, workflowData }: ActionLogProps) => { {!!children?.length && ( -
    {branchText}
    - {renderAccordion(children, currentLevel)} + {renderAccordion(children, currentLevel, parallelLabel)}
    )} diff --git a/apps/web/src/pages/workflow/components/action-log/types.d.ts b/apps/web/src/pages/workflow/components/action-log/types.d.ts index dc6cde07..79719315 100644 --- a/apps/web/src/pages/workflow/components/action-log/types.d.ts +++ b/apps/web/src/pages/workflow/components/action-log/types.d.ts @@ -13,6 +13,10 @@ export interface AccordionLog * Unique ID of web usage */ $$token: string; + /** + * Parallel branch or not + */ + $$isParallelBranch?: boolean; /** * Node Type */ diff --git a/packages/locales/src/lang/en/workflow.json b/packages/locales/src/lang/en/workflow.json index 8700c392..c0b4c04d 100644 --- a/packages/locales/src/lang/en/workflow.json +++ b/packages/locales/src/lang/en/workflow.json @@ -38,8 +38,8 @@ "workflow.message.delete_tip": "Are you sure you want to delete the workflow? It cannot be restore after deletion.", "workflow.label.deletion": "Deletion", "workflow.editor.form_param_select_placeholder": "Enter or reference a variable", - "workflow.label.branch": "Branch{1}", - "workflow.label.parallel": "Parallel{1}", + "workflow.label.branch": "BRANCH{1}", + "workflow.label.parallel": "PARALLEL{1}", "workflow.label.no_log_record": "No log is recorded", "workflow.node.trigger_switch_label": "Context", "workflow.editor.form_param_timer_type": "Timer Type", From 8809566bd857b4e7ae881ee997bde1072293566c Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 2 Jan 2025 17:17:40 +0800 Subject: [PATCH 156/172] feat: get the exactly design data to render run log --- .../editor/components/log-panel/index.tsx | 31 ++++++------- .../editor/components/test-button/index.tsx | 46 ++++++++++++++----- .../components/test-button/log-list.tsx | 3 +- .../src/pages/workflow/views/editor/store.ts | 31 +++++++++---- apps/web/src/services/http/workflow.ts | 2 +- 5 files changed, 74 insertions(+), 39 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx index 351656d7..12f2ea83 100644 --- a/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/log-panel/index.tsx @@ -32,7 +32,6 @@ const LogPanel: React.FC = ({ designMode }) => { logDetail, logDetailLoading, addTestLog, - setTestLogs, setOpenLogPanel, setLogDetail, setLogDetailLoading, @@ -44,7 +43,6 @@ const LogPanel: React.FC = ({ designMode }) => { 'logDetail', 'logDetailLoading', 'addTestLog', - 'setTestLogs', 'setOpenLogPanel', 'setLogDetail', 'setLogDetailLoading', @@ -52,10 +50,6 @@ const LogPanel: React.FC = ({ designMode }) => { ]), ); const { updateNodesStatus, checkWorkflowValid } = useWorkflow(); - const flowData = useMemo(() => { - if (!openLogPanel) return; - return toObject(); - }, [openLogPanel, toObject]); const title = useMemo(() => { switch (logPanelMode) { case 'testRun': @@ -86,11 +80,11 @@ const LogPanel: React.FC = ({ designMode }) => { useValidate(); const [entryInput, setEntryInput] = useState(''); const hasInput = useMemo(() => { - if (!openLogPanel || !flowData || !isTestRunMode) return false; + if (!openLogPanel || !isTestRunMode) return false; const nodes = getNodes(); return !!nodes.find(({ type }) => type === 'trigger' || type === 'listener'); - }, [openLogPanel, flowData, isTestRunMode, getNodes]); + }, [openLogPanel, isTestRunMode, getNodes]); const genDemoData = useCallback(() => { if (!openLogPanel || !isTestRunMode) return; @@ -177,7 +171,7 @@ const LogPanel: React.FC = ({ designMode }) => { {} as NonNullable[0]>, ); - setNodesDataValidResult(nodesCheckResult); + setNodesDataValidResult(nodesCheckResult, logPanelMode); updateNodesStatus(statusData); return; } @@ -195,12 +189,12 @@ const LogPanel: React.FC = ({ designMode }) => { const [error, resp] = await awaitWrap( workflowAPI.testFlow({ input, design_data: JSON.stringify(designData) }), ); + const data = getResponseData(resp); setLogDetailLoading(false); - if (error || !isRequestSuccess(resp)) return; - const data = getResponseData(resp); + if (error || !isRequestSuccess(resp) || !data) return; const nodeStatus = - data?.trace_infos.reduce( + data.trace_infos.reduce( (result: Record, { node_id: nodeId, status }) => { result[nodeId] = status; return result; @@ -208,14 +202,14 @@ const LogPanel: React.FC = ({ designMode }) => { {}, ) || null; - addTestLog({ id: genRandomString(8, { lowerCase: true }), ...data! }); - setLogDetail(data?.trace_infos); + addTestLog({ id: resp.data.request_id, flow_data: toObject(), ...data }); + setLogDetail({ traceInfos: data.trace_infos }); updateNodesStatus(nodeStatus); }, { manual: true, debounceWait: 300, - refreshDeps: [openLogPanel, isTestRunMode, entryInput, toObject], + refreshDeps: [openLogPanel, logPanelMode, isTestRunMode, entryInput, toObject], }, ); @@ -285,9 +279,12 @@ const LogPanel: React.FC = ({ designMode }) => {
    )} - {!!logDetail?.length && flowData ? ( + {logDetail?.traceInfos?.length ? (
    - +
    ) : ( !hasInput && ( diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx index 186abcb1..624380fc 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/index.tsx @@ -101,31 +101,55 @@ const TestButton: React.FC = ({ disabled }) => { updateNodesStatus(null); switch (type) { case 'test': { + console.log(1111, record); setLogPanelMode('testLog'); + setLogDetail({ traceInfos: record.trace_infos!, flowData: record.flow_data }); break; } case 'run': { + if (!wid) return; setLogPanelMode('runLog'); + setLogDetailLoading(true); + + let resp; + try { + resp = await Promise.all([ + workflowAPI.getLogDetail({ id: record.id }), + workflowAPI.getFlowDesign({ id: wid, version: record.version }), + ]); + } catch (e) { + return; + } + const [detailResp, flowResp] = resp; + + setLogDetailLoading(false); + if (!isRequestSuccess(detailResp) || !isRequestSuccess(flowResp)) return; + const traceInfos = getResponseData(detailResp)!.trace_info; + const designData = getResponseData(flowResp)?.design_data; + const flowData: undefined | Pick = + designData && typeof designData === 'string' + ? JSON.parse(designData) + : designData; + + setLogDetail({ traceInfos, flowData }); break; } default: { break; } } - - // TODO: get log detail - setLogDetailLoading(true); - const [error, resp] = await awaitWrap(workflowAPI.getLogDetail({ id: record.id })); - setLogDetailLoading(false); - - if (error || !isRequestSuccess(resp)) return; - const data = getResponseData(resp); - - setLogDetail(data?.trace_info); }, - [setLogDetail, setLogDetailLoading, setLogPanelMode, setOpenLogPanel, updateNodesStatus], + [ + wid, + setLogDetail, + setLogDetailLoading, + setLogPanelMode, + setOpenLogPanel, + updateNodesStatus, + ], ); + console.log({ testLogs }); // ---------- Tab ---------- const [tabKey, setTabKey] = useState(DEFAULT_TAB_KEY); const tabs = useMemo(() => { diff --git a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx index 71322bed..cd856b02 100644 --- a/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx +++ b/apps/web/src/pages/workflow/views/editor/components/test-button/log-list.tsx @@ -3,6 +3,7 @@ import { useTime, useVirtualList } from '@milesight/shared/src/hooks'; import { ErrorIcon, CheckCircleIcon } from '@milesight/shared/src/components'; import { type WorkflowAPISchema } from '@/services/http'; import { Empty } from '@/components'; +import type { FlowStore } from '../../store'; import './style.less'; export type LogType = 'test' | 'run'; @@ -10,7 +11,7 @@ export type LogType = 'test' | 'run'; export interface LogListProps { // type: LogType; - data: Omit[]; + data: NonNullable; loading?: boolean; diff --git a/apps/web/src/pages/workflow/views/editor/store.ts b/apps/web/src/pages/workflow/views/editor/store.ts index 4bd70116..1011e391 100644 --- a/apps/web/src/pages/workflow/views/editor/store.ts +++ b/apps/web/src/pages/workflow/views/editor/store.ts @@ -5,7 +5,7 @@ import { basicNodeConfigs } from '../../config'; import type { NodesDataValidResult } from './hooks'; import type { NodeConfigItem } from './typings'; -interface FlowStore { +export interface FlowStore { selectedNode?: WorkflowNode; /** Workflow Node Configs */ @@ -29,14 +29,23 @@ interface FlowStore { /** * Test Log List */ - testLogs?: Omit[]; + testLogs?: (PartialOptional< + WorkflowAPISchema['getLogList']['response']['content'][number], + 'version' + > & { + trace_infos?: FlowNodeTraceInfo[]; + flow_data?: Pick; + })[]; /** * Run Log List */ runLogs?: WorkflowAPISchema['getLogList']['response']['content']; - logDetail?: PartialOptional[]; + logDetail?: { + flowData?: Pick; + traceInfos: PartialOptional[]; + }; logDetailLoading?: boolean; @@ -57,7 +66,10 @@ interface FlowStore { setLogDetailLoading: (loading: FlowStore['logDetailLoading']) => void; - setNodesDataValidResult: (data: NodesDataValidResult | null) => void; + setNodesDataValidResult: ( + data: NodesDataValidResult | null, + logPanelMode?: FlowStore['logPanelMode'], + ) => void; } const useFlowStore = create( @@ -106,20 +118,21 @@ const useFlowStore = create( addTestLog: log => { set(state => { if (!log) return; - state.testLogs?.unshift(log); + const testLogs = [log, ...(state.testLogs || [])]; + return { testLogs }; }); }, setRunLogs: runLogs => set({ runLogs }), setLogDetail: detail => set({ logDetail: detail }), setLogDetailLoading: loading => set({ logDetailLoading: loading }), - setNodesDataValidResult(data) { + setNodesDataValidResult(data, logPanelMode = 'feVerify') { if (!data) { set({ openLogPanel: false, logPanelMode: undefined, logDetail: undefined }); return; } // console.log(data); - const logDetail = Object.entries(data).map(([id, { type, name, label, errMsgs }]) => { - const result: NonNullable[0] = { + const traceInfos = Object.entries(data).map(([id, { type, name, label, errMsgs }]) => { + const result: NonNullable['traceInfos'][0] = { node_id: id, node_label: label!, status: 'ERROR', @@ -129,7 +142,7 @@ const useFlowStore = create( }); // console.log(logDetail); - set({ openLogPanel: true, logPanelMode: 'feVerify', logDetail }); + set({ openLogPanel: true, logPanelMode, logDetail: { traceInfos } }); }, })), ); diff --git a/apps/web/src/services/http/workflow.ts b/apps/web/src/services/http/workflow.ts index 7ea1d025..6cea3748 100644 --- a/apps/web/src/services/http/workflow.ts +++ b/apps/web/src/services/http/workflow.ts @@ -282,7 +282,7 @@ export default attachAPI(client, { getFlowDesign: `GET ${API_PREFIX}/workflow/flows/:id/design?version=:version`, checkFlowDesign: `POST ${API_PREFIX}/workflow/flows/design/validate`, saveFlowDesign: `POST ${API_PREFIX}/workflow/flows/design`, - testFlow: `POST ${API_PREFIX}/workflow/flows/test`, + testFlow: `POST ${API_PREFIX}/workflow/flows/design/test`, testSingleNode: `POST ${API_PREFIX}/workflow/flows/node/test`, getFlowNodes: `GET ${API_PREFIX}/workflow/components`, getNodeForm: `GET ${API_PREFIX}/workflow/components/:id`, From bb8bc5bccfb2e36856c336caddda083d72a32891 Mon Sep 17 00:00:00 2001 From: jimco Date: Thu, 2 Jan 2025 17:19:13 +0800 Subject: [PATCH 157/172] fix: modify the code expression validators based on the new data type --- .../views/editor/hooks/useValidate.ts | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts index 0f6037e0..e6b24a1f 100644 --- a/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts +++ b/apps/web/src/pages/workflow/views/editor/hooks/useValidate.ts @@ -1,5 +1,6 @@ import { useMemo, useCallback } from 'react'; import { useReactFlow } from '@xyflow/react'; +import { isObject } from 'lodash-es'; import { useI18n } from '@milesight/shared/src/hooks'; import { toast } from '@milesight/shared/src/components'; import { @@ -225,10 +226,25 @@ const useValidate = () => { }, }, 'code.expression': { - checkRequired, - checkMaxLength(value: string, fieldName) { + checkRequired( + value: NonNullable['expression'], + fieldName, + ) { + if (!isObject(value) || !value.language || !value.expression) { + return getIntlText(ErrorIntlKey.required, { 1: fieldName }); + } + return true; + }, + checkMaxLength( + value: NonNullable['expression'], + fieldName, + ) { const maxLength = 2000; - if (value && value.length > maxLength) { + if ( + isObject(value) && + value.expression && + value.expression.length > maxLength + ) { return getIntlText(ErrorIntlKey.maxLength, { 1: fieldName, 2: maxLength, From fd3afd42c008afbf45010c184b6e9872f9675abf Mon Sep 17 00:00:00 2001 From: Jeffrey Date: Thu, 2 Jan 2025 17:34:28 +0800 Subject: [PATCH 158/172] feat: Tweak the onChange logic of the code editor --- .../code-editor/components/header/index.tsx | 2 ++ .../components/lang-select/index.tsx | 11 +++++-- .../web/src/components/code-editor/editor.tsx | 29 +++++++++---------- .../web/src/components/code-editor/types.d.ts | 13 ++++----- .../components/code-editor/index.tsx | 28 ++++++++++-------- 5 files changed, 46 insertions(+), 37 deletions(-) diff --git a/apps/web/src/components/code-editor/components/header/index.tsx b/apps/web/src/components/code-editor/components/header/index.tsx index 224eca0e..07852a1d 100644 --- a/apps/web/src/components/code-editor/components/header/index.tsx +++ b/apps/web/src/components/code-editor/components/header/index.tsx @@ -14,6 +14,7 @@ export default React.memo( title, style, renderOptions, + supportLangs, }: EditorToolbarProps) => { const { handleCopy } = useCopy(); @@ -25,6 +26,7 @@ export default React.memo( editorLang={editorLang} onEditorLangChange={setEditorLang} renderOptions={renderOptions} + supportLangs={supportLangs} /> ) : ( title diff --git a/apps/web/src/components/code-editor/components/lang-select/index.tsx b/apps/web/src/components/code-editor/components/lang-select/index.tsx index 259fd3f2..06fee12d 100644 --- a/apps/web/src/components/code-editor/components/lang-select/index.tsx +++ b/apps/web/src/components/code-editor/components/lang-select/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import { type SelectInputProps } from '@mui/material/Select/SelectInput'; import { ExpandMoreIcon, Select } from '@milesight/shared/src/components'; import { editorLangOptions } from '../../constant'; @@ -6,7 +6,7 @@ import type { EditorSupportLang, EditorSelectProps } from '../../types'; import './style.less'; export default React.memo( - ({ editorLang, onEditorLangChange, renderOptions }: EditorSelectProps) => { + ({ editorLang, onEditorLangChange, renderOptions, supportLangs }: EditorSelectProps) => { /** select change callback */ const handleChange = useCallback>['onChange']>( e => { @@ -16,6 +16,11 @@ export default React.memo( [onEditorLangChange], ); + const options = useMemo(() => { + if (!supportLangs) return editorLangOptions; + + return editorLangOptions.filter(({ value }) => supportLangs.includes(value)); + }, [supportLangs]); return (