From be664d7c011a473002c1b413bac8303f7905d160 Mon Sep 17 00:00:00 2001 From: dongyukun <1208714201@qq.com> Date: 星期四, 29 五月 2025 14:26:36 +0800 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- src/views/ai/chat/index/components/message/MessageList.vue | 284 src/views/ai/model/chatRole/ChatRoleForm.vue | 211 src/views/ai/mindmap/manager/index.vue | 190 src/views/ai/dashboard/components/conversation/ConversationListEmpty.vue | 78 src/views/ai/workflow/index.vue | 193 src/assets/ai/zhuanlu/ldggrqsyc_title.png | 0 src/assets/ai/zhuanlu/refresh.png | 0 src/views/ai/chat/manager/ChatMessageList.vue | 175 src/components/Dialog/src/DialogDashboard.vue | 118 src/api/ai/questiontemplate/index.js | 53 src/views/ai/music/index/title/index.vue | 25 src/views/ai/dashboard/components/message/MessageLoading.vue | 15 src/components/Tinyflow/Tinyflow.vue | 63 src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue | 148 src/router/modules/remaining.ts | 114 src/views/ai/image/index/components/dall3/index.vue | 363 src/views/ai/knowledge/segment/KnowledgeSegmentForm.vue | 101 src/assets/ai/zhuanlu/data_bg1.png | 0 src/assets/ai/zhuanlu/icon_conversion.png | 0 src/views/ai/music/index/list/songCard/index.vue | 36 src/views/ai/dashboard/components/role/RoleList.vue | 174 src/views/ai/chat/index/components/message/MessageKnowledge.vue | 104 src/views/ai/chat/index/components/role/RoleHeader.vue | 48 src/views/ai/knowledge/document/form/index.vue | 193 src/views/ai/music/index/list/songInfo/index.vue | 22 src/assets/ai/zhuanlu/icon_bg.png | 0 src/assets/ai/zhuanlu/mqhsssxx_title.png | 0 src/utils/rem.ts | 9 src/api/ai/questionparamsetting/index.js | 53 src/api/ai/image/index.ts | 102 src/views/ai/knowledge/document/form/ProcessStep.vue | 146 src/views/ai/model/model/index.vue | 190 src/views/ai/knowledge/document/index.vue | 236 src/views/ai/chat/index/components/message/MessageNewConversation.vue | 46 src/assets/ai/zhuanlu/right_label.png | 0 src/api/ai/knowledge/segment/index.ts | 75 src/views/ai/chat/manager/index.vue | 21 src/assets/ai/zhuanlu/scmbyyxzb_title.png | 0 src/views/ai/dashboard/components/message/ModelMessageList.vue | 193 src/components/Tinyflow/ui/index.css | 1 src/views/ai/utils/constants.ts | 470 src/views/ai/knowledge/knowledge/KnowledgeForm.vue | 162 src/assets/ai/zhuanlu/table_header_bg.png | 0 src/api/infra/monitordisk/index.ts | 10 src/api/ai/write/index.ts | 85 src/views/ai/dashboard/components/role/RoleCategoryList.vue | 53 src/assets/ai/zhuanlu/zlxx_title.png | 0 src/api/ai/knowledge/knowledge/index.ts | 44 src/assets/ai/zhuanlu/data_bg3.png | 0 src/views/ai/dashboard/components/message/MessageListEmpty.vue | 50 src/views/ai/chat/index/components/message/MessageListEmpty.vue | 83 src/views/ai/model/tool/index.vue | 177 src/views/ai/dashboard/components/conversation/CommonConversation.vue | 964 + src/assets/ai/zhuanlu/edit.png | 0 src/assets/ai/zhuanlu/ldghslyc_title.png | 0 src/assets/ai/zhuanlu/ddtljl_result_title.png | 0 src/assets/ai/zhuanlu/tsxx_title.png | 0 src/views/ai/model/model/ModelForm.vue | 220 src/views/ai/dashboard/components/message/MessageList.vue | 305 src/views/ai/image/index/components/ImageList.vue | 245 src/views/ai/questiontemplate/index.vue | 186 src/views/ai/chat/index/components/message/MessageLoading.vue | 15 src/components/Tinyflow/ui/index.d.ts | 41 src/views/ai/image/index/components/ImageCard.vue | 162 src/views/ai/model/apiKey/index.vue | 181 src/views/ai/workflow/form/BasicInfo.vue | 54 src/assets/ai/zhuanlu/content.png | 0 src/assets/ai/zhuanlu/left_label.png | 0 src/views/ai/questiontemplate/QuestionTemplateForm.vue | 148 src/views/ai/knowledge/document/form/SplitStep.vue | 238 src/views/ai/dashboard/components/message/MessageKnowledge.vue | 104 src/views/ai/music/index/list/index.vue | 108 src/assets/ai/zhuanlu/common_title.png | 0 src/api/ai/music/index.ts | 41 src/utils/index.ts | 58 src/views/ai/music/index/list/audioBar/index.vue | 70 src/views/ai/music/index/mode/index.vue | 35 src/api/infra/monitormem/index.ts | 15 src/views/ai/dashboard/zhuanlu/index.vue | 2347 +++ src/views/ai/dashboard/components/message/MessageNewConversation.vue | 46 src/views/ai/model/tool/ToolForm.vue | 112 src/api/ai/model/tool/index.ts | 42 src/views/ai/chat/manager/ChatConversationList.vue | 163 src/views/ai/model/apiKey/ApiKeyForm.vue | 132 src/views/ai/dashboard/components/role/RoleRepository.vue | 289 src/views/ai/music/index/mode/desc.vue | 55 src/api/ai/workflow/index.ts | 25 src/views/ai/image/index/components/common/index.vue | 224 src/views/ai/dashboard/components/message/HistoryMessageList.vue | 290 src/views/ai/chat/index/components/role/RoleList.vue | 174 src/views/ai/dashboard/components/conversation/CommonConversationList.vue | 526 src/views/ai/questionparamsetting/index.vue | 148 src/views/ai/write/index/index.vue | 76 src/api/ai/chat/message/index.ts | 142 src/views/ai/knowledge/segment/index.vue | 242 src/components/Tinyflow/ui/index.umd.js | 9 src/views/ai/music/manager/index.vue | 293 src/assets/ai/zhuanlu/mqxhssxx_title.png | 0 src/views/ai/chat/index/components/conversation/ConversationList.vue | 472 src/components/Tinyflow/ui/index.js | 16984 ++++++++++++++++++++++++++ src/views/ai/mindmap/index/index.vue | 92 src/views/ai/chat/index/components/role/RoleCategoryList.vue | 53 src/api/ai/chat/conversation/index.ts | 75 src/assets/ai/zhuanlu/copy.png | 0 src/views/ai/image/index/components/midjourney/index.vue | 350 src/assets/ai/zhuanlu/assistant.png | 0 src/views/ai/knowledge/document/form/UploadStep.vue | 273 src/assets/ai/zhuanlu/user.png | 0 src/assets/ai/zhuanlu/conversation.png | 0 src/views/infra/monitor/components/MonitorDisk.vue | 410 src/views/ai/write/index/components/Left.vue | 213 src/utils/dict.ts | 12 src/api/ai/model/chatRole/index.ts | 82 src/views/ai/music/index/mode/lyric.vue | 83 src/views/ai/dashboard/components/message/HistoryMessageDialog.vue | 375 src/views/ai/workflow/form/index.vue | 235 src/assets/ai/zhuanlu/data_bg2.png | 0 src/assets/ai/zhuanlu/mqhsjhxx_title.png | 0 src/views/ai/write/index/components/Right.vue | 120 src/views/ai/dashboard/components/conversation/CommonConversationUpdateForm.vue | 219 src/views/ai/dashboard/components/role/RoleHeader.vue | 48 src/views/ai/utils/utils.ts | 13 src/views/ai/dashboard/components/conversation/ConversationUpdateForm.vue | 148 src/components/MarkdownView/index.vue | 35 src/assets/ai/zhuanlu/content_select.png | 0 src/views/ai/dashboard/components/conversation/HistoryConversationList.vue | 459 src/components/Dialog/src/DialogHistory.vue | 138 src/views/ai/image/manager/index.vue | 252 src/assets/ai/zhuanlu/delete.png | 0 src/views/ai/mindmap/index/components/Right.vue | 167 src/views/ai/chat/index/components/role/RoleRepository.vue | 289 src/api/ai/model/model/index.ts | 54 src/views/ai/music/index/index.vue | 26 src/assets/ai/zhuanlu/data_bg4.png | 0 src/views/ai/knowledge/knowledge/index.vue | 220 src/api/ai/mindmap/index.ts | 60 src/views/ai/knowledge/knowledge/retrieval/index.vue | 163 src/views/infra/monitor/components/MonitorMem.vue | 138 src/views/ai/image/index/components/stableDiffusion/index.vue | 296 src/views/ai/image/index/index.vue | 155 src/views/ai/workflow/form/WorkflowDesign.vue | 49 src/assets/ai/zhuanlu/think_bg.png | 0 src/views/infra/monitor/components/test.html | 90 src/views/ai/mindmap/index/components/Left.vue | 78 src/views/ai/questionparamsetting/QuestionParamSettingForm.vue | 117 src/assets/ai/zhuanlu/bg.png | 0 src/views/ai/model/chatRole/index.vue | 200 src/api/ai/model/apiKey/index.ts | 44 src/api/ai/knowledge/document/index.ts | 54 src/views/ai/write/index/components/Tag.vue | 32 src/views/ai/dashboard/components/conversation/ConversationList.vue | 459 src/assets/ai/zhuanlu/conversation_big.png | 0 src/views/ai/image/square/index.vue | 104 src/views/ai/chat/index/index.vue | 773 + src/views/ai/write/manager/index.vue | 226 src/views/ai/image/index/components/ImageDetail.vue | 224 156 files changed, 38,380 insertions(+), 218 deletions(-) diff --git a/src/api/ai/chat/conversation/index.ts b/src/api/ai/chat/conversation/index.ts new file mode 100644 index 0000000..a37b262 --- /dev/null +++ b/src/api/ai/chat/conversation/index.ts @@ -0,0 +1,75 @@ +import request from '@/config/axios' + +// AI 聊天对话 VO +export interface ChatConversationVO { + id: number // ID 编号 + userId: number // 用户编号 + title: string // 对话标题 + pinned: boolean // 是否置顶 + roleId: number // 角色编号 + modelId: number // 模型编号 + model: string // 模型标志 + temperature: number // 温度参数 + maxTokens: number // 单条回复的最大 Token 数量 + maxContexts: number // 上下文的最大 Message 数量 + createTime?: Date // 创建时间 + // 额外字段 + systemMessage?: string // 角色设定 + modelName?: string // 模型名字 + roleAvatar?: string // 角色头像 + modelMaxTokens?: string // 模型的单条回复的最大 Token 数量 + modelMaxContexts?: string // 模型的上下文的最大 Message 数量 +} + +// AI 聊天对话 API +export const ChatConversationApi = { + // 获得【我的】聊天对话 + getChatConversationMy: async (id: number) => { + return await request.get({ url: `/ai/chat/conversation/get-my?id=${id}` }) + }, + + // 新增【我的】聊天对话 + createChatConversationMy: async (data?: ChatConversationVO) => { + return await request.post({ url: `/ai/chat/conversation/create-my`, data }) + }, + + // 新增【工业大模型】聊天对话 + createChatConversationEnergy: async (data?: ChatConversationVO) => { + return await request.post({ url: `/ai/chat/conversation/create-energy`, data }) + }, + + // 更新【我的】聊天对话 + updateChatConversationMy: async (data: ChatConversationVO) => { + return await request.put({ url: `/ai/chat/conversation/update-my`, data }) + }, + + // 删除【我的】聊天对话 + deleteChatConversationMy: async (id: string) => { + return await request.delete({ url: `/ai/chat/conversation/delete-my?id=${id}` }) + }, + + // 删除【我的】所有对话,置顶除外 + deleteChatConversationMyByUnpinned: async () => { + return await request.delete({ url: `/ai/chat/conversation/delete-by-unpinned` }) + }, + + // 获得【我的】聊天对话列表 + getChatConversationMyList: async () => { + return await request.get({ url: `/ai/chat/conversation/my-list` }) + }, + + // 获得【工业大模型】聊天对话列表 + getChatConversationEnergyList: async (modelName: string) => { + return await request.get({ url: `/ai/chat/conversation/energy-list?modelName=${modelName}` }) + }, + + // 获得对话分页 + getChatConversationPage: async (params: any) => { + return await request.get({ url: `/ai/chat/conversation/page`, params }) + }, + + // 管理员删除消息 + deleteChatConversationByAdmin: async (id: number) => { + return await request.delete({ url: `/ai/chat/conversation/delete-by-admin?id=${id}` }) + } +} diff --git a/src/api/ai/chat/message/index.ts b/src/api/ai/chat/message/index.ts new file mode 100644 index 0000000..1123524 --- /dev/null +++ b/src/api/ai/chat/message/index.ts @@ -0,0 +1,142 @@ +import request from '@/config/axios' +import { fetchEventSource } from '@microsoft/fetch-event-source' +import { getAccessToken } from '@/utils/auth' +import { config } from '@/config/axios/config' +import {refreshToken} from "@/api/login"; + +// 聊天VO +export interface ChatMessageVO { + id: number // 编号 + conversationId: number // 对话编号 + type: string // 消息类型 + userId: string // 用户编号 + roleId: string // 角色编号 + model: number // 模型标志 + modelId: number // 模型编号 + content: string // 聊天内容 + thinking: string // 聊天思考 + thinkingFlag: boolean // 聊天思考 + conclusion: string // 聊天结论 + tokens: number // 消耗 Token 数量 + segmentIds?: number[] // 段落编号 + segments?: { + id: number // 段落编号 + content: string // 段落内容 + documentId: number // 文档编号 + documentName: string // 文档名称 + }[] + createTime: Date // 创建时间 + roleAvatar: string // 角色头像 + userAvatar: string // 用户头像 +} + +// AI chat 聊天 +export const ChatMessageApi = { + // 消息列表 + getChatMessageListByConversationId: async (conversationId: number | null) => { + return await request.get({ + url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // 消息列表【工业大模型专用】 + getEnergyChatMessageListByConversationId: async (conversationId: number | null) => { + return await request.get({ + url: `/ai/chat/message/energy-list-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // 发送 Stream 消息 + // 为什么不用 axios 呢?因为它不支持 SSE 调用 + sendChatMessageStream: async ( + conversationId: number, + content: string, + ctrl, + enableContext: boolean, + onMessage, + onError, + onClose + ) => { + const token = getAccessToken() + return fetchEventSource(`${config.base_url}/ai/chat/message/send-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + openWhenHidden: true, + body: JSON.stringify({ + conversationId, + content, + useContext: enableContext + }), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + }) + }, + // 发送 Stream 消息 【工业大模型专用】 + // sendEnergyChatMessageStream: async ( + // conversationId: number, + // content: string, + // ctrl, + // enableContext: boolean, + // onMessage, + // onError, + // onClose + // ) => { + // const token = getAccessToken() + // return fetchEventSource(`${config.base_url}/ai/chat/message/send-energy-stream`, { + // method: 'post', + // headers: { + // 'Content-Type': 'application/json', + // Authorization: `Bearer ${token}` + // }, + // openWhenHidden: true, + // body: JSON.stringify({ + // conversationId, + // content, + // useContext: enableContext + // }), + // onmessage: onMessage, + // onerror: onError, + // onclose: onClose, + // signal: ctrl.signal + // }) + // }, + + // 删除消息 + deleteChatMessage: async (id: string) => { + return await request.delete({ url: `/ai/chat/message/delete?id=${id}` }) + }, + + // 删除指定对话的消息 + deleteByConversationId: async (conversationId: number) => { + return await request.delete({ + url: `/ai/chat/message/delete-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // // 删除消息【工业大模型专用】 + // deleteEnergyChatMessage: async (id: string) => { + // return await request.delete({ url: `/ai/chat/message/delete-energy?id=${id}` }) + // }, + + // 删除指定对话的消息【工业大模型专用】 + deleteEnergyByConversationId: async (conversationId: number) => { + return await request.delete({ + url: `/ai/chat/message/delete-energy-by-conversation-id?conversationId=${conversationId}` + }) + }, + + // 获得消息分页 + getChatMessagePage: async (params: any) => { + return await request.get({ url: '/ai/chat/message/page', params }) + }, + + // 管理员删除消息 + deleteChatMessageByAdmin: async (id: number) => { + return await request.delete({ url: `/ai/chat/message/delete-by-admin?id=${id}` }) + } +} diff --git a/src/api/ai/image/index.ts b/src/api/ai/image/index.ts new file mode 100644 index 0000000..ab2bcf7 --- /dev/null +++ b/src/api/ai/image/index.ts @@ -0,0 +1,102 @@ +import request from '@/config/axios' + +// AI 绘图 VO +export interface ImageVO { + id: number // 编号 + platform: string // 平台 + model: string // 模型 + prompt: string // 提示词 + width: number // 图片宽度 + height: number // 图片高度 + status: number // 状态 + publicStatus: boolean // 公开状态 + picUrl: string // 任务地址 + errorMessage: string // 错误信息 + options: any // 配置 Map<string, string> + taskId: number // 任务编号 + buttons: ImageMidjourneyButtonsVO[] // mj 操作按钮 + createTime: Date // 创建时间 + finishTime: Date // 完成时间 +} + +export interface ImageDrawReqVO { + prompt: string // 提示词 + modelId: number // 模型 + style: string // 图像生成的风格 + width: string // 图片宽度 + height: string // 图片高度 + options: object // 绘制参数,Map<String, String> +} + +export interface ImageMidjourneyImagineReqVO { + prompt: string // 提示词 + modelId: number // 模型 + base64Array: string[] // size不能为空 + width: string // 图片宽度 + height: string // 图片高度 + version: string // 版本 +} + +export interface ImageMidjourneyActionVO { + id: number // 图片编号 + customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 +} + +export interface ImageMidjourneyButtonsVO { + customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 + emoji: string // 图标 emoji + label: string // Make Variations 文本 + style: number // 样式: 2(Primary)、3(Green) +} + +// AI 图片 API +export const ImageApi = { + // 获取【我的】绘图分页 + getImagePageMy: async (params: any) => { + return await request.get({ url: `/ai/image/my-page`, params }) + }, + // 获取【我的】绘图记录 + getImageMy: async (id: number) => { + return await request.get({ url: `/ai/image/get-my?id=${id}` }) + }, + // 获取【我的】绘图记录列表 + getImageListMyByIds: async (ids: number[]) => { + return await request.get({ url: `/ai/image/my-list-by-ids`, params: { ids: ids.join(',') } }) + }, + // 生成图片 + drawImage: async (data: ImageDrawReqVO) => { + return await request.post({ url: `/ai/image/draw`, data }) + }, + // 删除【我的】绘画记录 + deleteImageMy: async (id: number) => { + return await request.delete({ url: `/ai/image/delete-my?id=${id}` }) + }, + + // ================ midjourney 专属 ================ + + // 【Midjourney】生成图片 + midjourneyImagine: async (data: ImageMidjourneyImagineReqVO) => { + return await request.post({ url: `/ai/image/midjourney/imagine`, data }) + }, + // 【Midjourney】Action 操作(二次生成图片) + midjourneyAction: async (data: ImageMidjourneyActionVO) => { + return await request.post({ url: `/ai/image/midjourney/action`, data }) + }, + + // ================ 绘图管理 ================ + + // 查询绘画分页 + getImagePage: async (params: any) => { + return await request.get({ url: `/ai/image/page`, params }) + }, + + // 更新绘画发布状态 + updateImage: async (data: any) => { + return await request.put({ url: '/ai/image/update', data }) + }, + + // 删除绘画 + deleteImage: async (id: number) => { + return await request.delete({ url: `/ai/image/delete?id=` + id }) + } +} diff --git a/src/api/ai/knowledge/document/index.ts b/src/api/ai/knowledge/document/index.ts new file mode 100644 index 0000000..62c24d5 --- /dev/null +++ b/src/api/ai/knowledge/document/index.ts @@ -0,0 +1,54 @@ +import request from '@/config/axios' + +// AI 知识库文档 VO +export interface KnowledgeDocumentVO { + id: number // 编号 + knowledgeId: number // 知识库编号 + name: string // 文档名称 + contentLength: number // 字符数 + tokens: number // token 数 + segmentMaxTokens: number // 分片最大 token 数 + retrievalCount: number // 召回次数 + status: number // 是否启用 +} + +// AI 知识库文档 API +export const KnowledgeDocumentApi = { + // 查询知识库文档分页 + getKnowledgeDocumentPage: async (params: any) => { + return await request.get({ url: `/ai/knowledge/document/page`, params }) + }, + + // 查询知识库文档详情 + getKnowledgeDocument: async (id: number) => { + return await request.get({ url: `/ai/knowledge/document/get?id=` + id }) + }, + + // 新增知识库文档(单个) + createKnowledgeDocument: async (data: any) => { + return await request.post({ url: `/ai/knowledge/document/create`, data }) + }, + + // 新增知识库文档(多个) + createKnowledgeDocumentList: async (data: any) => { + return await request.post({ url: `/ai/knowledge/document/create-list`, data }) + }, + + // 修改知识库文档 + updateKnowledgeDocument: async (data: any) => { + return await request.put({ url: `/ai/knowledge/document/update`, data }) + }, + + // 修改知识库文档状态 + updateKnowledgeDocumentStatus: async (data: any) => { + return await request.put({ + url: `/ai/knowledge/document/update-status`, + data + }) + }, + + // 删除知识库文档 + deleteKnowledgeDocument: async (id: number) => { + return await request.delete({ url: `/ai/knowledge/document/delete?id=` + id }) + } +} diff --git a/src/api/ai/knowledge/knowledge/index.ts b/src/api/ai/knowledge/knowledge/index.ts new file mode 100644 index 0000000..f9d3683 --- /dev/null +++ b/src/api/ai/knowledge/knowledge/index.ts @@ -0,0 +1,44 @@ +import request from '@/config/axios' + +// AI 知识库 VO +export interface KnowledgeVO { + id: number // 编号 + name: string // 知识库名称 + description: string // 知识库描述 + embeddingModelId: number // 嵌入模型编号,高质量模式时维护 + topK: number // topK + similarityThreshold: number // 相似度阈值 +} + +// AI 知识库 API +export const KnowledgeApi = { + // 查询知识库分页 + getKnowledgePage: async (params: any) => { + return await request.get({ url: `/ai/knowledge/page`, params }) + }, + + // 查询知识库详情 + getKnowledge: async (id: number) => { + return await request.get({ url: `/ai/knowledge/get?id=` + id }) + }, + + // 新增知识库 + createKnowledge: async (data: KnowledgeVO) => { + return await request.post({ url: `/ai/knowledge/create`, data }) + }, + + // 修改知识库 + updateKnowledge: async (data: KnowledgeVO) => { + return await request.put({ url: `/ai/knowledge/update`, data }) + }, + + // 删除知识库 + deleteKnowledge: async (id: number) => { + return await request.delete({ url: `/ai/knowledge/delete?id=` + id }) + }, + + // 获取知识库简单列表 + getSimpleKnowledgeList: async () => { + return await request.get({ url: `/ai/knowledge/simple-list` }) + } +} diff --git a/src/api/ai/knowledge/segment/index.ts b/src/api/ai/knowledge/segment/index.ts new file mode 100644 index 0000000..d234d99 --- /dev/null +++ b/src/api/ai/knowledge/segment/index.ts @@ -0,0 +1,75 @@ +import request from '@/config/axios' + +// AI 知识库分段 VO +export interface KnowledgeSegmentVO { + id: number // 编号 + documentId: number // 文档编号 + knowledgeId: number // 知识库编号 + vectorId: string // 向量库编号 + content: string // 切片内容 + contentLength: number // 切片内容长度 + tokens: number // token 数量 + retrievalCount: number // 召回次数 + status: number // 文档状态 + createTime: number // 创建时间 +} + +// AI 知识库分段 API +export const KnowledgeSegmentApi = { + // 查询知识库分段分页 + getKnowledgeSegmentPage: async (params: any) => { + return await request.get({ url: `/ai/knowledge/segment/page`, params }) + }, + + // 查询知识库分段详情 + getKnowledgeSegment: async (id: number) => { + return await request.get({ url: `/ai/knowledge/segment/get?id=` + id }) + }, + + // 删除知识库分段 + deleteKnowledgeSegment: async (id: number) => { + return await request.delete({ url: `/ai/knowledge/segment/delete?id=` + id }) + }, + + // 新增知识库分段 + createKnowledgeSegment: async (data: KnowledgeSegmentVO) => { + return await request.post({ url: `/ai/knowledge/segment/create`, data }) + }, + + // 修改知识库分段 + updateKnowledgeSegment: async (data: KnowledgeSegmentVO) => { + return await request.put({ url: `/ai/knowledge/segment/update`, data }) + }, + + // 修改知识库分段状态 + updateKnowledgeSegmentStatus: async (data: any) => { + return await request.put({ + url: `/ai/knowledge/segment/update-status`, + data + }) + }, + + // 切片内容 + splitContent: async (url: string, segmentMaxTokens: number) => { + return await request.get({ + url: `/ai/knowledge/segment/split`, + params: { url, segmentMaxTokens } + }) + }, + + // 获取文档处理列表 + getKnowledgeSegmentProcessList: async (documentIds: number[]) => { + return await request.get({ + url: `/ai/knowledge/segment/get-process-list`, + params: { documentIds: documentIds.join(',') } + }) + }, + + // 搜索知识库分段 + searchKnowledgeSegment: async (params: any) => { + return await request.get({ + url: `/ai/knowledge/segment/search`, + params + }) + } +} diff --git a/src/api/ai/mindmap/index.ts b/src/api/ai/mindmap/index.ts new file mode 100644 index 0000000..59b4fd8 --- /dev/null +++ b/src/api/ai/mindmap/index.ts @@ -0,0 +1,60 @@ +import { getAccessToken } from '@/utils/auth' +import { fetchEventSource } from '@microsoft/fetch-event-source' +import { config } from '@/config/axios/config' +import request from '@/config/axios' // AI 思维导图 VO + +// AI 思维导图 VO +export interface MindMapVO { + id: number // 编号 + userId: number // 用户编号 + prompt: string // 生成内容提示 + generatedContent: string // 生成的思维导图内容 + platform: string // 平台 + model: string // 模型 + errorMessage: string // 错误信息 +} + +// AI 思维导图生成 VO +export interface AiMindMapGenerateReqVO { + prompt: string +} + +export const AiMindMapApi = { + generateMindMap: ({ + data, + onClose, + onMessage, + onError, + ctrl + }: { + data: AiMindMapGenerateReqVO + onMessage?: (res: any) => void + onError?: (...args: any[]) => void + onClose?: (...args: any[]) => void + ctrl: AbortController + }) => { + const token = getAccessToken() + return fetchEventSource(`${config.base_url}/ai/mind-map/generate-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + openWhenHidden: true, + body: JSON.stringify(data), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + }) + }, + + // 查询思维导图分页 + getMindMapPage: async (params: any) => { + return await request.get({ url: `/ai/mind-map/page`, params }) + }, + // 删除思维导图 + deleteMindMap: async (id: number) => { + return await request.delete({ url: `/ai/mind-map/delete?id=` + id }) + } +} diff --git a/src/api/ai/model/apiKey/index.ts b/src/api/ai/model/apiKey/index.ts new file mode 100644 index 0000000..ed94836 --- /dev/null +++ b/src/api/ai/model/apiKey/index.ts @@ -0,0 +1,44 @@ +import request from '@/config/axios' + +// AI API 密钥 VO +export interface ApiKeyVO { + id: number // 编号 + name: string // 名称 + apiKey: string // 密钥 + platform: string // 平台 + url: string // 自定义 API 地址 + status: number // 状态 +} + +// AI API 密钥 API +export const ApiKeyApi = { + // 查询 API 密钥分页 + getApiKeyPage: async (params: any) => { + return await request.get({ url: `/ai/api-key/page`, params }) + }, + + // 获得 API 密钥列表 + getApiKeySimpleList: async () => { + return await request.get({ url: `/ai/api-key/simple-list` }) + }, + + // 查询 API 密钥详情 + getApiKey: async (id: number) => { + return await request.get({ url: `/ai/api-key/get?id=` + id }) + }, + + // 新增 API 密钥 + createApiKey: async (data: ApiKeyVO) => { + return await request.post({ url: `/ai/api-key/create`, data }) + }, + + // 修改 API 密钥 + updateApiKey: async (data: ApiKeyVO) => { + return await request.put({ url: `/ai/api-key/update`, data }) + }, + + // 删除 API 密钥 + deleteApiKey: async (id: number) => { + return await request.delete({ url: `/ai/api-key/delete?id=` + id }) + } +} diff --git a/src/api/ai/model/chatRole/index.ts b/src/api/ai/model/chatRole/index.ts new file mode 100644 index 0000000..6c5511a --- /dev/null +++ b/src/api/ai/model/chatRole/index.ts @@ -0,0 +1,82 @@ +import request from '@/config/axios' + +// AI 聊天角色 VO +export interface ChatRoleVO { + id: number // 角色编号 + modelId: number // 模型编号 + name: string // 角色名称 + avatar: string // 角色头像 + category: string // 角色类别 + sort: number // 角色排序 + description: string // 角色描述 + systemMessage: string // 角色设定 + welcomeMessage: string // 角色设定 + publicStatus: boolean // 是否公开 + status: number // 状态 + knowledgeIds?: number[] // 引用的知识库 ID 列表 + toolIds?: number[] // 引用的工具 ID 列表 +} + +// AI 聊天角色 分页请求 vo +export interface ChatRolePageReqVO { + name?: string // 角色名称 + category?: string // 角色类别 + publicStatus: boolean // 是否公开 + pageNo: number // 是否公开 + pageSize: number // 是否公开 +} + +// AI 聊天角色 API +export const ChatRoleApi = { + // 查询聊天角色分页 + getChatRolePage: async (params: any) => { + return await request.get({ url: `/ai/chat-role/page`, params }) + }, + + // 查询聊天角色详情 + getChatRole: async (id: number) => { + return await request.get({ url: `/ai/chat-role/get?id=` + id }) + }, + + // 新增聊天角色 + createChatRole: async (data: ChatRoleVO) => { + return await request.post({ url: `/ai/chat-role/create`, data }) + }, + + // 修改聊天角色 + updateChatRole: async (data: ChatRoleVO) => { + return await request.put({ url: `/ai/chat-role/update`, data }) + }, + + // 删除聊天角色 + deleteChatRole: async (id: number) => { + return await request.delete({ url: `/ai/chat-role/delete?id=` + id }) + }, + + // ======= chat 聊天 + + // 获取 my role + getMyPage: async (params: ChatRolePageReqVO) => { + return await request.get({ url: `/ai/chat-role/my-page`, params}) + }, + + // 获取角色分类 + getCategoryList: async () => { + return await request.get({ url: `/ai/chat-role/category-list`}) + }, + + // 创建角色 + createMy: async (data: ChatRoleVO) => { + return await request.post({ url: `/ai/chat-role/create-my`, data}) + }, + + // 更新角色 + updateMy: async (data: ChatRoleVO) => { + return await request.put({ url: `/ai/chat-role/update-my`, data}) + }, + + // 删除角色 my + deleteMy: async (id: number) => { + return await request.delete({ url: `/ai/chat-role/delete-my?id=` + id }) + }, +} diff --git a/src/api/ai/model/model/index.ts b/src/api/ai/model/model/index.ts new file mode 100644 index 0000000..7c485a0 --- /dev/null +++ b/src/api/ai/model/model/index.ts @@ -0,0 +1,54 @@ +import request from '@/config/axios' + +// AI 模型 VO +export interface ModelVO { + id: number // 编号 + keyId: number // API 秘钥编号 + name: string // 模型名字 + model: string // 模型标识 + platform: string // 模型平台 + type: number // 模型类型 + sort: number // 排序 + status: number // 状态 + temperature?: number // 温度参数 + maxTokens?: number // 单条回复的最大 Token 数量 + maxContexts?: number // 上下文的最大 Message 数量 +} + +// AI 模型 API +export const ModelApi = { + // 查询模型分页 + getModelPage: async (params: any) => { + return await request.get({ url: `/ai/model/page`, params }) + }, + + // 获得模型列表 + getModelSimpleList: async (type?: number) => { + return await request.get({ + url: `/ai/model/simple-list`, + params: { + type + } + }) + }, + + // 查询模型详情 + getModel: async (id: number) => { + return await request.get({ url: `/ai/model/get?id=` + id }) + }, + + // 新增模型 + createModel: async (data: ModelVO) => { + return await request.post({ url: `/ai/model/create`, data }) + }, + + // 修改模型 + updateModel: async (data: ModelVO) => { + return await request.put({ url: `/ai/model/update`, data }) + }, + + // 删除模型 + deleteModel: async (id: number) => { + return await request.delete({ url: `/ai/model/delete?id=` + id }) + } +} diff --git a/src/api/ai/model/tool/index.ts b/src/api/ai/model/tool/index.ts new file mode 100644 index 0000000..bfb896a --- /dev/null +++ b/src/api/ai/model/tool/index.ts @@ -0,0 +1,42 @@ +import request from '@/config/axios' + +// AI 工具 VO +export interface ToolVO { + id: number // 工具编号 + name: string // 工具名称 + description: string // 工具描述 + status: number // 状态 +} + +// AI 工具 API +export const ToolApi = { + // 查询工具分页 + getToolPage: async (params: any) => { + return await request.get({ url: `/ai/tool/page`, params }) + }, + + // 查询工具详情 + getTool: async (id: number) => { + return await request.get({ url: `/ai/tool/get?id=` + id }) + }, + + // 新增工具 + createTool: async (data: ToolVO) => { + return await request.post({ url: `/ai/tool/create`, data }) + }, + + // 修改工具 + updateTool: async (data: ToolVO) => { + return await request.put({ url: `/ai/tool/update`, data }) + }, + + // 删除工具 + deleteTool: async (id: number) => { + return await request.delete({ url: `/ai/tool/delete?id=` + id }) + }, + + // 获取工具简单列表 + getToolSimpleList: async () => { + return await request.get({ url: `/ai/tool/simple-list` }) + } +} diff --git a/src/api/ai/music/index.ts b/src/api/ai/music/index.ts new file mode 100644 index 0000000..74b8526 --- /dev/null +++ b/src/api/ai/music/index.ts @@ -0,0 +1,41 @@ +import request from '@/config/axios' + +// AI 音乐 VO +export interface MusicVO { + id: number // 编号 + userId: number // 用户编号 + title: string // 音乐名称 + lyric: string // 歌词 + imageUrl: string // 图片地址 + audioUrl: string // 音频地址 + videoUrl: string // 视频地址 + status: number // 音乐状态 + gptDescriptionPrompt: string // 描述词 + prompt: string // 提示词 + platform: string // 模型平台 + model: string // 模型 + generateMode: number // 生成模式 + tags: string // 音乐风格标签 + duration: number // 音乐时长 + publicStatus: boolean // 是否发布 + taskId: string // 任务id + errorMessage: string // 错误信息 +} + +// AI 音乐 API +export const MusicApi = { + // 查询音乐分页 + getMusicPage: async (params: any) => { + return await request.get({ url: `/ai/music/page`, params }) + }, + + // 更新音乐 + updateMusic: async (data: any) => { + return await request.put({ url: '/ai/music/update', data }) + }, + + // 删除音乐 + deleteMusic: async (id: number) => { + return await request.delete({ url: `/ai/music/delete?id=` + id }) + } +} diff --git a/src/api/ai/questionparamsetting/index.js b/src/api/ai/questionparamsetting/index.js new file mode 100644 index 0000000..6c337b3 --- /dev/null +++ b/src/api/ai/questionparamsetting/index.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 创建大模型问题设置参数 +export function createQuestionParamSetting(data) { + return request({ + url: '/ai/question-param-setting/create', + method: 'post', + data: data + }) +} + +// 更新大模型问题设置参数 +export function updateQuestionParamSetting(data) { + return request({ + url: '/ai/question-param-setting/update', + method: 'put', + data: data + }) +} + +// 删除大模型问题设置参数 +export function deleteQuestionParamSetting(id) { + return request({ + url: '/ai/question-param-setting/delete?id=' + id, + method: 'delete' + }) +} + +// 获得大模型问题设置参数 +export function getQuestionParamSetting(id) { + return request({ + url: '/ai/question-param-setting/get?id=' + id, + method: 'get' + }) +} + +// 获得大模型问题设置参数分页 +export function getQuestionParamSettingPage(params) { + return request({ + url: '/ai/question-param-setting/page', + method: 'get', + params + }) +} +// 导出大模型问题设置参数 Excel +export function exportQuestionParamSettingExcel(params) { + return request({ + url: '/ai/question-param-setting/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} \ No newline at end of file diff --git a/src/api/ai/questiontemplate/index.js b/src/api/ai/questiontemplate/index.js new file mode 100644 index 0000000..520ba3f --- /dev/null +++ b/src/api/ai/questiontemplate/index.js @@ -0,0 +1,53 @@ +import request from '@/utils/request' + +// 创建大模型问题模板 +export function createQuestionTemplate(data) { + return request({ + url: '/ai/question-template/create', + method: 'post', + data: data + }) +} + +// 更新大模型问题模板 +export function updateQuestionTemplate(data) { + return request({ + url: '/ai/question-template/update', + method: 'put', + data: data + }) +} + +// 删除大模型问题模板 +export function deleteQuestionTemplate(id) { + return request({ + url: '/ai/question-template/delete?id=' + id, + method: 'delete' + }) +} + +// 获得大模型问题模板 +export function getQuestionTemplate(id) { + return request({ + url: '/ai/question-template/get?id=' + id, + method: 'get' + }) +} + +// 获得大模型问题模板分页 +export function getQuestionTemplatePage(params) { + return request({ + url: '/ai/question-template/page', + method: 'get', + params + }) +} +// 导出大模型问题模板 Excel +export function exportQuestionTemplateExcel(params) { + return request({ + url: '/ai/question-template/export-excel', + method: 'get', + params, + responseType: 'blob' + }) +} \ No newline at end of file diff --git a/src/api/ai/workflow/index.ts b/src/api/ai/workflow/index.ts new file mode 100644 index 0000000..985ae90 --- /dev/null +++ b/src/api/ai/workflow/index.ts @@ -0,0 +1,25 @@ +import request from '@/config/axios' + +export const getWorkflowPage = async (params) => { + return await request.get({ url: '/ai/workflow/page', params }) +} + +export const getWorkflow = async (id) => { + return await request.get({ url: '/ai/workflow/get?id=' + id }) +} + +export const createWorkflow = async (data) => { + return await request.post({ url: '/ai/workflow/create', data }) +} + +export const updateWorkflow = async (data) => { + return await request.put({ url: '/ai/workflow/update', data }) +} + +export const deleteWorkflow = async (id) => { + return await request.delete({ url: '/ai/workflow/delete?id=' + id }) +} + +export const updateWorkflowModel = async (data) => { + return await request.put({ url: '/ai/workflow/updateWorkflowModel', data }) +} diff --git a/src/api/ai/write/index.ts b/src/api/ai/write/index.ts new file mode 100644 index 0000000..013f998 --- /dev/null +++ b/src/api/ai/write/index.ts @@ -0,0 +1,85 @@ +import { fetchEventSource } from '@microsoft/fetch-event-source' + +import { getAccessToken } from '@/utils/auth' +import { config } from '@/config/axios/config' +import { AiWriteTypeEnum } from '@/views/ai/utils/constants' +import request from '@/config/axios' + +export interface WriteVO { + type: AiWriteTypeEnum.WRITING | AiWriteTypeEnum.REPLY // 1:撰写 2:回复 + prompt: string // 写作内容提示 1。撰写 2回复 + originalContent: string // 原文 + length: number // 长度 + format: number // 格式 + tone: number // 语气 + language: number // 语言 + userId?: number // 用户编号 + platform?: string // 平台 + model?: string // 模型 + generatedContent?: string // 生成的内容 + errorMessage?: string // 错误信息 + createTime?: Date // 创建时间 +} + +export interface AiWritePageReqVO extends PageParam { + userId?: number // 用户编号 + type?: AiWriteTypeEnum // 写作类型 + platform?: string // 平台 + createTime?: [string, string] // 创建时间 +} + +export interface AiWriteRespVo { + id: number + userId: number + type: number + platform: string + model: string + prompt: string + generatedContent: string + originalContent: string + length: number + format: number + tone: number + language: number + errorMessage: string + createTime: string +} + +export const WriteApi = { + writeStream: ({ + data, + onClose, + onMessage, + onError, + ctrl + }: { + data: WriteVO + onMessage?: (res: any) => void + onError?: (...args: any[]) => void + onClose?: (...args: any[]) => void + ctrl: AbortController + }) => { + const token = getAccessToken() + return fetchEventSource(`${config.base_url}/ai/write/generate-stream`, { + method: 'post', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}` + }, + openWhenHidden: true, + body: JSON.stringify(data), + onmessage: onMessage, + onerror: onError, + onclose: onClose, + signal: ctrl.signal + }) + }, + // 获取写作列表 + getWritePage: (params: AiWritePageReqVO) => { + return request.get<PageResult<AiWriteRespVo[]>>({ url: `/ai/write/page`, params }) + }, + // 删除写作 + deleteWrite(id: number) { + return request.delete({ url: `/ai/write/delete`, params: { id } }) + } +} diff --git a/src/api/infra/monitordisk/index.ts b/src/api/infra/monitordisk/index.ts index e9a2512..7b56e74 100644 --- a/src/api/infra/monitordisk/index.ts +++ b/src/api/infra/monitordisk/index.ts @@ -25,6 +25,16 @@ return await request.get({ url: `/infra/monitor-disk/getMonitorDiskList`, params }) }, + // 查询主机列表 + getAllHost: async () => { + return await request.get({ url: `/infra/monitor-disk/getAllHost`}) + }, + + // 查询ip列表 + getAllIp: async () => { + return await request.get({ url: `/infra/monitor-disk/getAllIp`}) + }, + // 查询磁盘监控日志信息 getMonitorDiskInfo: async (params: any) => { return await request.get({ url: `/infra/monitor-disk/getMonitorDiskInfo`, params }) diff --git a/src/api/infra/monitormem/index.ts b/src/api/infra/monitormem/index.ts index c402272..16bef62 100644 --- a/src/api/infra/monitormem/index.ts +++ b/src/api/infra/monitormem/index.ts @@ -34,6 +34,21 @@ return await request.get({ url: `/infra/monitor-mem/get?id=` + id }) }, + // 查询主机列表 + getAllHost: async () => { + return await request.get({ url: `/infra/monitor-mem/getAllHost`}) + }, + + // 查询ip列表 + getAllIp: async () => { + return await request.get({ url: `/infra/monitor-mem/getAllIp`}) + }, + + // 查询服务列表 + getAllServer: async () => { + return await request.get({ url: `/infra/monitor-mem/getAllServer`}) + }, + // 新增内存监控日志 createMonitorMem: async (data: MonitorMemVO) => { return await request.post({ url: `/infra/monitor-mem/create`, data }) diff --git a/src/assets/ai/zhuanlu/assistant.png b/src/assets/ai/zhuanlu/assistant.png new file mode 100644 index 0000000..6ac16ef --- /dev/null +++ b/src/assets/ai/zhuanlu/assistant.png Binary files differ diff --git a/src/assets/ai/zhuanlu/bg.png b/src/assets/ai/zhuanlu/bg.png new file mode 100644 index 0000000..e01ae51 --- /dev/null +++ b/src/assets/ai/zhuanlu/bg.png Binary files differ diff --git a/src/assets/ai/zhuanlu/common_title.png b/src/assets/ai/zhuanlu/common_title.png new file mode 100644 index 0000000..7670b91 --- /dev/null +++ b/src/assets/ai/zhuanlu/common_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/content.png b/src/assets/ai/zhuanlu/content.png new file mode 100644 index 0000000..52e59ce --- /dev/null +++ b/src/assets/ai/zhuanlu/content.png Binary files differ diff --git a/src/assets/ai/zhuanlu/content_select.png b/src/assets/ai/zhuanlu/content_select.png new file mode 100644 index 0000000..d6b928f --- /dev/null +++ b/src/assets/ai/zhuanlu/content_select.png Binary files differ diff --git a/src/assets/ai/zhuanlu/conversation.png b/src/assets/ai/zhuanlu/conversation.png new file mode 100644 index 0000000..48b840f --- /dev/null +++ b/src/assets/ai/zhuanlu/conversation.png Binary files differ diff --git a/src/assets/ai/zhuanlu/conversation_big.png b/src/assets/ai/zhuanlu/conversation_big.png new file mode 100644 index 0000000..b51c940 --- /dev/null +++ b/src/assets/ai/zhuanlu/conversation_big.png Binary files differ diff --git a/src/assets/ai/zhuanlu/copy.png b/src/assets/ai/zhuanlu/copy.png new file mode 100644 index 0000000..f148cba --- /dev/null +++ b/src/assets/ai/zhuanlu/copy.png Binary files differ diff --git a/src/assets/ai/zhuanlu/data_bg1.png b/src/assets/ai/zhuanlu/data_bg1.png new file mode 100644 index 0000000..7b66c79 --- /dev/null +++ b/src/assets/ai/zhuanlu/data_bg1.png Binary files differ diff --git a/src/assets/ai/zhuanlu/data_bg2.png b/src/assets/ai/zhuanlu/data_bg2.png new file mode 100644 index 0000000..b8da4db --- /dev/null +++ b/src/assets/ai/zhuanlu/data_bg2.png Binary files differ diff --git a/src/assets/ai/zhuanlu/data_bg3.png b/src/assets/ai/zhuanlu/data_bg3.png new file mode 100644 index 0000000..bdcbe84 --- /dev/null +++ b/src/assets/ai/zhuanlu/data_bg3.png Binary files differ diff --git a/src/assets/ai/zhuanlu/data_bg4.png b/src/assets/ai/zhuanlu/data_bg4.png new file mode 100644 index 0000000..1f5f878 --- /dev/null +++ b/src/assets/ai/zhuanlu/data_bg4.png Binary files differ diff --git a/src/assets/ai/zhuanlu/ddtljl_result_title.png b/src/assets/ai/zhuanlu/ddtljl_result_title.png new file mode 100644 index 0000000..2162562 --- /dev/null +++ b/src/assets/ai/zhuanlu/ddtljl_result_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/delete.png b/src/assets/ai/zhuanlu/delete.png new file mode 100644 index 0000000..18f3848 --- /dev/null +++ b/src/assets/ai/zhuanlu/delete.png Binary files differ diff --git a/src/assets/ai/zhuanlu/edit.png b/src/assets/ai/zhuanlu/edit.png new file mode 100644 index 0000000..10254bb --- /dev/null +++ b/src/assets/ai/zhuanlu/edit.png Binary files differ diff --git a/src/assets/ai/zhuanlu/icon_bg.png b/src/assets/ai/zhuanlu/icon_bg.png new file mode 100644 index 0000000..01c8235 --- /dev/null +++ b/src/assets/ai/zhuanlu/icon_bg.png Binary files differ diff --git a/src/assets/ai/zhuanlu/icon_conversion.png b/src/assets/ai/zhuanlu/icon_conversion.png new file mode 100644 index 0000000..48b840f --- /dev/null +++ b/src/assets/ai/zhuanlu/icon_conversion.png Binary files differ diff --git a/src/assets/ai/zhuanlu/ldggrqsyc_title.png b/src/assets/ai/zhuanlu/ldggrqsyc_title.png new file mode 100644 index 0000000..4619d97 --- /dev/null +++ b/src/assets/ai/zhuanlu/ldggrqsyc_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/ldghslyc_title.png b/src/assets/ai/zhuanlu/ldghslyc_title.png new file mode 100644 index 0000000..d561e77 --- /dev/null +++ b/src/assets/ai/zhuanlu/ldghslyc_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/left_label.png b/src/assets/ai/zhuanlu/left_label.png new file mode 100644 index 0000000..2807509 --- /dev/null +++ b/src/assets/ai/zhuanlu/left_label.png Binary files differ diff --git a/src/assets/ai/zhuanlu/mqhsjhxx_title.png b/src/assets/ai/zhuanlu/mqhsjhxx_title.png new file mode 100644 index 0000000..b5f2526 --- /dev/null +++ b/src/assets/ai/zhuanlu/mqhsjhxx_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/mqhsssxx_title.png b/src/assets/ai/zhuanlu/mqhsssxx_title.png new file mode 100644 index 0000000..adabbc4 --- /dev/null +++ b/src/assets/ai/zhuanlu/mqhsssxx_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/mqxhssxx_title.png b/src/assets/ai/zhuanlu/mqxhssxx_title.png new file mode 100644 index 0000000..02d84f9 --- /dev/null +++ b/src/assets/ai/zhuanlu/mqxhssxx_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/refresh.png b/src/assets/ai/zhuanlu/refresh.png new file mode 100644 index 0000000..96e2c9b --- /dev/null +++ b/src/assets/ai/zhuanlu/refresh.png Binary files differ diff --git a/src/assets/ai/zhuanlu/right_label.png b/src/assets/ai/zhuanlu/right_label.png new file mode 100644 index 0000000..a74da5b --- /dev/null +++ b/src/assets/ai/zhuanlu/right_label.png Binary files differ diff --git a/src/assets/ai/zhuanlu/scmbyyxzb_title.png b/src/assets/ai/zhuanlu/scmbyyxzb_title.png new file mode 100644 index 0000000..912ee9b --- /dev/null +++ b/src/assets/ai/zhuanlu/scmbyyxzb_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/table_header_bg.png b/src/assets/ai/zhuanlu/table_header_bg.png new file mode 100644 index 0000000..034e76e --- /dev/null +++ b/src/assets/ai/zhuanlu/table_header_bg.png Binary files differ diff --git a/src/assets/ai/zhuanlu/think_bg.png b/src/assets/ai/zhuanlu/think_bg.png new file mode 100644 index 0000000..363a9bd --- /dev/null +++ b/src/assets/ai/zhuanlu/think_bg.png Binary files differ diff --git a/src/assets/ai/zhuanlu/tsxx_title.png b/src/assets/ai/zhuanlu/tsxx_title.png new file mode 100644 index 0000000..a722512 --- /dev/null +++ b/src/assets/ai/zhuanlu/tsxx_title.png Binary files differ diff --git a/src/assets/ai/zhuanlu/user.png b/src/assets/ai/zhuanlu/user.png new file mode 100644 index 0000000..e55f54f --- /dev/null +++ b/src/assets/ai/zhuanlu/user.png Binary files differ diff --git a/src/assets/ai/zhuanlu/zlxx_title.png b/src/assets/ai/zhuanlu/zlxx_title.png new file mode 100644 index 0000000..6e779cc --- /dev/null +++ b/src/assets/ai/zhuanlu/zlxx_title.png Binary files differ diff --git a/src/components/Dialog/src/DialogDashboard.vue b/src/components/Dialog/src/DialogDashboard.vue new file mode 100644 index 0000000..358931b --- /dev/null +++ b/src/components/Dialog/src/DialogDashboard.vue @@ -0,0 +1,118 @@ +<script lang="ts" setup> +import { propTypes } from '@/utils/propTypes' +import { isNumber } from '@/utils/is' +defineOptions({ name: 'DialogDashboard' }) + +const slots = useSlots() + +const props = defineProps({ + modelValue: propTypes.bool.def(false), + title: propTypes.string.def('Dialog'), + fullscreen: propTypes.bool.def(true), + width: propTypes.oneOfType([String, Number]).def('30%'), + scroll: propTypes.bool.def(false), // 是否开启滚动条。如果是的话,按照 maxHeight 设置最大高度 + maxHeight: propTypes.oneOfType([String, Number]).def('400px') +}) + +const getBindValue = computed(() => { + const delArr: string[] = ['fullscreen', 'title', 'maxHeight', 'appendToBody'] + const attrs = useAttrs() + const obj = { ...attrs, ...props } + for (const key in obj) { + if (delArr.indexOf(key) !== -1) { + delete obj[key] + } + } + return obj +}) + +</script> + +<template> + <ElDialog + v-bind="getBindValue" + :close-on-click-modal="true" + :width="width" + destroy-on-close + lock-scroll + draggable + class="dashboard-dialog" + :show-close="false" + > + <template #header="{ close }"> + <div class="relative h-30px flex items-center justify-between pl-15px pr-15px"> + <slot name="title"> + {{ title }} + </slot> + <div + class="absolute right-15px top-[50%] h-40px flex translate-y-[-50%] items-center justify-between" + > + <Icon + class="is-hover cursor-pointer" + icon="ep:close" + hover-color="var(--el-color-primary)" + color="#73C4FF" + @click="close" + /> + </div> + </div> + </template> + + <slot></slot> + <template v-if="slots.footer" #footer> + <slot name="footer"></slot> + </template> + </ElDialog> +</template> + +<style lang="scss"> +.el-dialog.is-draggable .el-dialog__header { + color: white !important; +} +::v-deep .el-input { + background: rgba(0,194,255,0.08) !important; +} +::v-deep .el-input__inner { + color: #8FD6FE; + background: rgba(0,194,255,0.08) !important; + border: 1px solid #1D9FE8 +} +.dashboard-dialog { + color: #73C4FF; + background: rgba(3,29,76,0.79); + border-radius: 4px 4px 4px 4px; + border: 1px solid; + .#{$elNamespace}-overlay-dialog { + display: flex; + justify-content: center; + align-items: center; + } + + .#{$elNamespace}-dialog { + margin: 0 !important; + + &__header { + height: 40px; + padding: 0; + margin-right: 0 !important; + margin-bottom: 20px; + background: + url("@/assets/ai/zhuanlu/common_title.png") no-repeat, + linear-gradient(to bottom, #0a1633dd, #0a1633dd); /* 叠加深色遮罩 */ + div { + color: #73C4FF; + margin-left: 20px; + } + } + + &__body { + padding: 15px !important; + } + + &__headerbtn { + color: #73C4FF; + top: 0; + } + } +} +</style> diff --git a/src/components/Dialog/src/DialogHistory.vue b/src/components/Dialog/src/DialogHistory.vue new file mode 100644 index 0000000..0b3dd5a --- /dev/null +++ b/src/components/Dialog/src/DialogHistory.vue @@ -0,0 +1,138 @@ +<script lang="ts" setup> +import { propTypes } from '@/utils/propTypes' +import { isNumber } from '@/utils/is' +defineOptions({ name: 'DialogHistory' }) + +const slots = useSlots() + +const props = defineProps({ + modelValue: propTypes.bool.def(false), + title: propTypes.string.def('Dialog'), + fullscreen: propTypes.bool.def(true), + width: propTypes.oneOfType([String, Number]).def('30%'), + scroll: propTypes.bool.def(false), // 是否开启滚动条。如果是的话,按照 maxHeight 设置最大高度 + maxHeight: propTypes.oneOfType([String, Number]).def('400px') +}) + +const getBindValue = computed(() => { + const delArr: string[] = ['fullscreen', 'title', 'maxHeight', 'appendToBody'] + const attrs = useAttrs() + const obj = { ...attrs, ...props } + for (const key in obj) { + if (delArr.indexOf(key) !== -1) { + delete obj[key] + } + } + return obj +}) + +const isFullscreen = ref(false) + +const toggleFull = () => { + isFullscreen.value = !unref(isFullscreen) +} + +const dialogHeight = ref(isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight) + +watch( + () => isFullscreen.value, + async (val: boolean) => { + await nextTick() + if (val) { + const windowHeight = document.documentElement.offsetHeight + dialogHeight.value = `${windowHeight - 55 - 60 - (slots.footer ? 63 : 0)}px` + } else { + dialogHeight.value = isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight + } + }, + { + immediate: true + } +) + +</script> + +<template> + <ElDialog + v-bind="getBindValue" + :fullscreen="isFullscreen" + :close-on-click-modal="true" + :width="width" + destroy-on-close + lock-scroll + draggable + class="history-dialog" + :show-close="false" + > + <template #header="{ close }"> + <div class="relative h-30px flex items-center justify-between pl-15px pr-15px"> + <slot name="title"> + {{ title }} + </slot> + <div + class="absolute right-15px top-[50%] h-40px flex translate-y-[-50%] items-center justify-between" + > + <Icon + v-if="fullscreen" + class="is-hover mr-10px cursor-pointer" + :icon="isFullscreen ? 'radix-icons:exit-full-screen' : 'radix-icons:enter-full-screen'" + color="#73C4FF" + hover-color="var(--el-color-primary)" + @click="toggleFull" + /> + <Icon + class="is-hover cursor-pointer" + icon="ep:close" + hover-color="var(--el-color-primary)" + color="#73C4FF" + @click="close" + /> + </div> + </div> + </template> + + <slot></slot> + <template v-if="slots.footer" #footer> + <slot name="footer"></slot> + </template> + </ElDialog> +</template> + +<style lang="scss"> +.history-dialog { + height: 90vh; + margin-top: 30px; + color: #73C4FF; + overflow: hidden; /* 防止内容溢出 */ + background: rgba(3,29,76,0.79); + border-radius: 4px 4px 4px 4px; + border: 1px solid; + .#{$elNamespace}-overlay-dialog { + display: flex; + justify-content: center; + align-items: center; + } + + .#{$elNamespace}-dialog { + margin: 0 !important; + + &__header { + height: 40px; + padding: 0; + margin-right: 0 !important; + background: + url("@/assets/ai/zhuanlu/common_title.png") left no-repeat, + linear-gradient(to bottom, #0a1633dd, #0a1633dd); /* 叠加深色遮罩 */ + div { + color: #73C4FF; + margin-left: 20px; + } + } + + &__headerbtn { + color: #73C4FF; + top: 0; + } + } +} +</style> diff --git a/src/components/MarkdownView/index.vue b/src/components/MarkdownView/index.vue index 74764d5..65541f6 100644 --- a/src/components/MarkdownView/index.vue +++ b/src/components/MarkdownView/index.vue @@ -34,8 +34,16 @@ /** 渲染 markdown */ const renderedMarkdown = computed(() => { - return md.render(props.content) + return formatContent(props.content) }) + +/** 保留换行符 */ +const formatContent = (text) => { + if (text) { + return text.replace(/\n/g, '<br>') + } + return text +} /** 初始化 **/ onMounted(async () => { @@ -51,14 +59,25 @@ <style lang="scss"> .markdown-view { - font-family: PingFang SC; - font-size: 0.95rem; - font-weight: 400; - line-height: 1.6rem; - letter-spacing: 0em; - text-align: left; - color: #3b3e55; max-width: 100%; + overflow-y: auto; /* 垂直方向溢出时显示滚动条 */ + overflow-x: hidden; /* 水平方向隐藏滚动条 */ + + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.15) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } pre { position: relative; diff --git a/src/components/Tinyflow/Tinyflow.vue b/src/components/Tinyflow/Tinyflow.vue new file mode 100644 index 0000000..6c4f21c --- /dev/null +++ b/src/components/Tinyflow/Tinyflow.vue @@ -0,0 +1,63 @@ +<template> + <div ref="divRef" :class="['tinyflow', className]" :style="style" style="height: 100%"> </div> +</template> + +<script setup lang="ts"> +import { Item, Tinyflow as TinyflowNative } from './ui' +import './ui/index.css' +import { onMounted, onUnmounted, ref } from 'vue' + +const props = defineProps<{ + className?: string + style?: Record<string, string> + data?: Record<string, any> + provider?: { + llm?: () => Item[] | Promise<Item[]> + knowledge?: () => Item[] | Promise<Item[]> + internal?: () => Item[] | Promise<Item[]> + } +}>() + +const divRef = ref<HTMLDivElement | null>(null) +let tinyflow: TinyflowNative | null = null +// 定义默认的 provider 方法 +const defaultProvider = { + llm: () => [] as Item[], + knowledge: () => [] as Item[], + internal: () => [] as Item[] +} + +onMounted(() => { + if (divRef.value) { + // 合并默认 provider 和传入的 props.provider + const mergedProvider = { + ...defaultProvider, + ...props.provider + } + tinyflow = new TinyflowNative({ + element: divRef.value as Element, + data: props.data || {}, + provider: mergedProvider + }) + } +}) + +onUnmounted(() => { + if (tinyflow) { + tinyflow.destroy() + tinyflow = null + } +}) + +const getData = () => { + if (tinyflow) { + return tinyflow.getData() + } + console.warn('Tinyflow instance is not initialized') + return null +} + +defineExpose({ + getData +}) +</script> diff --git a/src/components/Tinyflow/ui/index.css b/src/components/Tinyflow/ui/index.css new file mode 100644 index 0000000..8fa10c2 --- /dev/null +++ b/src/components/Tinyflow/ui/index.css @@ -0,0 +1 @@ +.svelte-flow{direction:ltr;--xy-edge-stroke-default: #b1b1b7;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #555;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(255, 255, 255, .5);--xy-minimap-background-color-default: #fff;--xy-minimap-mask-background-color-default: rgb(240, 240, 240, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #e2e2e2;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: transparent;--xy-background-pattern-dots-color-default: #91919a;--xy-background-pattern-lines-color-default: #eee;--xy-background-pattern-cross-color-default: #e2e2e2;background-color:var(--xy-background-color, var(--xy-background-color-default));--xy-node-color-default: inherit;--xy-node-border-default: 1px solid #1a192b;--xy-node-background-color-default: #fff;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #1a192b;--xy-node-border-radius-default: 3px;--xy-handle-background-color-default: #1a192b;--xy-handle-border-color-default: #fff;--xy-selection-background-color-default: rgba(0, 89, 220, .08);--xy-selection-border-default: 1px dotted rgba(0, 89, 220, .8);--xy-controls-button-background-color-default: #fefefe;--xy-controls-button-background-color-hover-default: #f4f4f4;--xy-controls-button-color-default: inherit;--xy-controls-button-color-hover-default: inherit;--xy-controls-button-border-color-default: #eee;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #ffffff;--xy-edge-label-color-default: inherit;--xy-resize-background-color-default: #3367d9}.svelte-flow.dark{--xy-edge-stroke-default: #3e3e3e;--xy-edge-stroke-width-default: 1;--xy-edge-stroke-selected-default: #727272;--xy-connectionline-stroke-default: #b1b1b7;--xy-connectionline-stroke-width-default: 1;--xy-attribution-background-color-default: rgba(150, 150, 150, .25);--xy-minimap-background-color-default: #141414;--xy-minimap-mask-background-color-default: rgb(60, 60, 60, .6);--xy-minimap-mask-stroke-color-default: transparent;--xy-minimap-mask-stroke-width-default: 1;--xy-minimap-node-background-color-default: #2b2b2b;--xy-minimap-node-stroke-color-default: transparent;--xy-minimap-node-stroke-width-default: 2;--xy-background-color-default: #141414;--xy-background-pattern-dots-color-default: #777;--xy-background-pattern-lines-color-default: #777;--xy-background-pattern-cross-color-default: #777;--xy-node-color-default: #f8f8f8;--xy-node-border-default: 1px solid #3c3c3c;--xy-node-background-color-default: #1e1e1e;--xy-node-group-background-color-default: rgba(240, 240, 240, .25);--xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, .08);--xy-node-boxshadow-selected-default: 0 0 0 .5px #999;--xy-handle-background-color-default: #bebebe;--xy-handle-border-color-default: #1e1e1e;--xy-selection-background-color-default: rgba(200, 200, 220, .08);--xy-selection-border-default: 1px dotted rgba(200, 200, 220, .8);--xy-controls-button-background-color-default: #2b2b2b;--xy-controls-button-background-color-hover-default: #3e3e3e;--xy-controls-button-color-default: #f8f8f8;--xy-controls-button-color-hover-default: #fff;--xy-controls-button-border-color-default: #5b5b5b;--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, .08);--xy-edge-label-background-color-default: #141414;--xy-edge-label-color-default: #f8f8f8}.svelte-flow__background{background-color:var(--xy-background-color, var(--xy-background-color-props, var(--xy-background-color-default)));pointer-events:none;z-index:-1}.svelte-flow__container{position:absolute;width:100%;height:100%;top:0;left:0}.svelte-flow__pane{z-index:1}.svelte-flow__pane.draggable{cursor:grab}.svelte-flow__pane.dragging{cursor:grabbing}.svelte-flow__pane.selection{cursor:pointer}.svelte-flow__viewport{transform-origin:0 0;z-index:2;pointer-events:none}.svelte-flow__renderer{z-index:4}.svelte-flow__selection{z-index:6}.svelte-flow__nodesselection-rect:focus,.svelte-flow__nodesselection-rect:focus-visible{outline:none}.svelte-flow__edge-path{stroke:var(--xy-edge-stroke, var(--xy-edge-stroke-default));stroke-width:var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default));fill:none}.svelte-flow__connection-path{stroke:var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default));stroke-width:var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default));fill:none}.svelte-flow .svelte-flow__edges{position:absolute}.svelte-flow .svelte-flow__edges svg{overflow:visible;position:absolute;pointer-events:none}.svelte-flow__edge{pointer-events:visibleStroke}.svelte-flow__edge.selectable{cursor:pointer}.svelte-flow__edge.animated path{stroke-dasharray:5;animation:dashdraw .5s linear infinite}.svelte-flow__edge.animated path.svelte-flow__edge-interaction{stroke-dasharray:none;animation:none}.svelte-flow__edge.inactive{pointer-events:none}.svelte-flow__edge.selected,.svelte-flow__edge:focus,.svelte-flow__edge:focus-visible{outline:none}.svelte-flow__edge.selected .svelte-flow__edge-path,.svelte-flow__edge.selectable:focus .svelte-flow__edge-path,.svelte-flow__edge.selectable:focus-visible .svelte-flow__edge-path{stroke:var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default))}.svelte-flow__edge-textwrapper{pointer-events:all}.svelte-flow__edge .svelte-flow__edge-text{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.svelte-flow__connection{pointer-events:none}.svelte-flow__connection .animated{stroke-dasharray:5;animation:dashdraw .5s linear infinite}svg.svelte-flow__connectionline{z-index:1001;overflow:visible;position:absolute}.svelte-flow__nodes{pointer-events:none;transform-origin:0 0}.svelte-flow__node{position:absolute;-webkit-user-select:none;-moz-user-select:none;user-select:none;pointer-events:all;transform-origin:0 0;box-sizing:border-box;cursor:default}.svelte-flow__node.selectable{cursor:pointer}.svelte-flow__node.draggable{cursor:grab;pointer-events:all}.svelte-flow__node.draggable.dragging{cursor:grabbing}.svelte-flow__nodesselection{z-index:3;transform-origin:left top;pointer-events:none}.svelte-flow__nodesselection-rect{position:absolute;pointer-events:all;cursor:grab}.svelte-flow__handle{position:absolute;pointer-events:none;min-width:5px;min-height:5px;width:6px;height:6px;background-color:var(--xy-handle-background-color, var(--xy-handle-background-color-default));border:1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default));border-radius:100%}.svelte-flow__handle.connectingfrom{pointer-events:all}.svelte-flow__handle.connectionindicator{pointer-events:all;cursor:crosshair}.svelte-flow__handle-bottom{top:auto;left:50%;bottom:0;transform:translate(-50%,50%)}.svelte-flow__handle-top{top:0;left:50%;transform:translate(-50%,-50%)}.svelte-flow__handle-left{top:50%;left:0;transform:translate(-50%,-50%)}.svelte-flow__handle-right{top:50%;right:0;transform:translate(50%,-50%)}.svelte-flow__edgeupdater{cursor:move;pointer-events:all}.svelte-flow__panel{position:absolute;z-index:5;margin:15px}.svelte-flow__panel.top{top:0}.svelte-flow__panel.bottom{bottom:0}.svelte-flow__panel.left{left:0}.svelte-flow__panel.right{right:0}.svelte-flow__panel.center{left:50%;transform:translate(-50%)}.svelte-flow__attribution{font-size:10px;background:var(--xy-attribution-background-color, var(--xy-attribution-background-color-default));padding:2px 3px;margin:0}.svelte-flow__attribution a{text-decoration:none;color:#999}@keyframes dashdraw{0%{stroke-dashoffset:10}}.svelte-flow__edgelabel-renderer{position:absolute;width:100%;height:100%;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none;left:0;top:0}.svelte-flow__viewport-portal{position:absolute;width:100%;height:100%;left:0;top:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}.svelte-flow__minimap{background:var( --xy-minimap-background-color-props, var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)) )}.svelte-flow__minimap-svg{display:block}.svelte-flow__minimap-mask{fill:var( --xy-minimap-mask-background-color-props, var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) );stroke:var( --xy-minimap-mask-stroke-color-props, var(--xy-minimap-mask-stroke-color, var(--xy-minimap-mask-stroke-color-default)) );stroke-width:var( --xy-minimap-mask-stroke-width-props, var(--xy-minimap-mask-stroke-width, var(--xy-minimap-mask-stroke-width-default)) )}.svelte-flow__minimap-node{fill:var( --xy-minimap-node-background-color-props, var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) );stroke:var( --xy-minimap-node-stroke-color-props, var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) );stroke-width:var( --xy-minimap-node-stroke-width-props, var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) )}.svelte-flow__background-pattern.dots{fill:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) )}.svelte-flow__background-pattern.lines{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) )}.svelte-flow__background-pattern.cross{stroke:var( --xy-background-pattern-color-props, var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) )}.svelte-flow__controls{display:flex;flex-direction:column;box-shadow:var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default))}.svelte-flow__controls.horizontal{flex-direction:row}.svelte-flow__controls-button{display:flex;justify-content:center;align-items:center;height:26px;width:26px;padding:4px;border:none;background:var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default));border-bottom:1px solid var( --xy-controls-button-border-color-props, var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) );color:var( --xy-controls-button-color-props, var(--xy-controls-button-color, var(--xy-controls-button-color-default)) );cursor:pointer;-webkit-user-select:none;-moz-user-select:none;user-select:none}.svelte-flow__controls-button svg{width:100%;max-width:12px;max-height:12px;fill:currentColor}.svelte-flow__edge.updating .svelte-flow__edge-path{stroke:#777}.svelte-flow__edge-text{font-size:10px}.svelte-flow__node.selectable:focus,.svelte-flow__node.selectable:focus-visible{outline:none}.svelte-flow__node-input,.svelte-flow__node-default,.svelte-flow__node-output,.svelte-flow__node-group{padding:10px;border-radius:var(--xy-node-border-radius, var(--xy-node-border-radius-default));width:150px;font-size:12px;color:var(--xy-node-color, var(--xy-node-color-default));text-align:center;border:var(--xy-node-border, var(--xy-node-border-default));background-color:var(--xy-node-background-color, var(--xy-node-background-color-default))}.svelte-flow__node-input.selectable:hover,.svelte-flow__node-default.selectable:hover,.svelte-flow__node-output.selectable:hover,.svelte-flow__node-group.selectable:hover{box-shadow:var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default))}.svelte-flow__node-input.selectable.selected,.svelte-flow__node-input.selectable:focus,.svelte-flow__node-input.selectable:focus-visible,.svelte-flow__node-default.selectable.selected,.svelte-flow__node-default.selectable:focus,.svelte-flow__node-default.selectable:focus-visible,.svelte-flow__node-output.selectable.selected,.svelte-flow__node-output.selectable:focus,.svelte-flow__node-output.selectable:focus-visible,.svelte-flow__node-group.selectable.selected,.svelte-flow__node-group.selectable:focus,.svelte-flow__node-group.selectable:focus-visible{box-shadow:var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default))}.svelte-flow__node-group{background-color:var(--xy-node-group-background-color, var(--xy-node-group-background-color-default))}.svelte-flow__nodesselection-rect,.svelte-flow__selection{background:var(--xy-selection-background-color, var(--xy-selection-background-color-default));border:var(--xy-selection-border, var(--xy-selection-border-default))}.svelte-flow__nodesselection-rect:focus,.svelte-flow__nodesselection-rect:focus-visible,.svelte-flow__selection:focus,.svelte-flow__selection:focus-visible{outline:none}.svelte-flow__controls-button:hover{background:var( --xy-controls-button-background-color-hover-props, var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) );color:var( --xy-controls-button-color-hover-props, var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) )}.svelte-flow__controls-button:disabled{pointer-events:none}.svelte-flow__controls-button:disabled svg{fill-opacity:.4}.svelte-flow__controls-button:last-child{border-bottom:none}.svelte-flow__resize-control{position:absolute}.svelte-flow__resize-control.left,.svelte-flow__resize-control.right{cursor:ew-resize}.svelte-flow__resize-control.top,.svelte-flow__resize-control.bottom{cursor:ns-resize}.svelte-flow__resize-control.top.left,.svelte-flow__resize-control.bottom.right{cursor:nwse-resize}.svelte-flow__resize-control.bottom.left,.svelte-flow__resize-control.top.right{cursor:nesw-resize}.svelte-flow__resize-control.handle{width:4px;height:4px;border:1px solid #fff;border-radius:1px;background-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));transform:translate(-50%,-50%)}.svelte-flow__resize-control.handle.left{left:0;top:50%}.svelte-flow__resize-control.handle.right{left:100%;top:50%}.svelte-flow__resize-control.handle.top{left:50%;top:0}.svelte-flow__resize-control.handle.bottom{left:50%;top:100%}.svelte-flow__resize-control.handle.top.left,.svelte-flow__resize-control.handle.bottom.left{left:0}.svelte-flow__resize-control.handle.top.right,.svelte-flow__resize-control.handle.bottom.right{left:100%}.svelte-flow__resize-control.line{border-color:var(--xy-resize-background-color, var(--xy-resize-background-color-default));border-width:0;border-style:solid}.svelte-flow__resize-control.line.left,.svelte-flow__resize-control.line.right{width:1px;transform:translate(-50%);top:0;height:100%}.svelte-flow__resize-control.line.left{left:0;border-left-width:1px}.svelte-flow__resize-control.line.right{left:100%;border-right-width:1px}.svelte-flow__resize-control.line.top,.svelte-flow__resize-control.line.bottom{height:1px;transform:translateY(-50%);left:0;width:100%}.svelte-flow__resize-control.line.top{top:0;border-top-width:1px}.svelte-flow__resize-control.line.bottom{border-bottom-width:1px;top:100%}.svelte-flow__edge-label{text-align:center;position:absolute;padding:2px;font-size:10px;cursor:pointer;color:var(--xy-edge-label-color, var(--xy-edge-label-color-default));background:var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default))}.svelte-flow__nodes,.svelte-flow__edgelabel-renderer{z-index:0}:root,:root .tf-theme-light{--tf-primary-color: #2563EB;--xy-node-boxshadow-selected: 0 0 0 1px var(--tf-primary-color);--xy-handle-background-color: var(--tf-primary-color)}.tf-btn{display:flex;align-items:center;justify-content:center;gap:2px;background:#fff;border:1px solid #ccc;cursor:pointer;border-radius:5px;padding:5px;margin:0;height:fit-content;width:fit-content}.tf-btn svg{fill:currentColor;width:16px;height:16px}.tf-btn:hover{border:1px solid var(--tf-primary-color)}.tf-input,.tf-textarea{display:flex;border-radius:5px;border:1px solid #ccc;padding:5px 10px;box-sizing:border-box;resize:vertical;outline:none}.tf-input::placeholder,.tf-textarea::placeholder{color:#ccc}.tf-input:focus,.tf-textarea:focus{border-color:var(--tf-primary-color);box-shadow:0 0 5px #51cbee33}.tf-input[disabled],.tf-textarea[disabled]{background-color:#f0f0f0;cursor:not-allowed;color:#aaa}.tf-select-input{display:flex;border:1px solid #ccc;padding:3px 10px;border-radius:5px;font-size:14px;justify-content:space-between;align-items:center;cursor:pointer;background:#fff;height:27px}.tf-select-input:focus{border-color:var(--tf-primary-color);box-shadow:0 0 5px #51cbee33}.tf-select-input-value{height:21px;min-width:10px;font-size:12px;display:flex;align-items:center}.tf-select-input-arrow{display:block;width:16px;height:16px;color:#666}.tf-select-input-placeholder{color:#ccc}.tf-select-content{display:flex;flex-direction:column;background:#fff;margin-top:5px;border:1px solid #ccc;border-radius:5px;padding:5px;width:max-content;min-width:100%;z-index:9999;box-sizing:border-box}.tf-select-content-item{display:flex;align-items:center;padding:5px 10px;border:none;background:#fff;border-radius:5px;cursor:pointer;line-height:100%;gap:2px}.tf-select-content-item span{width:16px;display:flex}.tf-select-content-item svg{width:16px;height:16px;margin:auto}.tf-select-content-item:hover{background:#f0f0f0}.tf-select-content-children{padding-left:14px}.tf-checkbox{width:14px;height:14px}.tf-tabs{display:flex;align-items:center;justify-content:center;gap:5px;padding:5px;border-radius:5px;border:none;background:#f4f4f5}.tf-tabs .tf-tabs-item{flex-grow:1;padding:5px 10px;cursor:pointer;border-radius:5px;display:flex;align-items:center;justify-content:center;font-size:14px;color:#808088}.tf-tabs .tf-tabs-item.active{background:#fff;color:#333;font-weight:500;box-shadow:0 0 5px #00000026}h3.tf-heading{font-weight:700;font-size:14px;margin-top:2px;margin-bottom:3px;color:#333}.tf-collapse{border:none;border-radius:5px}.tf-collapse-item-title{display:flex;align-items:center;cursor:pointer;font-size:14px}.tf-collapse-item-title-icon{display:flex;width:26px;height:26px;color:#2563eb;background:#cedafb;border-radius:5px;padding:3px;justify-content:center;align-items:center;margin-right:10px}.tf-collapse-item-title-icon svg{width:22px;height:22px;color:#3474ff}.tf-collapse-item-title-arrow{display:block;width:16px;height:16px;margin-left:auto}.tf-collapse-item-description{font-size:12px;margin:10px 0;color:#999}.svelte-flow__nodes .svelte-flow__node{border:3px solid transparent;border-radius:5px;box-sizing:border-box}.svelte-flow__nodes .svelte-flow__node .svelte-flow__handle{width:16px;height:16px;background:transparent;display:flex;justify-content:center;align-items:center;border:none}.svelte-flow__nodes .svelte-flow__node .svelte-flow__handle:after{content:" ";background:#2563eb;width:8px;height:8px;border-radius:100%;transition:width .1s,height .1s}.svelte-flow__nodes .svelte-flow__node .svelte-flow__handle:hover:after{width:16px;height:16px}.svelte-flow__nodes .svelte-flow__node div.loop_handle_wrapper:after{content:"循环体";background:#2563eb;width:100px;height:20px;border-radius:0;display:flex;color:#fff;justify-content:center;align-items:center}.svelte-flow__nodes .svelte-flow__node div.loop_handle_wrapper:hover:after{width:100px;height:20px}.svelte-flow__nodes .svelte-flow__node:after{content:" ";position:absolute;border-radius:5px;top:-2px;left:-2px;border:1px solid #ccc;height:calc(100% + 2px);width:calc(100% + 2px)}.svelte-flow__nodes .svelte-flow__node:hover{border:3px solid #bacaef7d}.svelte-flow__nodes .svelte-flow__node.selectable.selected{border:3px solid #bacaef7d;box-shadow:var(--xy-node-boxshadow-selected)}.svelte-flow__nodes .svelte-flow__node:hover:after{display:none}.svelte-flow__nodes .svelte-flow__node.selectable.selected:after{display:none}.tf-node-wrapper{border-radius:5px;min-width:300px;background:#fff}.tf-node-wrapper-title{height:30px;background:#eff1f5;color:#bcbcbc;font-size:12px;display:flex;align-items:center;padding-left:5px;border-bottom:1px solid #ccc;font-weight:300;letter-spacing:1px}.tf-node-wrapper-body{padding:10px}.svelte-flow__attribution a{display:none}.tf-toolbar{position:absolute;top:10px;left:10px;z-index:9999;display:flex;gap:5px;transition:transform .5s ease,opacity .5s ease;transform:translate(-220px)}.tf-toolbar.show{transform:translate(0)}.tf-toolbar-container{background:#fff;border:1px solid #eee;border-radius:5px;box-shadow:0 0 5px #0000001a;padding:10px;width:180px}.tf-toolbar-container-header{display:flex}.tf-toolbar-container-body{display:flex;margin-top:20px}.tf-toolbar-container-body .tf-toolbar-container-base,.tf-toolbar-container-body .tf-toolbar-container-tools{display:flex;flex-direction:column;gap:4px;flex-grow:1}.tf-toolbar-container-body .tf-toolbar-container-base .tf-btn,.tf-toolbar-container-body .tf-toolbar-container-tools .tf-btn{border:none;width:100%;justify-content:flex-start;height:40px;gap:10px;cursor:grabbing;border-radius:5px}.tf-toolbar-container-body .tf-toolbar-container-base .tf-btn svg,.tf-toolbar-container-body .tf-toolbar-container-tools .tf-btn svg{width:20px;height:20px;fill:#2563eb}.tf-toolbar-container-body .tf-toolbar-container-base .tf-btn:hover,.tf-toolbar-container-body .tf-toolbar-container-tools .tf-btn:hover{background:#f1f1f1}.tinyflow-logo:after{content:"Tinyflow.ai";font-size:145px;display:flex;align-items:center;justify-content:center;width:100%;height:100%;font-weight:800;color:#03153b54;text-shadow:1px 3px 6px #cedafb,0 0 0 #000,1px 3px 6px #fff;opacity:.1} diff --git a/src/components/Tinyflow/ui/index.d.ts b/src/components/Tinyflow/ui/index.d.ts new file mode 100644 index 0000000..38a3132 --- /dev/null +++ b/src/components/Tinyflow/ui/index.d.ts @@ -0,0 +1,41 @@ +import { Edge } from '@xyflow/svelte'; +import { Node as Node_2 } from '@xyflow/svelte'; +import { useSvelteFlow } from '@xyflow/svelte'; +import { Viewport } from '@xyflow/svelte'; + +export declare type Item = { + value: number | string; + label: string; + children?: Item[]; +}; + +export declare class Tinyflow { + private options; + private rootEl; + private svelteFlowInstance; + constructor(options: TinyflowOptions); + private _init; + private _setOptions; + getOptions(): TinyflowOptions; + getData(): { + nodes: Node_2[]; + edges: Edge[]; + viewport: Viewport; + }; + setData(data: TinyflowData): void; + destroy(): void; +} + +export declare type TinyflowData = Partial<ReturnType<ReturnType<typeof useSvelteFlow>['toObject']>>; + +export declare type TinyflowOptions = { + element: string | Element; + data?: TinyflowData; + provider?: { + llm?: () => Item[] | Promise<Item[]>; + knowledge?: () => Item[] | Promise<Item[]>; + internal?: () => Item[] | Promise<Item[]>; + }; +}; + +export { } diff --git a/src/components/Tinyflow/ui/index.js b/src/components/Tinyflow/ui/index.js new file mode 100644 index 0000000..80e77b5 --- /dev/null +++ b/src/components/Tinyflow/ui/index.js @@ -0,0 +1,16984 @@ +var tf = Object.defineProperty; +var Pa = (e) => { + throw TypeError(e); +}; +var nf = (e, t, n) => t in e ? tf(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n; +var wt = (e, t, n) => nf(e, typeof t != "symbol" ? t + "" : t, n), Ji = (e, t, n) => t.has(e) || Pa("Cannot " + n); +var it = (e, t, n) => (Ji(e, t, "read from private field"), n ? n.call(e) : t.get(e)), rr = (e, t, n) => t.has(e) ? Pa("Cannot add the same private member more than once") : t instanceof WeakSet ? t.add(e) : t.set(e, n), Gr = (e, t, n, r) => (Ji(e, t, "write to private field"), r ? r.call(e, n) : t.set(e, n), n), Na = (e, t, n) => (Ji(e, t, "access private method"), n); +const rf = "5"; +var Ll; +typeof window < "u" && ((Ll = window.__svelte ?? (window.__svelte = {})).v ?? (Ll.v = /* @__PURE__ */ new Set())).add(rf); +let Br = !1, of = !1; +function sf() { + Br = !0; +} +sf(); +const Os = 1, Is = 2, Ol = 4, af = 8, lf = 16, uf = 1, cf = 2, Il = 4, df = 8, ff = 16, zl = 1, gf = 2, zs = "[", Rs = "[!", Bs = "]", _r = {}, Pt = Symbol(), Rl = "http://www.w3.org/2000/svg", Ma = !1, nn = 2, Bl = 4, Si = 8, Ys = 16, On = 32, Yr = 64, ti = 128, qt = 256, ni = 512, mt = 1024, In = 2048, gr = 4096, Mn = 8192, Pi = 16384, hf = 32768, Zr = 65536, vf = 1 << 17, pf = 1 << 19, Yl = 1 << 20, Wn = Symbol("$state"), Zs = Symbol("legacy props"), mf = Symbol(""); +var Co = Array.isArray, yf = Array.prototype.indexOf, Xs = Array.from, ri = Object.keys, so = Object.defineProperty, Tn = Object.getOwnPropertyDescriptor, Zl = Object.getOwnPropertyDescriptors, wf = Object.prototype, _f = Array.prototype, Fs = Object.getPrototypeOf; +function Ur(e) { + return typeof e == "function"; +} +const dt = () => { +}; +function xf(e) { + return e(); +} +function ao(e) { + for (var t = 0; t < e.length; t++) + e[t](); +} +const bf = typeof requestIdleCallback > "u" ? (e) => setTimeout(e, 1) : requestIdleCallback; +let lo = [], uo = []; +function Xl() { + var e = lo; + lo = [], ao(e); +} +function Fl() { + var e = uo; + uo = [], ao(e); +} +function ko(e) { + lo.length === 0 && queueMicrotask(Xl), lo.push(e); +} +function Cf(e) { + uo.length === 0 && bf(Fl), uo.push(e); +} +function Ta() { + lo.length > 0 && Xl(), uo.length > 0 && Fl(); +} +function Wl(e) { + return e === this.v; +} +function Ws(e, t) { + return e != e ? t == t : e !== t || e !== null && typeof e == "object" || typeof e == "function"; +} +function Ks(e) { + return !Ws(e, this.v); +} +function kf(e) { + throw new Error("https://svelte.dev/e/effect_in_teardown"); +} +function $f() { + throw new Error("https://svelte.dev/e/effect_in_unowned_derived"); +} +function Ef(e) { + throw new Error("https://svelte.dev/e/effect_orphan"); +} +function Sf() { + throw new Error("https://svelte.dev/e/effect_update_depth_exceeded"); +} +function Pf() { + throw new Error("https://svelte.dev/e/hydration_failed"); +} +function Nf(e) { + throw new Error("https://svelte.dev/e/props_invalid_value"); +} +function Mf() { + throw new Error("https://svelte.dev/e/state_descriptors_fixed"); +} +function Tf() { + throw new Error("https://svelte.dev/e/state_prototype_fixed"); +} +function Hf() { + throw new Error("https://svelte.dev/e/state_unsafe_local_read"); +} +function Vf() { + throw new Error("https://svelte.dev/e/state_unsafe_mutation"); +} +function Mt(e, t) { + var n = { + f: 0, + // TODO ideally we could skip this altogether, but it causes type errors + v: e, + reactions: null, + equals: Wl, + rv: 0, + wv: 0 + }; + return n; +} +function Un(e) { + return /* @__PURE__ */ Kl(Mt(e)); +} +// @__NO_SIDE_EFFECTS__ +function $o(e, t = !1) { + var r; + const n = Mt(e); + return t || (n.equals = Ks), Br && Ze !== null && Ze.l !== null && ((r = Ze.l).s ?? (r.s = [])).push(n), n; +} +function re(e, t = !1) { + return /* @__PURE__ */ Kl(/* @__PURE__ */ $o(e, t)); +} +// @__NO_SIDE_EFFECTS__ +function Kl(e) { + return je !== null && !en && je.f & nn && (vn === null ? Lf([e]) : vn.push(e)), e; +} +function U(e, t) { + return je !== null && !en && Di() && je.f & (nn | Ys) && // If the source was created locally within the current derived, then + // we allow the mutation. + (vn === null || !vn.includes(e)) && Vf(), gs(e, t); +} +function gs(e, t) { + return e.equals(t) || (e.v, e.v = t, e.wv = tu(), ql(e, In), Di() && qe !== null && qe.f & mt && !(qe.f & (On | Yr)) && (En === null ? Of([e]) : En.push(e))), t; +} +function Ha(e, t = 1) { + var n = h(e), r = t === 1 ? n++ : n--; + return U(e, n), r; +} +function ql(e, t) { + var n = e.reactions; + if (n !== null) + for (var r = Di(), o = n.length, i = 0; i < o; i++) { + var s = n[i], a = s.f; + a & In || !r && s === qe || (rn(s, t), a & (mt | qt) && (a & nn ? ql( + /** @type {Derived} */ + s, + gr + ) : Hi( + /** @type {Effect} */ + s + ))); + } +} +// @__NO_SIDE_EFFECTS__ +function Me(e) { + var t = nn | In, n = je !== null && je.f & nn ? ( + /** @type {Derived} */ + je + ) : null; + return qe === null || n !== null && n.f & qt ? t |= qt : qe.f |= Yl, { + ctx: Ze, + deps: null, + effects: null, + equals: Wl, + f: t, + fn: e, + reactions: null, + rv: 0, + v: ( + /** @type {V} */ + null + ), + wv: 0, + parent: n ?? qe + }; +} +// @__NO_SIDE_EFFECTS__ +function pe(e) { + const t = /* @__PURE__ */ Me(e); + return t.equals = Ks, t; +} +function Gl(e) { + var t = e.effects; + if (t !== null) { + e.effects = null; + for (var n = 0; n < t.length; n += 1) + Gt( + /** @type {Effect} */ + t[n] + ); + } +} +function Df(e) { + for (var t = e.parent; t !== null; ) { + if (!(t.f & nn)) + return ( + /** @type {Effect} */ + t + ); + t = t.parent; + } + return null; +} +function Af(e) { + var t, n = qe; + Jn(Df(e)); + try { + Gl(e), t = ru(e); + } finally { + Jn(n); + } + return t; +} +function Ul(e) { + var t = Af(e), n = (Xn || e.f & qt) && e.deps !== null ? gr : mt; + rn(e, n), e.equals(t) || (e.v = t, e.wv = tu()); +} +function Ni(e) { + console.warn("https://svelte.dev/e/hydration_mismatch"); +} +let Pe = !1; +function It(e) { + Pe = e; +} +let De; +function Ct(e) { + if (e === null) + throw Ni(), _r; + return De = e; +} +function yn() { + return Ct( + /** @type {TemplateNode} */ + /* @__PURE__ */ xn(De) + ); +} +function Z(e) { + if (Pe) { + if (/* @__PURE__ */ xn(De) !== null) + throw Ni(), _r; + De = e; + } +} +function Se(e = 1) { + if (Pe) { + for (var t = e, n = De; t--; ) + n = /** @type {TemplateNode} */ + /* @__PURE__ */ xn(n); + De = n; + } +} +function hs() { + for (var e = 0, t = De; ; ) { + if (t.nodeType === 8) { + var n = ( + /** @type {Comment} */ + t.data + ); + if (n === Bs) { + if (e === 0) return t; + e -= 1; + } else (n === zs || n === Rs) && (e += 1); + } + var r = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ xn(t) + ); + t.remove(), t = r; + } +} +function Tt(e, t = null, n) { + if (typeof e != "object" || e === null || Wn in e) + return e; + const r = Fs(e); + if (r !== wf && r !== _f) + return e; + var o = /* @__PURE__ */ new Map(), i = Co(e), s = Mt(0); + i && o.set("length", Mt( + /** @type {any[]} */ + e.length + )); + var a; + return new Proxy( + /** @type {any} */ + e, + { + defineProperty(l, u, c) { + (!("value" in c) || c.configurable === !1 || c.enumerable === !1 || c.writable === !1) && Mf(); + var f = o.get(u); + return f === void 0 ? (f = Mt(c.value), o.set(u, f)) : U(f, Tt(c.value, a)), !0; + }, + deleteProperty(l, u) { + var c = o.get(u); + if (c === void 0) + u in l && o.set(u, Mt(Pt)); + else { + if (i && typeof u == "string") { + var f = ( + /** @type {Source<number>} */ + o.get("length") + ), d = Number(u); + Number.isInteger(d) && d < f.v && U(f, d); + } + U(c, Pt), Va(s); + } + return !0; + }, + get(l, u, c) { + var p; + if (u === Wn) + return e; + var f = o.get(u), d = u in l; + if (f === void 0 && (!d || (p = Tn(l, u)) != null && p.writable) && (f = Mt(Tt(d ? l[u] : Pt, a)), o.set(u, f)), f !== void 0) { + var g = h(f); + return g === Pt ? void 0 : g; + } + return Reflect.get(l, u, c); + }, + getOwnPropertyDescriptor(l, u) { + var c = Reflect.getOwnPropertyDescriptor(l, u); + if (c && "value" in c) { + var f = o.get(u); + f && (c.value = h(f)); + } else if (c === void 0) { + var d = o.get(u), g = d == null ? void 0 : d.v; + if (d !== void 0 && g !== Pt) + return { + enumerable: !0, + configurable: !0, + value: g, + writable: !0 + }; + } + return c; + }, + has(l, u) { + var g; + if (u === Wn) + return !0; + var c = o.get(u), f = c !== void 0 && c.v !== Pt || Reflect.has(l, u); + if (c !== void 0 || qe !== null && (!f || (g = Tn(l, u)) != null && g.writable)) { + c === void 0 && (c = Mt(f ? Tt(l[u], a) : Pt), o.set(u, c)); + var d = h(c); + if (d === Pt) + return !1; + } + return f; + }, + set(l, u, c, f) { + var _; + var d = o.get(u), g = u in l; + if (i && u === "length") + for (var p = c; p < /** @type {Source<number>} */ + d.v; p += 1) { + var x = o.get(p + ""); + x !== void 0 ? U(x, Pt) : p in l && (x = Mt(Pt), o.set(p + "", x)); + } + d === void 0 ? (!g || (_ = Tn(l, u)) != null && _.writable) && (d = Mt(void 0), U(d, Tt(c, a)), o.set(u, d)) : (g = d.v !== Pt, U(d, Tt(c, a))); + var C = Reflect.getOwnPropertyDescriptor(l, u); + if (C != null && C.set && C.set.call(f, c), !g) { + if (i && typeof u == "string") { + var $ = ( + /** @type {Source<number>} */ + o.get("length") + ), m = Number(u); + Number.isInteger(m) && m >= $.v && U($, m + 1); + } + Va(s); + } + return !0; + }, + ownKeys(l) { + h(s); + var u = Reflect.ownKeys(l).filter((d) => { + var g = o.get(d); + return g === void 0 || g.v !== Pt; + }); + for (var [c, f] of o) + f.v !== Pt && !(c in l) && u.push(c); + return u; + }, + setPrototypeOf() { + Tf(); + } + } + ); +} +function Va(e, t = 1) { + U(e, e.v + t); +} +var Nt, jl, Jl, Ql; +function vs() { + if (Nt === void 0) { + Nt = window, jl = /Firefox/.test(navigator.userAgent); + var e = Element.prototype, t = Node.prototype; + Jl = Tn(t, "firstChild").get, Ql = Tn(t, "nextSibling").get, e.__click = void 0, e.__className = void 0, e.__attributes = null, e.__styles = null, e.__e = void 0, Text.prototype.__t = void 0; + } +} +function Vn(e = "") { + return document.createTextNode(e); +} +// @__NO_SIDE_EFFECTS__ +function bt(e) { + return Jl.call(e); +} +// @__NO_SIDE_EFFECTS__ +function xn(e) { + return Ql.call(e); +} +function X(e, t) { + if (!Pe) + return /* @__PURE__ */ bt(e); + var n = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ bt(De) + ); + if (n === null) + n = De.appendChild(Vn()); + else if (t && n.nodeType !== 3) { + var r = Vn(); + return n == null || n.before(r), Ct(r), r; + } + return Ct(n), n; +} +function be(e, t) { + if (!Pe) { + var n = ( + /** @type {DocumentFragment} */ + /* @__PURE__ */ bt( + /** @type {Node} */ + e + ) + ); + return n instanceof Comment && n.data === "" ? /* @__PURE__ */ xn(n) : n; + } + return De; +} +function z(e, t = 1, n = !1) { + let r = Pe ? De : e; + for (var o; t--; ) + o = r, r = /** @type {TemplateNode} */ + /* @__PURE__ */ xn(r); + if (!Pe) + return r; + var i = r == null ? void 0 : r.nodeType; + if (n && i !== 3) { + var s = Vn(); + return r === null ? o == null || o.after(s) : r.before(s), Ct(s), s; + } + return Ct(r), /** @type {TemplateNode} */ + r; +} +function qs(e) { + e.textContent = ""; +} +let qo = !1, oi = !1, ii = null, ir = !1, Gs = !1; +function Da(e) { + Gs = e; +} +let oo = []; +let je = null, en = !1; +function jn(e) { + je = e; +} +let qe = null; +function Jn(e) { + qe = e; +} +let vn = null; +function Lf(e) { + vn = e; +} +let _t = null, Lt = 0, En = null; +function Of(e) { + En = e; +} +let eu = 1, si = 0, Xn = !1; +function tu() { + return ++eu; +} +function Xr(e) { + var f; + var t = e.f; + if (t & In) + return !0; + if (t & gr) { + var n = e.deps, r = (t & qt) !== 0; + if (n !== null) { + var o, i, s = (t & ni) !== 0, a = r && qe !== null && !Xn, l = n.length; + if (s || a) { + var u = ( + /** @type {Derived} */ + e + ), c = u.parent; + for (o = 0; o < l; o++) + i = n[o], (s || !((f = i == null ? void 0 : i.reactions) != null && f.includes(u))) && (i.reactions ?? (i.reactions = [])).push(u); + s && (u.f ^= ni), a && c !== null && !(c.f & qt) && (u.f ^= qt); + } + for (o = 0; o < l; o++) + if (i = n[o], Xr( + /** @type {Derived} */ + i + ) && Ul( + /** @type {Derived} */ + i + ), i.wv > e.wv) + return !0; + } + (!r || qe !== null && !Xn) && rn(e, mt); + } + return !1; +} +function If(e, t) { + for (var n = t; n !== null; ) { + if (n.f & ti) + try { + n.fn(e); + return; + } catch { + n.f ^= ti; + } + n = n.parent; + } + throw qo = !1, e; +} +function zf(e) { + return (e.f & Pi) === 0 && (e.parent === null || (e.parent.f & ti) === 0); +} +function Mi(e, t, n, r) { + if (qo) { + if (n === null && (qo = !1), zf(t)) + throw e; + return; + } + n !== null && (qo = !0); + { + If(e, t); + return; + } +} +function nu(e, t, n = !0) { + var r = e.reactions; + if (r !== null) + for (var o = 0; o < r.length; o++) { + var i = r[o]; + i.f & nn ? nu( + /** @type {Derived} */ + i, + t, + !1 + ) : t === i && (n ? rn(i, In) : i.f & mt && rn(i, gr), Hi( + /** @type {Effect} */ + i + )); + } +} +function ru(e) { + var g; + var t = _t, n = Lt, r = En, o = je, i = Xn, s = vn, a = Ze, l = en, u = e.f; + _t = /** @type {null | Value[]} */ + null, Lt = 0, En = null, Xn = (u & qt) !== 0 && (en || !ir || je === null), je = u & (On | Yr) ? null : e, vn = null, Aa(e.ctx), en = !1, si++; + try { + var c = ( + /** @type {Function} */ + (0, e.fn)() + ), f = e.deps; + if (_t !== null) { + var d; + if (ai(e, Lt), f !== null && Lt > 0) + for (f.length = Lt + _t.length, d = 0; d < _t.length; d++) + f[Lt + d] = _t[d]; + else + e.deps = f = _t; + if (!Xn) + for (d = Lt; d < f.length; d++) + ((g = f[d]).reactions ?? (g.reactions = [])).push(e); + } else f !== null && Lt < f.length && (ai(e, Lt), f.length = Lt); + if (Di() && En !== null && !en && f !== null && !(e.f & (nn | gr | In))) + for (d = 0; d < /** @type {Source[]} */ + En.length; d++) + nu( + En[d], + /** @type {Effect} */ + e + ); + return o !== null && si++, c; + } finally { + _t = t, Lt = n, En = r, je = o, Xn = i, vn = s, Aa(a), en = l; + } +} +function Rf(e, t) { + let n = t.reactions; + if (n !== null) { + var r = yf.call(n, e); + if (r !== -1) { + var o = n.length - 1; + o === 0 ? n = t.reactions = null : (n[r] = n[o], n.pop()); + } + } + n === null && t.f & nn && // Destroying a child effect while updating a parent effect can cause a dependency to appear + // to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps` + // allows us to skip the expensive work of disconnecting and immediately reconnecting it + (_t === null || !_t.includes(t)) && (rn(t, gr), t.f & (qt | ni) || (t.f ^= ni), Gl( + /** @type {Derived} **/ + t + ), ai( + /** @type {Derived} **/ + t, + 0 + )); +} +function ai(e, t) { + var n = e.deps; + if (n !== null) + for (var r = t; r < n.length; r++) + Rf(e, n[r]); +} +function Ti(e) { + var t = e.f; + if (!(t & Pi)) { + rn(e, mt); + var n = qe, r = Ze, o = ir; + qe = e, ir = !0; + try { + t & Ys ? Gf(e) : lu(e), au(e); + var i = ru(e); + e.teardown = typeof i == "function" ? i : null, e.wv = eu; + var s = e.deps, a; + Ma && of && e.f & In; + } catch (l) { + Mi(l, e, n, r || e.ctx); + } finally { + ir = o, qe = n; + } + } +} +function Bf() { + try { + Sf(); + } catch (e) { + if (ii !== null) + Mi(e, ii, null); + else + throw e; + } +} +function ou() { + var e = ir; + try { + var t = 0; + for (ir = !0; oo.length > 0; ) { + t++ > 1e3 && Bf(); + var n = oo, r = n.length; + oo = []; + for (var o = 0; o < r; o++) { + var i = n[o]; + i.f & mt || (i.f ^= mt); + var s = Zf(i); + Yf(s); + } + } + } finally { + oi = !1, ir = e, ii = null; + } +} +function Yf(e) { + var t = e.length; + if (t !== 0) + for (var n = 0; n < t; n++) { + var r = e[n]; + if (!(r.f & (Pi | Mn))) + try { + Xr(r) && (Ti(r), r.deps === null && r.first === null && r.nodes_start === null && (r.teardown === null ? uu(r) : r.fn = null)); + } catch (o) { + Mi(o, r, null, r.ctx); + } + } +} +function Hi(e) { + oi || (oi = !0, queueMicrotask(ou)); + for (var t = ii = e; t.parent !== null; ) { + t = t.parent; + var n = t.f; + if (n & (Yr | On)) { + if (!(n & mt)) return; + t.f ^= mt; + } + } + oo.push(t); +} +function Zf(e) { + for (var t = [], n = e.first; n !== null; ) { + var r = n.f, o = (r & On) !== 0, i = o && (r & mt) !== 0; + if (!i && !(r & Mn)) { + if (r & Bl) + t.push(n); + else if (o) + n.f ^= mt; + else { + var s = je; + try { + je = n, Xr(n) && Ti(n); + } catch (u) { + Mi(u, n, null, n.ctx); + } finally { + je = s; + } + } + var a = n.first; + if (a !== null) { + n = a; + continue; + } + } + var l = n.parent; + for (n = n.next; n === null && l !== null; ) + n = l.next, l = l.parent; + } + return t; +} +function y(e) { + var t; + for (Ta(); oo.length > 0; ) + oi = !0, ou(), Ta(); + return ( + /** @type {T} */ + t + ); +} +function h(e) { + var t = e.f, n = (t & nn) !== 0; + if (je !== null && !en) { + vn !== null && vn.includes(e) && Hf(); + var r = je.deps; + e.rv < si && (e.rv = si, _t === null && r !== null && r[Lt] === e ? Lt++ : _t === null ? _t = [e] : (!Xn || !_t.includes(e)) && _t.push(e)); + } else if (n && /** @type {Derived} */ + e.deps === null && /** @type {Derived} */ + e.effects === null) { + var o = ( + /** @type {Derived} */ + e + ), i = o.parent; + i !== null && !(i.f & qt) && (o.f ^= qt); + } + return n && (o = /** @type {Derived} */ + e, Xr(o) && Ul(o)), e.v; +} +function wn(e) { + var t = en; + try { + return en = !0, e(); + } finally { + en = t; + } +} +const Xf = -7169; +function rn(e, t) { + e.f = e.f & Xf | t; +} +function j(e) { + if (!(typeof e != "object" || !e || e instanceof EventTarget)) { + if (Wn in e) + ps(e); + else if (!Array.isArray(e)) + for (let t in e) { + const n = e[t]; + typeof n == "object" && n && Wn in n && ps(n); + } + } +} +function ps(e, t = /* @__PURE__ */ new Set()) { + if (typeof e == "object" && e !== null && // We don't want to traverse DOM elements + !(e instanceof EventTarget) && !t.has(e)) { + t.add(e), e instanceof Date && e.getTime(); + for (let r in e) + try { + ps(e[r], t); + } catch { + } + const n = Fs(e); + if (n !== Object.prototype && n !== Array.prototype && n !== Map.prototype && n !== Set.prototype && n !== Date.prototype) { + const r = Zl(n); + for (let o in r) { + const i = r[o].get; + if (i) + try { + i.call(e); + } catch { + } + } + } + } +} +function iu(e) { + qe === null && je === null && Ef(), je !== null && je.f & qt && qe === null && $f(), Gs && kf(); +} +function Ff(e, t) { + var n = t.last; + n === null ? t.last = t.first = e : (n.next = e, e.prev = n, t.last = e); +} +function hr(e, t, n, r = !0) { + var o = (e & Yr) !== 0, i = qe, s = { + ctx: Ze, + deps: null, + nodes_start: null, + nodes_end: null, + f: e | In, + first: null, + fn: t, + last: null, + next: null, + parent: o ? null : i, + prev: null, + teardown: null, + transitions: null, + wv: 0 + }; + if (n) + try { + Ti(s), s.f |= hf; + } catch (u) { + throw Gt(s), u; + } + else t !== null && Hi(s); + var a = n && s.deps === null && s.first === null && s.nodes_start === null && s.teardown === null && (s.f & (Yl | ti)) === 0; + if (!a && !o && r && (i !== null && Ff(s, i), je !== null && je.f & nn)) { + var l = ( + /** @type {Derived} */ + je + ); + (l.effects ?? (l.effects = [])).push(s); + } + return s; +} +function su(e) { + const t = hr(Si, null, !1); + return rn(t, mt), t.teardown = e, t; +} +function Nr(e) { + iu(); + var t = qe !== null && (qe.f & On) !== 0 && Ze !== null && !Ze.m; + if (t) { + var n = ( + /** @type {ComponentContext} */ + Ze + ); + (n.e ?? (n.e = [])).push({ + fn: e, + effect: qe, + reaction: je + }); + } else { + var r = Ot(e); + return r; + } +} +function Wf(e) { + return iu(), Fr(e); +} +function Kf(e) { + const t = hr(Yr, e, !0); + return () => { + Gt(t); + }; +} +function qf(e) { + const t = hr(Yr, e, !0); + return (n = {}) => new Promise((r) => { + n.outro ? Mr(t, () => { + Gt(t), r(void 0); + }) : (Gt(t), r(void 0)); + }); +} +function Ot(e) { + return hr(Bl, e, !1); +} +function he(e, t) { + var n = ( + /** @type {ComponentContextLegacy} */ + Ze + ), r = { effect: null, ran: !1 }; + n.l.r1.push(r), r.effect = Fr(() => { + e(), !r.ran && (r.ran = !0, U(n.l.r2, !0), wn(t)); + }); +} +function gt() { + var e = ( + /** @type {ComponentContextLegacy} */ + Ze + ); + Fr(() => { + if (h(e.l.r2)) { + for (var t of e.l.r1) { + var n = t.effect; + n.f & mt && rn(n, gr), Xr(n) && Ti(n), t.ran = !1; + } + e.l.r2.v = !1; + } + }); +} +function Fr(e) { + return hr(Si, e, !0); +} +function Ee(e, t = [], n = Me) { + const r = t.map(n); + return vr(() => e(...r.map(h))); +} +function vr(e, t = 0) { + return hr(Si | Ys | t, e, !0); +} +function Dn(e, t = !0) { + return hr(Si | On, e, !0, t); +} +function au(e) { + var t = e.teardown; + if (t !== null) { + const n = Gs, r = je; + Da(!0), jn(null); + try { + t.call(null); + } finally { + Da(n), jn(r); + } + } +} +function lu(e, t = !1) { + var n = e.first; + for (e.first = e.last = null; n !== null; ) { + var r = n.next; + Gt(n, t), n = r; + } +} +function Gf(e) { + for (var t = e.first; t !== null; ) { + var n = t.next; + t.f & On || Gt(t), t = n; + } +} +function Gt(e, t = !0) { + var n = !1; + if ((t || e.f & pf) && e.nodes_start !== null) { + for (var r = e.nodes_start, o = e.nodes_end; r !== null; ) { + var i = r === o ? null : ( + /** @type {TemplateNode} */ + /* @__PURE__ */ xn(r) + ); + r.remove(), r = i; + } + n = !0; + } + lu(e, t && !n), ai(e, 0), rn(e, Pi); + var s = e.transitions; + if (s !== null) + for (const l of s) + l.stop(); + au(e); + var a = e.parent; + a !== null && a.first !== null && uu(e), e.next = e.prev = e.teardown = e.ctx = e.deps = e.fn = e.nodes_start = e.nodes_end = null; +} +function uu(e) { + var t = e.parent, n = e.prev, r = e.next; + n !== null && (n.next = r), r !== null && (r.prev = n), t !== null && (t.first === e && (t.first = r), t.last === e && (t.last = n)); +} +function Mr(e, t) { + var n = []; + Us(e, n, !0), cu(n, () => { + Gt(e), t && t(); + }); +} +function cu(e, t) { + var n = e.length; + if (n > 0) { + var r = () => --n || t(); + for (var o of e) + o.out(r); + } else + t(); +} +function Us(e, t, n) { + if (!(e.f & Mn)) { + if (e.f ^= Mn, e.transitions !== null) + for (const s of e.transitions) + (s.is_global || n) && t.push(s); + for (var r = e.first; r !== null; ) { + var o = r.next, i = (r.f & Zr) !== 0 || (r.f & On) !== 0; + Us(r, t, i ? n : !1), r = o; + } + } +} +function co(e) { + du(e, !0); +} +function du(e, t) { + if (e.f & Mn) { + e.f ^= Mn, e.f & mt || (e.f ^= mt), Xr(e) && (rn(e, In), Hi(e)); + for (var n = e.first; n !== null; ) { + var r = n.next, o = (n.f & Zr) !== 0 || (n.f & On) !== 0; + du(n, o ? t : !1), n = r; + } + if (e.transitions !== null) + for (const i of e.transitions) + (i.is_global || t) && i.in(); + } +} +function Vi(e) { + throw new Error("https://svelte.dev/e/lifecycle_outside_component"); +} +let Ze = null; +function Aa(e) { + Ze = e; +} +function ar(e) { + return ( + /** @type {T} */ + js().get(e) + ); +} +function Tr(e, t) { + return js().set(e, t), t; +} +function Uf(e) { + return js().has(e); +} +function de(e, t = !1, n) { + Ze = { + p: Ze, + c: null, + e: null, + m: !1, + s: e, + x: null, + l: null + }, Br && !t && (Ze.l = { + s: null, + u: null, + r1: [], + r2: Mt(!1) + }); +} +function fe(e) { + const t = Ze; + if (t !== null) { + e !== void 0 && (t.x = e); + const s = t.e; + if (s !== null) { + var n = qe, r = je; + t.e = null; + try { + for (var o = 0; o < s.length; o++) { + var i = s[o]; + Jn(i.effect), jn(i.reaction), Ot(i.fn); + } + } finally { + Jn(n), jn(r); + } + } + Ze = t.p, t.m = !0; + } + return e || /** @type {T} */ + {}; +} +function Di() { + return !Br || Ze !== null && Ze.l === null; +} +function js(e) { + return Ze === null && Vi(), Ze.c ?? (Ze.c = new Map(jf(Ze) || void 0)); +} +function jf(e) { + let t = e.p; + for (; t !== null; ) { + const n = t.c; + if (n !== null) + return n; + t = t.p; + } + return null; +} +function Jf(e) { + return e.endsWith("capture") && e !== "gotpointercapture" && e !== "lostpointercapture"; +} +const Qf = [ + "beforeinput", + "click", + "change", + "dblclick", + "contextmenu", + "focusin", + "focusout", + "input", + "keydown", + "keyup", + "mousedown", + "mousemove", + "mouseout", + "mouseover", + "mouseup", + "pointerdown", + "pointermove", + "pointerout", + "pointerover", + "pointerup", + "touchend", + "touchmove", + "touchstart" +]; +function e1(e) { + return Qf.includes(e); +} +const t1 = { + // no `class: 'className'` because we handle that separately + formnovalidate: "formNoValidate", + ismap: "isMap", + nomodule: "noModule", + playsinline: "playsInline", + readonly: "readOnly", + defaultvalue: "defaultValue", + defaultchecked: "defaultChecked", + srcobject: "srcObject", + novalidate: "noValidate", + allowfullscreen: "allowFullscreen", + disablepictureinpicture: "disablePictureInPicture", + disableremoteplayback: "disableRemotePlayback" +}; +function n1(e) { + return e = e.toLowerCase(), t1[e] ?? e; +} +const r1 = ["touchstart", "touchmove"]; +function o1(e) { + return r1.includes(e); +} +const i1 = ( + /** @type {const} */ + ["textarea", "script", "style", "title"] +); +function s1(e) { + return i1.includes( + /** @type {RAW_TEXT_ELEMENTS[number]} */ + e + ); +} +function a1(e, t) { + if (t) { + const n = document.body; + e.autofocus = !0, ko(() => { + document.activeElement === n && e.focus(); + }); + } +} +function l1(e) { + Pe && /* @__PURE__ */ bt(e) !== null && qs(e); +} +let La = !1; +function u1() { + La || (La = !0, document.addEventListener( + "reset", + (e) => { + Promise.resolve().then(() => { + var t; + if (!e.defaultPrevented) + for ( + const n of + /**@type {HTMLFormElement} */ + e.target.elements + ) + (t = n.__on_r) == null || t.call(n); + }); + }, + // In the capture phase to guarantee we get noticed of it (no possiblity of stopPropagation) + { capture: !0 } + )); +} +function c1(e) { + var t = je, n = qe; + jn(null), Jn(null); + try { + return e(); + } finally { + jn(t), Jn(n); + } +} +const fu = /* @__PURE__ */ new Set(), ms = /* @__PURE__ */ new Set(); +function gu(e, t, n, r = {}) { + function o(i) { + if (r.capture || eo.call(t, i), !i.cancelBubble) + return c1(() => n == null ? void 0 : n.call(this, i)); + } + return e.startsWith("pointer") || e.startsWith("touch") || e === "wheel" ? ko(() => { + t.addEventListener(e, o, r); + }) : t.addEventListener(e, o, r), o; +} +function Ye(e, t, n, r, o) { + var i = { capture: r, passive: o }, s = gu(e, t, n, i); + (t === document.body || t === window || t === document) && su(() => { + t.removeEventListener(e, s, i); + }); +} +function Ai(e) { + for (var t = 0; t < e.length; t++) + fu.add(e[t]); + for (var n of ms) + n(e); +} +function eo(e) { + var m; + var t = this, n = ( + /** @type {Node} */ + t.ownerDocument + ), r = e.type, o = ((m = e.composedPath) == null ? void 0 : m.call(e)) || [], i = ( + /** @type {null | Element} */ + o[0] || e.target + ), s = 0, a = e.__root; + if (a) { + var l = o.indexOf(a); + if (l !== -1 && (t === document || t === /** @type {any} */ + window)) { + e.__root = t; + return; + } + var u = o.indexOf(t); + if (u === -1) + return; + l <= u && (s = l); + } + if (i = /** @type {Element} */ + o[s] || e.target, i !== t) { + so(e, "currentTarget", { + configurable: !0, + get() { + return i || n; + } + }); + var c = je, f = qe; + jn(null), Jn(null); + try { + for (var d, g = []; i !== null; ) { + var p = i.assignedSlot || i.parentNode || /** @type {any} */ + i.host || null; + try { + var x = i["__" + r]; + if (x !== void 0 && (!/** @type {any} */ + i.disabled || // DOM could've been updated already by the time this is reached, so we check this as well + // -> the target could not have been disabled because it emits the event in the first place + e.target === i)) + if (Co(x)) { + var [C, ...$] = x; + C.apply(i, [e, ...$]); + } else + x.call(i, e); + } catch (_) { + d ? g.push(_) : d = _; + } + if (e.cancelBubble || p === t || p === null) + break; + i = p; + } + if (d) { + for (let _ of g) + queueMicrotask(() => { + throw _; + }); + throw d; + } + } finally { + e.__root = t, delete e.currentTarget, jn(c), Jn(f); + } + } +} +function Js(e) { + var t = document.createElement("template"); + return t.innerHTML = e, t.content; +} +function Vt(e, t) { + var n = ( + /** @type {Effect} */ + qe + ); + n.nodes_start === null && (n.nodes_start = e, n.nodes_end = t); +} +// @__NO_SIDE_EFFECTS__ +function ne(e, t) { + var n = (t & zl) !== 0, r = (t & gf) !== 0, o, i = !e.startsWith("<!>"); + return () => { + if (Pe) + return Vt(De, null), De; + o === void 0 && (o = Js(i ? e : "<!>" + e), n || (o = /** @type {Node} */ + /* @__PURE__ */ bt(o))); + var s = ( + /** @type {TemplateNode} */ + r || jl ? document.importNode(o, !0) : o.cloneNode(!0) + ); + if (n) { + var a = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ bt(s) + ), l = ( + /** @type {TemplateNode} */ + s.lastChild + ); + Vt(a, l); + } else + Vt(s, s); + return s; + }; +} +// @__NO_SIDE_EFFECTS__ +function _e(e, t, n = "svg") { + var r = !e.startsWith("<!>"), o = (t & zl) !== 0, i = `<${n}>${r ? e : "<!>" + e}</${n}>`, s; + return () => { + if (Pe) + return Vt(De, null), De; + if (!s) { + var a = ( + /** @type {DocumentFragment} */ + Js(i) + ), l = ( + /** @type {Element} */ + /* @__PURE__ */ bt(a) + ); + if (o) + for (s = document.createDocumentFragment(); /* @__PURE__ */ bt(l); ) + s.appendChild( + /** @type {Node} */ + /* @__PURE__ */ bt(l) + ); + else + s = /** @type {Element} */ + /* @__PURE__ */ bt(l); + } + var u = ( + /** @type {TemplateNode} */ + s.cloneNode(!0) + ); + if (o) { + var c = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ bt(u) + ), f = ( + /** @type {TemplateNode} */ + u.lastChild + ); + Vt(c, f); + } else + Vt(u, u); + return u; + }; +} +function Ie(e = "") { + if (!Pe) { + var t = Vn(e + ""); + return Vt(t, t), t; + } + var n = De; + return n.nodeType !== 3 && (n.before(n = Vn()), Ct(n)), Vt(n, n), n; +} +function et() { + if (Pe) + return Vt(De, null), De; + var e = document.createDocumentFragment(), t = document.createComment(""), n = Vn(); + return e.append(t, n), Vt(t, n), e; +} +function L(e, t) { + if (Pe) { + qe.nodes_end = De, yn(); + return; + } + e !== null && e.before( + /** @type {Node} */ + t + ); +} +function Rt(e, t) { + var n = t == null ? "" : typeof t == "object" ? t + "" : t; + n !== (e.__t ?? (e.__t = e.nodeValue)) && (e.__t = n, e.nodeValue = n + ""); +} +function hu(e, t) { + return vu(e, t); +} +function d1(e, t) { + vs(), t.intro = t.intro ?? !1; + const n = t.target, r = Pe, o = De; + try { + for (var i = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ bt(n) + ); i && (i.nodeType !== 8 || /** @type {Comment} */ + i.data !== zs); ) + i = /** @type {TemplateNode} */ + /* @__PURE__ */ xn(i); + if (!i) + throw _r; + It(!0), Ct( + /** @type {Comment} */ + i + ), yn(); + const s = vu(e, { ...t, anchor: i }); + if (De === null || De.nodeType !== 8 || /** @type {Comment} */ + De.data !== Bs) + throw Ni(), _r; + return It(!1), /** @type {Exports} */ + s; + } catch (s) { + if (s === _r) + return t.recover === !1 && Pf(), vs(), qs(n), It(!1), hu(e, t); + throw s; + } finally { + It(r), Ct(o); + } +} +const mr = /* @__PURE__ */ new Map(); +function vu(e, { target: t, anchor: n, props: r = {}, events: o, context: i, intro: s = !0 }) { + vs(); + var a = /* @__PURE__ */ new Set(), l = (f) => { + for (var d = 0; d < f.length; d++) { + var g = f[d]; + if (!a.has(g)) { + a.add(g); + var p = o1(g); + t.addEventListener(g, eo, { passive: p }); + var x = mr.get(g); + x === void 0 ? (document.addEventListener(g, eo, { passive: p }), mr.set(g, 1)) : mr.set(g, x + 1); + } + } + }; + l(Xs(fu)), ms.add(l); + var u = void 0, c = qf(() => { + var f = n ?? t.appendChild(Vn()); + return Dn(() => { + if (i) { + de({}); + var d = ( + /** @type {ComponentContext} */ + Ze + ); + d.c = i; + } + o && (r.$$events = o), Pe && Vt( + /** @type {TemplateNode} */ + f, + null + ), u = e(f, r) || {}, Pe && (qe.nodes_end = De), i && fe(); + }), () => { + var p; + for (var d of a) { + t.removeEventListener(d, eo); + var g = ( + /** @type {number} */ + mr.get(d) + ); + --g === 0 ? (document.removeEventListener(d, eo), mr.delete(d)) : mr.set(d, g); + } + ms.delete(l), f !== n && ((p = f.parentNode) == null || p.removeChild(f)); + }; + }); + return ys.set(u, c), u; +} +let ys = /* @__PURE__ */ new WeakMap(); +function f1(e, t) { + const n = ys.get(e); + return n ? (ys.delete(e), n(t)) : Promise.resolve(); +} +function ke(e, t, [n, r] = [0, 0]) { + Pe && n === 0 && yn(); + var o = e, i = null, s = null, a = Pt, l = n > 0 ? Zr : 0, u = !1; + const c = (d, g = !0) => { + u = !0, f(g, d); + }, f = (d, g) => { + if (a === (a = d)) return; + let p = !1; + if (Pe && r !== -1) { + if (n === 0) { + const C = ( + /** @type {Comment} */ + o.data + ); + C === zs ? r = 0 : C === Rs ? r = 1 / 0 : (r = parseInt(C.substring(1)), r !== r && (r = a ? 1 / 0 : -1)); + } + const x = r > n; + !!a === x && (o = hs(), Ct(o), It(!1), p = !0, r = -1); + } + a ? (i ? co(i) : g && (i = Dn(() => g(o))), s && Mr(s, () => { + s = null; + })) : (s ? co(s) : g && (s = Dn(() => g(o, [n + 1, r]))), i && Mr(i, () => { + i = null; + })), p && It(!0); + }; + vr(() => { + u = !1, t(c), u || f(null, null); + }, l), Pe && (o = De); +} +function Li(e, t) { + return t; +} +function g1(e, t, n, r) { + for (var o = [], i = t.length, s = 0; s < i; s++) + Us(t[s].e, o, !0); + var a = i > 0 && o.length === 0 && n !== null; + if (a) { + var l = ( + /** @type {Element} */ + /** @type {Element} */ + n.parentNode + ); + qs(l), l.append( + /** @type {Element} */ + n + ), r.clear(), Bn(e, t[0].prev, t[i - 1].next); + } + cu(o, () => { + for (var u = 0; u < i; u++) { + var c = t[u]; + a || (r.delete(c.k), Bn(e, c.prev, c.next)), Gt(c.e, !a); + } + }); +} +function Yt(e, t, n, r, o, i = null) { + var s = e, a = { flags: t, items: /* @__PURE__ */ new Map(), first: null }, l = (t & Ol) !== 0; + if (l) { + var u = ( + /** @type {Element} */ + e + ); + s = Pe ? Ct( + /** @type {Comment | Text} */ + /* @__PURE__ */ bt(u) + ) : u.appendChild(Vn()); + } + Pe && yn(); + var c = null, f = !1, d = /* @__PURE__ */ pe(() => { + var g = n(); + return Co(g) ? g : g == null ? [] : Xs(g); + }); + vr(() => { + var g = h(d), p = g.length; + if (f && p === 0) + return; + f = p === 0; + let x = !1; + if (Pe) { + var C = ( + /** @type {Comment} */ + s.data === Rs + ); + C !== (p === 0) && (s = hs(), Ct(s), It(!1), x = !0); + } + if (Pe) { + for (var $ = null, m, _ = 0; _ < p; _++) { + if (De.nodeType === 8 && /** @type {Comment} */ + De.data === Bs) { + s = /** @type {Comment} */ + De, x = !0, It(!1); + break; + } + var v = g[_], b = r(v, _); + m = pu( + De, + a, + $, + null, + v, + b, + _, + o, + t, + n + ), a.items.set(b, m), $ = m; + } + p > 0 && Ct(hs()); + } + Pe || h1(g, a, s, o, t, r, n), i !== null && (p === 0 ? c ? co(c) : c = Dn(() => i(s)) : c !== null && Mr(c, () => { + c = null; + })), x && It(!0), h(d); + }), Pe && (s = De); +} +function h1(e, t, n, r, o, i, s) { + var S, T, k, P; + var a = (o & af) !== 0, l = (o & (Os | Is)) !== 0, u = e.length, c = t.items, f = t.first, d = f, g, p = null, x, C = [], $ = [], m, _, v, b; + if (a) + for (b = 0; b < u; b += 1) + m = e[b], _ = i(m, b), v = c.get(_), v !== void 0 && ((S = v.a) == null || S.measure(), (x ?? (x = /* @__PURE__ */ new Set())).add(v)); + for (b = 0; b < u; b += 1) { + if (m = e[b], _ = i(m, b), v = c.get(_), v === void 0) { + var N = d ? ( + /** @type {TemplateNode} */ + d.e.nodes_start + ) : n; + p = pu( + N, + t, + p, + p === null ? t.first : p.next, + m, + _, + b, + r, + o, + s + ), c.set(_, p), C = [], $ = [], d = p.next; + continue; + } + if (l && v1(v, m, b, o), v.e.f & Mn && (co(v.e), a && ((T = v.a) == null || T.unfix(), (x ?? (x = /* @__PURE__ */ new Set())).delete(v))), v !== d) { + if (g !== void 0 && g.has(v)) { + if (C.length < $.length) { + var E = $[0], M; + p = E.prev; + var D = C[0], V = C[C.length - 1]; + for (M = 0; M < C.length; M += 1) + Oa(C[M], E, n); + for (M = 0; M < $.length; M += 1) + g.delete($[M]); + Bn(t, D.prev, V.next), Bn(t, p, D), Bn(t, V, E), d = E, p = V, b -= 1, C = [], $ = []; + } else + g.delete(v), Oa(v, d, n), Bn(t, v.prev, v.next), Bn(t, v, p === null ? t.first : p.next), Bn(t, p, v), p = v; + continue; + } + for (C = [], $ = []; d !== null && d.k !== _; ) + d.e.f & Mn || (g ?? (g = /* @__PURE__ */ new Set())).add(d), $.push(d), d = d.next; + if (d === null) + continue; + v = d; + } + C.push(v), p = v, d = v.next; + } + if (d !== null || g !== void 0) { + for (var A = g === void 0 ? [] : Xs(g); d !== null; ) + d.e.f & Mn || A.push(d), d = d.next; + var O = A.length; + if (O > 0) { + var R = o & Ol && u === 0 ? n : null; + if (a) { + for (b = 0; b < O; b += 1) + (k = A[b].a) == null || k.measure(); + for (b = 0; b < O; b += 1) + (P = A[b].a) == null || P.fix(); + } + g1(t, A, R, c); + } + } + a && ko(() => { + var H; + if (x !== void 0) + for (v of x) + (H = v.a) == null || H.apply(); + }), qe.first = t.first && t.first.e, qe.last = p && p.e; +} +function v1(e, t, n, r) { + r & Os && gs(e.v, t), r & Is ? gs( + /** @type {Value<number>} */ + e.i, + n + ) : e.i = n; +} +function pu(e, t, n, r, o, i, s, a, l, u) { + var c = (l & Os) !== 0, f = (l & lf) === 0, d = c ? f ? /* @__PURE__ */ $o(o) : Mt(o) : o, g = l & Is ? Mt(s) : s, p = { + i: g, + v: d, + k: i, + a: null, + // @ts-expect-error + e: null, + prev: n, + next: r + }; + try { + return p.e = Dn(() => a(e, d, g, u), Pe), p.e.prev = n && n.e, p.e.next = r && r.e, n === null ? t.first = p : (n.next = p, n.e.next = p.e), r !== null && (r.prev = p, r.e.prev = p.e), p; + } finally { + } +} +function Oa(e, t, n) { + for (var r = e.next ? ( + /** @type {TemplateNode} */ + e.next.e.nodes_start + ) : n, o = t ? ( + /** @type {TemplateNode} */ + t.e.nodes_start + ) : n, i = ( + /** @type {TemplateNode} */ + e.e.nodes_start + ); i !== r; ) { + var s = ( + /** @type {TemplateNode} */ + /* @__PURE__ */ xn(i) + ); + o.before(i), i = s; + } +} +function Bn(e, t, n) { + t === null ? e.first = n : (t.next = n, t.e.next = n && n.e), n !== null && (n.prev = t, n.e.prev = t && t.e); +} +function mu(e, t, n, r, o) { + var i = e, s = "", a; + vr(() => { + if (s === (s = t() ?? "")) { + Pe && yn(); + return; + } + a !== void 0 && (Gt(a), a = void 0), s !== "" && (a = Dn(() => { + if (Pe) { + De.data; + for (var l = yn(), u = l; l !== null && (l.nodeType !== 8 || /** @type {Comment} */ + l.data !== ""); ) + u = l, l = /** @type {TemplateNode} */ + /* @__PURE__ */ xn(l); + if (l === null) + throw Ni(), _r; + Vt(De, u), i = Ct(l); + return; + } + var c = s + "", f = Js(c); + Vt( + /** @type {TemplateNode} */ + /* @__PURE__ */ bt(f), + /** @type {TemplateNode} */ + f.lastChild + ), i.before(f); + })); + }); +} +function pt(e, t, n, r, o) { + var a; + Pe && yn(); + var i = (a = t.$$slots) == null ? void 0 : a[n], s = !1; + i === !0 && (i = t[n === "default" ? "children" : n], s = !0), i === void 0 || i(e, s ? () => r : r); +} +function p1(e) { + const t = {}; + e.children && (t.default = !0); + for (const n in e.$$slots) + t[n] = !0; + return t; +} +function lr(e, t, ...n) { + var r = e, o = dt, i; + vr(() => { + o !== (o = t()) && (i && (Gt(i), i = null), i = Dn(() => ( + /** @type {SnippetFn} */ + o(r, ...n) + ))); + }, Zr), Pe && (r = De); +} +function yu(e, t, n) { + Pe && yn(); + var r = e, o, i; + vr(() => { + o !== (o = t()) && (i && (Mr(i), i = null), o && (i = Dn(() => n(r, o)))); + }, Zr), Pe && (r = De); +} +function m1(e, t, n, r, o, i) { + let s = Pe; + Pe && yn(); + var a, l, u = null; + Pe && De.nodeType === 1 && (u = /** @type {Element} */ + De, yn()); + var c = ( + /** @type {TemplateNode} */ + Pe ? De : e + ), f; + vr(() => { + const d = t() || null; + var g = d === "svg" ? Rl : null; + d !== a && (f && (d === null ? Mr(f, () => { + f = null, l = null; + }) : d === l ? co(f) : Gt(f)), d && d !== l && (f = Dn(() => { + if (u = Pe ? ( + /** @type {Element} */ + u + ) : g ? document.createElementNS(g, d) : document.createElement(d), Vt(u, u), r) { + Pe && s1(d) && u.append(document.createComment("")); + var p = ( + /** @type {TemplateNode} */ + Pe ? /* @__PURE__ */ bt(u) : u.appendChild(Vn()) + ); + Pe && (p === null ? It(!1) : Ct(p)), r(u, p); + } + qe.nodes_end = u, c.before(u); + })), a = d, a && (l = a)); + }, Zr), s && (It(!0), Ct(c)); +} +function Je(e, t) { + ko(() => { + var n = e.getRootNode(), r = ( + /** @type {ShadowRoot} */ + n.host ? ( + /** @type {ShadowRoot} */ + n + ) : ( + /** @type {Document} */ + n.head ?? /** @type {Document} */ + n.ownerDocument.head + ) + ); + if (!r.querySelector("#" + t.hash)) { + const o = document.createElement("style"); + o.id = t.hash, o.textContent = t.code, r.appendChild(o); + } + }); +} +function vt(e, t, n) { + Ot(() => { + var r = wn(() => t(e, n == null ? void 0 : n()) || {}); + if (n && (r != null && r.update)) { + var o = !1, i = ( + /** @type {any} */ + {} + ); + Fr(() => { + var s = n(); + j(s), o && Ws(i, s) && (i = s, r.update(s)); + }), o = !0; + } + if (r != null && r.destroy) + return () => ( + /** @type {Function} */ + r.destroy() + ); + }); +} +function wu(e) { + var t, n, r = ""; + if (typeof e == "string" || typeof e == "number") r += e; + else if (typeof e == "object") if (Array.isArray(e)) { + var o = e.length; + for (t = 0; t < o; t++) e[t] && (n = wu(e[t])) && (r && (r += " "), r += n); + } else for (n in e) e[n] && (r && (r += " "), r += n); + return r; +} +function y1() { + for (var e, t, n = 0, r = "", o = arguments.length; n < o; n++) (e = arguments[n]) && (t = wu(e)) && (r && (r += " "), r += t); + return r; +} +function bn(e) { + return typeof e == "object" ? y1(e) : e ?? ""; +} +const Ia = [...` +\r\f \v\uFEFF`]; +function w1(e, t, n) { + var r = e == null ? "" : "" + e; + if (t && (r = r ? r + " " + t : t), n) { + for (var o in n) + if (n[o]) + r = r ? r + " " + o : o; + else if (r.length) + for (var i = o.length, s = 0; (s = r.indexOf(o, s)) >= 0; ) { + var a = s + i; + (s === 0 || Ia.includes(r[s - 1])) && (a === r.length || Ia.includes(r[a])) ? r = (s === 0 ? "" : r.substring(0, s)) + r.substring(a + 1) : s = a; + } + } + return r === "" ? null : r; +} +function kt(e, t, n, r, o, i) { + var s = e.__className; + if (Pe || s !== n) { + var a = w1(n, r, i); + (!Pe || a !== e.getAttribute("class")) && (a == null ? e.removeAttribute("class") : t ? e.className = a : e.setAttribute("class", a)), e.__className = n; + } else if (i) + for (var l in i) { + var u = !!i[l]; + (o == null || u !== !!o[l]) && e.classList.toggle(l, u); + } + return i; +} +const jr = Symbol("class"); +function io(e) { + if (Pe) { + var t = !1, n = () => { + if (!t) { + if (t = !0, e.hasAttribute("value")) { + var r = e.value; + ce(e, "value", null), e.value = r; + } + if (e.hasAttribute("checked")) { + var o = e.checked; + ce(e, "checked", null), e.checked = o; + } + } + }; + e.__on_r = n, Cf(n), u1(); + } +} +function Qi(e, t) { + var n = e.__attributes ?? (e.__attributes = {}); + n.value === (n.value = // treat null and undefined the same for the initial value + t ?? void 0) || // @ts-expect-error + // `progress` elements always need their value set when it's `0` + e.value === t && (t !== 0 || e.nodeName !== "PROGRESS") || (e.value = t ?? ""); +} +function _1(e, t) { + t ? e.hasAttribute("selected") || e.setAttribute("selected", "") : e.removeAttribute("selected"); +} +function ce(e, t, n, r) { + var o = e.__attributes ?? (e.__attributes = {}); + Pe && (o[t] = e.getAttribute(t), t === "src" || t === "srcset" || t === "href" && e.nodeName === "LINK") || o[t] !== (o[t] = n) && (t === "style" && "__styles" in e && (e.__styles = {}), t === "loading" && (e[mf] = n), n == null ? e.removeAttribute(t) : typeof n != "string" && _u(e).includes(t) ? e[t] = n : e.setAttribute(t, n)); +} +function on(e, t, n, r, o = !1, i = !1, s = !1) { + let a = Pe && i; + a && It(!1); + var l = t || {}, u = e.tagName === "OPTION"; + for (var c in t) + c in n || (n[c] = null); + n.class ? n.class = bn(n.class) : (r || n[jr]) && (n.class = null); + var f = _u(e), d = ( + /** @type {Record<string, unknown>} **/ + e.__attributes ?? (e.__attributes = {}) + ); + for (const _ in n) { + let v = n[_]; + if (u && _ === "value" && v == null) { + e.value = e.__value = "", l[_] = v; + continue; + } + if (_ === "class") { + var g = e.namespaceURI === "http://www.w3.org/1999/xhtml"; + kt(e, g, v, r, t == null ? void 0 : t[jr], n[jr]), l[_] = v, l[jr] = n[jr]; + continue; + } + var p = l[_]; + if (v !== p) { + l[_] = v; + var x = _[0] + _[1]; + if (x !== "$$") { + if (x === "on") { + const b = {}, N = "$$" + _; + let E = _.slice(2); + var C = e1(E); + if (Jf(E) && (E = E.slice(0, -7), b.capture = !0), !C && p) { + if (v != null) continue; + e.removeEventListener(E, l[N], b), l[N] = null; + } + if (v != null) + if (C) + e[`__${E}`] = v, Ai([E]); + else { + let M = function(D) { + l[_].call(this, D); + }; + l[N] = gu(E, e, M, b); + } + else C && (e[`__${E}`] = void 0); + } else if (_ === "style" && v != null) + e.style.cssText = v + ""; + else if (_ === "autofocus") + a1( + /** @type {HTMLElement} */ + e, + !!v + ); + else if (!i && (_ === "__value" || _ === "value" && v != null)) + e.value = e.__value = v; + else if (_ === "selected" && u) + _1( + /** @type {HTMLOptionElement} */ + e, + v + ); + else { + var $ = _; + o || ($ = n1($)); + var m = $ === "defaultValue" || $ === "defaultChecked"; + if (v == null && !i && !m) + if (d[_] = null, $ === "value" || $ === "checked") { + let b = ( + /** @type {HTMLInputElement} */ + e + ); + const N = t === void 0; + if ($ === "value") { + let E = b.defaultValue; + b.removeAttribute($), b.defaultValue = E, b.value = b.__value = N ? E : null; + } else { + let E = b.defaultChecked; + b.removeAttribute($), b.defaultChecked = E, b.checked = N ? E : !1; + } + } else + e.removeAttribute(_); + else m || f.includes($) && (i || typeof v != "string") ? e[$] = v : typeof v != "function" && ce(e, $, v); + } + _ === "style" && "__styles" in e && (e.__styles = {}); + } + } + } + return a && It(!0), l; +} +var za = /* @__PURE__ */ new Map(); +function _u(e) { + var t = za.get(e.nodeName); + if (t) return t; + za.set(e.nodeName, t = []); + for (var n, r = e, o = Element.prototype; o !== r; ) { + n = Zl(r); + for (var i in n) + n[i].set && t.push(i); + r = Fs(r); + } + return t; +} +function st(e, t, n, r) { + var o = e.__styles ?? (e.__styles = {}); + o[t] !== n && (o[t] = n, n == null ? e.style.removeProperty(t) : e.style.setProperty(t, n, "")); +} +var Zn, Pr, bo, $i, xu; +const Ei = class Ei { + /** @param {ResizeObserverOptions} options */ + constructor(t) { + rr(this, $i); + /** */ + rr(this, Zn, /* @__PURE__ */ new WeakMap()); + /** @type {ResizeObserver | undefined} */ + rr(this, Pr); + /** @type {ResizeObserverOptions} */ + rr(this, bo); + Gr(this, bo, t); + } + /** + * @param {Element} element + * @param {(entry: ResizeObserverEntry) => any} listener + */ + observe(t, n) { + var r = it(this, Zn).get(t) || /* @__PURE__ */ new Set(); + return r.add(n), it(this, Zn).set(t, r), Na(this, $i, xu).call(this).observe(t, it(this, bo)), () => { + var o = it(this, Zn).get(t); + o.delete(n), o.size === 0 && (it(this, Zn).delete(t), it(this, Pr).unobserve(t)); + }; + } +}; +Zn = new WeakMap(), Pr = new WeakMap(), bo = new WeakMap(), $i = new WeakSet(), xu = function() { + return it(this, Pr) ?? Gr(this, Pr, new ResizeObserver( + /** @param {any} entries */ + (t) => { + for (var n of t) { + Ei.entries.set(n.target, n); + for (var r of it(this, Zn).get(n.target) || []) + r(n); + } + } + )); +}, /** @static */ +wt(Ei, "entries", /* @__PURE__ */ new WeakMap()); +let ws = Ei; +var x1 = /* @__PURE__ */ new ws({ + box: "border-box" +}); +function Ra(e, t, n) { + var r = x1.observe(e, () => n(e[t])); + Ot(() => (wn(() => n(e[t])), r)); +} +function Ba(e, t) { + return e === t || (e == null ? void 0 : e[Wn]) === t; +} +function An(e = {}, t, n, r) { + return Ot(() => { + var o, i; + return Fr(() => { + o = i, i = [], wn(() => { + e !== n(...i) && (t(e, ...i), o && Ba(n(...o), e) && t(null, ...o)); + }); + }), () => { + ko(() => { + i && Ba(n(...i), e) && t(null, ...i); + }); + }; + }), e; +} +function es(e) { + return function(...t) { + var n = ( + /** @type {Event} */ + t[0] + ); + return n.stopPropagation(), e == null ? void 0 : e.apply(this, t); + }; +} +function He(e = !1) { + const t = ( + /** @type {ComponentContextLegacy} */ + Ze + ), n = t.l.u; + if (!n) return; + let r = () => j(t.s); + if (e) { + let o = 0, i = ( + /** @type {Record<string, any>} */ + {} + ); + const s = /* @__PURE__ */ Me(() => { + let a = !1; + const l = t.s; + for (const u in l) + l[u] !== i[u] && (i[u] = l[u], a = !0); + return a && o++, o; + }); + r = () => h(s); + } + n.b.length && Wf(() => { + Ya(t, r), ao(n.b); + }), Nr(() => { + const o = wn(() => n.m.map(xf)); + return () => { + for (const i of o) + typeof i == "function" && i(); + }; + }), n.a.length && Nr(() => { + Ya(t, r), ao(n.a); + }); +} +function Ya(e, t) { + if (e.l.s) + for (const n of e.l.s) h(n); + t(); +} +function Ve(e, t) { + var i; + var n = ( + /** @type {Record<string, Function[] | Function>} */ + (i = e.$$events) == null ? void 0 : i[t.type] + ), r = Co(n) ? n.slice() : n == null ? [] : [n]; + for (var o of r) + o.call(this, t); +} +function un(e) { + Ze === null && Vi(), Br && Ze.l !== null ? C1(Ze).m.push(e) : Nr(() => { + const t = wn(e); + if (typeof t == "function") return ( + /** @type {() => void} */ + t + ); + }); +} +function Qs(e) { + Ze === null && Vi(), un(() => () => wn(e)); +} +function b1(e, t, { bubbles: n = !1, cancelable: r = !1 } = {}) { + return new CustomEvent(e, { detail: t, bubbles: n, cancelable: r }); +} +function Oi() { + const e = Ze; + return e === null && Vi(), (t, n, r) => { + var i; + const o = ( + /** @type {Record<string, Function | Function[]>} */ + (i = e.s.$$events) == null ? void 0 : i[ + /** @type {any} */ + t + ] + ); + if (o) { + const s = Co(o) ? o.slice() : [o], a = b1( + /** @type {string} */ + t, + n, + r + ); + for (const l of s) + l.call(e.x, a); + return !a.defaultPrevented; + } + return !0; + }; +} +function C1(e) { + var t = ( + /** @type {ComponentContextLegacy} */ + e.l + ); + return t.u ?? (t.u = { a: [], b: [], m: [] }); +} +function ea(e, t, n) { + if (e == null) + return t(void 0), n && n(void 0), dt; + const r = wn( + () => e.subscribe( + t, + // @ts-expect-error + n + ) + ); + return r.unsubscribe ? () => r.unsubscribe() : r; +} +const yr = []; +function Ft(e, t) { + return { + subscribe: we(e, t).subscribe + }; +} +function we(e, t = dt) { + let n = null; + const r = /* @__PURE__ */ new Set(); + function o(a) { + if (Ws(e, a) && (e = a, n)) { + const l = !yr.length; + for (const u of r) + u[1](), yr.push(u, e); + if (l) { + for (let u = 0; u < yr.length; u += 2) + yr[u][0](yr[u + 1]); + yr.length = 0; + } + } + } + function i(a) { + o(a( + /** @type {T} */ + e + )); + } + function s(a, l = dt) { + const u = [a, l]; + return r.add(u), r.size === 1 && (n = t(o, i) || dt), a( + /** @type {T} */ + e + ), () => { + r.delete(u), r.size === 0 && n && (n(), n = null); + }; + } + return { set: o, update: i, subscribe: s }; +} +function Kn(e, t, n) { + const r = !Array.isArray(e), o = r ? [e] : e; + if (!o.every(Boolean)) + throw new Error("derived() expects stores as input, got a falsy value"); + const i = t.length < 2; + return Ft(n, (s, a) => { + let l = !1; + const u = []; + let c = 0, f = dt; + const d = () => { + if (c) + return; + f(); + const p = t(r ? u[0] : u, s, a); + i ? s(p) : f = typeof p == "function" ? p : dt; + }, g = o.map( + (p, x) => ea( + p, + (C) => { + u[x] = C, c &= ~(1 << x), l && d(); + }, + () => { + c |= 1 << x; + } + ) + ); + return l = !0, d(), function() { + ao(g), f(), l = !1; + }; + }); +} +function q(e) { + let t; + return ea(e, (n) => t = n)(), t; +} +let Bo = !1, _s = Symbol(); +function Q(e, t, n) { + const r = n[t] ?? (n[t] = { + store: null, + source: /* @__PURE__ */ $o(void 0), + unsubscribe: dt + }); + if (r.store !== e && !(_s in n)) + if (r.unsubscribe(), r.store = e ?? null, e == null) + r.source.v = void 0, r.unsubscribe = dt; + else { + var o = !0; + r.unsubscribe = ea(e, (i) => { + o ? r.source.v = i : U(r.source, i); + }), o = !1; + } + return e && _s in n ? q(e) : h(r.source); +} +function k1(e, t, n) { + let r = n[t]; + return r && r.store !== e && (r.unsubscribe(), r.unsubscribe = dt), e; +} +function li(e, t) { + return e.set(t), t; +} +function tt() { + const e = {}; + function t() { + su(() => { + for (var n in e) + e[n].unsubscribe(); + so(e, _s, { + enumerable: !1, + value: !0 + }); + }); + } + return [e, t]; +} +function $1(e) { + var t = Bo; + try { + return Bo = !1, [e(), Bo]; + } finally { + Bo = t; + } +} +const E1 = { + get(e, t) { + if (!e.exclude.includes(t)) + return e.props[t]; + }, + set(e, t) { + return !1; + }, + getOwnPropertyDescriptor(e, t) { + if (!e.exclude.includes(t) && t in e.props) + return { + enumerable: !0, + configurable: !0, + value: e.props[t] + }; + }, + has(e, t) { + return e.exclude.includes(t) ? !1 : t in e.props; + }, + ownKeys(e) { + return Reflect.ownKeys(e.props).filter((t) => !e.exclude.includes(t)); + } +}; +// @__NO_SIDE_EFFECTS__ +function yt(e, t, n) { + return new Proxy( + { props: e, exclude: t }, + E1 + ); +} +const S1 = { + get(e, t) { + if (!e.exclude.includes(t)) + return h(e.version), t in e.special ? e.special[t]() : e.props[t]; + }, + set(e, t, n) { + return t in e.special || (e.special[t] = w( + { + get [t]() { + return e.props[t]; + } + }, + /** @type {string} */ + t, + Il + )), e.special[t](n), Ha(e.version), !0; + }, + getOwnPropertyDescriptor(e, t) { + if (!e.exclude.includes(t) && t in e.props) + return { + enumerable: !0, + configurable: !0, + value: e.props[t] + }; + }, + deleteProperty(e, t) { + return e.exclude.includes(t) || (e.exclude.push(t), Ha(e.version)), !0; + }, + has(e, t) { + return e.exclude.includes(t) ? !1 : t in e.props; + }, + ownKeys(e) { + return Reflect.ownKeys(e.props).filter((t) => !e.exclude.includes(t)); + } +}; +function nt(e, t) { + return new Proxy({ props: e, exclude: t, special: {}, version: Mt(0) }, S1); +} +const P1 = { + get(e, t) { + let n = e.props.length; + for (; n--; ) { + let r = e.props[n]; + if (Ur(r) && (r = r()), typeof r == "object" && r !== null && t in r) return r[t]; + } + }, + set(e, t, n) { + let r = e.props.length; + for (; r--; ) { + let o = e.props[r]; + Ur(o) && (o = o()); + const i = Tn(o, t); + if (i && i.set) + return i.set(n), !0; + } + return !1; + }, + getOwnPropertyDescriptor(e, t) { + let n = e.props.length; + for (; n--; ) { + let r = e.props[n]; + if (Ur(r) && (r = r()), typeof r == "object" && r !== null && t in r) { + const o = Tn(r, t); + return o && !o.configurable && (o.configurable = !0), o; + } + } + }, + has(e, t) { + if (t === Wn || t === Zs) return !1; + for (let n of e.props) + if (Ur(n) && (n = n()), n != null && t in n) return !0; + return !1; + }, + ownKeys(e) { + const t = []; + for (let n of e.props) { + Ur(n) && (n = n()); + for (const r in n) + t.includes(r) || t.push(r); + } + return t; + } +}; +function ut(...e) { + return new Proxy({ props: e }, P1); +} +function w(e, t, n, r) { + var N; + var o = (n & uf) !== 0, i = !Br || (n & cf) !== 0, s = (n & df) !== 0, a = (n & ff) !== 0, l = !1, u; + s ? [u, l] = $1(() => ( + /** @type {V} */ + e[t] + )) : u = /** @type {V} */ + e[t]; + var c = Wn in e || Zs in e, f = s && (((N = Tn(e, t)) == null ? void 0 : N.set) ?? (c && t in e && ((E) => e[t] = E))) || void 0, d = ( + /** @type {V} */ + r + ), g = !0, p = !1, x = () => (p = !0, g && (g = !1, a ? d = wn( + /** @type {() => V} */ + r + ) : d = /** @type {V} */ + r), d); + u === void 0 && r !== void 0 && (f && i && Nf(), u = x(), f && f(u)); + var C; + if (i) + C = () => { + var E = ( + /** @type {V} */ + e[t] + ); + return E === void 0 ? x() : (g = !0, p = !1, E); + }; + else { + var $ = (o ? Me : pe)( + () => ( + /** @type {V} */ + e[t] + ) + ); + $.f |= vf, C = () => { + var E = h($); + return E !== void 0 && (d = /** @type {V} */ + void 0), E === void 0 ? d : E; + }; + } + if (!(n & Il)) + return C; + if (f) { + var m = e.$$legacy; + return function(E, M) { + return arguments.length > 0 ? ((!i || !M || m || l) && f(M ? C() : E), E) : C(); + }; + } + var _ = !1, v = /* @__PURE__ */ $o(u), b = /* @__PURE__ */ Me(() => { + var E = C(), M = h(v); + return _ ? (_ = !1, M) : v.v = E; + }); + return o || (b.equals = Ks), function(E, M) { + if (arguments.length > 0) { + const D = M ? h(b) : i && s ? Tt(E) : E; + return b.equals(D) || (_ = !0, U(v, D), p && d !== void 0 && (d = D), wn(() => h(b))), E; + } + return h(b); + }; +} +function N1(e) { + return new M1(e); +} +var Sn, Wt; +class M1 { + /** + * @param {ComponentConstructorOptions & { + * component: any; + * }} options + */ + constructor(t) { + /** @type {any} */ + rr(this, Sn); + /** @type {Record<string, any>} */ + rr(this, Wt); + var i; + var n = /* @__PURE__ */ new Map(), r = (s, a) => { + var l = /* @__PURE__ */ $o(a); + return n.set(s, l), l; + }; + const o = new Proxy( + { ...t.props || {}, $$events: {} }, + { + get(s, a) { + return h(n.get(a) ?? r(a, Reflect.get(s, a))); + }, + has(s, a) { + return a === Zs ? !0 : (h(n.get(a) ?? r(a, Reflect.get(s, a))), Reflect.has(s, a)); + }, + set(s, a, l) { + return U(n.get(a) ?? r(a, l), l), Reflect.set(s, a, l); + } + } + ); + Gr(this, Wt, (t.hydrate ? d1 : hu)(t.component, { + target: t.target, + anchor: t.anchor, + props: o, + context: t.context, + intro: t.intro ?? !1, + recover: t.recover + })), (!((i = t == null ? void 0 : t.props) != null && i.$$host) || t.sync === !1) && y(), Gr(this, Sn, o.$$events); + for (const s of Object.keys(it(this, Wt))) + s === "$set" || s === "$destroy" || s === "$on" || so(this, s, { + get() { + return it(this, Wt)[s]; + }, + /** @param {any} value */ + set(a) { + it(this, Wt)[s] = a; + }, + enumerable: !0 + }); + it(this, Wt).$set = /** @param {Record<string, any>} next */ + (s) => { + Object.assign(o, s); + }, it(this, Wt).$destroy = () => { + f1(it(this, Wt)); + }; + } + /** @param {Record<string, any>} props */ + $set(t) { + it(this, Wt).$set(t); + } + /** + * @param {string} event + * @param {(...args: any[]) => any} callback + * @returns {any} + */ + $on(t, n) { + it(this, Sn)[t] = it(this, Sn)[t] || []; + const r = (...o) => n.call(this, ...o); + return it(this, Sn)[t].push(r), () => { + it(this, Sn)[t] = it(this, Sn)[t].filter( + /** @param {any} fn */ + (o) => o !== r + ); + }; + } + $destroy() { + it(this, Wt).$destroy(); + } +} +Sn = new WeakMap(), Wt = new WeakMap(); +let bu; +typeof HTMLElement == "function" && (bu = class extends HTMLElement { + /** + * @param {*} $$componentCtor + * @param {*} $$slots + * @param {*} use_shadow_dom + */ + constructor(t, n, r) { + super(); + /** The Svelte component constructor */ + wt(this, "$$ctor"); + /** Slots */ + wt(this, "$$s"); + /** @type {any} The Svelte component instance */ + wt(this, "$$c"); + /** Whether or not the custom element is connected */ + wt(this, "$$cn", !1); + /** @type {Record<string, any>} Component props data */ + wt(this, "$$d", {}); + /** `true` if currently in the process of reflecting component props back to attributes */ + wt(this, "$$r", !1); + /** @type {Record<string, CustomElementPropDefinition>} Props definition (name, reflected, type etc) */ + wt(this, "$$p_d", {}); + /** @type {Record<string, EventListenerOrEventListenerObject[]>} Event listeners */ + wt(this, "$$l", {}); + /** @type {Map<EventListenerOrEventListenerObject, Function>} Event listener unsubscribe functions */ + wt(this, "$$l_u", /* @__PURE__ */ new Map()); + /** @type {any} The managed render effect for reflecting attributes */ + wt(this, "$$me"); + this.$$ctor = t, this.$$s = n, r && this.attachShadow({ mode: "open" }); + } + /** + * @param {string} type + * @param {EventListenerOrEventListenerObject} listener + * @param {boolean | AddEventListenerOptions} [options] + */ + addEventListener(t, n, r) { + if (this.$$l[t] = this.$$l[t] || [], this.$$l[t].push(n), this.$$c) { + const o = this.$$c.$on(t, n); + this.$$l_u.set(n, o); + } + super.addEventListener(t, n, r); + } + /** + * @param {string} type + * @param {EventListenerOrEventListenerObject} listener + * @param {boolean | AddEventListenerOptions} [options] + */ + removeEventListener(t, n, r) { + if (super.removeEventListener(t, n, r), this.$$c) { + const o = this.$$l_u.get(n); + o && (o(), this.$$l_u.delete(n)); + } + } + async connectedCallback() { + if (this.$$cn = !0, !this.$$c) { + let t = function(o) { + return (i) => { + const s = document.createElement("slot"); + o !== "default" && (s.name = o), L(i, s); + }; + }; + if (await Promise.resolve(), !this.$$cn || this.$$c) + return; + const n = {}, r = T1(this); + for (const o of this.$$s) + o in r && (o === "default" && !this.$$d.children ? (this.$$d.children = t(o), n.default = !0) : n[o] = t(o)); + for (const o of this.attributes) { + const i = this.$$g_p(o.name); + i in this.$$d || (this.$$d[i] = Go(i, o.value, this.$$p_d, "toProp")); + } + for (const o in this.$$p_d) + !(o in this.$$d) && this[o] !== void 0 && (this.$$d[o] = this[o], delete this[o]); + this.$$c = N1({ + component: this.$$ctor, + target: this.shadowRoot || this, + props: { + ...this.$$d, + $$slots: n, + $$host: this + } + }), this.$$me = Kf(() => { + Fr(() => { + var o; + this.$$r = !0; + for (const i of ri(this.$$c)) { + if (!((o = this.$$p_d[i]) != null && o.reflect)) continue; + this.$$d[i] = this.$$c[i]; + const s = Go( + i, + this.$$d[i], + this.$$p_d, + "toAttribute" + ); + s == null ? this.removeAttribute(this.$$p_d[i].attribute || i) : this.setAttribute(this.$$p_d[i].attribute || i, s); + } + this.$$r = !1; + }); + }); + for (const o in this.$$l) + for (const i of this.$$l[o]) { + const s = this.$$c.$on(o, i); + this.$$l_u.set(i, s); + } + this.$$l = {}; + } + } + // We don't need this when working within Svelte code, but for compatibility of people using this outside of Svelte + // and setting attributes through setAttribute etc, this is helpful + /** + * @param {string} attr + * @param {string} _oldValue + * @param {string} newValue + */ + attributeChangedCallback(t, n, r) { + var o; + this.$$r || (t = this.$$g_p(t), this.$$d[t] = Go(t, r, this.$$p_d, "toProp"), (o = this.$$c) == null || o.$set({ [t]: this.$$d[t] })); + } + disconnectedCallback() { + this.$$cn = !1, Promise.resolve().then(() => { + !this.$$cn && this.$$c && (this.$$c.$destroy(), this.$$me(), this.$$c = void 0); + }); + } + /** + * @param {string} attribute_name + */ + $$g_p(t) { + return ri(this.$$p_d).find( + (n) => this.$$p_d[n].attribute === t || !this.$$p_d[n].attribute && n.toLowerCase() === t + ) || t; + } +}); +function Go(e, t, n, r) { + var i; + const o = (i = n[e]) == null ? void 0 : i.type; + if (t = o === "Boolean" && typeof t != "boolean" ? t != null : t, !r || !n[e]) + return t; + if (r === "toAttribute") + switch (o) { + case "Object": + case "Array": + return t == null ? null : JSON.stringify(t); + case "Boolean": + return t ? "" : null; + case "Number": + return t ?? null; + default: + return t; + } + else + switch (o) { + case "Object": + case "Array": + return t && JSON.parse(t); + case "Boolean": + return t; + // conversion already handled above + case "Number": + return t != null ? +t : t; + default: + return t; + } +} +function T1(e) { + const t = {}; + return e.childNodes.forEach((n) => { + t[ + /** @type {Element} node */ + n.slot || "default" + ] = !0; + }), t; +} +function ae(e, t, n, r, o, i) { + let s = class extends bu { + constructor() { + super(e, n, o), this.$$p_d = t; + } + static get observedAttributes() { + return ri(t).map( + (a) => (t[a].attribute || a).toLowerCase() + ); + } + }; + return ri(t).forEach((a) => { + so(s.prototype, a, { + get() { + return this.$$c && a in this.$$c ? this.$$c[a] : this.$$d[a]; + }, + set(l) { + var f; + l = Go(a, l, t), this.$$d[a] = l; + var u = this.$$c; + if (u) { + var c = (f = Tn(u, a)) == null ? void 0 : f.get; + c ? u[a] = l : u.$set({ [a]: l }); + } + } + }); + }), r.forEach((a) => { + so(s.prototype, a, { + get() { + var l; + return (l = this.$$c) == null ? void 0 : l[a]; + } + }); + }), e.element = /** @type {any} */ + s, s; +} +function Et(e) { + if (typeof e == "string" || typeof e == "number") return "" + e; + let t = ""; + if (Array.isArray(e)) + for (let n = 0, r; n < e.length; n++) + (r = Et(e[n])) !== "" && (t += (t && " ") + r); + else + for (let n in e) + e[n] && (t += (t && " ") + n); + return t; +} +var H1 = { value: () => { +} }; +function Ii() { + for (var e = 0, t = arguments.length, n = {}, r; e < t; ++e) { + if (!(r = arguments[e] + "") || r in n || /[\s.]/.test(r)) throw new Error("illegal type: " + r); + n[r] = []; + } + return new Uo(n); +} +function Uo(e) { + this._ = e; +} +function V1(e, t) { + return e.trim().split(/^|\s+/).map(function(n) { + var r = "", o = n.indexOf("."); + if (o >= 0 && (r = n.slice(o + 1), n = n.slice(0, o)), n && !t.hasOwnProperty(n)) throw new Error("unknown type: " + n); + return { type: n, name: r }; + }); +} +Uo.prototype = Ii.prototype = { + constructor: Uo, + on: function(e, t) { + var n = this._, r = V1(e + "", n), o, i = -1, s = r.length; + if (arguments.length < 2) { + for (; ++i < s; ) if ((o = (e = r[i]).type) && (o = D1(n[o], e.name))) return o; + return; + } + if (t != null && typeof t != "function") throw new Error("invalid callback: " + t); + for (; ++i < s; ) + if (o = (e = r[i]).type) n[o] = Za(n[o], e.name, t); + else if (t == null) for (o in n) n[o] = Za(n[o], e.name, null); + return this; + }, + copy: function() { + var e = {}, t = this._; + for (var n in t) e[n] = t[n].slice(); + return new Uo(e); + }, + call: function(e, t) { + if ((o = arguments.length - 2) > 0) for (var n = new Array(o), r = 0, o, i; r < o; ++r) n[r] = arguments[r + 2]; + if (!this._.hasOwnProperty(e)) throw new Error("unknown type: " + e); + for (i = this._[e], r = 0, o = i.length; r < o; ++r) i[r].value.apply(t, n); + }, + apply: function(e, t, n) { + if (!this._.hasOwnProperty(e)) throw new Error("unknown type: " + e); + for (var r = this._[e], o = 0, i = r.length; o < i; ++o) r[o].value.apply(t, n); + } +}; +function D1(e, t) { + for (var n = 0, r = e.length, o; n < r; ++n) + if ((o = e[n]).name === t) + return o.value; +} +function Za(e, t, n) { + for (var r = 0, o = e.length; r < o; ++r) + if (e[r].name === t) { + e[r] = H1, e = e.slice(0, r).concat(e.slice(r + 1)); + break; + } + return n != null && e.push({ name: t, value: n }), e; +} +var xs = "http://www.w3.org/1999/xhtml"; +const Xa = { + svg: "http://www.w3.org/2000/svg", + xhtml: xs, + xlink: "http://www.w3.org/1999/xlink", + xml: "http://www.w3.org/XML/1998/namespace", + xmlns: "http://www.w3.org/2000/xmlns/" +}; +function zi(e) { + var t = e += "", n = t.indexOf(":"); + return n >= 0 && (t = e.slice(0, n)) !== "xmlns" && (e = e.slice(n + 1)), Xa.hasOwnProperty(t) ? { space: Xa[t], local: e } : e; +} +function A1(e) { + return function() { + var t = this.ownerDocument, n = this.namespaceURI; + return n === xs && t.documentElement.namespaceURI === xs ? t.createElement(e) : t.createElementNS(n, e); + }; +} +function L1(e) { + return function() { + return this.ownerDocument.createElementNS(e.space, e.local); + }; +} +function Cu(e) { + var t = zi(e); + return (t.local ? L1 : A1)(t); +} +function O1() { +} +function ta(e) { + return e == null ? O1 : function() { + return this.querySelector(e); + }; +} +function I1(e) { + typeof e != "function" && (e = ta(e)); + for (var t = this._groups, n = t.length, r = new Array(n), o = 0; o < n; ++o) + for (var i = t[o], s = i.length, a = r[o] = new Array(s), l, u, c = 0; c < s; ++c) + (l = i[c]) && (u = e.call(l, l.__data__, c, i)) && ("__data__" in l && (u.__data__ = l.__data__), a[c] = u); + return new Zt(r, this._parents); +} +function z1(e) { + return e == null ? [] : Array.isArray(e) ? e : Array.from(e); +} +function R1() { + return []; +} +function ku(e) { + return e == null ? R1 : function() { + return this.querySelectorAll(e); + }; +} +function B1(e) { + return function() { + return z1(e.apply(this, arguments)); + }; +} +function Y1(e) { + typeof e == "function" ? e = B1(e) : e = ku(e); + for (var t = this._groups, n = t.length, r = [], o = [], i = 0; i < n; ++i) + for (var s = t[i], a = s.length, l, u = 0; u < a; ++u) + (l = s[u]) && (r.push(e.call(l, l.__data__, u, s)), o.push(l)); + return new Zt(r, o); +} +function $u(e) { + return function() { + return this.matches(e); + }; +} +function Eu(e) { + return function(t) { + return t.matches(e); + }; +} +var Z1 = Array.prototype.find; +function X1(e) { + return function() { + return Z1.call(this.children, e); + }; +} +function F1() { + return this.firstElementChild; +} +function W1(e) { + return this.select(e == null ? F1 : X1(typeof e == "function" ? e : Eu(e))); +} +var K1 = Array.prototype.filter; +function q1() { + return Array.from(this.children); +} +function G1(e) { + return function() { + return K1.call(this.children, e); + }; +} +function U1(e) { + return this.selectAll(e == null ? q1 : G1(typeof e == "function" ? e : Eu(e))); +} +function j1(e) { + typeof e != "function" && (e = $u(e)); + for (var t = this._groups, n = t.length, r = new Array(n), o = 0; o < n; ++o) + for (var i = t[o], s = i.length, a = r[o] = [], l, u = 0; u < s; ++u) + (l = i[u]) && e.call(l, l.__data__, u, i) && a.push(l); + return new Zt(r, this._parents); +} +function Su(e) { + return new Array(e.length); +} +function J1() { + return new Zt(this._enter || this._groups.map(Su), this._parents); +} +function ui(e, t) { + this.ownerDocument = e.ownerDocument, this.namespaceURI = e.namespaceURI, this._next = null, this._parent = e, this.__data__ = t; +} +ui.prototype = { + constructor: ui, + appendChild: function(e) { + return this._parent.insertBefore(e, this._next); + }, + insertBefore: function(e, t) { + return this._parent.insertBefore(e, t); + }, + querySelector: function(e) { + return this._parent.querySelector(e); + }, + querySelectorAll: function(e) { + return this._parent.querySelectorAll(e); + } +}; +function Q1(e) { + return function() { + return e; + }; +} +function eg(e, t, n, r, o, i) { + for (var s = 0, a, l = t.length, u = i.length; s < u; ++s) + (a = t[s]) ? (a.__data__ = i[s], r[s] = a) : n[s] = new ui(e, i[s]); + for (; s < l; ++s) + (a = t[s]) && (o[s] = a); +} +function tg(e, t, n, r, o, i, s) { + var a, l, u = /* @__PURE__ */ new Map(), c = t.length, f = i.length, d = new Array(c), g; + for (a = 0; a < c; ++a) + (l = t[a]) && (d[a] = g = s.call(l, l.__data__, a, t) + "", u.has(g) ? o[a] = l : u.set(g, l)); + for (a = 0; a < f; ++a) + g = s.call(e, i[a], a, i) + "", (l = u.get(g)) ? (r[a] = l, l.__data__ = i[a], u.delete(g)) : n[a] = new ui(e, i[a]); + for (a = 0; a < c; ++a) + (l = t[a]) && u.get(d[a]) === l && (o[a] = l); +} +function ng(e) { + return e.__data__; +} +function rg(e, t) { + if (!arguments.length) return Array.from(this, ng); + var n = t ? tg : eg, r = this._parents, o = this._groups; + typeof e != "function" && (e = Q1(e)); + for (var i = o.length, s = new Array(i), a = new Array(i), l = new Array(i), u = 0; u < i; ++u) { + var c = r[u], f = o[u], d = f.length, g = og(e.call(c, c && c.__data__, u, r)), p = g.length, x = a[u] = new Array(p), C = s[u] = new Array(p), $ = l[u] = new Array(d); + n(c, f, x, C, $, g, t); + for (var m = 0, _ = 0, v, b; m < p; ++m) + if (v = x[m]) { + for (m >= _ && (_ = m + 1); !(b = C[_]) && ++_ < p; ) ; + v._next = b || null; + } + } + return s = new Zt(s, r), s._enter = a, s._exit = l, s; +} +function og(e) { + return typeof e == "object" && "length" in e ? e : Array.from(e); +} +function ig() { + return new Zt(this._exit || this._groups.map(Su), this._parents); +} +function sg(e, t, n) { + var r = this.enter(), o = this, i = this.exit(); + return typeof e == "function" ? (r = e(r), r && (r = r.selection())) : r = r.append(e + ""), t != null && (o = t(o), o && (o = o.selection())), n == null ? i.remove() : n(i), r && o ? r.merge(o).order() : o; +} +function ag(e) { + for (var t = e.selection ? e.selection() : e, n = this._groups, r = t._groups, o = n.length, i = r.length, s = Math.min(o, i), a = new Array(o), l = 0; l < s; ++l) + for (var u = n[l], c = r[l], f = u.length, d = a[l] = new Array(f), g, p = 0; p < f; ++p) + (g = u[p] || c[p]) && (d[p] = g); + for (; l < o; ++l) + a[l] = n[l]; + return new Zt(a, this._parents); +} +function lg() { + for (var e = this._groups, t = -1, n = e.length; ++t < n; ) + for (var r = e[t], o = r.length - 1, i = r[o], s; --o >= 0; ) + (s = r[o]) && (i && s.compareDocumentPosition(i) ^ 4 && i.parentNode.insertBefore(s, i), i = s); + return this; +} +function ug(e) { + e || (e = cg); + function t(f, d) { + return f && d ? e(f.__data__, d.__data__) : !f - !d; + } + for (var n = this._groups, r = n.length, o = new Array(r), i = 0; i < r; ++i) { + for (var s = n[i], a = s.length, l = o[i] = new Array(a), u, c = 0; c < a; ++c) + (u = s[c]) && (l[c] = u); + l.sort(t); + } + return new Zt(o, this._parents).order(); +} +function cg(e, t) { + return e < t ? -1 : e > t ? 1 : e >= t ? 0 : NaN; +} +function dg() { + var e = arguments[0]; + return arguments[0] = this, e.apply(null, arguments), this; +} +function fg() { + return Array.from(this); +} +function gg() { + for (var e = this._groups, t = 0, n = e.length; t < n; ++t) + for (var r = e[t], o = 0, i = r.length; o < i; ++o) { + var s = r[o]; + if (s) return s; + } + return null; +} +function hg() { + let e = 0; + for (const t of this) ++e; + return e; +} +function vg() { + return !this.node(); +} +function pg(e) { + for (var t = this._groups, n = 0, r = t.length; n < r; ++n) + for (var o = t[n], i = 0, s = o.length, a; i < s; ++i) + (a = o[i]) && e.call(a, a.__data__, i, o); + return this; +} +function mg(e) { + return function() { + this.removeAttribute(e); + }; +} +function yg(e) { + return function() { + this.removeAttributeNS(e.space, e.local); + }; +} +function wg(e, t) { + return function() { + this.setAttribute(e, t); + }; +} +function _g(e, t) { + return function() { + this.setAttributeNS(e.space, e.local, t); + }; +} +function xg(e, t) { + return function() { + var n = t.apply(this, arguments); + n == null ? this.removeAttribute(e) : this.setAttribute(e, n); + }; +} +function bg(e, t) { + return function() { + var n = t.apply(this, arguments); + n == null ? this.removeAttributeNS(e.space, e.local) : this.setAttributeNS(e.space, e.local, n); + }; +} +function Cg(e, t) { + var n = zi(e); + if (arguments.length < 2) { + var r = this.node(); + return n.local ? r.getAttributeNS(n.space, n.local) : r.getAttribute(n); + } + return this.each((t == null ? n.local ? yg : mg : typeof t == "function" ? n.local ? bg : xg : n.local ? _g : wg)(n, t)); +} +function Pu(e) { + return e.ownerDocument && e.ownerDocument.defaultView || e.document && e || e.defaultView; +} +function kg(e) { + return function() { + this.style.removeProperty(e); + }; +} +function $g(e, t, n) { + return function() { + this.style.setProperty(e, t, n); + }; +} +function Eg(e, t, n) { + return function() { + var r = t.apply(this, arguments); + r == null ? this.style.removeProperty(e) : this.style.setProperty(e, r, n); + }; +} +function Sg(e, t, n) { + return arguments.length > 1 ? this.each((t == null ? kg : typeof t == "function" ? Eg : $g)(e, t, n ?? "")) : Hr(this.node(), e); +} +function Hr(e, t) { + return e.style.getPropertyValue(t) || Pu(e).getComputedStyle(e, null).getPropertyValue(t); +} +function Pg(e) { + return function() { + delete this[e]; + }; +} +function Ng(e, t) { + return function() { + this[e] = t; + }; +} +function Mg(e, t) { + return function() { + var n = t.apply(this, arguments); + n == null ? delete this[e] : this[e] = n; + }; +} +function Tg(e, t) { + return arguments.length > 1 ? this.each((t == null ? Pg : typeof t == "function" ? Mg : Ng)(e, t)) : this.node()[e]; +} +function Nu(e) { + return e.trim().split(/^|\s+/); +} +function na(e) { + return e.classList || new Mu(e); +} +function Mu(e) { + this._node = e, this._names = Nu(e.getAttribute("class") || ""); +} +Mu.prototype = { + add: function(e) { + var t = this._names.indexOf(e); + t < 0 && (this._names.push(e), this._node.setAttribute("class", this._names.join(" "))); + }, + remove: function(e) { + var t = this._names.indexOf(e); + t >= 0 && (this._names.splice(t, 1), this._node.setAttribute("class", this._names.join(" "))); + }, + contains: function(e) { + return this._names.indexOf(e) >= 0; + } +}; +function Tu(e, t) { + for (var n = na(e), r = -1, o = t.length; ++r < o; ) n.add(t[r]); +} +function Hu(e, t) { + for (var n = na(e), r = -1, o = t.length; ++r < o; ) n.remove(t[r]); +} +function Hg(e) { + return function() { + Tu(this, e); + }; +} +function Vg(e) { + return function() { + Hu(this, e); + }; +} +function Dg(e, t) { + return function() { + (t.apply(this, arguments) ? Tu : Hu)(this, e); + }; +} +function Ag(e, t) { + var n = Nu(e + ""); + if (arguments.length < 2) { + for (var r = na(this.node()), o = -1, i = n.length; ++o < i; ) if (!r.contains(n[o])) return !1; + return !0; + } + return this.each((typeof t == "function" ? Dg : t ? Hg : Vg)(n, t)); +} +function Lg() { + this.textContent = ""; +} +function Og(e) { + return function() { + this.textContent = e; + }; +} +function Ig(e) { + return function() { + var t = e.apply(this, arguments); + this.textContent = t ?? ""; + }; +} +function zg(e) { + return arguments.length ? this.each(e == null ? Lg : (typeof e == "function" ? Ig : Og)(e)) : this.node().textContent; +} +function Rg() { + this.innerHTML = ""; +} +function Bg(e) { + return function() { + this.innerHTML = e; + }; +} +function Yg(e) { + return function() { + var t = e.apply(this, arguments); + this.innerHTML = t ?? ""; + }; +} +function Zg(e) { + return arguments.length ? this.each(e == null ? Rg : (typeof e == "function" ? Yg : Bg)(e)) : this.node().innerHTML; +} +function Xg() { + this.nextSibling && this.parentNode.appendChild(this); +} +function Fg() { + return this.each(Xg); +} +function Wg() { + this.previousSibling && this.parentNode.insertBefore(this, this.parentNode.firstChild); +} +function Kg() { + return this.each(Wg); +} +function qg(e) { + var t = typeof e == "function" ? e : Cu(e); + return this.select(function() { + return this.appendChild(t.apply(this, arguments)); + }); +} +function Gg() { + return null; +} +function Ug(e, t) { + var n = typeof e == "function" ? e : Cu(e), r = t == null ? Gg : typeof t == "function" ? t : ta(t); + return this.select(function() { + return this.insertBefore(n.apply(this, arguments), r.apply(this, arguments) || null); + }); +} +function jg() { + var e = this.parentNode; + e && e.removeChild(this); +} +function Jg() { + return this.each(jg); +} +function Qg() { + var e = this.cloneNode(!1), t = this.parentNode; + return t ? t.insertBefore(e, this.nextSibling) : e; +} +function eh() { + var e = this.cloneNode(!0), t = this.parentNode; + return t ? t.insertBefore(e, this.nextSibling) : e; +} +function th(e) { + return this.select(e ? eh : Qg); +} +function nh(e) { + return arguments.length ? this.property("__data__", e) : this.node().__data__; +} +function rh(e) { + return function(t) { + e.call(this, t, this.__data__); + }; +} +function oh(e) { + return e.trim().split(/^|\s+/).map(function(t) { + var n = "", r = t.indexOf("."); + return r >= 0 && (n = t.slice(r + 1), t = t.slice(0, r)), { type: t, name: n }; + }); +} +function ih(e) { + return function() { + var t = this.__on; + if (t) { + for (var n = 0, r = -1, o = t.length, i; n < o; ++n) + i = t[n], (!e.type || i.type === e.type) && i.name === e.name ? this.removeEventListener(i.type, i.listener, i.options) : t[++r] = i; + ++r ? t.length = r : delete this.__on; + } + }; +} +function sh(e, t, n) { + return function() { + var r = this.__on, o, i = rh(t); + if (r) { + for (var s = 0, a = r.length; s < a; ++s) + if ((o = r[s]).type === e.type && o.name === e.name) { + this.removeEventListener(o.type, o.listener, o.options), this.addEventListener(o.type, o.listener = i, o.options = n), o.value = t; + return; + } + } + this.addEventListener(e.type, i, n), o = { type: e.type, name: e.name, value: t, listener: i, options: n }, r ? r.push(o) : this.__on = [o]; + }; +} +function ah(e, t, n) { + var r = oh(e + ""), o, i = r.length, s; + if (arguments.length < 2) { + var a = this.node().__on; + if (a) { + for (var l = 0, u = a.length, c; l < u; ++l) + for (o = 0, c = a[l]; o < i; ++o) + if ((s = r[o]).type === c.type && s.name === c.name) + return c.value; + } + return; + } + for (a = t ? sh : ih, o = 0; o < i; ++o) this.each(a(r[o], t, n)); + return this; +} +function Vu(e, t, n) { + var r = Pu(e), o = r.CustomEvent; + typeof o == "function" ? o = new o(t, n) : (o = r.document.createEvent("Event"), n ? (o.initEvent(t, n.bubbles, n.cancelable), o.detail = n.detail) : o.initEvent(t, !1, !1)), e.dispatchEvent(o); +} +function lh(e, t) { + return function() { + return Vu(this, e, t); + }; +} +function uh(e, t) { + return function() { + return Vu(this, e, t.apply(this, arguments)); + }; +} +function ch(e, t) { + return this.each((typeof t == "function" ? uh : lh)(e, t)); +} +function* dh() { + for (var e = this._groups, t = 0, n = e.length; t < n; ++t) + for (var r = e[t], o = 0, i = r.length, s; o < i; ++o) + (s = r[o]) && (yield s); +} +var Du = [null]; +function Zt(e, t) { + this._groups = e, this._parents = t; +} +function Eo() { + return new Zt([[document.documentElement]], Du); +} +function fh() { + return this; +} +Zt.prototype = Eo.prototype = { + constructor: Zt, + select: I1, + selectAll: Y1, + selectChild: W1, + selectChildren: U1, + filter: j1, + data: rg, + enter: J1, + exit: ig, + join: sg, + merge: ag, + selection: fh, + order: lg, + sort: ug, + call: dg, + nodes: fg, + node: gg, + size: hg, + empty: vg, + each: pg, + attr: Cg, + style: Sg, + property: Tg, + classed: Ag, + text: zg, + html: Zg, + raise: Fg, + lower: Kg, + append: qg, + insert: Ug, + remove: Jg, + clone: th, + datum: nh, + on: ah, + dispatch: ch, + [Symbol.iterator]: dh +}; +function Kt(e) { + return typeof e == "string" ? new Zt([[document.querySelector(e)]], [document.documentElement]) : new Zt([[e]], Du); +} +function gh(e) { + let t; + for (; t = e.sourceEvent; ) e = t; + return e; +} +function Qt(e, t) { + if (e = gh(e), t === void 0 && (t = e.currentTarget), t) { + var n = t.ownerSVGElement || t; + if (n.createSVGPoint) { + var r = n.createSVGPoint(); + return r.x = e.clientX, r.y = e.clientY, r = r.matrixTransform(t.getScreenCTM().inverse()), [r.x, r.y]; + } + if (t.getBoundingClientRect) { + var o = t.getBoundingClientRect(); + return [e.clientX - o.left - t.clientLeft, e.clientY - o.top - t.clientTop]; + } + } + return [e.pageX, e.pageY]; +} +const hh = { passive: !1 }, fo = { capture: !0, passive: !1 }; +function ts(e) { + e.stopImmediatePropagation(); +} +function xr(e) { + e.preventDefault(), e.stopImmediatePropagation(); +} +function Au(e) { + var t = e.document.documentElement, n = Kt(e).on("dragstart.drag", xr, fo); + "onselectstart" in t ? n.on("selectstart.drag", xr, fo) : (t.__noselect = t.style.MozUserSelect, t.style.MozUserSelect = "none"); +} +function Lu(e, t) { + var n = e.document.documentElement, r = Kt(e).on("dragstart.drag", null); + t && (r.on("click.drag", xr, fo), setTimeout(function() { + r.on("click.drag", null); + }, 0)), "onselectstart" in n ? r.on("selectstart.drag", null) : (n.style.MozUserSelect = n.__noselect, delete n.__noselect); +} +const Yo = (e) => () => e; +function bs(e, { + sourceEvent: t, + subject: n, + target: r, + identifier: o, + active: i, + x: s, + y: a, + dx: l, + dy: u, + dispatch: c +}) { + Object.defineProperties(this, { + type: { value: e, enumerable: !0, configurable: !0 }, + sourceEvent: { value: t, enumerable: !0, configurable: !0 }, + subject: { value: n, enumerable: !0, configurable: !0 }, + target: { value: r, enumerable: !0, configurable: !0 }, + identifier: { value: o, enumerable: !0, configurable: !0 }, + active: { value: i, enumerable: !0, configurable: !0 }, + x: { value: s, enumerable: !0, configurable: !0 }, + y: { value: a, enumerable: !0, configurable: !0 }, + dx: { value: l, enumerable: !0, configurable: !0 }, + dy: { value: u, enumerable: !0, configurable: !0 }, + _: { value: c } + }); +} +bs.prototype.on = function() { + var e = this._.on.apply(this._, arguments); + return e === this._ ? this : e; +}; +function vh(e) { + return !e.ctrlKey && !e.button; +} +function ph() { + return this.parentNode; +} +function mh(e, t) { + return t ?? { x: e.x, y: e.y }; +} +function yh() { + return navigator.maxTouchPoints || "ontouchstart" in this; +} +function wh() { + var e = vh, t = ph, n = mh, r = yh, o = {}, i = Ii("start", "drag", "end"), s = 0, a, l, u, c, f = 0; + function d(v) { + v.on("mousedown.drag", g).filter(r).on("touchstart.drag", C).on("touchmove.drag", $, hh).on("touchend.drag touchcancel.drag", m).style("touch-action", "none").style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + function g(v, b) { + if (!(c || !e.call(this, v, b))) { + var N = _(this, t.call(this, v, b), v, b, "mouse"); + N && (Kt(v.view).on("mousemove.drag", p, fo).on("mouseup.drag", x, fo), Au(v.view), ts(v), u = !1, a = v.clientX, l = v.clientY, N("start", v)); + } + } + function p(v) { + if (xr(v), !u) { + var b = v.clientX - a, N = v.clientY - l; + u = b * b + N * N > f; + } + o.mouse("drag", v); + } + function x(v) { + Kt(v.view).on("mousemove.drag mouseup.drag", null), Lu(v.view, u), xr(v), o.mouse("end", v); + } + function C(v, b) { + if (e.call(this, v, b)) { + var N = v.changedTouches, E = t.call(this, v, b), M = N.length, D, V; + for (D = 0; D < M; ++D) + (V = _(this, E, v, b, N[D].identifier, N[D])) && (ts(v), V("start", v, N[D])); + } + } + function $(v) { + var b = v.changedTouches, N = b.length, E, M; + for (E = 0; E < N; ++E) + (M = o[b[E].identifier]) && (xr(v), M("drag", v, b[E])); + } + function m(v) { + var b = v.changedTouches, N = b.length, E, M; + for (c && clearTimeout(c), c = setTimeout(function() { + c = null; + }, 500), E = 0; E < N; ++E) + (M = o[b[E].identifier]) && (ts(v), M("end", v, b[E])); + } + function _(v, b, N, E, M, D) { + var V = i.copy(), A = Qt(D || N, b), O, R, S; + if ((S = n.call(v, new bs("beforestart", { + sourceEvent: N, + target: d, + identifier: M, + active: s, + x: A[0], + y: A[1], + dx: 0, + dy: 0, + dispatch: V + }), E)) != null) + return O = S.x - A[0] || 0, R = S.y - A[1] || 0, function T(k, P, H) { + var I = A, B; + switch (k) { + case "start": + o[M] = T, B = s++; + break; + case "end": + delete o[M], --s; + // falls through + case "drag": + A = Qt(H || P, b), B = s; + break; + } + V.call( + k, + v, + new bs(k, { + sourceEvent: P, + subject: S, + target: d, + identifier: M, + active: B, + x: A[0] + O, + y: A[1] + R, + dx: A[0] - I[0], + dy: A[1] - I[1], + dispatch: V + }), + E + ); + }; + } + return d.filter = function(v) { + return arguments.length ? (e = typeof v == "function" ? v : Yo(!!v), d) : e; + }, d.container = function(v) { + return arguments.length ? (t = typeof v == "function" ? v : Yo(v), d) : t; + }, d.subject = function(v) { + return arguments.length ? (n = typeof v == "function" ? v : Yo(v), d) : n; + }, d.touchable = function(v) { + return arguments.length ? (r = typeof v == "function" ? v : Yo(!!v), d) : r; + }, d.on = function() { + var v = i.on.apply(i, arguments); + return v === i ? d : v; + }, d.clickDistance = function(v) { + return arguments.length ? (f = (v = +v) * v, d) : Math.sqrt(f); + }, d; +} +function ra(e, t, n) { + e.prototype = t.prototype = n, n.constructor = e; +} +function Ou(e, t) { + var n = Object.create(e.prototype); + for (var r in t) n[r] = t[r]; + return n; +} +function So() { +} +var go = 0.7, ci = 1 / go, br = "\\s*([+-]?\\d+)\\s*", ho = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*", pn = "\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*", _h = /^#([0-9a-f]{3,8})$/, xh = new RegExp(`^rgb\\(${br},${br},${br}\\)$`), bh = new RegExp(`^rgb\\(${pn},${pn},${pn}\\)$`), Ch = new RegExp(`^rgba\\(${br},${br},${br},${ho}\\)$`), kh = new RegExp(`^rgba\\(${pn},${pn},${pn},${ho}\\)$`), $h = new RegExp(`^hsl\\(${ho},${pn},${pn}\\)$`), Eh = new RegExp(`^hsla\\(${ho},${pn},${pn},${ho}\\)$`), Fa = { + aliceblue: 15792383, + antiquewhite: 16444375, + aqua: 65535, + aquamarine: 8388564, + azure: 15794175, + beige: 16119260, + bisque: 16770244, + black: 0, + blanchedalmond: 16772045, + blue: 255, + blueviolet: 9055202, + brown: 10824234, + burlywood: 14596231, + cadetblue: 6266528, + chartreuse: 8388352, + chocolate: 13789470, + coral: 16744272, + cornflowerblue: 6591981, + cornsilk: 16775388, + crimson: 14423100, + cyan: 65535, + darkblue: 139, + darkcyan: 35723, + darkgoldenrod: 12092939, + darkgray: 11119017, + darkgreen: 25600, + darkgrey: 11119017, + darkkhaki: 12433259, + darkmagenta: 9109643, + darkolivegreen: 5597999, + darkorange: 16747520, + darkorchid: 10040012, + darkred: 9109504, + darksalmon: 15308410, + darkseagreen: 9419919, + darkslateblue: 4734347, + darkslategray: 3100495, + darkslategrey: 3100495, + darkturquoise: 52945, + darkviolet: 9699539, + deeppink: 16716947, + deepskyblue: 49151, + dimgray: 6908265, + dimgrey: 6908265, + dodgerblue: 2003199, + firebrick: 11674146, + floralwhite: 16775920, + forestgreen: 2263842, + fuchsia: 16711935, + gainsboro: 14474460, + ghostwhite: 16316671, + gold: 16766720, + goldenrod: 14329120, + gray: 8421504, + green: 32768, + greenyellow: 11403055, + grey: 8421504, + honeydew: 15794160, + hotpink: 16738740, + indianred: 13458524, + indigo: 4915330, + ivory: 16777200, + khaki: 15787660, + lavender: 15132410, + lavenderblush: 16773365, + lawngreen: 8190976, + lemonchiffon: 16775885, + lightblue: 11393254, + lightcoral: 15761536, + lightcyan: 14745599, + lightgoldenrodyellow: 16448210, + lightgray: 13882323, + lightgreen: 9498256, + lightgrey: 13882323, + lightpink: 16758465, + lightsalmon: 16752762, + lightseagreen: 2142890, + lightskyblue: 8900346, + lightslategray: 7833753, + lightslategrey: 7833753, + lightsteelblue: 11584734, + lightyellow: 16777184, + lime: 65280, + limegreen: 3329330, + linen: 16445670, + magenta: 16711935, + maroon: 8388608, + mediumaquamarine: 6737322, + mediumblue: 205, + mediumorchid: 12211667, + mediumpurple: 9662683, + mediumseagreen: 3978097, + mediumslateblue: 8087790, + mediumspringgreen: 64154, + mediumturquoise: 4772300, + mediumvioletred: 13047173, + midnightblue: 1644912, + mintcream: 16121850, + mistyrose: 16770273, + moccasin: 16770229, + navajowhite: 16768685, + navy: 128, + oldlace: 16643558, + olive: 8421376, + olivedrab: 7048739, + orange: 16753920, + orangered: 16729344, + orchid: 14315734, + palegoldenrod: 15657130, + palegreen: 10025880, + paleturquoise: 11529966, + palevioletred: 14381203, + papayawhip: 16773077, + peachpuff: 16767673, + peru: 13468991, + pink: 16761035, + plum: 14524637, + powderblue: 11591910, + purple: 8388736, + rebeccapurple: 6697881, + red: 16711680, + rosybrown: 12357519, + royalblue: 4286945, + saddlebrown: 9127187, + salmon: 16416882, + sandybrown: 16032864, + seagreen: 3050327, + seashell: 16774638, + sienna: 10506797, + silver: 12632256, + skyblue: 8900331, + slateblue: 6970061, + slategray: 7372944, + slategrey: 7372944, + snow: 16775930, + springgreen: 65407, + steelblue: 4620980, + tan: 13808780, + teal: 32896, + thistle: 14204888, + tomato: 16737095, + turquoise: 4251856, + violet: 15631086, + wheat: 16113331, + white: 16777215, + whitesmoke: 16119285, + yellow: 16776960, + yellowgreen: 10145074 +}; +ra(So, vo, { + copy(e) { + return Object.assign(new this.constructor(), this, e); + }, + displayable() { + return this.rgb().displayable(); + }, + hex: Wa, + // Deprecated! Use color.formatHex. + formatHex: Wa, + formatHex8: Sh, + formatHsl: Ph, + formatRgb: Ka, + toString: Ka +}); +function Wa() { + return this.rgb().formatHex(); +} +function Sh() { + return this.rgb().formatHex8(); +} +function Ph() { + return Iu(this).formatHsl(); +} +function Ka() { + return this.rgb().formatRgb(); +} +function vo(e) { + var t, n; + return e = (e + "").trim().toLowerCase(), (t = _h.exec(e)) ? (n = t[1].length, t = parseInt(t[1], 16), n === 6 ? qa(t) : n === 3 ? new Ht(t >> 8 & 15 | t >> 4 & 240, t >> 4 & 15 | t & 240, (t & 15) << 4 | t & 15, 1) : n === 8 ? Zo(t >> 24 & 255, t >> 16 & 255, t >> 8 & 255, (t & 255) / 255) : n === 4 ? Zo(t >> 12 & 15 | t >> 8 & 240, t >> 8 & 15 | t >> 4 & 240, t >> 4 & 15 | t & 240, ((t & 15) << 4 | t & 15) / 255) : null) : (t = xh.exec(e)) ? new Ht(t[1], t[2], t[3], 1) : (t = bh.exec(e)) ? new Ht(t[1] * 255 / 100, t[2] * 255 / 100, t[3] * 255 / 100, 1) : (t = Ch.exec(e)) ? Zo(t[1], t[2], t[3], t[4]) : (t = kh.exec(e)) ? Zo(t[1] * 255 / 100, t[2] * 255 / 100, t[3] * 255 / 100, t[4]) : (t = $h.exec(e)) ? ja(t[1], t[2] / 100, t[3] / 100, 1) : (t = Eh.exec(e)) ? ja(t[1], t[2] / 100, t[3] / 100, t[4]) : Fa.hasOwnProperty(e) ? qa(Fa[e]) : e === "transparent" ? new Ht(NaN, NaN, NaN, 0) : null; +} +function qa(e) { + return new Ht(e >> 16 & 255, e >> 8 & 255, e & 255, 1); +} +function Zo(e, t, n, r) { + return r <= 0 && (e = t = n = NaN), new Ht(e, t, n, r); +} +function Nh(e) { + return e instanceof So || (e = vo(e)), e ? (e = e.rgb(), new Ht(e.r, e.g, e.b, e.opacity)) : new Ht(); +} +function Cs(e, t, n, r) { + return arguments.length === 1 ? Nh(e) : new Ht(e, t, n, r ?? 1); +} +function Ht(e, t, n, r) { + this.r = +e, this.g = +t, this.b = +n, this.opacity = +r; +} +ra(Ht, Cs, Ou(So, { + brighter(e) { + return e = e == null ? ci : Math.pow(ci, e), new Ht(this.r * e, this.g * e, this.b * e, this.opacity); + }, + darker(e) { + return e = e == null ? go : Math.pow(go, e), new Ht(this.r * e, this.g * e, this.b * e, this.opacity); + }, + rgb() { + return this; + }, + clamp() { + return new Ht(sr(this.r), sr(this.g), sr(this.b), di(this.opacity)); + }, + displayable() { + return -0.5 <= this.r && this.r < 255.5 && -0.5 <= this.g && this.g < 255.5 && -0.5 <= this.b && this.b < 255.5 && 0 <= this.opacity && this.opacity <= 1; + }, + hex: Ga, + // Deprecated! Use color.formatHex. + formatHex: Ga, + formatHex8: Mh, + formatRgb: Ua, + toString: Ua +})); +function Ga() { + return `#${or(this.r)}${or(this.g)}${or(this.b)}`; +} +function Mh() { + return `#${or(this.r)}${or(this.g)}${or(this.b)}${or((isNaN(this.opacity) ? 1 : this.opacity) * 255)}`; +} +function Ua() { + const e = di(this.opacity); + return `${e === 1 ? "rgb(" : "rgba("}${sr(this.r)}, ${sr(this.g)}, ${sr(this.b)}${e === 1 ? ")" : `, ${e})`}`; +} +function di(e) { + return isNaN(e) ? 1 : Math.max(0, Math.min(1, e)); +} +function sr(e) { + return Math.max(0, Math.min(255, Math.round(e) || 0)); +} +function or(e) { + return e = sr(e), (e < 16 ? "0" : "") + e.toString(16); +} +function ja(e, t, n, r) { + return r <= 0 ? e = t = n = NaN : n <= 0 || n >= 1 ? e = t = NaN : t <= 0 && (e = NaN), new tn(e, t, n, r); +} +function Iu(e) { + if (e instanceof tn) return new tn(e.h, e.s, e.l, e.opacity); + if (e instanceof So || (e = vo(e)), !e) return new tn(); + if (e instanceof tn) return e; + e = e.rgb(); + var t = e.r / 255, n = e.g / 255, r = e.b / 255, o = Math.min(t, n, r), i = Math.max(t, n, r), s = NaN, a = i - o, l = (i + o) / 2; + return a ? (t === i ? s = (n - r) / a + (n < r) * 6 : n === i ? s = (r - t) / a + 2 : s = (t - n) / a + 4, a /= l < 0.5 ? i + o : 2 - i - o, s *= 60) : a = l > 0 && l < 1 ? 0 : s, new tn(s, a, l, e.opacity); +} +function Th(e, t, n, r) { + return arguments.length === 1 ? Iu(e) : new tn(e, t, n, r ?? 1); +} +function tn(e, t, n, r) { + this.h = +e, this.s = +t, this.l = +n, this.opacity = +r; +} +ra(tn, Th, Ou(So, { + brighter(e) { + return e = e == null ? ci : Math.pow(ci, e), new tn(this.h, this.s, this.l * e, this.opacity); + }, + darker(e) { + return e = e == null ? go : Math.pow(go, e), new tn(this.h, this.s, this.l * e, this.opacity); + }, + rgb() { + var e = this.h % 360 + (this.h < 0) * 360, t = isNaN(e) || isNaN(this.s) ? 0 : this.s, n = this.l, r = n + (n < 0.5 ? n : 1 - n) * t, o = 2 * n - r; + return new Ht( + ns(e >= 240 ? e - 240 : e + 120, o, r), + ns(e, o, r), + ns(e < 120 ? e + 240 : e - 120, o, r), + this.opacity + ); + }, + clamp() { + return new tn(Ja(this.h), Xo(this.s), Xo(this.l), di(this.opacity)); + }, + displayable() { + return (0 <= this.s && this.s <= 1 || isNaN(this.s)) && 0 <= this.l && this.l <= 1 && 0 <= this.opacity && this.opacity <= 1; + }, + formatHsl() { + const e = di(this.opacity); + return `${e === 1 ? "hsl(" : "hsla("}${Ja(this.h)}, ${Xo(this.s) * 100}%, ${Xo(this.l) * 100}%${e === 1 ? ")" : `, ${e})`}`; + } +})); +function Ja(e) { + return e = (e || 0) % 360, e < 0 ? e + 360 : e; +} +function Xo(e) { + return Math.max(0, Math.min(1, e || 0)); +} +function ns(e, t, n) { + return (e < 60 ? t + (n - t) * e / 60 : e < 180 ? n : e < 240 ? t + (n - t) * (240 - e) / 60 : t) * 255; +} +const zu = (e) => () => e; +function Hh(e, t) { + return function(n) { + return e + n * t; + }; +} +function Vh(e, t, n) { + return e = Math.pow(e, n), t = Math.pow(t, n) - e, n = 1 / n, function(r) { + return Math.pow(e + r * t, n); + }; +} +function Dh(e) { + return (e = +e) == 1 ? Ru : function(t, n) { + return n - t ? Vh(t, n, e) : zu(isNaN(t) ? n : t); + }; +} +function Ru(e, t) { + var n = t - e; + return n ? Hh(e, n) : zu(isNaN(e) ? t : e); +} +const Qa = function e(t) { + var n = Dh(t); + function r(o, i) { + var s = n((o = Cs(o)).r, (i = Cs(i)).r), a = n(o.g, i.g), l = n(o.b, i.b), u = Ru(o.opacity, i.opacity); + return function(c) { + return o.r = s(c), o.g = a(c), o.b = l(c), o.opacity = u(c), o + ""; + }; + } + return r.gamma = e, r; +}(1); +function Yn(e, t) { + return e = +e, t = +t, function(n) { + return e * (1 - n) + t * n; + }; +} +var ks = /[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g, rs = new RegExp(ks.source, "g"); +function Ah(e) { + return function() { + return e; + }; +} +function Lh(e) { + return function(t) { + return e(t) + ""; + }; +} +function Oh(e, t) { + var n = ks.lastIndex = rs.lastIndex = 0, r, o, i, s = -1, a = [], l = []; + for (e = e + "", t = t + ""; (r = ks.exec(e)) && (o = rs.exec(t)); ) + (i = o.index) > n && (i = t.slice(n, i), a[s] ? a[s] += i : a[++s] = i), (r = r[0]) === (o = o[0]) ? a[s] ? a[s] += o : a[++s] = o : (a[++s] = null, l.push({ i: s, x: Yn(r, o) })), n = rs.lastIndex; + return n < t.length && (i = t.slice(n), a[s] ? a[s] += i : a[++s] = i), a.length < 2 ? l[0] ? Lh(l[0].x) : Ah(t) : (t = l.length, function(u) { + for (var c = 0, f; c < t; ++c) a[(f = l[c]).i] = f.x(u); + return a.join(""); + }); +} +var el = 180 / Math.PI, $s = { + translateX: 0, + translateY: 0, + rotate: 0, + skewX: 0, + scaleX: 1, + scaleY: 1 +}; +function Bu(e, t, n, r, o, i) { + var s, a, l; + return (s = Math.sqrt(e * e + t * t)) && (e /= s, t /= s), (l = e * n + t * r) && (n -= e * l, r -= t * l), (a = Math.sqrt(n * n + r * r)) && (n /= a, r /= a, l /= a), e * r < t * n && (e = -e, t = -t, l = -l, s = -s), { + translateX: o, + translateY: i, + rotate: Math.atan2(t, e) * el, + skewX: Math.atan(l) * el, + scaleX: s, + scaleY: a + }; +} +var Fo; +function Ih(e) { + const t = new (typeof DOMMatrix == "function" ? DOMMatrix : WebKitCSSMatrix)(e + ""); + return t.isIdentity ? $s : Bu(t.a, t.b, t.c, t.d, t.e, t.f); +} +function zh(e) { + return e == null || (Fo || (Fo = document.createElementNS("http://www.w3.org/2000/svg", "g")), Fo.setAttribute("transform", e), !(e = Fo.transform.baseVal.consolidate())) ? $s : (e = e.matrix, Bu(e.a, e.b, e.c, e.d, e.e, e.f)); +} +function Yu(e, t, n, r) { + function o(u) { + return u.length ? u.pop() + " " : ""; + } + function i(u, c, f, d, g, p) { + if (u !== f || c !== d) { + var x = g.push("translate(", null, t, null, n); + p.push({ i: x - 4, x: Yn(u, f) }, { i: x - 2, x: Yn(c, d) }); + } else (f || d) && g.push("translate(" + f + t + d + n); + } + function s(u, c, f, d) { + u !== c ? (u - c > 180 ? c += 360 : c - u > 180 && (u += 360), d.push({ i: f.push(o(f) + "rotate(", null, r) - 2, x: Yn(u, c) })) : c && f.push(o(f) + "rotate(" + c + r); + } + function a(u, c, f, d) { + u !== c ? d.push({ i: f.push(o(f) + "skewX(", null, r) - 2, x: Yn(u, c) }) : c && f.push(o(f) + "skewX(" + c + r); + } + function l(u, c, f, d, g, p) { + if (u !== f || c !== d) { + var x = g.push(o(g) + "scale(", null, ",", null, ")"); + p.push({ i: x - 4, x: Yn(u, f) }, { i: x - 2, x: Yn(c, d) }); + } else (f !== 1 || d !== 1) && g.push(o(g) + "scale(" + f + "," + d + ")"); + } + return function(u, c) { + var f = [], d = []; + return u = e(u), c = e(c), i(u.translateX, u.translateY, c.translateX, c.translateY, f, d), s(u.rotate, c.rotate, f, d), a(u.skewX, c.skewX, f, d), l(u.scaleX, u.scaleY, c.scaleX, c.scaleY, f, d), u = c = null, function(g) { + for (var p = -1, x = d.length, C; ++p < x; ) f[(C = d[p]).i] = C.x(g); + return f.join(""); + }; + }; +} +var Rh = Yu(Ih, "px, ", "px)", "deg)"), Bh = Yu(zh, ", ", ")", ")"), Yh = 1e-12; +function tl(e) { + return ((e = Math.exp(e)) + 1 / e) / 2; +} +function Zh(e) { + return ((e = Math.exp(e)) - 1 / e) / 2; +} +function Xh(e) { + return ((e = Math.exp(2 * e)) - 1) / (e + 1); +} +const Fh = function e(t, n, r) { + function o(i, s) { + var a = i[0], l = i[1], u = i[2], c = s[0], f = s[1], d = s[2], g = c - a, p = f - l, x = g * g + p * p, C, $; + if (x < Yh) + $ = Math.log(d / u) / t, C = function(E) { + return [ + a + E * g, + l + E * p, + u * Math.exp(t * E * $) + ]; + }; + else { + var m = Math.sqrt(x), _ = (d * d - u * u + r * x) / (2 * u * n * m), v = (d * d - u * u - r * x) / (2 * d * n * m), b = Math.log(Math.sqrt(_ * _ + 1) - _), N = Math.log(Math.sqrt(v * v + 1) - v); + $ = (N - b) / t, C = function(E) { + var M = E * $, D = tl(b), V = u / (n * m) * (D * Xh(t * M + b) - Zh(b)); + return [ + a + V * g, + l + V * p, + u * D / tl(t * M + b) + ]; + }; + } + return C.duration = $ * 1e3 * t / Math.SQRT2, C; + } + return o.rho = function(i) { + var s = Math.max(1e-3, +i), a = s * s, l = a * a; + return e(s, a, l); + }, o; +}(Math.SQRT2, 2, 4); +var Vr = 0, to = 0, Jr = 0, Zu = 1e3, fi, no, gi = 0, ur = 0, Ri = 0, po = typeof performance == "object" && performance.now ? performance : Date, Xu = typeof window == "object" && window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : function(e) { + setTimeout(e, 17); +}; +function oa() { + return ur || (Xu(Wh), ur = po.now() + Ri); +} +function Wh() { + ur = 0; +} +function hi() { + this._call = this._time = this._next = null; +} +hi.prototype = Fu.prototype = { + constructor: hi, + restart: function(e, t, n) { + if (typeof e != "function") throw new TypeError("callback is not a function"); + n = (n == null ? oa() : +n) + (t == null ? 0 : +t), !this._next && no !== this && (no ? no._next = this : fi = this, no = this), this._call = e, this._time = n, Es(); + }, + stop: function() { + this._call && (this._call = null, this._time = 1 / 0, Es()); + } +}; +function Fu(e, t, n) { + var r = new hi(); + return r.restart(e, t, n), r; +} +function Kh() { + oa(), ++Vr; + for (var e = fi, t; e; ) + (t = ur - e._time) >= 0 && e._call.call(void 0, t), e = e._next; + --Vr; +} +function nl() { + ur = (gi = po.now()) + Ri, Vr = to = 0; + try { + Kh(); + } finally { + Vr = 0, Gh(), ur = 0; + } +} +function qh() { + var e = po.now(), t = e - gi; + t > Zu && (Ri -= t, gi = e); +} +function Gh() { + for (var e, t = fi, n, r = 1 / 0; t; ) + t._call ? (r > t._time && (r = t._time), e = t, t = t._next) : (n = t._next, t._next = null, t = e ? e._next = n : fi = n); + no = e, Es(r); +} +function Es(e) { + if (!Vr) { + to && (to = clearTimeout(to)); + var t = e - ur; + t > 24 ? (e < 1 / 0 && (to = setTimeout(nl, e - po.now() - Ri)), Jr && (Jr = clearInterval(Jr))) : (Jr || (gi = po.now(), Jr = setInterval(qh, Zu)), Vr = 1, Xu(nl)); + } +} +function rl(e, t, n) { + var r = new hi(); + return t = t == null ? 0 : +t, r.restart((o) => { + r.stop(), e(o + t); + }, t, n), r; +} +var Uh = Ii("start", "end", "cancel", "interrupt"), jh = [], Wu = 0, ol = 1, Ss = 2, jo = 3, il = 4, Ps = 5, Jo = 6; +function Bi(e, t, n, r, o, i) { + var s = e.__transition; + if (!s) e.__transition = {}; + else if (n in s) return; + Jh(e, n, { + name: t, + index: r, + // For context during callback. + group: o, + // For context during callback. + on: Uh, + tween: jh, + time: i.time, + delay: i.delay, + duration: i.duration, + ease: i.ease, + timer: null, + state: Wu + }); +} +function ia(e, t) { + var n = cn(e, t); + if (n.state > Wu) throw new Error("too late; already scheduled"); + return n; +} +function Cn(e, t) { + var n = cn(e, t); + if (n.state > jo) throw new Error("too late; already running"); + return n; +} +function cn(e, t) { + var n = e.__transition; + if (!n || !(n = n[t])) throw new Error("transition not found"); + return n; +} +function Jh(e, t, n) { + var r = e.__transition, o; + r[t] = n, n.timer = Fu(i, 0, n.time); + function i(u) { + n.state = ol, n.timer.restart(s, n.delay, n.time), n.delay <= u && s(u - n.delay); + } + function s(u) { + var c, f, d, g; + if (n.state !== ol) return l(); + for (c in r) + if (g = r[c], g.name === n.name) { + if (g.state === jo) return rl(s); + g.state === il ? (g.state = Jo, g.timer.stop(), g.on.call("interrupt", e, e.__data__, g.index, g.group), delete r[c]) : +c < t && (g.state = Jo, g.timer.stop(), g.on.call("cancel", e, e.__data__, g.index, g.group), delete r[c]); + } + if (rl(function() { + n.state === jo && (n.state = il, n.timer.restart(a, n.delay, n.time), a(u)); + }), n.state = Ss, n.on.call("start", e, e.__data__, n.index, n.group), n.state === Ss) { + for (n.state = jo, o = new Array(d = n.tween.length), c = 0, f = -1; c < d; ++c) + (g = n.tween[c].value.call(e, e.__data__, n.index, n.group)) && (o[++f] = g); + o.length = f + 1; + } + } + function a(u) { + for (var c = u < n.duration ? n.ease.call(null, u / n.duration) : (n.timer.restart(l), n.state = Ps, 1), f = -1, d = o.length; ++f < d; ) + o[f].call(e, c); + n.state === Ps && (n.on.call("end", e, e.__data__, n.index, n.group), l()); + } + function l() { + n.state = Jo, n.timer.stop(), delete r[t]; + for (var u in r) return; + delete e.__transition; + } +} +function Qo(e, t) { + var n = e.__transition, r, o, i = !0, s; + if (n) { + t = t == null ? null : t + ""; + for (s in n) { + if ((r = n[s]).name !== t) { + i = !1; + continue; + } + o = r.state > Ss && r.state < Ps, r.state = Jo, r.timer.stop(), r.on.call(o ? "interrupt" : "cancel", e, e.__data__, r.index, r.group), delete n[s]; + } + i && delete e.__transition; + } +} +function Qh(e) { + return this.each(function() { + Qo(this, e); + }); +} +function ev(e, t) { + var n, r; + return function() { + var o = Cn(this, e), i = o.tween; + if (i !== n) { + r = n = i; + for (var s = 0, a = r.length; s < a; ++s) + if (r[s].name === t) { + r = r.slice(), r.splice(s, 1); + break; + } + } + o.tween = r; + }; +} +function tv(e, t, n) { + var r, o; + if (typeof n != "function") throw new Error(); + return function() { + var i = Cn(this, e), s = i.tween; + if (s !== r) { + o = (r = s).slice(); + for (var a = { name: t, value: n }, l = 0, u = o.length; l < u; ++l) + if (o[l].name === t) { + o[l] = a; + break; + } + l === u && o.push(a); + } + i.tween = o; + }; +} +function nv(e, t) { + var n = this._id; + if (e += "", arguments.length < 2) { + for (var r = cn(this.node(), n).tween, o = 0, i = r.length, s; o < i; ++o) + if ((s = r[o]).name === e) + return s.value; + return null; + } + return this.each((t == null ? ev : tv)(n, e, t)); +} +function sa(e, t, n) { + var r = e._id; + return e.each(function() { + var o = Cn(this, r); + (o.value || (o.value = {}))[t] = n.apply(this, arguments); + }), function(o) { + return cn(o, r).value[t]; + }; +} +function Ku(e, t) { + var n; + return (typeof t == "number" ? Yn : t instanceof vo ? Qa : (n = vo(t)) ? (t = n, Qa) : Oh)(e, t); +} +function rv(e) { + return function() { + this.removeAttribute(e); + }; +} +function ov(e) { + return function() { + this.removeAttributeNS(e.space, e.local); + }; +} +function iv(e, t, n) { + var r, o = n + "", i; + return function() { + var s = this.getAttribute(e); + return s === o ? null : s === r ? i : i = t(r = s, n); + }; +} +function sv(e, t, n) { + var r, o = n + "", i; + return function() { + var s = this.getAttributeNS(e.space, e.local); + return s === o ? null : s === r ? i : i = t(r = s, n); + }; +} +function av(e, t, n) { + var r, o, i; + return function() { + var s, a = n(this), l; + return a == null ? void this.removeAttribute(e) : (s = this.getAttribute(e), l = a + "", s === l ? null : s === r && l === o ? i : (o = l, i = t(r = s, a))); + }; +} +function lv(e, t, n) { + var r, o, i; + return function() { + var s, a = n(this), l; + return a == null ? void this.removeAttributeNS(e.space, e.local) : (s = this.getAttributeNS(e.space, e.local), l = a + "", s === l ? null : s === r && l === o ? i : (o = l, i = t(r = s, a))); + }; +} +function uv(e, t) { + var n = zi(e), r = n === "transform" ? Bh : Ku; + return this.attrTween(e, typeof t == "function" ? (n.local ? lv : av)(n, r, sa(this, "attr." + e, t)) : t == null ? (n.local ? ov : rv)(n) : (n.local ? sv : iv)(n, r, t)); +} +function cv(e, t) { + return function(n) { + this.setAttribute(e, t.call(this, n)); + }; +} +function dv(e, t) { + return function(n) { + this.setAttributeNS(e.space, e.local, t.call(this, n)); + }; +} +function fv(e, t) { + var n, r; + function o() { + var i = t.apply(this, arguments); + return i !== r && (n = (r = i) && dv(e, i)), n; + } + return o._value = t, o; +} +function gv(e, t) { + var n, r; + function o() { + var i = t.apply(this, arguments); + return i !== r && (n = (r = i) && cv(e, i)), n; + } + return o._value = t, o; +} +function hv(e, t) { + var n = "attr." + e; + if (arguments.length < 2) return (n = this.tween(n)) && n._value; + if (t == null) return this.tween(n, null); + if (typeof t != "function") throw new Error(); + var r = zi(e); + return this.tween(n, (r.local ? fv : gv)(r, t)); +} +function vv(e, t) { + return function() { + ia(this, e).delay = +t.apply(this, arguments); + }; +} +function pv(e, t) { + return t = +t, function() { + ia(this, e).delay = t; + }; +} +function mv(e) { + var t = this._id; + return arguments.length ? this.each((typeof e == "function" ? vv : pv)(t, e)) : cn(this.node(), t).delay; +} +function yv(e, t) { + return function() { + Cn(this, e).duration = +t.apply(this, arguments); + }; +} +function wv(e, t) { + return t = +t, function() { + Cn(this, e).duration = t; + }; +} +function _v(e) { + var t = this._id; + return arguments.length ? this.each((typeof e == "function" ? yv : wv)(t, e)) : cn(this.node(), t).duration; +} +function xv(e, t) { + if (typeof t != "function") throw new Error(); + return function() { + Cn(this, e).ease = t; + }; +} +function bv(e) { + var t = this._id; + return arguments.length ? this.each(xv(t, e)) : cn(this.node(), t).ease; +} +function Cv(e, t) { + return function() { + var n = t.apply(this, arguments); + if (typeof n != "function") throw new Error(); + Cn(this, e).ease = n; + }; +} +function kv(e) { + if (typeof e != "function") throw new Error(); + return this.each(Cv(this._id, e)); +} +function $v(e) { + typeof e != "function" && (e = $u(e)); + for (var t = this._groups, n = t.length, r = new Array(n), o = 0; o < n; ++o) + for (var i = t[o], s = i.length, a = r[o] = [], l, u = 0; u < s; ++u) + (l = i[u]) && e.call(l, l.__data__, u, i) && a.push(l); + return new Ln(r, this._parents, this._name, this._id); +} +function Ev(e) { + if (e._id !== this._id) throw new Error(); + for (var t = this._groups, n = e._groups, r = t.length, o = n.length, i = Math.min(r, o), s = new Array(r), a = 0; a < i; ++a) + for (var l = t[a], u = n[a], c = l.length, f = s[a] = new Array(c), d, g = 0; g < c; ++g) + (d = l[g] || u[g]) && (f[g] = d); + for (; a < r; ++a) + s[a] = t[a]; + return new Ln(s, this._parents, this._name, this._id); +} +function Sv(e) { + return (e + "").trim().split(/^|\s+/).every(function(t) { + var n = t.indexOf("."); + return n >= 0 && (t = t.slice(0, n)), !t || t === "start"; + }); +} +function Pv(e, t, n) { + var r, o, i = Sv(t) ? ia : Cn; + return function() { + var s = i(this, e), a = s.on; + a !== r && (o = (r = a).copy()).on(t, n), s.on = o; + }; +} +function Nv(e, t) { + var n = this._id; + return arguments.length < 2 ? cn(this.node(), n).on.on(e) : this.each(Pv(n, e, t)); +} +function Mv(e) { + return function() { + var t = this.parentNode; + for (var n in this.__transition) if (+n !== e) return; + t && t.removeChild(this); + }; +} +function Tv() { + return this.on("end.remove", Mv(this._id)); +} +function Hv(e) { + var t = this._name, n = this._id; + typeof e != "function" && (e = ta(e)); + for (var r = this._groups, o = r.length, i = new Array(o), s = 0; s < o; ++s) + for (var a = r[s], l = a.length, u = i[s] = new Array(l), c, f, d = 0; d < l; ++d) + (c = a[d]) && (f = e.call(c, c.__data__, d, a)) && ("__data__" in c && (f.__data__ = c.__data__), u[d] = f, Bi(u[d], t, n, d, u, cn(c, n))); + return new Ln(i, this._parents, t, n); +} +function Vv(e) { + var t = this._name, n = this._id; + typeof e != "function" && (e = ku(e)); + for (var r = this._groups, o = r.length, i = [], s = [], a = 0; a < o; ++a) + for (var l = r[a], u = l.length, c, f = 0; f < u; ++f) + if (c = l[f]) { + for (var d = e.call(c, c.__data__, f, l), g, p = cn(c, n), x = 0, C = d.length; x < C; ++x) + (g = d[x]) && Bi(g, t, n, x, d, p); + i.push(d), s.push(c); + } + return new Ln(i, s, t, n); +} +var Dv = Eo.prototype.constructor; +function Av() { + return new Dv(this._groups, this._parents); +} +function Lv(e, t) { + var n, r, o; + return function() { + var i = Hr(this, e), s = (this.style.removeProperty(e), Hr(this, e)); + return i === s ? null : i === n && s === r ? o : o = t(n = i, r = s); + }; +} +function qu(e) { + return function() { + this.style.removeProperty(e); + }; +} +function Ov(e, t, n) { + var r, o = n + "", i; + return function() { + var s = Hr(this, e); + return s === o ? null : s === r ? i : i = t(r = s, n); + }; +} +function Iv(e, t, n) { + var r, o, i; + return function() { + var s = Hr(this, e), a = n(this), l = a + ""; + return a == null && (l = a = (this.style.removeProperty(e), Hr(this, e))), s === l ? null : s === r && l === o ? i : (o = l, i = t(r = s, a)); + }; +} +function zv(e, t) { + var n, r, o, i = "style." + t, s = "end." + i, a; + return function() { + var l = Cn(this, e), u = l.on, c = l.value[i] == null ? a || (a = qu(t)) : void 0; + (u !== n || o !== c) && (r = (n = u).copy()).on(s, o = c), l.on = r; + }; +} +function Rv(e, t, n) { + var r = (e += "") == "transform" ? Rh : Ku; + return t == null ? this.styleTween(e, Lv(e, r)).on("end.style." + e, qu(e)) : typeof t == "function" ? this.styleTween(e, Iv(e, r, sa(this, "style." + e, t))).each(zv(this._id, e)) : this.styleTween(e, Ov(e, r, t), n).on("end.style." + e, null); +} +function Bv(e, t, n) { + return function(r) { + this.style.setProperty(e, t.call(this, r), n); + }; +} +function Yv(e, t, n) { + var r, o; + function i() { + var s = t.apply(this, arguments); + return s !== o && (r = (o = s) && Bv(e, s, n)), r; + } + return i._value = t, i; +} +function Zv(e, t, n) { + var r = "style." + (e += ""); + if (arguments.length < 2) return (r = this.tween(r)) && r._value; + if (t == null) return this.tween(r, null); + if (typeof t != "function") throw new Error(); + return this.tween(r, Yv(e, t, n ?? "")); +} +function Xv(e) { + return function() { + this.textContent = e; + }; +} +function Fv(e) { + return function() { + var t = e(this); + this.textContent = t ?? ""; + }; +} +function Wv(e) { + return this.tween("text", typeof e == "function" ? Fv(sa(this, "text", e)) : Xv(e == null ? "" : e + "")); +} +function Kv(e) { + return function(t) { + this.textContent = e.call(this, t); + }; +} +function qv(e) { + var t, n; + function r() { + var o = e.apply(this, arguments); + return o !== n && (t = (n = o) && Kv(o)), t; + } + return r._value = e, r; +} +function Gv(e) { + var t = "text"; + if (arguments.length < 1) return (t = this.tween(t)) && t._value; + if (e == null) return this.tween(t, null); + if (typeof e != "function") throw new Error(); + return this.tween(t, qv(e)); +} +function Uv() { + for (var e = this._name, t = this._id, n = Gu(), r = this._groups, o = r.length, i = 0; i < o; ++i) + for (var s = r[i], a = s.length, l, u = 0; u < a; ++u) + if (l = s[u]) { + var c = cn(l, t); + Bi(l, e, n, u, s, { + time: c.time + c.delay + c.duration, + delay: 0, + duration: c.duration, + ease: c.ease + }); + } + return new Ln(r, this._parents, e, n); +} +function jv() { + var e, t, n = this, r = n._id, o = n.size(); + return new Promise(function(i, s) { + var a = { value: s }, l = { value: function() { + --o === 0 && i(); + } }; + n.each(function() { + var u = Cn(this, r), c = u.on; + c !== e && (t = (e = c).copy(), t._.cancel.push(a), t._.interrupt.push(a), t._.end.push(l)), u.on = t; + }), o === 0 && i(); + }); +} +var Jv = 0; +function Ln(e, t, n, r) { + this._groups = e, this._parents = t, this._name = n, this._id = r; +} +function Gu() { + return ++Jv; +} +var $n = Eo.prototype; +Ln.prototype = { + constructor: Ln, + select: Hv, + selectAll: Vv, + selectChild: $n.selectChild, + selectChildren: $n.selectChildren, + filter: $v, + merge: Ev, + selection: Av, + transition: Uv, + call: $n.call, + nodes: $n.nodes, + node: $n.node, + size: $n.size, + empty: $n.empty, + each: $n.each, + on: Nv, + attr: uv, + attrTween: hv, + style: Rv, + styleTween: Zv, + text: Wv, + textTween: Gv, + remove: Tv, + tween: nv, + delay: mv, + duration: _v, + ease: bv, + easeVarying: kv, + end: jv, + [Symbol.iterator]: $n[Symbol.iterator] +}; +function Qv(e) { + return ((e *= 2) <= 1 ? e * e * e : (e -= 2) * e * e + 2) / 2; +} +var e0 = { + time: null, + // Set on use. + delay: 0, + duration: 250, + ease: Qv +}; +function t0(e, t) { + for (var n; !(n = e.__transition) || !(n = n[t]); ) + if (!(e = e.parentNode)) + throw new Error(`transition ${t} not found`); + return n; +} +function n0(e) { + var t, n; + e instanceof Ln ? (t = e._id, e = e._name) : (t = Gu(), (n = e0).time = oa(), e = e == null ? null : e + ""); + for (var r = this._groups, o = r.length, i = 0; i < o; ++i) + for (var s = r[i], a = s.length, l, u = 0; u < a; ++u) + (l = s[u]) && Bi(l, e, t, u, s, n || t0(l, t)); + return new Ln(r, this._parents, e, t); +} +Eo.prototype.interrupt = Qh; +Eo.prototype.transition = n0; +const Wo = (e) => () => e; +function r0(e, { + sourceEvent: t, + target: n, + transform: r, + dispatch: o +}) { + Object.defineProperties(this, { + type: { value: e, enumerable: !0, configurable: !0 }, + sourceEvent: { value: t, enumerable: !0, configurable: !0 }, + target: { value: n, enumerable: !0, configurable: !0 }, + transform: { value: r, enumerable: !0, configurable: !0 }, + _: { value: o } + }); +} +function Pn(e, t, n) { + this.k = e, this.x = t, this.y = n; +} +Pn.prototype = { + constructor: Pn, + scale: function(e) { + return e === 1 ? this : new Pn(this.k * e, this.x, this.y); + }, + translate: function(e, t) { + return e === 0 & t === 0 ? this : new Pn(this.k, this.x + this.k * e, this.y + this.k * t); + }, + apply: function(e) { + return [e[0] * this.k + this.x, e[1] * this.k + this.y]; + }, + applyX: function(e) { + return e * this.k + this.x; + }, + applyY: function(e) { + return e * this.k + this.y; + }, + invert: function(e) { + return [(e[0] - this.x) / this.k, (e[1] - this.y) / this.k]; + }, + invertX: function(e) { + return (e - this.x) / this.k; + }, + invertY: function(e) { + return (e - this.y) / this.k; + }, + rescaleX: function(e) { + return e.copy().domain(e.range().map(this.invertX, this).map(e.invert, e)); + }, + rescaleY: function(e) { + return e.copy().domain(e.range().map(this.invertY, this).map(e.invert, e)); + }, + toString: function() { + return "translate(" + this.x + "," + this.y + ") scale(" + this.k + ")"; + } +}; +var Yi = new Pn(1, 0, 0); +Uu.prototype = Pn.prototype; +function Uu(e) { + for (; !e.__zoom; ) if (!(e = e.parentNode)) return Yi; + return e.__zoom; +} +function os(e) { + e.stopImmediatePropagation(); +} +function Qr(e) { + e.preventDefault(), e.stopImmediatePropagation(); +} +function o0(e) { + return (!e.ctrlKey || e.type === "wheel") && !e.button; +} +function i0() { + var e = this; + return e instanceof SVGElement ? (e = e.ownerSVGElement || e, e.hasAttribute("viewBox") ? (e = e.viewBox.baseVal, [[e.x, e.y], [e.x + e.width, e.y + e.height]]) : [[0, 0], [e.width.baseVal.value, e.height.baseVal.value]]) : [[0, 0], [e.clientWidth, e.clientHeight]]; +} +function sl() { + return this.__zoom || Yi; +} +function s0(e) { + return -e.deltaY * (e.deltaMode === 1 ? 0.05 : e.deltaMode ? 1 : 2e-3) * (e.ctrlKey ? 10 : 1); +} +function a0() { + return navigator.maxTouchPoints || "ontouchstart" in this; +} +function l0(e, t, n) { + var r = e.invertX(t[0][0]) - n[0][0], o = e.invertX(t[1][0]) - n[1][0], i = e.invertY(t[0][1]) - n[0][1], s = e.invertY(t[1][1]) - n[1][1]; + return e.translate( + o > r ? (r + o) / 2 : Math.min(0, r) || Math.max(0, o), + s > i ? (i + s) / 2 : Math.min(0, i) || Math.max(0, s) + ); +} +function ju() { + var e = o0, t = i0, n = l0, r = s0, o = a0, i = [0, 1 / 0], s = [[-1 / 0, -1 / 0], [1 / 0, 1 / 0]], a = 250, l = Fh, u = Ii("start", "zoom", "end"), c, f, d, g = 500, p = 150, x = 0, C = 10; + function $(S) { + S.property("__zoom", sl).on("wheel.zoom", M, { passive: !1 }).on("mousedown.zoom", D).on("dblclick.zoom", V).filter(o).on("touchstart.zoom", A).on("touchmove.zoom", O).on("touchend.zoom touchcancel.zoom", R).style("-webkit-tap-highlight-color", "rgba(0,0,0,0)"); + } + $.transform = function(S, T, k, P) { + var H = S.selection ? S.selection() : S; + H.property("__zoom", sl), S !== H ? b(S, T, k, P) : H.interrupt().each(function() { + N(this, arguments).event(P).start().zoom(null, typeof T == "function" ? T.apply(this, arguments) : T).end(); + }); + }, $.scaleBy = function(S, T, k, P) { + $.scaleTo(S, function() { + var H = this.__zoom.k, I = typeof T == "function" ? T.apply(this, arguments) : T; + return H * I; + }, k, P); + }, $.scaleTo = function(S, T, k, P) { + $.transform(S, function() { + var H = t.apply(this, arguments), I = this.__zoom, B = k == null ? v(H) : typeof k == "function" ? k.apply(this, arguments) : k, F = I.invert(B), K = typeof T == "function" ? T.apply(this, arguments) : T; + return n(_(m(I, K), B, F), H, s); + }, k, P); + }, $.translateBy = function(S, T, k, P) { + $.transform(S, function() { + return n(this.__zoom.translate( + typeof T == "function" ? T.apply(this, arguments) : T, + typeof k == "function" ? k.apply(this, arguments) : k + ), t.apply(this, arguments), s); + }, null, P); + }, $.translateTo = function(S, T, k, P, H) { + $.transform(S, function() { + var I = t.apply(this, arguments), B = this.__zoom, F = P == null ? v(I) : typeof P == "function" ? P.apply(this, arguments) : P; + return n(Yi.translate(F[0], F[1]).scale(B.k).translate( + typeof T == "function" ? -T.apply(this, arguments) : -T, + typeof k == "function" ? -k.apply(this, arguments) : -k + ), I, s); + }, P, H); + }; + function m(S, T) { + return T = Math.max(i[0], Math.min(i[1], T)), T === S.k ? S : new Pn(T, S.x, S.y); + } + function _(S, T, k) { + var P = T[0] - k[0] * S.k, H = T[1] - k[1] * S.k; + return P === S.x && H === S.y ? S : new Pn(S.k, P, H); + } + function v(S) { + return [(+S[0][0] + +S[1][0]) / 2, (+S[0][1] + +S[1][1]) / 2]; + } + function b(S, T, k, P) { + S.on("start.zoom", function() { + N(this, arguments).event(P).start(); + }).on("interrupt.zoom end.zoom", function() { + N(this, arguments).event(P).end(); + }).tween("zoom", function() { + var H = this, I = arguments, B = N(H, I).event(P), F = t.apply(H, I), K = k == null ? v(F) : typeof k == "function" ? k.apply(H, I) : k, ie = Math.max(F[1][0] - F[0][0], F[1][1] - F[0][1]), ee = H.__zoom, W = typeof T == "function" ? T.apply(H, I) : T, ue = l(ee.invert(K).concat(ie / ee.k), W.invert(K).concat(ie / W.k)); + return function(me) { + if (me === 1) me = W; + else { + var Ce = ue(me), ge = ie / Ce[2]; + me = new Pn(ge, K[0] - Ce[0] * ge, K[1] - Ce[1] * ge); + } + B.zoom(null, me); + }; + }); + } + function N(S, T, k) { + return !k && S.__zooming || new E(S, T); + } + function E(S, T) { + this.that = S, this.args = T, this.active = 0, this.sourceEvent = null, this.extent = t.apply(S, T), this.taps = 0; + } + E.prototype = { + event: function(S) { + return S && (this.sourceEvent = S), this; + }, + start: function() { + return ++this.active === 1 && (this.that.__zooming = this, this.emit("start")), this; + }, + zoom: function(S, T) { + return this.mouse && S !== "mouse" && (this.mouse[1] = T.invert(this.mouse[0])), this.touch0 && S !== "touch" && (this.touch0[1] = T.invert(this.touch0[0])), this.touch1 && S !== "touch" && (this.touch1[1] = T.invert(this.touch1[0])), this.that.__zoom = T, this.emit("zoom"), this; + }, + end: function() { + return --this.active === 0 && (delete this.that.__zooming, this.emit("end")), this; + }, + emit: function(S) { + var T = Kt(this.that).datum(); + u.call( + S, + this.that, + new r0(S, { + sourceEvent: this.sourceEvent, + target: $, + transform: this.that.__zoom, + dispatch: u + }), + T + ); + } + }; + function M(S, ...T) { + if (!e.apply(this, arguments)) return; + var k = N(this, T).event(S), P = this.__zoom, H = Math.max(i[0], Math.min(i[1], P.k * Math.pow(2, r.apply(this, arguments)))), I = Qt(S); + if (k.wheel) + (k.mouse[0][0] !== I[0] || k.mouse[0][1] !== I[1]) && (k.mouse[1] = P.invert(k.mouse[0] = I)), clearTimeout(k.wheel); + else { + if (P.k === H) return; + k.mouse = [I, P.invert(I)], Qo(this), k.start(); + } + Qr(S), k.wheel = setTimeout(B, p), k.zoom("mouse", n(_(m(P, H), k.mouse[0], k.mouse[1]), k.extent, s)); + function B() { + k.wheel = null, k.end(); + } + } + function D(S, ...T) { + if (d || !e.apply(this, arguments)) return; + var k = S.currentTarget, P = N(this, T, !0).event(S), H = Kt(S.view).on("mousemove.zoom", K, !0).on("mouseup.zoom", ie, !0), I = Qt(S, k), B = S.clientX, F = S.clientY; + Au(S.view), os(S), P.mouse = [I, this.__zoom.invert(I)], Qo(this), P.start(); + function K(ee) { + if (Qr(ee), !P.moved) { + var W = ee.clientX - B, ue = ee.clientY - F; + P.moved = W * W + ue * ue > x; + } + P.event(ee).zoom("mouse", n(_(P.that.__zoom, P.mouse[0] = Qt(ee, k), P.mouse[1]), P.extent, s)); + } + function ie(ee) { + H.on("mousemove.zoom mouseup.zoom", null), Lu(ee.view, P.moved), Qr(ee), P.event(ee).end(); + } + } + function V(S, ...T) { + if (e.apply(this, arguments)) { + var k = this.__zoom, P = Qt(S.changedTouches ? S.changedTouches[0] : S, this), H = k.invert(P), I = k.k * (S.shiftKey ? 0.5 : 2), B = n(_(m(k, I), P, H), t.apply(this, T), s); + Qr(S), a > 0 ? Kt(this).transition().duration(a).call(b, B, P, S) : Kt(this).call($.transform, B, P, S); + } + } + function A(S, ...T) { + if (e.apply(this, arguments)) { + var k = S.touches, P = k.length, H = N(this, T, S.changedTouches.length === P).event(S), I, B, F, K; + for (os(S), B = 0; B < P; ++B) + F = k[B], K = Qt(F, this), K = [K, this.__zoom.invert(K), F.identifier], H.touch0 ? !H.touch1 && H.touch0[2] !== K[2] && (H.touch1 = K, H.taps = 0) : (H.touch0 = K, I = !0, H.taps = 1 + !!c); + c && (c = clearTimeout(c)), I && (H.taps < 2 && (f = K[0], c = setTimeout(function() { + c = null; + }, g)), Qo(this), H.start()); + } + } + function O(S, ...T) { + if (this.__zooming) { + var k = N(this, T).event(S), P = S.changedTouches, H = P.length, I, B, F, K; + for (Qr(S), I = 0; I < H; ++I) + B = P[I], F = Qt(B, this), k.touch0 && k.touch0[2] === B.identifier ? k.touch0[0] = F : k.touch1 && k.touch1[2] === B.identifier && (k.touch1[0] = F); + if (B = k.that.__zoom, k.touch1) { + var ie = k.touch0[0], ee = k.touch0[1], W = k.touch1[0], ue = k.touch1[1], me = (me = W[0] - ie[0]) * me + (me = W[1] - ie[1]) * me, Ce = (Ce = ue[0] - ee[0]) * Ce + (Ce = ue[1] - ee[1]) * Ce; + B = m(B, Math.sqrt(me / Ce)), F = [(ie[0] + W[0]) / 2, (ie[1] + W[1]) / 2], K = [(ee[0] + ue[0]) / 2, (ee[1] + ue[1]) / 2]; + } else if (k.touch0) F = k.touch0[0], K = k.touch0[1]; + else return; + k.zoom("touch", n(_(B, F, K), k.extent, s)); + } + } + function R(S, ...T) { + if (this.__zooming) { + var k = N(this, T).event(S), P = S.changedTouches, H = P.length, I, B; + for (os(S), d && clearTimeout(d), d = setTimeout(function() { + d = null; + }, g), I = 0; I < H; ++I) + B = P[I], k.touch0 && k.touch0[2] === B.identifier ? delete k.touch0 : k.touch1 && k.touch1[2] === B.identifier && delete k.touch1; + if (k.touch1 && !k.touch0 && (k.touch0 = k.touch1, delete k.touch1), k.touch0) k.touch0[1] = this.__zoom.invert(k.touch0[0]); + else if (k.end(), k.taps === 2 && (B = Qt(B, this), Math.hypot(f[0] - B[0], f[1] - B[1]) < C)) { + var F = Kt(this).on("dblclick.zoom"); + F && F.apply(this, arguments); + } + } + } + return $.wheelDelta = function(S) { + return arguments.length ? (r = typeof S == "function" ? S : Wo(+S), $) : r; + }, $.filter = function(S) { + return arguments.length ? (e = typeof S == "function" ? S : Wo(!!S), $) : e; + }, $.touchable = function(S) { + return arguments.length ? (o = typeof S == "function" ? S : Wo(!!S), $) : o; + }, $.extent = function(S) { + return arguments.length ? (t = typeof S == "function" ? S : Wo([[+S[0][0], +S[0][1]], [+S[1][0], +S[1][1]]]), $) : t; + }, $.scaleExtent = function(S) { + return arguments.length ? (i[0] = +S[0], i[1] = +S[1], $) : [i[0], i[1]]; + }, $.translateExtent = function(S) { + return arguments.length ? (s[0][0] = +S[0][0], s[1][0] = +S[1][0], s[0][1] = +S[0][1], s[1][1] = +S[1][1], $) : [[s[0][0], s[0][1]], [s[1][0], s[1][1]]]; + }, $.constrain = function(S) { + return arguments.length ? (n = S, $) : n; + }, $.duration = function(S) { + return arguments.length ? (a = +S, $) : a; + }, $.interpolate = function(S) { + return arguments.length ? (l = S, $) : l; + }, $.on = function() { + var S = u.on.apply(u, arguments); + return S === u ? $ : S; + }, $.clickDistance = function(S) { + return arguments.length ? (x = (S = +S) * S, $) : Math.sqrt(x); + }, $.tapDistance = function(S) { + return arguments.length ? (C = +S, $) : C; + }, $; +} +const Dr = { + error001: () => "[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001", + error002: () => "It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.", + error003: (e) => `Node type "${e}" not found. Using fallback type "default".`, + error004: () => "The React Flow parent container needs a width and a height to render the graph.", + error005: () => "Only child nodes can use a parent extent.", + error006: () => "Can't create edge. An edge needs a source and a target.", + error007: (e) => `The old edge with id=${e} does not exist.`, + error009: (e) => `Marker type "${e}" doesn't exist.`, + error008: (e, { id: t, sourceHandle: n, targetHandle: r }) => `Couldn't create edge for ${e} handle id: "${e === "source" ? n : r}", edge id: ${t}.`, + error010: () => "Handle: No node id found. Make sure to only use a Handle inside a custom Node.", + error011: (e) => `Edge type "${e}" not found. Using fallback type "default".`, + error012: (e) => `Node with id "${e}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`, + error013: (e = "react") => `It seems that you haven't loaded the styles. Please import '@xyflow/${e}/dist/style.css' or base.css to make sure everything is working properly.`, + error014: () => "useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.", + error015: () => "It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs." +}, vi = [ + [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], + [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY] +]; +var cr; +(function(e) { + e.Strict = "strict", e.Loose = "loose"; +})(cr || (cr = {})); +var qn; +(function(e) { + e.Free = "free", e.Vertical = "vertical", e.Horizontal = "horizontal"; +})(qn || (qn = {})); +var pi; +(function(e) { + e.Partial = "partial", e.Full = "full"; +})(pi || (pi = {})); +const Ns = { + inProgress: !1, + isValid: null, + from: null, + fromHandle: null, + fromPosition: null, + fromNode: null, + to: null, + toHandle: null, + toPosition: null, + toNode: null +}; +var Cr; +(function(e) { + e.Bezier = "default", e.Straight = "straight", e.Step = "step", e.SmoothStep = "smoothstep", e.SimpleBezier = "simplebezier"; +})(Cr || (Cr = {})); +var mo; +(function(e) { + e.Arrow = "arrow", e.ArrowClosed = "arrowclosed"; +})(mo || (mo = {})); +var $e; +(function(e) { + e.Left = "left", e.Top = "top", e.Right = "right", e.Bottom = "bottom"; +})($e || ($e = {})); +const al = { + [$e.Left]: $e.Right, + [$e.Right]: $e.Left, + [$e.Top]: $e.Bottom, + [$e.Bottom]: $e.Top +}; +function u0(e, t) { + if (!e && !t) + return !0; + if (!e || !t || e.size !== t.size) + return !1; + if (!e.size && !t.size) + return !0; + for (const n of e.keys()) + if (!t.has(n)) + return !1; + return !0; +} +function ll(e, t, n) { + if (!n) + return; + const r = []; + e.forEach((o, i) => { + t != null && t.has(i) || r.push(o); + }), r.length && n(r); +} +function c0(e) { + return e === null ? null : e ? "valid" : "invalid"; +} +const d0 = (e) => "id" in e && "source" in e && "target" in e, f0 = (e) => "id" in e && "position" in e && !("source" in e) && !("target" in e), aa = (e) => "id" in e && "internals" in e && !("source" in e) && !("target" in e), Po = (e, t = [0, 0]) => { + const { width: n, height: r } = tr(e), o = e.origin ?? t, i = n * o[0], s = r * o[1]; + return { + x: e.position.x - i, + y: e.position.y - s + }; +}, g0 = (e, t = { nodeOrigin: [0, 0], nodeLookup: void 0 }) => { + if (e.length === 0) + return { x: 0, y: 0, width: 0, height: 0 }; + const n = e.reduce((r, o) => { + const i = typeof o == "string"; + let s = !t.nodeLookup && !i ? o : void 0; + t.nodeLookup && (s = i ? t.nodeLookup.get(o) : aa(o) ? o : t.nodeLookup.get(o.id)); + const a = s ? mi(s, t.nodeOrigin) : { x: 0, y: 0, x2: 0, y2: 0 }; + return Zi(r, a); + }, { x: 1 / 0, y: 1 / 0, x2: -1 / 0, y2: -1 / 0 }); + return Xi(n); +}, No = (e, t = {}) => { + if (e.size === 0) + return { x: 0, y: 0, width: 0, height: 0 }; + let n = { x: 1 / 0, y: 1 / 0, x2: -1 / 0, y2: -1 / 0 }; + return e.forEach((r) => { + if (t.filter === void 0 || t.filter(r)) { + const o = mi(r); + n = Zi(n, o); + } + }), Xi(n); +}, Ju = (e, t, [n, r, o] = [0, 0, 1], i = !1, s = !1) => { + const a = { + ...Mo(t, [n, r, o]), + width: t.width / o, + height: t.height / o + }, l = []; + for (const u of e.values()) { + const { measured: c, selectable: f = !0, hidden: d = !1 } = u; + if (s && !f || d) + continue; + const g = c.width ?? u.width ?? u.initialWidth ?? null, p = c.height ?? u.height ?? u.initialHeight ?? null, x = yo(a, Lr(u)), C = (g ?? 0) * (p ?? 0), $ = i && x > 0; + (!u.internals.handleBounds || $ || x >= C || u.dragging) && l.push(u); + } + return l; +}, Ms = (e, t) => { + const n = /* @__PURE__ */ new Set(); + return e.forEach((r) => { + n.add(r.id); + }), t.filter((r) => n.has(r.source) || n.has(r.target)); +}; +function ul(e, t) { + const n = /* @__PURE__ */ new Map(), r = t != null && t.nodes ? new Set(t.nodes.map((o) => o.id)) : null; + return e.forEach((o) => { + o.measured.width && o.measured.height && ((t == null ? void 0 : t.includeHiddenNodes) || !o.hidden) && (!r || r.has(o.id)) && n.set(o.id, o); + }), n; +} +async function cl({ nodes: e, width: t, height: n, panZoom: r, minZoom: o, maxZoom: i }, s) { + if (e.size === 0) + return Promise.resolve(!1); + const a = No(e), l = ua(a, t, n, (s == null ? void 0 : s.minZoom) ?? o, (s == null ? void 0 : s.maxZoom) ?? i, (s == null ? void 0 : s.padding) ?? 0.1); + return await r.setViewport(l, { duration: s == null ? void 0 : s.duration }), Promise.resolve(!0); +} +function h0({ nodeId: e, nextPosition: t, nodeLookup: n, nodeOrigin: r = [0, 0], nodeExtent: o, onError: i }) { + const s = n.get(e), a = s.parentId ? n.get(s.parentId) : void 0, { x: l, y: u } = a ? a.internals.positionAbsolute : { x: 0, y: 0 }, c = s.origin ?? r; + let f = o; + if (s.extent === "parent" && !s.expandParent) + if (!a) + i == null || i("005", Dr.error005()); + else { + const g = a.measured.width, p = a.measured.height; + g && p && (f = [ + [l, u], + [l + g, u + p] + ]); + } + else a && Or(s.extent) && (f = [ + [s.extent[0][0] + l, s.extent[0][1] + u], + [s.extent[1][0] + l, s.extent[1][1] + u] + ]); + const d = Or(f) ? dr(t, f, s.measured) : t; + return (s.measured.width === void 0 || s.measured.height === void 0) && (i == null || i("015", Dr.error015())), { + position: { + x: d.x - l + (s.measured.width ?? 0) * c[0], + y: d.y - u + (s.measured.height ?? 0) * c[1] + }, + positionAbsolute: d + }; +} +async function Qu({ nodesToRemove: e = [], edgesToRemove: t = [], nodes: n, edges: r, onBeforeDelete: o }) { + const i = new Set(e.map((d) => d.id)), s = []; + for (const d of n) { + if (d.deletable === !1) + continue; + const g = i.has(d.id), p = !g && d.parentId && s.find((x) => x.id === d.parentId); + (g || p) && s.push(d); + } + const a = new Set(t.map((d) => d.id)), l = r.filter((d) => d.deletable !== !1), c = Ms(s, l); + for (const d of l) + a.has(d.id) && !c.find((p) => p.id === d.id) && c.push(d); + if (!o) + return { + edges: c, + nodes: s + }; + const f = await o({ + nodes: s, + edges: c + }); + return typeof f == "boolean" ? f ? { edges: c, nodes: s } : { edges: [], nodes: [] } : f; +} +const Ar = (e, t = 0, n = 1) => Math.min(Math.max(e, t), n), dr = (e = { x: 0, y: 0 }, t, n) => ({ + x: Ar(e.x, t[0][0], t[1][0] - ((n == null ? void 0 : n.width) ?? 0)), + y: Ar(e.y, t[0][1], t[1][1] - ((n == null ? void 0 : n.height) ?? 0)) +}); +function ec(e, t, n) { + const { width: r, height: o } = tr(n), { x: i, y: s } = n.internals.positionAbsolute; + return dr(e, [ + [i, s], + [i + r, s + o] + ], t); +} +const dl = (e, t, n) => e < t ? Ar(Math.abs(e - t), 1, t) / t : e > n ? -Ar(Math.abs(e - n), 1, t) / t : 0, tc = (e, t, n = 15, r = 40) => { + const o = dl(e.x, r, t.width - r) * n, i = dl(e.y, r, t.height - r) * n; + return [o, i]; +}, Zi = (e, t) => ({ + x: Math.min(e.x, t.x), + y: Math.min(e.y, t.y), + x2: Math.max(e.x2, t.x2), + y2: Math.max(e.y2, t.y2) +}), Ts = ({ x: e, y: t, width: n, height: r }) => ({ + x: e, + y: t, + x2: e + n, + y2: t + r +}), Xi = ({ x: e, y: t, x2: n, y2: r }) => ({ + x: e, + y: t, + width: n - e, + height: r - t +}), Lr = (e, t = [0, 0]) => { + var o, i; + const { x: n, y: r } = aa(e) ? e.internals.positionAbsolute : Po(e, t); + return { + x: n, + y: r, + width: ((o = e.measured) == null ? void 0 : o.width) ?? e.width ?? e.initialWidth ?? 0, + height: ((i = e.measured) == null ? void 0 : i.height) ?? e.height ?? e.initialHeight ?? 0 + }; +}, mi = (e, t = [0, 0]) => { + var o, i; + const { x: n, y: r } = aa(e) ? e.internals.positionAbsolute : Po(e, t); + return { + x: n, + y: r, + x2: n + (((o = e.measured) == null ? void 0 : o.width) ?? e.width ?? e.initialWidth ?? 0), + y2: r + (((i = e.measured) == null ? void 0 : i.height) ?? e.height ?? e.initialHeight ?? 0) + }; +}, nc = (e, t) => Xi(Zi(Ts(e), Ts(t))), yo = (e, t) => { + const n = Math.max(0, Math.min(e.x + e.width, t.x + t.width) - Math.max(e.x, t.x)), r = Math.max(0, Math.min(e.y + e.height, t.y + t.height) - Math.max(e.y, t.y)); + return Math.ceil(n * r); +}, fl = (e) => Nn(e.width) && Nn(e.height) && Nn(e.x) && Nn(e.y), Nn = (e) => !isNaN(e) && isFinite(e), v0 = (e, t) => { +}, la = (e, t = [1, 1]) => ({ + x: t[0] * Math.round(e.x / t[0]), + y: t[1] * Math.round(e.y / t[1]) +}), Mo = ({ x: e, y: t }, [n, r, o], i = !1, s = [1, 1]) => { + const a = { + x: (e - n) / o, + y: (t - r) / o + }; + return i ? la(a, s) : a; +}, rc = ({ x: e, y: t }, [n, r, o]) => ({ + x: e * o + n, + y: t * o + r +}), ua = (e, t, n, r, o, i) => { + const s = t / (e.width * (1 + i)), a = n / (e.height * (1 + i)), l = Math.min(s, a), u = Ar(l, r, o), c = e.x + e.width / 2, f = e.y + e.height / 2, d = t / 2 - c * u, g = n / 2 - f * u; + return { x: d, y: g, zoom: u }; +}, yi = () => { + var e; + return typeof navigator < "u" && ((e = navigator == null ? void 0 : navigator.userAgent) == null ? void 0 : e.indexOf("Mac")) >= 0; +}; +function Or(e) { + return e !== void 0 && e !== "parent"; +} +function tr(e) { + var t, n; + return { + width: ((t = e.measured) == null ? void 0 : t.width) ?? e.width ?? e.initialWidth ?? 0, + height: ((n = e.measured) == null ? void 0 : n.height) ?? e.height ?? e.initialHeight ?? 0 + }; +} +function oc(e) { + var t, n; + return (((t = e.measured) == null ? void 0 : t.width) ?? e.width ?? e.initialWidth) !== void 0 && (((n = e.measured) == null ? void 0 : n.height) ?? e.height ?? e.initialHeight) !== void 0; +} +function p0(e, t = { width: 0, height: 0 }, n, r, o) { + const i = { ...e }, s = r.get(n); + if (s) { + const a = s.origin || o; + i.x += s.internals.positionAbsolute.x - (t.width ?? 0) * a[0], i.y += s.internals.positionAbsolute.y - (t.height ?? 0) * a[1]; + } + return i; +} +function is(e, { snapGrid: t = [0, 0], snapToGrid: n = !1, transform: r, containerBounds: o }) { + const { x: i, y: s } = Hn(e), a = Mo({ x: i - ((o == null ? void 0 : o.left) ?? 0), y: s - ((o == null ? void 0 : o.top) ?? 0) }, r), { x: l, y: u } = n ? la(a, t) : a; + return { + xSnapped: l, + ySnapped: u, + ...a + }; +} +const ca = (e) => ({ + width: e.offsetWidth, + height: e.offsetHeight +}), m0 = (e) => { + var t; + return ((t = e == null ? void 0 : e.getRootNode) == null ? void 0 : t.call(e)) || (window == null ? void 0 : window.document); +}, y0 = ["INPUT", "SELECT", "TEXTAREA"]; +function w0(e) { + var r, o; + const t = ((o = (r = e.composedPath) == null ? void 0 : r.call(e)) == null ? void 0 : o[0]) || e.target; + return (t == null ? void 0 : t.nodeType) !== 1 ? !1 : y0.includes(t.nodeName) || t.hasAttribute("contenteditable") || !!t.closest(".nokey"); +} +const ic = (e) => "clientX" in e, Hn = (e, t) => { + var i, s; + const n = ic(e), r = n ? e.clientX : (i = e.touches) == null ? void 0 : i[0].clientX, o = n ? e.clientY : (s = e.touches) == null ? void 0 : s[0].clientY; + return { + x: r - ((t == null ? void 0 : t.left) ?? 0), + y: o - ((t == null ? void 0 : t.top) ?? 0) + }; +}, gl = (e, t, n, r, o) => { + const i = t.querySelectorAll(`.${e}`); + return !i || !i.length ? null : Array.from(i).map((s) => { + const a = s.getBoundingClientRect(); + return { + id: s.getAttribute("data-handleid"), + type: e, + nodeId: o, + position: s.getAttribute("data-handlepos"), + x: (a.left - n.left) / r, + y: (a.top - n.top) / r, + ...ca(s) + }; + }); +}; +function _0({ sourceX: e, sourceY: t, targetX: n, targetY: r, sourceControlX: o, sourceControlY: i, targetControlX: s, targetControlY: a }) { + const l = e * 0.125 + o * 0.375 + s * 0.375 + n * 0.125, u = t * 0.125 + i * 0.375 + a * 0.375 + r * 0.125, c = Math.abs(l - e), f = Math.abs(u - t); + return [l, u, c, f]; +} +function Ko(e, t) { + return e >= 0 ? 0.5 * e : t * 25 * Math.sqrt(-e); +} +function hl({ pos: e, x1: t, y1: n, x2: r, y2: o, c: i }) { + switch (e) { + case $e.Left: + return [t - Ko(t - r, i), n]; + case $e.Right: + return [t + Ko(r - t, i), n]; + case $e.Top: + return [t, n - Ko(n - o, i)]; + case $e.Bottom: + return [t, n + Ko(o - n, i)]; + } +} +function sc({ sourceX: e, sourceY: t, sourcePosition: n = $e.Bottom, targetX: r, targetY: o, targetPosition: i = $e.Top, curvature: s = 0.25 }) { + const [a, l] = hl({ + pos: n, + x1: e, + y1: t, + x2: r, + y2: o, + c: s + }), [u, c] = hl({ + pos: i, + x1: r, + y1: o, + x2: e, + y2: t, + c: s + }), [f, d, g, p] = _0({ + sourceX: e, + sourceY: t, + targetX: r, + targetY: o, + sourceControlX: a, + sourceControlY: l, + targetControlX: u, + targetControlY: c + }); + return [ + `M${e},${t} C${a},${l} ${u},${c} ${r},${o}`, + f, + d, + g, + p + ]; +} +function ac({ sourceX: e, sourceY: t, targetX: n, targetY: r }) { + const o = Math.abs(n - e) / 2, i = n < e ? n + o : n - o, s = Math.abs(r - t) / 2, a = r < t ? r + s : r - s; + return [i, a, o, s]; +} +function x0({ sourceNode: e, targetNode: t, selected: n = !1, zIndex: r = 0, elevateOnSelect: o = !1 }) { + if (!o) + return r; + const i = n || t.selected || e.selected, s = Math.max(e.internals.z || 0, t.internals.z || 0, 1e3); + return r + (i ? s : 0); +} +function b0({ sourceNode: e, targetNode: t, width: n, height: r, transform: o }) { + const i = Zi(mi(e), mi(t)); + i.x === i.x2 && (i.x2 += 1), i.y === i.y2 && (i.y2 += 1); + const s = { + x: -o[0] / o[2], + y: -o[1] / o[2], + width: n / o[2], + height: r / o[2] + }; + return yo(s, Xi(i)) > 0; +} +const C0 = ({ source: e, sourceHandle: t, target: n, targetHandle: r }) => `xy-edge__${e}${t || ""}-${n}${r || ""}`, k0 = (e, t) => t.some((n) => n.source === e.source && n.target === e.target && (n.sourceHandle === e.sourceHandle || !n.sourceHandle && !e.sourceHandle) && (n.targetHandle === e.targetHandle || !n.targetHandle && !e.targetHandle)), $0 = (e, t) => { + if (!e.source || !e.target) + return t; + let n; + return d0(e) ? n = { ...e } : n = { + ...e, + id: C0(e) + }, k0(n, t) ? t : (n.sourceHandle === null && delete n.sourceHandle, n.targetHandle === null && delete n.targetHandle, t.concat(n)); +}; +function Hs({ sourceX: e, sourceY: t, targetX: n, targetY: r }) { + const [o, i, s, a] = ac({ + sourceX: e, + sourceY: t, + targetX: n, + targetY: r + }); + return [`M ${e},${t}L ${n},${r}`, o, i, s, a]; +} +const vl = { + [$e.Left]: { x: -1, y: 0 }, + [$e.Right]: { x: 1, y: 0 }, + [$e.Top]: { x: 0, y: -1 }, + [$e.Bottom]: { x: 0, y: 1 } +}, E0 = ({ source: e, sourcePosition: t = $e.Bottom, target: n }) => t === $e.Left || t === $e.Right ? e.x < n.x ? { x: 1, y: 0 } : { x: -1, y: 0 } : e.y < n.y ? { x: 0, y: 1 } : { x: 0, y: -1 }, pl = (e, t) => Math.sqrt(Math.pow(t.x - e.x, 2) + Math.pow(t.y - e.y, 2)); +function S0({ source: e, sourcePosition: t = $e.Bottom, target: n, targetPosition: r = $e.Top, center: o, offset: i }) { + const s = vl[t], a = vl[r], l = { x: e.x + s.x * i, y: e.y + s.y * i }, u = { x: n.x + a.x * i, y: n.y + a.y * i }, c = E0({ + source: l, + sourcePosition: t, + target: u + }), f = c.x !== 0 ? "x" : "y", d = c[f]; + let g = [], p, x; + const C = { x: 0, y: 0 }, $ = { x: 0, y: 0 }, [m, _, v, b] = ac({ + sourceX: e.x, + sourceY: e.y, + targetX: n.x, + targetY: n.y + }); + if (s[f] * a[f] === -1) { + p = o.x ?? m, x = o.y ?? _; + const E = [ + { x: p, y: l.y }, + { x: p, y: u.y } + ], M = [ + { x: l.x, y: x }, + { x: u.x, y: x } + ]; + s[f] === d ? g = f === "x" ? E : M : g = f === "x" ? M : E; + } else { + const E = [{ x: l.x, y: u.y }], M = [{ x: u.x, y: l.y }]; + if (f === "x" ? g = s.x === d ? M : E : g = s.y === d ? E : M, t === r) { + const R = Math.abs(e[f] - n[f]); + if (R <= i) { + const S = Math.min(i - 1, i - R); + s[f] === d ? C[f] = (l[f] > e[f] ? -1 : 1) * S : $[f] = (u[f] > n[f] ? -1 : 1) * S; + } + } + if (t !== r) { + const R = f === "x" ? "y" : "x", S = s[f] === a[R], T = l[R] > u[R], k = l[R] < u[R]; + (s[f] === 1 && (!S && T || S && k) || s[f] !== 1 && (!S && k || S && T)) && (g = f === "x" ? E : M); + } + const D = { x: l.x + C.x, y: l.y + C.y }, V = { x: u.x + $.x, y: u.y + $.y }, A = Math.max(Math.abs(D.x - g[0].x), Math.abs(V.x - g[0].x)), O = Math.max(Math.abs(D.y - g[0].y), Math.abs(V.y - g[0].y)); + A >= O ? (p = (D.x + V.x) / 2, x = g[0].y) : (p = g[0].x, x = (D.y + V.y) / 2); + } + return [[ + e, + { x: l.x + C.x, y: l.y + C.y }, + ...g, + { x: u.x + $.x, y: u.y + $.y }, + n + ], p, x, v, b]; +} +function P0(e, t, n, r) { + const o = Math.min(pl(e, t) / 2, pl(t, n) / 2, r), { x: i, y: s } = t; + if (e.x === i && i === n.x || e.y === s && s === n.y) + return `L${i} ${s}`; + if (e.y === s) { + const u = e.x < n.x ? -1 : 1, c = e.y < n.y ? 1 : -1; + return `L ${i + o * u},${s}Q ${i},${s} ${i},${s + o * c}`; + } + const a = e.x < n.x ? 1 : -1, l = e.y < n.y ? -1 : 1; + return `L ${i},${s + o * l}Q ${i},${s} ${i + o * a},${s}`; +} +function wi({ sourceX: e, sourceY: t, sourcePosition: n = $e.Bottom, targetX: r, targetY: o, targetPosition: i = $e.Top, borderRadius: s = 5, centerX: a, centerY: l, offset: u = 20 }) { + const [c, f, d, g, p] = S0({ + source: { x: e, y: t }, + sourcePosition: n, + target: { x: r, y: o }, + targetPosition: i, + center: { x: a, y: l }, + offset: u + }); + return [c.reduce((C, $, m) => { + let _ = ""; + return m > 0 && m < c.length - 1 ? _ = P0(c[m - 1], $, c[m + 1], s) : _ = `${m === 0 ? "M" : "L"}${$.x} ${$.y}`, C += _, C; + }, ""), f, d, g, p]; +} +function ml(e) { + var t; + return e && !!(e.internals.handleBounds || (t = e.handles) != null && t.length) && !!(e.measured.width || e.width || e.initialWidth); +} +function N0(e) { + var f; + const { sourceNode: t, targetNode: n } = e; + if (!ml(t) || !ml(n)) + return null; + const r = t.internals.handleBounds || yl(t.handles), o = n.internals.handleBounds || yl(n.handles), i = wl((r == null ? void 0 : r.source) ?? [], e.sourceHandle), s = wl( + // when connection type is loose we can define all handles as sources and connect source -> source + e.connectionMode === cr.Strict ? (o == null ? void 0 : o.target) ?? [] : ((o == null ? void 0 : o.target) ?? []).concat((o == null ? void 0 : o.source) ?? []), + e.targetHandle + ); + if (!i || !s) + return (f = e.onError) == null || f.call(e, "008", Dr.error008(i ? "target" : "source", { + id: e.id, + sourceHandle: e.sourceHandle, + targetHandle: e.targetHandle + })), null; + const a = (i == null ? void 0 : i.position) || $e.Bottom, l = (s == null ? void 0 : s.position) || $e.Top, u = wo(t, i, a), c = wo(n, s, l); + return { + sourceX: u.x, + sourceY: u.y, + targetX: c.x, + targetY: c.y, + sourcePosition: a, + targetPosition: l + }; +} +function yl(e) { + if (!e) + return null; + const t = [], n = []; + for (const r of e) + r.width = r.width ?? 1, r.height = r.height ?? 1, r.type === "source" ? t.push(r) : r.type === "target" && n.push(r); + return { + source: t, + target: n + }; +} +function wo(e, t, n = $e.Left, r = !1) { + const o = ((t == null ? void 0 : t.x) ?? 0) + e.internals.positionAbsolute.x, i = ((t == null ? void 0 : t.y) ?? 0) + e.internals.positionAbsolute.y, { width: s, height: a } = t ?? tr(e); + if (r) + return { x: o + s / 2, y: i + a / 2 }; + switch ((t == null ? void 0 : t.position) ?? n) { + case $e.Top: + return { x: o + s / 2, y: i }; + case $e.Right: + return { x: o + s, y: i + a / 2 }; + case $e.Bottom: + return { x: o + s / 2, y: i + a }; + case $e.Left: + return { x: o, y: i + a / 2 }; + } +} +function wl(e, t) { + return e && (t ? e.find((n) => n.id === t) : e[0]) || null; +} +function Vs(e, t) { + return e ? typeof e == "string" ? e : `${t ? `${t}__` : ""}${Object.keys(e).sort().map((r) => `${r}=${e[r]}`).join("&")}` : ""; +} +function M0(e, { id: t, defaultColor: n, defaultMarkerStart: r, defaultMarkerEnd: o }) { + const i = /* @__PURE__ */ new Set(); + return e.reduce((s, a) => ([a.markerStart || r, a.markerEnd || o].forEach((l) => { + if (l && typeof l == "object") { + const u = Vs(l, t); + i.has(u) || (s.push({ id: u, color: l.color || n, ...l }), i.add(u)); + } + }), s), []).sort((s, a) => s.id.localeCompare(a.id)); +} +function T0(e, t, n, r, o) { + let i = 0.5; + o === "start" ? i = 0 : o === "end" && (i = 1); + let s = [ + (e.x + e.width * i) * t.zoom + t.x, + e.y * t.zoom + t.y - r + ], a = [-100 * i, -100]; + switch (n) { + case $e.Right: + s = [ + (e.x + e.width) * t.zoom + t.x + r, + (e.y + e.height * i) * t.zoom + t.y + ], a = [0, -100 * i]; + break; + case $e.Bottom: + s[1] = (e.y + e.height) * t.zoom + t.y + r, a[1] = 0; + break; + case $e.Left: + s = [ + e.x * t.zoom + t.x - r, + (e.y + e.height * i) * t.zoom + t.y + ], a = [-100, -100 * i]; + break; + } + return `translate(${s[0]}px, ${s[1]}px) translate(${a[0]}%, ${a[1]}%)`; +} +const da = { + nodeOrigin: [0, 0], + nodeExtent: vi, + elevateNodesOnSelect: !0, + defaults: {} +}, H0 = { + ...da, + checkEquality: !0 +}; +function fa(e, t) { + const n = { ...e }; + for (const r in t) + t[r] !== void 0 && (n[r] = t[r]); + return n; +} +function V0(e, t, n) { + const r = fa(da, n); + for (const o of e.values()) + if (o.parentId) + ga(o, e, t, r); + else { + const i = Po(o, r.nodeOrigin), s = Or(o.extent) ? o.extent : r.nodeExtent, a = dr(i, s, tr(o)); + o.internals.positionAbsolute = a; + } +} +function lc(e, t, n, r) { + var a, l; + const o = fa(H0, r), i = new Map(t), s = o != null && o.elevateNodesOnSelect ? 1e3 : 0; + t.clear(), n.clear(); + for (const u of e) { + let c = i.get(u.id); + if (o.checkEquality && u === (c == null ? void 0 : c.internals.userNode)) + t.set(u.id, c); + else { + const f = Po(u, o.nodeOrigin), d = Or(u.extent) ? u.extent : o.nodeExtent, g = dr(f, d, tr(u)); + c = { + ...o.defaults, + ...u, + measured: { + width: (a = u.measured) == null ? void 0 : a.width, + height: (l = u.measured) == null ? void 0 : l.height + }, + internals: { + positionAbsolute: g, + // if user re-initializes the node or removes `measured` for whatever reason, we reset the handleBounds so that the node gets re-measured + handleBounds: u.measured ? c == null ? void 0 : c.internals.handleBounds : void 0, + z: uc(u, s), + userNode: u + } + }, t.set(u.id, c); + } + u.parentId && ga(c, t, n, r); + } +} +function D0(e, t) { + if (!e.parentId) + return; + const n = t.get(e.parentId); + n ? n.set(e.id, e) : t.set(e.parentId, /* @__PURE__ */ new Map([[e.id, e]])); +} +function ga(e, t, n, r) { + const { elevateNodesOnSelect: o, nodeOrigin: i, nodeExtent: s } = fa(da, r), a = e.parentId, l = t.get(a); + if (!l) { + console.warn(`Parent node ${a} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`); + return; + } + D0(e, n); + const u = o ? 1e3 : 0, { x: c, y: f, z: d } = A0(e, l, i, s, u), { positionAbsolute: g } = e.internals, p = c !== g.x || f !== g.y; + (p || d !== e.internals.z) && t.set(e.id, { + ...e, + internals: { + ...e.internals, + positionAbsolute: p ? { x: c, y: f } : g, + z: d + } + }); +} +function uc(e, t) { + return (Nn(e.zIndex) ? e.zIndex : 0) + (e.selected ? t : 0); +} +function A0(e, t, n, r, o) { + const { x: i, y: s } = t.internals.positionAbsolute, a = tr(e), l = Po(e, n), u = Or(e.extent) ? dr(l, e.extent, a) : l; + let c = dr({ x: i + u.x, y: s + u.y }, r, a); + e.extent === "parent" && (c = ec(c, a, t)); + const f = uc(e, o), d = t.internals.z ?? 0; + return { + x: c.x, + y: c.y, + z: d > f ? d : f + }; +} +function L0(e, t, n, r = [0, 0]) { + var s; + const o = [], i = /* @__PURE__ */ new Map(); + for (const a of e) { + const l = t.get(a.parentId); + if (!l) + continue; + const u = ((s = i.get(a.parentId)) == null ? void 0 : s.expandedRect) ?? Lr(l), c = nc(u, a.rect); + i.set(a.parentId, { expandedRect: c, parent: l }); + } + return i.size > 0 && i.forEach(({ expandedRect: a, parent: l }, u) => { + var _; + const c = l.internals.positionAbsolute, f = tr(l), d = l.origin ?? r, g = a.x < c.x ? Math.round(Math.abs(c.x - a.x)) : 0, p = a.y < c.y ? Math.round(Math.abs(c.y - a.y)) : 0, x = Math.max(f.width, Math.round(a.width)), C = Math.max(f.height, Math.round(a.height)), $ = (x - f.width) * d[0], m = (C - f.height) * d[1]; + (g > 0 || p > 0 || $ || m) && (o.push({ + id: u, + type: "position", + position: { + x: l.position.x - g + $, + y: l.position.y - p + m + } + }), (_ = n.get(u)) == null || _.forEach((v) => { + e.some((b) => b.id === v.id) || o.push({ + id: v.id, + type: "position", + position: { + x: v.position.x + g, + y: v.position.y + p + } + }); + })), (f.width < a.width || f.height < a.height || g || p) && o.push({ + id: u, + type: "dimensions", + setAttributes: !0, + dimensions: { + width: x + (g ? d[0] * g - $ : 0), + height: C + (p ? d[1] * p - m : 0) + } + }); + }), o; +} +function O0(e, t, n, r, o, i) { + const s = r == null ? void 0 : r.querySelector(".xyflow__viewport"); + let a = !1; + if (!s) + return { changes: [], updatedInternals: a }; + const l = [], u = window.getComputedStyle(s), { m22: c } = new window.DOMMatrixReadOnly(u.transform), f = []; + for (const d of e.values()) { + const g = t.get(d.id); + if (!g) + continue; + if (g.hidden) { + t.set(g.id, { + ...g, + internals: { + ...g.internals, + handleBounds: void 0 + } + }), a = !0; + continue; + } + const p = ca(d.nodeElement), x = g.measured.width !== p.width || g.measured.height !== p.height; + if (!!(p.width && p.height && (x || !g.internals.handleBounds || d.force))) { + const $ = d.nodeElement.getBoundingClientRect(), m = Or(g.extent) ? g.extent : i; + let { positionAbsolute: _ } = g.internals; + g.parentId && g.extent === "parent" ? _ = ec(_, p, t.get(g.parentId)) : m && (_ = dr(_, m, p)); + const v = { + ...g, + measured: p, + internals: { + ...g.internals, + positionAbsolute: _, + handleBounds: { + source: gl("source", d.nodeElement, $, c, g.id), + target: gl("target", d.nodeElement, $, c, g.id) + } + } + }; + t.set(g.id, v), g.parentId && ga(v, t, n, { nodeOrigin: o }), a = !0, x && (l.push({ + id: g.id, + type: "dimensions", + dimensions: p + }), g.expandParent && g.parentId && f.push({ + id: g.id, + parentId: g.parentId, + rect: Lr(v, o) + })); + } + } + if (f.length > 0) { + const d = L0(f, t, n, o); + l.push(...d); + } + return { changes: l, updatedInternals: a }; +} +async function I0({ delta: e, panZoom: t, transform: n, translateExtent: r, width: o, height: i }) { + if (!t || !e.x && !e.y) + return Promise.resolve(!1); + const s = await t.setViewportConstrained({ + x: n[0] + e.x, + y: n[1] + e.y, + zoom: n[2] + }, [ + [0, 0], + [o, i] + ], r), a = !!s && (s.x !== n[0] || s.y !== n[1] || s.k !== n[2]); + return Promise.resolve(a); +} +function _l(e, t, n, r, o, i) { + let s = o; + const a = r.get(s) || /* @__PURE__ */ new Map(); + r.set(s, a.set(n, t)), s = `${o}-${e}`; + const l = r.get(s) || /* @__PURE__ */ new Map(); + if (r.set(s, l.set(n, t)), i) { + s = `${o}-${e}-${i}`; + const u = r.get(s) || /* @__PURE__ */ new Map(); + r.set(s, u.set(n, t)); + } +} +function cc(e, t, n) { + e.clear(), t.clear(); + for (const r of n) { + const { source: o, target: i, sourceHandle: s = null, targetHandle: a = null } = r, l = { edgeId: r.id, source: o, target: i, sourceHandle: s, targetHandle: a }, u = `${o}-${s}--${i}-${a}`, c = `${i}-${a}--${o}-${s}`; + _l("source", l, c, e, o, s), _l("target", l, u, e, i, a), t.set(r.id, r); + } +} +function z0(e, t) { + if (e === null || t === null) + return !1; + const n = Array.isArray(e) ? e : [e], r = Array.isArray(t) ? t : [t]; + if (n.length !== r.length) + return !1; + for (let o = 0; o < n.length; o++) + if (n[o].id !== r[o].id || n[o].type !== r[o].type || !Object.is(n[o].data, r[o].data)) + return !1; + return !0; +} +function dc(e, t) { + if (!e.parentId) + return !1; + const n = t.get(e.parentId); + return n ? n.selected ? !0 : dc(n, t) : !1; +} +function xl(e, t, n) { + var o; + let r = e; + do { + if ((o = r == null ? void 0 : r.matches) != null && o.call(r, t)) + return !0; + if (r === n) + return !1; + r = r == null ? void 0 : r.parentElement; + } while (r); + return !1; +} +function R0(e, t, n, r) { + const o = /* @__PURE__ */ new Map(); + for (const [i, s] of e) + if ((s.selected || s.id === r) && (!s.parentId || !dc(s, e)) && (s.draggable || t && typeof s.draggable > "u")) { + const a = e.get(i); + a && o.set(i, { + id: i, + position: a.position || { x: 0, y: 0 }, + distance: { + x: n.x - a.internals.positionAbsolute.x, + y: n.y - a.internals.positionAbsolute.y + }, + extent: a.extent, + parentId: a.parentId, + origin: a.origin, + expandParent: a.expandParent, + internals: { + positionAbsolute: a.internals.positionAbsolute || { x: 0, y: 0 } + }, + measured: { + width: a.measured.width ?? 0, + height: a.measured.height ?? 0 + } + }); + } + return o; +} +function ss({ nodeId: e, dragItems: t, nodeLookup: n, dragging: r = !0 }) { + var s, a, l; + const o = []; + for (const [u, c] of t) { + const f = (s = n.get(u)) == null ? void 0 : s.internals.userNode; + f && o.push({ + ...f, + position: c.position, + dragging: r + }); + } + if (!e) + return [o[0], o]; + const i = (a = n.get(e)) == null ? void 0 : a.internals.userNode; + return [ + i ? { + ...i, + position: ((l = t.get(e)) == null ? void 0 : l.position) || i.position, + dragging: r + } : o[0], + o + ]; +} +function B0({ onNodeMouseDown: e, getStoreItems: t, onDragStart: n, onDrag: r, onDragStop: o }) { + let i = { x: null, y: null }, s = 0, a = /* @__PURE__ */ new Map(), l = !1, u = { x: 0, y: 0 }, c = null, f = !1, d = null, g = !1; + function p({ noDragClassName: C, handleSelector: $, domNode: m, isSelectable: _, nodeId: v, nodeClickDistance: b = 0 }) { + d = Kt(m); + function N({ x: V, y: A }, O) { + const { nodeLookup: R, nodeExtent: S, snapGrid: T, snapToGrid: k, nodeOrigin: P, onNodeDrag: H, onSelectionDrag: I, onError: B, updateNodePositions: F } = t(); + i = { x: V, y: A }; + let K = !1, ie = { x: 0, y: 0, x2: 0, y2: 0 }; + if (a.size > 1 && S) { + const ee = No(a); + ie = Ts(ee); + } + for (const [ee, W] of a) { + if (!R.has(ee)) + continue; + let ue = { x: V - W.distance.x, y: A - W.distance.y }; + k && (ue = la(ue, T)); + let me = [ + [S[0][0], S[0][1]], + [S[1][0], S[1][1]] + ]; + if (a.size > 1 && S && !W.extent) { + const { positionAbsolute: ze } = W.internals, G = ze.x - ie.x + S[0][0], se = ze.x + W.measured.width - ie.x2 + S[1][0], Te = ze.y - ie.y + S[0][1], Ae = ze.y + W.measured.height - ie.y2 + S[1][1]; + me = [ + [G, Te], + [se, Ae] + ]; + } + const { position: Ce, positionAbsolute: ge } = h0({ + nodeId: ee, + nextPosition: ue, + nodeLookup: R, + nodeExtent: me, + nodeOrigin: P, + onError: B + }); + K = K || W.position.x !== Ce.x || W.position.y !== Ce.y, W.position = Ce, W.internals.positionAbsolute = ge; + } + if (K && (F(a, !0), O && (r || H || !v && I))) { + const [ee, W] = ss({ + nodeId: v, + dragItems: a, + nodeLookup: R + }); + r == null || r(O, a, ee, W), H == null || H(O, ee, W), v || I == null || I(O, W); + } + } + async function E() { + if (!c) + return; + const { transform: V, panBy: A, autoPanSpeed: O, autoPanOnNodeDrag: R } = t(); + if (!R) { + l = !1, cancelAnimationFrame(s); + return; + } + const [S, T] = tc(u, c, O); + (S !== 0 || T !== 0) && (i.x = (i.x ?? 0) - S / V[2], i.y = (i.y ?? 0) - T / V[2], await A({ x: S, y: T }) && N(i, null)), s = requestAnimationFrame(E); + } + function M(V) { + var K; + const { nodeLookup: A, multiSelectionActive: O, nodesDraggable: R, transform: S, snapGrid: T, snapToGrid: k, selectNodesOnDrag: P, onNodeDragStart: H, onSelectionDragStart: I, unselectNodesAndEdges: B } = t(); + f = !0, (!P || !_) && !O && v && ((K = A.get(v)) != null && K.selected || B()), _ && P && v && (e == null || e(v)); + const F = is(V.sourceEvent, { transform: S, snapGrid: T, snapToGrid: k, containerBounds: c }); + if (i = F, a = R0(A, R, F, v), a.size > 0 && (n || H || !v && I)) { + const [ie, ee] = ss({ + nodeId: v, + dragItems: a, + nodeLookup: A + }); + n == null || n(V.sourceEvent, a, ie, ee), H == null || H(V.sourceEvent, ie, ee), v || I == null || I(V.sourceEvent, ee); + } + } + const D = wh().clickDistance(b).on("start", (V) => { + const { domNode: A, nodeDragThreshold: O, transform: R, snapGrid: S, snapToGrid: T } = t(); + c = (A == null ? void 0 : A.getBoundingClientRect()) || null, g = !1, O === 0 && M(V), i = is(V.sourceEvent, { transform: R, snapGrid: S, snapToGrid: T, containerBounds: c }), u = Hn(V.sourceEvent, c); + }).on("drag", (V) => { + const { autoPanOnNodeDrag: A, transform: O, snapGrid: R, snapToGrid: S, nodeDragThreshold: T, nodeLookup: k } = t(), P = is(V.sourceEvent, { transform: O, snapGrid: R, snapToGrid: S, containerBounds: c }); + if ((V.sourceEvent.type === "touchmove" && V.sourceEvent.touches.length > 1 || // if user deletes a node while dragging, we need to abort the drag to prevent errors + v && !k.has(v)) && (g = !0), !g) { + if (!l && A && f && (l = !0, E()), !f) { + const H = P.xSnapped - (i.x ?? 0), I = P.ySnapped - (i.y ?? 0); + Math.sqrt(H * H + I * I) > T && M(V); + } + (i.x !== P.xSnapped || i.y !== P.ySnapped) && a && f && (u = Hn(V.sourceEvent, c), N(P, V.sourceEvent)); + } + }).on("end", (V) => { + if (!(!f || g) && (l = !1, f = !1, cancelAnimationFrame(s), a.size > 0)) { + const { nodeLookup: A, updateNodePositions: O, onNodeDragStop: R, onSelectionDragStop: S } = t(); + if (O(a, !1), o || R || !v && S) { + const [T, k] = ss({ + nodeId: v, + dragItems: a, + nodeLookup: A, + dragging: !1 + }); + o == null || o(V.sourceEvent, a, T, k), R == null || R(V.sourceEvent, T, k), v || S == null || S(V.sourceEvent, k); + } + } + }).filter((V) => { + const A = V.target; + return !V.button && (!C || !xl(A, `.${C}`, m)) && (!$ || xl(A, $, m)); + }); + d.call(D); + } + function x() { + d == null || d.on(".drag", null); + } + return { + update: p, + destroy: x + }; +} +function Y0(e, t, n) { + const r = [], o = { + x: e.x - n, + y: e.y - n, + width: n * 2, + height: n * 2 + }; + for (const i of t.values()) + yo(o, Lr(i)) > 0 && r.push(i); + return r; +} +const Z0 = 250; +function X0(e, t, n, r) { + var a, l; + let o = [], i = 1 / 0; + const s = Y0(e, n, t + Z0); + for (const u of s) { + const c = [...((a = u.internals.handleBounds) == null ? void 0 : a.source) ?? [], ...((l = u.internals.handleBounds) == null ? void 0 : l.target) ?? []]; + for (const f of c) { + if (r.nodeId === f.nodeId && r.type === f.type && r.id === f.id) + continue; + const { x: d, y: g } = wo(u, f, f.position, !0), p = Math.sqrt(Math.pow(d - e.x, 2) + Math.pow(g - e.y, 2)); + p > t || (p < i ? (o = [{ ...f, x: d, y: g }], i = p) : p === i && o.push({ ...f, x: d, y: g })); + } + } + if (!o.length) + return null; + if (o.length > 1) { + const u = r.type === "source" ? "target" : "source"; + return o.find((c) => c.type === u) ?? o[0]; + } + return o[0]; +} +function fc(e, t, n, r, o, i = !1) { + var u, c, f; + const s = r.get(e); + if (!s) + return null; + const a = o === "strict" ? (u = s.internals.handleBounds) == null ? void 0 : u[t] : [...((c = s.internals.handleBounds) == null ? void 0 : c.source) ?? [], ...((f = s.internals.handleBounds) == null ? void 0 : f.target) ?? []], l = (n ? a == null ? void 0 : a.find((d) => d.id === n) : a == null ? void 0 : a[0]) ?? null; + return l && i ? { ...l, ...wo(s, l, l.position, !0) } : l; +} +function gc(e, t) { + return e || (t != null && t.classList.contains("target") ? "target" : t != null && t.classList.contains("source") ? "source" : null); +} +function F0(e, t) { + let n = null; + return t ? n = !0 : e && !t && (n = !1), n; +} +const hc = () => !0; +function W0(e, { connectionMode: t, connectionRadius: n, handleId: r, nodeId: o, edgeUpdaterType: i, isTarget: s, domNode: a, nodeLookup: l, lib: u, autoPanOnConnect: c, flowId: f, panBy: d, cancelConnection: g, onConnectStart: p, onConnect: x, onConnectEnd: C, isValidConnection: $ = hc, onReconnectEnd: m, updateConnection: _, getTransform: v, getFromHandle: b, autoPanSpeed: N }) { + const E = m0(e.target); + let M = 0, D; + const { x: V, y: A } = Hn(e), O = E == null ? void 0 : E.elementFromPoint(V, A), R = gc(i, O), S = a == null ? void 0 : a.getBoundingClientRect(); + if (!S || !R) + return; + const T = fc(o, R, r, l, t); + if (!T) + return; + let k = Hn(e, S), P = !1, H = null, I = !1, B = null; + function F() { + if (!c || !S) + return; + const [ge, ze] = tc(k, S, N); + d({ x: ge, y: ze }), M = requestAnimationFrame(F); + } + const K = { + ...T, + nodeId: o, + type: R, + position: T.position + }, ie = l.get(o), W = { + inProgress: !0, + isValid: null, + from: wo(ie, K, $e.Left, !0), + fromHandle: K, + fromPosition: K.position, + fromNode: ie, + to: k, + toHandle: null, + toPosition: al[K.position], + toNode: null + }; + _(W); + let ue = W; + p == null || p(e, { nodeId: o, handleId: r, handleType: R }); + function me(ge) { + if (!b() || !K) { + Ce(ge); + return; + } + const ze = v(); + k = Hn(ge, S), D = X0(Mo(k, ze, !1, [1, 1]), n, l, K), P || (F(), P = !0); + const G = vc(ge, { + handle: D, + connectionMode: t, + fromNodeId: o, + fromHandleId: r, + fromType: s ? "target" : "source", + isValidConnection: $, + doc: E, + lib: u, + flowId: f, + nodeLookup: l + }); + B = G.handleDomNode, H = G.connection, I = F0(!!D, G.isValid); + const se = { + // from stays the same + ...ue, + isValid: I, + to: D && I ? rc({ x: D.x, y: D.y }, ze) : k, + toHandle: G.toHandle, + toPosition: I && G.toHandle ? G.toHandle.position : al[K.position], + toNode: G.toHandle ? l.get(G.toHandle.nodeId) : null + }; + I && D && ue.toHandle && se.toHandle && ue.toHandle.type === se.toHandle.type && ue.toHandle.nodeId === se.toHandle.nodeId && ue.toHandle.id === se.toHandle.id && ue.to.x === se.to.x && ue.to.y === se.to.y || (_(se), ue = se); + } + function Ce(ge) { + (D || B) && H && I && (x == null || x(H)); + const { inProgress: ze, ...G } = ue, se = { + ...G, + toPosition: ue.toHandle ? ue.toPosition : null + }; + C == null || C(ge, se), i && (m == null || m(ge, se)), g(), cancelAnimationFrame(M), P = !1, I = !1, H = null, B = null, E.removeEventListener("mousemove", me), E.removeEventListener("mouseup", Ce), E.removeEventListener("touchmove", me), E.removeEventListener("touchend", Ce); + } + E.addEventListener("mousemove", me), E.addEventListener("mouseup", Ce), E.addEventListener("touchmove", me), E.addEventListener("touchend", Ce); +} +function vc(e, { handle: t, connectionMode: n, fromNodeId: r, fromHandleId: o, fromType: i, doc: s, lib: a, flowId: l, isValidConnection: u = hc, nodeLookup: c }) { + const f = i === "target", d = t ? s.querySelector(`.${a}-flow__handle[data-id="${l}-${t == null ? void 0 : t.nodeId}-${t == null ? void 0 : t.id}-${t == null ? void 0 : t.type}"]`) : null, { x: g, y: p } = Hn(e), x = s.elementFromPoint(g, p), C = x != null && x.classList.contains(`${a}-flow__handle`) ? x : d, $ = { + handleDomNode: C, + isValid: !1, + connection: null, + toHandle: null + }; + if (C) { + const m = gc(void 0, C), _ = C.getAttribute("data-nodeid"), v = C.getAttribute("data-handleid"), b = C.classList.contains("connectable"), N = C.classList.contains("connectableend"); + if (!_ || !m) + return $; + const E = { + source: f ? _ : r, + sourceHandle: f ? v : o, + target: f ? r : _, + targetHandle: f ? o : v + }; + $.connection = E; + const D = b && N && (n === cr.Strict ? f && m === "source" || !f && m === "target" : _ !== r || v !== o); + $.isValid = D && u(E), $.toHandle = fc(_, m, v, c, n, !1); + } + return $; +} +const K0 = { + onPointerDown: W0, + isValid: vc +}; +function q0({ domNode: e, panZoom: t, getTransform: n, getViewScale: r }) { + const o = Kt(e); + function i({ translateExtent: a, width: l, height: u, zoomStep: c = 10, pannable: f = !0, zoomable: d = !0, inversePan: g = !1 }) { + const p = (_) => { + const v = n(); + if (_.sourceEvent.type !== "wheel" || !t) + return; + const b = -_.sourceEvent.deltaY * (_.sourceEvent.deltaMode === 1 ? 0.05 : _.sourceEvent.deltaMode ? 1 : 2e-3) * c, N = v[2] * Math.pow(2, b); + t.scaleTo(N); + }; + let x = [0, 0]; + const C = (_) => { + (_.sourceEvent.type === "mousedown" || _.sourceEvent.type === "touchstart") && (x = [ + _.sourceEvent.clientX ?? _.sourceEvent.touches[0].clientX, + _.sourceEvent.clientY ?? _.sourceEvent.touches[0].clientY + ]); + }, $ = (_) => { + const v = n(); + if (_.sourceEvent.type !== "mousemove" && _.sourceEvent.type !== "touchmove" || !t) + return; + const b = [ + _.sourceEvent.clientX ?? _.sourceEvent.touches[0].clientX, + _.sourceEvent.clientY ?? _.sourceEvent.touches[0].clientY + ], N = [b[0] - x[0], b[1] - x[1]]; + x = b; + const E = r() * Math.max(v[2], Math.log(v[2])) * (g ? -1 : 1), M = { + x: v[0] - N[0] * E, + y: v[1] - N[1] * E + }, D = [ + [0, 0], + [l, u] + ]; + t.setViewportConstrained({ + x: M.x, + y: M.y, + zoom: v[2] + }, D, a); + }, m = ju().on("start", C).on("zoom", f ? $ : null).on("zoom.wheel", d ? p : null); + o.call(m, {}); + } + function s() { + o.on("zoom", null); + } + return { + update: i, + destroy: s, + pointer: Qt + }; +} +const G0 = (e, t) => e.x !== t.x || e.y !== t.y || e.zoom !== t.k, Fi = (e) => ({ + x: e.x, + y: e.y, + zoom: e.k +}), as = ({ x: e, y: t, zoom: n }) => Yi.translate(e, t).scale(n), wr = (e, t) => e.target.closest(`.${t}`), pc = (e, t) => t === 2 && Array.isArray(e) && e.includes(2), ls = (e, t = 0, n = () => { +}) => { + const r = typeof t == "number" && t > 0; + return r || n(), r ? e.transition().duration(t).on("end", n) : e; +}, mc = (e) => { + const t = e.ctrlKey && yi() ? 10 : 1; + return -e.deltaY * (e.deltaMode === 1 ? 0.05 : e.deltaMode ? 1 : 2e-3) * t; +}; +function U0({ zoomPanValues: e, noWheelClassName: t, d3Selection: n, d3Zoom: r, panOnScrollMode: o, panOnScrollSpeed: i, zoomOnPinch: s, onPanZoomStart: a, onPanZoom: l, onPanZoomEnd: u }) { + return (c) => { + if (wr(c, t)) + return !1; + c.preventDefault(), c.stopImmediatePropagation(); + const f = n.property("__zoom").k || 1; + if (c.ctrlKey && s) { + const C = Qt(c), $ = mc(c), m = f * Math.pow(2, $); + r.scaleTo(n, m, C, c); + return; + } + const d = c.deltaMode === 1 ? 20 : 1; + let g = o === qn.Vertical ? 0 : c.deltaX * d, p = o === qn.Horizontal ? 0 : c.deltaY * d; + !yi() && c.shiftKey && o !== qn.Vertical && (g = c.deltaY * d, p = 0), r.translateBy( + n, + -(g / f) * i, + -(p / f) * i, + // @ts-ignore + { internal: !0 } + ); + const x = Fi(n.property("__zoom")); + clearTimeout(e.panScrollTimeout), e.isPanScrolling || (e.isPanScrolling = !0, a == null || a(c, x)), e.isPanScrolling && (l == null || l(c, x), e.panScrollTimeout = setTimeout(() => { + u == null || u(c, x), e.isPanScrolling = !1; + }, 150)); + }; +} +function j0({ noWheelClassName: e, preventScrolling: t, d3ZoomHandler: n }) { + return function(r, o) { + if (!t && r.type === "wheel" && !r.ctrlKey || wr(r, e)) + return null; + r.preventDefault(), n.call(this, r, o); + }; +} +function J0({ zoomPanValues: e, onDraggingChange: t, onPanZoomStart: n }) { + return (r) => { + var i, s, a; + if ((i = r.sourceEvent) != null && i.internal) + return; + const o = Fi(r.transform); + e.mouseButton = ((s = r.sourceEvent) == null ? void 0 : s.button) || 0, e.isZoomingOrPanning = !0, e.prevViewport = o, ((a = r.sourceEvent) == null ? void 0 : a.type) === "mousedown" && t(!0), n && (n == null || n(r.sourceEvent, o)); + }; +} +function Q0({ zoomPanValues: e, panOnDrag: t, onPaneContextMenu: n, onTransformChange: r, onPanZoom: o }) { + return (i) => { + var s, a; + e.usedRightMouseButton = !!(n && pc(t, e.mouseButton ?? 0)), (s = i.sourceEvent) != null && s.sync || r([i.transform.x, i.transform.y, i.transform.k]), o && !((a = i.sourceEvent) != null && a.internal) && (o == null || o(i.sourceEvent, Fi(i.transform))); + }; +} +function e2({ zoomPanValues: e, panOnDrag: t, panOnScroll: n, onDraggingChange: r, onPanZoomEnd: o, onPaneContextMenu: i }) { + return (s) => { + var a; + if (!((a = s.sourceEvent) != null && a.internal) && (e.isZoomingOrPanning = !1, i && pc(t, e.mouseButton ?? 0) && !e.usedRightMouseButton && s.sourceEvent && i(s.sourceEvent), e.usedRightMouseButton = !1, r(!1), o && G0(e.prevViewport, s.transform))) { + const l = Fi(s.transform); + e.prevViewport = l, clearTimeout(e.timerId), e.timerId = setTimeout( + () => { + o == null || o(s.sourceEvent, l); + }, + // we need a setTimeout for panOnScroll to supress multiple end events fired during scroll + n ? 150 : 0 + ); + } + }; +} +function t2({ zoomActivationKeyPressed: e, zoomOnScroll: t, zoomOnPinch: n, panOnDrag: r, panOnScroll: o, zoomOnDoubleClick: i, userSelectionActive: s, noWheelClassName: a, noPanClassName: l, lib: u }) { + return (c) => { + var p; + const f = e || t, d = n && c.ctrlKey; + if (c.button === 1 && c.type === "mousedown" && (wr(c, `${u}-flow__node`) || wr(c, `${u}-flow__edge`))) + return !0; + if (!r && !f && !o && !i && !n || s || wr(c, a) && c.type === "wheel" || wr(c, l) && (c.type !== "wheel" || o && c.type === "wheel" && !e) || !n && c.ctrlKey && c.type === "wheel") + return !1; + if (!n && c.type === "touchstart" && ((p = c.touches) == null ? void 0 : p.length) > 1) + return c.preventDefault(), !1; + if (!f && !o && !d && c.type === "wheel" || !r && (c.type === "mousedown" || c.type === "touchstart") || Array.isArray(r) && !r.includes(c.button) && c.type === "mousedown") + return !1; + const g = Array.isArray(r) && r.includes(c.button) || !c.button || c.button <= 1; + return (!c.ctrlKey || c.type === "wheel") && g; + }; +} +function n2({ domNode: e, minZoom: t, maxZoom: n, paneClickDistance: r, translateExtent: o, viewport: i, onPanZoom: s, onPanZoomStart: a, onPanZoomEnd: l, onDraggingChange: u }) { + const c = { + isZoomingOrPanning: !1, + usedRightMouseButton: !1, + prevViewport: { x: 0, y: 0, zoom: 0 }, + mouseButton: 0, + timerId: void 0, + panScrollTimeout: void 0, + isPanScrolling: !1 + }, f = e.getBoundingClientRect(), d = ju().clickDistance(!Nn(r) || r < 0 ? 0 : r).scaleExtent([t, n]).translateExtent(o), g = Kt(e).call(d); + _({ + x: i.x, + y: i.y, + zoom: Ar(i.zoom, t, n) + }, [ + [0, 0], + [f.width, f.height] + ], o); + const p = g.on("wheel.zoom"), x = g.on("dblclick.zoom"); + d.wheelDelta(mc); + function C(O, R) { + return g ? new Promise((S) => { + d == null || d.transform(ls(g, R == null ? void 0 : R.duration, () => S(!0)), O); + }) : Promise.resolve(!1); + } + function $({ noWheelClassName: O, noPanClassName: R, onPaneContextMenu: S, userSelectionActive: T, panOnScroll: k, panOnDrag: P, panOnScrollMode: H, panOnScrollSpeed: I, preventScrolling: B, zoomOnPinch: F, zoomOnScroll: K, zoomOnDoubleClick: ie, zoomActivationKeyPressed: ee, lib: W, onTransformChange: ue }) { + T && !c.isZoomingOrPanning && m(); + const Ce = k && !ee && !T ? U0({ + zoomPanValues: c, + noWheelClassName: O, + d3Selection: g, + d3Zoom: d, + panOnScrollMode: H, + panOnScrollSpeed: I, + zoomOnPinch: F, + onPanZoomStart: a, + onPanZoom: s, + onPanZoomEnd: l + }) : j0({ + noWheelClassName: O, + preventScrolling: B, + d3ZoomHandler: p + }); + if (g.on("wheel.zoom", Ce, { passive: !1 }), !T) { + const ze = J0({ + zoomPanValues: c, + onDraggingChange: u, + onPanZoomStart: a + }); + d.on("start", ze); + const G = Q0({ + zoomPanValues: c, + panOnDrag: P, + onPaneContextMenu: !!S, + onPanZoom: s, + onTransformChange: ue + }); + d.on("zoom", G); + const se = e2({ + zoomPanValues: c, + panOnDrag: P, + panOnScroll: k, + onPaneContextMenu: S, + onPanZoomEnd: l, + onDraggingChange: u + }); + d.on("end", se); + } + const ge = t2({ + zoomActivationKeyPressed: ee, + panOnDrag: P, + zoomOnScroll: K, + panOnScroll: k, + zoomOnDoubleClick: ie, + zoomOnPinch: F, + userSelectionActive: T, + noPanClassName: R, + noWheelClassName: O, + lib: W + }); + d.filter(ge), ie ? g.on("dblclick.zoom", x) : g.on("dblclick.zoom", null); + } + function m() { + d.on("zoom", null); + } + async function _(O, R, S) { + const T = as(O), k = d == null ? void 0 : d.constrain()(T, R, S); + return k && await C(k), new Promise((P) => P(k)); + } + async function v(O, R) { + const S = as(O); + return await C(S, R), new Promise((T) => T(S)); + } + function b(O) { + if (g) { + const R = as(O), S = g.property("__zoom"); + (S.k !== O.zoom || S.x !== O.x || S.y !== O.y) && (d == null || d.transform(g, R, null, { sync: !0 })); + } + } + function N() { + const O = g ? Uu(g.node()) : { x: 0, y: 0, k: 1 }; + return { x: O.x, y: O.y, zoom: O.k }; + } + function E(O, R) { + return g ? new Promise((S) => { + d == null || d.scaleTo(ls(g, R == null ? void 0 : R.duration, () => S(!0)), O); + }) : Promise.resolve(!1); + } + function M(O, R) { + return g ? new Promise((S) => { + d == null || d.scaleBy(ls(g, R == null ? void 0 : R.duration, () => S(!0)), O); + }) : Promise.resolve(!1); + } + function D(O) { + d == null || d.scaleExtent(O); + } + function V(O) { + d == null || d.translateExtent(O); + } + function A(O) { + const R = !Nn(O) || O < 0 ? 0 : O; + d == null || d.clickDistance(R); + } + return { + update: $, + destroy: m, + setViewport: v, + setViewportConstrained: _, + getViewport: N, + scaleTo: E, + scaleBy: M, + setScaleExtent: D, + setTranslateExtent: V, + syncViewport: b, + setClickDistance: A + }; +} +var bl; +(function(e) { + e.Line = "line", e.Handle = "handle"; +})(bl || (bl = {})); +var r2 = /* @__PURE__ */ ne('<div role="button" tabindex="-1"><!></div>'); +function Qn(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(ie, "$connectable", n), i = () => Q(Ce, "$connectionRadius", n), s = () => Q(ue, "$domNode", n), a = () => Q(me, "$nodeLookup", n), l = () => Q(W, "$connectionMode", n), u = () => Q(G, "$lib", n), c = () => Q(Fe, "$autoPanOnConnect", n), f = () => Q(Oe, "$flowId", n), d = () => Q(ze, "$isValidConnectionStore", n), g = () => Q(Te, "$onedgecreate", n), p = () => Q(oe, "$onConnectAction", n), x = () => Q(ve, "$onConnectStartAction", n), C = () => Q(xe, "$onConnectEndAction", n), $ = () => Q(ge, "$viewport", n), m = () => Q(ct, "$connection", n), _ = () => Q(Le, "$edges", n), v = () => Q(Qe, "$connectionLookup", n), b = re(), N = re(), E = re(), M = re(), D = re(), V = re(), A = re(), O = re(); + let R = w(t, "id", 12, void 0), S = w(t, "type", 12, "source"), T = w(t, "position", 28, () => $e.Top), k = w(t, "style", 12, void 0), P = w(t, "isValidConnection", 12, void 0), H = w(t, "onconnect", 12, void 0), I = w(t, "ondisconnect", 12, void 0), B = w(t, "isConnectable", 12, void 0), F = w(t, "class", 12, void 0); + const K = ar("svelteflow__node_id"), ie = ar("svelteflow__node_connectable"), ee = Ue(), { + connectionMode: W, + domNode: ue, + nodeLookup: me, + connectionRadius: Ce, + viewport: ge, + isValidConnection: ze, + lib: G, + addEdge: se, + onedgecreate: Te, + panBy: Ae, + cancelConnection: Xe, + updateConnection: te, + autoPanOnConnect: Fe, + edges: Le, + connectionLookup: Qe, + onconnect: oe, + onconnectstart: ve, + onconnectend: xe, + flowId: Oe, + connection: ct + } = ee; + function lt(Ne) { + const rt = ic(Ne); + (rt && Ne.button === 0 || !rt) && K0.onPointerDown(Ne, { + handleId: h(E), + nodeId: K, + isTarget: h(b), + connectionRadius: i(), + domNode: s(), + nodeLookup: a(), + connectionMode: l(), + lib: u(), + autoPanOnConnect: c(), + flowId: f(), + isValidConnection: P() ?? d(), + updateConnection: te, + cancelConnection: Xe, + panBy: Ae, + onConnect: (ye) => { + var at; + const ot = g() ? g()(ye) : ye; + ot && (se(ot), (at = p()) == null || at(ye)); + }, + onConnectStart: (ye, ot) => { + var at; + (at = x()) == null || at(ye, { + nodeId: ot.nodeId, + handleId: ot.handleId, + handleType: ot.handleType + }); + }, + onConnectEnd: (ye, ot) => { + var at; + (at = C()) == null || at(ye, ot); + }, + getTransform: () => [ + $().x, + $().y, + $().zoom + ], + getFromHandle: () => m().fromHandle + }); + } + let J = re(null), Re = re(); + he(() => j(S()), () => { + U(b, S() === "target"); + }), he( + () => (j(B()), o()), + () => { + U(N, B() !== void 0 ? B() : o()); + } + ), he(() => j(R()), () => { + U(E, R() || null); + }), he( + () => (j(H()), j(I()), _(), v(), j(S()), j(R())), + () => { + (H() || I()) && (_(), U(Re, v().get(`${K}-${S()}${R() ? `-${R()}` : ""}`))); + } + ), he( + () => (h(J), h(Re), j(I()), j(H())), + () => { + if (h(J) && !u0(h(Re), h(J))) { + const Ne = h(Re) ?? /* @__PURE__ */ new Map(); + ll(h(J), Ne, I()), ll(Ne, h(J), H()); + } + U(J, h(Re) ?? /* @__PURE__ */ new Map()); + } + ), he(() => m(), () => { + U(M, !!m().fromHandle); + }), he( + () => (m(), j(S()), h(E)), + () => { + var Ne, rt, ye; + U(D, ((Ne = m().fromHandle) == null ? void 0 : Ne.nodeId) === K && ((rt = m().fromHandle) == null ? void 0 : rt.type) === S() && ((ye = m().fromHandle) == null ? void 0 : ye.id) === h(E)); + } + ), he( + () => (m(), j(S()), h(E)), + () => { + var Ne, rt, ye; + U(V, ((Ne = m().toHandle) == null ? void 0 : Ne.nodeId) === K && ((rt = m().toHandle) == null ? void 0 : rt.type) === S() && ((ye = m().toHandle) == null ? void 0 : ye.id) === h(E)); + } + ), he( + () => (l(), m(), j(S()), h(E)), + () => { + var Ne, rt, ye; + U(A, l() === cr.Strict ? ((Ne = m().fromHandle) == null ? void 0 : Ne.type) !== S() : K !== ((rt = m().fromHandle) == null ? void 0 : rt.nodeId) || h(E) !== ((ye = m().fromHandle) == null ? void 0 : ye.id)); + } + ), he(() => (h(V), m()), () => { + U(O, h(V) && m().isValid); + }), gt(), He(); + var le = r2(); + ce(le, "data-nodeid", K); + let fn; + var Ut = X(le); + pt(Ut, t, "default", {}), Z(le), Ee( + (Ne) => { + ce(le, "data-handleid", h(E)), ce(le, "data-handlepos", T()), ce(le, "data-id", `${f() ?? ""}-${K ?? ""}-${R() || ""}-${S() ?? ""}`), fn = kt(le, 1, bn(Ne), null, fn, { + valid: h(O), + connectingto: h(V), + connectingfrom: h(D), + source: !h(b), + target: h(b), + connectablestart: h(N), + connectableend: h(N), + connectable: h(N), + connectionindicator: h(N) && (!h(M) || h(A)) + }), ce(le, "style", k()); + }, + [ + () => Et([ + "svelte-flow__handle", + `svelte-flow__handle-${T()}`, + "nodrag", + "nopan", + T(), + F() + ]) + ], + pe + ), Ye("mousedown", le, lt), Ye("touchstart", le, lt), L(e, le); + var gn = fe({ + get id() { + return R(); + }, + set id(Ne) { + R(Ne), y(); + }, + get type() { + return S(); + }, + set type(Ne) { + S(Ne), y(); + }, + get position() { + return T(); + }, + set position(Ne) { + T(Ne), y(); + }, + get style() { + return k(); + }, + set style(Ne) { + k(Ne), y(); + }, + get isValidConnection() { + return P(); + }, + set isValidConnection(Ne) { + P(Ne), y(); + }, + get onconnect() { + return H(); + }, + set onconnect(Ne) { + H(Ne), y(); + }, + get ondisconnect() { + return I(); + }, + set ondisconnect(Ne) { + I(Ne), y(); + }, + get isConnectable() { + return B(); + }, + set isConnectable(Ne) { + B(Ne), y(); + }, + get class() { + return F(); + }, + set class(Ne) { + F(Ne), y(); + } + }); + return r(), gn; +} +ae( + Qn, + { + id: {}, + type: {}, + position: {}, + style: {}, + isValidConnection: {}, + onconnect: {}, + ondisconnect: {}, + isConnectable: {}, + class: {} + }, + ["default"], + [], + !0 +); +var o2 = /* @__PURE__ */ ne("<!> <!>", 1); +function _i(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, ["data", "targetPosition", "sourcePosition"]), de(t, !1); + let r = w(t, "data", 28, () => ({ label: "Node" })), o = w(t, "targetPosition", 12, void 0), i = w(t, "sourcePosition", 12, void 0); + He(); + var s = o2(), a = be(s); + const l = /* @__PURE__ */ pe(() => o() ?? $e.Top); + Qn(a, { + type: "target", + get position() { + return h(l); + } + }); + var u = z(a), c = z(u); + const f = /* @__PURE__ */ pe(() => i() ?? $e.Bottom); + return Qn(c, { + type: "source", + get position() { + return h(f); + } + }), Ee(() => { + var d; + return Rt(u, ` ${((d = r()) == null ? void 0 : d.label) ?? ""} `); + }), L(e, s), fe({ + get data() { + return r(); + }, + set data(d) { + r(d), y(); + }, + get targetPosition() { + return o(); + }, + set targetPosition(d) { + o(d), y(); + }, + get sourcePosition() { + return i(); + }, + set sourcePosition(d) { + i(d), y(); + } + }); +} +ae( + _i, + { + data: {}, + targetPosition: {}, + sourcePosition: {} + }, + [], + [], + !0 +); +var i2 = /* @__PURE__ */ ne(" <!>", 1); +function yc(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, ["data", "sourcePosition"]), de(t, !1); + let r = w(t, "data", 28, () => ({ label: "Node" })), o = w(t, "sourcePosition", 12, void 0); + He(), Se(); + var i = i2(), s = be(i), a = z(s); + const l = /* @__PURE__ */ pe(() => o() ?? $e.Bottom); + return Qn(a, { + type: "source", + get position() { + return h(l); + } + }), Ee(() => { + var u; + return Rt(s, `${((u = r()) == null ? void 0 : u.label) ?? ""} `); + }), L(e, i), fe({ + get data() { + return r(); + }, + set data(u) { + r(u), y(); + }, + get sourcePosition() { + return o(); + }, + set sourcePosition(u) { + o(u), y(); + } + }); +} +ae(yc, { data: {}, sourcePosition: {} }, [], [], !0); +var s2 = /* @__PURE__ */ ne(" <!>", 1); +function wc(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, ["data", "targetPosition"]), de(t, !1); + let r = w(t, "data", 28, () => ({ label: "Node" })), o = w(t, "targetPosition", 12, void 0); + He(), Se(); + var i = s2(), s = be(i), a = z(s); + const l = /* @__PURE__ */ pe(() => o() ?? $e.Top); + return Qn(a, { + type: "target", + get position() { + return h(l); + } + }), Ee(() => { + var u; + return Rt(s, `${((u = r()) == null ? void 0 : u.label) ?? ""} `); + }), L(e, i), fe({ + get data() { + return r(); + }, + set data(u) { + r(u), y(); + }, + get targetPosition() { + return o(); + }, + set targetPosition(u) { + o(u), y(); + } + }); +} +ae(wc, { data: {}, targetPosition: {} }, [], [], !0); +function _c(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, []); +} +ae(_c, {}, [], [], !0); +function Cl(e, t, n) { + if (!t) + return; + const r = n ? t.querySelector(n) : t; + r && r.appendChild(e); +} +function kr(e, { target: t, domNode: n }) { + return Cl(e, n, t), { + async update({ target: r, domNode: o }) { + Cl(e, o, r); + }, + destroy() { + e.parentNode && e.parentNode.removeChild(e); + } + }; +} +var a2 = /* @__PURE__ */ ne("<div><!></div>"); +function xc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(i, "$domNode", n), { domNode: i } = Ue(); + He(); + var s = a2(), a = X(s); + pt(a, t, "default", {}), Z(s), vt(s, (l, u) => kr == null ? void 0 : kr(l, u), () => ({ + target: ".svelte-flow__edgelabel-renderer", + domNode: o() + })), L(e, s), fe(), r(); +} +ae(xc, {}, ["default"], [], !0); +function bc() { + const { edgeLookup: e, selectionRect: t, selectionRectMode: n, multiselectionKeyPressed: r, addSelectedEdges: o, unselectNodesAndEdges: i, elementsSelectable: s } = Ue(); + return (a) => { + const l = q(e).get(a); + if (!l) { + console.warn("012", Dr.error012(a)); + return; + } + (l.selectable || q(s) && typeof l.selectable > "u") && (t.set(null), n.set(null), l.selected ? l.selected && q(r) && i({ nodes: [], edges: [l] }) : o([a])); + }; +} +var l2 = /* @__PURE__ */ ne('<div class="svelte-flow__edge-label" role="button" tabindex="-1"><!></div>'); +function Cc(e, t) { + de(t, !1); + let n = w(t, "style", 12, void 0), r = w(t, "x", 12, void 0), o = w(t, "y", 12, void 0); + const i = bc(), s = ar("svelteflow__edge_id"); + return He(), xc(e, { + children: (a, l) => { + var u = l2(), c = X(u); + pt(c, t, "default", {}), Z(u), Ee(() => { + ce(u, "style", "pointer-events: all;" + n()), st(u, "transform", `translate(-50%, -50%) translate(${r() ?? ""}px,${o() ?? ""}px)`); + }), Ye("keyup", u, () => { + }), Ye("click", u, () => { + s && i(s); + }), L(a, u); + }, + $$slots: { default: !0 } + }), fe({ + get style() { + return n(); + }, + set style(a) { + n(a), y(); + }, + get x() { + return r(); + }, + set x(a) { + r(a), y(); + }, + get y() { + return o(); + }, + set y(a) { + o(a), y(); + } + }); +} +ae(Cc, { style: {}, x: {}, y: {} }, ["default"], [], !0); +var u2 = /* @__PURE__ */ _e('<path fill="none" class="svelte-flow__edge-interaction"></path>'), c2 = /* @__PURE__ */ _e('<path fill="none"></path><!><!>', 1); +function To(e, t) { + de(t, !1); + let n = w(t, "id", 12, void 0), r = w(t, "path", 12), o = w(t, "label", 12, void 0), i = w(t, "labelX", 12, void 0), s = w(t, "labelY", 12, void 0), a = w(t, "labelStyle", 12, void 0), l = w(t, "markerStart", 12, void 0), u = w(t, "markerEnd", 12, void 0), c = w(t, "style", 12, void 0), f = w(t, "interactionWidth", 12, 20), d = w(t, "class", 12, void 0), g = f() === void 0 ? 20 : f(); + He(); + var p = c2(), x = be(p), C = z(x); + { + var $ = (v) => { + var b = u2(); + ce(b, "stroke-opacity", 0), ce(b, "stroke-width", g), Ee(() => ce(b, "d", r())), L(v, b); + }; + ke(C, (v) => { + g && v($); + }); + } + var m = z(C); + { + var _ = (v) => { + Cc(v, { + get x() { + return i(); + }, + get y() { + return s(); + }, + get style() { + return a(); + }, + children: (b, N) => { + Se(); + var E = Ie(); + Ee(() => Rt(E, o())), L(b, E); + }, + $$slots: { default: !0 } + }); + }; + ke(m, (v) => { + o() && v(_); + }); + } + return Ee( + (v) => { + ce(x, "id", n()), ce(x, "d", r()), kt(x, 0, bn(v)), ce(x, "marker-start", l()), ce(x, "marker-end", u()), ce(x, "style", c()); + }, + [ + () => Et(["svelte-flow__edge-path", d()]) + ], + pe + ), L(e, p), fe({ + get id() { + return n(); + }, + set id(v) { + n(v), y(); + }, + get path() { + return r(); + }, + set path(v) { + r(v), y(); + }, + get label() { + return o(); + }, + set label(v) { + o(v), y(); + }, + get labelX() { + return i(); + }, + set labelX(v) { + i(v), y(); + }, + get labelY() { + return s(); + }, + set labelY(v) { + s(v), y(); + }, + get labelStyle() { + return a(); + }, + set labelStyle(v) { + a(v), y(); + }, + get markerStart() { + return l(); + }, + set markerStart(v) { + l(v), y(); + }, + get markerEnd() { + return u(); + }, + set markerEnd(v) { + u(v), y(); + }, + get style() { + return c(); + }, + set style(v) { + c(v), y(); + }, + get interactionWidth() { + return f(); + }, + set interactionWidth(v) { + f(v), y(); + }, + get class() { + return d(); + }, + set class(v) { + d(v), y(); + } + }); +} +ae( + To, + { + id: {}, + path: {}, + label: {}, + labelX: {}, + labelY: {}, + labelStyle: {}, + markerStart: {}, + markerEnd: {}, + style: {}, + interactionWidth: {}, + class: {} + }, + [], + [], + !0 +); +function xi(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, [ + "label", + "labelStyle", + "style", + "markerStart", + "markerEnd", + "interactionWidth", + "sourceX", + "sourceY", + "sourcePosition", + "targetX", + "targetY", + "targetPosition" + ]), de(t, !1); + const r = re(), o = re(), i = re(); + let s = w(t, "label", 12, void 0), a = w(t, "labelStyle", 12, void 0), l = w(t, "style", 12, void 0), u = w(t, "markerStart", 12, void 0), c = w(t, "markerEnd", 12, void 0), f = w(t, "interactionWidth", 12, void 0), d = w(t, "sourceX", 12), g = w(t, "sourceY", 12), p = w(t, "sourcePosition", 12), x = w(t, "targetX", 12), C = w(t, "targetY", 12), $ = w(t, "targetPosition", 12); + return he( + () => (h(r), h(o), h(i), j(d()), j(g()), j(x()), j(C()), j(p()), j($())), + () => { + ((m) => (U(r, m[0]), U(o, m[1]), U(i, m[2])))(sc({ + sourceX: d(), + sourceY: g(), + targetX: x(), + targetY: C(), + sourcePosition: p(), + targetPosition: $() + })); + } + ), gt(), He(), To(e, { + get path() { + return h(r); + }, + get labelX() { + return h(o); + }, + get labelY() { + return h(i); + }, + get label() { + return s(); + }, + get labelStyle() { + return a(); + }, + get markerStart() { + return u(); + }, + get markerEnd() { + return c(); + }, + get interactionWidth() { + return f(); + }, + get style() { + return l(); + } + }), fe({ + get label() { + return s(); + }, + set label(m) { + s(m), y(); + }, + get labelStyle() { + return a(); + }, + set labelStyle(m) { + a(m), y(); + }, + get style() { + return l(); + }, + set style(m) { + l(m), y(); + }, + get markerStart() { + return u(); + }, + set markerStart(m) { + u(m), y(); + }, + get markerEnd() { + return c(); + }, + set markerEnd(m) { + c(m), y(); + }, + get interactionWidth() { + return f(); + }, + set interactionWidth(m) { + f(m), y(); + }, + get sourceX() { + return d(); + }, + set sourceX(m) { + d(m), y(); + }, + get sourceY() { + return g(); + }, + set sourceY(m) { + g(m), y(); + }, + get sourcePosition() { + return p(); + }, + set sourcePosition(m) { + p(m), y(); + }, + get targetX() { + return x(); + }, + set targetX(m) { + x(m), y(); + }, + get targetY() { + return C(); + }, + set targetY(m) { + C(m), y(); + }, + get targetPosition() { + return $(); + }, + set targetPosition(m) { + $(m), y(); + } + }); +} +ae( + xi, + { + label: {}, + labelStyle: {}, + style: {}, + markerStart: {}, + markerEnd: {}, + interactionWidth: {}, + sourceX: {}, + sourceY: {}, + sourcePosition: {}, + targetX: {}, + targetY: {}, + targetPosition: {} + }, + [], + [], + !0 +); +function kc(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, [ + "label", + "labelStyle", + "style", + "markerStart", + "markerEnd", + "interactionWidth", + "sourceX", + "sourceY", + "sourcePosition", + "targetX", + "targetY", + "targetPosition" + ]), de(t, !1); + const r = re(), o = re(), i = re(); + let s = w(t, "label", 12, void 0), a = w(t, "labelStyle", 12, void 0), l = w(t, "style", 12, void 0), u = w(t, "markerStart", 12, void 0), c = w(t, "markerEnd", 12, void 0), f = w(t, "interactionWidth", 12, void 0), d = w(t, "sourceX", 12), g = w(t, "sourceY", 12), p = w(t, "sourcePosition", 12), x = w(t, "targetX", 12), C = w(t, "targetY", 12), $ = w(t, "targetPosition", 12); + return he( + () => (h(r), h(o), h(i), j(d()), j(g()), j(x()), j(C()), j(p()), j($())), + () => { + ((m) => (U(r, m[0]), U(o, m[1]), U(i, m[2])))(wi({ + sourceX: d(), + sourceY: g(), + targetX: x(), + targetY: C(), + sourcePosition: p(), + targetPosition: $() + })); + } + ), gt(), He(), To(e, { + get path() { + return h(r); + }, + get labelX() { + return h(o); + }, + get labelY() { + return h(i); + }, + get label() { + return s(); + }, + get labelStyle() { + return a(); + }, + get markerStart() { + return u(); + }, + get markerEnd() { + return c(); + }, + get interactionWidth() { + return f(); + }, + get style() { + return l(); + } + }), fe({ + get label() { + return s(); + }, + set label(m) { + s(m), y(); + }, + get labelStyle() { + return a(); + }, + set labelStyle(m) { + a(m), y(); + }, + get style() { + return l(); + }, + set style(m) { + l(m), y(); + }, + get markerStart() { + return u(); + }, + set markerStart(m) { + u(m), y(); + }, + get markerEnd() { + return c(); + }, + set markerEnd(m) { + c(m), y(); + }, + get interactionWidth() { + return f(); + }, + set interactionWidth(m) { + f(m), y(); + }, + get sourceX() { + return d(); + }, + set sourceX(m) { + d(m), y(); + }, + get sourceY() { + return g(); + }, + set sourceY(m) { + g(m), y(); + }, + get sourcePosition() { + return p(); + }, + set sourcePosition(m) { + p(m), y(); + }, + get targetX() { + return x(); + }, + set targetX(m) { + x(m), y(); + }, + get targetY() { + return C(); + }, + set targetY(m) { + C(m), y(); + }, + get targetPosition() { + return $(); + }, + set targetPosition(m) { + $(m), y(); + } + }); +} +ae( + kc, + { + label: {}, + labelStyle: {}, + style: {}, + markerStart: {}, + markerEnd: {}, + interactionWidth: {}, + sourceX: {}, + sourceY: {}, + sourcePosition: {}, + targetX: {}, + targetY: {}, + targetPosition: {} + }, + [], + [], + !0 +); +function $c(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, [ + "label", + "labelStyle", + "style", + "markerStart", + "markerEnd", + "interactionWidth", + "sourceX", + "sourceY", + "targetX", + "targetY" + ]), de(t, !1); + const r = re(), o = re(), i = re(); + let s = w(t, "label", 12, void 0), a = w(t, "labelStyle", 12, void 0), l = w(t, "style", 12, void 0), u = w(t, "markerStart", 12, void 0), c = w(t, "markerEnd", 12, void 0), f = w(t, "interactionWidth", 12, void 0), d = w(t, "sourceX", 12), g = w(t, "sourceY", 12), p = w(t, "targetX", 12), x = w(t, "targetY", 12); + return he( + () => (h(r), h(o), h(i), j(d()), j(g()), j(p()), j(x())), + () => { + ((C) => (U(r, C[0]), U(o, C[1]), U(i, C[2])))(Hs({ + sourceX: d(), + sourceY: g(), + targetX: p(), + targetY: x() + })); + } + ), gt(), He(), To(e, { + get path() { + return h(r); + }, + get labelX() { + return h(o); + }, + get labelY() { + return h(i); + }, + get label() { + return s(); + }, + get labelStyle() { + return a(); + }, + get markerStart() { + return u(); + }, + get markerEnd() { + return c(); + }, + get interactionWidth() { + return f(); + }, + get style() { + return l(); + } + }), fe({ + get label() { + return s(); + }, + set label(C) { + s(C), y(); + }, + get labelStyle() { + return a(); + }, + set labelStyle(C) { + a(C), y(); + }, + get style() { + return l(); + }, + set style(C) { + l(C), y(); + }, + get markerStart() { + return u(); + }, + set markerStart(C) { + u(C), y(); + }, + get markerEnd() { + return c(); + }, + set markerEnd(C) { + c(C), y(); + }, + get interactionWidth() { + return f(); + }, + set interactionWidth(C) { + f(C), y(); + }, + get sourceX() { + return d(); + }, + set sourceX(C) { + d(C), y(); + }, + get sourceY() { + return g(); + }, + set sourceY(C) { + g(C), y(); + }, + get targetX() { + return p(); + }, + set targetX(C) { + p(C), y(); + }, + get targetY() { + return x(); + }, + set targetY(C) { + x(C), y(); + } + }); +} +ae( + $c, + { + label: {}, + labelStyle: {}, + style: {}, + markerStart: {}, + markerEnd: {}, + interactionWidth: {}, + sourceX: {}, + sourceY: {}, + targetX: {}, + targetY: {} + }, + [], + [], + !0 +); +function Ec(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]); + nt(n, [ + "label", + "labelStyle", + "style", + "markerStart", + "markerEnd", + "interactionWidth", + "sourceX", + "sourceY", + "sourcePosition", + "targetX", + "targetY", + "targetPosition" + ]), de(t, !1); + const r = re(), o = re(), i = re(); + let s = w(t, "label", 12, void 0), a = w(t, "labelStyle", 12, void 0), l = w(t, "style", 12, void 0), u = w(t, "markerStart", 12, void 0), c = w(t, "markerEnd", 12, void 0), f = w(t, "interactionWidth", 12, void 0), d = w(t, "sourceX", 12), g = w(t, "sourceY", 12), p = w(t, "sourcePosition", 12), x = w(t, "targetX", 12), C = w(t, "targetY", 12), $ = w(t, "targetPosition", 12); + return he( + () => (h(r), h(o), h(i), j(d()), j(g()), j(x()), j(C()), j(p()), j($())), + () => { + ((m) => (U(r, m[0]), U(o, m[1]), U(i, m[2])))(wi({ + sourceX: d(), + sourceY: g(), + targetX: x(), + targetY: C(), + sourcePosition: p(), + targetPosition: $(), + borderRadius: 0 + })); + } + ), gt(), He(), To(e, { + get path() { + return h(r); + }, + get labelX() { + return h(o); + }, + get labelY() { + return h(i); + }, + get label() { + return s(); + }, + get labelStyle() { + return a(); + }, + get markerStart() { + return u(); + }, + get markerEnd() { + return c(); + }, + get interactionWidth() { + return f(); + }, + get style() { + return l(); + } + }), fe({ + get label() { + return s(); + }, + set label(m) { + s(m), y(); + }, + get labelStyle() { + return a(); + }, + set labelStyle(m) { + a(m), y(); + }, + get style() { + return l(); + }, + set style(m) { + l(m), y(); + }, + get markerStart() { + return u(); + }, + set markerStart(m) { + u(m), y(); + }, + get markerEnd() { + return c(); + }, + set markerEnd(m) { + c(m), y(); + }, + get interactionWidth() { + return f(); + }, + set interactionWidth(m) { + f(m), y(); + }, + get sourceX() { + return d(); + }, + set sourceX(m) { + d(m), y(); + }, + get sourceY() { + return g(); + }, + set sourceY(m) { + g(m), y(); + }, + get sourcePosition() { + return p(); + }, + set sourcePosition(m) { + p(m), y(); + }, + get targetX() { + return x(); + }, + set targetX(m) { + x(m), y(); + }, + get targetY() { + return C(); + }, + set targetY(m) { + C(m), y(); + }, + get targetPosition() { + return $(); + }, + set targetPosition(m) { + $(m), y(); + } + }); +} +ae( + Ec, + { + label: {}, + labelStyle: {}, + style: {}, + markerStart: {}, + markerEnd: {}, + interactionWidth: {}, + sourceX: {}, + sourceY: {}, + sourcePosition: {}, + targetX: {}, + targetY: {}, + targetPosition: {} + }, + [], + [], + !0 +); +function d2(e, t) { + const n = e.set, r = t.set, o = q(e), i = q(t); + let a = o.length === 0 && i.length > 0 ? i : o; + e.set(a); + const l = (u) => { + const c = n(u); + return a = c, r(a), c; + }; + e.set = t.set = l, e.update = t.update = (u) => l(u(a)); +} +function f2(e, t) { + const n = e.set, r = t.set; + let o = q(t); + e.set(o); + const i = (s) => { + n(s), r(s), o = s; + }; + e.set = t.set = i, e.update = t.update = (s) => i(s(o)); +} +const g2 = (e, t, n) => { + if (!n) + return; + const r = q(e), o = t.set, i = n.set; + let s = n ? q(n) : { x: 0, y: 0, zoom: 1 }; + t.set(s), t.set = (a) => (o(a), i(a), s = a, a), n.set = (a) => (r == null || r.syncViewport(a), o(a), i(a), s = a, a), t.update = (a) => { + t.set(a(s)); + }, n.update = (a) => { + n.set(a(s)); + }; +}, h2 = (e, t, n, r = [0, 0], o = vi) => { + const { subscribe: i, set: s, update: a } = we([]); + let l = e, u = {}, c = !0; + const f = (x) => (lc(x, t, n, { + elevateNodesOnSelect: c, + nodeOrigin: r, + nodeExtent: o, + defaults: u, + checkEquality: !1 + }), l = x, s(l), l), d = (x) => f(x(l)), g = (x) => { + u = x; + }, p = (x) => { + c = x.elevateNodesOnSelect ?? c; + }; + return f(l), { + subscribe: i, + set: f, + update: d, + setDefaultOptions: g, + setOptions: p + }; +}, v2 = (e, t, n, r) => { + const { subscribe: o, set: i, update: s } = we([]); + let a = e, l = {}; + const u = (d) => { + const g = l ? d.map((p) => ({ ...l, ...p })) : d; + cc(t, n, g), a = g, i(a); + }, c = (d) => u(d(a)), f = (d) => { + l = d; + }; + return u(a), { + subscribe: o, + set: u, + update: c, + setDefaultOptions: f + }; +}, Sc = { + input: yc, + output: wc, + default: _i, + group: _c +}, Pc = { + straight: $c, + smoothstep: kc, + default: xi, + step: Ec +}, p2 = ({ nodes: e = [], edges: t = [], width: n, height: r, fitView: o, nodeOrigin: i, nodeExtent: s }) => { + const a = /* @__PURE__ */ new Map(), l = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), f = i ?? [0, 0], d = s ?? vi; + lc(e, a, l, { + nodeExtent: d, + nodeOrigin: f, + elevateNodesOnSelect: !1, + checkEquality: !1 + }), cc(u, c, t); + let g = { x: 0, y: 0, zoom: 1 }; + if (o && n && r) { + const p = No(a, { + filter: (x) => !!((x.width || x.initialWidth) && (x.height || x.initialHeight)) + }); + g = ua(p, n, r, 0.5, 2, 0.1); + } + return { + flowId: we(null), + nodes: h2(e, a, l, f, d), + nodeLookup: Ft(a), + parentLookup: Ft(l), + edgeLookup: Ft(c), + visibleNodes: Ft([]), + edges: v2(t, u, c), + visibleEdges: Ft([]), + connectionLookup: Ft(u), + height: we(500), + width: we(500), + minZoom: we(0.5), + maxZoom: we(2), + nodeOrigin: we(f), + nodeDragThreshold: we(1), + nodeExtent: we(d), + translateExtent: we(vi), + autoPanOnNodeDrag: we(!0), + autoPanOnConnect: we(!0), + fitViewOnInit: we(!1), + fitViewOnInitDone: we(!1), + fitViewOptions: we(void 0), + panZoom: we(null), + snapGrid: we(null), + dragging: we(!1), + selectionRect: we(null), + selectionKeyPressed: we(!1), + multiselectionKeyPressed: we(!1), + deleteKeyPressed: we(!1), + panActivationKeyPressed: we(!1), + zoomActivationKeyPressed: we(!1), + selectionRectMode: we(null), + selectionMode: we(pi.Partial), + nodeTypes: we(Sc), + edgeTypes: we(Pc), + viewport: we(g), + connectionMode: we(cr.Strict), + domNode: we(null), + connection: Ft(Ns), + connectionLineType: we(Cr.Bezier), + connectionRadius: we(20), + isValidConnection: we(() => !0), + nodesDraggable: we(!0), + nodesConnectable: we(!0), + elementsSelectable: we(!0), + selectNodesOnDrag: we(!0), + markers: Ft([]), + defaultMarkerColor: we("#b1b1b7"), + lib: Ft("svelte"), + onlyRenderVisibleElements: we(!1), + onerror: we(v0), + ondelete: we(void 0), + onedgecreate: we(void 0), + onconnect: we(void 0), + onconnectstart: we(void 0), + onconnectend: we(void 0), + onbeforedelete: we(void 0), + nodesInitialized: we(!1), + edgesInitialized: we(!1), + viewportInitialized: we(!1), + initialized: Ft(!1) + }; +}; +function m2(e) { + const t = Kn([ + e.edges, + e.nodes, + e.nodeLookup, + e.onlyRenderVisibleElements, + e.viewport, + e.width, + e.height + ], ([n, , r, o, i, s, a]) => o && s && a ? n.filter((u) => { + const c = r.get(u.source), f = r.get(u.target); + return c && f && b0({ + sourceNode: c, + targetNode: f, + width: s, + height: a, + transform: [i.x, i.y, i.zoom] + }); + }) : n); + return Kn([t, e.nodes, e.nodeLookup, e.connectionMode, e.onerror], ([n, , r, o, i]) => n.reduce((a, l) => { + const u = r.get(l.source), c = r.get(l.target); + if (!u || !c) + return a; + const f = N0({ + id: l.id, + sourceNode: u, + targetNode: c, + sourceHandle: l.sourceHandle || null, + targetHandle: l.targetHandle || null, + connectionMode: o, + onError: i + }); + return f && a.push({ + ...l, + zIndex: x0({ + selected: l.selected, + zIndex: l.zIndex, + sourceNode: u, + targetNode: c, + elevateOnSelect: !1 + }), + ...f + }), a; + }, [])); +} +function y2(e) { + return Kn([ + e.nodeLookup, + e.onlyRenderVisibleElements, + e.width, + e.height, + e.viewport, + e.nodes + ], ([t, n, r, o, i]) => { + const s = [i.x, i.y, i.zoom]; + return n ? Ju(t, { x: 0, y: 0, width: r, height: o }, s, !0) : Array.from(t.values()); + }); +} +const Wi = Symbol(); +function Nc({ nodes: e, edges: t, width: n, height: r, fitView: o, nodeOrigin: i, nodeExtent: s }) { + const a = p2({ + nodes: e, + edges: t, + width: n, + height: r, + fitView: o, + nodeOrigin: i, + nodeExtent: s + }); + function l(k) { + a.nodeTypes.set({ + ...Sc, + ...k + }); + } + function u(k) { + a.edgeTypes.set({ + ...Pc, + ...k + }); + } + function c(k) { + const P = q(a.edges); + a.edges.set($0(k, P)); + } + const f = (k, P = !1) => { + var I; + const H = q(a.nodeLookup); + for (const [B, F] of k) { + const K = (I = H.get(B)) == null ? void 0 : I.internals.userNode; + K && (K.position = F.position, K.dragging = P); + } + a.nodes.update((B) => B); + }; + function d(k) { + var F, K, ie; + const P = q(a.nodeLookup), H = q(a.parentLookup), { changes: I, updatedInternals: B } = O0(k, P, q(a.parentLookup), q(a.domNode), q(a.nodeOrigin)); + if (B) { + if (V0(P, H, { nodeOrigin: i, nodeExtent: s }), !q(a.fitViewOnInitDone) && q(a.fitViewOnInit)) { + const ee = q(a.fitViewOptions), W = p({ + ...ee, + nodes: ee == null ? void 0 : ee.nodes + }); + a.fitViewOnInitDone.set(W); + } + for (const ee of I) { + const W = (F = P.get(ee.id)) == null ? void 0 : F.internals.userNode; + if (W) + switch (ee.type) { + case "dimensions": { + const ue = { ...W.measured, ...ee.dimensions }; + ee.setAttributes && (W.width = ((K = ee.dimensions) == null ? void 0 : K.width) ?? W.width, W.height = ((ie = ee.dimensions) == null ? void 0 : ie.height) ?? W.height), W.measured = ue; + break; + } + case "position": + W.position = ee.position ?? W.position; + break; + } + } + a.nodes.update((ee) => ee), q(a.nodesInitialized) || a.nodesInitialized.set(!0); + } + } + function g(k) { + const P = q(a.panZoom), H = q(a.domNode); + if (!P || !H) + return Promise.resolve(!1); + const { width: I, height: B } = ca(H), F = ul(q(a.nodeLookup), k); + return cl({ + nodes: F, + width: I, + height: B, + minZoom: q(a.minZoom), + maxZoom: q(a.maxZoom), + panZoom: P + }, k); + } + function p(k) { + const P = q(a.panZoom); + if (!P) + return !1; + const H = ul(q(a.nodeLookup), k); + return cl({ + nodes: H, + width: q(a.width), + height: q(a.height), + minZoom: q(a.minZoom), + maxZoom: q(a.maxZoom), + panZoom: P + }, k), H.size > 0; + } + function x(k, P) { + const H = q(a.panZoom); + return H ? H.scaleBy(k, P) : Promise.resolve(!1); + } + function C(k) { + return x(1.2, k); + } + function $(k) { + return x(1 / 1.2, k); + } + function m(k) { + const P = q(a.panZoom); + P && (P.setScaleExtent([k, q(a.maxZoom)]), a.minZoom.set(k)); + } + function _(k) { + const P = q(a.panZoom); + P && (P.setScaleExtent([q(a.minZoom), k]), a.maxZoom.set(k)); + } + function v(k) { + const P = q(a.panZoom); + P && (P.setTranslateExtent(k), a.translateExtent.set(k)); + } + function b(k) { + let P = !1; + return k.forEach((H) => { + H.selected && (H.selected = !1, P = !0); + }), P; + } + function N(k) { + var P; + (P = q(a.panZoom)) == null || P.setClickDistance(k); + } + function E(k) { + b((k == null ? void 0 : k.nodes) || q(a.nodes)) && a.nodes.set(q(a.nodes)), b((k == null ? void 0 : k.edges) || q(a.edges)) && a.edges.set(q(a.edges)); + } + a.deleteKeyPressed.subscribe(async (k) => { + var P; + if (k) { + const H = q(a.nodes), I = q(a.edges), B = H.filter((ee) => ee.selected), F = I.filter((ee) => ee.selected), { nodes: K, edges: ie } = await Qu({ + nodesToRemove: B, + edgesToRemove: F, + nodes: H, + edges: I, + onBeforeDelete: q(a.onbeforedelete) + }); + (K.length || ie.length) && (a.nodes.update((ee) => ee.filter((W) => !K.some((ue) => ue.id === W.id))), a.edges.update((ee) => ee.filter((W) => !ie.some((ue) => ue.id === W.id))), (P = q(a.ondelete)) == null || P({ + nodes: K, + edges: ie + })); + } + }); + function M(k) { + const P = q(a.multiselectionKeyPressed); + a.nodes.update((H) => H.map((I) => { + const B = k.includes(I.id), F = P && I.selected || B; + return I.selected = F, I; + })), P || a.edges.update((H) => H.map((I) => (I.selected = !1, I))); + } + function D(k) { + const P = q(a.multiselectionKeyPressed); + a.edges.update((H) => H.map((I) => { + const B = k.includes(I.id), F = P && I.selected || B; + return I.selected = F, I; + })), P || a.nodes.update((H) => H.map((I) => (I.selected = !1, I))); + } + function V(k) { + var H; + const P = (H = q(a.nodes)) == null ? void 0 : H.find((I) => I.id === k); + if (!P) { + console.warn("012", Dr.error012(k)); + return; + } + a.selectionRect.set(null), a.selectionRectMode.set(null), P.selected ? P.selected && q(a.multiselectionKeyPressed) && E({ nodes: [P], edges: [] }) : M([k]); + } + function A(k) { + const P = q(a.viewport); + return I0({ + delta: k, + panZoom: q(a.panZoom), + transform: [P.x, P.y, P.zoom], + translateExtent: q(a.translateExtent), + width: q(a.width), + height: q(a.height) + }); + } + const O = we(Ns), R = (k) => { + O.set({ ...k }); + }; + function S() { + O.set(Ns); + } + function T() { + a.fitViewOnInitDone.set(!1), a.selectionRect.set(null), a.selectionRectMode.set(null), a.snapGrid.set(null), a.isValidConnection.set(() => !0), E(), S(); + } + return { + // state + ...a, + // derived state + visibleEdges: m2(a), + visibleNodes: y2(a), + connection: Kn([O, a.viewport], ([k, P]) => k.inProgress ? { + ...k, + to: Mo(k.to, [P.x, P.y, P.zoom]) + } : { ...k }), + markers: Kn([a.edges, a.defaultMarkerColor, a.flowId], ([k, P, H]) => M0(k, { defaultColor: P, id: H })), + initialized: (() => { + let k = !1; + const P = q(a.nodes).length, H = q(a.edges).length; + return Kn([a.nodesInitialized, a.edgesInitialized, a.viewportInitialized], ([I, B, F]) => k || (P === 0 ? k = F : H === 0 ? k = F && I : k = F && I && B, k)); + })(), + // actions + syncNodeStores: (k) => d2(a.nodes, k), + syncEdgeStores: (k) => f2(a.edges, k), + syncViewport: (k) => g2(a.panZoom, a.viewport, k), + setNodeTypes: l, + setEdgeTypes: u, + addEdge: c, + updateNodePositions: f, + updateNodeInternals: d, + zoomIn: C, + zoomOut: $, + fitView: (k) => g(k), + setMinZoom: m, + setMaxZoom: _, + setTranslateExtent: v, + setPaneClickDistance: N, + unselectNodesAndEdges: E, + addSelectedNodes: M, + addSelectedEdges: D, + handleNodeSelection: V, + panBy: A, + updateConnection: R, + cancelConnection: S, + reset: T + }; +} +function Ue() { + const e = ar(Wi); + if (!e) + throw new Error("In order to use useStore you need to wrap your component in a <SvelteFlowProvider />"); + return e.getStore(); +} +function w2({ nodes: e, edges: t, width: n, height: r, fitView: o, nodeOrigin: i, nodeExtent: s }) { + const a = Nc({ nodes: e, edges: t, width: n, height: r, fitView: o, nodeOrigin: i, nodeExtent: s }); + return Tr(Wi, { + getStore: () => a + }), a; +} +function us(e, t) { + const { panZoom: n, minZoom: r, maxZoom: o, initialViewport: i, viewport: s, dragging: a, translateExtent: l, paneClickDistance: u } = t, c = n2({ + domNode: e, + minZoom: r, + maxZoom: o, + translateExtent: l, + viewport: i, + paneClickDistance: u, + onDraggingChange: a.set + }), f = c.getViewport(); + return s.set(f), n.set(c), c.update(t), { + update(d) { + c.update(d); + } + }; +} +var _2 = /* @__PURE__ */ ne('<div class="svelte-flow__zoom svelte-4xkw84"><!></div>'); +const x2 = { + hash: "svelte-4xkw84", + code: ".svelte-flow__zoom.svelte-4xkw84 {width:100%;height:100%;position:absolute;top:0;left:0;z-index:4;}" +}; +function Mc(e, t) { + de(t, !1), Je(e, x2); + const [n, r] = tt(), o = () => Q(H, "$panActivationKeyPressed", n), i = () => Q(R, "$minZoom", n), s = () => Q(S, "$maxZoom", n), a = () => Q(I, "$zoomActivationKeyPressed", n), l = () => Q(O, "$selectionRect", n), u = () => Q(k, "$translateExtent", n), c = () => Q(P, "$lib", n), f = re(), d = re(), g = re(); + let p = w(t, "initialViewport", 12, void 0), x = w(t, "onMoveStart", 12, void 0), C = w(t, "onMove", 12, void 0), $ = w(t, "onMoveEnd", 12, void 0), m = w(t, "panOnScrollMode", 12), _ = w(t, "preventScrolling", 12), v = w(t, "zoomOnScroll", 12), b = w(t, "zoomOnDoubleClick", 12), N = w(t, "zoomOnPinch", 12), E = w(t, "panOnDrag", 12), M = w(t, "panOnScroll", 12), D = w(t, "paneClickDistance", 12); + const { + viewport: V, + panZoom: A, + selectionRect: O, + minZoom: R, + maxZoom: S, + dragging: T, + translateExtent: k, + lib: P, + panActivationKeyPressed: H, + zoomActivationKeyPressed: I, + viewportInitialized: B + } = Ue(), F = (W) => V.set({ + x: W[0], + y: W[1], + zoom: W[2] + }); + un(() => { + li(B, !0); + }), he(() => j(p()), () => { + U(f, p() || { x: 0, y: 0, zoom: 1 }); + }), he( + () => (o(), j(E())), + () => { + U(d, o() || E()); + } + ), he( + () => (o(), j(M())), + () => { + U(g, o() || M()); + } + ), gt(), He(); + var K = _2(), ie = X(K); + pt(ie, t, "default", {}), Z(K), vt(K, (W, ue) => us == null ? void 0 : us(W, ue), () => ({ + viewport: V, + minZoom: i(), + maxZoom: s(), + initialViewport: h(f), + dragging: T, + panZoom: A, + onPanZoomStart: x(), + onPanZoom: C(), + onPanZoomEnd: $(), + zoomOnScroll: v(), + zoomOnDoubleClick: b(), + zoomOnPinch: N(), + panOnScroll: h(g), + panOnDrag: h(d), + panOnScrollSpeed: 0.5, + panOnScrollMode: m() || qn.Free, + zoomActivationKeyPressed: a(), + preventScrolling: typeof _() == "boolean" ? _() : !0, + noPanClassName: "nopan", + noWheelClassName: "nowheel", + userSelectionActive: !!l(), + translateExtent: u(), + lib: c(), + paneClickDistance: D(), + onTransformChange: F + })), L(e, K); + var ee = fe({ + get initialViewport() { + return p(); + }, + set initialViewport(W) { + p(W), y(); + }, + get onMoveStart() { + return x(); + }, + set onMoveStart(W) { + x(W), y(); + }, + get onMove() { + return C(); + }, + set onMove(W) { + C(W), y(); + }, + get onMoveEnd() { + return $(); + }, + set onMoveEnd(W) { + $(W), y(); + }, + get panOnScrollMode() { + return m(); + }, + set panOnScrollMode(W) { + m(W), y(); + }, + get preventScrolling() { + return _(); + }, + set preventScrolling(W) { + _(W), y(); + }, + get zoomOnScroll() { + return v(); + }, + set zoomOnScroll(W) { + v(W), y(); + }, + get zoomOnDoubleClick() { + return b(); + }, + set zoomOnDoubleClick(W) { + b(W), y(); + }, + get zoomOnPinch() { + return N(); + }, + set zoomOnPinch(W) { + N(W), y(); + }, + get panOnDrag() { + return E(); + }, + set panOnDrag(W) { + E(W), y(); + }, + get panOnScroll() { + return M(); + }, + set panOnScroll(W) { + M(W), y(); + }, + get paneClickDistance() { + return D(); + }, + set paneClickDistance(W) { + D(W), y(); + } + }); + return r(), ee; +} +ae( + Mc, + { + initialViewport: {}, + onMoveStart: {}, + onMove: {}, + onMoveEnd: {}, + panOnScrollMode: {}, + preventScrolling: {}, + zoomOnScroll: {}, + zoomOnDoubleClick: {}, + zoomOnPinch: {}, + panOnDrag: {}, + panOnScroll: {}, + paneClickDistance: {} + }, + ["default"], + [], + !0 +); +function kl(e, t) { + return (n) => { + n.target === t && (e == null || e(n)); + }; +} +function $l(e) { + return (t) => { + const n = e.includes(t.id); + return t.selected !== n && (t.selected = n), t; + }; +} +var b2 = /* @__PURE__ */ ne("<div><!></div>"); +const C2 = { + hash: "svelte-1esy7hx", + code: ".svelte-flow__pane.svelte-1esy7hx {position:absolute;top:0;left:0;width:100%;height:100%;}" +}; +function Tc(e, t) { + de(t, !1), Je(e, C2); + const [n, r] = tt(), o = () => Q(S, "$panActivationKeyPressed", n), i = () => Q(O, "$selectionKeyPressed", n), s = () => Q(V, "$selectionRect", n), a = () => Q(D, "$elementsSelectable", n), l = () => Q(A, "$selectionRectMode", n), u = () => Q(N, "$edges", n), c = () => Q(b, "$nodeLookup", n), f = () => Q(E, "$viewport", n), d = () => Q(R, "$selectionMode", n), g = () => Q(M, "$dragging", n), p = re(), x = re(), C = re(); + let $ = w(t, "panOnDrag", 12, void 0), m = w(t, "selectionOnDrag", 12, void 0); + const _ = Oi(), { + nodes: v, + nodeLookup: b, + edges: N, + viewport: E, + dragging: M, + elementsSelectable: D, + selectionRect: V, + selectionRectMode: A, + selectionKeyPressed: O, + selectionMode: R, + panActivationKeyPressed: S, + unselectNodesAndEdges: T + } = Ue(); + let k = re(), P = null, H = [], I = !1; + function B(G) { + if (I) { + I = !1; + return; + } + _("paneclick", { event: G }), T(), A.set(null); + } + function F(G) { + var Ae, Xe; + if (P = h(k).getBoundingClientRect(), !D || !h(x) || G.button !== 0 || G.target !== h(k) || !P) + return; + (Xe = (Ae = G.target) == null ? void 0 : Ae.setPointerCapture) == null || Xe.call(Ae, G.pointerId); + const { x: se, y: Te } = Hn(G, P); + T(), V.set({ + width: 0, + height: 0, + startX: se, + startY: Te, + x: se, + y: Te + }); + } + function K(G) { + if (!h(x) || !P || !s()) + return; + I = !0; + const se = Hn(G, P), Te = s().startX ?? 0, Ae = s().startY ?? 0, Xe = { + ...s(), + x: se.x < Te ? se.x : Te, + y: se.y < Ae ? se.y : Ae, + width: Math.abs(se.x - Te), + height: Math.abs(se.y - Ae) + }, te = H.map((oe) => oe.id), Fe = Ms(H, u()).map((oe) => oe.id); + H = Ju( + c(), + Xe, + [ + f().x, + f().y, + f().zoom + ], + d() === pi.Partial, + !0 + ); + const Le = Ms(H, u()).map((oe) => oe.id), Qe = H.map((oe) => oe.id); + (te.length !== Qe.length || Qe.some((oe) => !te.includes(oe))) && v.update((oe) => oe.map($l(Qe))), (Fe.length !== Le.length || Le.some((oe) => !Fe.includes(oe))) && N.update((oe) => oe.map($l(Le))), A.set("user"), V.set(Xe); + } + function ie(G) { + var se, Te; + G.button === 0 && ((Te = (se = G.target) == null ? void 0 : se.releasePointerCapture) == null || Te.call(se, G.pointerId), !h(x) && l() === "user" && G.target === h(k) && (B == null || B(G)), V.set(null), H.length > 0 && li(A, "nodes"), i() && (I = !1)); + } + const ee = (G) => { + var se; + if (Array.isArray(h(p)) && ((se = h(p)) != null && se.includes(2))) { + G.preventDefault(); + return; + } + _("panecontextmenu", { event: G }); + }; + he( + () => (o(), j($())), + () => { + U(p, o() || $()); + } + ), he( + () => (i(), s(), j(m()), h(p)), + () => { + U(x, i() || s() || m() && h(p) !== !0); + } + ), he( + () => (a(), h(x), l()), + () => { + U(C, a() && (h(x) || l() === "user")); + } + ), gt(), He(); + var W = b2(), ue = /* @__PURE__ */ Me(() => h(C) ? void 0 : kl(B, h(k))), me = /* @__PURE__ */ Me(() => kl(ee, h(k))); + let Ce; + var ge = X(W); + pt(ge, t, "default", {}), Z(W), An(W, (G) => U(k, G), () => h(k)), Ee( + (G) => Ce = kt(W, 1, "svelte-flow__pane svelte-1esy7hx", null, Ce, { + draggable: G, + dragging: g(), + selection: h(x) + }), + [ + () => $() === !0 || Array.isArray($()) && $().includes(0) + ], + pe + ), Ye("click", W, function(...G) { + var se; + (se = h(ue)) == null || se.apply(this, G); + }), Ye("pointerdown", W, function(...G) { + var se; + (se = h(C) ? F : void 0) == null || se.apply(this, G); + }), Ye("pointermove", W, function(...G) { + var se; + (se = h(C) ? K : void 0) == null || se.apply(this, G); + }), Ye("pointerup", W, function(...G) { + var se; + (se = h(C) ? ie : void 0) == null || se.apply(this, G); + }), Ye("contextmenu", W, function(...G) { + var se; + (se = h(me)) == null || se.apply(this, G); + }), L(e, W); + var ze = fe({ + get panOnDrag() { + return $(); + }, + set panOnDrag(G) { + $(G), y(); + }, + get selectionOnDrag() { + return m(); + }, + set selectionOnDrag(G) { + m(G), y(); + } + }); + return r(), ze; +} +ae(Tc, { panOnDrag: {}, selectionOnDrag: {} }, ["default"], [], !0); +var k2 = /* @__PURE__ */ ne('<div class="svelte-flow__viewport xyflow__viewport svelte-1floaup"><!></div>'); +const $2 = { + hash: "svelte-1floaup", + code: ".svelte-flow__viewport.svelte-1floaup {width:100%;height:100%;position:absolute;top:0;left:0;}" +}; +function Hc(e, t) { + de(t, !1), Je(e, $2); + const [n, r] = tt(), o = () => Q(i, "$viewport", n), { viewport: i } = Ue(); + He(); + var s = k2(), a = X(s); + pt(a, t, "default", {}), Z(s), Ee(() => ce(s, "style", `transform: translate(${o().x ?? ""}px, ${o().y ?? ""}px) scale(${o().zoom ?? ""})`)), L(e, s), fe(), r(); +} +ae(Hc, {}, ["default"], [], !0); +function $r(e, t) { + const { store: n, onDrag: r, onDragStart: o, onDragStop: i, onNodeMouseDown: s } = t, a = B0({ + onDrag: r, + onDragStart: o, + onDragStop: i, + onNodeMouseDown: s, + getStoreItems: () => { + const u = q(n.snapGrid), c = q(n.viewport); + return { + nodes: q(n.nodes), + nodeLookup: q(n.nodeLookup), + edges: q(n.edges), + nodeExtent: q(n.nodeExtent), + snapGrid: u || [0, 0], + snapToGrid: !!u, + nodeOrigin: q(n.nodeOrigin), + multiSelectionActive: q(n.multiselectionKeyPressed), + domNode: q(n.domNode), + transform: [c.x, c.y, c.zoom], + autoPanOnNodeDrag: q(n.autoPanOnNodeDrag), + nodesDraggable: q(n.nodesDraggable), + selectNodesOnDrag: q(n.selectNodesOnDrag), + nodeDragThreshold: q(n.nodeDragThreshold), + unselectNodesAndEdges: n.unselectNodesAndEdges, + updateNodePositions: n.updateNodePositions, + panBy: n.panBy + }; + } + }); + function l(u, c) { + if (c.disabled) { + a.destroy(); + return; + } + a.update({ + domNode: u, + noDragClassName: c.noDragClass, + handleSelector: c.handleSelector, + nodeId: c.nodeId, + isSelectable: c.isSelectable, + nodeClickDistance: c.nodeClickDistance + }); + } + return l(e, t), { + update(u) { + l(e, u); + }, + destroy() { + a.destroy(); + } + }; +} +function E2({ width: e, height: t, initialWidth: n, initialHeight: r, measuredWidth: o, measuredHeight: i }) { + if (o === void 0 && i === void 0) { + const s = e ?? n, a = t ?? r; + return { + width: s ? `width:${s}px;` : "", + height: a ? `height:${a}px;` : "" + }; + } + return { + width: e ? `width:${e}px;` : "", + height: t ? `height:${t}px;` : "" + }; +} +var S2 = /* @__PURE__ */ ne("<div><!></div>"); +function Vc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(me, "$nodeTypes", n), i = () => Q(se, "$elementsSelectable", n), s = () => Q(Te, "$nodesDraggable", n), a = () => Q(Fe, "$connectableStore", n), l = re(void 0, !0), u = re(void 0, !0), c = re(void 0, !0), f = re(void 0, !0); + let d = w(t, "node", 13), g = w(t, "id", 13), p = w(t, "data", 29, () => ({})), x = w(t, "selected", 13, !1), C = w(t, "draggable", 13, void 0), $ = w(t, "selectable", 13, void 0), m = w(t, "connectable", 13, !0), _ = w(t, "deletable", 13, !0), v = w(t, "hidden", 13, !1), b = w(t, "dragging", 13, !1), N = w(t, "resizeObserver", 13, null), E = w(t, "style", 13, void 0), M = w(t, "type", 13, "default"), D = w(t, "isParent", 13, !1), V = w(t, "positionX", 13), A = w(t, "positionY", 13), O = w(t, "sourcePosition", 13, void 0), R = w(t, "targetPosition", 13, void 0), S = w(t, "zIndex", 13), T = w(t, "measuredWidth", 13, void 0), k = w(t, "measuredHeight", 13, void 0), P = w(t, "initialWidth", 13, void 0), H = w(t, "initialHeight", 13, void 0), I = w(t, "width", 13, void 0), B = w(t, "height", 13, void 0), F = w(t, "dragHandle", 13, void 0), K = w(t, "initialized", 13, !1), ie = w(t, "parentId", 13, void 0), ee = w(t, "nodeClickDistance", 13, void 0), W = w(t, "class", 13, ""); + const ue = Ue(), { + nodeTypes: me, + nodeDragThreshold: Ce, + selectNodesOnDrag: ge, + handleNodeSelection: ze, + updateNodeInternals: G, + elementsSelectable: se, + nodesDraggable: Te + } = ue; + let Ae = re(void 0, !0), Xe = re(null, !0); + const te = Oi(), Fe = we(m()); + let Le = re(void 0, !0), Qe = re(void 0, !0), oe = re(void 0, !0); + Tr("svelteflow__node_id", g()), Tr("svelteflow__node_connectable", Fe), Qs(() => { + var J; + h(Xe) && ((J = N()) == null || J.unobserve(h(Xe))); + }); + function ve(J) { + $() && (!q(ge) || !C() || q(Ce) > 0) && ze(g()), te("nodeclick", { node: d().internals.userNode, event: J }); + } + he(() => j(M()), () => { + U(l, M() || "default"); + }), he(() => (o(), h(l)), () => { + U(u, !!o()[h(l)]); + }), he( + () => (o(), h(l), _i), + () => { + U(c, o()[h(l)] || _i); + } + ), he( + () => (h(u), j(M())), + () => { + h(u) || console.warn("003", Dr.error003(M())); + } + ), he( + () => (j(I()), j(B()), j(P()), j(H()), j(T()), j(k())), + () => { + U(f, E2({ + width: I(), + height: B(), + initialWidth: P(), + initialHeight: H(), + measuredWidth: T(), + measuredHeight: k() + })); + } + ), he(() => j(m()), () => { + Fe.set(!!m()); + }), he( + () => (h(Le), h(l), h(Qe), j(O()), h(oe), j(R()), j(g()), h(Ae)), + () => { + (h(Le) && h(l) !== h(Le) || h(Qe) && O() !== h(Qe) || h(oe) && R() !== h(oe)) && requestAnimationFrame(() => G(/* @__PURE__ */ new Map([ + [ + g(), + { + id: g(), + nodeElement: h(Ae), + force: !0 + } + ] + ]))), U(Le, h(l)), U(Qe, O()), U(oe, R()); + } + ), he( + () => (j(N()), h(Ae), h(Xe), j(K())), + () => { + N() && (h(Ae) !== h(Xe) || !K()) && (h(Xe) && N().unobserve(h(Xe)), h(Ae) && N().observe(h(Ae)), U(Xe, h(Ae))); + } + ), gt(), He(!0); + var xe = et(), Oe = be(xe); + { + var ct = (J) => { + var Re = S2(); + let le; + var fn = X(Re); + const Ut = /* @__PURE__ */ pe(() => x() ?? !1), gn = /* @__PURE__ */ pe(() => $() ?? i() ?? !0), Ne = /* @__PURE__ */ pe(() => _() ?? !0), rt = /* @__PURE__ */ pe(() => C() ?? s() ?? !0); + yu(fn, () => h(c), (ye, ot) => { + ot(ye, { + get data() { + return p(); + }, + get id() { + return g(); + }, + get selected() { + return h(Ut); + }, + get selectable() { + return h(gn); + }, + get deletable() { + return h(Ne); + }, + get sourcePosition() { + return O(); + }, + get targetPosition() { + return R(); + }, + get zIndex() { + return S(); + }, + get dragging() { + return b(); + }, + get draggable() { + return h(rt); + }, + get dragHandle() { + return F(); + }, + get parentId() { + return ie(); + }, + get type() { + return h(l); + }, + get isConnectable() { + return a(); + }, + get positionAbsoluteX() { + return V(); + }, + get positionAbsoluteY() { + return A(); + }, + get width() { + return I(); + }, + get height() { + return B(); + } + }); + }), Z(Re), vt(Re, (ye, ot) => $r == null ? void 0 : $r(ye, ot), () => ({ + nodeId: g(), + isSelectable: $(), + disabled: !1, + handleSelector: F(), + noDragClass: "nodrag", + nodeClickDistance: ee(), + onNodeMouseDown: ze, + onDrag: (ye, ot, at, Xt) => { + te("nodedrag", { event: ye, targetNode: at, nodes: Xt }); + }, + onDragStart: (ye, ot, at, Xt) => { + te("nodedragstart", { event: ye, targetNode: at, nodes: Xt }); + }, + onDragStop: (ye, ot, at, Xt) => { + te("nodedragstop", { event: ye, targetNode: at, nodes: Xt }); + }, + store: ue + })), An(Re, (ye) => U(Ae, ye), () => h(Ae)), Ot(() => Ye("click", Re, ve)), Ot(() => Ye("mouseenter", Re, (ye) => te("nodemouseenter", { node: d(), event: ye }))), Ot(() => Ye("mouseleave", Re, (ye) => te("nodemouseleave", { node: d(), event: ye }))), Ot(() => Ye("mousemove", Re, (ye) => te("nodemousemove", { node: d(), event: ye }))), Ot(() => Ye("contextmenu", Re, (ye) => te("nodecontextmenu", { node: d(), event: ye }))), Ee( + (ye) => { + ce(Re, "data-id", g()), le = kt(Re, 1, bn(ye), null, le, { + dragging: b(), + selected: x(), + draggable: C(), + connectable: m(), + selectable: $(), + nopan: C(), + parent: D() + }), ce(Re, "style", `${E() ?? ""};${h(f).width ?? ""}${h(f).height ?? ""}`), st(Re, "z-index", S()), st(Re, "transform", `translate(${V() ?? ""}px, ${A() ?? ""}px)`), st(Re, "visibility", K() ? "visible" : "hidden"); + }, + [ + () => Et([ + "svelte-flow__node", + `svelte-flow__node-${h(l)}`, + W() + ]) + ], + pe + ), L(J, Re); + }; + ke(Oe, (J) => { + v() || J(ct); + }); + } + L(e, xe); + var lt = fe({ + get node() { + return d(); + }, + set node(J) { + d(J), y(); + }, + get id() { + return g(); + }, + set id(J) { + g(J), y(); + }, + get data() { + return p(); + }, + set data(J) { + p(J), y(); + }, + get selected() { + return x(); + }, + set selected(J) { + x(J), y(); + }, + get draggable() { + return C(); + }, + set draggable(J) { + C(J), y(); + }, + get selectable() { + return $(); + }, + set selectable(J) { + $(J), y(); + }, + get connectable() { + return m(); + }, + set connectable(J) { + m(J), y(); + }, + get deletable() { + return _(); + }, + set deletable(J) { + _(J), y(); + }, + get hidden() { + return v(); + }, + set hidden(J) { + v(J), y(); + }, + get dragging() { + return b(); + }, + set dragging(J) { + b(J), y(); + }, + get resizeObserver() { + return N(); + }, + set resizeObserver(J) { + N(J), y(); + }, + get style() { + return E(); + }, + set style(J) { + E(J), y(); + }, + get type() { + return M(); + }, + set type(J) { + M(J), y(); + }, + get isParent() { + return D(); + }, + set isParent(J) { + D(J), y(); + }, + get positionX() { + return V(); + }, + set positionX(J) { + V(J), y(); + }, + get positionY() { + return A(); + }, + set positionY(J) { + A(J), y(); + }, + get sourcePosition() { + return O(); + }, + set sourcePosition(J) { + O(J), y(); + }, + get targetPosition() { + return R(); + }, + set targetPosition(J) { + R(J), y(); + }, + get zIndex() { + return S(); + }, + set zIndex(J) { + S(J), y(); + }, + get measuredWidth() { + return T(); + }, + set measuredWidth(J) { + T(J), y(); + }, + get measuredHeight() { + return k(); + }, + set measuredHeight(J) { + k(J), y(); + }, + get initialWidth() { + return P(); + }, + set initialWidth(J) { + P(J), y(); + }, + get initialHeight() { + return H(); + }, + set initialHeight(J) { + H(J), y(); + }, + get width() { + return I(); + }, + set width(J) { + I(J), y(); + }, + get height() { + return B(); + }, + set height(J) { + B(J), y(); + }, + get dragHandle() { + return F(); + }, + set dragHandle(J) { + F(J), y(); + }, + get initialized() { + return K(); + }, + set initialized(J) { + K(J), y(); + }, + get parentId() { + return ie(); + }, + set parentId(J) { + ie(J), y(); + }, + get nodeClickDistance() { + return ee(); + }, + set nodeClickDistance(J) { + ee(J), y(); + }, + get class() { + return W(); + }, + set class(J) { + W(J), y(); + } + }); + return r(), lt; +} +ae( + Vc, + { + node: {}, + id: {}, + data: {}, + selected: {}, + draggable: {}, + selectable: {}, + connectable: {}, + deletable: {}, + hidden: {}, + dragging: {}, + resizeObserver: {}, + style: {}, + type: {}, + isParent: {}, + positionX: {}, + positionY: {}, + sourcePosition: {}, + targetPosition: {}, + zIndex: {}, + measuredWidth: {}, + measuredHeight: {}, + initialWidth: {}, + initialHeight: {}, + width: {}, + height: {}, + dragHandle: {}, + initialized: {}, + parentId: {}, + nodeClickDistance: {}, + class: {} + }, + [], + [], + !0 +); +var P2 = /* @__PURE__ */ ne('<div class="svelte-flow__nodes svelte-tf4uy4"></div>'); +const N2 = { + hash: "svelte-tf4uy4", + code: ".svelte-flow__nodes.svelte-tf4uy4 {width:100%;height:100%;position:absolute;left:0;top:0;}" +}; +function Dc(e, t) { + de(t, !1), Je(e, N2); + const [n, r] = tt(), o = () => Q(c, "$visibleNodes", n), i = () => Q(f, "$nodesDraggable", n), s = () => Q(g, "$elementsSelectable", n), a = () => Q(d, "$nodesConnectable", n), l = () => Q(x, "$parentLookup", n); + let u = w(t, "nodeClickDistance", 12, 0); + const { + visibleNodes: c, + nodesDraggable: f, + nodesConnectable: d, + elementsSelectable: g, + updateNodeInternals: p, + parentLookup: x + } = Ue(), C = typeof ResizeObserver > "u" ? null : new ResizeObserver((_) => { + const v = /* @__PURE__ */ new Map(); + _.forEach((b) => { + const N = b.target.getAttribute("data-id"); + v.set(N, { id: N, nodeElement: b.target, force: !0 }); + }), p(v); + }); + Qs(() => { + C == null || C.disconnect(); + }), He(); + var $ = P2(); + Yt($, 5, o, (_) => _.id, (_, v) => { + const b = /* @__PURE__ */ pe(() => !!h(v).selected), N = /* @__PURE__ */ pe(() => !!h(v).hidden), E = /* @__PURE__ */ pe(() => !!(h(v).draggable || i() && typeof h(v).draggable > "u")), M = /* @__PURE__ */ pe(() => !!(h(v).selectable || s() && typeof h(v).selectable > "u")), D = /* @__PURE__ */ pe(() => !!(h(v).connectable || a() && typeof h(v).connectable > "u")), V = /* @__PURE__ */ pe(() => h(v).deletable ?? !0), A = /* @__PURE__ */ pe(() => l().has(h(v).id)), O = /* @__PURE__ */ pe(() => h(v).type ?? "default"), R = /* @__PURE__ */ pe(() => h(v).internals.z ?? 0), S = /* @__PURE__ */ pe(() => oc(h(v))); + Vc(_, { + get node() { + return h(v); + }, + get id() { + return h(v).id; + }, + get data() { + return h(v).data; + }, + get selected() { + return h(b); + }, + get hidden() { + return h(N); + }, + get draggable() { + return h(E); + }, + get selectable() { + return h(M); + }, + get connectable() { + return h(D); + }, + get deletable() { + return h(V); + }, + get positionX() { + return h(v).internals.positionAbsolute.x; + }, + get positionY() { + return h(v).internals.positionAbsolute.y; + }, + get isParent() { + return h(A); + }, + get style() { + return h(v).style; + }, + get class() { + return h(v).class; + }, + get type() { + return h(O); + }, + get sourcePosition() { + return h(v).sourcePosition; + }, + get targetPosition() { + return h(v).targetPosition; + }, + get dragging() { + return h(v).dragging; + }, + get zIndex() { + return h(R); + }, + get dragHandle() { + return h(v).dragHandle; + }, + get initialized() { + return h(S); + }, + get width() { + return h(v).width; + }, + get height() { + return h(v).height; + }, + get initialWidth() { + return h(v).initialWidth; + }, + get initialHeight() { + return h(v).initialHeight; + }, + get measuredWidth() { + return h(v).measured.width; + }, + get measuredHeight() { + return h(v).measured.height; + }, + get parentId() { + return h(v).parentId; + }, + resizeObserver: C, + get nodeClickDistance() { + return u(); + }, + $$events: { + nodeclick(T) { + Ve.call(this, t, T); + }, + nodemouseenter(T) { + Ve.call(this, t, T); + }, + nodemousemove(T) { + Ve.call(this, t, T); + }, + nodemouseleave(T) { + Ve.call(this, t, T); + }, + nodedrag(T) { + Ve.call(this, t, T); + }, + nodedragstart(T) { + Ve.call(this, t, T); + }, + nodedragstop(T) { + Ve.call(this, t, T); + }, + nodecontextmenu(T) { + Ve.call(this, t, T); + } + } + }); + }), Z($), L(e, $); + var m = fe({ + get nodeClickDistance() { + return u(); + }, + set nodeClickDistance(_) { + u(_), y(); + } + }); + return r(), m; +} +ae(Dc, { nodeClickDistance: {} }, [], [], !0); +var M2 = /* @__PURE__ */ _e('<svg><g role="img"><!></g></svg>'); +function Ac(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(W, "$edgeTypes", n), i = () => Q(ue, "$flowId", n), s = () => Q(me, "$elementsSelectable", n), a = () => Q(ee, "$edgeLookup", n), l = re(void 0, !0), u = re(void 0, !0), c = re(void 0, !0), f = re(void 0, !0), d = re(void 0, !0); + let g = w(t, "id", 13), p = w(t, "type", 13, "default"), x = w(t, "source", 13, ""), C = w(t, "target", 13, ""), $ = w(t, "data", 29, () => ({})), m = w(t, "style", 13, void 0), _ = w(t, "zIndex", 13, void 0), v = w(t, "animated", 13, !1), b = w(t, "selected", 13, !1), N = w(t, "selectable", 13, void 0), E = w(t, "deletable", 13, void 0), M = w(t, "hidden", 13, !1), D = w(t, "label", 13, void 0), V = w(t, "labelStyle", 13, void 0), A = w(t, "markerStart", 13, void 0), O = w(t, "markerEnd", 13, void 0), R = w(t, "sourceHandle", 13, void 0), S = w(t, "targetHandle", 13, void 0), T = w(t, "sourceX", 13), k = w(t, "sourceY", 13), P = w(t, "targetX", 13), H = w(t, "targetY", 13), I = w(t, "sourcePosition", 13), B = w(t, "targetPosition", 13), F = w(t, "ariaLabel", 13, void 0), K = w(t, "interactionWidth", 13, void 0), ie = w(t, "class", 13, ""); + Tr("svelteflow__edge_id", g()); + const { + edgeLookup: ee, + edgeTypes: W, + flowId: ue, + elementsSelectable: me + } = Ue(), Ce = Oi(), ge = bc(); + function ze(te) { + const Fe = a().get(g()); + Fe && (ge(g()), Ce("edgeclick", { event: te, edge: Fe })); + } + function G(te, Fe) { + const Le = a().get(g()); + Le && Ce(Fe, { event: te, edge: Le }); + } + he(() => j(p()), () => { + U(l, p() || "default"); + }), he( + () => (o(), h(l), xi), + () => { + U(u, o()[h(l)] || xi); + } + ), he( + () => (j(A()), i()), + () => { + U(c, A() ? `url('#${Vs(A(), i())}')` : void 0); + } + ), he( + () => (j(O()), i()), + () => { + U(f, O() ? `url('#${Vs(O(), i())}')` : void 0); + } + ), he( + () => (j(N()), s()), + () => { + U(d, N() ?? s()); + } + ), gt(), He(!0); + var se = et(), Te = be(se); + { + var Ae = (te) => { + var Fe = M2(), Le = X(Fe); + let Qe; + var oe = X(Le); + const ve = /* @__PURE__ */ pe(() => E() ?? !0); + yu(oe, () => h(u), (xe, Oe) => { + Oe(xe, { + get id() { + return g(); + }, + get source() { + return x(); + }, + get target() { + return C(); + }, + get sourceX() { + return T(); + }, + get sourceY() { + return k(); + }, + get targetX() { + return P(); + }, + get targetY() { + return H(); + }, + get sourcePosition() { + return I(); + }, + get targetPosition() { + return B(); + }, + get animated() { + return v(); + }, + get selected() { + return b(); + }, + get label() { + return D(); + }, + get labelStyle() { + return V(); + }, + get data() { + return $(); + }, + get style() { + return m(); + }, + get interactionWidth() { + return K(); + }, + get selectable() { + return h(d); + }, + get deletable() { + return h(ve); + }, + get type() { + return h(l); + }, + get sourceHandleId() { + return R(); + }, + get targetHandleId() { + return S(); + }, + get markerStart() { + return h(c); + }, + get markerEnd() { + return h(f); + } + }); + }), Z(Le), Z(Fe), Ee( + (xe) => { + st(Fe, "z-index", _()), Qe = kt(Le, 0, bn(xe), null, Qe, { + animated: v(), + selected: b(), + selectable: h(d) + }), ce(Le, "data-id", g()), ce(Le, "aria-label", F() === null ? void 0 : F() ? F() : `Edge from ${x()} to ${C()}`); + }, + [ + () => Et(["svelte-flow__edge", ie()]) + ], + pe + ), Ye("click", Le, ze), Ye("contextmenu", Le, (xe) => { + G(xe, "edgecontextmenu"); + }), Ye("mouseenter", Le, (xe) => { + G(xe, "edgemouseenter"); + }), Ye("mouseleave", Le, (xe) => { + G(xe, "edgemouseleave"); + }), L(te, Fe); + }; + ke(Te, (te) => { + M() || te(Ae); + }); + } + L(e, se); + var Xe = fe({ + get id() { + return g(); + }, + set id(te) { + g(te), y(); + }, + get type() { + return p(); + }, + set type(te) { + p(te), y(); + }, + get source() { + return x(); + }, + set source(te) { + x(te), y(); + }, + get target() { + return C(); + }, + set target(te) { + C(te), y(); + }, + get data() { + return $(); + }, + set data(te) { + $(te), y(); + }, + get style() { + return m(); + }, + set style(te) { + m(te), y(); + }, + get zIndex() { + return _(); + }, + set zIndex(te) { + _(te), y(); + }, + get animated() { + return v(); + }, + set animated(te) { + v(te), y(); + }, + get selected() { + return b(); + }, + set selected(te) { + b(te), y(); + }, + get selectable() { + return N(); + }, + set selectable(te) { + N(te), y(); + }, + get deletable() { + return E(); + }, + set deletable(te) { + E(te), y(); + }, + get hidden() { + return M(); + }, + set hidden(te) { + M(te), y(); + }, + get label() { + return D(); + }, + set label(te) { + D(te), y(); + }, + get labelStyle() { + return V(); + }, + set labelStyle(te) { + V(te), y(); + }, + get markerStart() { + return A(); + }, + set markerStart(te) { + A(te), y(); + }, + get markerEnd() { + return O(); + }, + set markerEnd(te) { + O(te), y(); + }, + get sourceHandle() { + return R(); + }, + set sourceHandle(te) { + R(te), y(); + }, + get targetHandle() { + return S(); + }, + set targetHandle(te) { + S(te), y(); + }, + get sourceX() { + return T(); + }, + set sourceX(te) { + T(te), y(); + }, + get sourceY() { + return k(); + }, + set sourceY(te) { + k(te), y(); + }, + get targetX() { + return P(); + }, + set targetX(te) { + P(te), y(); + }, + get targetY() { + return H(); + }, + set targetY(te) { + H(te), y(); + }, + get sourcePosition() { + return I(); + }, + set sourcePosition(te) { + I(te), y(); + }, + get targetPosition() { + return B(); + }, + set targetPosition(te) { + B(te), y(); + }, + get ariaLabel() { + return F(); + }, + set ariaLabel(te) { + F(te), y(); + }, + get interactionWidth() { + return K(); + }, + set interactionWidth(te) { + K(te), y(); + }, + get class() { + return ie(); + }, + set class(te) { + ie(te), y(); + } + }); + return r(), Xe; +} +ae( + Ac, + { + id: {}, + type: {}, + source: {}, + target: {}, + data: {}, + style: {}, + zIndex: {}, + animated: {}, + selected: {}, + selectable: {}, + deletable: {}, + hidden: {}, + label: {}, + labelStyle: {}, + markerStart: {}, + markerEnd: {}, + sourceHandle: {}, + targetHandle: {}, + sourceX: {}, + sourceY: {}, + targetX: {}, + targetY: {}, + sourcePosition: {}, + targetPosition: {}, + ariaLabel: {}, + interactionWidth: {}, + class: {} + }, + [], + [], + !0 +); +function Lc(e, t) { + de(t, !1); + let n = w(t, "onMount", 12, void 0), r = w(t, "onDestroy", 12, void 0); + return un(() => { + var o; + return (o = n()) == null || o(), r(); + }), He(), fe({ + get onMount() { + return n(); + }, + set onMount(o) { + n(o), y(); + }, + get onDestroy() { + return r(); + }, + set onDestroy(o) { + r(o), y(); + } + }); +} +ae(Lc, { onMount: {}, onDestroy: {} }, [], [], !0); +var T2 = /* @__PURE__ */ _e("<defs></defs>"); +function Oc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(i, "$markers", n), { markers: i } = Ue(); + He(); + var s = T2(); + Yt(s, 5, o, (a) => a.id, (a, l) => { + Ic(a, ut(() => h(l))); + }), Z(s), L(e, s), fe(), r(); +} +ae(Oc, {}, [], [], !0); +var H2 = /* @__PURE__ */ _e('<polyline stroke-linecap="round" stroke-linejoin="round" fill="none" points="-5,-4 0,0 -5,4"></polyline>'), V2 = /* @__PURE__ */ _e('<polyline stroke-linecap="round" stroke-linejoin="round" points="-5,-4 0,0 -5,4 -5,-4"></polyline>'), D2 = /* @__PURE__ */ _e('<marker class="svelte-flow__arrowhead" viewBox="-10 -10 20 20" refX="0" refY="0"><!></marker>'); +function Ic(e, t) { + de(t, !1); + let n = w(t, "id", 12), r = w(t, "type", 12), o = w(t, "width", 12, 12.5), i = w(t, "height", 12, 12.5), s = w(t, "markerUnits", 12, "strokeWidth"), a = w(t, "orient", 12, "auto-start-reverse"), l = w(t, "color", 12, void 0), u = w(t, "strokeWidth", 12, void 0); + He(); + var c = D2(), f = X(c); + { + var d = (p) => { + var x = H2(); + Ee(() => { + ce(x, "stroke", l()), ce(x, "stroke-width", u()); + }), L(p, x); + }, g = (p, x) => { + { + var C = ($) => { + var m = V2(); + Ee(() => { + ce(m, "stroke", l()), ce(m, "stroke-width", u()), ce(m, "fill", l()); + }), L($, m); + }; + ke( + p, + ($) => { + r() === mo.ArrowClosed && $(C); + }, + x + ); + } + }; + ke(f, (p) => { + r() === mo.Arrow ? p(d) : p(g, !1); + }); + } + return Z(c), Ee(() => { + ce(c, "id", n()), ce(c, "markerWidth", `${o()}`), ce(c, "markerHeight", `${i()}`), ce(c, "markerUnits", s()), ce(c, "orient", a()); + }), L(e, c), fe({ + get id() { + return n(); + }, + set id(p) { + n(p), y(); + }, + get type() { + return r(); + }, + set type(p) { + r(p), y(); + }, + get width() { + return o(); + }, + set width(p) { + o(p), y(); + }, + get height() { + return i(); + }, + set height(p) { + i(p), y(); + }, + get markerUnits() { + return s(); + }, + set markerUnits(p) { + s(p), y(); + }, + get orient() { + return a(); + }, + set orient(p) { + a(p), y(); + }, + get color() { + return l(); + }, + set color(p) { + l(p), y(); + }, + get strokeWidth() { + return u(); + }, + set strokeWidth(p) { + u(p), y(); + } + }); +} +ae( + Ic, + { + id: {}, + type: {}, + width: {}, + height: {}, + markerUnits: {}, + orient: {}, + color: {}, + strokeWidth: {} + }, + [], + [], + !0 +); +var A2 = /* @__PURE__ */ ne('<div class="svelte-flow__edges"><svg class="svelte-flow__marker"><!></svg> <!> <!></div>'); +function zc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(a, "$visibleEdges", n), i = () => Q(c, "$elementsSelectable", n); + let s = w(t, "defaultEdgeOptions", 12); + const { + visibleEdges: a, + edgesInitialized: l, + edges: { setDefaultOptions: u }, + elementsSelectable: c + } = Ue(); + un(() => { + s() && u(s()); + }), He(); + var f = A2(), d = X(f), g = X(d); + Oc(g, {}), Z(d); + var p = z(d, 2); + Yt(p, 1, o, (m) => m.id, (m, _) => { + const v = /* @__PURE__ */ pe(() => h(_).selectable ?? i()), b = /* @__PURE__ */ pe(() => h(_).type || "default"); + Ac(m, { + get id() { + return h(_).id; + }, + get source() { + return h(_).source; + }, + get target() { + return h(_).target; + }, + get data() { + return h(_).data; + }, + get style() { + return h(_).style; + }, + get animated() { + return h(_).animated; + }, + get selected() { + return h(_).selected; + }, + get selectable() { + return h(v); + }, + get deletable() { + return h(_).deletable; + }, + get hidden() { + return h(_).hidden; + }, + get label() { + return h(_).label; + }, + get labelStyle() { + return h(_).labelStyle; + }, + get markerStart() { + return h(_).markerStart; + }, + get markerEnd() { + return h(_).markerEnd; + }, + get sourceHandle() { + return h(_).sourceHandle; + }, + get targetHandle() { + return h(_).targetHandle; + }, + get sourceX() { + return h(_).sourceX; + }, + get sourceY() { + return h(_).sourceY; + }, + get targetX() { + return h(_).targetX; + }, + get targetY() { + return h(_).targetY; + }, + get sourcePosition() { + return h(_).sourcePosition; + }, + get targetPosition() { + return h(_).targetPosition; + }, + get ariaLabel() { + return h(_).ariaLabel; + }, + get interactionWidth() { + return h(_).interactionWidth; + }, + get class() { + return h(_).class; + }, + get type() { + return h(b); + }, + get zIndex() { + return h(_).zIndex; + }, + $$events: { + edgeclick(N) { + Ve.call(this, t, N); + }, + edgecontextmenu(N) { + Ve.call(this, t, N); + }, + edgemouseenter(N) { + Ve.call(this, t, N); + }, + edgemouseleave(N) { + Ve.call(this, t, N); + } + } + }); + }); + var x = z(p, 2); + { + var C = (m) => { + Lc(m, { + onMount: () => { + li(l, !0); + }, + onDestroy: () => { + li(l, !1); + } + }); + }; + ke(x, (m) => { + o().length > 0 && m(C); + }); + } + Z(f), L(e, f); + var $ = fe({ + get defaultEdgeOptions() { + return s(); + }, + set defaultEdgeOptions(m) { + s(m), y(); + } + }); + return r(), $; +} +ae(zc, { defaultEdgeOptions: {} }, [], [], !0); +var L2 = /* @__PURE__ */ ne('<div class="svelte-flow__selection svelte-1iugwpu"></div>'); +const O2 = { + hash: "svelte-1iugwpu", + code: ".svelte-flow__selection.svelte-1iugwpu {position:absolute;top:0;left:0;}" +}; +function ha(e, t) { + de(t, !1), Je(e, O2); + let n = w(t, "x", 12, 0), r = w(t, "y", 12, 0), o = w(t, "width", 12, 0), i = w(t, "height", 12, 0), s = w(t, "isVisible", 12, !0); + var a = et(), l = be(a); + { + var u = (c) => { + var f = L2(); + Ee(() => { + st(f, "width", typeof o() == "string" ? o() : `${o()}px`), st(f, "height", typeof i() == "string" ? i() : `${i()}px`), st(f, "transform", `translate(${n()}px, ${r()}px)`); + }), L(c, f); + }; + ke(l, (c) => { + s() && c(u); + }); + } + return L(e, a), fe({ + get x() { + return n(); + }, + set x(c) { + n(c), y(); + }, + get y() { + return r(); + }, + set y(c) { + r(c), y(); + }, + get width() { + return o(); + }, + set width(c) { + o(c), y(); + }, + get height() { + return i(); + }, + set height(c) { + i(c), y(); + }, + get isVisible() { + return s(); + }, + set isVisible(c) { + s(c), y(); + } + }); +} +ae( + ha, + { + x: {}, + y: {}, + width: {}, + height: {}, + isVisible: {} + }, + [], + [], + !0 +); +function Rc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(s, "$selectionRect", n), i = () => Q(a, "$selectionRectMode", n), { selectionRect: s, selectionRectMode: a } = Ue(); + He(); + const l = /* @__PURE__ */ pe(() => !!(o() && i() === "user")), u = /* @__PURE__ */ pe(() => { + var g; + return (g = o()) == null ? void 0 : g.width; + }), c = /* @__PURE__ */ pe(() => { + var g; + return (g = o()) == null ? void 0 : g.height; + }), f = /* @__PURE__ */ pe(() => { + var g; + return (g = o()) == null ? void 0 : g.x; + }), d = /* @__PURE__ */ pe(() => { + var g; + return (g = o()) == null ? void 0 : g.y; + }); + ha(e, { + get isVisible() { + return h(l); + }, + get width() { + return h(u); + }, + get height() { + return h(c); + }, + get x() { + return h(f); + }, + get y() { + return h(d); + } + }), fe(), r(); +} +ae(Rc, {}, [], [], !0); +var I2 = /* @__PURE__ */ ne('<div class="selection-wrapper nopan svelte-5pxri" role="button" tabindex="-1"><!></div>'); +const z2 = { + hash: "svelte-5pxri", + code: ".selection-wrapper.svelte-5pxri {position:absolute;top:0;left:0;z-index:7;pointer-events:all;}" +}; +function Bc(e, t) { + de(t, !1), Je(e, z2); + const [n, r] = tt(), o = () => Q(l, "$selectionRectMode", n), i = () => Q(c, "$nodeLookup", n), s = () => Q(u, "$nodes", n), a = Ue(), { selectionRectMode: l, nodes: u, nodeLookup: c } = a, f = Oi(); + let d = re(null); + function g(m) { + const _ = s().filter((v) => v.selected); + f("selectioncontextmenu", { nodes: _, event: m }); + } + function p(m) { + const _ = s().filter((v) => v.selected); + f("selectionclick", { nodes: _, event: m }); + } + he( + () => (o(), i(), s()), + () => { + o() === "nodes" && (U(d, No(i(), { filter: (m) => !!m.selected })), s()); + } + ), gt(), He(); + var x = et(), C = be(x); + { + var $ = (m) => { + var _ = I2(), v = X(_); + ha(v, { width: "100%", height: "100%", x: 0, y: 0 }), Z(_), vt(_, (b, N) => $r == null ? void 0 : $r(b, N), () => ({ + disabled: !1, + store: a, + onDrag: (b, N, E, M) => { + f("nodedrag", { event: b, targetNode: null, nodes: M }); + }, + onDragStart: (b, N, E, M) => { + f("nodedragstart", { event: b, targetNode: null, nodes: M }); + }, + onDragStop: (b, N, E, M) => { + f("nodedragstop", { event: b, targetNode: null, nodes: M }); + } + })), Ot(() => Ye("contextmenu", _, g)), Ot(() => Ye("click", _, p)), Ot(() => Ye("keyup", _, () => { + })), Ee(() => ce(_, "style", `width: ${h(d).width ?? ""}px; height: ${h(d).height ?? ""}px; transform: translate(${h(d).x ?? ""}px, ${h(d).y ?? ""}px)`)), L(m, _); + }; + ke(C, (m) => { + o() === "nodes" && h(d) && Nn(h(d).x) && Nn(h(d).y) && m($); + }); + } + L(e, x), fe(), r(); +} +ae(Bc, {}, [], [], !0); +function We(e, t) { + let { enabled: n = !0, trigger: r, type: o = "keydown" } = t; + function i(s) { + const a = Array.isArray(r) ? r : [r], l = { + alt: s.altKey, + ctrl: s.ctrlKey, + shift: s.shiftKey, + meta: s.metaKey + }; + for (const u of a) { + const c = { + modifier: [], + preventDefault: !1, + enabled: !0, + ...u + }, { modifier: f, key: d, callback: g, preventDefault: p, enabled: x } = c; + if (x) { + if (f.length && !(Array.isArray(f) ? f : [f]).map( + (m) => typeof m == "string" ? [m] : m + ).some( + (m) => m.every((_) => l[_]) + )) + continue; + if (s.key === d) { + p && s.preventDefault(); + const C = { + node: e, + trigger: c, + originalEvent: s + }; + e.dispatchEvent(new CustomEvent("shortcut", { detail: C })), g == null || g(C); + } + } + } + } + return n && e.addEventListener(o, i), { + update: (s) => { + const { enabled: a = !0, type: l = "keydown" } = s; + n && (!a || o !== l) ? e.removeEventListener(o, i) : !n && a && e.addEventListener(l, i), n = a, o = l, r = s.trigger; + }, + destroy: () => { + e.removeEventListener(o, i); + } + }; +} +function Yc(e, t) { + de(t, !1); + let n = w(t, "selectionKey", 12, "Shift"), r = w(t, "multiSelectionKey", 28, () => yi() ? "Meta" : "Control"), o = w(t, "deleteKey", 12, "Backspace"), i = w(t, "panActivationKey", 12, " "), s = w(t, "zoomActivationKey", 28, () => yi() ? "Meta" : "Control"); + const { + selectionKeyPressed: a, + multiselectionKeyPressed: l, + deleteKeyPressed: u, + panActivationKeyPressed: c, + zoomActivationKeyPressed: f, + selectionRect: d + } = Ue(); + function g(m) { + return m !== null && typeof m == "object"; + } + function p(m) { + return g(m) ? m.modifier || [] : []; + } + function x(m) { + return m == null ? "" : g(m) ? m.key : m; + } + function C(m, _) { + return (Array.isArray(m) ? m : [m]).map((b) => { + const N = x(b); + return { + key: N, + modifier: p(b), + enabled: N !== null, + callback: _ + }; + }); + } + function $() { + d.set(null), a.set(!1), l.set(!1), u.set(!1), c.set(!1), f.set(!1); + } + return He(), Ye("blur", Nt, $), Ye("contextmenu", Nt, $), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(n(), () => a.set(!0)), + type: "keydown" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(n(), () => a.set(!1)), + type: "keyup" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(r(), () => l.set(!0)), + type: "keydown" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(r(), () => l.set(!1)), + type: "keyup" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(o(), (m) => { + !(m.originalEvent.ctrlKey || m.originalEvent.metaKey || m.originalEvent.shiftKey) && !w0(m.originalEvent) && u.set(!0); + }), + type: "keydown" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(o(), () => u.set(!1)), + type: "keyup" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(i(), () => c.set(!0)), + type: "keydown" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(i(), () => c.set(!1)), + type: "keyup" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(s(), () => f.set(!0)), + type: "keydown" + })), vt(Nt, (m, _) => We == null ? void 0 : We(m, _), () => ({ + trigger: C(s(), () => f.set(!1)), + type: "keyup" + })), fe({ + get selectionKey() { + return n(); + }, + set selectionKey(m) { + n(m), y(); + }, + get multiSelectionKey() { + return r(); + }, + set multiSelectionKey(m) { + r(m), y(); + }, + get deleteKey() { + return o(); + }, + set deleteKey(m) { + o(m), y(); + }, + get panActivationKey() { + return i(); + }, + set panActivationKey(m) { + i(m), y(); + }, + get zoomActivationKey() { + return s(); + }, + set zoomActivationKey(m) { + s(m), y(); + } + }); +} +ae( + Yc, + { + selectionKey: {}, + multiSelectionKey: {}, + deleteKey: {}, + panActivationKey: {}, + zoomActivationKey: {} + }, + [], + [], + !0 +); +var R2 = /* @__PURE__ */ _e('<path fill="none" class="svelte-flow__connection-path"></path>'), B2 = /* @__PURE__ */ _e('<svg class="svelte-flow__connectionline"><g><!><!></g></svg>'); +function Zc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(g, "$connection", n), i = () => Q(p, "$connectionLineType", n), s = () => Q(f, "$width", n), a = () => Q(d, "$height", n); + let l = w(t, "containerStyle", 12, ""), u = w(t, "style", 12, ""), c = w(t, "isCustomComponent", 12, !1); + const { + width: f, + height: d, + connection: g, + connectionLineType: p + } = Ue(); + let x = re(null); + he( + () => (o(), j(c()), i(), h(x), Hs), + () => { + if (o().inProgress && !c()) { + const { from: v, to: b, fromPosition: N, toPosition: E } = o(), M = { + sourceX: v.x, + sourceY: v.y, + sourcePosition: N, + targetX: b.x, + targetY: b.y, + targetPosition: E + }; + switch (i()) { + case Cr.Bezier: + ((D) => U(x, D[0]))(sc(M)); + break; + case Cr.Step: + ((D) => U(x, D[0]))(wi({ ...M, borderRadius: 0 })); + break; + case Cr.SmoothStep: + ((D) => U(x, D[0]))(wi(M)); + break; + default: + ((D) => U(x, D[0]))(Hs(M)); + } + } + } + ), gt(), He(); + var C = et(), $ = be(C); + { + var m = (v) => { + var b = B2(), N = X(b), E = X(N); + pt(E, t, "connectionLine", {}); + var M = z(E); + { + var D = (V) => { + var A = R2(); + Ee(() => { + ce(A, "d", h(x)), ce(A, "style", u()); + }), L(V, A); + }; + ke(M, (V) => { + c() || V(D); + }); + } + Z(N), Z(b), Ee( + (V) => { + ce(b, "width", s()), ce(b, "height", a()), ce(b, "style", l()), kt(N, 0, bn(V)); + }, + [ + () => Et([ + "svelte-flow__connection", + c0(o().isValid) + ]) + ], + pe + ), L(v, b); + }; + ke($, (v) => { + o().inProgress && v(m); + }); + } + L(e, C); + var _ = fe({ + get containerStyle() { + return l(); + }, + set containerStyle(v) { + l(v), y(); + }, + get style() { + return u(); + }, + set style(v) { + u(v), y(); + }, + get isCustomComponent() { + return c(); + }, + set isCustomComponent(v) { + c(v), y(); + } + }); + return r(), _; +} +ae( + Zc, + { + containerStyle: {}, + style: {}, + isCustomComponent: {} + }, + ["connectionLine"], + [], + !0 +); +var Y2 = /* @__PURE__ */ ne("<div><!></div>"); +function Ho(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]), r = nt(n, ["position", "style", "class"]); + de(t, !1); + const [o, i] = tt(), s = () => Q(f, "$selectionRectMode", o), a = re(); + let l = w(t, "position", 12, "top-right"), u = w(t, "style", 12, void 0), c = w(t, "class", 12, void 0); + const { selectionRectMode: f } = Ue(); + he(() => j(l()), () => { + U(a, `${l()}`.split("-")); + }), gt(), He(); + var d = Y2(); + let g; + var p = X(d); + pt(p, t, "default", {}), Z(d), Ee( + (C) => { + g = on(d, g, { + class: C, + style: u(), + ...r + }), st(d, "pointer-events", s() ? "none" : ""); + }, + [ + () => Et([ + "svelte-flow__panel", + c(), + ...h(a) + ]) + ], + pe + ), L(e, d); + var x = fe({ + get position() { + return l(); + }, + set position(C) { + l(C), y(); + }, + get style() { + return u(); + }, + set style(C) { + u(C), y(); + }, + get class() { + return c(); + }, + set class(C) { + c(C), y(); + } + }); + return i(), x; +} +ae(Ho, { position: {}, style: {}, class: {} }, ["default"], [], !0); +var Z2 = /* @__PURE__ */ ne('<a href="https://svelteflow.dev" target="_blank" rel="noopener noreferrer" aria-label="Svelte Flow attribution">Svelte Flow</a>'); +function Xc(e, t) { + de(t, !1); + let n = w(t, "proOptions", 12, void 0), r = w(t, "position", 12, "bottom-right"); + He(); + var o = et(), i = be(o); + { + var s = (a) => { + Ho(a, { + get position() { + return r(); + }, + class: "svelte-flow__attribution", + "data-message": "Feel free to remove the attribution or check out how you could support us: https://svelteflow.dev/support-us", + children: (l, u) => { + var c = Z2(); + L(l, c); + }, + $$slots: { default: !0 } + }); + }; + ke(i, (a) => { + var l; + (l = n()) != null && l.hideAttribution || a(s); + }); + } + return L(e, o), fe({ + get proOptions() { + return n(); + }, + set proOptions(a) { + n(a), y(); + }, + get position() { + return r(); + }, + set position(a) { + r(a), y(); + } + }); +} +ae(Xc, { proOptions: {}, position: {} }, [], [], !0); +function El(e, { nodeTypes: t, edgeTypes: n, minZoom: r, maxZoom: o, translateExtent: i, paneClickDistance: s }) { + t !== void 0 && e.setNodeTypes(t), n !== void 0 && e.setEdgeTypes(n), r !== void 0 && e.setMinZoom(r), o !== void 0 && e.setMaxZoom(o), i !== void 0 && e.setTranslateExtent(i), s !== void 0 && e.setPaneClickDistance(s); +} +const X2 = (e) => Object.keys(e); +function Sl(e, t) { + X2(t).forEach((n) => { + const r = t[n]; + r !== void 0 && e[n].set(r); + }); +} +function F2() { + return typeof window > "u" || !window.matchMedia ? null : window.matchMedia("(prefers-color-scheme: dark)"); +} +function W2(e = "light") { + return Ft("light", (n) => { + if (e !== "system") { + n(e); + return; + } + const r = F2(), o = () => n(r != null && r.matches ? "dark" : "light"); + return n(r != null && r.matches ? "dark" : "light"), r == null || r.addEventListener("change", o), () => { + r == null || r.removeEventListener("change", o); + }; + }); +} +var K2 = /* @__PURE__ */ ne('<!> <!> <div class="svelte-flow__edgelabel-renderer"></div> <div class="svelte-flow__viewport-portal"></div> <!> <!>', 1), q2 = /* @__PURE__ */ ne("<!> <!>", 1), G2 = /* @__PURE__ */ ne("<div><!> <!> <!> <!></div>"); +const U2 = { + hash: "svelte-12wlba6", + code: ".svelte-flow.svelte-12wlba6 {width:100%;height:100%;overflow:hidden;position:relative;z-index:0;background-color:var(--background-color, var(--background-color-default));}:root {--background-color-default: #fff;--background-pattern-color-default: #ddd;--minimap-mask-color-default: rgb(240, 240, 240, 0.6);--minimap-mask-stroke-color-default: none;--minimap-mask-stroke-width-default: 1;--controls-button-background-color-default: #fefefe;--controls-button-background-color-hover-default: #f4f4f4;--controls-button-color-default: inherit;--controls-button-color-hover-default: inherit;--controls-button-border-color-default: #eee;}" +}; +function Fc(e, t) { + const n = p1(t), r = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]), o = nt(r, [ + "id", + "nodes", + "edges", + "fitView", + "fitViewOptions", + "minZoom", + "maxZoom", + "initialViewport", + "viewport", + "nodeTypes", + "edgeTypes", + "selectionKey", + "selectionMode", + "panActivationKey", + "multiSelectionKey", + "zoomActivationKey", + "nodesDraggable", + "nodesConnectable", + "nodeDragThreshold", + "elementsSelectable", + "snapGrid", + "deleteKey", + "connectionRadius", + "connectionLineType", + "connectionMode", + "connectionLineStyle", + "connectionLineContainerStyle", + "onMoveStart", + "onMove", + "onMoveEnd", + "isValidConnection", + "translateExtent", + "nodeExtent", + "onlyRenderVisibleElements", + "panOnScrollMode", + "preventScrolling", + "zoomOnScroll", + "zoomOnDoubleClick", + "zoomOnPinch", + "panOnScroll", + "panOnDrag", + "selectionOnDrag", + "autoPanOnConnect", + "autoPanOnNodeDrag", + "onerror", + "ondelete", + "onedgecreate", + "attributionPosition", + "proOptions", + "defaultEdgeOptions", + "width", + "height", + "colorMode", + "onconnect", + "onconnectstart", + "onconnectend", + "onbeforedelete", + "oninit", + "nodeOrigin", + "paneClickDistance", + "nodeClickDistance", + "defaultMarkerColor", + "style", + "class" + ]); + de(t, !1), Je(e, U2); + const [i, s] = tt(), a = () => Q(_(), "$viewport", i), l = () => Q(ji, "$initialized", i), u = () => Q(h(c), "$colorModeClass", i), c = re(); + let f = w(t, "id", 12, "1"), d = w(t, "nodes", 12), g = w(t, "edges", 12), p = w(t, "fitView", 12, void 0), x = w(t, "fitViewOptions", 12, void 0), C = w(t, "minZoom", 12, void 0), $ = w(t, "maxZoom", 12, void 0), m = w(t, "initialViewport", 12, void 0), _ = w(t, "viewport", 12, void 0), v = w(t, "nodeTypes", 12, void 0), b = w(t, "edgeTypes", 12, void 0), N = w(t, "selectionKey", 12, void 0), E = w(t, "selectionMode", 12, void 0), M = w(t, "panActivationKey", 12, void 0), D = w(t, "multiSelectionKey", 12, void 0), V = w(t, "zoomActivationKey", 12, void 0), A = w(t, "nodesDraggable", 12, void 0), O = w(t, "nodesConnectable", 12, void 0), R = w(t, "nodeDragThreshold", 12, void 0), S = w(t, "elementsSelectable", 12, void 0), T = w(t, "snapGrid", 12, void 0), k = w(t, "deleteKey", 12, void 0), P = w(t, "connectionRadius", 12, void 0), H = w(t, "connectionLineType", 12, void 0), I = w(t, "connectionMode", 28, () => cr.Strict), B = w(t, "connectionLineStyle", 12, ""), F = w(t, "connectionLineContainerStyle", 12, ""), K = w(t, "onMoveStart", 12, void 0), ie = w(t, "onMove", 12, void 0), ee = w(t, "onMoveEnd", 12, void 0), W = w(t, "isValidConnection", 12, void 0), ue = w(t, "translateExtent", 12, void 0), me = w(t, "nodeExtent", 12, void 0), Ce = w(t, "onlyRenderVisibleElements", 12, void 0), ge = w(t, "panOnScrollMode", 28, () => qn.Free), ze = w(t, "preventScrolling", 12, !0), G = w(t, "zoomOnScroll", 12, !0), se = w(t, "zoomOnDoubleClick", 12, !0), Te = w(t, "zoomOnPinch", 12, !0), Ae = w(t, "panOnScroll", 12, !1), Xe = w(t, "panOnDrag", 12, !0), te = w(t, "selectionOnDrag", 12, void 0), Fe = w(t, "autoPanOnConnect", 12, !0), Le = w(t, "autoPanOnNodeDrag", 12, !0), Qe = w(t, "onerror", 12, void 0), oe = w(t, "ondelete", 12, void 0), ve = w(t, "onedgecreate", 12, void 0), xe = w(t, "attributionPosition", 12, void 0), Oe = w(t, "proOptions", 12, void 0), ct = w(t, "defaultEdgeOptions", 12, void 0), lt = w(t, "width", 12, void 0), J = w(t, "height", 12, void 0), Re = w(t, "colorMode", 12, "light"), le = w(t, "onconnect", 12, void 0), fn = w(t, "onconnectstart", 12, void 0), Ut = w(t, "onconnectend", 12, void 0), gn = w(t, "onbeforedelete", 12, void 0), Ne = w(t, "oninit", 12, void 0), rt = w(t, "nodeOrigin", 12, void 0), ye = w(t, "paneClickDistance", 12, 0), ot = w(t, "nodeClickDistance", 12, 0), at = w(t, "defaultMarkerColor", 12, "#b1b1b7"), Xt = w(t, "style", 12, void 0), Kr = w(t, "class", 12, void 0), At = re(), St = re(), hn = re(); + const jt = a() || m(), ft = Uf(Wi) ? Ue() : w2({ + nodes: q(d()), + edges: q(g()), + width: lt(), + height: J(), + fitView: p(), + nodeOrigin: rt(), + nodeExtent: me() + }); + un(() => (ft.width.set(h(St)), ft.height.set(h(hn)), ft.domNode.set(h(At)), ft.syncNodeStores(d()), ft.syncEdgeStores(g()), ft.syncViewport(_()), p() !== void 0 && ft.fitViewOnInit.set(p()), x() && ft.fitViewOptions.set(x()), El(ft, { + nodeTypes: v(), + edgeTypes: b(), + minZoom: C(), + maxZoom: $(), + translateExtent: ue(), + paneClickDistance: ye() + }), () => { + ft.reset(); + })); + const { initialized: ji } = ft; + let nr = re(!1); + he( + () => (h(St), h(hn)), + () => { + h(St) !== void 0 && h(hn) !== void 0 && (ft.width.set(h(St)), ft.height.set(h(hn))); + } + ), he( + () => (h(nr), l(), j(Ne())), + () => { + var Y; + !h(nr) && l() && ((Y = Ne()) == null || Y(), U(nr, !0)); + } + ), he( + () => (j(f()), j(H()), j(P()), j(E()), j(T()), j(at()), j(A()), j(O()), j(S()), j(Ce()), j(W()), j(Fe()), j(Le()), j(Qe()), j(oe()), j(ve()), j(I()), j(R()), j(le()), j(fn()), j(Ut()), j(gn()), j(rt()), Sl), + () => { + const Y = { + flowId: f(), + connectionLineType: H(), + connectionRadius: P(), + selectionMode: E(), + snapGrid: T(), + defaultMarkerColor: at(), + nodesDraggable: A(), + nodesConnectable: O(), + elementsSelectable: S(), + onlyRenderVisibleElements: Ce(), + isValidConnection: W(), + autoPanOnConnect: Fe(), + autoPanOnNodeDrag: Le(), + onerror: Qe(), + ondelete: oe(), + onedgecreate: ve(), + connectionMode: I(), + nodeDragThreshold: R(), + onconnect: le(), + onconnectstart: fn(), + onconnectend: Ut(), + onbeforedelete: gn(), + nodeOrigin: rt() + }; + Sl(ft, Y); + } + ), he( + () => (j(v()), j(b()), j(C()), j($()), j(ue()), j(ye())), + () => { + El(ft, { + nodeTypes: v(), + edgeTypes: b(), + minZoom: C(), + maxZoom: $(), + translateExtent: ue(), + paneClickDistance: ye() + }); + } + ), he( + () => j(Re()), + () => { + k1(U(c, W2(Re())), "$colorModeClass", i); + } + ), gt(), He(); + var Jt = G2(); + let Io; + var zo = X(Jt); + Yc(zo, { + get selectionKey() { + return N(); + }, + get deleteKey() { + return k(); + }, + get panActivationKey() { + return M(); + }, + get multiSelectionKey() { + return D(); + }, + get zoomActivationKey() { + return V(); + } + }); + var Ro = z(zo, 2); + const Rd = /* @__PURE__ */ pe(() => ge() === void 0 ? qn.Free : ge()), Bd = /* @__PURE__ */ pe(() => ze() === void 0 ? !0 : ze()), Yd = /* @__PURE__ */ pe(() => G() === void 0 ? !0 : G()), Zd = /* @__PURE__ */ pe(() => se() === void 0 ? !0 : se()), Xd = /* @__PURE__ */ pe(() => Te() === void 0 ? !0 : Te()), Fd = /* @__PURE__ */ pe(() => Ae() === void 0 ? !1 : Ae()), Wd = /* @__PURE__ */ pe(() => Xe() === void 0 ? !0 : Xe()), Kd = /* @__PURE__ */ pe(() => ye() === void 0 ? 0 : ye()); + Mc(Ro, { + initialViewport: jt, + get onMoveStart() { + return K(); + }, + get onMove() { + return ie(); + }, + get onMoveEnd() { + return ee(); + }, + get panOnScrollMode() { + return h(Rd); + }, + get preventScrolling() { + return h(Bd); + }, + get zoomOnScroll() { + return h(Yd); + }, + get zoomOnDoubleClick() { + return h(Zd); + }, + get zoomOnPinch() { + return h(Xd); + }, + get panOnScroll() { + return h(Fd); + }, + get panOnDrag() { + return h(Wd); + }, + get paneClickDistance() { + return h(Kd); + }, + children: (Y, gw) => { + const Ud = /* @__PURE__ */ pe(() => Xe() === void 0 ? !0 : Xe()); + Tc(Y, { + get panOnDrag() { + return h(Ud); + }, + get selectionOnDrag() { + return te(); + }, + $$events: { + paneclick(qr) { + Ve.call(this, t, qr); + }, + panecontextmenu(qr) { + Ve.call(this, t, qr); + } + }, + children: (qr, hw) => { + var xa = q2(), ba = be(xa); + Hc(ba, { + children: (Jd, vw) => { + var Ca = K2(), ka = be(Ca); + zc(ka, { + get defaultEdgeOptions() { + return ct(); + }, + $$events: { + edgeclick(Be) { + Ve.call(this, t, Be); + }, + edgecontextmenu(Be) { + Ve.call(this, t, Be); + }, + edgemouseenter(Be) { + Ve.call(this, t, Be); + }, + edgemouseleave(Be) { + Ve.call(this, t, Be); + } + } + }); + var $a = z(ka, 2); + Zc($a, { + get containerStyle() { + return F(); + }, + get style() { + return B(); + }, + isCustomComponent: n.connectionLine, + $$slots: { + connectionLine: (Be, pw) => { + var Sa = et(), ef = be(Sa); + pt(ef, t, "connectionLine", {}), L(Be, Sa); + } + } + }); + var Ea = z($a, 6); + Dc(Ea, { + get nodeClickDistance() { + return ot(); + }, + $$events: { + nodeclick(Be) { + Ve.call(this, t, Be); + }, + nodemouseenter(Be) { + Ve.call(this, t, Be); + }, + nodemousemove(Be) { + Ve.call(this, t, Be); + }, + nodemouseleave(Be) { + Ve.call(this, t, Be); + }, + nodedragstart(Be) { + Ve.call(this, t, Be); + }, + nodedrag(Be) { + Ve.call(this, t, Be); + }, + nodedragstop(Be) { + Ve.call(this, t, Be); + }, + nodecontextmenu(Be) { + Ve.call(this, t, Be); + } + } + }); + var Qd = z(Ea, 2); + Bc(Qd, { + $$events: { + selectionclick(Be) { + Ve.call(this, t, Be); + }, + selectioncontextmenu(Be) { + Ve.call(this, t, Be); + }, + nodedragstart(Be) { + Ve.call(this, t, Be); + }, + nodedrag(Be) { + Ve.call(this, t, Be); + }, + nodedragstop(Be) { + Ve.call(this, t, Be); + } + } + }), L(Jd, Ca); + }, + $$slots: { default: !0 } + }); + var jd = z(ba, 2); + Rc(jd, {}), L(qr, xa); + }, + $$slots: { default: !0 } + }); + }, + $$slots: { default: !0 } + }); + var _a = z(Ro, 2); + Xc(_a, { + get proOptions() { + return Oe(); + }, + get position() { + return xe(); + } + }); + var qd = z(_a, 2); + pt(qd, t, "default", {}), Z(Jt), An(Jt, (Y) => U(At, Y), () => h(At)), Ee( + (Y) => Io = on( + Jt, + Io, + { + style: Xt(), + class: Y, + "data-testid": "svelte-flow__wrapper", + ...o, + role: "application" + }, + "svelte-12wlba6" + ), + [ + () => Et([ + "svelte-flow", + Kr(), + u() + ]) + ], + pe + ), Ra(Jt, "clientWidth", (Y) => U(St, Y)), Ra(Jt, "clientHeight", (Y) => U(hn, Y)), Ye("dragover", Jt, function(Y) { + Ve.call(this, t, Y); + }), Ye("drop", Jt, function(Y) { + Ve.call(this, t, Y); + }), L(e, Jt); + var Gd = fe({ + get id() { + return f(); + }, + set id(Y) { + f(Y), y(); + }, + get nodes() { + return d(); + }, + set nodes(Y) { + d(Y), y(); + }, + get edges() { + return g(); + }, + set edges(Y) { + g(Y), y(); + }, + get fitView() { + return p(); + }, + set fitView(Y) { + p(Y), y(); + }, + get fitViewOptions() { + return x(); + }, + set fitViewOptions(Y) { + x(Y), y(); + }, + get minZoom() { + return C(); + }, + set minZoom(Y) { + C(Y), y(); + }, + get maxZoom() { + return $(); + }, + set maxZoom(Y) { + $(Y), y(); + }, + get initialViewport() { + return m(); + }, + set initialViewport(Y) { + m(Y), y(); + }, + get viewport() { + return _(); + }, + set viewport(Y) { + _(Y), y(); + }, + get nodeTypes() { + return v(); + }, + set nodeTypes(Y) { + v(Y), y(); + }, + get edgeTypes() { + return b(); + }, + set edgeTypes(Y) { + b(Y), y(); + }, + get selectionKey() { + return N(); + }, + set selectionKey(Y) { + N(Y), y(); + }, + get selectionMode() { + return E(); + }, + set selectionMode(Y) { + E(Y), y(); + }, + get panActivationKey() { + return M(); + }, + set panActivationKey(Y) { + M(Y), y(); + }, + get multiSelectionKey() { + return D(); + }, + set multiSelectionKey(Y) { + D(Y), y(); + }, + get zoomActivationKey() { + return V(); + }, + set zoomActivationKey(Y) { + V(Y), y(); + }, + get nodesDraggable() { + return A(); + }, + set nodesDraggable(Y) { + A(Y), y(); + }, + get nodesConnectable() { + return O(); + }, + set nodesConnectable(Y) { + O(Y), y(); + }, + get nodeDragThreshold() { + return R(); + }, + set nodeDragThreshold(Y) { + R(Y), y(); + }, + get elementsSelectable() { + return S(); + }, + set elementsSelectable(Y) { + S(Y), y(); + }, + get snapGrid() { + return T(); + }, + set snapGrid(Y) { + T(Y), y(); + }, + get deleteKey() { + return k(); + }, + set deleteKey(Y) { + k(Y), y(); + }, + get connectionRadius() { + return P(); + }, + set connectionRadius(Y) { + P(Y), y(); + }, + get connectionLineType() { + return H(); + }, + set connectionLineType(Y) { + H(Y), y(); + }, + get connectionMode() { + return I(); + }, + set connectionMode(Y) { + I(Y), y(); + }, + get connectionLineStyle() { + return B(); + }, + set connectionLineStyle(Y) { + B(Y), y(); + }, + get connectionLineContainerStyle() { + return F(); + }, + set connectionLineContainerStyle(Y) { + F(Y), y(); + }, + get onMoveStart() { + return K(); + }, + set onMoveStart(Y) { + K(Y), y(); + }, + get onMove() { + return ie(); + }, + set onMove(Y) { + ie(Y), y(); + }, + get onMoveEnd() { + return ee(); + }, + set onMoveEnd(Y) { + ee(Y), y(); + }, + get isValidConnection() { + return W(); + }, + set isValidConnection(Y) { + W(Y), y(); + }, + get translateExtent() { + return ue(); + }, + set translateExtent(Y) { + ue(Y), y(); + }, + get nodeExtent() { + return me(); + }, + set nodeExtent(Y) { + me(Y), y(); + }, + get onlyRenderVisibleElements() { + return Ce(); + }, + set onlyRenderVisibleElements(Y) { + Ce(Y), y(); + }, + get panOnScrollMode() { + return ge(); + }, + set panOnScrollMode(Y) { + ge(Y), y(); + }, + get preventScrolling() { + return ze(); + }, + set preventScrolling(Y) { + ze(Y), y(); + }, + get zoomOnScroll() { + return G(); + }, + set zoomOnScroll(Y) { + G(Y), y(); + }, + get zoomOnDoubleClick() { + return se(); + }, + set zoomOnDoubleClick(Y) { + se(Y), y(); + }, + get zoomOnPinch() { + return Te(); + }, + set zoomOnPinch(Y) { + Te(Y), y(); + }, + get panOnScroll() { + return Ae(); + }, + set panOnScroll(Y) { + Ae(Y), y(); + }, + get panOnDrag() { + return Xe(); + }, + set panOnDrag(Y) { + Xe(Y), y(); + }, + get selectionOnDrag() { + return te(); + }, + set selectionOnDrag(Y) { + te(Y), y(); + }, + get autoPanOnConnect() { + return Fe(); + }, + set autoPanOnConnect(Y) { + Fe(Y), y(); + }, + get autoPanOnNodeDrag() { + return Le(); + }, + set autoPanOnNodeDrag(Y) { + Le(Y), y(); + }, + get onerror() { + return Qe(); + }, + set onerror(Y) { + Qe(Y), y(); + }, + get ondelete() { + return oe(); + }, + set ondelete(Y) { + oe(Y), y(); + }, + get onedgecreate() { + return ve(); + }, + set onedgecreate(Y) { + ve(Y), y(); + }, + get attributionPosition() { + return xe(); + }, + set attributionPosition(Y) { + xe(Y), y(); + }, + get proOptions() { + return Oe(); + }, + set proOptions(Y) { + Oe(Y), y(); + }, + get defaultEdgeOptions() { + return ct(); + }, + set defaultEdgeOptions(Y) { + ct(Y), y(); + }, + get width() { + return lt(); + }, + set width(Y) { + lt(Y), y(); + }, + get height() { + return J(); + }, + set height(Y) { + J(Y), y(); + }, + get colorMode() { + return Re(); + }, + set colorMode(Y) { + Re(Y), y(); + }, + get onconnect() { + return le(); + }, + set onconnect(Y) { + le(Y), y(); + }, + get onconnectstart() { + return fn(); + }, + set onconnectstart(Y) { + fn(Y), y(); + }, + get onconnectend() { + return Ut(); + }, + set onconnectend(Y) { + Ut(Y), y(); + }, + get onbeforedelete() { + return gn(); + }, + set onbeforedelete(Y) { + gn(Y), y(); + }, + get oninit() { + return Ne(); + }, + set oninit(Y) { + Ne(Y), y(); + }, + get nodeOrigin() { + return rt(); + }, + set nodeOrigin(Y) { + rt(Y), y(); + }, + get paneClickDistance() { + return ye(); + }, + set paneClickDistance(Y) { + ye(Y), y(); + }, + get nodeClickDistance() { + return ot(); + }, + set nodeClickDistance(Y) { + ot(Y), y(); + }, + get defaultMarkerColor() { + return at(); + }, + set defaultMarkerColor(Y) { + at(Y), y(); + }, + get style() { + return Xt(); + }, + set style(Y) { + Xt(Y), y(); + }, + get class() { + return Kr(); + }, + set class(Y) { + Kr(Y), y(); + } + }); + return s(), Gd; +} +ae( + Fc, + { + id: {}, + nodes: {}, + edges: {}, + fitView: {}, + fitViewOptions: {}, + minZoom: {}, + maxZoom: {}, + initialViewport: {}, + viewport: {}, + nodeTypes: {}, + edgeTypes: {}, + selectionKey: {}, + selectionMode: {}, + panActivationKey: {}, + multiSelectionKey: {}, + zoomActivationKey: {}, + nodesDraggable: {}, + nodesConnectable: {}, + nodeDragThreshold: {}, + elementsSelectable: {}, + snapGrid: {}, + deleteKey: {}, + connectionRadius: {}, + connectionLineType: {}, + connectionMode: {}, + connectionLineStyle: {}, + connectionLineContainerStyle: {}, + onMoveStart: {}, + onMove: {}, + onMoveEnd: {}, + isValidConnection: {}, + translateExtent: {}, + nodeExtent: {}, + onlyRenderVisibleElements: {}, + panOnScrollMode: {}, + preventScrolling: {}, + zoomOnScroll: {}, + zoomOnDoubleClick: {}, + zoomOnPinch: {}, + panOnScroll: {}, + panOnDrag: {}, + selectionOnDrag: {}, + autoPanOnConnect: {}, + autoPanOnNodeDrag: {}, + onerror: {}, + ondelete: {}, + onedgecreate: {}, + attributionPosition: {}, + proOptions: {}, + defaultEdgeOptions: {}, + width: {}, + height: {}, + colorMode: {}, + onconnect: {}, + onconnectstart: {}, + onconnectend: {}, + onbeforedelete: {}, + oninit: {}, + nodeOrigin: {}, + paneClickDistance: {}, + nodeClickDistance: {}, + defaultMarkerColor: {}, + style: {}, + class: {} + }, + ["connectionLine", "default"], + [], + !0 +); +function Wc(e, t) { + de(t, !1); + let n = w(t, "initialNodes", 12, void 0), r = w(t, "initialEdges", 12, void 0), o = w(t, "initialWidth", 12, void 0), i = w(t, "initialHeight", 12, void 0), s = w(t, "fitView", 12, void 0), a = w(t, "nodeOrigin", 12, void 0); + const l = Nc({ + nodes: n(), + edges: r(), + width: o(), + height: i(), + nodeOrigin: a(), + fitView: s() + }); + Tr(Wi, { getStore: () => l }), Qs(() => { + l.reset(); + }), He(); + var u = et(), c = be(u); + return pt(c, t, "default", {}), L(e, u), fe({ + get initialNodes() { + return n(); + }, + set initialNodes(f) { + n(f), y(); + }, + get initialEdges() { + return r(); + }, + set initialEdges(f) { + r(f), y(); + }, + get initialWidth() { + return o(); + }, + set initialWidth(f) { + o(f), y(); + }, + get initialHeight() { + return i(); + }, + set initialHeight(f) { + i(f), y(); + }, + get fitView() { + return s(); + }, + set fitView(f) { + s(f), y(); + }, + get nodeOrigin() { + return a(); + }, + set nodeOrigin(f) { + a(f), y(); + } + }); +} +ae( + Wc, + { + initialNodes: {}, + initialEdges: {}, + initialWidth: {}, + initialHeight: {}, + fitView: {}, + nodeOrigin: {} + }, + ["default"], + [], + !0 +); +var j2 = /* @__PURE__ */ ne("<button><!></button>"); +function ro(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]), r = nt(n, [ + "class", + "bgColor", + "bgColorHover", + "color", + "colorHover", + "borderColor" + ]); + de(t, !1); + let o = w(t, "class", 12, void 0), i = w(t, "bgColor", 12, void 0), s = w(t, "bgColorHover", 12, void 0), a = w(t, "color", 12, void 0), l = w(t, "colorHover", 12, void 0), u = w(t, "borderColor", 12, void 0); + He(); + var c = j2(); + let f; + var d = X(c); + return pt(d, t, "default", { class: "button-svg" }), Z(c), Ee( + (g) => { + f = on(c, f, { type: "button", class: g, ...r }), st(c, "--xy-controls-button-background-color-props", i()), st(c, "--xy-controls-button-background-color-hover-props", s()), st(c, "--xy-controls-button-color-props", a()), st(c, "--xy-controls-button-color-hover-props", l()), st(c, "--xy-controls-button-border-color-props", u()); + }, + [ + () => Et([ + "svelte-flow__controls-button", + o() + ]) + ], + pe + ), Ye("click", c, function(g) { + Ve.call(this, t, g); + }), L(e, c), fe({ + get class() { + return o(); + }, + set class(g) { + o(g), y(); + }, + get bgColor() { + return i(); + }, + set bgColor(g) { + i(g), y(); + }, + get bgColorHover() { + return s(); + }, + set bgColorHover(g) { + s(g), y(); + }, + get color() { + return a(); + }, + set color(g) { + a(g), y(); + }, + get colorHover() { + return l(); + }, + set colorHover(g) { + l(g), y(); + }, + get borderColor() { + return u(); + }, + set borderColor(g) { + u(g), y(); + } + }); +} +ae( + ro, + { + class: {}, + bgColor: {}, + bgColorHover: {}, + color: {}, + colorHover: {}, + borderColor: {} + }, + ["default"], + [], + !0 +); +var J2 = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"></path></svg>'); +function Kc(e) { + var t = J2(); + L(e, t); +} +ae(Kc, {}, [], [], !0); +var Q2 = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5"><path d="M0 0h32v4.2H0z"></path></svg>'); +function qc(e) { + var t = Q2(); + L(e, t); +} +ae(qc, {}, [], [], !0); +var ep = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30"><path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"></path></svg>'); +function Gc(e) { + var t = ep(); + L(e, t); +} +ae(Gc, {}, [], [], !0); +var tp = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32"><path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"></path></svg>'); +function Uc(e) { + var t = tp(); + L(e, t); +} +ae(Uc, {}, [], [], !0); +var np = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32"><path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"></path></svg>'); +function jc(e) { + var t = np(); + L(e, t); +} +ae(jc, {}, [], [], !0); +var rp = /* @__PURE__ */ ne("<!> <!>", 1), op = /* @__PURE__ */ ne("<!> <!> <!> <!> <!> <!>", 1); +function Jc(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(H, "$nodesDraggable", n), i = () => Q(I, "$nodesConnectable", n), s = () => Q(B, "$elementsSelectable", n), a = () => Q(T, "$viewport", n), l = () => Q(k, "$minZoom", n), u = () => Q(P, "$maxZoom", n), c = re(), f = re(), d = re(), g = re(); + let p = w(t, "position", 12, "bottom-left"), x = w(t, "showZoom", 12, !0), C = w(t, "showFitView", 12, !0), $ = w(t, "showLock", 12, !0), m = w(t, "buttonBgColor", 12, void 0), _ = w(t, "buttonBgColorHover", 12, void 0), v = w(t, "buttonColor", 12, void 0), b = w(t, "buttonColorHover", 12, void 0), N = w(t, "buttonBorderColor", 12, void 0), E = w(t, "ariaLabel", 12, void 0), M = w(t, "style", 12, void 0), D = w(t, "orientation", 12, "vertical"), V = w(t, "fitViewOptions", 12, void 0), A = w(t, "class", 12, ""); + const { + zoomIn: O, + zoomOut: R, + fitView: S, + viewport: T, + minZoom: k, + maxZoom: P, + nodesDraggable: H, + nodesConnectable: I, + elementsSelectable: B + } = Ue(), F = { + bgColor: m(), + bgColorHover: _(), + color: v(), + colorHover: b(), + borderColor: N() + }, K = () => { + O(); + }, ie = () => { + R(); + }, ee = () => { + S(V()); + }, W = () => { + U(c, !h(c)), H.set(h(c)), I.set(h(c)), B.set(h(c)); + }; + he( + () => (o(), i(), s()), + () => { + U(c, o() || i() || s()); + } + ), he(() => (a(), l()), () => { + U(f, a().zoom <= l()); + }), he(() => (a(), u()), () => { + U(d, a().zoom >= u()); + }), he(() => j(D()), () => { + U(g, D() === "horizontal" ? "horizontal" : "vertical"); + }), gt(), He(); + const ue = /* @__PURE__ */ pe(() => Et([ + "svelte-flow__controls", + h(g), + A() + ])), me = /* @__PURE__ */ pe(() => E() ?? "Svelte Flow controls"); + Ho(e, { + get class() { + return h(ue); + }, + get position() { + return p(); + }, + "data-testid": "svelte-flow__controls", + get "aria-label"() { + return h(me); + }, + get style() { + return M(); + }, + children: (ge, ze) => { + var G = op(), se = be(G); + pt(se, t, "before", {}); + var Te = z(se, 2); + { + var Ae = (ve) => { + var xe = rp(), Oe = be(xe); + ro(Oe, ut( + { + class: "svelte-flow__controls-zoomin", + title: "zoom in", + "aria-label": "zoom in", + get disabled() { + return h(d); + } + }, + F, + { + $$events: { click: K }, + children: (lt, J) => { + Kc(lt); + }, + $$slots: { default: !0 } + } + )); + var ct = z(Oe, 2); + ro(ct, ut( + { + class: "svelte-flow__controls-zoomout", + title: "zoom out", + "aria-label": "zoom out", + get disabled() { + return h(f); + } + }, + F, + { + $$events: { click: ie }, + children: (lt, J) => { + qc(lt); + }, + $$slots: { default: !0 } + } + )), L(ve, xe); + }; + ke(Te, (ve) => { + x() && ve(Ae); + }); + } + var Xe = z(Te, 2); + { + var te = (ve) => { + ro(ve, ut( + { + class: "svelte-flow__controls-fitview", + title: "fit view", + "aria-label": "fit view" + }, + F, + { + $$events: { click: ee }, + children: (xe, Oe) => { + Gc(xe); + }, + $$slots: { default: !0 } + } + )); + }; + ke(Xe, (ve) => { + C() && ve(te); + }); + } + var Fe = z(Xe, 2); + { + var Le = (ve) => { + ro(ve, ut( + { + class: "svelte-flow__controls-interactive", + title: "toggle interactivity", + "aria-label": "toggle interactivity" + }, + F, + { + $$events: { click: W }, + children: (xe, Oe) => { + var ct = et(), lt = be(ct); + { + var J = (le) => { + jc(le); + }, Re = (le) => { + Uc(le); + }; + ke(lt, (le) => { + h(c) ? le(J) : le(Re, !1); + }); + } + L(xe, ct); + }, + $$slots: { default: !0 } + } + )); + }; + ke(Fe, (ve) => { + $() && ve(Le); + }); + } + var Qe = z(Fe, 2); + pt(Qe, t, "default", {}); + var oe = z(Qe, 2); + pt(oe, t, "after", {}), L(ge, G); + }, + $$slots: { default: !0 } + }); + var Ce = fe({ + get position() { + return p(); + }, + set position(ge) { + p(ge), y(); + }, + get showZoom() { + return x(); + }, + set showZoom(ge) { + x(ge), y(); + }, + get showFitView() { + return C(); + }, + set showFitView(ge) { + C(ge), y(); + }, + get showLock() { + return $(); + }, + set showLock(ge) { + $(ge), y(); + }, + get buttonBgColor() { + return m(); + }, + set buttonBgColor(ge) { + m(ge), y(); + }, + get buttonBgColorHover() { + return _(); + }, + set buttonBgColorHover(ge) { + _(ge), y(); + }, + get buttonColor() { + return v(); + }, + set buttonColor(ge) { + v(ge), y(); + }, + get buttonColorHover() { + return b(); + }, + set buttonColorHover(ge) { + b(ge), y(); + }, + get buttonBorderColor() { + return N(); + }, + set buttonBorderColor(ge) { + N(ge), y(); + }, + get ariaLabel() { + return E(); + }, + set ariaLabel(ge) { + E(ge), y(); + }, + get style() { + return M(); + }, + set style(ge) { + M(ge), y(); + }, + get orientation() { + return D(); + }, + set orientation(ge) { + D(ge), y(); + }, + get fitViewOptions() { + return V(); + }, + set fitViewOptions(ge) { + V(ge), y(); + }, + get class() { + return A(); + }, + set class(ge) { + A(ge), y(); + } + }); + return r(), Ce; +} +ae( + Jc, + { + position: {}, + showZoom: {}, + showFitView: {}, + showLock: {}, + buttonBgColor: {}, + buttonBgColorHover: {}, + buttonColor: {}, + buttonColorHover: {}, + buttonBorderColor: {}, + ariaLabel: {}, + style: {}, + orientation: {}, + fitViewOptions: {}, + class: {} + }, + ["before", "default", "after"], + [], + !0 +); +var Gn; +(function(e) { + e.Lines = "lines", e.Dots = "dots", e.Cross = "cross"; +})(Gn || (Gn = {})); +var ip = /* @__PURE__ */ _e("<circle></circle>"); +function Qc(e, t) { + de(t, !1); + let n = w(t, "radius", 12, 5), r = w(t, "class", 12, ""); + He(); + var o = ip(); + return Ee( + (i) => { + ce(o, "cx", n()), ce(o, "cy", n()), ce(o, "r", n()), kt(o, 0, bn(i)); + }, + [ + () => Et([ + "svelte-flow__background-pattern", + "dots", + r() + ]) + ], + pe + ), L(e, o), fe({ + get radius() { + return n(); + }, + set radius(i) { + n(i), y(); + }, + get class() { + return r(); + }, + set class(i) { + r(i), y(); + } + }); +} +ae(Qc, { radius: {}, class: {} }, [], [], !0); +var sp = /* @__PURE__ */ _e("<path></path>"); +function ed(e, t) { + de(t, !1); + let n = w(t, "lineWidth", 12, 1), r = w(t, "dimensions", 12), o = w(t, "variant", 12, void 0), i = w(t, "class", 12, ""); + He(); + var s = sp(); + return Ee( + (a) => { + ce(s, "stroke-width", n()), ce(s, "d", `M${r()[0] / 2} 0 V${r()[1]} M0 ${r()[1] / 2} H${r()[0]}`), kt(s, 0, bn(a)); + }, + [ + () => Et([ + "svelte-flow__background-pattern", + o(), + i() + ]) + ], + pe + ), L(e, s), fe({ + get lineWidth() { + return n(); + }, + set lineWidth(a) { + n(a), y(); + }, + get dimensions() { + return r(); + }, + set dimensions(a) { + r(a), y(); + }, + get variant() { + return o(); + }, + set variant(a) { + o(a), y(); + }, + get class() { + return i(); + }, + set class(a) { + i(a), y(); + } + }); +} +ae( + ed, + { + lineWidth: {}, + dimensions: {}, + variant: {}, + class: {} + }, + [], + [], + !0 +); +const ap = { + [Gn.Dots]: 1, + [Gn.Lines]: 1, + [Gn.Cross]: 6 +}; +var lp = /* @__PURE__ */ _e('<svg data-testid="svelte-flow__background"><pattern patternUnits="userSpaceOnUse"><!></pattern><rect x="0" y="0" width="100%" height="100%"></rect></svg>'); +const up = { + hash: "svelte-1r7pe8d", + code: ".svelte-flow__background.svelte-1r7pe8d {position:absolute;width:100%;height:100%;top:0;left:0;}" +}; +function td(e, t) { + de(t, !1), Je(e, up); + const [n, r] = tt(), o = () => Q(b, "$flowId", n), i = () => Q(v, "$viewport", n), s = re(), a = re(), l = re(), u = re(), c = re(); + let f = w(t, "id", 12, void 0), d = w(t, "variant", 28, () => Gn.Dots), g = w(t, "gap", 12, 20), p = w(t, "size", 12, 1), x = w(t, "lineWidth", 12, 1), C = w(t, "bgColor", 12, void 0), $ = w(t, "patternColor", 12, void 0), m = w(t, "patternClass", 12, void 0), _ = w(t, "class", 12, ""); + const { viewport: v, flowId: b } = Ue(), N = p() || ap[d()], E = d() === Gn.Dots, M = d() === Gn.Cross, D = Array.isArray(g()) ? g() : [g(), g()]; + he( + () => (o(), j(f())), + () => { + U(s, `background-pattern-${o()}-${f() ? f() : ""}`); + } + ), he(() => i(), () => { + U(a, [ + D[0] * i().zoom || 1, + D[1] * i().zoom || 1 + ]); + }), he(() => i(), () => { + U(l, N * i().zoom); + }), he(() => (h(l), h(a)), () => { + U(u, M ? [h(l), h(l)] : h(a)); + }), he( + () => (h(l), h(u)), + () => { + U(c, E ? [ + h(l) / 2, + h(l) / 2 + ] : [ + h(u)[0] / 2, + h(u)[1] / 2 + ]); + } + ), gt(), He(); + var V = lp(), A = X(V), O = X(A); + { + var R = (P) => { + const H = /* @__PURE__ */ pe(() => h(l) / 2); + Qc(P, { + get radius() { + return h(H); + }, + get class() { + return m(); + } + }); + }, S = (P) => { + ed(P, { + get dimensions() { + return h(u); + }, + get variant() { + return d(); + }, + get lineWidth() { + return x(); + }, + get class() { + return m(); + } + }); + }; + ke(O, (P) => { + E ? P(R) : P(S, !1); + }); + } + Z(A); + var T = z(A); + Z(V), Ee( + (P) => { + kt(V, 0, bn(P), "svelte-1r7pe8d"), st(V, "--xy-background-color-props", C()), st(V, "--xy-background-pattern-color-props", $()), ce(A, "id", h(s)), ce(A, "x", i().x % h(a)[0]), ce(A, "y", i().y % h(a)[1]), ce(A, "width", h(a)[0]), ce(A, "height", h(a)[1]), ce(A, "patternTransform", `translate(-${h(c)[0]},-${h(c)[1]})`), ce(T, "fill", `url(#${h(s)})`); + }, + [ + () => Et(["svelte-flow__background", _()]) + ], + pe + ), L(e, V); + var k = fe({ + get id() { + return f(); + }, + set id(P) { + f(P), y(); + }, + get variant() { + return d(); + }, + set variant(P) { + d(P), y(); + }, + get gap() { + return g(); + }, + set gap(P) { + g(P), y(); + }, + get size() { + return p(); + }, + set size(P) { + p(P), y(); + }, + get lineWidth() { + return x(); + }, + set lineWidth(P) { + x(P), y(); + }, + get bgColor() { + return C(); + }, + set bgColor(P) { + C(P), y(); + }, + get patternColor() { + return $(); + }, + set patternColor(P) { + $(P), y(); + }, + get patternClass() { + return m(); + }, + set patternClass(P) { + m(P), y(); + }, + get class() { + return _(); + }, + set class(P) { + _(P), y(); + } + }); + return r(), k; +} +ae( + td, + { + id: {}, + variant: {}, + gap: {}, + size: {}, + lineWidth: {}, + bgColor: {}, + patternColor: {}, + patternClass: {}, + class: {} + }, + [], + [], + !0 +); +var cp = /* @__PURE__ */ _e("<rect></rect>"); +function nd(e, t) { + de(t, !1); + let n = w(t, "x", 12), r = w(t, "y", 12), o = w(t, "width", 12, 0), i = w(t, "height", 12, 0), s = w(t, "borderRadius", 12, 5), a = w(t, "color", 12, void 0), l = w(t, "shapeRendering", 12), u = w(t, "strokeColor", 12, void 0), c = w(t, "strokeWidth", 12, 2), f = w(t, "selected", 12, !1), d = w(t, "class", 12, ""); + He(); + var g = cp(); + let p; + return Ee( + (x) => { + p = kt(g, 0, bn(x), null, p, { selected: f() }), ce(g, "x", n()), ce(g, "y", r()), ce(g, "rx", s()), ce(g, "ry", s()), ce(g, "width", o()), ce(g, "height", i()), ce(g, "style", `${a() ? `fill: ${a()};` : ""}${u() ? `stroke: ${u()};` : ""}${c() ? `stroke-width: ${c()};` : ""}`), ce(g, "shape-rendering", l()); + }, + [ + () => Et(["svelte-flow__minimap-node", d()]) + ], + pe + ), L(e, g), fe({ + get x() { + return n(); + }, + set x(x) { + n(x), y(); + }, + get y() { + return r(); + }, + set y(x) { + r(x), y(); + }, + get width() { + return o(); + }, + set width(x) { + o(x), y(); + }, + get height() { + return i(); + }, + set height(x) { + i(x), y(); + }, + get borderRadius() { + return s(); + }, + set borderRadius(x) { + s(x), y(); + }, + get color() { + return a(); + }, + set color(x) { + a(x), y(); + }, + get shapeRendering() { + return l(); + }, + set shapeRendering(x) { + l(x), y(); + }, + get strokeColor() { + return u(); + }, + set strokeColor(x) { + u(x), y(); + }, + get strokeWidth() { + return c(); + }, + set strokeWidth(x) { + c(x), y(); + }, + get selected() { + return f(); + }, + set selected(x) { + f(x), y(); + }, + get class() { + return d(); + }, + set class(x) { + d(x), y(); + } + }); +} +ae( + nd, + { + x: {}, + y: {}, + width: {}, + height: {}, + borderRadius: {}, + color: {}, + shapeRendering: {}, + strokeColor: {}, + strokeWidth: {}, + selected: {}, + class: {} + }, + [], + [], + !0 +); +function cs(e, t) { + const n = q0({ + domNode: e, + panZoom: t.panZoom, + getTransform: () => { + const o = q(t.viewport); + return [o.x, o.y, o.zoom]; + }, + getViewScale: t.getViewScale + }); + function r(o) { + n.update({ + translateExtent: o.translateExtent, + width: o.width, + height: o.height, + inversePan: o.inversePan, + zoomStep: o.zoomStep, + pannable: o.pannable, + zoomable: o.zoomable + }); + } + return { + update: r, + destroy() { + n.destroy(); + } + }; +} +const ds = (e) => e instanceof Function ? e : () => e; +var dp = /* @__PURE__ */ _e("<title> </title>"), fp = /* @__PURE__ */ _e('<svg class="svelte-flow__minimap-svg" role="img"><!><!><path class="svelte-flow__minimap-mask" fill-rule="evenodd" pointer-events="none"></path></svg>'); +function rd(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(Xe, "$flowId", n), i = () => Q(se, "$viewport", n), s = () => Q(Te, "$containerWidth", n), a = () => Q(Ae, "$containerHeight", n), l = () => Q(G, "$nodeLookup", n), u = () => Q(ze, "$nodes", n), c = () => Q(te, "$panZoom", n), f = () => Q(Fe, "$translateExtent", n), d = re(), g = re(), p = re(), x = re(), C = re(), $ = re(), m = re(), _ = re(), v = re(), b = re(), N = re(), E = re(), M = re(); + let D = w(t, "position", 12, "bottom-right"), V = w(t, "ariaLabel", 12, "Mini map"), A = w(t, "nodeStrokeColor", 12, "transparent"), O = w(t, "nodeColor", 12, void 0), R = w(t, "nodeClass", 12, ""), S = w(t, "nodeBorderRadius", 12, 5), T = w(t, "nodeStrokeWidth", 12, 2), k = w(t, "bgColor", 12, void 0), P = w(t, "maskColor", 12, void 0), H = w(t, "maskStrokeColor", 12, void 0), I = w(t, "maskStrokeWidth", 12, void 0), B = w(t, "width", 12, void 0), F = w(t, "height", 12, void 0), K = w(t, "pannable", 12, !0), ie = w(t, "zoomable", 12, !0), ee = w(t, "inversePan", 12, void 0), W = w(t, "zoomStep", 12, void 0), ue = w(t, "style", 12, ""), me = w(t, "class", 12, ""); + const Ce = 200, ge = 150, { + nodes: ze, + nodeLookup: G, + viewport: se, + width: Te, + height: Ae, + flowId: Xe, + panZoom: te, + translateExtent: Fe + } = Ue(), Le = O() === void 0 ? void 0 : ds(O()), Qe = ds(A()), oe = ds(R()), ve = ( + // @ts-expect-error - TS doesn't know about chrome + typeof window > "u" || window.chrome ? "crispEdges" : "geometricPrecision" + ), xe = `svelte-flow__minimap-desc-${o()}`; + let Oe = re(h(d)); + const ct = () => h($); + he( + () => (i(), s(), a()), + () => { + U(d, { + x: -i().x / i().zoom, + y: -i().y / i().zoom, + width: s() / i().zoom, + height: a() / i().zoom + }); + } + ), he( + () => (l(), h(d), u()), + () => { + U(Oe, l().size > 0 ? nc(No(l()), h(d)) : h(d)), u(); + } + ), he(() => j(B()), () => { + U(g, B() ?? Ce); + }), he(() => j(F()), () => { + U(p, F() ?? ge); + }), he( + () => (h(Oe), h(g)), + () => { + U(x, h(Oe).width / h(g)); + } + ), he( + () => (h(Oe), h(p)), + () => { + U(C, h(Oe).height / h(p)); + } + ), he( + () => (h(x), h(C)), + () => { + U($, Math.max(h(x), h(C))); + } + ), he(() => (h($), h(g)), () => { + U(m, h($) * h(g)); + }), he( + () => (h($), h(p)), + () => { + U(_, h($) * h(p)); + } + ), he(() => h($), () => { + U(v, 5 * h($)); + }), he( + () => (h(Oe), h(m), h(v)), + () => { + U(b, h(Oe).x - (h(m) - h(Oe).width) / 2 - h(v)); + } + ), he( + () => (h(Oe), h(_), h(v)), + () => { + U(N, h(Oe).y - (h(_) - h(Oe).height) / 2 - h(v)); + } + ), he(() => (h(m), h(v)), () => { + U(E, h(m) + h(v) * 2); + }), he(() => (h(_), h(v)), () => { + U(M, h(_) + h(v) * 2); + }), gt(), He(); + const lt = /* @__PURE__ */ pe(() => ue() + (k() ? `;--xy-minimap-background-color-props:${k()}` : "")), J = /* @__PURE__ */ pe(() => Et(["svelte-flow__minimap", me()])); + Ho(e, { + get position() { + return D(); + }, + get style() { + return h(lt); + }, + get class() { + return h(J); + }, + "data-testid": "svelte-flow__minimap", + children: (le, fn) => { + var Ut = et(), gn = be(Ut); + { + var Ne = (rt) => { + var ye = fp(); + ce(ye, "aria-labelledby", xe); + var ot = X(ye); + { + var at = (At) => { + var St = dp(); + ce(St, "id", xe); + var hn = X(St, !0); + Z(St), Ee(() => Rt(hn, V())), L(At, St); + }; + ke(ot, (At) => { + V() && At(at); + }); + } + var Xt = z(ot); + Yt(Xt, 1, u, (At) => At.id, (At, St) => { + var hn = et(); + const jt = /* @__PURE__ */ pe(() => l().get(h(St).id)); + var ft = be(hn); + { + var ji = (nr) => { + const Jt = /* @__PURE__ */ pe(() => tr(h(jt))), Io = /* @__PURE__ */ pe(() => Le == null ? void 0 : Le(h(jt))), zo = /* @__PURE__ */ pe(() => Qe(h(jt))), Ro = /* @__PURE__ */ pe(() => oe(h(jt))); + nd(nr, ut( + { + get x() { + return h(jt).internals.positionAbsolute.x; + }, + get y() { + return h(jt).internals.positionAbsolute.y; + } + }, + () => h(Jt), + { + get selected() { + return h(jt).selected; + }, + get color() { + return h(Io); + }, + get borderRadius() { + return S(); + }, + get strokeColor() { + return h(zo); + }, + get strokeWidth() { + return T(); + }, + shapeRendering: ve, + get class() { + return h(Ro); + } + } + )); + }; + ke(ft, (nr) => { + h(jt) && oc(h(jt)) && nr(ji); + }); + } + L(At, hn); + }); + var Kr = z(Xt); + Z(ye), vt(ye, (At, St) => cs == null ? void 0 : cs(At, St), () => ({ + panZoom: c(), + viewport: se, + getViewScale: ct, + translateExtent: f(), + width: s(), + height: a(), + inversePan: ee(), + zoomStep: W(), + pannable: K(), + zoomable: ie() + })), Ee(() => { + ce(ye, "width", h(g)), ce(ye, "height", h(p)), ce(ye, "viewBox", `${h(b) ?? ""} ${h(N) ?? ""} ${h(E) ?? ""} ${h(M) ?? ""}`), st(ye, "--xy-minimap-mask-background-color-props", P()), st(ye, "--xy-minimap-mask-stroke-color-props", H()), st(ye, "--xy-minimap-mask-stroke-width-props", I() ? I() * h($) : void 0), ce(Kr, "d", `M${h(b) - h(v)},${h(N) - h(v)}h${h(E) + h(v) * 2}v${h(M) + h(v) * 2}h${-h(E) - h(v) * 2}z + M${h(d).x ?? ""},${h(d).y ?? ""}h${h(d).width ?? ""}v${h(d).height ?? ""}h${-h(d).width}z`); + }), L(rt, ye); + }; + ke(gn, (rt) => { + c() && rt(Ne); + }); + } + L(le, Ut); + }, + $$slots: { default: !0 } + }); + var Re = fe({ + get position() { + return D(); + }, + set position(le) { + D(le), y(); + }, + get ariaLabel() { + return V(); + }, + set ariaLabel(le) { + V(le), y(); + }, + get nodeStrokeColor() { + return A(); + }, + set nodeStrokeColor(le) { + A(le), y(); + }, + get nodeColor() { + return O(); + }, + set nodeColor(le) { + O(le), y(); + }, + get nodeClass() { + return R(); + }, + set nodeClass(le) { + R(le), y(); + }, + get nodeBorderRadius() { + return S(); + }, + set nodeBorderRadius(le) { + S(le), y(); + }, + get nodeStrokeWidth() { + return T(); + }, + set nodeStrokeWidth(le) { + T(le), y(); + }, + get bgColor() { + return k(); + }, + set bgColor(le) { + k(le), y(); + }, + get maskColor() { + return P(); + }, + set maskColor(le) { + P(le), y(); + }, + get maskStrokeColor() { + return H(); + }, + set maskStrokeColor(le) { + H(le), y(); + }, + get maskStrokeWidth() { + return I(); + }, + set maskStrokeWidth(le) { + I(le), y(); + }, + get width() { + return B(); + }, + set width(le) { + B(le), y(); + }, + get height() { + return F(); + }, + set height(le) { + F(le), y(); + }, + get pannable() { + return K(); + }, + set pannable(le) { + K(le), y(); + }, + get zoomable() { + return ie(); + }, + set zoomable(le) { + ie(le), y(); + }, + get inversePan() { + return ee(); + }, + set inversePan(le) { + ee(le), y(); + }, + get zoomStep() { + return W(); + }, + set zoomStep(le) { + W(le), y(); + }, + get style() { + return ue(); + }, + set style(le) { + ue(le), y(); + }, + get class() { + return me(); + }, + set class(le) { + me(le), y(); + } + }); + return r(), Re; +} +ae( + rd, + { + position: {}, + ariaLabel: {}, + nodeStrokeColor: {}, + nodeColor: {}, + nodeClass: {}, + nodeBorderRadius: {}, + nodeStrokeWidth: {}, + bgColor: {}, + maskColor: {}, + maskStrokeColor: {}, + maskStrokeWidth: {}, + width: {}, + height: {}, + pannable: {}, + zoomable: {}, + inversePan: {}, + zoomStep: {}, + style: {}, + class: {} + }, + [], + [], + !0 +); +const Pl = (e) => f0(e); +function Dt() { + const { zoomIn: e, zoomOut: t, fitView: n, onbeforedelete: r, snapGrid: o, viewport: i, width: s, height: a, minZoom: l, maxZoom: u, panZoom: c, nodes: f, edges: d, domNode: g, nodeLookup: p, nodeOrigin: x, edgeLookup: C, connectionLookup: $ } = Ue(), m = (b) => { + var V, A; + const N = q(p), E = Pl(b) ? b : N.get(b.id), M = E.parentId ? p0(E.position, E.measured, E.parentId, N, q(x)) : E.position, D = { + ...E, + position: M, + width: ((V = E.measured) == null ? void 0 : V.width) ?? E.width, + height: ((A = E.measured) == null ? void 0 : A.height) ?? E.height + }; + return Lr(D); + }, _ = (b, N, E = { replace: !1 }) => { + var V; + const M = (V = q(p).get(b)) == null ? void 0 : V.internals.userNode; + if (!M) + return; + const D = typeof N == "function" ? N(M) : N; + E.replace ? f.update((A) => A.map((O) => O.id === b ? Pl(D) ? D : { ...O, ...D } : O)) : (Object.assign(M, D), f.update((A) => A)); + }, v = (b) => q(p).get(b); + return { + zoomIn: e, + zoomOut: t, + getInternalNode: v, + getNode: (b) => { + var N; + return (N = v(b)) == null ? void 0 : N.internals.userNode; + }, + getNodes: (b) => b === void 0 ? q(f) : Nl(q(p), b), + getEdge: (b) => q(C).get(b), + getEdges: (b) => b === void 0 ? q(d) : Nl(q(C), b), + setZoom: (b, N) => { + const E = q(c); + return E ? E.scaleTo(b, { duration: N == null ? void 0 : N.duration }) : Promise.resolve(!1); + }, + getZoom: () => q(i).zoom, + setViewport: async (b, N) => { + const E = q(i), M = q(c); + return M ? (await M.setViewport({ + x: b.x ?? E.x, + y: b.y ?? E.y, + zoom: b.zoom ?? E.zoom + }, { duration: N == null ? void 0 : N.duration }), Promise.resolve(!0)) : Promise.resolve(!1); + }, + getViewport: () => q(i), + setCenter: async (b, N, E) => { + const M = typeof (E == null ? void 0 : E.zoom) < "u" ? E.zoom : q(u), D = q(c); + return D ? (await D.setViewport({ + x: q(s) / 2 - b * M, + y: q(a) / 2 - N * M, + zoom: M + }, { duration: E == null ? void 0 : E.duration }), Promise.resolve(!0)) : Promise.resolve(!1); + }, + fitView: n, + fitBounds: async (b, N) => { + const E = q(c); + if (!E) + return Promise.resolve(!1); + const M = ua(b, q(s), q(a), q(l), q(u), (N == null ? void 0 : N.padding) ?? 0.1); + return await E.setViewport(M, { duration: N == null ? void 0 : N.duration }), Promise.resolve(!0); + }, + getIntersectingNodes: (b, N = !0, E) => { + const M = fl(b), D = M ? b : m(b); + return D ? (E || q(f)).filter((V) => { + const A = q(p).get(V.id); + if (!A || !M && V.id === b.id) + return !1; + const O = Lr(A), R = yo(O, D); + return N && R > 0 || R >= D.width * D.height; + }) : []; + }, + isNodeIntersecting: (b, N, E = !0) => { + const D = fl(b) ? b : m(b); + if (!D) + return !1; + const V = yo(D, N); + return E && V > 0 || V >= D.width * D.height; + }, + deleteElements: async ({ nodes: b = [], edges: N = [] }) => { + const { nodes: E, edges: M } = await Qu({ + nodesToRemove: b, + edgesToRemove: N, + nodes: q(f), + edges: q(d), + onBeforeDelete: q(r) + }); + return E && f.update((D) => D.filter((V) => !E.some(({ id: A }) => A === V.id))), M && d.update((D) => D.filter((V) => !M.some(({ id: A }) => A === V.id))), { + deletedNodes: E, + deletedEdges: M + }; + }, + screenToFlowPosition: (b, N = { snapToGrid: !0 }) => { + const E = q(g); + if (!E) + return b; + const M = N.snapToGrid ? q(o) : !1, { x: D, y: V, zoom: A } = q(i), { x: O, y: R } = E.getBoundingClientRect(), S = { + x: b.x - O, + y: b.y - R + }; + return Mo(S, [D, V, A], M !== null, M || [1, 1]); + }, + /** + * + * @param position + * @returns + */ + flowToScreenPosition: (b) => { + const N = q(g); + if (!N) + return b; + const { x: E, y: M, zoom: D } = q(i), { x: V, y: A } = N.getBoundingClientRect(), O = rc(b, [E, M, D]); + return { + x: O.x + V, + y: O.y + A + }; + }, + toObject: () => ({ + nodes: q(f).map((b) => ({ + ...b, + // we want to make sure that changes to the nodes object that gets returned by toObject + // do not affect the nodes object + position: { ...b.position }, + data: { ...b.data } + })), + edges: q(d).map((b) => ({ ...b })), + viewport: { ...q(i) } + }), + updateNode: _, + updateNodeData: (b, N, E) => { + var V; + const M = (V = q(p).get(b)) == null ? void 0 : V.internals.userNode; + if (!M) + return; + const D = typeof N == "function" ? N(M) : N; + M.data = E != null && E.replace ? D : { ...M.data, ...D }, f.update((A) => A); + }, + getNodesBounds: (b) => { + const N = q(p), E = q(x); + return g0(b, { nodeLookup: N, nodeOrigin: E }); + }, + getHandleConnections: ({ type: b, id: N, nodeId: E }) => { + var M; + return Array.from(((M = q($).get(`${E}-${b}-${N ?? null}`)) == null ? void 0 : M.values()) ?? []); + }, + viewport: i + }; +} +function Nl(e, t) { + var r; + const n = []; + for (const o of t) { + const i = e.get(o); + if (i) { + const s = "internals" in i ? (r = i.internals) == null ? void 0 : r.userNode : i; + n.push(s); + } + } + return n; +} +var gp = /* @__PURE__ */ ne('<div class="svelte-flow__node-toolbar"><!></div>'); +function od(e, t) { + de(t, !1); + const [n, r] = tt(), o = () => Q(_, "$nodes", n), i = () => Q(m, "$nodeLookup", n), s = () => Q($, "$viewport", n), a = () => Q(C, "$domNode", n), l = re(), u = re(), c = re(); + let f = w(t, "nodeId", 12, void 0), d = w(t, "position", 12, void 0), g = w(t, "align", 12, void 0), p = w(t, "offset", 12, void 0), x = w(t, "isVisible", 12, void 0); + const { domNode: C, viewport: $, nodeLookup: m, nodes: _ } = Ue(), { getNodesBounds: v } = Dt(), b = ar("svelteflow__node_id"); + let N = re(), E = re([]), M = p() !== void 0 ? p() : 10, D = d() !== void 0 ? d() : $e.Top, V = g() !== void 0 ? g() : "center"; + he( + () => (o(), j(f()), i()), + () => { + o(); + const T = Array.isArray(f()) ? f() : [f() || b]; + U(E, T.reduce( + (k, P) => { + const H = i().get(P); + return H && k.push(H), k; + }, + [] + )); + } + ), he( + () => (h(E), s()), + () => { + const T = v(h(E)); + T && U(N, T0(T, s(), D, M, V)); + } + ), he(() => h(E), () => { + U(l, h(E).length === 0 ? 1 : Math.max(...h(E).map((T) => (T.internals.z || 5) + 1))); + }), he(() => o(), () => { + U(u, o().filter((T) => T.selected).length); + }), he( + () => (j(x()), h(E), h(u)), + () => { + U(c, typeof x() == "boolean" ? x() : h(E).length === 1 && h(E)[0].selected && h(u) === 1); + } + ), gt(), He(); + var A = et(), O = be(A); + { + var R = (T) => { + var k = gp(), P = X(k); + pt(P, t, "default", {}), Z(k), vt(k, (H, I) => kr == null ? void 0 : kr(H, I), () => ({ domNode: a() })), Ee( + (H) => { + ce(k, "data-id", H), st(k, "position", "absolute"), st(k, "transform", h(N)), st(k, "z-index", h(l)); + }, + [ + () => h(E).reduce((H, I) => `${H}${I.id} `, "").trim() + ], + pe + ), L(T, k); + }; + ke(O, (T) => { + a() && h(c) && h(E) && T(R); + }); + } + L(e, A); + var S = fe({ + get nodeId() { + return f(); + }, + set nodeId(T) { + f(T), y(); + }, + get position() { + return d(); + }, + set position(T) { + d(T), y(); + }, + get align() { + return g(); + }, + set align(T) { + g(T), y(); + }, + get offset() { + return p(); + }, + set offset(T) { + p(T), y(); + }, + get isVisible() { + return x(); + }, + set isVisible(T) { + x(T), y(); + } + }); + return r(), S; +} +ae( + od, + { + nodeId: {}, + position: {}, + align: {}, + offset: {}, + isVisible: {} + }, + ["default"], + [], + !0 +); +function pr(e) { + const { nodes: t, nodeLookup: n } = Ue(); + let r = [], o = !0; + return Kn([t, n], ([, i], s) => { + var c; + const a = [], l = Array.isArray(e), u = l ? e : [e]; + for (const f of u) { + const d = (c = i.get(f)) == null ? void 0 : c.internals.userNode; + d && a.push({ + id: d.id, + type: d.type, + data: d.data + }); + } + (!z0(a, r) || o) && (r = a, s(l ? a : a[0] ?? null), o = !1); + }); +} +const Ml = "tinyflow-component"; +class yw { + constructor(t) { + wt(this, "options"); + wt(this, "rootEl"); + wt(this, "svelteFlowInstance"); + if (typeof t.element != "string" && !(t.element instanceof Element)) + throw new Error("element must be a string or Element"); + this._setOptions(t), this._init(); + } + _init() { + if (typeof this.options.element == "string") { + if (this.rootEl = document.querySelector(this.options.element), !this.rootEl) + throw new Error( + `element not found by document.querySelector('${this.options.element}')` + ); + } else if (this.options.element instanceof Element) + this.rootEl = this.options.element; + else + throw new Error("element must be a string or Element"); + const t = document.createElement(Ml); + t.style.display = "block", t.style.width = "100%", t.style.height = "100%", t.classList.add("tf-theme-light"), t.options = this.options, t.onInit = (n) => { + this.svelteFlowInstance = n; + }, this.rootEl.appendChild(t); + } + _setOptions(t) { + this.options = { + ...t + }; + } + getOptions() { + return this.options; + } + getData() { + return this.svelteFlowInstance.toObject(); + } + setData(t) { + this.options.data = t; + const n = document.createElement(Ml); + n.style.display = "block", n.style.width = "100%", n.style.height = "100%", n.classList.add("tf-theme-light"), n.options = this.options, n.onInit = (r) => { + this.svelteFlowInstance = r; + }, this.destroy(), this.rootEl.appendChild(n); + } + destroy() { + for (; this.rootEl.firstChild; ) + this.rootEl.removeChild(this.rootEl.firstChild); + } +} +const hp = () => { + const e = we([]), t = we([]), n = we({ x: 250, y: 100, zoom: 1 }); + return { + nodes: e, + edges: t, + viewport: n, + init: (r, o) => { + e.set(r), t.set(o); + }, + addNode: (r) => { + e.update((o) => [...o, r]); + }, + removeNode: (r) => { + e.update((o) => o.filter((i) => i.id !== r)); + }, + updateNode: (r, o) => { + e.update((i) => i.map((s) => s.id === r ? o : s)); + }, + updateNodeData: (r, o) => { + e.update( + (i) => i.map((s) => s.id === r ? { ...s, data: { ...s.data, ...o } } : s) + ); + }, + selectNodeOnly: (r) => { + e.update( + (o) => o.map( + (i) => i.id === r ? { ...i, selected: !0 } : { ...i, selected: !1 } + ) + ); + }, + addEdge: (r) => { + t.update((o) => [...o, r]); + }, + removeEdge: (r) => { + t.update((o) => o.filter((i) => i.id !== r)); + }, + updateEdge: (r, o) => { + t.update((i) => i.map((s) => s.id === r ? o : s)); + }, + updateEdgeData: (r, o) => { + t.update((i) => i.map((s) => s.id === r ? { ...s, data: o } : s)); + } + }; +}, ei = hp(); +var vp = /* @__PURE__ */ ne("<button><!></button>"); +function Ke(e, t) { + de(t, !0); + const n = w(t, "children", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "children" + ]); + var o = vp(); + let i; + var s = X(o); + return lr(s, () => n() ?? dt), Z(o), Ee(() => i = on(o, i, { + type: "button", + ...r, + class: `tf-btn nopan nodrag ${t.class ?? ""}` + })), L(e, o), fe({ + get children() { + return n(); + }, + set children(a) { + n(a), y(); + } + }); +} +ae(Ke, { children: {} }, [], [], !0); +var pp = /* @__PURE__ */ ne("<input>"); +function id(e, t) { + de(t, !0); + const n = /* @__PURE__ */ yt(t, ["$$slots", "$$events", "$$legacy", "$$host"]); + var r = pp(); + io(r); + let o; + Ee(() => o = on(r, o, { + type: "checkbox", + ...n, + class: `tf-checkbox nopan nodrag ${t.class ?? ""}` + })), L(e, r), fe(); +} +ae(id, {}, [], [], !0); +var mp = /* @__PURE__ */ ne("<input>"); +function xt(e, t) { + de(t, !0); + const n = /* @__PURE__ */ yt(t, ["$$slots", "$$events", "$$legacy", "$$host"]); + var r = mp(); + io(r); + let o; + Ee(() => o = on(r, o, { + type: "text", + ...n, + class: `tf-input nopan nodrag ${t.class ?? ""}` + })), L(e, r), fe(); +} +ae(xt, {}, [], [], !0); +var yp = /* @__PURE__ */ ne("<textarea></textarea>"); +function $t(e, t) { + de(t, !0); + const n = /* @__PURE__ */ yt(t, ["$$slots", "$$events", "$$legacy", "$$host"]); + var r = yp(); + l1(r); + let o; + Ee(() => o = on(r, o, { + ...n, + class: `tf-textarea nodrag ${t.class ?? ""}` + })), L(e, r), fe(); +} +ae($t, {}, [], [], !0); +var wp = /* @__PURE__ */ ne('<div role="button"><!></div>'), _p = /* @__PURE__ */ ne("<div></div>"); +function sd(e, t) { + const n = nt(t, [ + "children", + "$$slots", + "$$events", + "$$legacy", + "$$host" + ]), r = nt(n, ["items", "onChange", "activeIndex"]); + de(t, !1); + let o = w(t, "items", 28, () => []), i = w(t, "onChange", 12, () => { + }), s = w(t, "activeIndex", 12, 0); + function a(c, f) { + var d; + s(f), (d = i()) == null || d(c, f); + } + He(); + var l = _p(); + let u; + return Yt(l, 5, o, Li, (c, f, d) => { + var g = wp(); + ce(g, "tabindex", d), g.__click = () => a(h(f), d), g.__keydown = ($) => { + ($.key === "Enter" || $.key === " ") && ($.preventDefault(), a(h(f), d)); + }; + var p = X(g); + { + var x = ($) => { + var m = Ie(); + Ee(() => Rt(m, h(f).label)), L($, m); + }, C = ($) => { + var m = et(), _ = be(m); + lr(_, () => h(f).label ?? dt), L($, m); + }; + ke(p, ($) => { + typeof h(f).label == "string" ? $(x) : $(C, !1); + }); + } + Z(g), Ee(() => kt(g, 1, `tf-tabs-item ${(d === s() ? "active" : "") ?? ""}`)), L(c, g); + }), Z(l), Ee(() => u = on(l, u, { + ...r, + class: `tf-tabs ${r.class ?? ""}` + })), L(e, l), fe({ + get items() { + return o(); + }, + set items(c) { + o(c), y(); + }, + get onChange() { + return i(); + }, + set onChange(c) { + i(c), y(); + }, + get activeIndex() { + return s(); + }, + set activeIndex(c) { + s(c), y(); + } + }); +} +Ai(["click", "keydown"]); +ae(sd, { items: {}, onChange: {}, activeIndex: {} }, [], [], !0); +var xp = (e, t, n) => t(h(n)), bp = (e, t, n) => { + (e.key === "Enter" || e.key === " ") && (e.preventDefault(), t(h(n))); +}, Cp = /* @__PURE__ */ ne('<span class="tf-collapse-item-title-icon"><!></span>'), kp = /* @__PURE__ */ ne('<div class="tf-collapse-item-description"><!></div>'), $p = /* @__PURE__ */ ne('<div class="tf-collapse-item-content"><!></div>'), Ep = /* @__PURE__ */ ne('<div class="tf-collapse-item"><div class="tf-collapse-item-title" role="button"><!> <!> <span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.1717 12.0007L8.22192 7.05093L9.63614 5.63672L16.0001 12.0007L9.63614 18.3646L8.22192 16.9504L13.1717 12.0007Z"></path></svg></span></div> <!> <!></div>'), Sp = /* @__PURE__ */ ne("<div></div>"); +const Pp = { + hash: "svelte-1jfktzw", + code: `\r + /* 定义旋转的 CSS 类 */.rotate-90.svelte-1jfktzw {transform:rotate(90deg);transition:transform 0.3s ease;}` +}; +function ad(e, t) { + de(t, !0), Je(e, Pp); + let n = w(t, "items", 7), r = w(t, "onChange", 7), o = w(t, "activeKeys", 31, () => Tt([])); + function i(a) { + var l; + o().includes(a.key) ? o(o().filter((u) => u !== a.key)) : (o().push(a.key), o(o())), (l = r()) == null || l(a, o()); + } + var s = Sp(); + return Yt(s, 21, n, Li, (a, l, u) => { + var c = Ep(), f = X(c); + ce(f, "tabindex", u), f.__click = [xp, i, l], f.__keydown = [bp, i, l]; + var d = X(f); + { + var g = (v) => { + var b = Cp(), N = X(b); + Fn(N, { + get target() { + return h(l).icon; + } + }), Z(b), L(v, b); + }; + ke(d, (v) => { + h(l).icon && v(g); + }); + } + var p = z(d, 2); + Fn(p, { + get target() { + return h(l).title; + } + }); + var x = z(p, 2); + Z(f); + var C = z(f, 2); + { + var $ = (v) => { + var b = kp(), N = X(b); + Fn(N, { + get target() { + return h(l).description; + } + }), Z(b), L(v, b); + }; + ke(C, (v) => { + h(l).description && v($); + }); + } + var m = z(C, 2); + { + var _ = (v) => { + var b = $p(), N = X(b); + Fn(N, { + get target() { + return h(l).content; + } + }), Z(b), L(v, b); + }; + ke(m, (v) => { + o().includes(h(l).key) && v(_); + }); + } + Z(c), Ee((v) => kt(x, 1, `tf-collapse-item-title-arrow ${v ?? ""}`, "svelte-1jfktzw"), [ + () => o().includes(h(l).key) ? "rotate-90" : "" + ]), L(a, c); + }), Z(s), Ee(() => { + ce(s, "style", t.style), kt(s, 1, `tf-collapse ${t.class ?? ""}`, "svelte-1jfktzw"); + }), L(e, s), fe({ + get items() { + return n(); + }, + set items(a) { + n(a), y(); + }, + get onChange() { + return r(); + }, + set onChange(a) { + r(a), y(); + }, + get activeKeys() { + return o(); + }, + set activeKeys(a = []) { + o(a), y(); + } + }); +} +Ai(["click", "keydown"]); +ae(ad, { items: {}, onChange: {}, activeKeys: {} }, [], [], !0); +function Fn(e, t) { + de(t, !0); + let n = w(t, "target", 7); + typeof n() > "u" && n("undefined"); + var r = et(), o = be(r); + { + var i = (a) => { + var l = et(), u = be(l); + lr(u, () => n() ?? dt), L(a, l); + }, s = (a) => { + var l = et(), u = be(l); + mu(u, n), L(a, l); + }; + ke(o, (a) => { + typeof n() == "function" ? a(i) : a(s, !1); + }); + } + return L(e, r), fe({ + get target() { + return n(); + }, + set target(a) { + n(a), y(); + } + }); +} +ae(Fn, { target: {} }, [], [], !0); +var Np = (e, t, n) => t(h(n)), Mp = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 14L8 10H16L12 14Z"></path></svg>'), Tp = /* @__PURE__ */ ne('<div class="tf-select-content-children"><!></div>'), Hp = /* @__PURE__ */ ne('<button class="tf-select-content-item"><span><!></span> <!></button> <!>', 1), Vp = /* @__PURE__ */ ne('<div class="tf-select-content nopan nodrag"><!></div>'), Dp = /* @__PURE__ */ ne("<!> <!>", 1), Ap = /* @__PURE__ */ ne('<div class="tf-select-input-placeholder"> </div>'), Lp = /* @__PURE__ */ ne('<button><div class="tf-select-input-value"></div> <div class="tf-select-input-arrow"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"></path></svg></div></button>'), Op = /* @__PURE__ */ ne("<div><!></div>"); +function sn(e, t) { + de(t, !0); + const n = (_, v = dt) => { + var b = et(), N = be(b); + Yt(N, 19, v, (E, M) => `${M}_${E.value}`, (E, M) => { + var D = Hp(), V = be(D); + V.__click = [Np, x, M]; + var A = X(V), O = X(A); + { + var R = (P) => { + var H = Mp(); + L(P, H); + }; + ke(O, (P) => { + h(M).children && h(M).children.length > 0 && P(R); + }); + } + Z(A); + var S = z(A, 2); + Fn(S, { + get target() { + return h(M).label; + } + }), Z(V); + var T = z(V, 2); + { + var k = (P) => { + var H = Tp(), I = X(H); + n(I, () => h(M).children), Z(H), L(P, H); + }; + ke(T, (P) => { + h(M).children && h(M).children.length > 0 && (l() || c().includes(h(M).value)) && P(k); + }); + } + L(E, D); + }), L(_, b); + }; + let r = w(t, "items", 7), o = w(t, "onExpand", 7), i = w(t, "onSelect", 7), s = w(t, "value", 23, () => []), a = w(t, "defaultValue", 23, () => []), l = w(t, "expandAll", 7, !0), u = w(t, "multiple", 7, !1), c = w(t, "expandValue", 23, () => []), f = w(t, "placeholder", 7), d = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "items", + "onExpand", + "onSelect", + "value", + "defaultValue", + "expandAll", + "multiple", + "expandValue", + "placeholder" + ]), g = /* @__PURE__ */ Me(() => { + const _ = [], v = (b) => { + for (let N of b) + s().length > 0 ? s().includes(N.value) && _.push(N) : a().includes(N.value) && _.push(N), N.children && N.children.length > 0 && v(N.children); + }; + return v(r()), _; + }), p; + function x(_) { + var v, b; + if (_.children && _.children.length > 0) { + (v = o()) == null || v(_); + return; + } else + p == null || p.hide(), (b = i()) == null || b(_); + } + var C = Op(); + let $; + var m = X(C); + return An( + Lo(m, { + floating: (v) => { + var b = Vp(), N = X(b); + n(N, r), Z(b), L(v, b); + }, + children: (v, b) => { + var N = Lp(); + let E; + var M = X(N); + Yt( + M, + 23, + () => h(g), + (D, V) => `${V}_${D.value}`, + (D, V, A) => { + var O = et(), R = be(O); + { + var S = (k) => { + var P = et(), H = be(P); + { + var I = (B) => { + Fn(B, { + get target() { + return h(V).label; + } + }); + }; + ke(H, (B) => { + h(A) === 0 && B(I); + }); + } + L(k, P); + }, T = (k) => { + var P = Dp(), H = be(P); + Fn(H, { + get target() { + return h(V).label; + } + }); + var I = z(H, 2); + { + var B = (F) => { + var K = Ie(","); + L(F, K); + }; + ke(I, (F) => { + h(A) < h(g).length - 1 && F(B); + }); + } + L(k, P); + }; + ke(R, (k) => { + u() ? k(T, !1) : k(S); + }); + } + L(D, O); + }, + (D) => { + var V = Ap(), A = X(V, !0); + Z(V), Ee(() => Rt(A, f())), L(D, V); + } + ), Z(M), Se(2), Z(N), Ee(() => E = on(N, E, { + class: "tf-select-input nopan nodrag", + ...d + })), L(v, N); + }, + $$slots: { floating: !0, default: !0 } + }), + (v) => p = v, + () => p + ), Z(C), Ee(() => $ = on(C, $, { + ...d, + class: `tf-select ${d.class ?? ""}` + })), L(e, C), fe({ + get items() { + return r(); + }, + set items(_) { + r(_), y(); + }, + get onExpand() { + return o(); + }, + set onExpand(_) { + o(_), y(); + }, + get onSelect() { + return i(); + }, + set onSelect(_) { + i(_), y(); + }, + get value() { + return s(); + }, + set value(_ = []) { + s(_), y(); + }, + get defaultValue() { + return a(); + }, + set defaultValue(_ = []) { + a(_), y(); + }, + get expandAll() { + return l(); + }, + set expandAll(_ = !0) { + l(_), y(); + }, + get multiple() { + return u(); + }, + set multiple(_ = !1) { + u(_), y(); + }, + get expandValue() { + return c(); + }, + set expandValue(_ = []) { + c(_), y(); + }, + get placeholder() { + return f(); + }, + set placeholder(_) { + f(_), y(); + } + }); +} +Ai(["click"]); +ae( + sn, + { + items: {}, + onExpand: {}, + onSelect: {}, + value: {}, + defaultValue: {}, + expandAll: {}, + multiple: {}, + expandValue: {}, + placeholder: {} + }, + [], + [], + !0 +); +const _o = Math.min, Er = Math.max, bi = Math.round, mn = (e) => ({ + x: e, + y: e +}), Ip = { + left: "right", + right: "left", + bottom: "top", + top: "bottom" +}, zp = { + start: "end", + end: "start" +}; +function Ds(e, t, n) { + return Er(e, _o(t, n)); +} +function Vo(e, t) { + return typeof e == "function" ? e(t) : e; +} +function fr(e) { + return e.split("-")[0]; +} +function Do(e) { + return e.split("-")[1]; +} +function ld(e) { + return e === "x" ? "y" : "x"; +} +function va(e) { + return e === "y" ? "height" : "width"; +} +function Ir(e) { + return ["top", "bottom"].includes(fr(e)) ? "y" : "x"; +} +function pa(e) { + return ld(Ir(e)); +} +function Rp(e, t, n) { + n === void 0 && (n = !1); + const r = Do(e), o = pa(e), i = va(o); + let s = o === "x" ? r === (n ? "end" : "start") ? "right" : "left" : r === "start" ? "bottom" : "top"; + return t.reference[i] > t.floating[i] && (s = Ci(s)), [s, Ci(s)]; +} +function Bp(e) { + const t = Ci(e); + return [As(e), t, As(t)]; +} +function As(e) { + return e.replace(/start|end/g, (t) => zp[t]); +} +function Yp(e, t, n) { + const r = ["left", "right"], o = ["right", "left"], i = ["top", "bottom"], s = ["bottom", "top"]; + switch (e) { + case "top": + case "bottom": + return n ? t ? o : r : t ? r : o; + case "left": + case "right": + return t ? i : s; + default: + return []; + } +} +function Zp(e, t, n, r) { + const o = Do(e); + let i = Yp(fr(e), n === "start", r); + return o && (i = i.map((s) => s + "-" + o), t && (i = i.concat(i.map(As)))), i; +} +function Ci(e) { + return e.replace(/left|right|bottom|top/g, (t) => Ip[t]); +} +function Xp(e) { + return { + top: 0, + right: 0, + bottom: 0, + left: 0, + ...e + }; +} +function ud(e) { + return typeof e != "number" ? Xp(e) : { + top: e, + right: e, + bottom: e, + left: e + }; +} +function ki(e) { + const { + x: t, + y: n, + width: r, + height: o + } = e; + return { + width: r, + height: o, + top: n, + left: t, + right: t + r, + bottom: n + o, + x: t, + y: n + }; +} +function Tl(e, t, n) { + let { + reference: r, + floating: o + } = e; + const i = Ir(t), s = pa(t), a = va(s), l = fr(t), u = i === "y", c = r.x + r.width / 2 - o.width / 2, f = r.y + r.height / 2 - o.height / 2, d = r[a] / 2 - o[a] / 2; + let g; + switch (l) { + case "top": + g = { + x: c, + y: r.y - o.height + }; + break; + case "bottom": + g = { + x: c, + y: r.y + r.height + }; + break; + case "right": + g = { + x: r.x + r.width, + y: f + }; + break; + case "left": + g = { + x: r.x - o.width, + y: f + }; + break; + default: + g = { + x: r.x, + y: r.y + }; + } + switch (Do(t)) { + case "start": + g[s] -= d * (n && u ? -1 : 1); + break; + case "end": + g[s] += d * (n && u ? -1 : 1); + break; + } + return g; +} +const Fp = async (e, t, n) => { + const { + placement: r = "bottom", + strategy: o = "absolute", + middleware: i = [], + platform: s + } = n, a = i.filter(Boolean), l = await (s.isRTL == null ? void 0 : s.isRTL(t)); + let u = await s.getElementRects({ + reference: e, + floating: t, + strategy: o + }), { + x: c, + y: f + } = Tl(u, r, l), d = r, g = {}, p = 0; + for (let x = 0; x < a.length; x++) { + const { + name: C, + fn: $ + } = a[x], { + x: m, + y: _, + data: v, + reset: b + } = await $({ + x: c, + y: f, + initialPlacement: r, + placement: d, + strategy: o, + middlewareData: g, + rects: u, + platform: s, + elements: { + reference: e, + floating: t + } + }); + c = m ?? c, f = _ ?? f, g = { + ...g, + [C]: { + ...g[C], + ...v + } + }, b && p <= 50 && (p++, typeof b == "object" && (b.placement && (d = b.placement), b.rects && (u = b.rects === !0 ? await s.getElementRects({ + reference: e, + floating: t, + strategy: o + }) : b.rects), { + x: c, + y: f + } = Tl(u, d, l)), x = -1); + } + return { + x: c, + y: f, + placement: d, + strategy: o, + middlewareData: g + }; +}; +async function cd(e, t) { + var n; + t === void 0 && (t = {}); + const { + x: r, + y: o, + platform: i, + rects: s, + elements: a, + strategy: l + } = e, { + boundary: u = "clippingAncestors", + rootBoundary: c = "viewport", + elementContext: f = "floating", + altBoundary: d = !1, + padding: g = 0 + } = Vo(t, e), p = ud(g), C = a[d ? f === "floating" ? "reference" : "floating" : f], $ = ki(await i.getClippingRect({ + element: (n = await (i.isElement == null ? void 0 : i.isElement(C))) == null || n ? C : C.contextElement || await (i.getDocumentElement == null ? void 0 : i.getDocumentElement(a.floating)), + boundary: u, + rootBoundary: c, + strategy: l + })), m = f === "floating" ? { + x: r, + y: o, + width: s.floating.width, + height: s.floating.height + } : s.reference, _ = await (i.getOffsetParent == null ? void 0 : i.getOffsetParent(a.floating)), v = await (i.isElement == null ? void 0 : i.isElement(_)) ? await (i.getScale == null ? void 0 : i.getScale(_)) || { + x: 1, + y: 1 + } : { + x: 1, + y: 1 + }, b = ki(i.convertOffsetParentRelativeRectToViewportRelativeRect ? await i.convertOffsetParentRelativeRectToViewportRelativeRect({ + elements: a, + rect: m, + offsetParent: _, + strategy: l + }) : m); + return { + top: ($.top - b.top + p.top) / v.y, + bottom: (b.bottom - $.bottom + p.bottom) / v.y, + left: ($.left - b.left + p.left) / v.x, + right: (b.right - $.right + p.right) / v.x + }; +} +const Wp = (e) => ({ + name: "arrow", + options: e, + async fn(t) { + const { + x: n, + y: r, + placement: o, + rects: i, + platform: s, + elements: a, + middlewareData: l + } = t, { + element: u, + padding: c = 0 + } = Vo(e, t) || {}; + if (u == null) + return {}; + const f = ud(c), d = { + x: n, + y: r + }, g = pa(o), p = va(g), x = await s.getDimensions(u), C = g === "y", $ = C ? "top" : "left", m = C ? "bottom" : "right", _ = C ? "clientHeight" : "clientWidth", v = i.reference[p] + i.reference[g] - d[g] - i.floating[p], b = d[g] - i.reference[g], N = await (s.getOffsetParent == null ? void 0 : s.getOffsetParent(u)); + let E = N ? N[_] : 0; + (!E || !await (s.isElement == null ? void 0 : s.isElement(N))) && (E = a.floating[_] || i.floating[p]); + const M = v / 2 - b / 2, D = E / 2 - x[p] / 2 - 1, V = _o(f[$], D), A = _o(f[m], D), O = V, R = E - x[p] - A, S = E / 2 - x[p] / 2 + M, T = Ds(O, S, R), k = !l.arrow && Do(o) != null && S !== T && i.reference[p] / 2 - (S < O ? V : A) - x[p] / 2 < 0, P = k ? S < O ? S - O : S - R : 0; + return { + [g]: d[g] + P, + data: { + [g]: T, + centerOffset: S - T - P, + ...k && { + alignmentOffset: P + } + }, + reset: k + }; + } +}), Kp = function(e) { + return e === void 0 && (e = {}), { + name: "flip", + options: e, + async fn(t) { + var n, r; + const { + placement: o, + middlewareData: i, + rects: s, + initialPlacement: a, + platform: l, + elements: u + } = t, { + mainAxis: c = !0, + crossAxis: f = !0, + fallbackPlacements: d, + fallbackStrategy: g = "bestFit", + fallbackAxisSideDirection: p = "none", + flipAlignment: x = !0, + ...C + } = Vo(e, t); + if ((n = i.arrow) != null && n.alignmentOffset) + return {}; + const $ = fr(o), m = Ir(a), _ = fr(a) === a, v = await (l.isRTL == null ? void 0 : l.isRTL(u.floating)), b = d || (_ || !x ? [Ci(a)] : Bp(a)), N = p !== "none"; + !d && N && b.push(...Zp(a, x, p, v)); + const E = [a, ...b], M = await cd(t, C), D = []; + let V = ((r = i.flip) == null ? void 0 : r.overflows) || []; + if (c && D.push(M[$]), f) { + const S = Rp(o, s, v); + D.push(M[S[0]], M[S[1]]); + } + if (V = [...V, { + placement: o, + overflows: D + }], !D.every((S) => S <= 0)) { + var A, O; + const S = (((A = i.flip) == null ? void 0 : A.index) || 0) + 1, T = E[S]; + if (T) + return { + data: { + index: S, + overflows: V + }, + reset: { + placement: T + } + }; + let k = (O = V.filter((P) => P.overflows[0] <= 0).sort((P, H) => P.overflows[1] - H.overflows[1])[0]) == null ? void 0 : O.placement; + if (!k) + switch (g) { + case "bestFit": { + var R; + const P = (R = V.filter((H) => { + if (N) { + const I = Ir(H.placement); + return I === m || // Create a bias to the `y` side axis due to horizontal + // reading directions favoring greater width. + I === "y"; + } + return !0; + }).map((H) => [H.placement, H.overflows.filter((I) => I > 0).reduce((I, B) => I + B, 0)]).sort((H, I) => H[1] - I[1])[0]) == null ? void 0 : R[0]; + P && (k = P); + break; + } + case "initialPlacement": + k = a; + break; + } + if (o !== k) + return { + reset: { + placement: k + } + }; + } + return {}; + } + }; +}; +async function qp(e, t) { + const { + placement: n, + platform: r, + elements: o + } = e, i = await (r.isRTL == null ? void 0 : r.isRTL(o.floating)), s = fr(n), a = Do(n), l = Ir(n) === "y", u = ["left", "top"].includes(s) ? -1 : 1, c = i && l ? -1 : 1, f = Vo(t, e); + let { + mainAxis: d, + crossAxis: g, + alignmentAxis: p + } = typeof f == "number" ? { + mainAxis: f, + crossAxis: 0, + alignmentAxis: null + } : { + mainAxis: f.mainAxis || 0, + crossAxis: f.crossAxis || 0, + alignmentAxis: f.alignmentAxis + }; + return a && typeof p == "number" && (g = a === "end" ? p * -1 : p), l ? { + x: g * c, + y: d * u + } : { + x: d * u, + y: g * c + }; +} +const Gp = function(e) { + return e === void 0 && (e = 0), { + name: "offset", + options: e, + async fn(t) { + var n, r; + const { + x: o, + y: i, + placement: s, + middlewareData: a + } = t, l = await qp(t, e); + return s === ((n = a.offset) == null ? void 0 : n.placement) && (r = a.arrow) != null && r.alignmentOffset ? {} : { + x: o + l.x, + y: i + l.y, + data: { + ...l, + placement: s + } + }; + } + }; +}, Up = function(e) { + return e === void 0 && (e = {}), { + name: "shift", + options: e, + async fn(t) { + const { + x: n, + y: r, + placement: o + } = t, { + mainAxis: i = !0, + crossAxis: s = !1, + limiter: a = { + fn: (C) => { + let { + x: $, + y: m + } = C; + return { + x: $, + y: m + }; + } + }, + ...l + } = Vo(e, t), u = { + x: n, + y: r + }, c = await cd(t, l), f = Ir(fr(o)), d = ld(f); + let g = u[d], p = u[f]; + if (i) { + const C = d === "y" ? "top" : "left", $ = d === "y" ? "bottom" : "right", m = g + c[C], _ = g - c[$]; + g = Ds(m, g, _); + } + if (s) { + const C = f === "y" ? "top" : "left", $ = f === "y" ? "bottom" : "right", m = p + c[C], _ = p - c[$]; + p = Ds(m, p, _); + } + const x = a.fn({ + ...t, + [d]: g, + [f]: p + }); + return { + ...x, + data: { + x: x.x - n, + y: x.y - r, + enabled: { + [d]: i, + [f]: s + } + } + }; + } + }; +}; +function Ki() { + return typeof window < "u"; +} +function Wr(e) { + return dd(e) ? (e.nodeName || "").toLowerCase() : "#document"; +} +function Bt(e) { + var t; + return (e == null || (t = e.ownerDocument) == null ? void 0 : t.defaultView) || window; +} +function zn(e) { + var t; + return (t = (dd(e) ? e.ownerDocument : e.document) || window.document) == null ? void 0 : t.documentElement; +} +function dd(e) { + return Ki() ? e instanceof Node || e instanceof Bt(e).Node : !1; +} +function an(e) { + return Ki() ? e instanceof Element || e instanceof Bt(e).Element : !1; +} +function _n(e) { + return Ki() ? e instanceof HTMLElement || e instanceof Bt(e).HTMLElement : !1; +} +function Hl(e) { + return !Ki() || typeof ShadowRoot > "u" ? !1 : e instanceof ShadowRoot || e instanceof Bt(e).ShadowRoot; +} +function Ao(e) { + const { + overflow: t, + overflowX: n, + overflowY: r, + display: o + } = ln(e); + return /auto|scroll|overlay|hidden|clip/.test(t + r + n) && !["inline", "contents"].includes(o); +} +function jp(e) { + return ["table", "td", "th"].includes(Wr(e)); +} +function qi(e) { + return [":popover-open", ":modal"].some((t) => { + try { + return e.matches(t); + } catch { + return !1; + } + }); +} +function ma(e) { + const t = ya(), n = an(e) ? ln(e) : e; + return ["transform", "translate", "scale", "rotate", "perspective"].some((r) => n[r] ? n[r] !== "none" : !1) || (n.containerType ? n.containerType !== "normal" : !1) || !t && (n.backdropFilter ? n.backdropFilter !== "none" : !1) || !t && (n.filter ? n.filter !== "none" : !1) || ["transform", "translate", "scale", "rotate", "perspective", "filter"].some((r) => (n.willChange || "").includes(r)) || ["paint", "layout", "strict", "content"].some((r) => (n.contain || "").includes(r)); +} +function Jp(e) { + let t = er(e); + for (; _n(t) && !zr(t); ) { + if (ma(t)) + return t; + if (qi(t)) + return null; + t = er(t); + } + return null; +} +function ya() { + return typeof CSS > "u" || !CSS.supports ? !1 : CSS.supports("-webkit-backdrop-filter", "none"); +} +function zr(e) { + return ["html", "body", "#document"].includes(Wr(e)); +} +function ln(e) { + return Bt(e).getComputedStyle(e); +} +function Gi(e) { + return an(e) ? { + scrollLeft: e.scrollLeft, + scrollTop: e.scrollTop + } : { + scrollLeft: e.scrollX, + scrollTop: e.scrollY + }; +} +function er(e) { + if (Wr(e) === "html") + return e; + const t = ( + // Step into the shadow DOM of the parent of a slotted node. + e.assignedSlot || // DOM Element detected. + e.parentNode || // ShadowRoot detected. + Hl(e) && e.host || // Fallback. + zn(e) + ); + return Hl(t) ? t.host : t; +} +function fd(e) { + const t = er(e); + return zr(t) ? e.ownerDocument ? e.ownerDocument.body : e.body : _n(t) && Ao(t) ? t : fd(t); +} +function gd(e, t, n) { + var r; + t === void 0 && (t = []); + const o = fd(e), i = o === ((r = e.ownerDocument) == null ? void 0 : r.body), s = Bt(o); + return i ? (Ls(s), t.concat(s, s.visualViewport || [], Ao(o) ? o : [], [])) : t.concat(o, gd(o, [])); +} +function Ls(e) { + return e.parent && Object.getPrototypeOf(e.parent) ? e.frameElement : null; +} +function hd(e) { + const t = ln(e); + let n = parseFloat(t.width) || 0, r = parseFloat(t.height) || 0; + const o = _n(e), i = o ? e.offsetWidth : n, s = o ? e.offsetHeight : r, a = bi(n) !== i || bi(r) !== s; + return a && (n = i, r = s), { + width: n, + height: r, + $: a + }; +} +function vd(e) { + return an(e) ? e : e.contextElement; +} +function Sr(e) { + const t = vd(e); + if (!_n(t)) + return mn(1); + const n = t.getBoundingClientRect(), { + width: r, + height: o, + $: i + } = hd(t); + let s = (i ? bi(n.width) : n.width) / r, a = (i ? bi(n.height) : n.height) / o; + return (!s || !Number.isFinite(s)) && (s = 1), (!a || !Number.isFinite(a)) && (a = 1), { + x: s, + y: a + }; +} +const Qp = /* @__PURE__ */ mn(0); +function pd(e) { + const t = Bt(e); + return !ya() || !t.visualViewport ? Qp : { + x: t.visualViewport.offsetLeft, + y: t.visualViewport.offsetTop + }; +} +function em(e, t, n) { + return t === void 0 && (t = !1), !n || t && n !== Bt(e) ? !1 : t; +} +function xo(e, t, n, r) { + t === void 0 && (t = !1), n === void 0 && (n = !1); + const o = e.getBoundingClientRect(), i = vd(e); + let s = mn(1); + t && (r ? an(r) && (s = Sr(r)) : s = Sr(e)); + const a = em(i, n, r) ? pd(i) : mn(0); + let l = (o.left + a.x) / s.x, u = (o.top + a.y) / s.y, c = o.width / s.x, f = o.height / s.y; + if (i) { + const d = Bt(i), g = r && an(r) ? Bt(r) : r; + let p = d, x = Ls(p); + for (; x && r && g !== p; ) { + const C = Sr(x), $ = x.getBoundingClientRect(), m = ln(x), _ = $.left + (x.clientLeft + parseFloat(m.paddingLeft)) * C.x, v = $.top + (x.clientTop + parseFloat(m.paddingTop)) * C.y; + l *= C.x, u *= C.y, c *= C.x, f *= C.y, l += _, u += v, p = Bt(x), x = Ls(p); + } + } + return ki({ + width: c, + height: f, + x: l, + y: u + }); +} +function wa(e, t) { + const n = Gi(e).scrollLeft; + return t ? t.left + n : xo(zn(e)).left + n; +} +function md(e, t, n) { + n === void 0 && (n = !1); + const r = e.getBoundingClientRect(), o = r.left + t.scrollLeft - (n ? 0 : ( + // RTL <body> scrollbar. + wa(e, r) + )), i = r.top + t.scrollTop; + return { + x: o, + y: i + }; +} +function tm(e) { + let { + elements: t, + rect: n, + offsetParent: r, + strategy: o + } = e; + const i = o === "fixed", s = zn(r), a = t ? qi(t.floating) : !1; + if (r === s || a && i) + return n; + let l = { + scrollLeft: 0, + scrollTop: 0 + }, u = mn(1); + const c = mn(0), f = _n(r); + if ((f || !f && !i) && ((Wr(r) !== "body" || Ao(s)) && (l = Gi(r)), _n(r))) { + const g = xo(r); + u = Sr(r), c.x = g.x + r.clientLeft, c.y = g.y + r.clientTop; + } + const d = s && !f && !i ? md(s, l, !0) : mn(0); + return { + width: n.width * u.x, + height: n.height * u.y, + x: n.x * u.x - l.scrollLeft * u.x + c.x + d.x, + y: n.y * u.y - l.scrollTop * u.y + c.y + d.y + }; +} +function nm(e) { + return Array.from(e.getClientRects()); +} +function rm(e) { + const t = zn(e), n = Gi(e), r = e.ownerDocument.body, o = Er(t.scrollWidth, t.clientWidth, r.scrollWidth, r.clientWidth), i = Er(t.scrollHeight, t.clientHeight, r.scrollHeight, r.clientHeight); + let s = -n.scrollLeft + wa(e); + const a = -n.scrollTop; + return ln(r).direction === "rtl" && (s += Er(t.clientWidth, r.clientWidth) - o), { + width: o, + height: i, + x: s, + y: a + }; +} +function om(e, t) { + const n = Bt(e), r = zn(e), o = n.visualViewport; + let i = r.clientWidth, s = r.clientHeight, a = 0, l = 0; + if (o) { + i = o.width, s = o.height; + const u = ya(); + (!u || u && t === "fixed") && (a = o.offsetLeft, l = o.offsetTop); + } + return { + width: i, + height: s, + x: a, + y: l + }; +} +function im(e, t) { + const n = xo(e, !0, t === "fixed"), r = n.top + e.clientTop, o = n.left + e.clientLeft, i = _n(e) ? Sr(e) : mn(1), s = e.clientWidth * i.x, a = e.clientHeight * i.y, l = o * i.x, u = r * i.y; + return { + width: s, + height: a, + x: l, + y: u + }; +} +function Vl(e, t, n) { + let r; + if (t === "viewport") + r = om(e, n); + else if (t === "document") + r = rm(zn(e)); + else if (an(t)) + r = im(t, n); + else { + const o = pd(e); + r = { + x: t.x - o.x, + y: t.y - o.y, + width: t.width, + height: t.height + }; + } + return ki(r); +} +function yd(e, t) { + const n = er(e); + return n === t || !an(n) || zr(n) ? !1 : ln(n).position === "fixed" || yd(n, t); +} +function sm(e, t) { + const n = t.get(e); + if (n) + return n; + let r = gd(e, []).filter((a) => an(a) && Wr(a) !== "body"), o = null; + const i = ln(e).position === "fixed"; + let s = i ? er(e) : e; + for (; an(s) && !zr(s); ) { + const a = ln(s), l = ma(s); + !l && a.position === "fixed" && (o = null), (i ? !l && !o : !l && a.position === "static" && !!o && ["absolute", "fixed"].includes(o.position) || Ao(s) && !l && yd(e, s)) ? r = r.filter((c) => c !== s) : o = a, s = er(s); + } + return t.set(e, r), r; +} +function am(e) { + let { + element: t, + boundary: n, + rootBoundary: r, + strategy: o + } = e; + const s = [...n === "clippingAncestors" ? qi(t) ? [] : sm(t, this._c) : [].concat(n), r], a = s[0], l = s.reduce((u, c) => { + const f = Vl(t, c, o); + return u.top = Er(f.top, u.top), u.right = _o(f.right, u.right), u.bottom = _o(f.bottom, u.bottom), u.left = Er(f.left, u.left), u; + }, Vl(t, a, o)); + return { + width: l.right - l.left, + height: l.bottom - l.top, + x: l.left, + y: l.top + }; +} +function lm(e) { + const { + width: t, + height: n + } = hd(e); + return { + width: t, + height: n + }; +} +function um(e, t, n) { + const r = _n(t), o = zn(t), i = n === "fixed", s = xo(e, !0, i, t); + let a = { + scrollLeft: 0, + scrollTop: 0 + }; + const l = mn(0); + if (r || !r && !i) + if ((Wr(t) !== "body" || Ao(o)) && (a = Gi(t)), r) { + const d = xo(t, !0, i, t); + l.x = d.x + t.clientLeft, l.y = d.y + t.clientTop; + } else o && (l.x = wa(o)); + const u = o && !r && !i ? md(o, a) : mn(0), c = s.left + a.scrollLeft - l.x - u.x, f = s.top + a.scrollTop - l.y - u.y; + return { + x: c, + y: f, + width: s.width, + height: s.height + }; +} +function fs(e) { + return ln(e).position === "static"; +} +function Dl(e, t) { + if (!_n(e) || ln(e).position === "fixed") + return null; + if (t) + return t(e); + let n = e.offsetParent; + return zn(e) === n && (n = n.ownerDocument.body), n; +} +function wd(e, t) { + const n = Bt(e); + if (qi(e)) + return n; + if (!_n(e)) { + let o = er(e); + for (; o && !zr(o); ) { + if (an(o) && !fs(o)) + return o; + o = er(o); + } + return n; + } + let r = Dl(e, t); + for (; r && jp(r) && fs(r); ) + r = Dl(r, t); + return r && zr(r) && fs(r) && !ma(r) ? n : r || Jp(e) || n; +} +const cm = async function(e) { + const t = this.getOffsetParent || wd, n = this.getDimensions, r = await n(e.floating); + return { + reference: um(e.reference, await t(e.floating), e.strategy), + floating: { + x: 0, + y: 0, + width: r.width, + height: r.height + } + }; +}; +function dm(e) { + return ln(e).direction === "rtl"; +} +const fm = { + convertOffsetParentRelativeRectToViewportRelativeRect: tm, + getDocumentElement: zn, + getClippingRect: am, + getOffsetParent: wd, + getElementRects: cm, + getClientRects: nm, + getDimensions: lm, + getScale: Sr, + isElement: an, + isRTL: dm +}, gm = Gp, hm = Up, vm = Kp, pm = Wp, mm = (e, t, n) => { + const r = /* @__PURE__ */ new Map(), o = { + platform: fm, + ...n + }, i = { + ...o.platform, + _c: r + }; + return Fp(e, t, { + ...o, + platform: i + }); +}, ym = ({ + trigger: e, + triggerEvent: t, + floatContent: n, + placement: r = "bottom", + offsetOptions: o, + flipOptions: i, + shiftOptions: s, + interactive: a, + showArrow: l +}) => { + if (typeof e == "string") { + const $ = document.querySelector(e); + if ($) + e = $; + else + throw new Error("element not found by document.querySelector('" + e + "')"); + } + let u; + if (typeof n == "string") { + const $ = document.querySelector(n); + if ($) + u = $; + else + throw new Error("element not found by document.querySelector('" + n + "')"); + } else + u = n; + let c; + l && (c = document.createElement("div"), c.style.position = "absolute", c.style.backgroundColor = "#222", c.style.width = "8px", c.style.height = "8px", c.style.transform = "rotate(45deg)", c.style.display = "none", u.firstElementChild.before(c)); + function f() { + mm(e, u, { + placement: r, + middleware: [ + gm(o), + // 手动偏移配置 + vm(i), + //自动翻转 + hm(s), + //自动偏移(使得浮动元素能够进入视野) + ...l ? [pm({ element: c })] : [] + ] + }).then(({ x: $, y: m, placement: _, middlewareData: v }) => { + if (Object.assign(u.style, { + left: `${$}px`, + top: `${m}px` + }), l) { + const { x: b, y: N } = v.arrow, E = { + top: "bottom", + right: "left", + bottom: "top", + left: "right" + }[_.split("-")[0]]; + Object.assign(c.style, { + zIndex: -1, + left: b != null ? `${b}px` : "", + top: N != null ? `${N}px` : "", + right: "", + bottom: "", + [E]: "2px" + }); + } + }); + } + let d = !1; + function g() { + u.style.display = "block", u.style.visibility = "block", u.style.position = "absolute", l && (c.style.display = "block"), d = !0, f(); + } + function p() { + u.style.display = "none", l && (c.style.display = "none"), d = !1; + } + function x($) { + $.stopPropagation(), d ? p() : g(); + } + function C($) { + u.contains($.target) || p(); + } + return (!t || t.length == 0) && (t = ["click"]), t.forEach(($) => { + e.addEventListener($, x); + }), document.addEventListener("click", C), { + destroy() { + t.forEach(($) => { + e.removeEventListener($, x); + }), document.removeEventListener("click", C); + }, + hide() { + p(); + }, + isVisible() { + return d; + } + }; +}; +var wm = /* @__PURE__ */ ne('<div style="position: relative"><div><!></div> <div style="display: none; width: 100%;z-index: 9999"><!></div></div>'); +function Lo(e, t) { + de(t, !0); + const n = w(t, "children", 7), r = w(t, "floating", 7), o = w(t, "placement", 7, "bottom"); + let i, s, a; + un(() => (a = ym({ + trigger: i, + floatContent: s, + interactive: !0, + placement: o() + }), () => { + a.destroy(); + })); + function l() { + a.hide(); + } + var u = wm(), c = X(u), f = X(c); + lr(f, n), Z(c), An(c, (p) => i = p, () => i); + var d = z(c, 2), g = X(d); + return lr(g, r), Z(d), An(d, (p) => s = p, () => s), Z(u), L(e, u), fe({ + hide: l, + get children() { + return n(); + }, + set children(p) { + n(p), y(); + }, + get floating() { + return r(); + }, + set floating(p) { + r(p), y(); + }, + get placement() { + return o(); + }, + set placement(p = "bottom") { + o(p), y(); + } + }); +} +ae(Lo, { children: {}, floating: {}, placement: {} }, [], ["hide"], !0); +function Ge(e, t) { + de(t, !0); + const n = w(t, "children", 7), r = w(t, "level", 7, 1), o = w(t, "mt", 7), i = w(t, "mb", 7); + var s = et(), a = be(s); + return m1(a, () => `h${r()}`, !1, (l, u) => { + let c; + Ee(() => c = on( + l, + c, + { + class: "tf-heading", + style: `margin-top:${o() || "0"};margin-bottom:${i() || "0"}` + }, + void 0, + l.namespaceURI === Rl, + l.nodeName.includes("-") + )); + var f = et(), d = be(f); + lr(d, () => n() ?? dt), L(u, f); + }), L(e, s), fe({ + get children() { + return n(); + }, + set children(l) { + n(l), y(); + }, + get level() { + return r(); + }, + set level(l = 1) { + r(l), y(); + }, + get mt() { + return o(); + }, + set mt(l) { + o(l), y(); + }, + get mb() { + return i(); + }, + set mb(l) { + i(l), y(); + } + }); +} +ae(Ge, { children: {}, level: {}, mt: {}, mb: {} }, [], [], !0); +var _m = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="svelte-1rvn4a8"><path d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z" class="svelte-1rvn4a8"></path></svg>'); +const xm = { + hash: "svelte-1rvn4a8", + code: ".input-btn-more {border:1px solid transparent;padding:3px;&:hover {background:#eee;border:1px solid transparent;}}" +}; +function Ui(e, t) { + de(t, !0), Je(e, xm); + const n = /* @__PURE__ */ yt(t, ["$$slots", "$$events", "$$legacy", "$$host"]); + Ke(e, ut(() => n, { + get class() { + return `input-btn-more ${t.class ?? ""}`; + }, + children: (r, o) => { + var i = _m(); + L(r, i); + }, + $$slots: { default: !0 } + })), fe(); +} +ae(Ui, {}, [], [], !0); +const bm = () => { + const e = Ue(); + return { + deleteNode: (n) => { + e.nodes.update((r) => r.filter((o) => o.id !== n)), e.edges.update( + (r) => r.filter((o) => o.source !== n && o.target !== n) + ); + } + }; +}, Rr = (e = 16) => { + const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", n = new Uint8Array(e); + return crypto.getRandomValues(n), Array.from(n, (r) => t[r % t.length]).join(""); +}, Cm = () => { + const { nodes: e, nodeLookup: t } = Ue(); + return { + copyNode: (r) => { + var s; + const i = (s = q(t).get(r)) == null ? void 0 : s.internals.userNode; + if (i) { + const a = Rr(), l = { + ...i, + id: a, + position: { + x: i.position.x + 50, + y: i.position.y + 50 + } + }; + e.update((u) => [...u, l]), e.update( + (u) => u.map( + (c) => c.id === a ? { ...c, selected: !0 } : { ...c, selected: !1 } + ) + ); + } + } + }; +}; +var km = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8 18.3915V5.60846L18.2264 12L8 18.3915ZM6 3.80421V20.1957C6 20.9812 6.86395 21.46 7.53 21.0437L20.6432 12.848C21.2699 12.4563 21.2699 11.5436 20.6432 11.152L7.53 2.95621C6.86395 2.53993 6 3.01878 6 3.80421Z"></path></svg>'), $m = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM5.00242 8L5.00019 20H14.9998V8H5.00242ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z"></path></svg>'), Em = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M17 6H22V8H20V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V8H2V6H7V3C7 2.44772 7.44772 2 8 2H16C16.5523 2 17 2.44772 17 3V6ZM18 8H6V20H18V8ZM9 11H11V17H9V11ZM13 11H15V17H13V11ZM9 4V6H15V4H9Z"></path></svg>'), Sm = /* @__PURE__ */ ne('<div class="tf-node-toolbar svelte-44dmwv"><!> <!> <!></div>'), Pm = /* @__PURE__ */ ne('<!> <div class="tf-node-wrapper"><div class="tf-node-wrapper-title">TinyFlow.ai</div> <div class="tf-node-wrapper-body"><!></div></div> <!> <!> <!>', 1); +const Nm = { + hash: "svelte-44dmwv", + code: ".tf-node-toolbar.svelte-44dmwv {display:flex;gap:5px;padding:5px;border-radius:5px;background:#fff;border:1px solid #eee;box-shadow:0 0 5px rgba(0, 0, 0, 0.1);}.tf-node-toolbar-item {border:1px solid transparent;}" +}; +function dn(e, t) { + de(t, !0), Je(e, Nm); + const n = w(t, "data", 7), r = w(t, "id", 7, ""), o = w(t, "icon", 7), i = w(t, "handle", 7), s = w(t, "children", 7), a = w(t, "allowExecute", 7, !0), l = w(t, "allowCopy", 7, !0), u = w(t, "allowDelete", 7, !0), c = w(t, "showSourceHandle", 7, !0), f = w(t, "showTargetHandle", 7, !0); + let d = n().expand ? ["key"] : []; + const { updateNodeData: g } = Dt(), p = [ + { + key: "key", + icon: o(), + title: n().title, + description: n().description, + content: s() + } + ], { deleteNode: x } = bm(), { copyNode: C } = Cm(); + var $ = Pm(), m = be($); + { + var _ = (O) => { + od(O, { + get position() { + return $e.Top; + }, + align: "end", + children: (R, S) => { + var T = Sm(), k = X(T); + { + var P = (K) => { + Ke(K, { + class: "tf-node-toolbar-item", + children: (ie, ee) => { + var W = km(); + L(ie, W); + }, + $$slots: { default: !0 } + }); + }; + ke(k, (K) => { + a() && K(P); + }); + } + var H = z(k, 2); + { + var I = (K) => { + Ke(K, { + class: "tf-node-toolbar-item", + onclick: () => { + C(r()); + }, + children: (ie, ee) => { + var W = $m(); + L(ie, W); + }, + $$slots: { default: !0 } + }); + }; + ke(H, (K) => { + l() && K(I); + }); + } + var B = z(H, 2); + { + var F = (K) => { + Ke(K, { + class: "tf-node-toolbar-item", + onclick: () => { + x(r()); + }, + children: (ie, ee) => { + var W = Em(); + L(ie, W); + }, + $$slots: { default: !0 } + }); + }; + ke(B, (K) => { + u() && K(F); + }); + } + Z(T), L(R, T); + }, + $$slots: { default: !0 } + }); + }; + ke(m, (O) => { + (a() || l() || u()) && O(_); + }); + } + var v = z(m, 2), b = z(X(v), 2), N = X(b); + ad(N, { + items: p, + activeKeys: d, + onChange: (O, R) => { + g(r(), { expand: R == null ? void 0 : R.includes("key") }); + } + }), Z(b), Z(v); + var E = z(v, 2); + { + var M = (O) => { + Qn(O, { + type: "target", + get position() { + return $e.Left; + }, + style: " left: -12px;top: 20px" + }); + }; + ke(E, (O) => { + f() && O(M); + }); + } + var D = z(E, 2); + { + var V = (O) => { + Qn(O, { + type: "source", + get position() { + return $e.Right; + }, + style: "right: -12px;top: 20px" + }); + }; + ke(D, (O) => { + c() && O(V); + }); + } + var A = z(D, 2); + return lr(A, () => i() ?? dt), L(e, $), fe({ + get data() { + return n(); + }, + set data(O) { + n(O), y(); + }, + get id() { + return r(); + }, + set id(O = "") { + r(O), y(); + }, + get icon() { + return o(); + }, + set icon(O) { + o(O), y(); + }, + get handle() { + return i(); + }, + set handle(O) { + i(O), y(); + }, + get children() { + return s(); + }, + set children(O) { + s(O), y(); + }, + get allowExecute() { + return a(); + }, + set allowExecute(O = !0) { + a(O), y(); + }, + get allowCopy() { + return l(); + }, + set allowCopy(O = !0) { + l(O), y(); + }, + get allowDelete() { + return u(); + }, + set allowDelete(O = !0) { + u(O), y(); + }, + get showSourceHandle() { + return c(); + }, + set showSourceHandle(O = !0) { + c(O), y(); + }, + get showTargetHandle() { + return f(); + }, + set showTargetHandle(O = !0) { + f(O), y(); + } + }); +} +ae( + dn, + { + data: {}, + id: {}, + icon: {}, + handle: {}, + children: {}, + allowExecute: {}, + allowCopy: {}, + allowDelete: {}, + showSourceHandle: {}, + showTargetHandle: {} + }, + [], + [], + !0 +); +function ht() { + return ar("svelteflow__node_id"); +} +const _d = [ + { + value: "String", + label: "String" + }, + { + value: "Number", + label: "Number" + }, + { + value: "Boolean", + label: "Boolean" + }, + { + value: "File", + label: "File" + }, + { + value: "Object", + label: "Object" + }, + { + value: "Array", + label: "Array" + } +], Mm = [ + { + value: "ref", + label: "引用" + }, + { + value: "input", + label: "固定值" + } +]; +var Tm = /* @__PURE__ */ ne('<div class="input-more-setting svelte-laou7w"><div class="input-more-item svelte-laou7w">参数类型: <!></div> <div class="input-more-item svelte-laou7w">默认值: <!></div> <div class="input-more-item svelte-laou7w">参数描述: <!></div> <div class="input-more-item svelte-laou7w"><!></div></div>'), Hm = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z"></path></svg>'), Vm = /* @__PURE__ */ ne('<div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div>', 1); +const Dm = { + hash: "svelte-laou7w", + code: ".input-item.svelte-laou7w {display:flex;align-items:center;}.input-more-setting.svelte-laou7w {display:flex;flex-direction:column;gap:10px;padding:10px;background:#fff;border:1px solid #ddd;border-radius:5px;width:200px;box-shadow:0 0 10px 2px rgba(0, 0, 0, 0.1);}.input-more-setting.svelte-laou7w .input-more-item:where(.svelte-laou7w) {display:flex;flex-direction:column;gap:3px;font-size:12px;color:#666;}" +}; +function xd(e, t) { + de(t, !0), Je(e, Dm); + const [n, r] = tt(), o = () => Q(h(l), "$node", n), i = w(t, "parameter", 7), s = w(t, "index", 7); + let a = ht(), l = /* @__PURE__ */ Me(() => pr(a)), u = /* @__PURE__ */ Me(() => { + var M, D; + return { + ...i(), + ...(D = (M = o()) == null ? void 0 : M.data) == null ? void 0 : D.parameters[s()] + }; + }); + const { updateNodeData: c } = Dt(), f = (M) => { + const D = M.target.value; + c(a, (V) => { + let A = V.data.parameters; + return A[s()].name = D, { parameters: A }; + }); + }, d = (M) => { + const D = M.target.checked; + c(a, (V) => { + let A = V.data.parameters; + return A[s()].required = D, { parameters: A }; + }); + }, g = (M) => { + const D = M.value; + D && c(a, (V) => { + let A = V.data.parameters; + return A[s()].dataType = D, { parameters: A }; + }); + }; + let p; + const x = () => { + c(a, (M) => { + let D = M.data.parameters; + return D.splice(s(), 1), { parameters: [...D] }; + }), p == null || p.hide(); + }; + var C = Vm(), $ = be(C), m = X($); + xt(m, { + style: "width: 100%;", + get value() { + return h(u).name; + }, + placeholder: "请输入参数名称", + oninput: f + }), Z($); + var _ = z($, 2), v = X(_); + id(v, { + get checked() { + return h(u).required; + }, + onchange: d + }), Z(_); + var b = z(_, 2), N = X(b); + An( + Lo(N, { + placement: "bottom", + floating: (D) => { + var V = Tm(), A = X(V), O = z(X(A)); + const R = /* @__PURE__ */ Me(() => h(u).dataType ? [h(u).dataType] : ["String"]); + sn(O, { + items: _d, + style: "width: 100%", + onSelect: g, + get value() { + return h(R); + } + }), Z(A); + var S = z(A, 2), T = z(X(S)); + $t(T, { rows: 1, style: "width: 100%;" }), Z(S); + var k = z(S, 2), P = z(X(k)); + $t(P, { rows: 3, style: "width: 100%;" }), Z(k); + var H = z(k, 2), I = X(H); + Ke(I, { + onclick: x, + children: (B, F) => { + Se(); + var K = Ie("删除"); + L(B, K); + }, + $$slots: { default: !0 } + }), Z(H), Z(V), L(D, V); + }, + children: (D, V) => { + Ke(D, { + class: "input-btn-more", + children: (A, O) => { + var R = Hm(); + L(A, R); + }, + $$slots: { default: !0 } + }); + }, + $$slots: { floating: !0, default: !0 } + }), + (D) => p = D, + () => p + ), Z(b), L(e, C); + var E = fe({ + get parameter() { + return i(); + }, + set parameter(M) { + i(M), y(); + }, + get index() { + return s(); + }, + set index(M) { + s(M), y(); + } + }); + return r(), E; +} +ae(xd, { parameter: {}, index: {} }, [], [], !0); +var Am = /* @__PURE__ */ ne('<div class="input-header svelte-3n0wca">参数名称</div> <div class="input-header svelte-3n0wca">必填</div> <div class="input-header svelte-3n0wca"></div>', 1), Lm = /* @__PURE__ */ ne('<div class="none-params svelte-3n0wca">无输入参数</div>'), Om = /* @__PURE__ */ ne('<div class="input-container svelte-3n0wca"><!> <!></div>'); +const Im = { + hash: "svelte-3n0wca", + code: `.input-container.svelte-3n0wca {display:grid;grid-template-columns:80% 10% 10%;row-gap:5px;column-gap:3px;}.input-container.svelte-3n0wca .none-params:where(.svelte-3n0wca) {font-size:12px;background:#f8f8f8;height:40px;display:flex;justify-content:center;align-items:center;border-radius:5px;width:calc(100% - 5px);grid-column:1 / -1; + /* 从第一列开始到最后一列结束 */}.input-container.svelte-3n0wca .input-header:where(.svelte-3n0wca) {font-size:12px;color:#666;}` +}; +function bd(e, t) { + de(t, !0), Je(e, Im); + const [n, r] = tt(), o = () => Q(h(s), "$node", n); + let i = ht(), s = /* @__PURE__ */ Me(() => pr(i)), a = /* @__PURE__ */ Me(() => { + var d, g; + return [...((g = (d = o()) == null ? void 0 : d.data) == null ? void 0 : g.parameters) || []]; + }); + var l = Om(), u = X(l); + { + var c = (d) => { + var g = Am(); + Se(4), L(d, g); + }; + ke(u, (d) => { + h(a).length !== 0 && d(c); + }); + } + var f = z(u, 2); + Yt( + f, + 19, + () => h(a), + (d) => d.id, + (d, g, p) => { + xd(d, { + get parameter() { + return h(g); + }, + get index() { + return h(p); + } + }); + }, + (d) => { + var g = Lm(); + L(d, g); + } + ), Z(l), L(e, l), fe(), r(); +} +ae(bd, {}, [], [], !0); +const Cd = (e) => { + !e || e.length == 0 || e.forEach((t) => { + t.id || (t.id = Rr()), Cd(t.children); + }); +}, kn = () => { + const { updateNodeData: e } = Dt(); + return { + addParameter: (t, n = "parameters", r) => { + Cd(r == null ? void 0 : r.children); + const o = { + ...r, + id: Rr() + }; + e(t, (i) => { + let s = i.data[n]; + return s ? s.push(o) : s = [o], { + [n]: [...s] + }; + }); + } + }; +}; +var zm = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12C15 13.6569 13.6569 15 12 15Z"></path></svg>'), Rm = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Bm = /* @__PURE__ */ ne('<div class="heading svelte-r5g35l"><!> <!></div> <!>', 1); +const Ym = { + hash: "svelte-r5g35l", + code: ".heading.svelte-r5g35l {display:flex;margin-bottom:10px;}.input-btn-more {border:1px solid transparent;padding:3px;}.input-btn-more:hover {background:#eee;border:1px solid transparent;}" +}; +function kd(e, t) { + de(t, !0), Je(e, Ym); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(); + return dn(e, ut(() => r, { + get data() { + return n(); + }, + allowExecute: !1, + showTargetHandle: !1, + icon: (a) => { + var l = zm(); + L(a, l); + }, + children: (a, l) => { + var u = Bm(), c = be(u), f = X(c); + Ge(f, { + level: 3, + children: (p, x) => { + Se(); + var C = Ie("输入参数"); + L(p, C); + }, + $$slots: { default: !0 } + }); + var d = z(f, 2); + Ke(d, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (p, x) => { + var C = Rm(); + L(p, C); + }, + $$slots: { default: !0 } + }), Z(c); + var g = z(c, 2); + bd(g, {}), L(a, u); + }, + $$slots: { icon: !0, default: !0 } + })), fe({ + get data() { + return n(); + }, + set data(s) { + n(s), y(); + } + }); +} +ae(kd, { data: {} }, [], [], !0); +const $d = (e, t, n) => { + for (let r of n) + r.target === t && r.source && (e.push(r.source), $d(e, r.source, n)); +}, Al = (e, t) => { + if (e.type === "startNode") { + const n = e.data.parameters, r = []; + if (n) + for (const o of n) + r.push({ + label: o.name + (t ? ` (Array<${o.dataType || "String"}>)` : ` (${o.dataType || "String"})`), + value: e.id + "." + o.name + }); + return { + label: e.data.title, + value: e.id, + children: r + }; + } else { + if (e.type === "loopNode" && t) + return { + label: e.data.title, + value: e.id, + children: [ + { + label: "loopItem", + value: e.id + ".loop" + }, + { + label: "index (Number)", + value: e.id + ".index" + } + ] + }; + { + const n = e.data.outputDefs; + if (n) { + const r = (o, i) => !o || o.length === 0 ? [] : o.map((s) => ({ + label: s.name + (t ? ` (Array<${s.dataType || "String"}>)` : ` (${s.dataType || "String"})`), + // label: param.name , + value: i + "." + s.name, + children: r(s.children, i + "." + s.name) + })); + return { + label: e.data.title, + value: e.id, + children: r(n, e.id) + }; + } + } + } +}, Zm = (e = !1) => { + const t = ht(), n = pr(t), { nodes: r, edges: o } = Ue(); + return Kn([n, r, o], ([i, s, a]) => { + const l = []; + if (e) { + for (let u of s) + if (u.parentId === i.id) { + const c = Al(u, u.parentId === i.id); + c && l.push(c); + } + } else { + const u = []; + $d(u, t, a); + for (let c of s) + if (u.includes(c.id)) { + const f = Al(c, c.parentId === i.id); + f && l.push(f); + } + } + return l; + }); +}; +var Xm = /* @__PURE__ */ ne('<div class="input-more-setting svelte-laou7w"><div class="input-more-item svelte-laou7w">数据来源: <!></div> <div class="input-more-item svelte-laou7w">默认值: <!></div> <div class="input-more-item svelte-laou7w">参数描述: <!></div> <div class="input-more-item svelte-laou7w"><!></div></div>'), Fm = /* @__PURE__ */ ne('<div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div>', 1); +const Wm = { + hash: "svelte-laou7w", + code: ".input-item.svelte-laou7w {display:flex;align-items:center;}.input-more-setting.svelte-laou7w {display:flex;flex-direction:column;gap:10px;padding:10px;background:#fff;border:1px solid #ddd;border-radius:5px;width:200px;box-shadow:0 0 10px 2px rgba(0, 0, 0, 0.1);}.input-more-setting.svelte-laou7w .input-more-item:where(.svelte-laou7w) {display:flex;flex-direction:column;gap:3px;font-size:12px;color:#666;}" +}; +function Ed(e, t) { + de(t, !0), Je(e, Wm); + const [n, r] = tt(), o = () => Q(h(c), "$node", n), i = () => Q(v, "$selectItems", n), s = w(t, "parameter", 7), a = w(t, "index", 7), l = w(t, "dataKeyName", 7); + let u = ht(), c = /* @__PURE__ */ Me(() => pr(u)), f = /* @__PURE__ */ Me(() => { + var T; + return { + ...s(), + ...(T = o()) == null ? void 0 : T.data[l()][a()] + }; + }); + const { updateNodeData: d } = Dt(), g = (T, k) => { + d(u, (P) => { + let H = P.data[l()]; + return H[a()] = { ...H[a()], [T]: k }, { [l()]: H }; + }); + }, p = (T) => { + const k = T.target.value; + g("name", k); + }, x = (T) => { + const k = T.target.value; + g("value", k); + }, C = (T) => { + const k = T.value; + g("ref", k); + }, $ = (T) => { + const k = T.value; + g("refType", k); + }; + let m; + const _ = () => { + d(u, (T) => { + let k = T.data[l()]; + return k.splice(a(), 1), { [l()]: [...k] }; + }), m == null || m.hide(); + }, v = Zm(); + var b = Fm(), N = be(b), E = X(N); + xt(E, { + style: "width: 100%;", + get value() { + return h(f).name; + }, + placeholder: "请输入参数名称", + oninput: p + }), Z(N); + var M = z(N, 2), D = X(M); + { + var V = (T) => { + xt(T, { + get value() { + return h(f).value; + }, + placeholder: "请输入参数值", + oninput: x + }); + }, A = (T) => { + const k = /* @__PURE__ */ Me(() => [h(f).ref]); + sn(T, { + get items() { + return i(); + }, + style: "width: 100%", + defaultValue: ["ref"], + get value() { + return h(k); + }, + expandAll: !0, + onSelect: C + }); + }; + ke(D, (T) => { + h(f).refType === "input" ? T(V) : T(A, !1); + }); + } + Z(M); + var O = z(M, 2), R = X(O); + An( + Lo(R, { + placement: "bottom", + floating: (k) => { + var P = Xm(), H = X(P), I = z(X(H)); + const B = /* @__PURE__ */ Me(() => h(f).refType ? [h(f).refType] : []); + sn(I, { + items: Mm, + style: "width: 100%", + defaultValue: ["ref"], + get value() { + return h(B); + }, + onSelect: $ + }), Z(H); + var F = z(H, 2), K = z(X(F)); + $t(K, { + rows: 1, + style: "width: 100%;", + onchange: (me) => { + const Ce = me.target.value; + g("defaultValue", Ce); + } + }), Z(F); + var ie = z(F, 2), ee = z(X(ie)); + $t(ee, { + rows: 3, + style: "width: 100%;", + onchange: (me) => { + const Ce = me.target.value; + g("description", Ce); + } + }), Z(ie); + var W = z(ie, 2), ue = X(W); + Ke(ue, { + onclick: _, + children: (me, Ce) => { + Se(); + var ge = Ie("删除"); + L(me, ge); + }, + $$slots: { default: !0 } + }), Z(W), Z(P), L(k, P); + }, + children: (k, P) => { + Ui(k, {}); + }, + $$slots: { floating: !0, default: !0 } + }), + (k) => m = k, + () => m + ), Z(O), L(e, b); + var S = fe({ + get parameter() { + return s(); + }, + set parameter(T) { + s(T), y(); + }, + get index() { + return a(); + }, + set index(T) { + a(T), y(); + }, + get dataKeyName() { + return l(); + }, + set dataKeyName(T) { + l(T), y(); + } + }); + return r(), S; +} +ae(Ed, { parameter: {}, index: {}, dataKeyName: {} }, [], [], !0); +var Km = /* @__PURE__ */ ne('<div class="input-header svelte-1sm1mgi">参数名称</div> <div class="input-header svelte-1sm1mgi">参数值</div> <div class="input-header svelte-1sm1mgi"></div>', 1), qm = /* @__PURE__ */ ne('<div class="none-params svelte-1sm1mgi"> </div>'), Gm = /* @__PURE__ */ ne('<div class="input-container svelte-1sm1mgi"><!> <!></div>'); +const Um = { + hash: "svelte-1sm1mgi", + code: `.input-container.svelte-1sm1mgi {display:grid;grid-template-columns:40% 50% 10%;row-gap:5px;column-gap:3px;}.input-container.svelte-1sm1mgi .none-params:where(.svelte-1sm1mgi) {font-size:12px;background:#f8f8f8;height:40px;display:flex;justify-content:center;align-items:center;border-radius:5px;width:calc(100% - 5px);grid-column:1 / -1; + /* 从第一列开始到最后一列结束 */}.input-container.svelte-1sm1mgi .input-header:where(.svelte-1sm1mgi) {font-size:12px;color:#666;}` +}; +function zt(e, t) { + de(t, !0), Je(e, Um); + const [n, r] = tt(), o = () => Q(h(l), "$node", n), i = w(t, "noneParameterText", 7, "无输入参数"), s = w(t, "dataKeyName", 7, "parameters"); + let a = ht(), l = /* @__PURE__ */ Me(() => pr(a)), u = /* @__PURE__ */ Me(() => { + var x; + return [...((x = o()) == null ? void 0 : x.data[s()]) || []]; + }); + var c = Gm(), f = X(c); + { + var d = (x) => { + var C = Km(); + Se(4), L(x, C); + }; + ke(f, (x) => { + h(u).length !== 0 && x(d); + }); + } + var g = z(f, 2); + Yt( + g, + 19, + () => h(u), + (x) => x.id, + (x, C, $) => { + Ed(x, { + get parameter() { + return h(C); + }, + get index() { + return h($); + }, + get dataKeyName() { + return s(); + } + }); + }, + (x) => { + var C = qm(), $ = X(C, !0); + Z(C), Ee(() => Rt($, i())), L(x, C); + } + ), Z(c), L(e, c); + var p = fe({ + get noneParameterText() { + return i(); + }, + set noneParameterText(x = "无输入参数") { + i(x), y(); + }, + get dataKeyName() { + return s(); + }, + set dataKeyName(x = "parameters") { + s(x), y(); + } + }); + return r(), p; +} +ae(zt, { noneParameterText: {}, dataKeyName: {} }, [], [], !0); +var jm = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6 5.1438V16.0002H18.3391L6 5.1438ZM4 2.932C4 2.07155 5.01456 1.61285 5.66056 2.18123L21.6501 16.2494C22.3423 16.8584 21.9116 18.0002 20.9896 18.0002H6V22H4V2.932Z"></path></svg>'), Jm = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Qm = /* @__PURE__ */ ne('<div class="heading svelte-11h445j"><!> <!></div> <!>', 1); +const ey = { + hash: "svelte-11h445j", + code: ".heading.svelte-11h445j {display:flex;margin-bottom:10px;}" +}; +function Sd(e, t) { + de(t, !0), Je(e, ey); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(); + return dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + allowExecute: !1, + showSourceHandle: !1, + icon: (a) => { + var l = jm(); + L(a, l); + }, + children: (a, l) => { + var u = Qm(), c = be(u), f = X(c); + Ge(f, { + level: 3, + children: (p, x) => { + Se(); + var C = Ie("输出参数"); + L(p, C); + }, + $$slots: { default: !0 } + }); + var d = z(f, 2); + Ke(d, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o, "outputDefs"); + }, + children: (p, x) => { + var C = Jm(); + L(p, C); + }, + $$slots: { default: !0 } + }), Z(c); + var g = z(c, 2); + zt(g, { + noneParameterText: "无输出参数", + dataKeyName: "outputDefs" + }), L(a, u); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(s) { + n(s), y(); + } + }); +} +ae(Sd, { data: {} }, [], [], !0); +const Oo = () => ar("tinyflow_options"); +var ty = /* @__PURE__ */ _e('<svg style="transform: scaleY(-1)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 8V16C13 17.6569 11.6569 19 10 19H7.82929C7.41746 20.1652 6.30622 21 5 21C3.34315 21 2 19.6569 2 18C2 16.3431 3.34315 15 5 15C6.30622 15 7.41746 15.8348 7.82929 17H10C10.5523 17 11 16.5523 11 16V8C11 6.34315 12.3431 5 14 5H17V2L22 6L17 10V7H14C13.4477 7 13 7.44772 13 8ZM5 19C5.55228 19 6 18.5523 6 18C6 17.4477 5.55228 17 5 17C4.44772 17 4 17.4477 4 18C4 18.5523 4.44772 19 5 19Z"></path></svg>'), ny = /* @__PURE__ */ ne('<div class="input-more-item svelte-1cfeest"><!></div>'), ry = /* @__PURE__ */ ne('<div class="input-more-setting svelte-1cfeest"><div class="input-more-item svelte-1cfeest">默认值: <!></div> <div class="input-more-item svelte-1cfeest">参数描述: <!></div> <!></div>'), oy = /* @__PURE__ */ ne('<div class="input-item svelte-1cfeest"><!> <!></div> <div class="input-item svelte-1cfeest"><!> <!></div> <div class="input-item svelte-1cfeest"><!></div>', 1); +const iy = { + hash: "svelte-1cfeest", + code: ".input-item.svelte-1cfeest {display:flex;align-items:center;gap:2px;}.input-more-setting.svelte-1cfeest {display:flex;flex-direction:column;gap:10px;padding:10px;background:#fff;border:1px solid #ddd;border-radius:5px;width:200px;box-shadow:0 0 10px 2px rgba(0, 0, 0, 0.1);}.input-more-setting.svelte-1cfeest .input-more-item:where(.svelte-1cfeest) {display:flex;flex-direction:column;gap:3px;font-size:12px;color:#666;}" +}; +function Pd(e, t) { + de(t, !0), Je(e, iy); + const [n, r] = tt(), o = () => Q(h(u), "$node", n), i = w(t, "parameter", 7), s = w(t, "position", 7), a = w(t, "dataKeyName", 7); + let l = ht(), u = /* @__PURE__ */ Me(() => pr(l)), c = /* @__PURE__ */ Me(() => { + var I; + let P = (I = o()) == null ? void 0 : I.data[a()], H; + if (P && s().length > 0) { + let B = P; + for (let F = 0; F < s().length; F++) { + const K = s()[F]; + F == s().length - 1 ? H = B[K] : B = B[K].children; + } + } + return { ...i(), ...H }; + }); + const { updateNodeData: f } = Dt(), d = (P, H) => { + f(l, (I) => { + const B = I.data[a()]; + if (B && s().length > 0) { + let F = B; + for (let K = 0; K < s().length; K++) { + const ie = s()[K]; + K == s().length - 1 ? F[ie] = { ...F[ie], [P]: H } : F = B[ie].children; + } + } + return { [a()]: B }; + }); + }, g = (P) => { + const H = P.target.value; + d("name", H); + }, p = (P) => { + const H = P.value; + d("dataType", H); + }; + let x; + const C = () => { + f(l, (P) => { + let H = P.data[a()]; + if (H && s().length > 0) { + let I = H; + for (let B = 0; B < s().length; B++) { + const F = s()[B]; + B == s().length - 1 ? I.splice(F, 1) : I = I[F].children; + } + } + return { [a()]: [...H] }; + }), x == null || x.hide(); + }, $ = () => { + f(l, (P) => { + let H = P.data[a()]; + if (H && s().length > 0) { + let I = H; + for (let B = 0; B < s().length; B++) { + const F = s()[B]; + B == s().length - 1 ? I[F].children ? I[F].children.push({ + id: Rr(), + name: "newParam", + dataType: "String" + }) : I[F].children = [ + { + id: Rr(), + name: "newParam", + dataType: "String" + } + ] : I = I[F].children; + } + } + return { [a()]: [...H] }; + }); + }; + var m = oy(), _ = be(m), v = X(_); + { + var b = (P) => { + var H = et(), I = be(H); + Yt(I, 17, s, Li, (B, F) => { + Se(); + var K = Ie(" "); + L(B, K); + }), L(P, H); + }; + ke(v, (P) => { + s().length > 1 && P(b); + }); + } + var N = z(v, 2); + const E = /* @__PURE__ */ Me(() => h(c).nameDisabled === !0); + xt(N, { + style: "width: 100%;", + get value() { + return h(c).name; + }, + placeholder: "请输入参数名称", + oninput: g, + get disabled() { + return h(E); + } + }), Z(_); + var M = z(_, 2), D = X(M); + const V = /* @__PURE__ */ Me(() => h(c).dataType ? [h(c).dataType] : []), A = /* @__PURE__ */ Me(() => h(c).dataTypeDisabled === !0); + sn(D, { + items: _d, + style: "width: 100%", + defaultValue: ["String"], + get value() { + return h(V); + }, + get disabled() { + return h(A); + }, + onSelect: p + }); + var O = z(D, 2); + { + var R = (P) => { + Ke(P, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: $, + children: (H, I) => { + var B = ty(); + L(H, B); + }, + $$slots: { default: !0 } + }); + }; + ke(O, (P) => { + (h(c).dataType === "Object" || h(c).dataType === "Array") && h(c).addChildDisabled !== !0 && P(R); + }); + } + Z(M); + var S = z(M, 2), T = X(S); + An( + Lo(T, { + placement: "bottom", + floating: (H) => { + var I = ry(), B = X(I), F = z(X(B)); + $t(F, { + rows: 1, + style: "width: 100%;", + onchange: (ue) => { + const me = ue.target.value; + d("defaultValue", me); + } + }), Z(B); + var K = z(B, 2), ie = z(X(K)); + $t(ie, { + rows: 3, + style: "width: 100%;", + onchange: (ue) => { + const me = ue.target.value; + d("description", me); + } + }), Z(K); + var ee = z(K, 2); + { + var W = (ue) => { + var me = ny(), Ce = X(me); + Ke(Ce, { + onclick: C, + children: (ge, ze) => { + Se(); + var G = Ie("删除"); + L(ge, G); + }, + $$slots: { default: !0 } + }), Z(me), L(ue, me); + }; + ke(ee, (ue) => { + h(c).deleteDisabled !== !0 && ue(W); + }); + } + Z(I), L(H, I); + }, + children: (H, I) => { + Ui(H, {}); + }, + $$slots: { floating: !0, default: !0 } + }), + (H) => x = H, + () => x + ), Z(S), L(e, m); + var k = fe({ + get parameter() { + return i(); + }, + set parameter(P) { + i(P), y(); + }, + get position() { + return s(); + }, + set position(P) { + s(P), y(); + }, + get dataKeyName() { + return a(); + }, + set dataKeyName(P) { + a(P), y(); + } + }); + return r(), k; +} +ae(Pd, { parameter: {}, position: {}, dataKeyName: {} }, [], [], !0); +var sy = /* @__PURE__ */ ne("<!> <!>", 1), ay = /* @__PURE__ */ ne('<div class="none-params svelte-1sm1mgi"> </div>'), ly = /* @__PURE__ */ ne('<div class="input-header svelte-1sm1mgi">参数名称</div> <div class="input-header svelte-1sm1mgi">参数类型</div> <div class="input-header svelte-1sm1mgi"></div>', 1), uy = /* @__PURE__ */ ne('<div class="input-container svelte-1sm1mgi"><!> <!></div>'); +const cy = { + hash: "svelte-1sm1mgi", + code: `.input-container.svelte-1sm1mgi {display:grid;grid-template-columns:40% 50% 10%;row-gap:5px;column-gap:3px;}.input-container.svelte-1sm1mgi .none-params:where(.svelte-1sm1mgi) {font-size:12px;background:#f8f8f8;height:40px;display:flex;justify-content:center;align-items:center;border-radius:5px;width:calc(100% - 5px);grid-column:1 / -1; + /* 从第一列开始到最后一列结束 */}.input-container.svelte-1sm1mgi .input-header:where(.svelte-1sm1mgi) {font-size:12px;color:#666;}` +}; +function Rn(e, t) { + de(t, !0), Je(e, cy); + const [n, r] = tt(), o = () => Q(h(u), "$node", n), i = (C, $ = dt, m = dt) => { + var _ = et(), v = be(_); + Yt( + v, + 19, + $, + (b) => `${b.id}_${b.children ? b.children.length : 0}`, + (b, N, E) => { + var M = sy(), D = be(M); + const V = /* @__PURE__ */ Me(() => [...m(), h(E)]); + Pd(D, { + get parameter() { + return h(N); + }, + get position() { + return h(V); + }, + get dataKeyName() { + return a(); + } + }); + var A = z(D, 2); + { + var O = (R) => { + var S = /* @__PURE__ */ pe(() => [...m(), h(E)]); + i(R, () => h(N).children, () => h(S)); + }; + ke(A, (R) => { + h(N).children && R(O); + }); + } + L(b, M); + }, + (b) => { + var N = et(), E = be(N); + { + var M = (D) => { + var V = ay(), A = X(V, !0); + Z(V), Ee(() => Rt(A, s())), L(D, V); + }; + ke(E, (D) => { + m().length === 0 && D(M); + }); + } + L(b, N); + } + ), L(C, _); + }, s = w(t, "noneParameterText", 7, "无输出参数"), a = w(t, "dataKeyName", 7, "outputDefs"); + let l = ht(), u = /* @__PURE__ */ Me(() => pr(l)), c = /* @__PURE__ */ Me(() => { + var C; + return [...((C = o()) == null ? void 0 : C.data[a()]) || []]; + }); + var f = uy(), d = X(f); + { + var g = (C) => { + var $ = ly(); + Se(4), L(C, $); + }; + ke(d, (C) => { + h(c).length !== 0 && C(g); + }); + } + var p = z(d, 2); + i(p, () => h(c) || [], () => []), Z(f), L(e, f); + var x = fe({ + get noneParameterText() { + return s(); + }, + set noneParameterText(C = "无输出参数") { + s(C), y(); + }, + get dataKeyName() { + return a(); + }, + set dataKeyName(C = "outputDefs") { + a(C), y(); + } + }); + return r(), x; +} +ae(Rn, { noneParameterText: {}, dataKeyName: {} }, [], [], !0); +var dy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M20.7134 7.12811L20.4668 7.69379C20.2864 8.10792 19.7136 8.10792 19.5331 7.69379L19.2866 7.12811C18.8471 6.11947 18.0555 5.31641 17.0677 4.87708L16.308 4.53922C15.8973 4.35653 15.8973 3.75881 16.308 3.57612L17.0252 3.25714C18.0384 2.80651 18.8442 1.97373 19.2761 0.930828L19.5293 0.319534C19.7058 -0.106511 20.2942 -0.106511 20.4706 0.319534L20.7238 0.930828C21.1558 1.97373 21.9616 2.80651 22.9748 3.25714L23.6919 3.57612C24.1027 3.75881 24.1027 4.35653 23.6919 4.53922L22.9323 4.87708C21.9445 5.31641 21.1529 6.11947 20.7134 7.12811ZM9 2C13.0675 2 16.426 5.03562 16.9337 8.96494L19.1842 12.5037C19.3324 12.7367 19.3025 13.0847 18.9593 13.2317L17 14.071V17C17 18.1046 16.1046 19 15 19H13.001L13 22H4L4.00025 18.3061C4.00033 17.1252 3.56351 16.0087 2.7555 15.0011C1.65707 13.6313 1 11.8924 1 10C1 5.58172 4.58172 2 9 2ZM9 4C5.68629 4 3 6.68629 3 10C3 11.3849 3.46818 12.6929 4.31578 13.7499C5.40965 15.114 6.00036 16.6672 6.00025 18.3063L6.00013 20H11.0007L11.0017 17H15V12.7519L16.5497 12.0881L15.0072 9.66262L14.9501 9.22118C14.5665 6.25141 12.0243 4 9 4ZM19.4893 16.9929L21.1535 18.1024C22.32 16.3562 23 14.2576 23 12.0001C23 11.317 22.9378 10.6486 22.8186 10L20.8756 10.5C20.9574 10.9878 21 11.489 21 12.0001C21 13.8471 20.4436 15.5642 19.4893 16.9929Z"></path></svg>'), fy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), gy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), hy = /* @__PURE__ */ ne('<div class="heading svelte-wn2kra"><!> <!></div> <!> <!> <div class="setting-title svelte-wn2kra">模型</div> <div class="setting-item svelte-wn2kra"><!> <!></div> <div class="setting-title svelte-wn2kra">采样参数</div> <div class="setting-item svelte-wn2kra"><div class="slider-container svelte-wn2kra"><label class="svelte-wn2kra"> </label> <input type="range" min="0" max="1" step="0.1" class="svelte-wn2kra"></div></div> <div class="setting-item svelte-wn2kra"><div class="slider-container svelte-wn2kra"><label class="svelte-wn2kra"> </label> <input type="range" min="0" max="1" step="0.1" class="svelte-wn2kra"></div></div> <div class="setting-item svelte-wn2kra"><div class="slider-container svelte-wn2kra"><label class="svelte-wn2kra"> </label> <input type="range" min="0" max="100" step="1" class="svelte-wn2kra"></div></div> <div class="setting-title svelte-wn2kra">系统提示词</div> <div class="setting-item svelte-wn2kra"><!></div> <div class="setting-title svelte-wn2kra">用户提示词</div> <div class="setting-item svelte-wn2kra"><!></div> <div class="heading svelte-wn2kra"><!> <!></div> <!>', 1); +const vy = { + hash: "svelte-wn2kra", + code: `.heading.svelte-wn2kra {display:flex;margin-bottom:10px;}.setting-title.svelte-wn2kra {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-wn2kra {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}\r + /* 新增样式 */.slider-container.svelte-wn2kra {width:100%;display:flex;flex-direction:column;gap:4px;}.slider-container.svelte-wn2kra label:where(.svelte-wn2kra) {font-size:12px;color:#666;display:flex;justify-content:space-between;align-items:center;}input[type="range"].svelte-wn2kra {width:100%;height:4px;background:#ddd;border-radius:2px;outline:none;-webkit-appearance:none;}input[type="range"].svelte-wn2kra::-webkit-slider-thumb {-webkit-appearance:none;width:14px;height:14px;background:#007bff;border-radius:50%;cursor:pointer;}` +}; +function Nd(e, t) { + de(t, !0), Je(e, vy); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), s = Oo(); + let a = Un(Tt([])); + un(async () => { + var c, f; + const u = await ((f = (c = s.provider) == null ? void 0 : c.llm) == null ? void 0 : f.call(c)); + h(a).push(...u || []); + }); + const { updateNodeData: l } = Dt(); + return dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (c) => { + var f = dy(); + L(c, f); + }, + children: (c, f) => { + var d = hy(), g = be(d), p = X(g); + Ge(p, { + level: 3, + children: (G, se) => { + Se(); + var Te = Ie("输入参数"); + L(G, Te); + }, + $$slots: { default: !0 } + }); + var x = z(p, 2); + Ke(x, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (G, se) => { + var Te = fy(); + L(G, Te); + }, + $$slots: { default: !0 } + }), Z(g); + var C = z(g, 2); + zt(C, {}); + var $ = z(C, 2); + Ge($, { + level: 3, + mt: "10px", + children: (G, se) => { + Se(); + var Te = Ie("模型设置"); + L(G, Te); + }, + $$slots: { default: !0 } + }); + var m = z($, 4), _ = X(m); + const v = /* @__PURE__ */ Me(() => n().llmId ? [n().llmId] : []); + sn(_, { + get items() { + return h(a); + }, + style: "width: 100%", + placeholder: "请选择模型", + onSelect: (G) => { + const se = G.value; + l(o, () => ({ llmId: se })); + }, + get value() { + return h(v); + } + }); + var b = z(_, 2); + Ui(b, {}), Z(m); + var N = z(m, 4), E = X(N), M = X(E), D = X(M); + Z(M); + var V = z(M, 2); + io(V), Z(E), Z(N); + var A = z(N, 2), O = X(A), R = X(O), S = X(R); + Z(R); + var T = z(R, 2); + io(T), Z(O), Z(A); + var k = z(A, 2), P = X(k), H = X(P), I = X(H); + Z(H); + var B = z(H, 2); + io(B), Z(P), Z(k); + var F = z(k, 4), K = X(F); + const ie = /* @__PURE__ */ Me(() => n().systemPrompt || ""); + $t(K, { + rows: 5, + placeholder: "请输入系统提示词", + style: "width: 100%", + get value() { + return h(ie); + }, + oninput: (G) => { + l(o, { systemPrompt: G.target.value }); + } + }), Z(F); + var ee = z(F, 4), W = X(ee); + const ue = /* @__PURE__ */ Me(() => n().userPrompt || ""); + $t(W, { + rows: 5, + placeholder: "请输入用户提示词", + style: "width: 100%", + get value() { + return h(ue); + }, + oninput: (G) => { + l(o, { userPrompt: G.target.value }); + } + }), Z(ee); + var me = z(ee, 2), Ce = X(me); + Ge(Ce, { + level: 3, + mt: "10px", + children: (G, se) => { + Se(); + var Te = Ie("输出参数"); + L(G, Te); + }, + $$slots: { default: !0 } + }); + var ge = z(Ce, 2); + Ke(ge, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o, "outputDefs"); + }, + children: (G, se) => { + var Te = gy(); + L(G, Te); + }, + $$slots: { default: !0 } + }), Z(me); + var ze = z(me, 2); + Rn(ze, {}), Ee(() => { + Rt(D, `Temperature: ${n().temperature ?? 0.5}`), Qi(V, n().temperature ?? 0.5), Rt(S, `Top P: ${n().topP ?? 0.9}`), Qi(T, n().topP ?? 0.9), Rt(I, `Top K: ${n().topK ?? 50}`), Qi(B, n().topK ?? 50); + }), Ye("mousedown", V, es(function(G) { + Ve.call(this, t, G); + })), Ye("input", V, (G) => l(o, { temperature: parseFloat(G.target.value) })), Ye("mousedown", T, es(function(G) { + Ve.call(this, t, G); + })), Ye("input", T, (G) => l(o, { topP: parseFloat(G.target.value) })), Ye("mousedown", B, es(function(G) { + Ve.call(this, t, G); + })), Ye("input", B, (G) => l(o, { topK: parseInt(G.target.value) })), L(c, d); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(u) { + n(u), y(); + } + }); +} +ae(Nd, { data: {} }, [], [], !0); +var py = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M23 12L15.9289 19.0711L14.5147 17.6569L20.1716 12L14.5147 6.34317L15.9289 4.92896L23 12ZM3.82843 12L9.48528 17.6569L8.07107 19.0711L1 12L8.07107 4.92896L9.48528 6.34317L3.82843 12Z"></path></svg>'), my = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), yy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), wy = /* @__PURE__ */ ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">执行引擎</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">执行代码</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!> <!></div> <!>', 1); +const _y = { + hash: "svelte-15t2v24", + code: ".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}" +}; +function Md(e, t) { + de(t, !0), Je(e, _y); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), { updateNodeData: s } = Dt(), a = [ + { label: "QLExpress", value: "qlexpress" }, + { label: "Groovy", value: "groovy" }, + { label: "JavaScript", value: "js" } + ]; + return dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (u) => { + var c = py(); + L(u, c); + }, + children: (u, c) => { + var f = wy(), d = be(f), g = X(d); + Ge(g, { + level: 3, + children: (A, O) => { + Se(); + var R = Ie("输入参数"); + L(A, R); + }, + $$slots: { default: !0 } + }); + var p = z(g, 2); + Ke(p, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (A, O) => { + var R = my(); + L(A, R); + }, + $$slots: { default: !0 } + }), Z(d); + var x = z(d, 2); + zt(x, {}); + var C = z(x, 2); + Ge(C, { + level: 3, + mt: "10px", + children: (A, O) => { + Se(); + var R = Ie("代码"); + L(A, R); + }, + $$slots: { default: !0 } + }); + var $ = z(C, 4), m = X($); + const _ = /* @__PURE__ */ Me(() => n().engine ? [n().engine] : ["qlexpress"]); + sn(m, { + items: a, + style: "width: 100%", + placeholder: "请选择执行引擎", + onSelect: (A) => { + const O = A.value; + s(o, () => ({ engine: O })); + }, + get value() { + return h(_); + } + }), Z($); + var v = z($, 4), b = X(v); + const N = /* @__PURE__ */ Me(() => n().code || ""); + $t(b, { + rows: 10, + placeholder: "请输入执行代码,注:输出内容需添加到_result中,如:_result.put(key, value)", + style: "width: 100%", + onchange: (A) => { + s(o, () => ({ code: A.target.value })); + }, + get value() { + return h(N); + } + }), Z(v); + var E = z(v, 2), M = X(E); + Ge(M, { + level: 3, + mt: "10px", + children: (A, O) => { + Se(); + var R = Ie("输出参数"); + L(A, R); + }, + $$slots: { default: !0 } + }); + var D = z(M, 2); + Ke(D, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o, "outputDefs"); + }, + children: (A, O) => { + var R = yy(); + L(A, R); + }, + $$slots: { default: !0 } + }), Z(E); + var V = z(E, 2); + Rn(V, {}), L(u, f); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(l) { + n(l), y(); + } + }); +} +ae(Md, { data: {} }, [], [], !0); +var xy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 4C2 3.44772 2.44772 3 3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4ZM4 5V19H20V5H4ZM7 8H17V11H15V10H13V14H14.5V16H9.5V14H11V10H9V11H7V8Z"></path></svg>'), by = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Cy = /* @__PURE__ */ ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">执行代码</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!></div> <!>', 1); +const ky = { + hash: "svelte-15t2v24", + code: ".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}" +}; +function Td(e, t) { + de(t, !0), Je(e, ky); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), { updateNodeData: s } = Dt(); + return Nr(() => { + (!n().outputDefs || n().outputDefs.length === 0) && i(o, "outputDefs", { + name: "output", + dataType: "String", + dataTypeDisabled: !0, + deleteDisabled: !0 + }); + }), dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (l) => { + var u = xy(); + L(l, u); + }, + children: (l, u) => { + var c = Cy(), f = be(c), d = X(f); + Ge(d, { + level: 3, + children: (N, E) => { + Se(); + var M = Ie("输入参数"); + L(N, M); + }, + $$slots: { default: !0 } + }); + var g = z(d, 2); + Ke(g, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (N, E) => { + var M = by(); + L(N, M); + }, + $$slots: { default: !0 } + }), Z(f); + var p = z(f, 2); + zt(p, {}); + var x = z(p, 2); + Ge(x, { + level: 3, + mt: "10px", + children: (N, E) => { + Se(); + var M = Ie("代码"); + L(N, M); + }, + $$slots: { default: !0 } + }); + var C = z(x, 4), $ = X(C); + const m = /* @__PURE__ */ Me(() => n().template || ""); + $t($, { + rows: 10, + placeholder: "请输入执行代码", + style: "width: 100%", + onchange: (N) => { + s(o, () => ({ template: N.target.value })); + }, + get value() { + return h(m); + } + }), Z(C); + var _ = z(C, 2), v = X(_); + Ge(v, { + level: 3, + mt: "10px", + children: (N, E) => { + Se(); + var M = Ie("输出参数"); + L(N, M); + }, + $$slots: { default: !0 } + }), Z(_); + var b = z(_, 2); + Rn(b, {}), L(l, c); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(a) { + n(a), y(); + } + }); +} +ae(Td, { data: {} }, [], [], !0); +var $y = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.23509 6.45329C4.85101 7.89148 4 9.84636 4 12C4 16.4183 7.58172 20 12 20C13.0808 20 14.1116 19.7857 15.0521 19.3972C15.1671 18.6467 14.9148 17.9266 14.8116 17.6746C14.582 17.115 13.8241 16.1582 12.5589 14.8308C12.2212 14.4758 12.2429 14.2035 12.3636 13.3943L12.3775 13.3029C12.4595 12.7486 12.5971 12.4209 14.4622 12.1248C15.4097 11.9746 15.6589 12.3533 16.0043 12.8777C16.0425 12.9358 16.0807 12.9928 16.1198 13.0499C16.4479 13.5297 16.691 13.6394 17.0582 13.8064C17.2227 13.881 17.428 13.9751 17.7031 14.1314C18.3551 14.504 18.3551 14.9247 18.3551 15.8472V15.9518C18.3551 16.3434 18.3168 16.6872 18.2566 16.9859C19.3478 15.6185 20 13.8854 20 12C20 8.70089 18.003 5.8682 15.1519 4.64482C14.5987 5.01813 13.8398 5.54726 13.575 5.91C13.4396 6.09538 13.2482 7.04166 12.6257 7.11976C12.4626 7.14023 12.2438 7.12589 12.012 7.11097C11.3905 7.07058 10.5402 7.01606 10.268 7.75495C10.0952 8.2232 10.0648 9.49445 10.6239 10.1543C10.7134 10.2597 10.7307 10.4547 10.6699 10.6735C10.59 10.9608 10.4286 11.1356 10.3783 11.1717C10.2819 11.1163 10.0896 10.8931 9.95938 10.7412C9.64554 10.3765 9.25405 9.92233 8.74797 9.78176C8.56395 9.73083 8.36166 9.68867 8.16548 9.64736C7.6164 9.53227 6.99443 9.40134 6.84992 9.09302C6.74442 8.8672 6.74488 8.55621 6.74529 8.22764C6.74529 7.8112 6.74529 7.34029 6.54129 6.88256C6.46246 6.70541 6.35689 6.56446 6.23509 6.45329ZM12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22Z"></path></svg>'), Ey = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Sy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Py = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Ny = /* @__PURE__ */ ne('<div class="heading svelte-1vtcqdz" style="padding-top: 10px"><!> <!></div> <!>', 1), My = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Ty = /* @__PURE__ */ ne('<div class="heading svelte-1vtcqdz" style="padding-top: 10px"><!> <!></div> <!>', 1), Hy = /* @__PURE__ */ ne('<div style="width: 100%"><!></div>'), Vy = /* @__PURE__ */ ne('<div style="width: 100%"><!></div>'), Dy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Ay = /* @__PURE__ */ ne('<div style="display: flex;gap: 2px;width: 100%;padding: 10px 0"><div><!></div> <div style="width: 100%"><!></div></div> <div class="heading svelte-1vtcqdz"><!> <!></div> <!> <div class="heading svelte-1vtcqdz" style="padding-top: 10px"><!> <!></div> <!> <!> <div class="radio-group svelte-1vtcqdz"><label class="svelte-1vtcqdz"><!>none</label> <label class="svelte-1vtcqdz"><!>form-data</label> <label class="svelte-1vtcqdz"><!>x-www-form-urlencoded</label> <label class="svelte-1vtcqdz"><!>json</label> <label class="svelte-1vtcqdz"><!>raw</label></div> <!> <!> <!> <!> <div class="heading svelte-1vtcqdz"><!> <!></div> <!>', 1); +const Ly = { + hash: "svelte-1vtcqdz", + code: ".heading.svelte-1vtcqdz {display:flex;margin-bottom:10px;}.radio-group.svelte-1vtcqdz {display:flex;margin:10px 0;}.radio-group.svelte-1vtcqdz label:where(.svelte-1vtcqdz) {display:flex;font-size:14px;}" +}; +function Hd(e, t) { + de(t, !0), Je(e, Ly); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = [ + { value: "get", label: "GET" }, + { value: "post", label: "POST" }, + { value: "put", label: "PUT" }, + { value: "delete", label: "DELETE" }, + { value: "head", label: "HEAD" }, + { value: "patch", label: "PATCH" } + ], i = ht(), { addParameter: s } = kn(), { updateNodeData: a } = Dt(); + return dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (u) => { + var c = $y(); + L(u, c); + }, + children: (u, c) => { + var f = Ay(), d = be(f), g = X(d), p = X(g); + const x = /* @__PURE__ */ Me(() => n().method ? [n().method] : ["get"]); + sn(p, { + items: o, + style: "width: 100%", + placeholder: "请选择请求方式", + onSelect: (oe) => { + const ve = oe.value; + a(i, () => ({ method: ve })); + }, + get value() { + return h(x); + } + }), Z(g); + var C = z(g, 2), $ = X(C); + const m = /* @__PURE__ */ Me(() => n().url || ""); + xt($, { + placeholder: "请输入url", + style: "width: 100%", + onchange: (oe) => { + a(i, () => ({ url: oe.target.value })); + }, + get value() { + return h(m); + } + }), Z(C), Z(d); + var _ = z(d, 2), v = X(_); + Ge(v, { + level: 3, + children: (oe, ve) => { + Se(); + var xe = Ie("Http 头信息"); + L(oe, xe); + }, + $$slots: { default: !0 } + }); + var b = z(v, 2); + Ke(b, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + s(i, "headers"); + }, + children: (oe, ve) => { + var xe = Ey(); + L(oe, xe); + }, + $$slots: { default: !0 } + }), Z(_); + var N = z(_, 2); + zt(N, { dataKeyName: "headers" }); + var E = z(N, 2), M = X(E); + Ge(M, { + level: 3, + children: (oe, ve) => { + Se(); + var xe = Ie("参数"); + L(oe, xe); + }, + $$slots: { default: !0 } + }); + var D = z(M, 2); + Ke(D, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + s(i, "urlParameters"); + }, + children: (oe, ve) => { + var xe = Sy(); + L(oe, xe); + }, + $$slots: { default: !0 } + }), Z(E); + var V = z(E, 2); + zt(V, { dataKeyName: "urlParameters" }); + var A = z(V, 2); + Ge(A, { + level: 3, + mt: "10px", + children: (oe, ve) => { + Se(); + var xe = Ie("Body"); + L(oe, xe); + }, + $$slots: { default: !0 } + }); + var O = z(A, 2), R = X(O), S = X(R); + const T = /* @__PURE__ */ Me(() => !n().bodyType); + xt(S, { + type: "radio", + name: "bodyType", + value: "", + get checked() { + return h(T); + }, + onchange: (oe) => { + var ve; + (ve = oe.target) != null && ve.checked && a(i, { bodyType: "" }); + } + }), Se(), Z(R); + var k = z(R, 2), P = X(k); + const H = /* @__PURE__ */ Me(() => n().bodyType === "form-data"); + xt(P, { + type: "radio", + name: "bodyType", + value: "form-data", + get checked() { + return h(H); + }, + onchange: (oe) => { + var ve; + (ve = oe.target) != null && ve.checked && a(i, { bodyType: "form-data" }); + } + }), Se(), Z(k); + var I = z(k, 2), B = X(I); + const F = /* @__PURE__ */ Me(() => n().bodyType === "x-www-form-urlencoded"); + xt(B, { + type: "radio", + name: "bodyType", + value: "x-www-form-urlencoded", + get checked() { + return h(F); + }, + onchange: (oe) => { + var ve; + (ve = oe.target) != null && ve.checked && a(i, { bodyType: "x-www-form-urlencoded" }); + } + }), Se(), Z(I); + var K = z(I, 2), ie = X(K); + const ee = /* @__PURE__ */ Me(() => n().bodyType === "json"); + xt(ie, { + type: "radio", + name: "bodyType", + value: "json", + get checked() { + return h(ee); + }, + onchange: (oe) => { + var ve; + (ve = oe.target) != null && ve.checked && a(i, { bodyType: "json" }); + } + }), Se(), Z(K); + var W = z(K, 2), ue = X(W); + const me = /* @__PURE__ */ Me(() => n().bodyType === "raw"); + xt(ue, { + type: "radio", + name: "bodyType", + value: "raw", + get checked() { + return h(me); + }, + onchange: (oe) => { + var ve; + (ve = oe.target) != null && ve.checked && a(i, { bodyType: "raw" }); + } + }), Se(), Z(W), Z(O); + var Ce = z(O, 2); + { + var ge = (oe) => { + var ve = Ny(), xe = be(ve), Oe = X(xe); + Ge(Oe, { + level: 3, + children: (J, Re) => { + Se(); + var le = Ie("参数"); + L(J, le); + }, + $$slots: { default: !0 } + }); + var ct = z(Oe, 2); + Ke(ct, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + s(i, "fromData"); + }, + children: (J, Re) => { + var le = Py(); + L(J, le); + }, + $$slots: { default: !0 } + }), Z(xe); + var lt = z(xe, 2); + zt(lt, { dataKeyName: "fromData" }), L(oe, ve); + }; + ke(Ce, (oe) => { + n().bodyType === "form-data" && oe(ge); + }); + } + var ze = z(Ce, 2); + { + var G = (oe) => { + var ve = Ty(), xe = be(ve), Oe = X(xe); + Ge(Oe, { + level: 3, + children: (J, Re) => { + Se(); + var le = Ie("参数"); + L(J, le); + }, + $$slots: { default: !0 } + }); + var ct = z(Oe, 2); + Ke(ct, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + s(i, "fromUrlencoded"); + }, + children: (J, Re) => { + var le = My(); + L(J, le); + }, + $$slots: { default: !0 } + }), Z(xe); + var lt = z(xe, 2); + zt(lt, { dataKeyName: "fromUrlencoded" }), L(oe, ve); + }; + ke(ze, (oe) => { + n().bodyType === "x-www-form-urlencoded" && oe(G); + }); + } + var se = z(ze, 2); + { + var Te = (oe) => { + var ve = Hy(), xe = X(ve); + $t(xe, { + rows: "5", + style: "width: 100%", + placeholder: "请输入 json 信息", + get value() { + return n().bodyJson; + }, + oninput: (Oe) => { + a(i, { bodyJson: Oe.target.value }); + } + }), Z(ve), L(oe, ve); + }; + ke(se, (oe) => { + n().bodyType === "json" && oe(Te); + }); + } + var Ae = z(se, 2); + { + var Xe = (oe) => { + var ve = Vy(), xe = X(ve); + $t(xe, { + rows: "5", + style: "width: 100%", + placeholder: "请输入请求信息", + get value() { + return n().bodyRaw; + }, + oninput: (Oe) => { + a(i, { bodyRaw: Oe.target.value }); + } + }), Z(ve), L(oe, ve); + }; + ke(Ae, (oe) => { + n().bodyType === "raw" && oe(Xe); + }); + } + var te = z(Ae, 2), Fe = X(te); + Ge(Fe, { + level: 3, + mt: "10px", + children: (oe, ve) => { + Se(); + var xe = Ie("输出参数"); + L(oe, xe); + }, + $$slots: { default: !0 } + }); + var Le = z(Fe, 2); + Ke(Le, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + s(i, "outputDefs"); + }, + children: (oe, ve) => { + var xe = Dy(); + L(oe, xe); + }, + $$slots: { default: !0 } + }), Z(te); + var Qe = z(te, 2); + Rn(Qe, {}), L(u, f); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(l) { + n(l), y(); + } + }); +} +ae(Hd, { data: {} }, [], [], !0); +var Oy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 5C13.567 5 12 6.567 12 8.5C12 10.433 13.567 12 15.5 12C17.433 12 19 10.433 19 8.5C19 6.567 17.433 5 15.5 5ZM10 8.5C10 5.46243 12.4624 3 15.5 3C18.5376 3 21 5.46243 21 8.5C21 9.6575 20.6424 10.7315 20.0317 11.6175L22.7071 14.2929L21.2929 15.7071L18.6175 13.0317C17.7315 13.6424 16.6575 14 15.5 14C12.4624 14 10 11.5376 10 8.5ZM3 4H8V6H3V4ZM3 11H8V13H3V11ZM21 18V20H3V18H21Z"></path></svg>'), Iy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), zy = /* @__PURE__ */ ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">知识库</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">获取数据量</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!></div> <!>', 1); +const Ry = { + hash: "svelte-15t2v24", + code: ".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}" +}; +function Vd(e, t) { + de(t, !0), Je(e, Ry); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), s = Oo(); + let a = Un(Tt([])); + un(async () => { + var c, f; + const u = await ((f = (c = s.provider) == null ? void 0 : c.knowledge) == null ? void 0 : f.call(c)); + h(a).push(...u || []); + }); + const { updateNodeData: l } = Dt(); + return Nr(() => { + (!n().outputDefs || n().outputDefs.length === 0) && i(o, "outputDefs", { + name: "documents", + dataType: "Array", + nameDisabled: !0, + dataTypeDisabled: !0, + addChildDisabled: !0, + children: [ + { + name: "title", + dataType: "String", + nameDisabled: !0, + dataTypeDisabled: !0 + }, + { + name: "content", + dataType: "String", + nameDisabled: !0, + dataTypeDisabled: !0 + }, + { + name: "documentId", + dataType: "Number", + nameDisabled: !0, + dataTypeDisabled: !0 + }, + { + name: "knowledgeId", + dataType: "Number", + nameDisabled: !0, + dataTypeDisabled: !0 + } + ] + }); + }), dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (c) => { + var f = Oy(); + L(c, f); + }, + children: (c, f) => { + var d = zy(), g = be(d), p = X(g); + Ge(p, { + level: 3, + children: (V, A) => { + Se(); + var O = Ie("输入参数"); + L(V, O); + }, + $$slots: { default: !0 } + }); + var x = z(p, 2); + Ke(x, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (V, A) => { + var O = Iy(); + L(V, O); + }, + $$slots: { default: !0 } + }), Z(g); + var C = z(g, 2); + zt(C, {}); + var $ = z(C, 2); + Ge($, { + level: 3, + mt: "10px", + children: (V, A) => { + Se(); + var O = Ie("知识库设置"); + L(V, O); + }, + $$slots: { default: !0 } + }); + var m = z($, 4), _ = X(m); + const v = /* @__PURE__ */ Me(() => n().knowledgeId ? [n().knowledgeId] : []); + sn(_, { + get items() { + return h(a); + }, + style: "width: 100%", + placeholder: "请选择知识库", + onSelect: (V) => { + const A = V.value; + l(o, () => ({ knowledgeId: A })); + }, + get value() { + return h(v); + } + }), Z(m); + var b = z(m, 4), N = X(b); + xt(N, { placeholder: "搜索的数据条数", style: "width: 100%" }), Z(b); + var E = z(b, 2), M = X(E); + Ge(M, { + level: 3, + mt: "10px", + children: (V, A) => { + Se(); + var O = Ie("输出参数"); + L(V, O); + }, + $$slots: { default: !0 } + }), Z(E); + var D = z(E, 2); + Rn(D, {}), L(c, d); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(u) { + n(u), y(); + } + }); +} +ae(Vd, { data: {} }, [], [], !0); +var By = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"></path></svg>'), Yy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Zy = /* @__PURE__ */ ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">API 服务商</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">API Key</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">关键字</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">数据量</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">其他参数</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!></div> <!>', 1); +const Xy = { + hash: "svelte-15t2v24", + code: ".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}" +}; +function Dd(e, t) { + de(t, !0), Je(e, Xy); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), s = Oo(); + let a = Un(Tt([])); + un(async () => { + var c; + const u = await ((c = s.provider) == null ? void 0 : c.knowledge()); + h(a).push(...u || []); + }); + const { updateNodeData: l } = Dt(); + return Nr(() => { + (!n().outputDefs || n().outputDefs.length === 0) && i(o, "outputDefs", { + name: "documents", + dataType: "Array", + nameDisabled: !0, + dataTypeDisabled: !0, + addChildDisabled: !0, + children: [ + { + name: "title", + dataType: "String", + nameDisabled: !0, + dataTypeDisabled: !0 + }, + { + name: "content", + dataType: "String", + nameDisabled: !0, + dataTypeDisabled: !0 + }, + { + name: "documentId", + dataType: "Number", + nameDisabled: !0, + dataTypeDisabled: !0 + }, + { + name: "knowledgeId", + dataType: "Number", + nameDisabled: !0, + dataTypeDisabled: !0 + } + ] + }); + }), dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (c) => { + var f = By(); + L(c, f); + }, + children: (c, f) => { + var d = Zy(), g = be(d), p = X(g); + Ge(p, { + level: 3, + children: (k, P) => { + Se(); + var H = Ie("输入参数"); + L(k, H); + }, + $$slots: { default: !0 } + }); + var x = z(p, 2); + Ke(x, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (k, P) => { + var H = Yy(); + L(k, H); + }, + $$slots: { default: !0 } + }), Z(g); + var C = z(g, 2); + zt(C, {}); + var $ = z(C, 2); + Ge($, { + level: 3, + mt: "10px", + children: (k, P) => { + Se(); + var H = Ie("搜索引擎设置"); + L(k, H); + }, + $$slots: { default: !0 } + }); + var m = z($, 4), _ = X(m); + const v = /* @__PURE__ */ Me(() => n().knowledgeId ? [n().knowledgeId] : []); + sn(_, { + get items() { + return h(a); + }, + style: "width: 100%", + placeholder: "请选择 API 服务商", + onSelect: (k) => { + const P = k.value; + l(o, () => ({ knowledgeId: P })); + }, + get value() { + return h(v); + } + }), Z(m); + var b = z(m, 4), N = X(b); + xt(N, { + placeholder: "请输入 API Key", + style: "width: 100%" + }), Z(b); + var E = z(b, 4), M = X(E); + xt(M, { placeholder: "请输入关键字", style: "width: 100%" }), Z(E); + var D = z(E, 4), V = X(D); + xt(V, { placeholder: "搜索的数据条数", style: "width: 100%" }), Z(D); + var A = z(D, 4), O = X(A); + $t(O, { + rows: 3, + placeholder: "请输入其他参数(Property 格式)", + style: "width: 100%" + }), Z(A); + var R = z(A, 2), S = X(R); + Ge(S, { + level: 3, + mt: "10px", + children: (k, P) => { + Se(); + var H = Ie("输出参数"); + L(k, H); + }, + $$slots: { default: !0 } + }), Z(R); + var T = z(R, 2); + Rn(T, {}), L(c, d); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(u) { + n(u), y(); + } + }); +} +ae(Dd, { data: {} }, [], [], !0); +var Fy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5.46257 4.43262C7.21556 2.91688 9.5007 2 12 2C17.5228 2 22 6.47715 22 12C22 14.1361 21.3302 16.1158 20.1892 17.7406L17 12H20C20 7.58172 16.4183 4 12 4C9.84982 4 7.89777 4.84827 6.46023 6.22842L5.46257 4.43262ZM18.5374 19.5674C16.7844 21.0831 14.4993 22 12 22C6.47715 22 2 17.5228 2 12C2 9.86386 2.66979 7.88416 3.8108 6.25944L7 12H4C4 16.4183 7.58172 20 12 20C14.1502 20 16.1022 19.1517 17.5398 17.7716L18.5374 19.5674Z"></path></svg>'), Wy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Ky = /* @__PURE__ */ ne('<div class="heading svelte-md8tgj"><!> <!></div> <!> <div class="heading svelte-md8tgj"><!></div> <!>', 1); +const qy = { + hash: "svelte-md8tgj", + code: ".heading.svelte-md8tgj {display:flex;margin-bottom:10px;}.loop_handle_wrapper ::after {content:'循环体';width:100px;height:20px;background:#000;color:#fff;display:flex;justify-content:center;align-items:center;}" +}; +function Ad(e, t) { + de(t, !0), Je(e, qy); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), s = Oo(); + let a = Un(Tt([])); + return un(async () => { + var u; + const l = await ((u = s.provider) == null ? void 0 : u.knowledge()); + h(a).push(...l || []); + }), dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (c) => { + var f = Fy(); + L(c, f); + }, + handle: (c) => { + Qn(c, { + type: "source", + get position() { + return $e.Bottom; + }, + id: "loop_handle", + style: "bottom: -12px;width: 100px", + class: "loop_handle_wrapper" + }); + }, + children: (c, f) => { + var d = Ky(), g = be(d), p = X(g); + Ge(p, { + level: 3, + children: (v, b) => { + Se(); + var N = Ie("循环变量"); + L(v, N); + }, + $$slots: { default: !0 } + }); + var x = z(p, 2); + Ke(x, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (v, b) => { + var N = Wy(); + L(v, N); + }, + $$slots: { default: !0 } + }), Z(g); + var C = z(g, 2); + zt(C, {}); + var $ = z(C, 2), m = X($); + Ge(m, { + level: 3, + mt: "10px", + children: (v, b) => { + Se(); + var N = Ie("输出参数"); + L(v, N); + }, + $$slots: { default: !0 } + }), Z($); + var _ = z($, 2); + Rn(_, {}), L(c, d); + }, + $$slots: { icon: !0, handle: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(l) { + n(l), y(); + } + }); +} +ae(Ad, { data: {} }, [], [], !0); +var Gy = /* @__PURE__ */ _e('<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor" p-id="2577" width="200" height="200"><path d="M312.096 408.576l67.84 67.84 45.312-45.216a32 32 0 0 1 45.248 45.248l-45.28 45.248 90.496 90.496 45.28-45.216a32 32 0 0 1 45.248 45.248l-45.248 45.248 67.904 67.872-90.528 90.528a224.064 224.064 0 0 1-292.544 21.024L176.32 906.368a32 32 0 0 1-45.248-45.248l69.504-69.472a224.064 224.064 0 0 1 21.024-292.576l90.496-90.496z m0 90.496L266.848 544.32a160 160 0 0 0-4.8 221.28l4.8 4.992a160 160 0 0 0 221.248 4.8l5.024-4.8 45.248-45.248-226.272-226.24z m610.272-384a32 32 0 0 1 0 45.248l-69.44 69.504a224.064 224.064 0 0 1-21.056 292.544l-90.528 90.528-316.8-316.8 90.56-90.496a224.064 224.064 0 0 1 292.544-21.024l69.44-69.504a32 32 0 0 1 45.28 0zM565.344 246.08l-5.024 4.8-45.248 45.248 226.272 226.272 45.248-45.248a160 160 0 0 0 4.8-221.28l-4.8-4.992a160 160 0 0 0-221.248-4.8z" p-id="2578"></path></svg>'), Uy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), jy = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'), Jy = /* @__PURE__ */ ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">选择内部接口</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!> <!></div> <!>', 1); +const Qy = { + hash: "svelte-15t2v24", + code: ".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}" +}; +function Ld(e, t) { + de(t, !0), Je(e, Qy); + const n = w(t, "data", 7), r = /* @__PURE__ */ yt(t, [ + "$$slots", + "$$events", + "$$legacy", + "$$host", + "data" + ]), o = ht(), { addParameter: i } = kn(), { updateNodeData: s } = Dt(), a = Oo(); + let l = Un(Tt([])); + return un(async () => { + var c, f; + const u = await ((f = (c = a.provider) == null ? void 0 : c.internal) == null ? void 0 : f.call(c)); + h(l).push(...u || []); + }), dn(e, ut( + { + get data() { + return n(); + } + }, + () => r, + { + icon: (c) => { + var f = Gy(); + L(c, f); + }, + children: (c, f) => { + var d = Jy(), g = be(d), p = X(g); + Ge(p, { + level: 3, + children: (D, V) => { + Se(); + var A = Ie("输入参数"); + L(D, A); + }, + $$slots: { default: !0 } + }); + var x = z(p, 2); + Ke(x, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o); + }, + children: (D, V) => { + var A = Uy(); + L(D, A); + }, + $$slots: { default: !0 } + }), Z(g); + var C = z(g, 2); + zt(C, {}); + var $ = z(C, 2); + Ge($, { + level: 3, + mt: "10px", + children: (D, V) => { + Se(); + var A = Ie("接口"); + L(D, A); + }, + $$slots: { default: !0 } + }); + var m = z($, 4), _ = X(m); + const v = /* @__PURE__ */ Me(() => n().method ? [n().method] : [""]); + sn(_, { + get items() { + return h(l); + }, + style: "width: 100%", + placeholder: "请选择内部接口", + onSelect: (D) => { + const V = D.value; + s(o, () => ({ method: V })); + }, + get value() { + return h(v); + } + }), Z(m); + var b = z(m, 2), N = X(b); + Ge(N, { + level: 3, + mt: "10px", + children: (D, V) => { + Se(); + var A = Ie("输出参数"); + L(D, A); + }, + $$slots: { default: !0 } + }); + var E = z(N, 2); + Ke(E, { + class: "input-btn-more", + style: "margin-left: auto", + onclick: () => { + i(o, "outputDefs"); + }, + children: (D, V) => { + var A = jy(); + L(D, A); + }, + $$slots: { default: !0 } + }), Z(b); + var M = z(b, 2); + Rn(M, {}), L(c, d); + }, + $$slots: { icon: !0, default: !0 } + } + )), fe({ + get data() { + return n(); + }, + set data(u) { + n(u), y(); + } + }); +} +ae(Ld, { data: {} }, [], [], !0); +const ew = { + startNode: kd, + codeNode: Md, + llmNode: Nd, + templateNode: Td, + httpNode: Hd, + knowledgeNode: Vd, + searchEngineNode: Dd, + loopNode: Ad, + internalNode: Ld, + endNode: Sd +}; +var tw = /* @__PURE__ */ ne("<!> ", 1); +function Od(e, t) { + de(t, !0); + const n = w(t, "icon", 7), r = w(t, "title", 7), o = w(t, "type", 7), i = w(t, "description", 7), s = w(t, "extra", 7); + return Ke(e, { + draggable: !0, + ondragstart: (l) => { + if (!l.dataTransfer) + return null; + const u = { + type: o(), + data: { + title: r(), + description: i(), + systemPrompt: "", + userPrompt: "", + ...s() + } + }; + l.dataTransfer.setData("application/tinyflow", JSON.stringify(u)), l.dataTransfer.effectAllowed = "move"; + }, + children: (l, u) => { + var c = tw(), f = be(c); + mu(f, n); + var d = z(f); + Ee(() => Rt(d, ` ${r() ?? ""}`)), L(l, c); + }, + $$slots: { default: !0 } + }), fe({ + get icon() { + return n(); + }, + set icon(l) { + n(l), y(); + }, + get title() { + return r(); + }, + set title(l) { + r(l), y(); + }, + get type() { + return o(); + }, + set type(l) { + o(l), y(); + }, + get description() { + return i(); + }, + set description(l) { + i(l), y(); + }, + get extra() { + return s(); + }, + set extra(l) { + s(l), y(); + } + }); +} +ae( + Od, + { + icon: {}, + title: {}, + type: {}, + description: {}, + extra: {} + }, + [], + [], + !0 +); +var nw = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.83582 12L11.0429 18.2071L12.4571 16.7929L7.66424 12L12.4571 7.20712L11.0429 5.79291L4.83582 12ZM10.4857 12L16.6928 18.2071L18.107 16.7929L13.3141 12L18.107 7.20712L16.6928 5.79291L10.4857 12Z"></path></svg>'), rw = /* @__PURE__ */ _e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19.1642 12L12.9571 5.79291L11.5429 7.20712L16.3358 12L11.5429 16.7929L12.9571 18.2071L19.1642 12ZM13.5143 12L7.30722 5.79291L5.89301 7.20712L10.6859 12L5.89301 16.7929L7.30722 18.2071L13.5143 12Z"></path></svg>'), ow = /* @__PURE__ */ ne('<div><div class="tf-toolbar-container "><div class="tf-toolbar-container-header"><!></div> <div class="tf-toolbar-container-body"><div class="tf-toolbar-container-base"></div> <div class="tf-toolbar-container-tools"><!></div></div></div> <!></div>'); +function Id(e) { + let t = Un("base"), n = Un("show"); + const r = [ + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12C15 13.6569 13.6569 15 12 15Z"></path></svg>', + title: "开始节点", + type: "startNode", + description: "开始定义输入参数" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5.46257 4.43262C7.21556 2.91688 9.5007 2 12 2C17.5228 2 22 6.47715 22 12C22 14.1361 21.3302 16.1158 20.1892 17.7406L17 12H20C20 7.58172 16.4183 4 12 4C9.84982 4 7.89777 4.84827 6.46023 6.22842L5.46257 4.43262ZM18.5374 19.5674C16.7844 21.0831 14.4993 22 12 22C6.47715 22 2 17.5228 2 12C2 9.86386 2.66979 7.88416 3.8108 6.25944L7 12H4C4 16.4183 7.58172 20 12 20C14.1502 20 16.1022 19.1517 17.5398 17.7716L18.5374 19.5674Z"></path></svg>', + title: "循环", + type: "loopNode", + description: "用于循环执行任务" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M20.7134 7.12811L20.4668 7.69379C20.2864 8.10792 19.7136 8.10792 19.5331 7.69379L19.2866 7.12811C18.8471 6.11947 18.0555 5.31641 17.0677 4.87708L16.308 4.53922C15.8973 4.35653 15.8973 3.75881 16.308 3.57612L17.0252 3.25714C18.0384 2.80651 18.8442 1.97373 19.2761 0.930828L19.5293 0.319534C19.7058 -0.106511 20.2942 -0.106511 20.4706 0.319534L20.7238 0.930828C21.1558 1.97373 21.9616 2.80651 22.9748 3.25714L23.6919 3.57612C24.1027 3.75881 24.1027 4.35653 23.6919 4.53922L22.9323 4.87708C21.9445 5.31641 21.1529 6.11947 20.7134 7.12811ZM9 2C13.0675 2 16.426 5.03562 16.9337 8.96494L19.1842 12.5037C19.3324 12.7367 19.3025 13.0847 18.9593 13.2317L17 14.071V17C17 18.1046 16.1046 19 15 19H13.001L13 22H4L4.00025 18.3061C4.00033 17.1252 3.56351 16.0087 2.7555 15.0011C1.65707 13.6313 1 11.8924 1 10C1 5.58172 4.58172 2 9 2ZM9 4C5.68629 4 3 6.68629 3 10C3 11.3849 3.46818 12.6929 4.31578 13.7499C5.40965 15.114 6.00036 16.6672 6.00025 18.3063L6.00013 20H11.0007L11.0017 17H15V12.7519L16.5497 12.0881L15.0072 9.66262L14.9501 9.22118C14.5665 6.25141 12.0243 4 9 4ZM19.4893 16.9929L21.1535 18.1024C22.32 16.3562 23 14.2576 23 12.0001C23 11.317 22.9378 10.6486 22.8186 10L20.8756 10.5C20.9574 10.9878 21 11.489 21 12.0001C21 13.8471 20.4436 15.5642 19.4893 16.9929Z"></path></svg>', + title: "大模型", + type: "llmNode", + description: "使用大模型处理问题" + }, + { + // icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.7134 7.12811L4.46682 7.69379C4.28637 8.10792 3.71357 8.10792 3.53312 7.69379L3.28656 7.12811C2.84706 6.11947 2.05545 5.31641 1.06767 4.87708L0.308047 4.53922C-0.102682 4.35653 -0.102682 3.75881 0.308047 3.57612L1.0252 3.25714C2.03838 2.80651 2.84417 1.97373 3.27612 0.930828L3.52932 0.319534C3.70578 -0.106511 4.29417 -0.106511 4.47063 0.319534L4.72382 0.930828C5.15577 1.97373 5.96158 2.80651 6.9748 3.25714L7.69188 3.57612C8.10271 3.75881 8.10271 4.35653 7.69188 4.53922L6.93228 4.87708C5.94451 5.31641 5.15288 6.11947 4.7134 7.12811ZM6.33421 15.8154C6.51032 15.233 6.7072 14.6562 6.93912 14.0327C8.99484 8.50636 12.4197 5.08172 18.0129 4.21479C17.5 5.35838 17.0151 6.15301 16.5858 6.58237C16.2521 6.91603 15.9185 7.24993 15.5848 7.58407L14.1721 8.99878L15.6279 10.4535C14.4976 12.5384 12.2652 14.1979 9.75193 14.512C8.43544 14.6766 7.29345 15.1188 6.33421 15.8154ZM18 9.99658L17 8.99728C17.3331 8.66372 17.6662 8.33039 18.0027 7.99391C19.0018 6.99303 20.0009 4.99392 21 1.99658C6.31105 1.99658 4.08854 15.422 3.06361 21.6132C3.0419 21.7443 3.02074 21.8722 3 21.9966H4.99824C5.66421 18.6635 7.33146 16.8301 10 16.4966C14 15.9966 17 12.9966 18 9.99658Z"></path></svg>', + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 5C13.567 5 12 6.567 12 8.5C12 10.433 13.567 12 15.5 12C17.433 12 19 10.433 19 8.5C19 6.567 17.433 5 15.5 5ZM10 8.5C10 5.46243 12.4624 3 15.5 3C18.5376 3 21 5.46243 21 8.5C21 9.6575 20.6424 10.7315 20.0317 11.6175L22.7071 14.2929L21.2929 15.7071L18.6175 13.0317C17.7315 13.6424 16.6575 14 15.5 14C12.4624 14 10 11.5376 10 8.5ZM3 4H8V6H3V4ZM3 11H8V13H3V11ZM21 18V20H3V18H21Z"></path></svg>', + title: "知识库", + type: "knowledgeNode", + description: "通过知识库获取内容" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"></path></svg>', + title: "搜索引擎", + type: "searchEngineNode", + description: "通过搜索引擎搜索内容" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.23509 6.45329C4.85101 7.89148 4 9.84636 4 12C4 16.4183 7.58172 20 12 20C13.0808 20 14.1116 19.7857 15.0521 19.3972C15.1671 18.6467 14.9148 17.9266 14.8116 17.6746C14.582 17.115 13.8241 16.1582 12.5589 14.8308C12.2212 14.4758 12.2429 14.2035 12.3636 13.3943L12.3775 13.3029C12.4595 12.7486 12.5971 12.4209 14.4622 12.1248C15.4097 11.9746 15.6589 12.3533 16.0043 12.8777C16.0425 12.9358 16.0807 12.9928 16.1198 13.0499C16.4479 13.5297 16.691 13.6394 17.0582 13.8064C17.2227 13.881 17.428 13.9751 17.7031 14.1314C18.3551 14.504 18.3551 14.9247 18.3551 15.8472V15.9518C18.3551 16.3434 18.3168 16.6872 18.2566 16.9859C19.3478 15.6185 20 13.8854 20 12C20 8.70089 18.003 5.8682 15.1519 4.64482C14.5987 5.01813 13.8398 5.54726 13.575 5.91C13.4396 6.09538 13.2482 7.04166 12.6257 7.11976C12.4626 7.14023 12.2438 7.12589 12.012 7.11097C11.3905 7.07058 10.5402 7.01606 10.268 7.75495C10.0952 8.2232 10.0648 9.49445 10.6239 10.1543C10.7134 10.2597 10.7307 10.4547 10.6699 10.6735C10.59 10.9608 10.4286 11.1356 10.3783 11.1717C10.2819 11.1163 10.0896 10.8931 9.95938 10.7412C9.64554 10.3765 9.25405 9.92233 8.74797 9.78176C8.56395 9.73083 8.36166 9.68867 8.16548 9.64736C7.6164 9.53227 6.99443 9.40134 6.84992 9.09302C6.74442 8.8672 6.74488 8.55621 6.74529 8.22764C6.74529 7.8112 6.74529 7.34029 6.54129 6.88256C6.46246 6.70541 6.35689 6.56446 6.23509 6.45329ZM12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22Z"></path></svg>', + title: "Http 请求", + type: "httpNode", + description: "通过 HTTP 请求获取数据" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M23 12L15.9289 19.0711L14.5147 17.6569L20.1716 12L14.5147 6.34317L15.9289 4.92896L23 12ZM3.82843 12L9.48528 17.6569L8.07107 19.0711L1 12L8.07107 4.92896L9.48528 6.34317L3.82843 12Z"></path></svg>', + title: "动态代码", + type: "codeNode", + description: "动态执行代码" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 4C2 3.44772 2.44772 3 3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4ZM4 5V19H20V5H4ZM7 8H17V11H15V10H13V14H14.5V16H9.5V14H11V10H9V11H7V8Z"></path></svg>', + title: "内容模板", + type: "templateNode", + description: "通过模板引擎生成内容" + }, + { + icon: '<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor" p-id="2577" width="200" height="200"><path d="M312.096 408.576l67.84 67.84 45.312-45.216a32 32 0 0 1 45.248 45.248l-45.28 45.248 90.496 90.496 45.28-45.216a32 32 0 0 1 45.248 45.248l-45.248 45.248 67.904 67.872-90.528 90.528a224.064 224.064 0 0 1-292.544 21.024L176.32 906.368a32 32 0 0 1-45.248-45.248l69.504-69.472a224.064 224.064 0 0 1 21.024-292.576l90.496-90.496z m0 90.496L266.848 544.32a160 160 0 0 0-4.8 221.28l4.8 4.992a160 160 0 0 0 221.248 4.8l5.024-4.8 45.248-45.248-226.272-226.24z m610.272-384a32 32 0 0 1 0 45.248l-69.44 69.504a224.064 224.064 0 0 1-21.056 292.544l-90.528 90.528-316.8-316.8 90.56-90.496a224.064 224.064 0 0 1 292.544-21.024l69.44-69.504a32 32 0 0 1 45.28 0zM565.344 246.08l-5.024 4.8-45.248 45.248 226.272 226.272 45.248-45.248a160 160 0 0 0 4.8-221.28l-4.8-4.992a160 160 0 0 0-221.248-4.8z" p-id="2578"></path></svg>', + title: "内部接口", + type: "internalNode", + description: "执行内部提供接口" + }, + { + icon: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6 5.1438V16.0002H18.3391L6 5.1438ZM4 2.932C4 2.07155 5.01456 1.61285 5.66056 2.18123L21.6501 16.2494C22.3423 16.8584 21.9116 18.0002 20.9896 18.0002H6V22H4V2.932Z"></path></svg>', + title: "结束节点", + type: "endNode", + description: "结束定义输出参数" + } + ], o = [ + { + label: "基础节点", + value: "base" + }, + { + label: "业务工具", + value: "tools" + } + ]; + var i = ow(), s = X(i), a = X(s), l = X(a); + sd(l, { + style: "width: 100%", + items: o, + onChange: (p) => { + U(t, Tt(p.value.toString())); + } + }), Z(a); + var u = z(a, 2), c = X(u); + Yt(c, 21, () => r, Li, (p, x) => { + Od(p, ut(() => h(x))); + }), Z(c); + var f = z(c, 2), d = X(f); + Ke(d, { + children: (p, x) => { + Se(); + var C = Ie("测试业务按钮"); + L(p, C); + }, + $$slots: { default: !0 } + }), Z(f), Z(u), Z(s); + var g = z(s, 2); + Ke(g, { + onclick: () => { + U(n, Tt(h(n) ? "" : "show")); + }, + children: (p, x) => { + var C = et(), $ = be(C); + { + var m = (v) => { + var b = nw(); + L(v, b); + }, _ = (v) => { + var b = rw(); + L(v, b); + }; + ke($, (v) => { + h(n) === "show" ? v(m) : v(_, !1); + }); + } + L(p, C); + }, + $$slots: { default: !0 } + }), Z(i), Ee(() => { + kt(i, 1, `tf-toolbar ${h(n) ?? ""}`), ce(c, "style", `display: ${(h(t) === "base" ? "flex" : "none") ?? ""}`), ce(f, "style", `display: ${(h(t) !== "base" ? "flex" : "none") ?? ""}`); + }), L(e, i); +} +ae(Id, {}, [], [], !0); +const iw = () => { + const { nodeLookup: e } = Ue(); + return { + getNode: (n) => { + var o; + return (o = q(e).get(n)) == null ? void 0 : o.internals.userNode; + } + }; +}, sw = () => { + const { nodes: e } = Ue(); + return { + ensureParentInNodesBefore: (n, r) => { + e.update((o) => { + let i = -1; + for (let l = 0; l < o.length; l++) + if (o[l].id === n) { + i = l; + break; + } + if (i <= 0) + return o; + let s = -1; + for (let l = 0; l < i; l++) + if (o[l].parentId === n || o[l].id === r) { + s = l; + break; + } + if (s == -1) + return o; + const a = o[i]; + for (let l = i; l > s; l--) + o[l] = o[l - 1]; + return o[s] = a, o; + }); + } + }; +}, aw = () => { + const { edges: e } = Ue(); + return { + getEdgesByTarget: (n) => q(e).filter((o) => o.target === n) + }; +}; +var lw = /* @__PURE__ */ ne('<div class="panel-content svelte-1oe15vw"><div>边属性设置</div> <div class="setting-title svelte-1oe15vw">边条件设置</div> <div class="setting-item"><!></div></div>'), uw = /* @__PURE__ */ ne("<!> <!> <!> <!>", 1), cw = /* @__PURE__ */ ne('<div style="position: relative; height: 100%; width: 100%"><!> <!></div>'); +const dw = { + hash: "svelte-1oe15vw", + code: ".panel-content.svelte-1oe15vw {padding:10px;background-color:#fff;border-radius:5px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);width:200px;border:1px solid #efefef;}.setting-title.svelte-1oe15vw {margin:10px 0;font-size:12px;color:#999;}" +}; +function zd(e, t) { + de(t, !0), Je(e, dw); + const n = w(t, "onInit", 7), r = Dt(); + n()(r); + let o = Un(!1); + const i = (_) => { + _.preventDefault(), _.dataTransfer && (_.dataTransfer.dropEffect = "move"); + }, s = (_) => { + var M; + _.preventDefault(); + const v = r.screenToFlowPosition({ + x: _.clientX - 250, + y: _.clientY - 100 + }), b = (M = _.dataTransfer) == null ? void 0 : M.getData("application/tinyflow"), N = b ? JSON.parse(b) : {}, E = { + id: `node_${Rr()}`, + position: v, + data: {}, + ...N + }; + ei.addNode(E), ei.selectNodeOnly(E.id); + }, { getNode: a } = iw(), l = (_) => { + const v = a(_.source), b = a(_.target); + if (_.sourceHandle === "loop_handle" || v.parentId) { + const N = r.getEdges(); + for (let E of N) + if (E.target === _.target) { + const M = a(E.source); + if (_.sourceHandle === "loop_handle" && M.parentId !== v.id || v.parentId && M.parentId !== v.parentId) + return !1; + } + } + return !(!v.parentId && b.parentId && b.parentId !== v.id); + }, { ensureParentInNodesBefore: u } = sw(), c = (_, v) => { + if (!v.isValid) + return; + const b = v.toNode; + if (b.parentId) + return; + const N = v.fromNode, E = v.fromHandle, M = { position: { ...b.position } }; + if (E.id === "loop_handle" ? M.parentId = N.id : N.parentId && (M.parentId = N.parentId), M.parentId) { + const D = a(M.parentId); + M.position = { + x: b.position.x - D.position.x, + y: b.position.y - D.position.y + }, u(M.parentId, b.id), r.updateNode(b.id, M); + } + }, { getEdgesByTarget: f } = aw(), d = (_) => { + _.edges.forEach((b) => { + const N = a(b.target); + if (N.parentId) { + const E = f(b.target), M = a(N.parentId); + if (E.length === 0) + r.updateNode(N.id, { + parentId: void 0, + position: { + x: N.position.x + M.position.x, + y: N.position.y + M.position.y + } + }); + else { + let D = !1; + for (let V = 0; V < E.length; V++) { + const A = E[V], O = a(A.source); + if (O.parentId || O.type === "loopNode") { + D = !0; + break; + } + } + D || r.updateNode(N.id, { + parentId: void 0, + position: { + x: N.position.x + M.position.x, + y: N.position.y + M.position.y + } + }); + } + } + }); + }, g = (_, v) => { + console.log("onconnectstart: ", _, v); + }, p = (_) => { + console.log("onconnect: ", _); + }; + var x = cw(), C = X(x); + Id(C); + var $ = z(C, 2); + const m = /* @__PURE__ */ Me(() => ({ + // animated: true, + // label: 'edge label', + markerEnd: { + type: mo.ArrowClosed, + // color: 'red', + width: 20, + height: 20 + } + })); + return Fc($, ut({ nodeTypes: ew }, ei, { + class: "tinyflow-logo", + isValidConnection: l, + onconnectend: c, + onconnectstart: g, + onconnect: p, + connectionRadius: 50, + ondelete: d, + onclick: (_) => { + const v = _.target; + v.classList.contains("svelte-flow__edge-interaction") || v.classList.contains("panel-content") || v.closest(".panel-content") || U(o, !1); + }, + get defaultEdgeOptions() { + return h(m); + }, + $$events: { + drop: s, + dragover: i, + edgeclick: () => { + U(o, !0); + } + }, + children: (_, v) => { + var b = uw(), N = be(b); + td(N, {}); + var E = z(N, 2); + Jc(E, {}); + var M = z(E, 2); + rd(M, {}); + var D = z(M, 2); + { + var V = (A) => { + Ho(A, { + children: (O, R) => { + var S = lw(), T = z(X(S), 4), k = X(T); + $t(k, { + rows: 3, + placeholder: "请输入边条件", + style: "width: 100%", + oninput: (P) => { + } + }), Z(T), Z(S), L(O, S); + }, + $$slots: { default: !0 } + }); + }; + ke(D, (A) => { + h(o) && A(V); + }); + } + L(_, b); + }, + $$slots: { default: !0 } + })), Z(x), L(e, x), fe({ + get onInit() { + return n(); + }, + set onInit(_) { + n(_), y(); + } + }); +} +ae(zd, { onInit: {} }, [], [], !0); +function fw(e, t) { + de(t, !0); + const n = w(t, "options", 7), r = w(t, "onInit", 7), { data: o } = n(); + return ei.init((o == null ? void 0 : o.nodes) || [], (o == null ? void 0 : o.edges) || []), Tr("tinyflow_options", n()), Wc(e, { + fitView: !0, + children: (i, s) => { + zd(i, { + get onInit() { + return r(); + } + }); + }, + $$slots: { default: !0 } + }), fe({ + get options() { + return n(); + }, + set options(i) { + n(i), y(); + }, + get onInit() { + return r(); + }, + set onInit(i) { + r(i), y(); + } + }); +} +customElements.define("tinyflow-component", ae(fw, { options: {}, onInit: {} }, [], [], !1)); +export { + yw as Tinyflow +}; +//# sourceMappingURL=index.js.map diff --git a/src/components/Tinyflow/ui/index.umd.js b/src/components/Tinyflow/ui/index.umd.js new file mode 100644 index 0000000..baf0ad5 --- /dev/null +++ b/src/components/Tinyflow/ui/index.umd.js @@ -0,0 +1,9 @@ +(function(We,Je){typeof exports=="object"&&typeof module<"u"?Je(exports):typeof define=="function"&&define.amd?define(["exports"],Je):(We=typeof globalThis<"u"?globalThis:We||self,Je(We.Tinyflow={}))})(this,function(We){"use strict";var fw=Object.defineProperty;var Rd=We=>{throw TypeError(We)};var gw=(We,Je,ot)=>Je in We?fw(We,Je,{enumerable:!0,configurable:!0,writable:!0,value:ot}):We[Je]=ot;var Nt=(We,Je,ot)=>gw(We,typeof Je!="symbol"?Je+"":Je,ot),ba=(We,Je,ot)=>Je.has(We)||Rd("Cannot "+ot);var ut=(We,Je,ot)=>(ba(We,Je,"read from private field"),ot?ot.call(We):Je.get(We)),wr=(We,Je,ot)=>Je.has(We)?Rd("Cannot add the same private member more than once"):Je instanceof WeakSet?Je.add(We):Je.set(We,ot),zo=(We,Je,ot,Ro)=>(ba(We,Je,"write to private field"),Ro?Ro.call(We,ot):Je.set(We,ot),ot),Bd=(We,Je,ot)=>(ba(We,Je,"access private method"),ot);var Md,or,qr,Lo,Gi,Yd,Zn,jt;const Je="5";typeof window<"u"&&((Md=window.__svelte??(window.__svelte={})).v??(Md.v=new Set)).add(Je);let ot=!1,Ro=!1;function Zd(){ot=!0}Zd();const es=1,ts=2,Ca=4,Xd=8,Fd=16,Wd=1,Kd=2,ka=4,qd=8,Gd=16,$a=1,Ud=2,ns="[",rs="[!",os="]",_r={},Tt=Symbol(),Ea="http://www.w3.org/2000/svg",Sa=!1,Qt=2,Pa=4,Bo=8,is=16,Pn=32,xr=64,Yo=128,Kt=256,Zo=512,yt=1024,Nn=2048,ir=4096,Tn=8192,Xo=16384,jd=32768,br=65536,Jd=1<<17,Qd=1<<19,Na=1<<20,Xn=Symbol("$state"),ss=Symbol("legacy props"),ef=Symbol("");var Gr=Array.isArray,tf=Array.prototype.indexOf,as=Array.from,Fo=Object.keys,Ur=Object.defineProperty,Mn=Object.getOwnPropertyDescriptor,Ta=Object.getOwnPropertyDescriptors,nf=Object.prototype,rf=Array.prototype,ls=Object.getPrototypeOf;function jr(e){return typeof e=="function"}const gt=()=>{};function of(e){return e()}function Jr(e){for(var t=0;t<e.length;t++)e[t]()}const sf=typeof requestIdleCallback>"u"?e=>setTimeout(e,1):requestIdleCallback;let Qr=[],eo=[];function Ma(){var e=Qr;Qr=[],Jr(e)}function Ha(){var e=eo;eo=[],Jr(e)}function to(e){Qr.length===0&&queueMicrotask(Ma),Qr.push(e)}function af(e){eo.length===0&&sf(Ha),eo.push(e)}function Va(){Qr.length>0&&Ma(),eo.length>0&&Ha()}function Da(e){return e===this.v}function us(e,t){return e!=e?t==t:e!==t||e!==null&&typeof e=="object"||typeof e=="function"}function cs(e){return!us(e,this.v)}function lf(e){throw new Error("https://svelte.dev/e/effect_in_teardown")}function uf(){throw new Error("https://svelte.dev/e/effect_in_unowned_derived")}function cf(e){throw new Error("https://svelte.dev/e/effect_orphan")}function df(){throw new Error("https://svelte.dev/e/effect_update_depth_exceeded")}function ff(){throw new Error("https://svelte.dev/e/hydration_failed")}function gf(e){throw new Error("https://svelte.dev/e/props_invalid_value")}function hf(){throw new Error("https://svelte.dev/e/state_descriptors_fixed")}function vf(){throw new Error("https://svelte.dev/e/state_prototype_fixed")}function pf(){throw new Error("https://svelte.dev/e/state_unsafe_local_read")}function mf(){throw new Error("https://svelte.dev/e/state_unsafe_mutation")}function Mt(e,t){var n={f:0,v:e,reactions:null,equals:Da,rv:0,wv:0};return n}function Fn(e){return Aa(Mt(e))}function no(e,t=!1){var r;const n=Mt(e);return t||(n.equals=cs),ot&&Ye!==null&&Ye.l!==null&&((r=Ye.l).s??(r.s=[])).push(n),n}function re(e,t=!1){return Aa(no(e,t))}function Aa(e){return Qe!==null&&!en&&Qe.f&Qt&&(mn===null?_f([e]):mn.push(e)),e}function U(e,t){return Qe!==null&&!en&&ni()&&Qe.f&(Qt|is)&&(mn===null||!mn.includes(e))&&mf(),ds(e,t)}function ds(e,t){return e.equals(t)||(e.v,e.v=t,e.wv=Wa(),Oa(e,Nn),ni()&&Ke!==null&&Ke.f&yt&&!(Ke.f&(Pn|xr))&&(Vn===null?xf([e]):Vn.push(e))),t}function La(e,t=1){var n=h(e),r=t===1?n++:n--;return U(e,n),r}function Oa(e,t){var n=e.reactions;if(n!==null)for(var r=ni(),o=n.length,i=0;i<o;i++){var s=n[i],a=s.f;a&Nn||!r&&s===Ke||(tn(s,t),a&(yt|Kt)&&(a&Qt?Oa(s,ir):ei(s)))}}function Ne(e){var t=Qt|Nn,n=Qe!==null&&Qe.f&Qt?Qe:null;return Ke===null||n!==null&&n.f&Kt?t|=Kt:Ke.f|=Na,{ctx:Ye,deps:null,effects:null,equals:Da,f:t,fn:e,reactions:null,rv:0,v:null,wv:0,parent:n??Ke}}function ve(e){const t=Ne(e);return t.equals=cs,t}function Ia(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;n<t.length;n+=1)qt(t[n])}}function yf(e){for(var t=e.parent;t!==null;){if(!(t.f&Qt))return t;t=t.parent}return null}function wf(e){var t,n=Ke;Kn(yf(e));try{Ia(e),t=qa(e)}finally{Kn(n)}return t}function za(e){var t=wf(e),n=(qn||e.f&Kt)&&e.deps!==null?ir:yt;tn(e,n),e.equals(t)||(e.v=t,e.wv=Wa())}function Wo(e){console.warn("https://svelte.dev/e/hydration_mismatch")}let Se=!1;function It(e){Se=e}let Ve;function bt(e){if(e===null)throw Wo(),_r;return Ve=e}function vn(){return bt(pn(Ve))}function Z(e){if(Se){if(pn(Ve)!==null)throw Wo(),_r;Ve=e}}function Pe(e=1){if(Se){for(var t=e,n=Ve;t--;)n=pn(n);Ve=n}}function fs(){for(var e=0,t=Ve;;){if(t.nodeType===8){var n=t.data;if(n===os){if(e===0)return t;e-=1}else(n===ns||n===rs)&&(e+=1)}var r=pn(t);t.remove(),t=r}}function Ht(e,t=null,n){if(typeof e!="object"||e===null||Xn in e)return e;const r=ls(e);if(r!==nf&&r!==rf)return e;var o=new Map,i=Gr(e),s=Mt(0);i&&o.set("length",Mt(e.length));var a;return new Proxy(e,{defineProperty(l,u,c){(!("value"in c)||c.configurable===!1||c.enumerable===!1||c.writable===!1)&&hf();var f=o.get(u);return f===void 0?(f=Mt(c.value),o.set(u,f)):U(f,Ht(c.value,a)),!0},deleteProperty(l,u){var c=o.get(u);if(c===void 0)u in l&&o.set(u,Mt(Tt));else{if(i&&typeof u=="string"){var f=o.get("length"),d=Number(u);Number.isInteger(d)&&d<f.v&&U(f,d)}U(c,Tt),Ra(s)}return!0},get(l,u,c){var p;if(u===Xn)return e;var f=o.get(u),d=u in l;if(f===void 0&&(!d||(p=Mn(l,u))!=null&&p.writable)&&(f=Mt(Ht(d?l[u]:Tt,a)),o.set(u,f)),f!==void 0){var g=h(f);return g===Tt?void 0:g}return Reflect.get(l,u,c)},getOwnPropertyDescriptor(l,u){var c=Reflect.getOwnPropertyDescriptor(l,u);if(c&&"value"in c){var f=o.get(u);f&&(c.value=h(f))}else if(c===void 0){var d=o.get(u),g=d==null?void 0:d.v;if(d!==void 0&&g!==Tt)return{enumerable:!0,configurable:!0,value:g,writable:!0}}return c},has(l,u){var g;if(u===Xn)return!0;var c=o.get(u),f=c!==void 0&&c.v!==Tt||Reflect.has(l,u);if(c!==void 0||Ke!==null&&(!f||(g=Mn(l,u))!=null&&g.writable)){c===void 0&&(c=Mt(f?Ht(l[u],a):Tt),o.set(u,c));var d=h(c);if(d===Tt)return!1}return f},set(l,u,c,f){var _;var d=o.get(u),g=u in l;if(i&&u==="length")for(var p=c;p<d.v;p+=1){var x=o.get(p+"");x!==void 0?U(x,Tt):p in l&&(x=Mt(Tt),o.set(p+"",x))}d===void 0?(!g||(_=Mn(l,u))!=null&&_.writable)&&(d=Mt(void 0),U(d,Ht(c,a)),o.set(u,d)):(g=d.v!==Tt,U(d,Ht(c,a)));var C=Reflect.getOwnPropertyDescriptor(l,u);if(C!=null&&C.set&&C.set.call(f,c),!g){if(i&&typeof u=="string"){var $=o.get("length"),m=Number(u);Number.isInteger(m)&&m>=$.v&&U($,m+1)}Ra(s)}return!0},ownKeys(l){h(s);var u=Reflect.ownKeys(l).filter(d=>{var g=o.get(d);return g===void 0||g.v!==Tt});for(var[c,f]of o)f.v!==Tt&&!(c in l)&&u.push(c);return u},setPrototypeOf(){vf()}})}function Ra(e,t=1){U(e,e.v+t)}var Vt,Ba,Ya,Za;function gs(){if(Vt===void 0){Vt=window,Ba=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype;Ya=Mn(t,"firstChild").get,Za=Mn(t,"nextSibling").get,e.__click=void 0,e.__className=void 0,e.__attributes=null,e.__styles=null,e.__e=void 0,Text.prototype.__t=void 0}}function Hn(e=""){return document.createTextNode(e)}function Ct(e){return Ya.call(e)}function pn(e){return Za.call(e)}function X(e,t){if(!Se)return Ct(e);var n=Ct(Ve);if(n===null)n=Ve.appendChild(Hn());else if(t&&n.nodeType!==3){var r=Hn();return n==null||n.before(r),bt(r),r}return bt(n),n}function xe(e,t){if(!Se){var n=Ct(e);return n instanceof Comment&&n.data===""?pn(n):n}return Ve}function z(e,t=1,n=!1){let r=Se?Ve:e;for(var o;t--;)o=r,r=pn(r);if(!Se)return r;var i=r==null?void 0:r.nodeType;if(n&&i!==3){var s=Hn();return r===null?o==null||o.after(s):r.before(s),bt(s),s}return bt(r),r}function hs(e){e.textContent=""}let Ko=!1,qo=!1,Go=null,sr=!1,vs=!1;function Xa(e){vs=e}let ro=[],hw=[],Qe=null,en=!1;function Wn(e){Qe=e}let Ke=null;function Kn(e){Ke=e}let mn=null;function _f(e){mn=e}let kt=null,zt=0,Vn=null;function xf(e){Vn=e}let Fa=1,Uo=0,qn=!1;function Wa(){return++Fa}function Cr(e){var f;var t=e.f;if(t&Nn)return!0;if(t&ir){var n=e.deps,r=(t&Kt)!==0;if(n!==null){var o,i,s=(t&Zo)!==0,a=r&&Ke!==null&&!qn,l=n.length;if(s||a){var u=e,c=u.parent;for(o=0;o<l;o++)i=n[o],(s||!((f=i==null?void 0:i.reactions)!=null&&f.includes(u)))&&(i.reactions??(i.reactions=[])).push(u);s&&(u.f^=Zo),a&&c!==null&&!(c.f&Kt)&&(u.f^=Kt)}for(o=0;o<l;o++)if(i=n[o],Cr(i)&&za(i),i.wv>e.wv)return!0}(!r||Ke!==null&&!qn)&&tn(e,yt)}return!1}function bf(e,t){for(var n=t;n!==null;){if(n.f&Yo)try{n.fn(e);return}catch{n.f^=Yo}n=n.parent}throw Ko=!1,e}function Cf(e){return(e.f&Xo)===0&&(e.parent===null||(e.parent.f&Yo)===0)}function jo(e,t,n,r){if(Ko){if(n===null&&(Ko=!1),Cf(t))throw e;return}n!==null&&(Ko=!0);{bf(e,t);return}}function Ka(e,t,n=!0){var r=e.reactions;if(r!==null)for(var o=0;o<r.length;o++){var i=r[o];i.f&Qt?Ka(i,t,!1):t===i&&(n?tn(i,Nn):i.f&yt&&tn(i,ir),ei(i))}}function qa(e){var g;var t=kt,n=zt,r=Vn,o=Qe,i=qn,s=mn,a=Ye,l=en,u=e.f;kt=null,zt=0,Vn=null,qn=(u&Kt)!==0&&(en||!sr||Qe===null),Qe=u&(Pn|xr)?null:e,mn=null,rl(e.ctx),en=!1,Uo++;try{var c=(0,e.fn)(),f=e.deps;if(kt!==null){var d;if(Jo(e,zt),f!==null&&zt>0)for(f.length=zt+kt.length,d=0;d<kt.length;d++)f[zt+d]=kt[d];else e.deps=f=kt;if(!qn)for(d=zt;d<f.length;d++)((g=f[d]).reactions??(g.reactions=[])).push(e)}else f!==null&&zt<f.length&&(Jo(e,zt),f.length=zt);if(ni()&&Vn!==null&&!en&&f!==null&&!(e.f&(Qt|ir|Nn)))for(d=0;d<Vn.length;d++)Ka(Vn[d],e);return o!==null&&Uo++,c}finally{kt=t,zt=n,Vn=r,Qe=o,qn=i,mn=s,rl(a),en=l}}function kf(e,t){let n=t.reactions;if(n!==null){var r=tf.call(n,e);if(r!==-1){var o=n.length-1;o===0?n=t.reactions=null:(n[r]=n[o],n.pop())}}n===null&&t.f&Qt&&(kt===null||!kt.includes(t))&&(tn(t,ir),t.f&(Kt|Zo)||(t.f^=Zo),Ia(t),Jo(t,0))}function Jo(e,t){var n=e.deps;if(n!==null)for(var r=t;r<n.length;r++)kf(e,n[r])}function Qo(e){var t=e.f;if(!(t&Xo)){tn(e,yt);var n=Ke,r=Ye,o=sr;Ke=e,sr=!0;try{t&is?Vf(e):Qa(e),Ja(e);var i=qa(e);e.teardown=typeof i=="function"?i:null,e.wv=Fa;var s=e.deps,a;Sa&&Ro&&e.f&Nn}catch(l){jo(l,e,n,r||e.ctx)}finally{sr=o,Ke=n}}}function $f(){try{df()}catch(e){if(Go!==null)jo(e,Go,null);else throw e}}function Ga(){var e=sr;try{var t=0;for(sr=!0;ro.length>0;){t++>1e3&&$f();var n=ro,r=n.length;ro=[];for(var o=0;o<r;o++){var i=n[o];i.f&yt||(i.f^=yt);var s=Sf(i);Ef(s)}}}finally{qo=!1,sr=e,Go=null}}function Ef(e){var t=e.length;if(t!==0)for(var n=0;n<t;n++){var r=e[n];if(!(r.f&(Xo|Tn)))try{Cr(r)&&(Qo(r),r.deps===null&&r.first===null&&r.nodes_start===null&&(r.teardown===null?el(r):r.fn=null))}catch(o){jo(o,r,null,r.ctx)}}}function ei(e){qo||(qo=!0,queueMicrotask(Ga));for(var t=Go=e;t.parent!==null;){t=t.parent;var n=t.f;if(n&(xr|Pn)){if(!(n&yt))return;t.f^=yt}}ro.push(t)}function Sf(e){for(var t=[],n=e.first;n!==null;){var r=n.f,o=(r&Pn)!==0,i=o&&(r&yt)!==0;if(!i&&!(r&Tn)){if(r&Pa)t.push(n);else if(o)n.f^=yt;else{var s=Qe;try{Qe=n,Cr(n)&&Qo(n)}catch(u){jo(u,n,null,n.ctx)}finally{Qe=s}}var a=n.first;if(a!==null){n=a;continue}}var l=n.parent;for(n=n.next;n===null&&l!==null;)n=l.next,l=l.parent}return t}function y(e){var t;for(Va();ro.length>0;)qo=!0,Ga(),Va();return t}function h(e){var t=e.f,n=(t&Qt)!==0;if(Qe!==null&&!en){mn!==null&&mn.includes(e)&&pf();var r=Qe.deps;e.rv<Uo&&(e.rv=Uo,kt===null&&r!==null&&r[zt]===e?zt++:kt===null?kt=[e]:(!qn||!kt.includes(e))&&kt.push(e))}else if(n&&e.deps===null&&e.effects===null){var o=e,i=o.parent;i!==null&&!(i.f&Kt)&&(o.f^=Kt)}return n&&(o=e,Cr(o)&&za(o)),e.v}function yn(e){var t=en;try{return en=!0,e()}finally{en=t}}const Pf=-7169;function tn(e,t){e.f=e.f&Pf|t}function j(e){if(!(typeof e!="object"||!e||e instanceof EventTarget)){if(Xn in e)ps(e);else if(!Array.isArray(e))for(let t in e){const n=e[t];typeof n=="object"&&n&&Xn in n&&ps(n)}}}function ps(e,t=new Set){if(typeof e=="object"&&e!==null&&!(e instanceof EventTarget)&&!t.has(e)){t.add(e),e instanceof Date&&e.getTime();for(let r in e)try{ps(e[r],t)}catch{}const n=ls(e);if(n!==Object.prototype&&n!==Array.prototype&&n!==Map.prototype&&n!==Set.prototype&&n!==Date.prototype){const r=Ta(n);for(let o in r){const i=r[o].get;if(i)try{i.call(e)}catch{}}}}}function Ua(e){Ke===null&&Qe===null&&cf(),Qe!==null&&Qe.f&Kt&&Ke===null&&uf(),vs&&lf()}function Nf(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function ar(e,t,n,r=!0){var o=(e&xr)!==0,i=Ke,s={ctx:Ye,deps:null,nodes_start:null,nodes_end:null,f:e|Nn,first:null,fn:t,last:null,next:null,parent:o?null:i,prev:null,teardown:null,transitions:null,wv:0};if(n)try{Qo(s),s.f|=jd}catch(u){throw qt(s),u}else t!==null&&ei(s);var a=n&&s.deps===null&&s.first===null&&s.nodes_start===null&&s.teardown===null&&(s.f&(Na|Yo))===0;if(!a&&!o&&r&&(i!==null&&Nf(s,i),Qe!==null&&Qe.f&Qt)){var l=Qe;(l.effects??(l.effects=[])).push(s)}return s}function ja(e){const t=ar(Bo,null,!1);return tn(t,yt),t.teardown=e,t}function kr(e){Ua();var t=Ke!==null&&(Ke.f&Pn)!==0&&Ye!==null&&!Ye.m;if(t){var n=Ye;(n.e??(n.e=[])).push({fn:e,effect:Ke,reaction:Qe})}else{var r=Rt(e);return r}}function Tf(e){return Ua(),$r(e)}function Mf(e){const t=ar(xr,e,!0);return()=>{qt(t)}}function Hf(e){const t=ar(xr,e,!0);return(n={})=>new Promise(r=>{n.outro?Er(t,()=>{qt(t),r(void 0)}):(qt(t),r(void 0))})}function Rt(e){return ar(Pa,e,!1)}function ge(e,t){var n=Ye,r={effect:null,ran:!1};n.l.r1.push(r),r.effect=$r(()=>{e(),!r.ran&&(r.ran=!0,U(n.l.r2,!0),yn(t))})}function vt(){var e=Ye;$r(()=>{if(h(e.l.r2)){for(var t of e.l.r1){var n=t.effect;n.f&yt&&tn(n,ir),Cr(n)&&Qo(n),t.ran=!1}e.l.r2.v=!1}})}function $r(e){return ar(Bo,e,!0)}function Ee(e,t=[],n=Ne){const r=t.map(n);return lr(()=>e(...r.map(h)))}function lr(e,t=0){return ar(Bo|is|t,e,!0)}function Dn(e,t=!0){return ar(Bo|Pn,e,!0,t)}function Ja(e){var t=e.teardown;if(t!==null){const n=vs,r=Qe;Xa(!0),Wn(null);try{t.call(null)}finally{Xa(n),Wn(r)}}}function Qa(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){var r=n.next;qt(n,t),n=r}}function Vf(e){for(var t=e.first;t!==null;){var n=t.next;t.f&Pn||qt(t),t=n}}function qt(e,t=!0){var n=!1;if((t||e.f&Qd)&&e.nodes_start!==null){for(var r=e.nodes_start,o=e.nodes_end;r!==null;){var i=r===o?null:pn(r);r.remove(),r=i}n=!0}Qa(e,t&&!n),Jo(e,0),tn(e,Xo);var s=e.transitions;if(s!==null)for(const l of s)l.stop();Ja(e);var a=e.parent;a!==null&&a.first!==null&&el(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes_start=e.nodes_end=null}function el(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Er(e,t){var n=[];ms(e,n,!0),tl(n,()=>{qt(e),t&&t()})}function tl(e,t){var n=e.length;if(n>0){var r=()=>--n||t();for(var o of e)o.out(r)}else t()}function ms(e,t,n){if(!(e.f&Tn)){if(e.f^=Tn,e.transitions!==null)for(const s of e.transitions)(s.is_global||n)&&t.push(s);for(var r=e.first;r!==null;){var o=r.next,i=(r.f&br)!==0||(r.f&Pn)!==0;ms(r,t,i?n:!1),r=o}}}function oo(e){nl(e,!0)}function nl(e,t){if(e.f&Tn){e.f^=Tn,e.f&yt||(e.f^=yt),Cr(e)&&(tn(e,Nn),ei(e));for(var n=e.first;n!==null;){var r=n.next,o=(n.f&br)!==0||(n.f&Pn)!==0;nl(n,o?t:!1),n=r}if(e.transitions!==null)for(const i of e.transitions)(i.is_global||t)&&i.in()}}function ti(e){throw new Error("https://svelte.dev/e/lifecycle_outside_component")}let Ye=null;function rl(e){Ye=e}function ur(e){return ys().get(e)}function Sr(e,t){return ys().set(e,t),t}function Df(e){return ys().has(e)}function ue(e,t=!1,n){Ye={p:Ye,c:null,e:null,m:!1,s:e,x:null,l:null},ot&&!t&&(Ye.l={s:null,u:null,r1:[],r2:Mt(!1)})}function ce(e){const t=Ye;if(t!==null){e!==void 0&&(t.x=e);const s=t.e;if(s!==null){var n=Ke,r=Qe;t.e=null;try{for(var o=0;o<s.length;o++){var i=s[o];Kn(i.effect),Wn(i.reaction),Rt(i.fn)}}finally{Kn(n),Wn(r)}}Ye=t.p,t.m=!0}return e||{}}function ni(){return!ot||Ye!==null&&Ye.l===null}function ys(e){return Ye===null&&ti(),Ye.c??(Ye.c=new Map(Af(Ye)||void 0))}function Af(e){let t=e.p;for(;t!==null;){const n=t.c;if(n!==null)return n;t=t.p}return null}function Lf(e){return e.endsWith("capture")&&e!=="gotpointercapture"&&e!=="lostpointercapture"}const Of=["beforeinput","click","change","dblclick","contextmenu","focusin","focusout","input","keydown","keyup","mousedown","mousemove","mouseout","mouseover","mouseup","pointerdown","pointermove","pointerout","pointerover","pointerup","touchend","touchmove","touchstart"];function If(e){return Of.includes(e)}const zf={formnovalidate:"formNoValidate",ismap:"isMap",nomodule:"noModule",playsinline:"playsInline",readonly:"readOnly",defaultvalue:"defaultValue",defaultchecked:"defaultChecked",srcobject:"srcObject",novalidate:"noValidate",allowfullscreen:"allowFullscreen",disablepictureinpicture:"disablePictureInPicture",disableremoteplayback:"disableRemotePlayback"};function Rf(e){return e=e.toLowerCase(),zf[e]??e}const Bf=["touchstart","touchmove"];function Yf(e){return Bf.includes(e)}const Zf=["textarea","script","style","title"];function Xf(e){return Zf.includes(e)}function Ff(e,t){if(t){const n=document.body;e.autofocus=!0,to(()=>{document.activeElement===n&&e.focus()})}}function Wf(e){Se&&Ct(e)!==null&&hs(e)}let ol=!1;function Kf(){ol||(ol=!0,document.addEventListener("reset",e=>{Promise.resolve().then(()=>{var t;if(!e.defaultPrevented)for(const n of e.target.elements)(t=n.__on_r)==null||t.call(n)})},{capture:!0}))}function qf(e){var t=Qe,n=Ke;Wn(null),Kn(null);try{return e()}finally{Wn(t),Kn(n)}}const il=new Set,ws=new Set;function sl(e,t,n,r={}){function o(i){if(r.capture||io.call(t,i),!i.cancelBubble)return qf(()=>n==null?void 0:n.call(this,i))}return e.startsWith("pointer")||e.startsWith("touch")||e==="wheel"?to(()=>{t.addEventListener(e,o,r)}):t.addEventListener(e,o,r),o}function Ze(e,t,n,r,o){var i={capture:r,passive:o},s=sl(e,t,n,i);(t===document.body||t===window||t===document)&&ja(()=>{t.removeEventListener(e,s,i)})}function ri(e){for(var t=0;t<e.length;t++)il.add(e[t]);for(var n of ws)n(e)}function io(e){var m;var t=this,n=t.ownerDocument,r=e.type,o=((m=e.composedPath)==null?void 0:m.call(e))||[],i=o[0]||e.target,s=0,a=e.__root;if(a){var l=o.indexOf(a);if(l!==-1&&(t===document||t===window)){e.__root=t;return}var u=o.indexOf(t);if(u===-1)return;l<=u&&(s=l)}if(i=o[s]||e.target,i!==t){Ur(e,"currentTarget",{configurable:!0,get(){return i||n}});var c=Qe,f=Ke;Wn(null),Kn(null);try{for(var d,g=[];i!==null;){var p=i.assignedSlot||i.parentNode||i.host||null;try{var x=i["__"+r];if(x!==void 0&&(!i.disabled||e.target===i))if(Gr(x)){var[C,...$]=x;C.apply(i,[e,...$])}else x.call(i,e)}catch(_){d?g.push(_):d=_}if(e.cancelBubble||p===t||p===null)break;i=p}if(d){for(let _ of g)queueMicrotask(()=>{throw _});throw d}}finally{e.__root=t,delete e.currentTarget,Wn(c),Kn(f)}}}function _s(e){var t=document.createElement("template");return t.innerHTML=e,t.content}function Dt(e,t){var n=Ke;n.nodes_start===null&&(n.nodes_start=e,n.nodes_end=t)}function ne(e,t){var n=(t&$a)!==0,r=(t&Ud)!==0,o,i=!e.startsWith("<!>");return()=>{if(Se)return Dt(Ve,null),Ve;o===void 0&&(o=_s(i?e:"<!>"+e),n||(o=Ct(o)));var s=r||Ba?document.importNode(o,!0):o.cloneNode(!0);if(n){var a=Ct(s),l=s.lastChild;Dt(a,l)}else Dt(s,s);return s}}function _e(e,t,n="svg"){var r=!e.startsWith("<!>"),o=(t&$a)!==0,i=`<${n}>${r?e:"<!>"+e}</${n}>`,s;return()=>{if(Se)return Dt(Ve,null),Ve;if(!s){var a=_s(i),l=Ct(a);if(o)for(s=document.createDocumentFragment();Ct(l);)s.appendChild(Ct(l));else s=Ct(l)}var u=s.cloneNode(!0);if(o){var c=Ct(u),f=u.lastChild;Dt(c,f)}else Dt(u,u);return u}}function Ae(e=""){if(!Se){var t=Hn(e+"");return Dt(t,t),t}var n=Ve;return n.nodeType!==3&&(n.before(n=Hn()),bt(n)),Dt(n,n),n}function tt(){if(Se)return Dt(Ve,null),Ve;var e=document.createDocumentFragment(),t=document.createComment(""),n=Hn();return e.append(t,n),Dt(t,n),e}function L(e,t){if(Se){Ke.nodes_end=Ve,vn();return}e!==null&&e.before(t)}function Bt(e,t){var n=t==null?"":typeof t=="object"?t+"":t;n!==(e.__t??(e.__t=e.nodeValue))&&(e.__t=n,e.nodeValue=n+"")}function al(e,t){return ll(e,t)}function Gf(e,t){gs(),t.intro=t.intro??!1;const n=t.target,r=Se,o=Ve;try{for(var i=Ct(n);i&&(i.nodeType!==8||i.data!==ns);)i=pn(i);if(!i)throw _r;It(!0),bt(i),vn();const s=ll(e,{...t,anchor:i});if(Ve===null||Ve.nodeType!==8||Ve.data!==os)throw Wo(),_r;return It(!1),s}catch(s){if(s===_r)return t.recover===!1&&ff(),gs(),hs(n),It(!1),al(e,t);throw s}finally{It(r),bt(o)}}const Pr=new Map;function ll(e,{target:t,anchor:n,props:r={},events:o,context:i,intro:s=!0}){gs();var a=new Set,l=f=>{for(var d=0;d<f.length;d++){var g=f[d];if(!a.has(g)){a.add(g);var p=Yf(g);t.addEventListener(g,io,{passive:p});var x=Pr.get(g);x===void 0?(document.addEventListener(g,io,{passive:p}),Pr.set(g,1)):Pr.set(g,x+1)}}};l(as(il)),ws.add(l);var u=void 0,c=Hf(()=>{var f=n??t.appendChild(Hn());return Dn(()=>{if(i){ue({});var d=Ye;d.c=i}o&&(r.$$events=o),Se&&Dt(f,null),u=e(f,r)||{},Se&&(Ke.nodes_end=Ve),i&&ce()}),()=>{var p;for(var d of a){t.removeEventListener(d,io);var g=Pr.get(d);--g===0?(document.removeEventListener(d,io),Pr.delete(d)):Pr.set(d,g)}ws.delete(l),f!==n&&((p=f.parentNode)==null||p.removeChild(f))}});return xs.set(u,c),u}let xs=new WeakMap;function Uf(e,t){const n=xs.get(e);return n?(xs.delete(e),n(t)):Promise.resolve()}function ke(e,t,[n,r]=[0,0]){Se&&n===0&&vn();var o=e,i=null,s=null,a=Tt,l=n>0?br:0,u=!1;const c=(d,g=!0)=>{u=!0,f(g,d)},f=(d,g)=>{if(a===(a=d))return;let p=!1;if(Se&&r!==-1){if(n===0){const C=o.data;C===ns?r=0:C===rs?r=1/0:(r=parseInt(C.substring(1)),r!==r&&(r=a?1/0:-1))}const x=r>n;!!a===x&&(o=fs(),bt(o),It(!1),p=!0,r=-1)}a?(i?oo(i):g&&(i=Dn(()=>g(o))),s&&Er(s,()=>{s=null})):(s?oo(s):g&&(s=Dn(()=>g(o,[n+1,r]))),i&&Er(i,()=>{i=null})),p&&It(!0)};lr(()=>{u=!1,t(c),u||f(null,null)},l),Se&&(o=Ve)}function oi(e,t){return t}function jf(e,t,n,r){for(var o=[],i=t.length,s=0;s<i;s++)ms(t[s].e,o,!0);var a=i>0&&o.length===0&&n!==null;if(a){var l=n.parentNode;hs(l),l.append(n),r.clear(),Gn(e,t[0].prev,t[i-1].next)}tl(o,()=>{for(var u=0;u<i;u++){var c=t[u];a||(r.delete(c.k),Gn(e,c.prev,c.next)),qt(c.e,!a)}})}function Yt(e,t,n,r,o,i=null){var s=e,a={flags:t,items:new Map,first:null},l=(t&Ca)!==0;if(l){var u=e;s=Se?bt(Ct(u)):u.appendChild(Hn())}Se&&vn();var c=null,f=!1,d=ve(()=>{var g=n();return Gr(g)?g:g==null?[]:as(g)});lr(()=>{var g=h(d),p=g.length;if(f&&p===0)return;f=p===0;let x=!1;if(Se){var C=s.data===rs;C!==(p===0)&&(s=fs(),bt(s),It(!1),x=!0)}if(Se){for(var $=null,m,_=0;_<p;_++){if(Ve.nodeType===8&&Ve.data===os){s=Ve,x=!0,It(!1);break}var v=g[_],b=r(v,_);m=ul(Ve,a,$,null,v,b,_,o,t,n),a.items.set(b,m),$=m}p>0&&bt(fs())}Se||Jf(g,a,s,o,t,r,n),i!==null&&(p===0?c?oo(c):c=Dn(()=>i(s)):c!==null&&Er(c,()=>{c=null})),x&&It(!0),h(d)}),Se&&(s=Ve)}function Jf(e,t,n,r,o,i,s){var S,M,k,P;var a=(o&Xd)!==0,l=(o&(es|ts))!==0,u=e.length,c=t.items,f=t.first,d=f,g,p=null,x,C=[],$=[],m,_,v,b;if(a)for(b=0;b<u;b+=1)m=e[b],_=i(m,b),v=c.get(_),v!==void 0&&((S=v.a)==null||S.measure(),(x??(x=new Set)).add(v));for(b=0;b<u;b+=1){if(m=e[b],_=i(m,b),v=c.get(_),v===void 0){var N=d?d.e.nodes_start:n;p=ul(N,t,p,p===null?t.first:p.next,m,_,b,r,o,s),c.set(_,p),C=[],$=[],d=p.next;continue}if(l&&Qf(v,m,b,o),v.e.f&Tn&&(oo(v.e),a&&((M=v.a)==null||M.unfix(),(x??(x=new Set)).delete(v))),v!==d){if(g!==void 0&&g.has(v)){if(C.length<$.length){var E=$[0],T;p=E.prev;var D=C[0],V=C[C.length-1];for(T=0;T<C.length;T+=1)cl(C[T],E,n);for(T=0;T<$.length;T+=1)g.delete($[T]);Gn(t,D.prev,V.next),Gn(t,p,D),Gn(t,V,E),d=E,p=V,b-=1,C=[],$=[]}else g.delete(v),cl(v,d,n),Gn(t,v.prev,v.next),Gn(t,v,p===null?t.first:p.next),Gn(t,p,v),p=v;continue}for(C=[],$=[];d!==null&&d.k!==_;)d.e.f&Tn||(g??(g=new Set)).add(d),$.push(d),d=d.next;if(d===null)continue;v=d}C.push(v),p=v,d=v.next}if(d!==null||g!==void 0){for(var A=g===void 0?[]:as(g);d!==null;)d.e.f&Tn||A.push(d),d=d.next;var O=A.length;if(O>0){var R=o&Ca&&u===0?n:null;if(a){for(b=0;b<O;b+=1)(k=A[b].a)==null||k.measure();for(b=0;b<O;b+=1)(P=A[b].a)==null||P.fix()}jf(t,A,R,c)}}a&&to(()=>{var H;if(x!==void 0)for(v of x)(H=v.a)==null||H.apply()}),Ke.first=t.first&&t.first.e,Ke.last=p&&p.e}function Qf(e,t,n,r){r&es&&ds(e.v,t),r&ts?ds(e.i,n):e.i=n}function ul(e,t,n,r,o,i,s,a,l,u){var c=(l&es)!==0,f=(l&Fd)===0,d=c?f?no(o):Mt(o):o,g=l&ts?Mt(s):s,p={i:g,v:d,k:i,a:null,e:null,prev:n,next:r};try{return p.e=Dn(()=>a(e,d,g,u),Se),p.e.prev=n&&n.e,p.e.next=r&&r.e,n===null?t.first=p:(n.next=p,n.e.next=p.e),r!==null&&(r.prev=p,r.e.prev=p.e),p}finally{}}function cl(e,t,n){for(var r=e.next?e.next.e.nodes_start:n,o=t?t.e.nodes_start:n,i=e.e.nodes_start;i!==r;){var s=pn(i);o.before(i),i=s}}function Gn(e,t,n){t===null?e.first=n:(t.next=n,t.e.next=n&&n.e),n!==null&&(n.prev=t,n.e.prev=t&&t.e)}function dl(e,t,n,r,o){var i=e,s="",a;lr(()=>{if(s===(s=t()??"")){Se&&vn();return}a!==void 0&&(qt(a),a=void 0),s!==""&&(a=Dn(()=>{if(Se){Ve.data;for(var l=vn(),u=l;l!==null&&(l.nodeType!==8||l.data!=="");)u=l,l=pn(l);if(l===null)throw Wo(),_r;Dt(Ve,u),i=bt(l);return}var c=s+"",f=_s(c);Dt(Ct(f),f.lastChild),i.before(f)}))})}function wt(e,t,n,r,o){var a;Se&&vn();var i=(a=t.$$slots)==null?void 0:a[n],s=!1;i===!0&&(i=t[n==="default"?"children":n],s=!0),i===void 0||i(e,s?()=>r:r)}function e1(e){const t={};e.children&&(t.default=!0);for(const n in e.$$slots)t[n]=!0;return t}function cr(e,t,...n){var r=e,o=gt,i;lr(()=>{o!==(o=t())&&(i&&(qt(i),i=null),i=Dn(()=>o(r,...n)))},br),Se&&(r=Ve)}function fl(e,t,n){Se&&vn();var r=e,o,i;lr(()=>{o!==(o=t())&&(i&&(Er(i),i=null),o&&(i=Dn(()=>n(r,o))))},br),Se&&(r=Ve)}function t1(e,t,n,r,o,i){let s=Se;Se&&vn();var a,l,u=null;Se&&Ve.nodeType===1&&(u=Ve,vn());var c=Se?Ve:e,f;lr(()=>{const d=t()||null;var g=d==="svg"?Ea:null;d!==a&&(f&&(d===null?Er(f,()=>{f=null,l=null}):d===l?oo(f):qt(f)),d&&d!==l&&(f=Dn(()=>{if(u=Se?u:g?document.createElementNS(g,d):document.createElement(d),Dt(u,u),r){Se&&Xf(d)&&u.append(document.createComment(""));var p=Se?Ct(u):u.appendChild(Hn());Se&&(p===null?It(!1):bt(p)),r(u,p)}Ke.nodes_end=u,c.before(u)})),a=d,a&&(l=a))},br),s&&(It(!0),bt(c))}function et(e,t){to(()=>{var n=e.getRootNode(),r=n.host?n:n.head??n.ownerDocument.head;if(!r.querySelector("#"+t.hash)){const o=document.createElement("style");o.id=t.hash,o.textContent=t.code,r.appendChild(o)}})}function _t(e,t,n){Rt(()=>{var r=yn(()=>t(e,n==null?void 0:n())||{});if(n&&(r!=null&&r.update)){var o=!1,i={};$r(()=>{var s=n();j(s),o&&us(i,s)&&(i=s,r.update(s))}),o=!0}if(r!=null&&r.destroy)return()=>r.destroy()})}function gl(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var o=e.length;for(t=0;t<o;t++)e[t]&&(n=gl(e[t]))&&(r&&(r+=" "),r+=n)}else for(n in e)e[n]&&(r&&(r+=" "),r+=n);return r}function n1(){for(var e,t,n=0,r="",o=arguments.length;n<o;n++)(e=arguments[n])&&(t=gl(e))&&(r&&(r+=" "),r+=t);return r}function wn(e){return typeof e=="object"?n1(e):e??""}const hl=[...` +\r\f \v\uFEFF`];function r1(e,t,n){var r=e==null?"":""+e;if(t&&(r=r?r+" "+t:t),n){for(var o in n)if(n[o])r=r?r+" "+o:o;else if(r.length)for(var i=o.length,s=0;(s=r.indexOf(o,s))>=0;){var a=s+i;(s===0||hl.includes(r[s-1]))&&(a===r.length||hl.includes(r[a]))?r=(s===0?"":r.substring(0,s))+r.substring(a+1):s=a}}return r===""?null:r}function $t(e,t,n,r,o,i){var s=e.__className;if(Se||s!==n){var a=r1(n,r,i);(!Se||a!==e.getAttribute("class"))&&(a==null?e.removeAttribute("class"):t?e.className=a:e.setAttribute("class",a)),e.__className=n}else if(i)for(var l in i){var u=!!i[l];(o==null||u!==!!o[l])&&e.classList.toggle(l,u)}return i}const so=Symbol("class");function ao(e){if(Se){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute("value")){var r=e.value;de(e,"value",null),e.value=r}if(e.hasAttribute("checked")){var o=e.checked;de(e,"checked",null),e.checked=o}}};e.__on_r=n,af(n),Kf()}}function bs(e,t){var n=e.__attributes??(e.__attributes={});n.value===(n.value=t??void 0)||e.value===t&&(t!==0||e.nodeName!=="PROGRESS")||(e.value=t??"")}function o1(e,t){t?e.hasAttribute("selected")||e.setAttribute("selected",""):e.removeAttribute("selected")}function de(e,t,n,r){var o=e.__attributes??(e.__attributes={});Se&&(o[t]=e.getAttribute(t),t==="src"||t==="srcset"||t==="href"&&e.nodeName==="LINK")||o[t]!==(o[t]=n)&&(t==="style"&&"__styles"in e&&(e.__styles={}),t==="loading"&&(e[ef]=n),n==null?e.removeAttribute(t):typeof n!="string"&&pl(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function nn(e,t,n,r,o=!1,i=!1,s=!1){let a=Se&&i;a&&It(!1);var l=t||{},u=e.tagName==="OPTION";for(var c in t)c in n||(n[c]=null);n.class?n.class=wn(n.class):(r||n[so])&&(n.class=null);var f=pl(e),d=e.__attributes??(e.__attributes={});for(const _ in n){let v=n[_];if(u&&_==="value"&&v==null){e.value=e.__value="",l[_]=v;continue}if(_==="class"){var g=e.namespaceURI==="http://www.w3.org/1999/xhtml";$t(e,g,v,r,t==null?void 0:t[so],n[so]),l[_]=v,l[so]=n[so];continue}var p=l[_];if(v!==p){l[_]=v;var x=_[0]+_[1];if(x!=="$$"){if(x==="on"){const b={},N="$$"+_;let E=_.slice(2);var C=If(E);if(Lf(E)&&(E=E.slice(0,-7),b.capture=!0),!C&&p){if(v!=null)continue;e.removeEventListener(E,l[N],b),l[N]=null}if(v!=null)if(C)e[`__${E}`]=v,ri([E]);else{let T=function(D){l[_].call(this,D)};l[N]=sl(E,e,T,b)}else C&&(e[`__${E}`]=void 0)}else if(_==="style"&&v!=null)e.style.cssText=v+"";else if(_==="autofocus")Ff(e,!!v);else if(!i&&(_==="__value"||_==="value"&&v!=null))e.value=e.__value=v;else if(_==="selected"&&u)o1(e,v);else{var $=_;o||($=Rf($));var m=$==="defaultValue"||$==="defaultChecked";if(v==null&&!i&&!m)if(d[_]=null,$==="value"||$==="checked"){let b=e;const N=t===void 0;if($==="value"){let E=b.defaultValue;b.removeAttribute($),b.defaultValue=E,b.value=b.__value=N?E:null}else{let E=b.defaultChecked;b.removeAttribute($),b.defaultChecked=E,b.checked=N?E:!1}}else e.removeAttribute(_);else m||f.includes($)&&(i||typeof v!="string")?e[$]=v:typeof v!="function"&&de(e,$,v)}_==="style"&&"__styles"in e&&(e.__styles={})}}}return a&&It(!0),l}var vl=new Map;function pl(e){var t=vl.get(e.nodeName);if(t)return t;vl.set(e.nodeName,t=[]);for(var n,r=e,o=Element.prototype;o!==r;){n=Ta(r);for(var i in n)n[i].set&&t.push(i);r=ls(r)}return t}function at(e,t,n,r){var o=e.__styles??(e.__styles={});o[t]!==n&&(o[t]=n,n==null?e.style.removeProperty(t):e.style.setProperty(t,n,""))}const Ui=class Ui{constructor(t){wr(this,Gi);wr(this,or,new WeakMap);wr(this,qr);wr(this,Lo);zo(this,Lo,t)}observe(t,n){var r=ut(this,or).get(t)||new Set;return r.add(n),ut(this,or).set(t,r),Bd(this,Gi,Yd).call(this).observe(t,ut(this,Lo)),()=>{var o=ut(this,or).get(t);o.delete(n),o.size===0&&(ut(this,or).delete(t),ut(this,qr).unobserve(t))}}};or=new WeakMap,qr=new WeakMap,Lo=new WeakMap,Gi=new WeakSet,Yd=function(){return ut(this,qr)??zo(this,qr,new ResizeObserver(t=>{for(var n of t){Ui.entries.set(n.target,n);for(var r of ut(this,or).get(n.target)||[])r(n)}}))},Nt(Ui,"entries",new WeakMap);let Cs=Ui;var i1=new Cs({box:"border-box"});function ml(e,t,n){var r=i1.observe(e,()=>n(e[t]));Rt(()=>(yn(()=>n(e[t])),r))}function yl(e,t){return e===t||(e==null?void 0:e[Xn])===t}function An(e={},t,n,r){return Rt(()=>{var o,i;return $r(()=>{o=i,i=[],yn(()=>{e!==n(...i)&&(t(e,...i),o&&yl(n(...o),e)&&t(null,...o))})}),()=>{to(()=>{i&&yl(n(...i),e)&&t(null,...i)})}}),e}function ks(e){return function(...t){var n=t[0];return n.stopPropagation(),e==null?void 0:e.apply(this,t)}}function He(e=!1){const t=Ye,n=t.l.u;if(!n)return;let r=()=>j(t.s);if(e){let o=0,i={};const s=Ne(()=>{let a=!1;const l=t.s;for(const u in l)l[u]!==i[u]&&(i[u]=l[u],a=!0);return a&&o++,o});r=()=>h(s)}n.b.length&&Tf(()=>{wl(t,r),Jr(n.b)}),kr(()=>{const o=yn(()=>n.m.map(of));return()=>{for(const i of o)typeof i=="function"&&i()}}),n.a.length&&kr(()=>{wl(t,r),Jr(n.a)})}function wl(e,t){if(e.l.s)for(const n of e.l.s)h(n);t()}function De(e,t){var i;var n=(i=e.$$events)==null?void 0:i[t.type],r=Gr(n)?n.slice():n==null?[]:[n];for(var o of r)o.call(this,t)}function rn(e){Ye===null&&ti(),ot&&Ye.l!==null?a1(Ye).m.push(e):kr(()=>{const t=yn(e);if(typeof t=="function")return t})}function $s(e){Ye===null&&ti(),rn(()=>()=>yn(e))}function s1(e,t,{bubbles:n=!1,cancelable:r=!1}={}){return new CustomEvent(e,{detail:t,bubbles:n,cancelable:r})}function ii(){const e=Ye;return e===null&&ti(),(t,n,r)=>{var i;const o=(i=e.s.$$events)==null?void 0:i[t];if(o){const s=Gr(o)?o.slice():[o],a=s1(t,n,r);for(const l of s)l.call(e.x,a);return!a.defaultPrevented}return!0}}function a1(e){var t=e.l;return t.u??(t.u={a:[],b:[],m:[]})}function Es(e,t,n){if(e==null)return t(void 0),n&&n(void 0),gt;const r=yn(()=>e.subscribe(t,n));return r.unsubscribe?()=>r.unsubscribe():r}const Nr=[];function Gt(e,t){return{subscribe:we(e,t).subscribe}}function we(e,t=gt){let n=null;const r=new Set;function o(a){if(us(e,a)&&(e=a,n)){const l=!Nr.length;for(const u of r)u[1](),Nr.push(u,e);if(l){for(let u=0;u<Nr.length;u+=2)Nr[u][0](Nr[u+1]);Nr.length=0}}}function i(a){o(a(e))}function s(a,l=gt){const u=[a,l];return r.add(u),r.size===1&&(n=t(o,i)||gt),a(e),()=>{r.delete(u),r.size===0&&n&&(n(),n=null)}}return{set:o,update:i,subscribe:s}}function Un(e,t,n){const r=!Array.isArray(e),o=r?[e]:e;if(!o.every(Boolean))throw new Error("derived() expects stores as input, got a falsy value");const i=t.length<2;return Gt(n,(s,a)=>{let l=!1;const u=[];let c=0,f=gt;const d=()=>{if(c)return;f();const p=t(r?u[0]:u,s,a);i?s(p):f=typeof p=="function"?p:gt},g=o.map((p,x)=>Es(p,C=>{u[x]=C,c&=~(1<<x),l&&d()},()=>{c|=1<<x}));return l=!0,d(),function(){Jr(g),f(),l=!1}})}function q(e){let t;return Es(e,n=>t=n)(),t}let si=!1,Ss=Symbol();function Q(e,t,n){const r=n[t]??(n[t]={store:null,source:no(void 0),unsubscribe:gt});if(r.store!==e&&!(Ss in n))if(r.unsubscribe(),r.store=e??null,e==null)r.source.v=void 0,r.unsubscribe=gt;else{var o=!0;r.unsubscribe=Es(e,i=>{o?r.source.v=i:U(r.source,i)}),o=!1}return e&&Ss in n?q(e):h(r.source)}function l1(e,t,n){let r=n[t];return r&&r.store!==e&&(r.unsubscribe(),r.unsubscribe=gt),e}function ai(e,t){return e.set(t),t}function nt(){const e={};function t(){ja(()=>{for(var n in e)e[n].unsubscribe();Ur(e,Ss,{enumerable:!1,value:!0})})}return[e,t]}function u1(e){var t=si;try{return si=!1,[e(),si]}finally{si=t}}const c1={get(e,t){if(!e.exclude.includes(t))return e.props[t]},set(e,t){return!1},getOwnPropertyDescriptor(e,t){if(!e.exclude.includes(t)&&t in e.props)return{enumerable:!0,configurable:!0,value:e.props[t]}},has(e,t){return e.exclude.includes(t)?!1:t in e.props},ownKeys(e){return Reflect.ownKeys(e.props).filter(t=>!e.exclude.includes(t))}};function xt(e,t,n){return new Proxy({props:e,exclude:t},c1)}const d1={get(e,t){if(!e.exclude.includes(t))return h(e.version),t in e.special?e.special[t]():e.props[t]},set(e,t,n){return t in e.special||(e.special[t]=w({get[t](){return e.props[t]}},t,ka)),e.special[t](n),La(e.version),!0},getOwnPropertyDescriptor(e,t){if(!e.exclude.includes(t)&&t in e.props)return{enumerable:!0,configurable:!0,value:e.props[t]}},deleteProperty(e,t){return e.exclude.includes(t)||(e.exclude.push(t),La(e.version)),!0},has(e,t){return e.exclude.includes(t)?!1:t in e.props},ownKeys(e){return Reflect.ownKeys(e.props).filter(t=>!e.exclude.includes(t))}};function it(e,t){return new Proxy({props:e,exclude:t,special:{},version:Mt(0)},d1)}const f1={get(e,t){let n=e.props.length;for(;n--;){let r=e.props[n];if(jr(r)&&(r=r()),typeof r=="object"&&r!==null&&t in r)return r[t]}},set(e,t,n){let r=e.props.length;for(;r--;){let o=e.props[r];jr(o)&&(o=o());const i=Mn(o,t);if(i&&i.set)return i.set(n),!0}return!1},getOwnPropertyDescriptor(e,t){let n=e.props.length;for(;n--;){let r=e.props[n];if(jr(r)&&(r=r()),typeof r=="object"&&r!==null&&t in r){const o=Mn(r,t);return o&&!o.configurable&&(o.configurable=!0),o}}},has(e,t){if(t===Xn||t===ss)return!1;for(let n of e.props)if(jr(n)&&(n=n()),n!=null&&t in n)return!0;return!1},ownKeys(e){const t=[];for(let n of e.props){jr(n)&&(n=n());for(const r in n)t.includes(r)||t.push(r)}return t}};function ft(...e){return new Proxy({props:e},f1)}function w(e,t,n,r){var N;var o=(n&Wd)!==0,i=!ot||(n&Kd)!==0,s=(n&qd)!==0,a=(n&Gd)!==0,l=!1,u;s?[u,l]=u1(()=>e[t]):u=e[t];var c=Xn in e||ss in e,f=s&&(((N=Mn(e,t))==null?void 0:N.set)??(c&&t in e&&(E=>e[t]=E)))||void 0,d=r,g=!0,p=!1,x=()=>(p=!0,g&&(g=!1,a?d=yn(r):d=r),d);u===void 0&&r!==void 0&&(f&&i&&gf(),u=x(),f&&f(u));var C;if(i)C=()=>{var E=e[t];return E===void 0?x():(g=!0,p=!1,E)};else{var $=(o?Ne:ve)(()=>e[t]);$.f|=Jd,C=()=>{var E=h($);return E!==void 0&&(d=void 0),E===void 0?d:E}}if(!(n&ka))return C;if(f){var m=e.$$legacy;return function(E,T){return arguments.length>0?((!i||!T||m||l)&&f(T?C():E),E):C()}}var _=!1,v=no(u),b=Ne(()=>{var E=C(),T=h(v);return _?(_=!1,T):v.v=E});return o||(b.equals=cs),function(E,T){if(arguments.length>0){const D=T?h(b):i&&s?Ht(E):E;return b.equals(D)||(_=!0,U(v,D),p&&d!==void 0&&(d=D),yn(()=>h(b))),E}return h(b)}}function g1(e){return new h1(e)}class h1{constructor(t){wr(this,Zn);wr(this,jt);var i;var n=new Map,r=(s,a)=>{var l=no(a);return n.set(s,l),l};const o=new Proxy({...t.props||{},$$events:{}},{get(s,a){return h(n.get(a)??r(a,Reflect.get(s,a)))},has(s,a){return a===ss?!0:(h(n.get(a)??r(a,Reflect.get(s,a))),Reflect.has(s,a))},set(s,a,l){return U(n.get(a)??r(a,l),l),Reflect.set(s,a,l)}});zo(this,jt,(t.hydrate?Gf:al)(t.component,{target:t.target,anchor:t.anchor,props:o,context:t.context,intro:t.intro??!1,recover:t.recover})),(!((i=t==null?void 0:t.props)!=null&&i.$$host)||t.sync===!1)&&y(),zo(this,Zn,o.$$events);for(const s of Object.keys(ut(this,jt)))s==="$set"||s==="$destroy"||s==="$on"||Ur(this,s,{get(){return ut(this,jt)[s]},set(a){ut(this,jt)[s]=a},enumerable:!0});ut(this,jt).$set=s=>{Object.assign(o,s)},ut(this,jt).$destroy=()=>{Uf(ut(this,jt))}}$set(t){ut(this,jt).$set(t)}$on(t,n){ut(this,Zn)[t]=ut(this,Zn)[t]||[];const r=(...o)=>n.call(this,...o);return ut(this,Zn)[t].push(r),()=>{ut(this,Zn)[t]=ut(this,Zn)[t].filter(o=>o!==r)}}$destroy(){ut(this,jt).$destroy()}}Zn=new WeakMap,jt=new WeakMap;let _l;typeof HTMLElement=="function"&&(_l=class extends HTMLElement{constructor(t,n,r){super();Nt(this,"$$ctor");Nt(this,"$$s");Nt(this,"$$c");Nt(this,"$$cn",!1);Nt(this,"$$d",{});Nt(this,"$$r",!1);Nt(this,"$$p_d",{});Nt(this,"$$l",{});Nt(this,"$$l_u",new Map);Nt(this,"$$me");this.$$ctor=t,this.$$s=n,r&&this.attachShadow({mode:"open"})}addEventListener(t,n,r){if(this.$$l[t]=this.$$l[t]||[],this.$$l[t].push(n),this.$$c){const o=this.$$c.$on(t,n);this.$$l_u.set(n,o)}super.addEventListener(t,n,r)}removeEventListener(t,n,r){if(super.removeEventListener(t,n,r),this.$$c){const o=this.$$l_u.get(n);o&&(o(),this.$$l_u.delete(n))}}async connectedCallback(){if(this.$$cn=!0,!this.$$c){let t=function(o){return i=>{const s=document.createElement("slot");o!=="default"&&(s.name=o),L(i,s)}};if(await Promise.resolve(),!this.$$cn||this.$$c)return;const n={},r=v1(this);for(const o of this.$$s)o in r&&(o==="default"&&!this.$$d.children?(this.$$d.children=t(o),n.default=!0):n[o]=t(o));for(const o of this.attributes){const i=this.$$g_p(o.name);i in this.$$d||(this.$$d[i]=li(i,o.value,this.$$p_d,"toProp"))}for(const o in this.$$p_d)!(o in this.$$d)&&this[o]!==void 0&&(this.$$d[o]=this[o],delete this[o]);this.$$c=g1({component:this.$$ctor,target:this.shadowRoot||this,props:{...this.$$d,$$slots:n,$$host:this}}),this.$$me=Mf(()=>{$r(()=>{var o;this.$$r=!0;for(const i of Fo(this.$$c)){if(!((o=this.$$p_d[i])!=null&&o.reflect))continue;this.$$d[i]=this.$$c[i];const s=li(i,this.$$d[i],this.$$p_d,"toAttribute");s==null?this.removeAttribute(this.$$p_d[i].attribute||i):this.setAttribute(this.$$p_d[i].attribute||i,s)}this.$$r=!1})});for(const o in this.$$l)for(const i of this.$$l[o]){const s=this.$$c.$on(o,i);this.$$l_u.set(i,s)}this.$$l={}}}attributeChangedCallback(t,n,r){var o;this.$$r||(t=this.$$g_p(t),this.$$d[t]=li(t,r,this.$$p_d,"toProp"),(o=this.$$c)==null||o.$set({[t]:this.$$d[t]}))}disconnectedCallback(){this.$$cn=!1,Promise.resolve().then(()=>{!this.$$cn&&this.$$c&&(this.$$c.$destroy(),this.$$me(),this.$$c=void 0)})}$$g_p(t){return Fo(this.$$p_d).find(n=>this.$$p_d[n].attribute===t||!this.$$p_d[n].attribute&&n.toLowerCase()===t)||t}});function li(e,t,n,r){var i;const o=(i=n[e])==null?void 0:i.type;if(t=o==="Boolean"&&typeof t!="boolean"?t!=null:t,!r||!n[e])return t;if(r==="toAttribute")switch(o){case"Object":case"Array":return t==null?null:JSON.stringify(t);case"Boolean":return t?"":null;case"Number":return t??null;default:return t}else switch(o){case"Object":case"Array":return t&&JSON.parse(t);case"Boolean":return t;case"Number":return t!=null?+t:t;default:return t}}function v1(e){const t={};return e.childNodes.forEach(n=>{t[n.slot||"default"]=!0}),t}function ie(e,t,n,r,o,i){let s=class extends _l{constructor(){super(e,n,o),this.$$p_d=t}static get observedAttributes(){return Fo(t).map(a=>(t[a].attribute||a).toLowerCase())}};return Fo(t).forEach(a=>{Ur(s.prototype,a,{get(){return this.$$c&&a in this.$$c?this.$$c[a]:this.$$d[a]},set(l){var f;l=li(a,l,t),this.$$d[a]=l;var u=this.$$c;if(u){var c=(f=Mn(u,a))==null?void 0:f.get;c?u[a]=l:u.$set({[a]:l})}}})}),r.forEach(a=>{Ur(s.prototype,a,{get(){var l;return(l=this.$$c)==null?void 0:l[a]}})}),e.element=s,s}function Et(e){if(typeof e=="string"||typeof e=="number")return""+e;let t="";if(Array.isArray(e))for(let n=0,r;n<e.length;n++)(r=Et(e[n]))!==""&&(t+=(t&&" ")+r);else for(let n in e)e[n]&&(t+=(t&&" ")+n);return t}var p1={value:()=>{}};function ui(){for(var e=0,t=arguments.length,n={},r;e<t;++e){if(!(r=arguments[e]+"")||r in n||/[\s.]/.test(r))throw new Error("illegal type: "+r);n[r]=[]}return new ci(n)}function ci(e){this._=e}function m1(e,t){return e.trim().split(/^|\s+/).map(function(n){var r="",o=n.indexOf(".");if(o>=0&&(r=n.slice(o+1),n=n.slice(0,o)),n&&!t.hasOwnProperty(n))throw new Error("unknown type: "+n);return{type:n,name:r}})}ci.prototype=ui.prototype={constructor:ci,on:function(e,t){var n=this._,r=m1(e+"",n),o,i=-1,s=r.length;if(arguments.length<2){for(;++i<s;)if((o=(e=r[i]).type)&&(o=y1(n[o],e.name)))return o;return}if(t!=null&&typeof t!="function")throw new Error("invalid callback: "+t);for(;++i<s;)if(o=(e=r[i]).type)n[o]=xl(n[o],e.name,t);else if(t==null)for(o in n)n[o]=xl(n[o],e.name,null);return this},copy:function(){var e={},t=this._;for(var n in t)e[n]=t[n].slice();return new ci(e)},call:function(e,t){if((o=arguments.length-2)>0)for(var n=new Array(o),r=0,o,i;r<o;++r)n[r]=arguments[r+2];if(!this._.hasOwnProperty(e))throw new Error("unknown type: "+e);for(i=this._[e],r=0,o=i.length;r<o;++r)i[r].value.apply(t,n)},apply:function(e,t,n){if(!this._.hasOwnProperty(e))throw new Error("unknown type: "+e);for(var r=this._[e],o=0,i=r.length;o<i;++o)r[o].value.apply(t,n)}};function y1(e,t){for(var n=0,r=e.length,o;n<r;++n)if((o=e[n]).name===t)return o.value}function xl(e,t,n){for(var r=0,o=e.length;r<o;++r)if(e[r].name===t){e[r]=p1,e=e.slice(0,r).concat(e.slice(r+1));break}return n!=null&&e.push({name:t,value:n}),e}var Ps="http://www.w3.org/1999/xhtml";const bl={svg:"http://www.w3.org/2000/svg",xhtml:Ps,xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace",xmlns:"http://www.w3.org/2000/xmlns/"};function di(e){var t=e+="",n=t.indexOf(":");return n>=0&&(t=e.slice(0,n))!=="xmlns"&&(e=e.slice(n+1)),bl.hasOwnProperty(t)?{space:bl[t],local:e}:e}function w1(e){return function(){var t=this.ownerDocument,n=this.namespaceURI;return n===Ps&&t.documentElement.namespaceURI===Ps?t.createElement(e):t.createElementNS(n,e)}}function _1(e){return function(){return this.ownerDocument.createElementNS(e.space,e.local)}}function Cl(e){var t=di(e);return(t.local?_1:w1)(t)}function x1(){}function Ns(e){return e==null?x1:function(){return this.querySelector(e)}}function b1(e){typeof e!="function"&&(e=Ns(e));for(var t=this._groups,n=t.length,r=new Array(n),o=0;o<n;++o)for(var i=t[o],s=i.length,a=r[o]=new Array(s),l,u,c=0;c<s;++c)(l=i[c])&&(u=e.call(l,l.__data__,c,i))&&("__data__"in l&&(u.__data__=l.__data__),a[c]=u);return new Zt(r,this._parents)}function C1(e){return e==null?[]:Array.isArray(e)?e:Array.from(e)}function k1(){return[]}function kl(e){return e==null?k1:function(){return this.querySelectorAll(e)}}function $1(e){return function(){return C1(e.apply(this,arguments))}}function E1(e){typeof e=="function"?e=$1(e):e=kl(e);for(var t=this._groups,n=t.length,r=[],o=[],i=0;i<n;++i)for(var s=t[i],a=s.length,l,u=0;u<a;++u)(l=s[u])&&(r.push(e.call(l,l.__data__,u,s)),o.push(l));return new Zt(r,o)}function $l(e){return function(){return this.matches(e)}}function El(e){return function(t){return t.matches(e)}}var S1=Array.prototype.find;function P1(e){return function(){return S1.call(this.children,e)}}function N1(){return this.firstElementChild}function T1(e){return this.select(e==null?N1:P1(typeof e=="function"?e:El(e)))}var M1=Array.prototype.filter;function H1(){return Array.from(this.children)}function V1(e){return function(){return M1.call(this.children,e)}}function D1(e){return this.selectAll(e==null?H1:V1(typeof e=="function"?e:El(e)))}function A1(e){typeof e!="function"&&(e=$l(e));for(var t=this._groups,n=t.length,r=new Array(n),o=0;o<n;++o)for(var i=t[o],s=i.length,a=r[o]=[],l,u=0;u<s;++u)(l=i[u])&&e.call(l,l.__data__,u,i)&&a.push(l);return new Zt(r,this._parents)}function Sl(e){return new Array(e.length)}function L1(){return new Zt(this._enter||this._groups.map(Sl),this._parents)}function fi(e,t){this.ownerDocument=e.ownerDocument,this.namespaceURI=e.namespaceURI,this._next=null,this._parent=e,this.__data__=t}fi.prototype={constructor:fi,appendChild:function(e){return this._parent.insertBefore(e,this._next)},insertBefore:function(e,t){return this._parent.insertBefore(e,t)},querySelector:function(e){return this._parent.querySelector(e)},querySelectorAll:function(e){return this._parent.querySelectorAll(e)}};function O1(e){return function(){return e}}function I1(e,t,n,r,o,i){for(var s=0,a,l=t.length,u=i.length;s<u;++s)(a=t[s])?(a.__data__=i[s],r[s]=a):n[s]=new fi(e,i[s]);for(;s<l;++s)(a=t[s])&&(o[s]=a)}function z1(e,t,n,r,o,i,s){var a,l,u=new Map,c=t.length,f=i.length,d=new Array(c),g;for(a=0;a<c;++a)(l=t[a])&&(d[a]=g=s.call(l,l.__data__,a,t)+"",u.has(g)?o[a]=l:u.set(g,l));for(a=0;a<f;++a)g=s.call(e,i[a],a,i)+"",(l=u.get(g))?(r[a]=l,l.__data__=i[a],u.delete(g)):n[a]=new fi(e,i[a]);for(a=0;a<c;++a)(l=t[a])&&u.get(d[a])===l&&(o[a]=l)}function R1(e){return e.__data__}function B1(e,t){if(!arguments.length)return Array.from(this,R1);var n=t?z1:I1,r=this._parents,o=this._groups;typeof e!="function"&&(e=O1(e));for(var i=o.length,s=new Array(i),a=new Array(i),l=new Array(i),u=0;u<i;++u){var c=r[u],f=o[u],d=f.length,g=Y1(e.call(c,c&&c.__data__,u,r)),p=g.length,x=a[u]=new Array(p),C=s[u]=new Array(p),$=l[u]=new Array(d);n(c,f,x,C,$,g,t);for(var m=0,_=0,v,b;m<p;++m)if(v=x[m]){for(m>=_&&(_=m+1);!(b=C[_])&&++_<p;);v._next=b||null}}return s=new Zt(s,r),s._enter=a,s._exit=l,s}function Y1(e){return typeof e=="object"&&"length"in e?e:Array.from(e)}function Z1(){return new Zt(this._exit||this._groups.map(Sl),this._parents)}function X1(e,t,n){var r=this.enter(),o=this,i=this.exit();return typeof e=="function"?(r=e(r),r&&(r=r.selection())):r=r.append(e+""),t!=null&&(o=t(o),o&&(o=o.selection())),n==null?i.remove():n(i),r&&o?r.merge(o).order():o}function F1(e){for(var t=e.selection?e.selection():e,n=this._groups,r=t._groups,o=n.length,i=r.length,s=Math.min(o,i),a=new Array(o),l=0;l<s;++l)for(var u=n[l],c=r[l],f=u.length,d=a[l]=new Array(f),g,p=0;p<f;++p)(g=u[p]||c[p])&&(d[p]=g);for(;l<o;++l)a[l]=n[l];return new Zt(a,this._parents)}function W1(){for(var e=this._groups,t=-1,n=e.length;++t<n;)for(var r=e[t],o=r.length-1,i=r[o],s;--o>=0;)(s=r[o])&&(i&&s.compareDocumentPosition(i)^4&&i.parentNode.insertBefore(s,i),i=s);return this}function K1(e){e||(e=q1);function t(f,d){return f&&d?e(f.__data__,d.__data__):!f-!d}for(var n=this._groups,r=n.length,o=new Array(r),i=0;i<r;++i){for(var s=n[i],a=s.length,l=o[i]=new Array(a),u,c=0;c<a;++c)(u=s[c])&&(l[c]=u);l.sort(t)}return new Zt(o,this._parents).order()}function q1(e,t){return e<t?-1:e>t?1:e>=t?0:NaN}function G1(){var e=arguments[0];return arguments[0]=this,e.apply(null,arguments),this}function U1(){return Array.from(this)}function j1(){for(var e=this._groups,t=0,n=e.length;t<n;++t)for(var r=e[t],o=0,i=r.length;o<i;++o){var s=r[o];if(s)return s}return null}function J1(){let e=0;for(const t of this)++e;return e}function Q1(){return!this.node()}function eg(e){for(var t=this._groups,n=0,r=t.length;n<r;++n)for(var o=t[n],i=0,s=o.length,a;i<s;++i)(a=o[i])&&e.call(a,a.__data__,i,o);return this}function tg(e){return function(){this.removeAttribute(e)}}function ng(e){return function(){this.removeAttributeNS(e.space,e.local)}}function rg(e,t){return function(){this.setAttribute(e,t)}}function og(e,t){return function(){this.setAttributeNS(e.space,e.local,t)}}function ig(e,t){return function(){var n=t.apply(this,arguments);n==null?this.removeAttribute(e):this.setAttribute(e,n)}}function sg(e,t){return function(){var n=t.apply(this,arguments);n==null?this.removeAttributeNS(e.space,e.local):this.setAttributeNS(e.space,e.local,n)}}function ag(e,t){var n=di(e);if(arguments.length<2){var r=this.node();return n.local?r.getAttributeNS(n.space,n.local):r.getAttribute(n)}return this.each((t==null?n.local?ng:tg:typeof t=="function"?n.local?sg:ig:n.local?og:rg)(n,t))}function Pl(e){return e.ownerDocument&&e.ownerDocument.defaultView||e.document&&e||e.defaultView}function lg(e){return function(){this.style.removeProperty(e)}}function ug(e,t,n){return function(){this.style.setProperty(e,t,n)}}function cg(e,t,n){return function(){var r=t.apply(this,arguments);r==null?this.style.removeProperty(e):this.style.setProperty(e,r,n)}}function dg(e,t,n){return arguments.length>1?this.each((t==null?lg:typeof t=="function"?cg:ug)(e,t,n??"")):Tr(this.node(),e)}function Tr(e,t){return e.style.getPropertyValue(t)||Pl(e).getComputedStyle(e,null).getPropertyValue(t)}function fg(e){return function(){delete this[e]}}function gg(e,t){return function(){this[e]=t}}function hg(e,t){return function(){var n=t.apply(this,arguments);n==null?delete this[e]:this[e]=n}}function vg(e,t){return arguments.length>1?this.each((t==null?fg:typeof t=="function"?hg:gg)(e,t)):this.node()[e]}function Nl(e){return e.trim().split(/^|\s+/)}function Ts(e){return e.classList||new Tl(e)}function Tl(e){this._node=e,this._names=Nl(e.getAttribute("class")||"")}Tl.prototype={add:function(e){var t=this._names.indexOf(e);t<0&&(this._names.push(e),this._node.setAttribute("class",this._names.join(" ")))},remove:function(e){var t=this._names.indexOf(e);t>=0&&(this._names.splice(t,1),this._node.setAttribute("class",this._names.join(" ")))},contains:function(e){return this._names.indexOf(e)>=0}};function Ml(e,t){for(var n=Ts(e),r=-1,o=t.length;++r<o;)n.add(t[r])}function Hl(e,t){for(var n=Ts(e),r=-1,o=t.length;++r<o;)n.remove(t[r])}function pg(e){return function(){Ml(this,e)}}function mg(e){return function(){Hl(this,e)}}function yg(e,t){return function(){(t.apply(this,arguments)?Ml:Hl)(this,e)}}function wg(e,t){var n=Nl(e+"");if(arguments.length<2){for(var r=Ts(this.node()),o=-1,i=n.length;++o<i;)if(!r.contains(n[o]))return!1;return!0}return this.each((typeof t=="function"?yg:t?pg:mg)(n,t))}function _g(){this.textContent=""}function xg(e){return function(){this.textContent=e}}function bg(e){return function(){var t=e.apply(this,arguments);this.textContent=t??""}}function Cg(e){return arguments.length?this.each(e==null?_g:(typeof e=="function"?bg:xg)(e)):this.node().textContent}function kg(){this.innerHTML=""}function $g(e){return function(){this.innerHTML=e}}function Eg(e){return function(){var t=e.apply(this,arguments);this.innerHTML=t??""}}function Sg(e){return arguments.length?this.each(e==null?kg:(typeof e=="function"?Eg:$g)(e)):this.node().innerHTML}function Pg(){this.nextSibling&&this.parentNode.appendChild(this)}function Ng(){return this.each(Pg)}function Tg(){this.previousSibling&&this.parentNode.insertBefore(this,this.parentNode.firstChild)}function Mg(){return this.each(Tg)}function Hg(e){var t=typeof e=="function"?e:Cl(e);return this.select(function(){return this.appendChild(t.apply(this,arguments))})}function Vg(){return null}function Dg(e,t){var n=typeof e=="function"?e:Cl(e),r=t==null?Vg:typeof t=="function"?t:Ns(t);return this.select(function(){return this.insertBefore(n.apply(this,arguments),r.apply(this,arguments)||null)})}function Ag(){var e=this.parentNode;e&&e.removeChild(this)}function Lg(){return this.each(Ag)}function Og(){var e=this.cloneNode(!1),t=this.parentNode;return t?t.insertBefore(e,this.nextSibling):e}function Ig(){var e=this.cloneNode(!0),t=this.parentNode;return t?t.insertBefore(e,this.nextSibling):e}function zg(e){return this.select(e?Ig:Og)}function Rg(e){return arguments.length?this.property("__data__",e):this.node().__data__}function Bg(e){return function(t){e.call(this,t,this.__data__)}}function Yg(e){return e.trim().split(/^|\s+/).map(function(t){var n="",r=t.indexOf(".");return r>=0&&(n=t.slice(r+1),t=t.slice(0,r)),{type:t,name:n}})}function Zg(e){return function(){var t=this.__on;if(t){for(var n=0,r=-1,o=t.length,i;n<o;++n)i=t[n],(!e.type||i.type===e.type)&&i.name===e.name?this.removeEventListener(i.type,i.listener,i.options):t[++r]=i;++r?t.length=r:delete this.__on}}}function Xg(e,t,n){return function(){var r=this.__on,o,i=Bg(t);if(r){for(var s=0,a=r.length;s<a;++s)if((o=r[s]).type===e.type&&o.name===e.name){this.removeEventListener(o.type,o.listener,o.options),this.addEventListener(o.type,o.listener=i,o.options=n),o.value=t;return}}this.addEventListener(e.type,i,n),o={type:e.type,name:e.name,value:t,listener:i,options:n},r?r.push(o):this.__on=[o]}}function Fg(e,t,n){var r=Yg(e+""),o,i=r.length,s;if(arguments.length<2){var a=this.node().__on;if(a){for(var l=0,u=a.length,c;l<u;++l)for(o=0,c=a[l];o<i;++o)if((s=r[o]).type===c.type&&s.name===c.name)return c.value}return}for(a=t?Xg:Zg,o=0;o<i;++o)this.each(a(r[o],t,n));return this}function Vl(e,t,n){var r=Pl(e),o=r.CustomEvent;typeof o=="function"?o=new o(t,n):(o=r.document.createEvent("Event"),n?(o.initEvent(t,n.bubbles,n.cancelable),o.detail=n.detail):o.initEvent(t,!1,!1)),e.dispatchEvent(o)}function Wg(e,t){return function(){return Vl(this,e,t)}}function Kg(e,t){return function(){return Vl(this,e,t.apply(this,arguments))}}function qg(e,t){return this.each((typeof t=="function"?Kg:Wg)(e,t))}function*Gg(){for(var e=this._groups,t=0,n=e.length;t<n;++t)for(var r=e[t],o=0,i=r.length,s;o<i;++o)(s=r[o])&&(yield s)}var Dl=[null];function Zt(e,t){this._groups=e,this._parents=t}function lo(){return new Zt([[document.documentElement]],Dl)}function Ug(){return this}Zt.prototype=lo.prototype={constructor:Zt,select:b1,selectAll:E1,selectChild:T1,selectChildren:D1,filter:A1,data:B1,enter:L1,exit:Z1,join:X1,merge:F1,selection:Ug,order:W1,sort:K1,call:G1,nodes:U1,node:j1,size:J1,empty:Q1,each:eg,attr:ag,style:dg,property:vg,classed:wg,text:Cg,html:Sg,raise:Ng,lower:Mg,append:Hg,insert:Dg,remove:Lg,clone:zg,datum:Rg,on:Fg,dispatch:qg,[Symbol.iterator]:Gg};function Ut(e){return typeof e=="string"?new Zt([[document.querySelector(e)]],[document.documentElement]):new Zt([[e]],Dl)}function jg(e){let t;for(;t=e.sourceEvent;)e=t;return e}function on(e,t){if(e=jg(e),t===void 0&&(t=e.currentTarget),t){var n=t.ownerSVGElement||t;if(n.createSVGPoint){var r=n.createSVGPoint();return r.x=e.clientX,r.y=e.clientY,r=r.matrixTransform(t.getScreenCTM().inverse()),[r.x,r.y]}if(t.getBoundingClientRect){var o=t.getBoundingClientRect();return[e.clientX-o.left-t.clientLeft,e.clientY-o.top-t.clientTop]}}return[e.pageX,e.pageY]}const Jg={passive:!1},uo={capture:!0,passive:!1};function Ms(e){e.stopImmediatePropagation()}function Mr(e){e.preventDefault(),e.stopImmediatePropagation()}function Al(e){var t=e.document.documentElement,n=Ut(e).on("dragstart.drag",Mr,uo);"onselectstart"in t?n.on("selectstart.drag",Mr,uo):(t.__noselect=t.style.MozUserSelect,t.style.MozUserSelect="none")}function Ll(e,t){var n=e.document.documentElement,r=Ut(e).on("dragstart.drag",null);t&&(r.on("click.drag",Mr,uo),setTimeout(function(){r.on("click.drag",null)},0)),"onselectstart"in n?r.on("selectstart.drag",null):(n.style.MozUserSelect=n.__noselect,delete n.__noselect)}const gi=e=>()=>e;function Hs(e,{sourceEvent:t,subject:n,target:r,identifier:o,active:i,x:s,y:a,dx:l,dy:u,dispatch:c}){Object.defineProperties(this,{type:{value:e,enumerable:!0,configurable:!0},sourceEvent:{value:t,enumerable:!0,configurable:!0},subject:{value:n,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:o,enumerable:!0,configurable:!0},active:{value:i,enumerable:!0,configurable:!0},x:{value:s,enumerable:!0,configurable:!0},y:{value:a,enumerable:!0,configurable:!0},dx:{value:l,enumerable:!0,configurable:!0},dy:{value:u,enumerable:!0,configurable:!0},_:{value:c}})}Hs.prototype.on=function(){var e=this._.on.apply(this._,arguments);return e===this._?this:e};function Qg(e){return!e.ctrlKey&&!e.button}function eh(){return this.parentNode}function th(e,t){return t??{x:e.x,y:e.y}}function nh(){return navigator.maxTouchPoints||"ontouchstart"in this}function rh(){var e=Qg,t=eh,n=th,r=nh,o={},i=ui("start","drag","end"),s=0,a,l,u,c,f=0;function d(v){v.on("mousedown.drag",g).filter(r).on("touchstart.drag",C).on("touchmove.drag",$,Jg).on("touchend.drag touchcancel.drag",m).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function g(v,b){if(!(c||!e.call(this,v,b))){var N=_(this,t.call(this,v,b),v,b,"mouse");N&&(Ut(v.view).on("mousemove.drag",p,uo).on("mouseup.drag",x,uo),Al(v.view),Ms(v),u=!1,a=v.clientX,l=v.clientY,N("start",v))}}function p(v){if(Mr(v),!u){var b=v.clientX-a,N=v.clientY-l;u=b*b+N*N>f}o.mouse("drag",v)}function x(v){Ut(v.view).on("mousemove.drag mouseup.drag",null),Ll(v.view,u),Mr(v),o.mouse("end",v)}function C(v,b){if(e.call(this,v,b)){var N=v.changedTouches,E=t.call(this,v,b),T=N.length,D,V;for(D=0;D<T;++D)(V=_(this,E,v,b,N[D].identifier,N[D]))&&(Ms(v),V("start",v,N[D]))}}function $(v){var b=v.changedTouches,N=b.length,E,T;for(E=0;E<N;++E)(T=o[b[E].identifier])&&(Mr(v),T("drag",v,b[E]))}function m(v){var b=v.changedTouches,N=b.length,E,T;for(c&&clearTimeout(c),c=setTimeout(function(){c=null},500),E=0;E<N;++E)(T=o[b[E].identifier])&&(Ms(v),T("end",v,b[E]))}function _(v,b,N,E,T,D){var V=i.copy(),A=on(D||N,b),O,R,S;if((S=n.call(v,new Hs("beforestart",{sourceEvent:N,target:d,identifier:T,active:s,x:A[0],y:A[1],dx:0,dy:0,dispatch:V}),E))!=null)return O=S.x-A[0]||0,R=S.y-A[1]||0,function M(k,P,H){var I=A,B;switch(k){case"start":o[T]=M,B=s++;break;case"end":delete o[T],--s;case"drag":A=on(H||P,b),B=s;break}V.call(k,v,new Hs(k,{sourceEvent:P,subject:S,target:d,identifier:T,active:B,x:A[0]+O,y:A[1]+R,dx:A[0]-I[0],dy:A[1]-I[1],dispatch:V}),E)}}return d.filter=function(v){return arguments.length?(e=typeof v=="function"?v:gi(!!v),d):e},d.container=function(v){return arguments.length?(t=typeof v=="function"?v:gi(v),d):t},d.subject=function(v){return arguments.length?(n=typeof v=="function"?v:gi(v),d):n},d.touchable=function(v){return arguments.length?(r=typeof v=="function"?v:gi(!!v),d):r},d.on=function(){var v=i.on.apply(i,arguments);return v===i?d:v},d.clickDistance=function(v){return arguments.length?(f=(v=+v)*v,d):Math.sqrt(f)},d}function Vs(e,t,n){e.prototype=t.prototype=n,n.constructor=e}function Ol(e,t){var n=Object.create(e.prototype);for(var r in t)n[r]=t[r];return n}function co(){}var fo=.7,hi=1/fo,Hr="\\s*([+-]?\\d+)\\s*",go="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)\\s*",_n="\\s*([+-]?(?:\\d*\\.)?\\d+(?:[eE][+-]?\\d+)?)%\\s*",oh=/^#([0-9a-f]{3,8})$/,ih=new RegExp(`^rgb\\(${Hr},${Hr},${Hr}\\)$`),sh=new RegExp(`^rgb\\(${_n},${_n},${_n}\\)$`),ah=new RegExp(`^rgba\\(${Hr},${Hr},${Hr},${go}\\)$`),lh=new RegExp(`^rgba\\(${_n},${_n},${_n},${go}\\)$`),uh=new RegExp(`^hsl\\(${go},${_n},${_n}\\)$`),ch=new RegExp(`^hsla\\(${go},${_n},${_n},${go}\\)$`),Il={aliceblue:15792383,antiquewhite:16444375,aqua:65535,aquamarine:8388564,azure:15794175,beige:16119260,bisque:16770244,black:0,blanchedalmond:16772045,blue:255,blueviolet:9055202,brown:10824234,burlywood:14596231,cadetblue:6266528,chartreuse:8388352,chocolate:13789470,coral:16744272,cornflowerblue:6591981,cornsilk:16775388,crimson:14423100,cyan:65535,darkblue:139,darkcyan:35723,darkgoldenrod:12092939,darkgray:11119017,darkgreen:25600,darkgrey:11119017,darkkhaki:12433259,darkmagenta:9109643,darkolivegreen:5597999,darkorange:16747520,darkorchid:10040012,darkred:9109504,darksalmon:15308410,darkseagreen:9419919,darkslateblue:4734347,darkslategray:3100495,darkslategrey:3100495,darkturquoise:52945,darkviolet:9699539,deeppink:16716947,deepskyblue:49151,dimgray:6908265,dimgrey:6908265,dodgerblue:2003199,firebrick:11674146,floralwhite:16775920,forestgreen:2263842,fuchsia:16711935,gainsboro:14474460,ghostwhite:16316671,gold:16766720,goldenrod:14329120,gray:8421504,green:32768,greenyellow:11403055,grey:8421504,honeydew:15794160,hotpink:16738740,indianred:13458524,indigo:4915330,ivory:16777200,khaki:15787660,lavender:15132410,lavenderblush:16773365,lawngreen:8190976,lemonchiffon:16775885,lightblue:11393254,lightcoral:15761536,lightcyan:14745599,lightgoldenrodyellow:16448210,lightgray:13882323,lightgreen:9498256,lightgrey:13882323,lightpink:16758465,lightsalmon:16752762,lightseagreen:2142890,lightskyblue:8900346,lightslategray:7833753,lightslategrey:7833753,lightsteelblue:11584734,lightyellow:16777184,lime:65280,limegreen:3329330,linen:16445670,magenta:16711935,maroon:8388608,mediumaquamarine:6737322,mediumblue:205,mediumorchid:12211667,mediumpurple:9662683,mediumseagreen:3978097,mediumslateblue:8087790,mediumspringgreen:64154,mediumturquoise:4772300,mediumvioletred:13047173,midnightblue:1644912,mintcream:16121850,mistyrose:16770273,moccasin:16770229,navajowhite:16768685,navy:128,oldlace:16643558,olive:8421376,olivedrab:7048739,orange:16753920,orangered:16729344,orchid:14315734,palegoldenrod:15657130,palegreen:10025880,paleturquoise:11529966,palevioletred:14381203,papayawhip:16773077,peachpuff:16767673,peru:13468991,pink:16761035,plum:14524637,powderblue:11591910,purple:8388736,rebeccapurple:6697881,red:16711680,rosybrown:12357519,royalblue:4286945,saddlebrown:9127187,salmon:16416882,sandybrown:16032864,seagreen:3050327,seashell:16774638,sienna:10506797,silver:12632256,skyblue:8900331,slateblue:6970061,slategray:7372944,slategrey:7372944,snow:16775930,springgreen:65407,steelblue:4620980,tan:13808780,teal:32896,thistle:14204888,tomato:16737095,turquoise:4251856,violet:15631086,wheat:16113331,white:16777215,whitesmoke:16119285,yellow:16776960,yellowgreen:10145074};Vs(co,ho,{copy(e){return Object.assign(new this.constructor,this,e)},displayable(){return this.rgb().displayable()},hex:zl,formatHex:zl,formatHex8:dh,formatHsl:fh,formatRgb:Rl,toString:Rl});function zl(){return this.rgb().formatHex()}function dh(){return this.rgb().formatHex8()}function fh(){return Fl(this).formatHsl()}function Rl(){return this.rgb().formatRgb()}function ho(e){var t,n;return e=(e+"").trim().toLowerCase(),(t=oh.exec(e))?(n=t[1].length,t=parseInt(t[1],16),n===6?Bl(t):n===3?new At(t>>8&15|t>>4&240,t>>4&15|t&240,(t&15)<<4|t&15,1):n===8?vi(t>>24&255,t>>16&255,t>>8&255,(t&255)/255):n===4?vi(t>>12&15|t>>8&240,t>>8&15|t>>4&240,t>>4&15|t&240,((t&15)<<4|t&15)/255):null):(t=ih.exec(e))?new At(t[1],t[2],t[3],1):(t=sh.exec(e))?new At(t[1]*255/100,t[2]*255/100,t[3]*255/100,1):(t=ah.exec(e))?vi(t[1],t[2],t[3],t[4]):(t=lh.exec(e))?vi(t[1]*255/100,t[2]*255/100,t[3]*255/100,t[4]):(t=uh.exec(e))?Xl(t[1],t[2]/100,t[3]/100,1):(t=ch.exec(e))?Xl(t[1],t[2]/100,t[3]/100,t[4]):Il.hasOwnProperty(e)?Bl(Il[e]):e==="transparent"?new At(NaN,NaN,NaN,0):null}function Bl(e){return new At(e>>16&255,e>>8&255,e&255,1)}function vi(e,t,n,r){return r<=0&&(e=t=n=NaN),new At(e,t,n,r)}function gh(e){return e instanceof co||(e=ho(e)),e?(e=e.rgb(),new At(e.r,e.g,e.b,e.opacity)):new At}function Ds(e,t,n,r){return arguments.length===1?gh(e):new At(e,t,n,r??1)}function At(e,t,n,r){this.r=+e,this.g=+t,this.b=+n,this.opacity=+r}Vs(At,Ds,Ol(co,{brighter(e){return e=e==null?hi:Math.pow(hi,e),new At(this.r*e,this.g*e,this.b*e,this.opacity)},darker(e){return e=e==null?fo:Math.pow(fo,e),new At(this.r*e,this.g*e,this.b*e,this.opacity)},rgb(){return this},clamp(){return new At(dr(this.r),dr(this.g),dr(this.b),pi(this.opacity))},displayable(){return-.5<=this.r&&this.r<255.5&&-.5<=this.g&&this.g<255.5&&-.5<=this.b&&this.b<255.5&&0<=this.opacity&&this.opacity<=1},hex:Yl,formatHex:Yl,formatHex8:hh,formatRgb:Zl,toString:Zl}));function Yl(){return`#${fr(this.r)}${fr(this.g)}${fr(this.b)}`}function hh(){return`#${fr(this.r)}${fr(this.g)}${fr(this.b)}${fr((isNaN(this.opacity)?1:this.opacity)*255)}`}function Zl(){const e=pi(this.opacity);return`${e===1?"rgb(":"rgba("}${dr(this.r)}, ${dr(this.g)}, ${dr(this.b)}${e===1?")":`, ${e})`}`}function pi(e){return isNaN(e)?1:Math.max(0,Math.min(1,e))}function dr(e){return Math.max(0,Math.min(255,Math.round(e)||0))}function fr(e){return e=dr(e),(e<16?"0":"")+e.toString(16)}function Xl(e,t,n,r){return r<=0?e=t=n=NaN:n<=0||n>=1?e=t=NaN:t<=0&&(e=NaN),new sn(e,t,n,r)}function Fl(e){if(e instanceof sn)return new sn(e.h,e.s,e.l,e.opacity);if(e instanceof co||(e=ho(e)),!e)return new sn;if(e instanceof sn)return e;e=e.rgb();var t=e.r/255,n=e.g/255,r=e.b/255,o=Math.min(t,n,r),i=Math.max(t,n,r),s=NaN,a=i-o,l=(i+o)/2;return a?(t===i?s=(n-r)/a+(n<r)*6:n===i?s=(r-t)/a+2:s=(t-n)/a+4,a/=l<.5?i+o:2-i-o,s*=60):a=l>0&&l<1?0:s,new sn(s,a,l,e.opacity)}function vh(e,t,n,r){return arguments.length===1?Fl(e):new sn(e,t,n,r??1)}function sn(e,t,n,r){this.h=+e,this.s=+t,this.l=+n,this.opacity=+r}Vs(sn,vh,Ol(co,{brighter(e){return e=e==null?hi:Math.pow(hi,e),new sn(this.h,this.s,this.l*e,this.opacity)},darker(e){return e=e==null?fo:Math.pow(fo,e),new sn(this.h,this.s,this.l*e,this.opacity)},rgb(){var e=this.h%360+(this.h<0)*360,t=isNaN(e)||isNaN(this.s)?0:this.s,n=this.l,r=n+(n<.5?n:1-n)*t,o=2*n-r;return new At(As(e>=240?e-240:e+120,o,r),As(e,o,r),As(e<120?e+240:e-120,o,r),this.opacity)},clamp(){return new sn(Wl(this.h),mi(this.s),mi(this.l),pi(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&0<=this.l&&this.l<=1&&0<=this.opacity&&this.opacity<=1},formatHsl(){const e=pi(this.opacity);return`${e===1?"hsl(":"hsla("}${Wl(this.h)}, ${mi(this.s)*100}%, ${mi(this.l)*100}%${e===1?")":`, ${e})`}`}}));function Wl(e){return e=(e||0)%360,e<0?e+360:e}function mi(e){return Math.max(0,Math.min(1,e||0))}function As(e,t,n){return(e<60?t+(n-t)*e/60:e<180?n:e<240?t+(n-t)*(240-e)/60:t)*255}const Kl=e=>()=>e;function ph(e,t){return function(n){return e+n*t}}function mh(e,t,n){return e=Math.pow(e,n),t=Math.pow(t,n)-e,n=1/n,function(r){return Math.pow(e+r*t,n)}}function yh(e){return(e=+e)==1?ql:function(t,n){return n-t?mh(t,n,e):Kl(isNaN(t)?n:t)}}function ql(e,t){var n=t-e;return n?ph(e,n):Kl(isNaN(e)?t:e)}const Gl=function e(t){var n=yh(t);function r(o,i){var s=n((o=Ds(o)).r,(i=Ds(i)).r),a=n(o.g,i.g),l=n(o.b,i.b),u=ql(o.opacity,i.opacity);return function(c){return o.r=s(c),o.g=a(c),o.b=l(c),o.opacity=u(c),o+""}}return r.gamma=e,r}(1);function jn(e,t){return e=+e,t=+t,function(n){return e*(1-n)+t*n}}var Ls=/[-+]?(?:\d+\.?\d*|\.?\d+)(?:[eE][-+]?\d+)?/g,Os=new RegExp(Ls.source,"g");function wh(e){return function(){return e}}function _h(e){return function(t){return e(t)+""}}function xh(e,t){var n=Ls.lastIndex=Os.lastIndex=0,r,o,i,s=-1,a=[],l=[];for(e=e+"",t=t+"";(r=Ls.exec(e))&&(o=Os.exec(t));)(i=o.index)>n&&(i=t.slice(n,i),a[s]?a[s]+=i:a[++s]=i),(r=r[0])===(o=o[0])?a[s]?a[s]+=o:a[++s]=o:(a[++s]=null,l.push({i:s,x:jn(r,o)})),n=Os.lastIndex;return n<t.length&&(i=t.slice(n),a[s]?a[s]+=i:a[++s]=i),a.length<2?l[0]?_h(l[0].x):wh(t):(t=l.length,function(u){for(var c=0,f;c<t;++c)a[(f=l[c]).i]=f.x(u);return a.join("")})}var Ul=180/Math.PI,Is={translateX:0,translateY:0,rotate:0,skewX:0,scaleX:1,scaleY:1};function jl(e,t,n,r,o,i){var s,a,l;return(s=Math.sqrt(e*e+t*t))&&(e/=s,t/=s),(l=e*n+t*r)&&(n-=e*l,r-=t*l),(a=Math.sqrt(n*n+r*r))&&(n/=a,r/=a,l/=a),e*r<t*n&&(e=-e,t=-t,l=-l,s=-s),{translateX:o,translateY:i,rotate:Math.atan2(t,e)*Ul,skewX:Math.atan(l)*Ul,scaleX:s,scaleY:a}}var yi;function bh(e){const t=new(typeof DOMMatrix=="function"?DOMMatrix:WebKitCSSMatrix)(e+"");return t.isIdentity?Is:jl(t.a,t.b,t.c,t.d,t.e,t.f)}function Ch(e){return e==null||(yi||(yi=document.createElementNS("http://www.w3.org/2000/svg","g")),yi.setAttribute("transform",e),!(e=yi.transform.baseVal.consolidate()))?Is:(e=e.matrix,jl(e.a,e.b,e.c,e.d,e.e,e.f))}function Jl(e,t,n,r){function o(u){return u.length?u.pop()+" ":""}function i(u,c,f,d,g,p){if(u!==f||c!==d){var x=g.push("translate(",null,t,null,n);p.push({i:x-4,x:jn(u,f)},{i:x-2,x:jn(c,d)})}else(f||d)&&g.push("translate("+f+t+d+n)}function s(u,c,f,d){u!==c?(u-c>180?c+=360:c-u>180&&(u+=360),d.push({i:f.push(o(f)+"rotate(",null,r)-2,x:jn(u,c)})):c&&f.push(o(f)+"rotate("+c+r)}function a(u,c,f,d){u!==c?d.push({i:f.push(o(f)+"skewX(",null,r)-2,x:jn(u,c)}):c&&f.push(o(f)+"skewX("+c+r)}function l(u,c,f,d,g,p){if(u!==f||c!==d){var x=g.push(o(g)+"scale(",null,",",null,")");p.push({i:x-4,x:jn(u,f)},{i:x-2,x:jn(c,d)})}else(f!==1||d!==1)&&g.push(o(g)+"scale("+f+","+d+")")}return function(u,c){var f=[],d=[];return u=e(u),c=e(c),i(u.translateX,u.translateY,c.translateX,c.translateY,f,d),s(u.rotate,c.rotate,f,d),a(u.skewX,c.skewX,f,d),l(u.scaleX,u.scaleY,c.scaleX,c.scaleY,f,d),u=c=null,function(g){for(var p=-1,x=d.length,C;++p<x;)f[(C=d[p]).i]=C.x(g);return f.join("")}}}var kh=Jl(bh,"px, ","px)","deg)"),$h=Jl(Ch,", ",")",")"),Eh=1e-12;function Ql(e){return((e=Math.exp(e))+1/e)/2}function Sh(e){return((e=Math.exp(e))-1/e)/2}function Ph(e){return((e=Math.exp(2*e))-1)/(e+1)}const Nh=function e(t,n,r){function o(i,s){var a=i[0],l=i[1],u=i[2],c=s[0],f=s[1],d=s[2],g=c-a,p=f-l,x=g*g+p*p,C,$;if(x<Eh)$=Math.log(d/u)/t,C=function(E){return[a+E*g,l+E*p,u*Math.exp(t*E*$)]};else{var m=Math.sqrt(x),_=(d*d-u*u+r*x)/(2*u*n*m),v=(d*d-u*u-r*x)/(2*d*n*m),b=Math.log(Math.sqrt(_*_+1)-_),N=Math.log(Math.sqrt(v*v+1)-v);$=(N-b)/t,C=function(E){var T=E*$,D=Ql(b),V=u/(n*m)*(D*Ph(t*T+b)-Sh(b));return[a+V*g,l+V*p,u*D/Ql(t*T+b)]}}return C.duration=$*1e3*t/Math.SQRT2,C}return o.rho=function(i){var s=Math.max(.001,+i),a=s*s,l=a*a;return e(s,a,l)},o}(Math.SQRT2,2,4);var Vr=0,vo=0,po=0,eu=1e3,wi,mo,_i=0,gr=0,xi=0,yo=typeof performance=="object"&&performance.now?performance:Date,tu=typeof window=="object"&&window.requestAnimationFrame?window.requestAnimationFrame.bind(window):function(e){setTimeout(e,17)};function zs(){return gr||(tu(Th),gr=yo.now()+xi)}function Th(){gr=0}function bi(){this._call=this._time=this._next=null}bi.prototype=nu.prototype={constructor:bi,restart:function(e,t,n){if(typeof e!="function")throw new TypeError("callback is not a function");n=(n==null?zs():+n)+(t==null?0:+t),!this._next&&mo!==this&&(mo?mo._next=this:wi=this,mo=this),this._call=e,this._time=n,Rs()},stop:function(){this._call&&(this._call=null,this._time=1/0,Rs())}};function nu(e,t,n){var r=new bi;return r.restart(e,t,n),r}function Mh(){zs(),++Vr;for(var e=wi,t;e;)(t=gr-e._time)>=0&&e._call.call(void 0,t),e=e._next;--Vr}function ru(){gr=(_i=yo.now())+xi,Vr=vo=0;try{Mh()}finally{Vr=0,Vh(),gr=0}}function Hh(){var e=yo.now(),t=e-_i;t>eu&&(xi-=t,_i=e)}function Vh(){for(var e,t=wi,n,r=1/0;t;)t._call?(r>t._time&&(r=t._time),e=t,t=t._next):(n=t._next,t._next=null,t=e?e._next=n:wi=n);mo=e,Rs(r)}function Rs(e){if(!Vr){vo&&(vo=clearTimeout(vo));var t=e-gr;t>24?(e<1/0&&(vo=setTimeout(ru,e-yo.now()-xi)),po&&(po=clearInterval(po))):(po||(_i=yo.now(),po=setInterval(Hh,eu)),Vr=1,tu(ru))}}function ou(e,t,n){var r=new bi;return t=t==null?0:+t,r.restart(o=>{r.stop(),e(o+t)},t,n),r}var Dh=ui("start","end","cancel","interrupt"),Ah=[],iu=0,su=1,Bs=2,Ci=3,au=4,Ys=5,ki=6;function $i(e,t,n,r,o,i){var s=e.__transition;if(!s)e.__transition={};else if(n in s)return;Lh(e,n,{name:t,index:r,group:o,on:Dh,tween:Ah,time:i.time,delay:i.delay,duration:i.duration,ease:i.ease,timer:null,state:iu})}function Zs(e,t){var n=an(e,t);if(n.state>iu)throw new Error("too late; already scheduled");return n}function xn(e,t){var n=an(e,t);if(n.state>Ci)throw new Error("too late; already running");return n}function an(e,t){var n=e.__transition;if(!n||!(n=n[t]))throw new Error("transition not found");return n}function Lh(e,t,n){var r=e.__transition,o;r[t]=n,n.timer=nu(i,0,n.time);function i(u){n.state=su,n.timer.restart(s,n.delay,n.time),n.delay<=u&&s(u-n.delay)}function s(u){var c,f,d,g;if(n.state!==su)return l();for(c in r)if(g=r[c],g.name===n.name){if(g.state===Ci)return ou(s);g.state===au?(g.state=ki,g.timer.stop(),g.on.call("interrupt",e,e.__data__,g.index,g.group),delete r[c]):+c<t&&(g.state=ki,g.timer.stop(),g.on.call("cancel",e,e.__data__,g.index,g.group),delete r[c])}if(ou(function(){n.state===Ci&&(n.state=au,n.timer.restart(a,n.delay,n.time),a(u))}),n.state=Bs,n.on.call("start",e,e.__data__,n.index,n.group),n.state===Bs){for(n.state=Ci,o=new Array(d=n.tween.length),c=0,f=-1;c<d;++c)(g=n.tween[c].value.call(e,e.__data__,n.index,n.group))&&(o[++f]=g);o.length=f+1}}function a(u){for(var c=u<n.duration?n.ease.call(null,u/n.duration):(n.timer.restart(l),n.state=Ys,1),f=-1,d=o.length;++f<d;)o[f].call(e,c);n.state===Ys&&(n.on.call("end",e,e.__data__,n.index,n.group),l())}function l(){n.state=ki,n.timer.stop(),delete r[t];for(var u in r)return;delete e.__transition}}function Ei(e,t){var n=e.__transition,r,o,i=!0,s;if(n){t=t==null?null:t+"";for(s in n){if((r=n[s]).name!==t){i=!1;continue}o=r.state>Bs&&r.state<Ys,r.state=ki,r.timer.stop(),r.on.call(o?"interrupt":"cancel",e,e.__data__,r.index,r.group),delete n[s]}i&&delete e.__transition}}function Oh(e){return this.each(function(){Ei(this,e)})}function Ih(e,t){var n,r;return function(){var o=xn(this,e),i=o.tween;if(i!==n){r=n=i;for(var s=0,a=r.length;s<a;++s)if(r[s].name===t){r=r.slice(),r.splice(s,1);break}}o.tween=r}}function zh(e,t,n){var r,o;if(typeof n!="function")throw new Error;return function(){var i=xn(this,e),s=i.tween;if(s!==r){o=(r=s).slice();for(var a={name:t,value:n},l=0,u=o.length;l<u;++l)if(o[l].name===t){o[l]=a;break}l===u&&o.push(a)}i.tween=o}}function Rh(e,t){var n=this._id;if(e+="",arguments.length<2){for(var r=an(this.node(),n).tween,o=0,i=r.length,s;o<i;++o)if((s=r[o]).name===e)return s.value;return null}return this.each((t==null?Ih:zh)(n,e,t))}function Xs(e,t,n){var r=e._id;return e.each(function(){var o=xn(this,r);(o.value||(o.value={}))[t]=n.apply(this,arguments)}),function(o){return an(o,r).value[t]}}function lu(e,t){var n;return(typeof t=="number"?jn:t instanceof ho?Gl:(n=ho(t))?(t=n,Gl):xh)(e,t)}function Bh(e){return function(){this.removeAttribute(e)}}function Yh(e){return function(){this.removeAttributeNS(e.space,e.local)}}function Zh(e,t,n){var r,o=n+"",i;return function(){var s=this.getAttribute(e);return s===o?null:s===r?i:i=t(r=s,n)}}function Xh(e,t,n){var r,o=n+"",i;return function(){var s=this.getAttributeNS(e.space,e.local);return s===o?null:s===r?i:i=t(r=s,n)}}function Fh(e,t,n){var r,o,i;return function(){var s,a=n(this),l;return a==null?void this.removeAttribute(e):(s=this.getAttribute(e),l=a+"",s===l?null:s===r&&l===o?i:(o=l,i=t(r=s,a)))}}function Wh(e,t,n){var r,o,i;return function(){var s,a=n(this),l;return a==null?void this.removeAttributeNS(e.space,e.local):(s=this.getAttributeNS(e.space,e.local),l=a+"",s===l?null:s===r&&l===o?i:(o=l,i=t(r=s,a)))}}function Kh(e,t){var n=di(e),r=n==="transform"?$h:lu;return this.attrTween(e,typeof t=="function"?(n.local?Wh:Fh)(n,r,Xs(this,"attr."+e,t)):t==null?(n.local?Yh:Bh)(n):(n.local?Xh:Zh)(n,r,t))}function qh(e,t){return function(n){this.setAttribute(e,t.call(this,n))}}function Gh(e,t){return function(n){this.setAttributeNS(e.space,e.local,t.call(this,n))}}function Uh(e,t){var n,r;function o(){var i=t.apply(this,arguments);return i!==r&&(n=(r=i)&&Gh(e,i)),n}return o._value=t,o}function jh(e,t){var n,r;function o(){var i=t.apply(this,arguments);return i!==r&&(n=(r=i)&&qh(e,i)),n}return o._value=t,o}function Jh(e,t){var n="attr."+e;if(arguments.length<2)return(n=this.tween(n))&&n._value;if(t==null)return this.tween(n,null);if(typeof t!="function")throw new Error;var r=di(e);return this.tween(n,(r.local?Uh:jh)(r,t))}function Qh(e,t){return function(){Zs(this,e).delay=+t.apply(this,arguments)}}function ev(e,t){return t=+t,function(){Zs(this,e).delay=t}}function tv(e){var t=this._id;return arguments.length?this.each((typeof e=="function"?Qh:ev)(t,e)):an(this.node(),t).delay}function nv(e,t){return function(){xn(this,e).duration=+t.apply(this,arguments)}}function rv(e,t){return t=+t,function(){xn(this,e).duration=t}}function ov(e){var t=this._id;return arguments.length?this.each((typeof e=="function"?nv:rv)(t,e)):an(this.node(),t).duration}function iv(e,t){if(typeof t!="function")throw new Error;return function(){xn(this,e).ease=t}}function sv(e){var t=this._id;return arguments.length?this.each(iv(t,e)):an(this.node(),t).ease}function av(e,t){return function(){var n=t.apply(this,arguments);if(typeof n!="function")throw new Error;xn(this,e).ease=n}}function lv(e){if(typeof e!="function")throw new Error;return this.each(av(this._id,e))}function uv(e){typeof e!="function"&&(e=$l(e));for(var t=this._groups,n=t.length,r=new Array(n),o=0;o<n;++o)for(var i=t[o],s=i.length,a=r[o]=[],l,u=0;u<s;++u)(l=i[u])&&e.call(l,l.__data__,u,i)&&a.push(l);return new Ln(r,this._parents,this._name,this._id)}function cv(e){if(e._id!==this._id)throw new Error;for(var t=this._groups,n=e._groups,r=t.length,o=n.length,i=Math.min(r,o),s=new Array(r),a=0;a<i;++a)for(var l=t[a],u=n[a],c=l.length,f=s[a]=new Array(c),d,g=0;g<c;++g)(d=l[g]||u[g])&&(f[g]=d);for(;a<r;++a)s[a]=t[a];return new Ln(s,this._parents,this._name,this._id)}function dv(e){return(e+"").trim().split(/^|\s+/).every(function(t){var n=t.indexOf(".");return n>=0&&(t=t.slice(0,n)),!t||t==="start"})}function fv(e,t,n){var r,o,i=dv(t)?Zs:xn;return function(){var s=i(this,e),a=s.on;a!==r&&(o=(r=a).copy()).on(t,n),s.on=o}}function gv(e,t){var n=this._id;return arguments.length<2?an(this.node(),n).on.on(e):this.each(fv(n,e,t))}function hv(e){return function(){var t=this.parentNode;for(var n in this.__transition)if(+n!==e)return;t&&t.removeChild(this)}}function vv(){return this.on("end.remove",hv(this._id))}function pv(e){var t=this._name,n=this._id;typeof e!="function"&&(e=Ns(e));for(var r=this._groups,o=r.length,i=new Array(o),s=0;s<o;++s)for(var a=r[s],l=a.length,u=i[s]=new Array(l),c,f,d=0;d<l;++d)(c=a[d])&&(f=e.call(c,c.__data__,d,a))&&("__data__"in c&&(f.__data__=c.__data__),u[d]=f,$i(u[d],t,n,d,u,an(c,n)));return new Ln(i,this._parents,t,n)}function mv(e){var t=this._name,n=this._id;typeof e!="function"&&(e=kl(e));for(var r=this._groups,o=r.length,i=[],s=[],a=0;a<o;++a)for(var l=r[a],u=l.length,c,f=0;f<u;++f)if(c=l[f]){for(var d=e.call(c,c.__data__,f,l),g,p=an(c,n),x=0,C=d.length;x<C;++x)(g=d[x])&&$i(g,t,n,x,d,p);i.push(d),s.push(c)}return new Ln(i,s,t,n)}var yv=lo.prototype.constructor;function wv(){return new yv(this._groups,this._parents)}function _v(e,t){var n,r,o;return function(){var i=Tr(this,e),s=(this.style.removeProperty(e),Tr(this,e));return i===s?null:i===n&&s===r?o:o=t(n=i,r=s)}}function uu(e){return function(){this.style.removeProperty(e)}}function xv(e,t,n){var r,o=n+"",i;return function(){var s=Tr(this,e);return s===o?null:s===r?i:i=t(r=s,n)}}function bv(e,t,n){var r,o,i;return function(){var s=Tr(this,e),a=n(this),l=a+"";return a==null&&(l=a=(this.style.removeProperty(e),Tr(this,e))),s===l?null:s===r&&l===o?i:(o=l,i=t(r=s,a))}}function Cv(e,t){var n,r,o,i="style."+t,s="end."+i,a;return function(){var l=xn(this,e),u=l.on,c=l.value[i]==null?a||(a=uu(t)):void 0;(u!==n||o!==c)&&(r=(n=u).copy()).on(s,o=c),l.on=r}}function kv(e,t,n){var r=(e+="")=="transform"?kh:lu;return t==null?this.styleTween(e,_v(e,r)).on("end.style."+e,uu(e)):typeof t=="function"?this.styleTween(e,bv(e,r,Xs(this,"style."+e,t))).each(Cv(this._id,e)):this.styleTween(e,xv(e,r,t),n).on("end.style."+e,null)}function $v(e,t,n){return function(r){this.style.setProperty(e,t.call(this,r),n)}}function Ev(e,t,n){var r,o;function i(){var s=t.apply(this,arguments);return s!==o&&(r=(o=s)&&$v(e,s,n)),r}return i._value=t,i}function Sv(e,t,n){var r="style."+(e+="");if(arguments.length<2)return(r=this.tween(r))&&r._value;if(t==null)return this.tween(r,null);if(typeof t!="function")throw new Error;return this.tween(r,Ev(e,t,n??""))}function Pv(e){return function(){this.textContent=e}}function Nv(e){return function(){var t=e(this);this.textContent=t??""}}function Tv(e){return this.tween("text",typeof e=="function"?Nv(Xs(this,"text",e)):Pv(e==null?"":e+""))}function Mv(e){return function(t){this.textContent=e.call(this,t)}}function Hv(e){var t,n;function r(){var o=e.apply(this,arguments);return o!==n&&(t=(n=o)&&Mv(o)),t}return r._value=e,r}function Vv(e){var t="text";if(arguments.length<1)return(t=this.tween(t))&&t._value;if(e==null)return this.tween(t,null);if(typeof e!="function")throw new Error;return this.tween(t,Hv(e))}function Dv(){for(var e=this._name,t=this._id,n=cu(),r=this._groups,o=r.length,i=0;i<o;++i)for(var s=r[i],a=s.length,l,u=0;u<a;++u)if(l=s[u]){var c=an(l,t);$i(l,e,n,u,s,{time:c.time+c.delay+c.duration,delay:0,duration:c.duration,ease:c.ease})}return new Ln(r,this._parents,e,n)}function Av(){var e,t,n=this,r=n._id,o=n.size();return new Promise(function(i,s){var a={value:s},l={value:function(){--o===0&&i()}};n.each(function(){var u=xn(this,r),c=u.on;c!==e&&(t=(e=c).copy(),t._.cancel.push(a),t._.interrupt.push(a),t._.end.push(l)),u.on=t}),o===0&&i()})}var Lv=0;function Ln(e,t,n,r){this._groups=e,this._parents=t,this._name=n,this._id=r}function cu(){return++Lv}var On=lo.prototype;Ln.prototype={constructor:Ln,select:pv,selectAll:mv,selectChild:On.selectChild,selectChildren:On.selectChildren,filter:uv,merge:cv,selection:wv,transition:Dv,call:On.call,nodes:On.nodes,node:On.node,size:On.size,empty:On.empty,each:On.each,on:gv,attr:Kh,attrTween:Jh,style:kv,styleTween:Sv,text:Tv,textTween:Vv,remove:vv,tween:Rh,delay:tv,duration:ov,ease:sv,easeVarying:lv,end:Av,[Symbol.iterator]:On[Symbol.iterator]};function Ov(e){return((e*=2)<=1?e*e*e:(e-=2)*e*e+2)/2}var Iv={time:null,delay:0,duration:250,ease:Ov};function zv(e,t){for(var n;!(n=e.__transition)||!(n=n[t]);)if(!(e=e.parentNode))throw new Error(`transition ${t} not found`);return n}function Rv(e){var t,n;e instanceof Ln?(t=e._id,e=e._name):(t=cu(),(n=Iv).time=zs(),e=e==null?null:e+"");for(var r=this._groups,o=r.length,i=0;i<o;++i)for(var s=r[i],a=s.length,l,u=0;u<a;++u)(l=s[u])&&$i(l,e,t,u,s,n||zv(l,t));return new Ln(r,this._parents,e,t)}lo.prototype.interrupt=Oh,lo.prototype.transition=Rv;const Si=e=>()=>e;function Bv(e,{sourceEvent:t,target:n,transform:r,dispatch:o}){Object.defineProperties(this,{type:{value:e,enumerable:!0,configurable:!0},sourceEvent:{value:t,enumerable:!0,configurable:!0},target:{value:n,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:o}})}function In(e,t,n){this.k=e,this.x=t,this.y=n}In.prototype={constructor:In,scale:function(e){return e===1?this:new In(this.k*e,this.x,this.y)},translate:function(e,t){return e===0&t===0?this:new In(this.k,this.x+this.k*e,this.y+this.k*t)},apply:function(e){return[e[0]*this.k+this.x,e[1]*this.k+this.y]},applyX:function(e){return e*this.k+this.x},applyY:function(e){return e*this.k+this.y},invert:function(e){return[(e[0]-this.x)/this.k,(e[1]-this.y)/this.k]},invertX:function(e){return(e-this.x)/this.k},invertY:function(e){return(e-this.y)/this.k},rescaleX:function(e){return e.copy().domain(e.range().map(this.invertX,this).map(e.invert,e))},rescaleY:function(e){return e.copy().domain(e.range().map(this.invertY,this).map(e.invert,e))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Pi=new In(1,0,0);du.prototype=In.prototype;function du(e){for(;!e.__zoom;)if(!(e=e.parentNode))return Pi;return e.__zoom}function Fs(e){e.stopImmediatePropagation()}function wo(e){e.preventDefault(),e.stopImmediatePropagation()}function Yv(e){return(!e.ctrlKey||e.type==="wheel")&&!e.button}function Zv(){var e=this;return e instanceof SVGElement?(e=e.ownerSVGElement||e,e.hasAttribute("viewBox")?(e=e.viewBox.baseVal,[[e.x,e.y],[e.x+e.width,e.y+e.height]]):[[0,0],[e.width.baseVal.value,e.height.baseVal.value]]):[[0,0],[e.clientWidth,e.clientHeight]]}function fu(){return this.__zoom||Pi}function Xv(e){return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*(e.ctrlKey?10:1)}function Fv(){return navigator.maxTouchPoints||"ontouchstart"in this}function Wv(e,t,n){var r=e.invertX(t[0][0])-n[0][0],o=e.invertX(t[1][0])-n[1][0],i=e.invertY(t[0][1])-n[0][1],s=e.invertY(t[1][1])-n[1][1];return e.translate(o>r?(r+o)/2:Math.min(0,r)||Math.max(0,o),s>i?(i+s)/2:Math.min(0,i)||Math.max(0,s))}function gu(){var e=Yv,t=Zv,n=Wv,r=Xv,o=Fv,i=[0,1/0],s=[[-1/0,-1/0],[1/0,1/0]],a=250,l=Nh,u=ui("start","zoom","end"),c,f,d,g=500,p=150,x=0,C=10;function $(S){S.property("__zoom",fu).on("wheel.zoom",T,{passive:!1}).on("mousedown.zoom",D).on("dblclick.zoom",V).filter(o).on("touchstart.zoom",A).on("touchmove.zoom",O).on("touchend.zoom touchcancel.zoom",R).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}$.transform=function(S,M,k,P){var H=S.selection?S.selection():S;H.property("__zoom",fu),S!==H?b(S,M,k,P):H.interrupt().each(function(){N(this,arguments).event(P).start().zoom(null,typeof M=="function"?M.apply(this,arguments):M).end()})},$.scaleBy=function(S,M,k,P){$.scaleTo(S,function(){var H=this.__zoom.k,I=typeof M=="function"?M.apply(this,arguments):M;return H*I},k,P)},$.scaleTo=function(S,M,k,P){$.transform(S,function(){var H=t.apply(this,arguments),I=this.__zoom,B=k==null?v(H):typeof k=="function"?k.apply(this,arguments):k,F=I.invert(B),K=typeof M=="function"?M.apply(this,arguments):M;return n(_(m(I,K),B,F),H,s)},k,P)},$.translateBy=function(S,M,k,P){$.transform(S,function(){return n(this.__zoom.translate(typeof M=="function"?M.apply(this,arguments):M,typeof k=="function"?k.apply(this,arguments):k),t.apply(this,arguments),s)},null,P)},$.translateTo=function(S,M,k,P,H){$.transform(S,function(){var I=t.apply(this,arguments),B=this.__zoom,F=P==null?v(I):typeof P=="function"?P.apply(this,arguments):P;return n(Pi.translate(F[0],F[1]).scale(B.k).translate(typeof M=="function"?-M.apply(this,arguments):-M,typeof k=="function"?-k.apply(this,arguments):-k),I,s)},P,H)};function m(S,M){return M=Math.max(i[0],Math.min(i[1],M)),M===S.k?S:new In(M,S.x,S.y)}function _(S,M,k){var P=M[0]-k[0]*S.k,H=M[1]-k[1]*S.k;return P===S.x&&H===S.y?S:new In(S.k,P,H)}function v(S){return[(+S[0][0]+ +S[1][0])/2,(+S[0][1]+ +S[1][1])/2]}function b(S,M,k,P){S.on("start.zoom",function(){N(this,arguments).event(P).start()}).on("interrupt.zoom end.zoom",function(){N(this,arguments).event(P).end()}).tween("zoom",function(){var H=this,I=arguments,B=N(H,I).event(P),F=t.apply(H,I),K=k==null?v(F):typeof k=="function"?k.apply(H,I):k,se=Math.max(F[1][0]-F[0][0],F[1][1]-F[0][1]),ee=H.__zoom,W=typeof M=="function"?M.apply(H,I):M,fe=l(ee.invert(K).concat(se/ee.k),W.invert(K).concat(se/W.k));return function(me){if(me===1)me=W;else{var Ce=fe(me),he=se/Ce[2];me=new In(he,K[0]-Ce[0]*he,K[1]-Ce[1]*he)}B.zoom(null,me)}})}function N(S,M,k){return!k&&S.__zooming||new E(S,M)}function E(S,M){this.that=S,this.args=M,this.active=0,this.sourceEvent=null,this.extent=t.apply(S,M),this.taps=0}E.prototype={event:function(S){return S&&(this.sourceEvent=S),this},start:function(){return++this.active===1&&(this.that.__zooming=this,this.emit("start")),this},zoom:function(S,M){return this.mouse&&S!=="mouse"&&(this.mouse[1]=M.invert(this.mouse[0])),this.touch0&&S!=="touch"&&(this.touch0[1]=M.invert(this.touch0[0])),this.touch1&&S!=="touch"&&(this.touch1[1]=M.invert(this.touch1[0])),this.that.__zoom=M,this.emit("zoom"),this},end:function(){return--this.active===0&&(delete this.that.__zooming,this.emit("end")),this},emit:function(S){var M=Ut(this.that).datum();u.call(S,this.that,new Bv(S,{sourceEvent:this.sourceEvent,target:$,transform:this.that.__zoom,dispatch:u}),M)}};function T(S,...M){if(!e.apply(this,arguments))return;var k=N(this,M).event(S),P=this.__zoom,H=Math.max(i[0],Math.min(i[1],P.k*Math.pow(2,r.apply(this,arguments)))),I=on(S);if(k.wheel)(k.mouse[0][0]!==I[0]||k.mouse[0][1]!==I[1])&&(k.mouse[1]=P.invert(k.mouse[0]=I)),clearTimeout(k.wheel);else{if(P.k===H)return;k.mouse=[I,P.invert(I)],Ei(this),k.start()}wo(S),k.wheel=setTimeout(B,p),k.zoom("mouse",n(_(m(P,H),k.mouse[0],k.mouse[1]),k.extent,s));function B(){k.wheel=null,k.end()}}function D(S,...M){if(d||!e.apply(this,arguments))return;var k=S.currentTarget,P=N(this,M,!0).event(S),H=Ut(S.view).on("mousemove.zoom",K,!0).on("mouseup.zoom",se,!0),I=on(S,k),B=S.clientX,F=S.clientY;Al(S.view),Fs(S),P.mouse=[I,this.__zoom.invert(I)],Ei(this),P.start();function K(ee){if(wo(ee),!P.moved){var W=ee.clientX-B,fe=ee.clientY-F;P.moved=W*W+fe*fe>x}P.event(ee).zoom("mouse",n(_(P.that.__zoom,P.mouse[0]=on(ee,k),P.mouse[1]),P.extent,s))}function se(ee){H.on("mousemove.zoom mouseup.zoom",null),Ll(ee.view,P.moved),wo(ee),P.event(ee).end()}}function V(S,...M){if(e.apply(this,arguments)){var k=this.__zoom,P=on(S.changedTouches?S.changedTouches[0]:S,this),H=k.invert(P),I=k.k*(S.shiftKey?.5:2),B=n(_(m(k,I),P,H),t.apply(this,M),s);wo(S),a>0?Ut(this).transition().duration(a).call(b,B,P,S):Ut(this).call($.transform,B,P,S)}}function A(S,...M){if(e.apply(this,arguments)){var k=S.touches,P=k.length,H=N(this,M,S.changedTouches.length===P).event(S),I,B,F,K;for(Fs(S),B=0;B<P;++B)F=k[B],K=on(F,this),K=[K,this.__zoom.invert(K),F.identifier],H.touch0?!H.touch1&&H.touch0[2]!==K[2]&&(H.touch1=K,H.taps=0):(H.touch0=K,I=!0,H.taps=1+!!c);c&&(c=clearTimeout(c)),I&&(H.taps<2&&(f=K[0],c=setTimeout(function(){c=null},g)),Ei(this),H.start())}}function O(S,...M){if(this.__zooming){var k=N(this,M).event(S),P=S.changedTouches,H=P.length,I,B,F,K;for(wo(S),I=0;I<H;++I)B=P[I],F=on(B,this),k.touch0&&k.touch0[2]===B.identifier?k.touch0[0]=F:k.touch1&&k.touch1[2]===B.identifier&&(k.touch1[0]=F);if(B=k.that.__zoom,k.touch1){var se=k.touch0[0],ee=k.touch0[1],W=k.touch1[0],fe=k.touch1[1],me=(me=W[0]-se[0])*me+(me=W[1]-se[1])*me,Ce=(Ce=fe[0]-ee[0])*Ce+(Ce=fe[1]-ee[1])*Ce;B=m(B,Math.sqrt(me/Ce)),F=[(se[0]+W[0])/2,(se[1]+W[1])/2],K=[(ee[0]+fe[0])/2,(ee[1]+fe[1])/2]}else if(k.touch0)F=k.touch0[0],K=k.touch0[1];else return;k.zoom("touch",n(_(B,F,K),k.extent,s))}}function R(S,...M){if(this.__zooming){var k=N(this,M).event(S),P=S.changedTouches,H=P.length,I,B;for(Fs(S),d&&clearTimeout(d),d=setTimeout(function(){d=null},g),I=0;I<H;++I)B=P[I],k.touch0&&k.touch0[2]===B.identifier?delete k.touch0:k.touch1&&k.touch1[2]===B.identifier&&delete k.touch1;if(k.touch1&&!k.touch0&&(k.touch0=k.touch1,delete k.touch1),k.touch0)k.touch0[1]=this.__zoom.invert(k.touch0[0]);else if(k.end(),k.taps===2&&(B=on(B,this),Math.hypot(f[0]-B[0],f[1]-B[1])<C)){var F=Ut(this).on("dblclick.zoom");F&&F.apply(this,arguments)}}}return $.wheelDelta=function(S){return arguments.length?(r=typeof S=="function"?S:Si(+S),$):r},$.filter=function(S){return arguments.length?(e=typeof S=="function"?S:Si(!!S),$):e},$.touchable=function(S){return arguments.length?(o=typeof S=="function"?S:Si(!!S),$):o},$.extent=function(S){return arguments.length?(t=typeof S=="function"?S:Si([[+S[0][0],+S[0][1]],[+S[1][0],+S[1][1]]]),$):t},$.scaleExtent=function(S){return arguments.length?(i[0]=+S[0],i[1]=+S[1],$):[i[0],i[1]]},$.translateExtent=function(S){return arguments.length?(s[0][0]=+S[0][0],s[1][0]=+S[1][0],s[0][1]=+S[0][1],s[1][1]=+S[1][1],$):[[s[0][0],s[0][1]],[s[1][0],s[1][1]]]},$.constrain=function(S){return arguments.length?(n=S,$):n},$.duration=function(S){return arguments.length?(a=+S,$):a},$.interpolate=function(S){return arguments.length?(l=S,$):l},$.on=function(){var S=u.on.apply(u,arguments);return S===u?$:S},$.clickDistance=function(S){return arguments.length?(x=(S=+S)*S,$):Math.sqrt(x)},$.tapDistance=function(S){return arguments.length?(C=+S,$):C},$}const Dr={error001:()=>"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:e=>`Node type "${e}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:e=>`The old edge with id=${e} does not exist.`,error009:e=>`Marker type "${e}" doesn't exist.`,error008:(e,{id:t,sourceHandle:n,targetHandle:r})=>`Couldn't create edge for ${e} handle id: "${e==="source"?n:r}", edge id: ${t}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:e=>`Edge type "${e}" not found. Using fallback type "default".`,error012:e=>`Node with id "${e}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(e="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${e}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},Ni=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]];var hr;(function(e){e.Strict="strict",e.Loose="loose"})(hr||(hr={}));var Jn;(function(e){e.Free="free",e.Vertical="vertical",e.Horizontal="horizontal"})(Jn||(Jn={}));var Ti;(function(e){e.Partial="partial",e.Full="full"})(Ti||(Ti={}));const Ws={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null};var Ar;(function(e){e.Bezier="default",e.Straight="straight",e.Step="step",e.SmoothStep="smoothstep",e.SimpleBezier="simplebezier"})(Ar||(Ar={}));var _o;(function(e){e.Arrow="arrow",e.ArrowClosed="arrowclosed"})(_o||(_o={}));var $e;(function(e){e.Left="left",e.Top="top",e.Right="right",e.Bottom="bottom"})($e||($e={}));const hu={[$e.Left]:$e.Right,[$e.Right]:$e.Left,[$e.Top]:$e.Bottom,[$e.Bottom]:$e.Top};function Kv(e,t){if(!e&&!t)return!0;if(!e||!t||e.size!==t.size)return!1;if(!e.size&&!t.size)return!0;for(const n of e.keys())if(!t.has(n))return!1;return!0}function vu(e,t,n){if(!n)return;const r=[];e.forEach((o,i)=>{t!=null&&t.has(i)||r.push(o)}),r.length&&n(r)}function qv(e){return e===null?null:e?"valid":"invalid"}const Gv=e=>"id"in e&&"source"in e&&"target"in e,Uv=e=>"id"in e&&"position"in e&&!("source"in e)&&!("target"in e),Ks=e=>"id"in e&&"internals"in e&&!("source"in e)&&!("target"in e),xo=(e,t=[0,0])=>{const{width:n,height:r}=Qn(e),o=e.origin??t,i=n*o[0],s=r*o[1];return{x:e.position.x-i,y:e.position.y-s}},jv=(e,t={nodeOrigin:[0,0],nodeLookup:void 0})=>{if(e.length===0)return{x:0,y:0,width:0,height:0};const n=e.reduce((r,o)=>{const i=typeof o=="string";let s=!t.nodeLookup&&!i?o:void 0;t.nodeLookup&&(s=i?t.nodeLookup.get(o):Ks(o)?o:t.nodeLookup.get(o.id));const a=s?Vi(s,t.nodeOrigin):{x:0,y:0,x2:0,y2:0};return Mi(r,a)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return Hi(n)},bo=(e,t={})=>{if(e.size===0)return{x:0,y:0,width:0,height:0};let n={x:1/0,y:1/0,x2:-1/0,y2:-1/0};return e.forEach(r=>{if(t.filter===void 0||t.filter(r)){const o=Vi(r);n=Mi(n,o)}}),Hi(n)},pu=(e,t,[n,r,o]=[0,0,1],i=!1,s=!1)=>{const a={...ko(t,[n,r,o]),width:t.width/o,height:t.height/o},l=[];for(const u of e.values()){const{measured:c,selectable:f=!0,hidden:d=!1}=u;if(s&&!f||d)continue;const g=c.width??u.width??u.initialWidth??null,p=c.height??u.height??u.initialHeight??null,x=Co(a,Or(u)),C=(g??0)*(p??0),$=i&&x>0;(!u.internals.handleBounds||$||x>=C||u.dragging)&&l.push(u)}return l},qs=(e,t)=>{const n=new Set;return e.forEach(r=>{n.add(r.id)}),t.filter(r=>n.has(r.source)||n.has(r.target))};function mu(e,t){const n=new Map,r=t!=null&&t.nodes?new Set(t.nodes.map(o=>o.id)):null;return e.forEach(o=>{o.measured.width&&o.measured.height&&((t==null?void 0:t.includeHiddenNodes)||!o.hidden)&&(!r||r.has(o.id))&&n.set(o.id,o)}),n}async function yu({nodes:e,width:t,height:n,panZoom:r,minZoom:o,maxZoom:i},s){if(e.size===0)return Promise.resolve(!1);const a=bo(e),l=js(a,t,n,(s==null?void 0:s.minZoom)??o,(s==null?void 0:s.maxZoom)??i,(s==null?void 0:s.padding)??.1);return await r.setViewport(l,{duration:s==null?void 0:s.duration}),Promise.resolve(!0)}function Jv({nodeId:e,nextPosition:t,nodeLookup:n,nodeOrigin:r=[0,0],nodeExtent:o,onError:i}){const s=n.get(e),a=s.parentId?n.get(s.parentId):void 0,{x:l,y:u}=a?a.internals.positionAbsolute:{x:0,y:0},c=s.origin??r;let f=o;if(s.extent==="parent"&&!s.expandParent)if(!a)i==null||i("005",Dr.error005());else{const g=a.measured.width,p=a.measured.height;g&&p&&(f=[[l,u],[l+g,u+p]])}else a&&Ir(s.extent)&&(f=[[s.extent[0][0]+l,s.extent[0][1]+u],[s.extent[1][0]+l,s.extent[1][1]+u]]);const d=Ir(f)?vr(t,f,s.measured):t;return(s.measured.width===void 0||s.measured.height===void 0)&&(i==null||i("015",Dr.error015())),{position:{x:d.x-l+(s.measured.width??0)*c[0],y:d.y-u+(s.measured.height??0)*c[1]},positionAbsolute:d}}async function wu({nodesToRemove:e=[],edgesToRemove:t=[],nodes:n,edges:r,onBeforeDelete:o}){const i=new Set(e.map(d=>d.id)),s=[];for(const d of n){if(d.deletable===!1)continue;const g=i.has(d.id),p=!g&&d.parentId&&s.find(x=>x.id===d.parentId);(g||p)&&s.push(d)}const a=new Set(t.map(d=>d.id)),l=r.filter(d=>d.deletable!==!1),c=qs(s,l);for(const d of l)a.has(d.id)&&!c.find(p=>p.id===d.id)&&c.push(d);if(!o)return{edges:c,nodes:s};const f=await o({nodes:s,edges:c});return typeof f=="boolean"?f?{edges:c,nodes:s}:{edges:[],nodes:[]}:f}const Lr=(e,t=0,n=1)=>Math.min(Math.max(e,t),n),vr=(e={x:0,y:0},t,n)=>({x:Lr(e.x,t[0][0],t[1][0]-((n==null?void 0:n.width)??0)),y:Lr(e.y,t[0][1],t[1][1]-((n==null?void 0:n.height)??0))});function _u(e,t,n){const{width:r,height:o}=Qn(n),{x:i,y:s}=n.internals.positionAbsolute;return vr(e,[[i,s],[i+r,s+o]],t)}const xu=(e,t,n)=>e<t?Lr(Math.abs(e-t),1,t)/t:e>n?-Lr(Math.abs(e-n),1,t)/t:0,bu=(e,t,n=15,r=40)=>{const o=xu(e.x,r,t.width-r)*n,i=xu(e.y,r,t.height-r)*n;return[o,i]},Mi=(e,t)=>({x:Math.min(e.x,t.x),y:Math.min(e.y,t.y),x2:Math.max(e.x2,t.x2),y2:Math.max(e.y2,t.y2)}),Gs=({x:e,y:t,width:n,height:r})=>({x:e,y:t,x2:e+n,y2:t+r}),Hi=({x:e,y:t,x2:n,y2:r})=>({x:e,y:t,width:n-e,height:r-t}),Or=(e,t=[0,0])=>{var o,i;const{x:n,y:r}=Ks(e)?e.internals.positionAbsolute:xo(e,t);return{x:n,y:r,width:((o=e.measured)==null?void 0:o.width)??e.width??e.initialWidth??0,height:((i=e.measured)==null?void 0:i.height)??e.height??e.initialHeight??0}},Vi=(e,t=[0,0])=>{var o,i;const{x:n,y:r}=Ks(e)?e.internals.positionAbsolute:xo(e,t);return{x:n,y:r,x2:n+(((o=e.measured)==null?void 0:o.width)??e.width??e.initialWidth??0),y2:r+(((i=e.measured)==null?void 0:i.height)??e.height??e.initialHeight??0)}},Cu=(e,t)=>Hi(Mi(Gs(e),Gs(t))),Co=(e,t)=>{const n=Math.max(0,Math.min(e.x+e.width,t.x+t.width)-Math.max(e.x,t.x)),r=Math.max(0,Math.min(e.y+e.height,t.y+t.height)-Math.max(e.y,t.y));return Math.ceil(n*r)},ku=e=>zn(e.width)&&zn(e.height)&&zn(e.x)&&zn(e.y),zn=e=>!isNaN(e)&&isFinite(e),Qv=(e,t)=>{},Us=(e,t=[1,1])=>({x:t[0]*Math.round(e.x/t[0]),y:t[1]*Math.round(e.y/t[1])}),ko=({x:e,y:t},[n,r,o],i=!1,s=[1,1])=>{const a={x:(e-n)/o,y:(t-r)/o};return i?Us(a,s):a},$u=({x:e,y:t},[n,r,o])=>({x:e*o+n,y:t*o+r}),js=(e,t,n,r,o,i)=>{const s=t/(e.width*(1+i)),a=n/(e.height*(1+i)),l=Math.min(s,a),u=Lr(l,r,o),c=e.x+e.width/2,f=e.y+e.height/2,d=t/2-c*u,g=n/2-f*u;return{x:d,y:g,zoom:u}},Di=()=>{var e;return typeof navigator<"u"&&((e=navigator==null?void 0:navigator.userAgent)==null?void 0:e.indexOf("Mac"))>=0};function Ir(e){return e!==void 0&&e!=="parent"}function Qn(e){var t,n;return{width:((t=e.measured)==null?void 0:t.width)??e.width??e.initialWidth??0,height:((n=e.measured)==null?void 0:n.height)??e.height??e.initialHeight??0}}function Eu(e){var t,n;return(((t=e.measured)==null?void 0:t.width)??e.width??e.initialWidth)!==void 0&&(((n=e.measured)==null?void 0:n.height)??e.height??e.initialHeight)!==void 0}function e0(e,t={width:0,height:0},n,r,o){const i={...e},s=r.get(n);if(s){const a=s.origin||o;i.x+=s.internals.positionAbsolute.x-(t.width??0)*a[0],i.y+=s.internals.positionAbsolute.y-(t.height??0)*a[1]}return i}function Js(e,{snapGrid:t=[0,0],snapToGrid:n=!1,transform:r,containerBounds:o}){const{x:i,y:s}=Rn(e),a=ko({x:i-((o==null?void 0:o.left)??0),y:s-((o==null?void 0:o.top)??0)},r),{x:l,y:u}=n?Us(a,t):a;return{xSnapped:l,ySnapped:u,...a}}const Qs=e=>({width:e.offsetWidth,height:e.offsetHeight}),t0=e=>{var t;return((t=e==null?void 0:e.getRootNode)==null?void 0:t.call(e))||(window==null?void 0:window.document)},n0=["INPUT","SELECT","TEXTAREA"];function r0(e){var r,o;const t=((o=(r=e.composedPath)==null?void 0:r.call(e))==null?void 0:o[0])||e.target;return(t==null?void 0:t.nodeType)!==1?!1:n0.includes(t.nodeName)||t.hasAttribute("contenteditable")||!!t.closest(".nokey")}const Su=e=>"clientX"in e,Rn=(e,t)=>{var i,s;const n=Su(e),r=n?e.clientX:(i=e.touches)==null?void 0:i[0].clientX,o=n?e.clientY:(s=e.touches)==null?void 0:s[0].clientY;return{x:r-((t==null?void 0:t.left)??0),y:o-((t==null?void 0:t.top)??0)}},Pu=(e,t,n,r,o)=>{const i=t.querySelectorAll(`.${e}`);return!i||!i.length?null:Array.from(i).map(s=>{const a=s.getBoundingClientRect();return{id:s.getAttribute("data-handleid"),type:e,nodeId:o,position:s.getAttribute("data-handlepos"),x:(a.left-n.left)/r,y:(a.top-n.top)/r,...Qs(s)}})};function o0({sourceX:e,sourceY:t,targetX:n,targetY:r,sourceControlX:o,sourceControlY:i,targetControlX:s,targetControlY:a}){const l=e*.125+o*.375+s*.375+n*.125,u=t*.125+i*.375+a*.375+r*.125,c=Math.abs(l-e),f=Math.abs(u-t);return[l,u,c,f]}function Ai(e,t){return e>=0?.5*e:t*25*Math.sqrt(-e)}function Nu({pos:e,x1:t,y1:n,x2:r,y2:o,c:i}){switch(e){case $e.Left:return[t-Ai(t-r,i),n];case $e.Right:return[t+Ai(r-t,i),n];case $e.Top:return[t,n-Ai(n-o,i)];case $e.Bottom:return[t,n+Ai(o-n,i)]}}function Tu({sourceX:e,sourceY:t,sourcePosition:n=$e.Bottom,targetX:r,targetY:o,targetPosition:i=$e.Top,curvature:s=.25}){const[a,l]=Nu({pos:n,x1:e,y1:t,x2:r,y2:o,c:s}),[u,c]=Nu({pos:i,x1:r,y1:o,x2:e,y2:t,c:s}),[f,d,g,p]=o0({sourceX:e,sourceY:t,targetX:r,targetY:o,sourceControlX:a,sourceControlY:l,targetControlX:u,targetControlY:c});return[`M${e},${t} C${a},${l} ${u},${c} ${r},${o}`,f,d,g,p]}function Mu({sourceX:e,sourceY:t,targetX:n,targetY:r}){const o=Math.abs(n-e)/2,i=n<e?n+o:n-o,s=Math.abs(r-t)/2,a=r<t?r+s:r-s;return[i,a,o,s]}function i0({sourceNode:e,targetNode:t,selected:n=!1,zIndex:r=0,elevateOnSelect:o=!1}){if(!o)return r;const i=n||t.selected||e.selected,s=Math.max(e.internals.z||0,t.internals.z||0,1e3);return r+(i?s:0)}function s0({sourceNode:e,targetNode:t,width:n,height:r,transform:o}){const i=Mi(Vi(e),Vi(t));i.x===i.x2&&(i.x2+=1),i.y===i.y2&&(i.y2+=1);const s={x:-o[0]/o[2],y:-o[1]/o[2],width:n/o[2],height:r/o[2]};return Co(s,Hi(i))>0}const a0=({source:e,sourceHandle:t,target:n,targetHandle:r})=>`xy-edge__${e}${t||""}-${n}${r||""}`,l0=(e,t)=>t.some(n=>n.source===e.source&&n.target===e.target&&(n.sourceHandle===e.sourceHandle||!n.sourceHandle&&!e.sourceHandle)&&(n.targetHandle===e.targetHandle||!n.targetHandle&&!e.targetHandle)),u0=(e,t)=>{if(!e.source||!e.target)return t;let n;return Gv(e)?n={...e}:n={...e,id:a0(e)},l0(n,t)?t:(n.sourceHandle===null&&delete n.sourceHandle,n.targetHandle===null&&delete n.targetHandle,t.concat(n))};function ea({sourceX:e,sourceY:t,targetX:n,targetY:r}){const[o,i,s,a]=Mu({sourceX:e,sourceY:t,targetX:n,targetY:r});return[`M ${e},${t}L ${n},${r}`,o,i,s,a]}const Hu={[$e.Left]:{x:-1,y:0},[$e.Right]:{x:1,y:0},[$e.Top]:{x:0,y:-1},[$e.Bottom]:{x:0,y:1}},c0=({source:e,sourcePosition:t=$e.Bottom,target:n})=>t===$e.Left||t===$e.Right?e.x<n.x?{x:1,y:0}:{x:-1,y:0}:e.y<n.y?{x:0,y:1}:{x:0,y:-1},Vu=(e,t)=>Math.sqrt(Math.pow(t.x-e.x,2)+Math.pow(t.y-e.y,2));function d0({source:e,sourcePosition:t=$e.Bottom,target:n,targetPosition:r=$e.Top,center:o,offset:i}){const s=Hu[t],a=Hu[r],l={x:e.x+s.x*i,y:e.y+s.y*i},u={x:n.x+a.x*i,y:n.y+a.y*i},c=c0({source:l,sourcePosition:t,target:u}),f=c.x!==0?"x":"y",d=c[f];let g=[],p,x;const C={x:0,y:0},$={x:0,y:0},[m,_,v,b]=Mu({sourceX:e.x,sourceY:e.y,targetX:n.x,targetY:n.y});if(s[f]*a[f]===-1){p=o.x??m,x=o.y??_;const E=[{x:p,y:l.y},{x:p,y:u.y}],T=[{x:l.x,y:x},{x:u.x,y:x}];s[f]===d?g=f==="x"?E:T:g=f==="x"?T:E}else{const E=[{x:l.x,y:u.y}],T=[{x:u.x,y:l.y}];if(f==="x"?g=s.x===d?T:E:g=s.y===d?E:T,t===r){const R=Math.abs(e[f]-n[f]);if(R<=i){const S=Math.min(i-1,i-R);s[f]===d?C[f]=(l[f]>e[f]?-1:1)*S:$[f]=(u[f]>n[f]?-1:1)*S}}if(t!==r){const R=f==="x"?"y":"x",S=s[f]===a[R],M=l[R]>u[R],k=l[R]<u[R];(s[f]===1&&(!S&&M||S&&k)||s[f]!==1&&(!S&&k||S&&M))&&(g=f==="x"?E:T)}const D={x:l.x+C.x,y:l.y+C.y},V={x:u.x+$.x,y:u.y+$.y},A=Math.max(Math.abs(D.x-g[0].x),Math.abs(V.x-g[0].x)),O=Math.max(Math.abs(D.y-g[0].y),Math.abs(V.y-g[0].y));A>=O?(p=(D.x+V.x)/2,x=g[0].y):(p=g[0].x,x=(D.y+V.y)/2)}return[[e,{x:l.x+C.x,y:l.y+C.y},...g,{x:u.x+$.x,y:u.y+$.y},n],p,x,v,b]}function f0(e,t,n,r){const o=Math.min(Vu(e,t)/2,Vu(t,n)/2,r),{x:i,y:s}=t;if(e.x===i&&i===n.x||e.y===s&&s===n.y)return`L${i} ${s}`;if(e.y===s){const u=e.x<n.x?-1:1,c=e.y<n.y?1:-1;return`L ${i+o*u},${s}Q ${i},${s} ${i},${s+o*c}`}const a=e.x<n.x?1:-1,l=e.y<n.y?-1:1;return`L ${i},${s+o*l}Q ${i},${s} ${i+o*a},${s}`}function Li({sourceX:e,sourceY:t,sourcePosition:n=$e.Bottom,targetX:r,targetY:o,targetPosition:i=$e.Top,borderRadius:s=5,centerX:a,centerY:l,offset:u=20}){const[c,f,d,g,p]=d0({source:{x:e,y:t},sourcePosition:n,target:{x:r,y:o},targetPosition:i,center:{x:a,y:l},offset:u});return[c.reduce((C,$,m)=>{let _="";return m>0&&m<c.length-1?_=f0(c[m-1],$,c[m+1],s):_=`${m===0?"M":"L"}${$.x} ${$.y}`,C+=_,C},""),f,d,g,p]}function Du(e){var t;return e&&!!(e.internals.handleBounds||(t=e.handles)!=null&&t.length)&&!!(e.measured.width||e.width||e.initialWidth)}function g0(e){var f;const{sourceNode:t,targetNode:n}=e;if(!Du(t)||!Du(n))return null;const r=t.internals.handleBounds||Au(t.handles),o=n.internals.handleBounds||Au(n.handles),i=Lu((r==null?void 0:r.source)??[],e.sourceHandle),s=Lu(e.connectionMode===hr.Strict?(o==null?void 0:o.target)??[]:((o==null?void 0:o.target)??[]).concat((o==null?void 0:o.source)??[]),e.targetHandle);if(!i||!s)return(f=e.onError)==null||f.call(e,"008",Dr.error008(i?"target":"source",{id:e.id,sourceHandle:e.sourceHandle,targetHandle:e.targetHandle})),null;const a=(i==null?void 0:i.position)||$e.Bottom,l=(s==null?void 0:s.position)||$e.Top,u=$o(t,i,a),c=$o(n,s,l);return{sourceX:u.x,sourceY:u.y,targetX:c.x,targetY:c.y,sourcePosition:a,targetPosition:l}}function Au(e){if(!e)return null;const t=[],n=[];for(const r of e)r.width=r.width??1,r.height=r.height??1,r.type==="source"?t.push(r):r.type==="target"&&n.push(r);return{source:t,target:n}}function $o(e,t,n=$e.Left,r=!1){const o=((t==null?void 0:t.x)??0)+e.internals.positionAbsolute.x,i=((t==null?void 0:t.y)??0)+e.internals.positionAbsolute.y,{width:s,height:a}=t??Qn(e);if(r)return{x:o+s/2,y:i+a/2};switch((t==null?void 0:t.position)??n){case $e.Top:return{x:o+s/2,y:i};case $e.Right:return{x:o+s,y:i+a/2};case $e.Bottom:return{x:o+s/2,y:i+a};case $e.Left:return{x:o,y:i+a/2}}}function Lu(e,t){return e&&(t?e.find(n=>n.id===t):e[0])||null}function ta(e,t){return e?typeof e=="string"?e:`${t?`${t}__`:""}${Object.keys(e).sort().map(r=>`${r}=${e[r]}`).join("&")}`:""}function h0(e,{id:t,defaultColor:n,defaultMarkerStart:r,defaultMarkerEnd:o}){const i=new Set;return e.reduce((s,a)=>([a.markerStart||r,a.markerEnd||o].forEach(l=>{if(l&&typeof l=="object"){const u=ta(l,t);i.has(u)||(s.push({id:u,color:l.color||n,...l}),i.add(u))}}),s),[]).sort((s,a)=>s.id.localeCompare(a.id))}function v0(e,t,n,r,o){let i=.5;o==="start"?i=0:o==="end"&&(i=1);let s=[(e.x+e.width*i)*t.zoom+t.x,e.y*t.zoom+t.y-r],a=[-100*i,-100];switch(n){case $e.Right:s=[(e.x+e.width)*t.zoom+t.x+r,(e.y+e.height*i)*t.zoom+t.y],a=[0,-100*i];break;case $e.Bottom:s[1]=(e.y+e.height)*t.zoom+t.y+r,a[1]=0;break;case $e.Left:s=[e.x*t.zoom+t.x-r,(e.y+e.height*i)*t.zoom+t.y],a=[-100,-100*i];break}return`translate(${s[0]}px, ${s[1]}px) translate(${a[0]}%, ${a[1]}%)`}const na={nodeOrigin:[0,0],nodeExtent:Ni,elevateNodesOnSelect:!0,defaults:{}},p0={...na,checkEquality:!0};function ra(e,t){const n={...e};for(const r in t)t[r]!==void 0&&(n[r]=t[r]);return n}function m0(e,t,n){const r=ra(na,n);for(const o of e.values())if(o.parentId)oa(o,e,t,r);else{const i=xo(o,r.nodeOrigin),s=Ir(o.extent)?o.extent:r.nodeExtent,a=vr(i,s,Qn(o));o.internals.positionAbsolute=a}}function Ou(e,t,n,r){var a,l;const o=ra(p0,r),i=new Map(t),s=o!=null&&o.elevateNodesOnSelect?1e3:0;t.clear(),n.clear();for(const u of e){let c=i.get(u.id);if(o.checkEquality&&u===(c==null?void 0:c.internals.userNode))t.set(u.id,c);else{const f=xo(u,o.nodeOrigin),d=Ir(u.extent)?u.extent:o.nodeExtent,g=vr(f,d,Qn(u));c={...o.defaults,...u,measured:{width:(a=u.measured)==null?void 0:a.width,height:(l=u.measured)==null?void 0:l.height},internals:{positionAbsolute:g,handleBounds:u.measured?c==null?void 0:c.internals.handleBounds:void 0,z:Iu(u,s),userNode:u}},t.set(u.id,c)}u.parentId&&oa(c,t,n,r)}}function y0(e,t){if(!e.parentId)return;const n=t.get(e.parentId);n?n.set(e.id,e):t.set(e.parentId,new Map([[e.id,e]]))}function oa(e,t,n,r){const{elevateNodesOnSelect:o,nodeOrigin:i,nodeExtent:s}=ra(na,r),a=e.parentId,l=t.get(a);if(!l){console.warn(`Parent node ${a} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}y0(e,n);const u=o?1e3:0,{x:c,y:f,z:d}=w0(e,l,i,s,u),{positionAbsolute:g}=e.internals,p=c!==g.x||f!==g.y;(p||d!==e.internals.z)&&t.set(e.id,{...e,internals:{...e.internals,positionAbsolute:p?{x:c,y:f}:g,z:d}})}function Iu(e,t){return(zn(e.zIndex)?e.zIndex:0)+(e.selected?t:0)}function w0(e,t,n,r,o){const{x:i,y:s}=t.internals.positionAbsolute,a=Qn(e),l=xo(e,n),u=Ir(e.extent)?vr(l,e.extent,a):l;let c=vr({x:i+u.x,y:s+u.y},r,a);e.extent==="parent"&&(c=_u(c,a,t));const f=Iu(e,o),d=t.internals.z??0;return{x:c.x,y:c.y,z:d>f?d:f}}function _0(e,t,n,r=[0,0]){var s;const o=[],i=new Map;for(const a of e){const l=t.get(a.parentId);if(!l)continue;const u=((s=i.get(a.parentId))==null?void 0:s.expandedRect)??Or(l),c=Cu(u,a.rect);i.set(a.parentId,{expandedRect:c,parent:l})}return i.size>0&&i.forEach(({expandedRect:a,parent:l},u)=>{var _;const c=l.internals.positionAbsolute,f=Qn(l),d=l.origin??r,g=a.x<c.x?Math.round(Math.abs(c.x-a.x)):0,p=a.y<c.y?Math.round(Math.abs(c.y-a.y)):0,x=Math.max(f.width,Math.round(a.width)),C=Math.max(f.height,Math.round(a.height)),$=(x-f.width)*d[0],m=(C-f.height)*d[1];(g>0||p>0||$||m)&&(o.push({id:u,type:"position",position:{x:l.position.x-g+$,y:l.position.y-p+m}}),(_=n.get(u))==null||_.forEach(v=>{e.some(b=>b.id===v.id)||o.push({id:v.id,type:"position",position:{x:v.position.x+g,y:v.position.y+p}})})),(f.width<a.width||f.height<a.height||g||p)&&o.push({id:u,type:"dimensions",setAttributes:!0,dimensions:{width:x+(g?d[0]*g-$:0),height:C+(p?d[1]*p-m:0)}})}),o}function x0(e,t,n,r,o,i){const s=r==null?void 0:r.querySelector(".xyflow__viewport");let a=!1;if(!s)return{changes:[],updatedInternals:a};const l=[],u=window.getComputedStyle(s),{m22:c}=new window.DOMMatrixReadOnly(u.transform),f=[];for(const d of e.values()){const g=t.get(d.id);if(!g)continue;if(g.hidden){t.set(g.id,{...g,internals:{...g.internals,handleBounds:void 0}}),a=!0;continue}const p=Qs(d.nodeElement),x=g.measured.width!==p.width||g.measured.height!==p.height;if(!!(p.width&&p.height&&(x||!g.internals.handleBounds||d.force))){const $=d.nodeElement.getBoundingClientRect(),m=Ir(g.extent)?g.extent:i;let{positionAbsolute:_}=g.internals;g.parentId&&g.extent==="parent"?_=_u(_,p,t.get(g.parentId)):m&&(_=vr(_,m,p));const v={...g,measured:p,internals:{...g.internals,positionAbsolute:_,handleBounds:{source:Pu("source",d.nodeElement,$,c,g.id),target:Pu("target",d.nodeElement,$,c,g.id)}}};t.set(g.id,v),g.parentId&&oa(v,t,n,{nodeOrigin:o}),a=!0,x&&(l.push({id:g.id,type:"dimensions",dimensions:p}),g.expandParent&&g.parentId&&f.push({id:g.id,parentId:g.parentId,rect:Or(v,o)}))}}if(f.length>0){const d=_0(f,t,n,o);l.push(...d)}return{changes:l,updatedInternals:a}}async function b0({delta:e,panZoom:t,transform:n,translateExtent:r,width:o,height:i}){if(!t||!e.x&&!e.y)return Promise.resolve(!1);const s=await t.setViewportConstrained({x:n[0]+e.x,y:n[1]+e.y,zoom:n[2]},[[0,0],[o,i]],r),a=!!s&&(s.x!==n[0]||s.y!==n[1]||s.k!==n[2]);return Promise.resolve(a)}function zu(e,t,n,r,o,i){let s=o;const a=r.get(s)||new Map;r.set(s,a.set(n,t)),s=`${o}-${e}`;const l=r.get(s)||new Map;if(r.set(s,l.set(n,t)),i){s=`${o}-${e}-${i}`;const u=r.get(s)||new Map;r.set(s,u.set(n,t))}}function Ru(e,t,n){e.clear(),t.clear();for(const r of n){const{source:o,target:i,sourceHandle:s=null,targetHandle:a=null}=r,l={edgeId:r.id,source:o,target:i,sourceHandle:s,targetHandle:a},u=`${o}-${s}--${i}-${a}`,c=`${i}-${a}--${o}-${s}`;zu("source",l,c,e,o,s),zu("target",l,u,e,i,a),t.set(r.id,r)}}function C0(e,t){if(e===null||t===null)return!1;const n=Array.isArray(e)?e:[e],r=Array.isArray(t)?t:[t];if(n.length!==r.length)return!1;for(let o=0;o<n.length;o++)if(n[o].id!==r[o].id||n[o].type!==r[o].type||!Object.is(n[o].data,r[o].data))return!1;return!0}function Bu(e,t){if(!e.parentId)return!1;const n=t.get(e.parentId);return n?n.selected?!0:Bu(n,t):!1}function Yu(e,t,n){var o;let r=e;do{if((o=r==null?void 0:r.matches)!=null&&o.call(r,t))return!0;if(r===n)return!1;r=r==null?void 0:r.parentElement}while(r);return!1}function k0(e,t,n,r){const o=new Map;for(const[i,s]of e)if((s.selected||s.id===r)&&(!s.parentId||!Bu(s,e))&&(s.draggable||t&&typeof s.draggable>"u")){const a=e.get(i);a&&o.set(i,{id:i,position:a.position||{x:0,y:0},distance:{x:n.x-a.internals.positionAbsolute.x,y:n.y-a.internals.positionAbsolute.y},extent:a.extent,parentId:a.parentId,origin:a.origin,expandParent:a.expandParent,internals:{positionAbsolute:a.internals.positionAbsolute||{x:0,y:0}},measured:{width:a.measured.width??0,height:a.measured.height??0}})}return o}function ia({nodeId:e,dragItems:t,nodeLookup:n,dragging:r=!0}){var s,a,l;const o=[];for(const[u,c]of t){const f=(s=n.get(u))==null?void 0:s.internals.userNode;f&&o.push({...f,position:c.position,dragging:r})}if(!e)return[o[0],o];const i=(a=n.get(e))==null?void 0:a.internals.userNode;return[i?{...i,position:((l=t.get(e))==null?void 0:l.position)||i.position,dragging:r}:o[0],o]}function $0({onNodeMouseDown:e,getStoreItems:t,onDragStart:n,onDrag:r,onDragStop:o}){let i={x:null,y:null},s=0,a=new Map,l=!1,u={x:0,y:0},c=null,f=!1,d=null,g=!1;function p({noDragClassName:C,handleSelector:$,domNode:m,isSelectable:_,nodeId:v,nodeClickDistance:b=0}){d=Ut(m);function N({x:V,y:A},O){const{nodeLookup:R,nodeExtent:S,snapGrid:M,snapToGrid:k,nodeOrigin:P,onNodeDrag:H,onSelectionDrag:I,onError:B,updateNodePositions:F}=t();i={x:V,y:A};let K=!1,se={x:0,y:0,x2:0,y2:0};if(a.size>1&&S){const ee=bo(a);se=Gs(ee)}for(const[ee,W]of a){if(!R.has(ee))continue;let fe={x:V-W.distance.x,y:A-W.distance.y};k&&(fe=Us(fe,M));let me=[[S[0][0],S[0][1]],[S[1][0],S[1][1]]];if(a.size>1&&S&&!W.extent){const{positionAbsolute:ze}=W.internals,G=ze.x-se.x+S[0][0],ae=ze.x+W.measured.width-se.x2+S[1][0],Me=ze.y-se.y+S[0][1],Le=ze.y+W.measured.height-se.y2+S[1][1];me=[[G,Me],[ae,Le]]}const{position:Ce,positionAbsolute:he}=Jv({nodeId:ee,nextPosition:fe,nodeLookup:R,nodeExtent:me,nodeOrigin:P,onError:B});K=K||W.position.x!==Ce.x||W.position.y!==Ce.y,W.position=Ce,W.internals.positionAbsolute=he}if(K&&(F(a,!0),O&&(r||H||!v&&I))){const[ee,W]=ia({nodeId:v,dragItems:a,nodeLookup:R});r==null||r(O,a,ee,W),H==null||H(O,ee,W),v||I==null||I(O,W)}}async function E(){if(!c)return;const{transform:V,panBy:A,autoPanSpeed:O,autoPanOnNodeDrag:R}=t();if(!R){l=!1,cancelAnimationFrame(s);return}const[S,M]=bu(u,c,O);(S!==0||M!==0)&&(i.x=(i.x??0)-S/V[2],i.y=(i.y??0)-M/V[2],await A({x:S,y:M})&&N(i,null)),s=requestAnimationFrame(E)}function T(V){var K;const{nodeLookup:A,multiSelectionActive:O,nodesDraggable:R,transform:S,snapGrid:M,snapToGrid:k,selectNodesOnDrag:P,onNodeDragStart:H,onSelectionDragStart:I,unselectNodesAndEdges:B}=t();f=!0,(!P||!_)&&!O&&v&&((K=A.get(v))!=null&&K.selected||B()),_&&P&&v&&(e==null||e(v));const F=Js(V.sourceEvent,{transform:S,snapGrid:M,snapToGrid:k,containerBounds:c});if(i=F,a=k0(A,R,F,v),a.size>0&&(n||H||!v&&I)){const[se,ee]=ia({nodeId:v,dragItems:a,nodeLookup:A});n==null||n(V.sourceEvent,a,se,ee),H==null||H(V.sourceEvent,se,ee),v||I==null||I(V.sourceEvent,ee)}}const D=rh().clickDistance(b).on("start",V=>{const{domNode:A,nodeDragThreshold:O,transform:R,snapGrid:S,snapToGrid:M}=t();c=(A==null?void 0:A.getBoundingClientRect())||null,g=!1,O===0&&T(V),i=Js(V.sourceEvent,{transform:R,snapGrid:S,snapToGrid:M,containerBounds:c}),u=Rn(V.sourceEvent,c)}).on("drag",V=>{const{autoPanOnNodeDrag:A,transform:O,snapGrid:R,snapToGrid:S,nodeDragThreshold:M,nodeLookup:k}=t(),P=Js(V.sourceEvent,{transform:O,snapGrid:R,snapToGrid:S,containerBounds:c});if((V.sourceEvent.type==="touchmove"&&V.sourceEvent.touches.length>1||v&&!k.has(v))&&(g=!0),!g){if(!l&&A&&f&&(l=!0,E()),!f){const H=P.xSnapped-(i.x??0),I=P.ySnapped-(i.y??0);Math.sqrt(H*H+I*I)>M&&T(V)}(i.x!==P.xSnapped||i.y!==P.ySnapped)&&a&&f&&(u=Rn(V.sourceEvent,c),N(P,V.sourceEvent))}}).on("end",V=>{if(!(!f||g)&&(l=!1,f=!1,cancelAnimationFrame(s),a.size>0)){const{nodeLookup:A,updateNodePositions:O,onNodeDragStop:R,onSelectionDragStop:S}=t();if(O(a,!1),o||R||!v&&S){const[M,k]=ia({nodeId:v,dragItems:a,nodeLookup:A,dragging:!1});o==null||o(V.sourceEvent,a,M,k),R==null||R(V.sourceEvent,M,k),v||S==null||S(V.sourceEvent,k)}}}).filter(V=>{const A=V.target;return!V.button&&(!C||!Yu(A,`.${C}`,m))&&(!$||Yu(A,$,m))});d.call(D)}function x(){d==null||d.on(".drag",null)}return{update:p,destroy:x}}function E0(e,t,n){const r=[],o={x:e.x-n,y:e.y-n,width:n*2,height:n*2};for(const i of t.values())Co(o,Or(i))>0&&r.push(i);return r}const S0=250;function P0(e,t,n,r){var a,l;let o=[],i=1/0;const s=E0(e,n,t+S0);for(const u of s){const c=[...((a=u.internals.handleBounds)==null?void 0:a.source)??[],...((l=u.internals.handleBounds)==null?void 0:l.target)??[]];for(const f of c){if(r.nodeId===f.nodeId&&r.type===f.type&&r.id===f.id)continue;const{x:d,y:g}=$o(u,f,f.position,!0),p=Math.sqrt(Math.pow(d-e.x,2)+Math.pow(g-e.y,2));p>t||(p<i?(o=[{...f,x:d,y:g}],i=p):p===i&&o.push({...f,x:d,y:g}))}}if(!o.length)return null;if(o.length>1){const u=r.type==="source"?"target":"source";return o.find(c=>c.type===u)??o[0]}return o[0]}function Zu(e,t,n,r,o,i=!1){var u,c,f;const s=r.get(e);if(!s)return null;const a=o==="strict"?(u=s.internals.handleBounds)==null?void 0:u[t]:[...((c=s.internals.handleBounds)==null?void 0:c.source)??[],...((f=s.internals.handleBounds)==null?void 0:f.target)??[]],l=(n?a==null?void 0:a.find(d=>d.id===n):a==null?void 0:a[0])??null;return l&&i?{...l,...$o(s,l,l.position,!0)}:l}function Xu(e,t){return e||(t!=null&&t.classList.contains("target")?"target":t!=null&&t.classList.contains("source")?"source":null)}function N0(e,t){let n=null;return t?n=!0:e&&!t&&(n=!1),n}const Fu=()=>!0;function T0(e,{connectionMode:t,connectionRadius:n,handleId:r,nodeId:o,edgeUpdaterType:i,isTarget:s,domNode:a,nodeLookup:l,lib:u,autoPanOnConnect:c,flowId:f,panBy:d,cancelConnection:g,onConnectStart:p,onConnect:x,onConnectEnd:C,isValidConnection:$=Fu,onReconnectEnd:m,updateConnection:_,getTransform:v,getFromHandle:b,autoPanSpeed:N}){const E=t0(e.target);let T=0,D;const{x:V,y:A}=Rn(e),O=E==null?void 0:E.elementFromPoint(V,A),R=Xu(i,O),S=a==null?void 0:a.getBoundingClientRect();if(!S||!R)return;const M=Zu(o,R,r,l,t);if(!M)return;let k=Rn(e,S),P=!1,H=null,I=!1,B=null;function F(){if(!c||!S)return;const[he,ze]=bu(k,S,N);d({x:he,y:ze}),T=requestAnimationFrame(F)}const K={...M,nodeId:o,type:R,position:M.position},se=l.get(o),W={inProgress:!0,isValid:null,from:$o(se,K,$e.Left,!0),fromHandle:K,fromPosition:K.position,fromNode:se,to:k,toHandle:null,toPosition:hu[K.position],toNode:null};_(W);let fe=W;p==null||p(e,{nodeId:o,handleId:r,handleType:R});function me(he){if(!b()||!K){Ce(he);return}const ze=v();k=Rn(he,S),D=P0(ko(k,ze,!1,[1,1]),n,l,K),P||(F(),P=!0);const G=Wu(he,{handle:D,connectionMode:t,fromNodeId:o,fromHandleId:r,fromType:s?"target":"source",isValidConnection:$,doc:E,lib:u,flowId:f,nodeLookup:l});B=G.handleDomNode,H=G.connection,I=N0(!!D,G.isValid);const ae={...fe,isValid:I,to:D&&I?$u({x:D.x,y:D.y},ze):k,toHandle:G.toHandle,toPosition:I&&G.toHandle?G.toHandle.position:hu[K.position],toNode:G.toHandle?l.get(G.toHandle.nodeId):null};I&&D&&fe.toHandle&&ae.toHandle&&fe.toHandle.type===ae.toHandle.type&&fe.toHandle.nodeId===ae.toHandle.nodeId&&fe.toHandle.id===ae.toHandle.id&&fe.to.x===ae.to.x&&fe.to.y===ae.to.y||(_(ae),fe=ae)}function Ce(he){(D||B)&&H&&I&&(x==null||x(H));const{inProgress:ze,...G}=fe,ae={...G,toPosition:fe.toHandle?fe.toPosition:null};C==null||C(he,ae),i&&(m==null||m(he,ae)),g(),cancelAnimationFrame(T),P=!1,I=!1,H=null,B=null,E.removeEventListener("mousemove",me),E.removeEventListener("mouseup",Ce),E.removeEventListener("touchmove",me),E.removeEventListener("touchend",Ce)}E.addEventListener("mousemove",me),E.addEventListener("mouseup",Ce),E.addEventListener("touchmove",me),E.addEventListener("touchend",Ce)}function Wu(e,{handle:t,connectionMode:n,fromNodeId:r,fromHandleId:o,fromType:i,doc:s,lib:a,flowId:l,isValidConnection:u=Fu,nodeLookup:c}){const f=i==="target",d=t?s.querySelector(`.${a}-flow__handle[data-id="${l}-${t==null?void 0:t.nodeId}-${t==null?void 0:t.id}-${t==null?void 0:t.type}"]`):null,{x:g,y:p}=Rn(e),x=s.elementFromPoint(g,p),C=x!=null&&x.classList.contains(`${a}-flow__handle`)?x:d,$={handleDomNode:C,isValid:!1,connection:null,toHandle:null};if(C){const m=Xu(void 0,C),_=C.getAttribute("data-nodeid"),v=C.getAttribute("data-handleid"),b=C.classList.contains("connectable"),N=C.classList.contains("connectableend");if(!_||!m)return $;const E={source:f?_:r,sourceHandle:f?v:o,target:f?r:_,targetHandle:f?o:v};$.connection=E;const D=b&&N&&(n===hr.Strict?f&&m==="source"||!f&&m==="target":_!==r||v!==o);$.isValid=D&&u(E),$.toHandle=Zu(_,m,v,c,n,!1)}return $}const M0={onPointerDown:T0,isValid:Wu};function H0({domNode:e,panZoom:t,getTransform:n,getViewScale:r}){const o=Ut(e);function i({translateExtent:a,width:l,height:u,zoomStep:c=10,pannable:f=!0,zoomable:d=!0,inversePan:g=!1}){const p=_=>{const v=n();if(_.sourceEvent.type!=="wheel"||!t)return;const b=-_.sourceEvent.deltaY*(_.sourceEvent.deltaMode===1?.05:_.sourceEvent.deltaMode?1:.002)*c,N=v[2]*Math.pow(2,b);t.scaleTo(N)};let x=[0,0];const C=_=>{(_.sourceEvent.type==="mousedown"||_.sourceEvent.type==="touchstart")&&(x=[_.sourceEvent.clientX??_.sourceEvent.touches[0].clientX,_.sourceEvent.clientY??_.sourceEvent.touches[0].clientY])},$=_=>{const v=n();if(_.sourceEvent.type!=="mousemove"&&_.sourceEvent.type!=="touchmove"||!t)return;const b=[_.sourceEvent.clientX??_.sourceEvent.touches[0].clientX,_.sourceEvent.clientY??_.sourceEvent.touches[0].clientY],N=[b[0]-x[0],b[1]-x[1]];x=b;const E=r()*Math.max(v[2],Math.log(v[2]))*(g?-1:1),T={x:v[0]-N[0]*E,y:v[1]-N[1]*E},D=[[0,0],[l,u]];t.setViewportConstrained({x:T.x,y:T.y,zoom:v[2]},D,a)},m=gu().on("start",C).on("zoom",f?$:null).on("zoom.wheel",d?p:null);o.call(m,{})}function s(){o.on("zoom",null)}return{update:i,destroy:s,pointer:on}}const V0=(e,t)=>e.x!==t.x||e.y!==t.y||e.zoom!==t.k,Oi=e=>({x:e.x,y:e.y,zoom:e.k}),sa=({x:e,y:t,zoom:n})=>Pi.translate(e,t).scale(n),zr=(e,t)=>e.target.closest(`.${t}`),Ku=(e,t)=>t===2&&Array.isArray(e)&&e.includes(2),aa=(e,t=0,n=()=>{})=>{const r=typeof t=="number"&&t>0;return r||n(),r?e.transition().duration(t).on("end",n):e},qu=e=>{const t=e.ctrlKey&&Di()?10:1;return-e.deltaY*(e.deltaMode===1?.05:e.deltaMode?1:.002)*t};function D0({zoomPanValues:e,noWheelClassName:t,d3Selection:n,d3Zoom:r,panOnScrollMode:o,panOnScrollSpeed:i,zoomOnPinch:s,onPanZoomStart:a,onPanZoom:l,onPanZoomEnd:u}){return c=>{if(zr(c,t))return!1;c.preventDefault(),c.stopImmediatePropagation();const f=n.property("__zoom").k||1;if(c.ctrlKey&&s){const C=on(c),$=qu(c),m=f*Math.pow(2,$);r.scaleTo(n,m,C,c);return}const d=c.deltaMode===1?20:1;let g=o===Jn.Vertical?0:c.deltaX*d,p=o===Jn.Horizontal?0:c.deltaY*d;!Di()&&c.shiftKey&&o!==Jn.Vertical&&(g=c.deltaY*d,p=0),r.translateBy(n,-(g/f)*i,-(p/f)*i,{internal:!0});const x=Oi(n.property("__zoom"));clearTimeout(e.panScrollTimeout),e.isPanScrolling||(e.isPanScrolling=!0,a==null||a(c,x)),e.isPanScrolling&&(l==null||l(c,x),e.panScrollTimeout=setTimeout(()=>{u==null||u(c,x),e.isPanScrolling=!1},150))}}function A0({noWheelClassName:e,preventScrolling:t,d3ZoomHandler:n}){return function(r,o){if(!t&&r.type==="wheel"&&!r.ctrlKey||zr(r,e))return null;r.preventDefault(),n.call(this,r,o)}}function L0({zoomPanValues:e,onDraggingChange:t,onPanZoomStart:n}){return r=>{var i,s,a;if((i=r.sourceEvent)!=null&&i.internal)return;const o=Oi(r.transform);e.mouseButton=((s=r.sourceEvent)==null?void 0:s.button)||0,e.isZoomingOrPanning=!0,e.prevViewport=o,((a=r.sourceEvent)==null?void 0:a.type)==="mousedown"&&t(!0),n&&(n==null||n(r.sourceEvent,o))}}function O0({zoomPanValues:e,panOnDrag:t,onPaneContextMenu:n,onTransformChange:r,onPanZoom:o}){return i=>{var s,a;e.usedRightMouseButton=!!(n&&Ku(t,e.mouseButton??0)),(s=i.sourceEvent)!=null&&s.sync||r([i.transform.x,i.transform.y,i.transform.k]),o&&!((a=i.sourceEvent)!=null&&a.internal)&&(o==null||o(i.sourceEvent,Oi(i.transform)))}}function I0({zoomPanValues:e,panOnDrag:t,panOnScroll:n,onDraggingChange:r,onPanZoomEnd:o,onPaneContextMenu:i}){return s=>{var a;if(!((a=s.sourceEvent)!=null&&a.internal)&&(e.isZoomingOrPanning=!1,i&&Ku(t,e.mouseButton??0)&&!e.usedRightMouseButton&&s.sourceEvent&&i(s.sourceEvent),e.usedRightMouseButton=!1,r(!1),o&&V0(e.prevViewport,s.transform))){const l=Oi(s.transform);e.prevViewport=l,clearTimeout(e.timerId),e.timerId=setTimeout(()=>{o==null||o(s.sourceEvent,l)},n?150:0)}}}function z0({zoomActivationKeyPressed:e,zoomOnScroll:t,zoomOnPinch:n,panOnDrag:r,panOnScroll:o,zoomOnDoubleClick:i,userSelectionActive:s,noWheelClassName:a,noPanClassName:l,lib:u}){return c=>{var p;const f=e||t,d=n&&c.ctrlKey;if(c.button===1&&c.type==="mousedown"&&(zr(c,`${u}-flow__node`)||zr(c,`${u}-flow__edge`)))return!0;if(!r&&!f&&!o&&!i&&!n||s||zr(c,a)&&c.type==="wheel"||zr(c,l)&&(c.type!=="wheel"||o&&c.type==="wheel"&&!e)||!n&&c.ctrlKey&&c.type==="wheel")return!1;if(!n&&c.type==="touchstart"&&((p=c.touches)==null?void 0:p.length)>1)return c.preventDefault(),!1;if(!f&&!o&&!d&&c.type==="wheel"||!r&&(c.type==="mousedown"||c.type==="touchstart")||Array.isArray(r)&&!r.includes(c.button)&&c.type==="mousedown")return!1;const g=Array.isArray(r)&&r.includes(c.button)||!c.button||c.button<=1;return(!c.ctrlKey||c.type==="wheel")&&g}}function R0({domNode:e,minZoom:t,maxZoom:n,paneClickDistance:r,translateExtent:o,viewport:i,onPanZoom:s,onPanZoomStart:a,onPanZoomEnd:l,onDraggingChange:u}){const c={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{x:0,y:0,zoom:0},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},f=e.getBoundingClientRect(),d=gu().clickDistance(!zn(r)||r<0?0:r).scaleExtent([t,n]).translateExtent(o),g=Ut(e).call(d);_({x:i.x,y:i.y,zoom:Lr(i.zoom,t,n)},[[0,0],[f.width,f.height]],o);const p=g.on("wheel.zoom"),x=g.on("dblclick.zoom");d.wheelDelta(qu);function C(O,R){return g?new Promise(S=>{d==null||d.transform(aa(g,R==null?void 0:R.duration,()=>S(!0)),O)}):Promise.resolve(!1)}function $({noWheelClassName:O,noPanClassName:R,onPaneContextMenu:S,userSelectionActive:M,panOnScroll:k,panOnDrag:P,panOnScrollMode:H,panOnScrollSpeed:I,preventScrolling:B,zoomOnPinch:F,zoomOnScroll:K,zoomOnDoubleClick:se,zoomActivationKeyPressed:ee,lib:W,onTransformChange:fe}){M&&!c.isZoomingOrPanning&&m();const Ce=k&&!ee&&!M?D0({zoomPanValues:c,noWheelClassName:O,d3Selection:g,d3Zoom:d,panOnScrollMode:H,panOnScrollSpeed:I,zoomOnPinch:F,onPanZoomStart:a,onPanZoom:s,onPanZoomEnd:l}):A0({noWheelClassName:O,preventScrolling:B,d3ZoomHandler:p});if(g.on("wheel.zoom",Ce,{passive:!1}),!M){const ze=L0({zoomPanValues:c,onDraggingChange:u,onPanZoomStart:a});d.on("start",ze);const G=O0({zoomPanValues:c,panOnDrag:P,onPaneContextMenu:!!S,onPanZoom:s,onTransformChange:fe});d.on("zoom",G);const ae=I0({zoomPanValues:c,panOnDrag:P,panOnScroll:k,onPaneContextMenu:S,onPanZoomEnd:l,onDraggingChange:u});d.on("end",ae)}const he=z0({zoomActivationKeyPressed:ee,panOnDrag:P,zoomOnScroll:K,panOnScroll:k,zoomOnDoubleClick:se,zoomOnPinch:F,userSelectionActive:M,noPanClassName:R,noWheelClassName:O,lib:W});d.filter(he),se?g.on("dblclick.zoom",x):g.on("dblclick.zoom",null)}function m(){d.on("zoom",null)}async function _(O,R,S){const M=sa(O),k=d==null?void 0:d.constrain()(M,R,S);return k&&await C(k),new Promise(P=>P(k))}async function v(O,R){const S=sa(O);return await C(S,R),new Promise(M=>M(S))}function b(O){if(g){const R=sa(O),S=g.property("__zoom");(S.k!==O.zoom||S.x!==O.x||S.y!==O.y)&&(d==null||d.transform(g,R,null,{sync:!0}))}}function N(){const O=g?du(g.node()):{x:0,y:0,k:1};return{x:O.x,y:O.y,zoom:O.k}}function E(O,R){return g?new Promise(S=>{d==null||d.scaleTo(aa(g,R==null?void 0:R.duration,()=>S(!0)),O)}):Promise.resolve(!1)}function T(O,R){return g?new Promise(S=>{d==null||d.scaleBy(aa(g,R==null?void 0:R.duration,()=>S(!0)),O)}):Promise.resolve(!1)}function D(O){d==null||d.scaleExtent(O)}function V(O){d==null||d.translateExtent(O)}function A(O){const R=!zn(O)||O<0?0:O;d==null||d.clickDistance(R)}return{update:$,destroy:m,setViewport:v,setViewportConstrained:_,getViewport:N,scaleTo:E,scaleBy:T,setScaleExtent:D,setTranslateExtent:V,syncViewport:b,setClickDistance:A}}var Gu;(function(e){e.Line="line",e.Handle="handle"})(Gu||(Gu={}));var B0=ne('<div role="button" tabindex="-1"><!></div>');function er(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(se,"$connectable",n),i=()=>Q(Ce,"$connectionRadius",n),s=()=>Q(fe,"$domNode",n),a=()=>Q(me,"$nodeLookup",n),l=()=>Q(W,"$connectionMode",n),u=()=>Q(G,"$lib",n),c=()=>Q(Fe,"$autoPanOnConnect",n),f=()=>Q(Ie,"$flowId",n),d=()=>Q(ze,"$isValidConnectionStore",n),g=()=>Q(Me,"$onedgecreate",n),p=()=>Q(oe,"$onConnectAction",n),x=()=>Q(pe,"$onConnectStartAction",n),C=()=>Q(be,"$onConnectEndAction",n),$=()=>Q(he,"$viewport",n),m=()=>Q(ht,"$connection",n),_=()=>Q(Oe,"$edges",n),v=()=>Q(rt,"$connectionLookup",n),b=re(),N=re(),E=re(),T=re(),D=re(),V=re(),A=re(),O=re();let R=w(t,"id",12,void 0),S=w(t,"type",12,"source"),M=w(t,"position",28,()=>$e.Top),k=w(t,"style",12,void 0),P=w(t,"isValidConnection",12,void 0),H=w(t,"onconnect",12,void 0),I=w(t,"ondisconnect",12,void 0),B=w(t,"isConnectable",12,void 0),F=w(t,"class",12,void 0);const K=ur("svelteflow__node_id"),se=ur("svelteflow__node_connectable"),ee=Ue(),{connectionMode:W,domNode:fe,nodeLookup:me,connectionRadius:Ce,viewport:he,isValidConnection:ze,lib:G,addEdge:ae,onedgecreate:Me,panBy:Le,cancelConnection:Xe,updateConnection:te,autoPanOnConnect:Fe,edges:Oe,connectionLookup:rt,onconnect:oe,onconnectstart:pe,onconnectend:be,flowId:Ie,connection:ht}=ee;function dt(Te){const st=Su(Te);(st&&Te.button===0||!st)&&M0.onPointerDown(Te,{handleId:h(E),nodeId:K,isTarget:h(b),connectionRadius:i(),domNode:s(),nodeLookup:a(),connectionMode:l(),lib:u(),autoPanOnConnect:c(),flowId:f(),isValidConnection:P()??d(),updateConnection:te,cancelConnection:Xe,panBy:Le,onConnect:ye=>{var ct;const lt=g()?g()(ye):ye;lt&&(ae(lt),(ct=p())==null||ct(ye))},onConnectStart:(ye,lt)=>{var ct;(ct=x())==null||ct(ye,{nodeId:lt.nodeId,handleId:lt.handleId,handleType:lt.handleType})},onConnectEnd:(ye,lt)=>{var ct;(ct=C())==null||ct(ye,lt)},getTransform:()=>[$().x,$().y,$().zoom],getFromHandle:()=>m().fromHandle})}let J=re(null),Re=re();ge(()=>j(S()),()=>{U(b,S()==="target")}),ge(()=>(j(B()),o()),()=>{U(N,B()!==void 0?B():o())}),ge(()=>j(R()),()=>{U(E,R()||null)}),ge(()=>(j(H()),j(I()),_(),v(),j(S()),j(R())),()=>{(H()||I())&&(_(),U(Re,v().get(`${K}-${S()}${R()?`-${R()}`:""}`)))}),ge(()=>(h(J),h(Re),j(I()),j(H())),()=>{if(h(J)&&!Kv(h(Re),h(J))){const Te=h(Re)??new Map;vu(h(J),Te,I()),vu(Te,h(J),H())}U(J,h(Re)??new Map)}),ge(()=>m(),()=>{U(T,!!m().fromHandle)}),ge(()=>(m(),j(S()),h(E)),()=>{var Te,st,ye;U(D,((Te=m().fromHandle)==null?void 0:Te.nodeId)===K&&((st=m().fromHandle)==null?void 0:st.type)===S()&&((ye=m().fromHandle)==null?void 0:ye.id)===h(E))}),ge(()=>(m(),j(S()),h(E)),()=>{var Te,st,ye;U(V,((Te=m().toHandle)==null?void 0:Te.nodeId)===K&&((st=m().toHandle)==null?void 0:st.type)===S()&&((ye=m().toHandle)==null?void 0:ye.id)===h(E))}),ge(()=>(l(),m(),j(S()),h(E)),()=>{var Te,st,ye;U(A,l()===hr.Strict?((Te=m().fromHandle)==null?void 0:Te.type)!==S():K!==((st=m().fromHandle)==null?void 0:st.nodeId)||h(E)!==((ye=m().fromHandle)==null?void 0:ye.id))}),ge(()=>(h(V),m()),()=>{U(O,h(V)&&m().isValid)}),vt(),He();var le=B0();de(le,"data-nodeid",K);let $n;var fn=X(le);wt(fn,t,"default",{}),Z(le),Ee(Te=>{de(le,"data-handleid",h(E)),de(le,"data-handlepos",M()),de(le,"data-id",`${f()??""}-${K??""}-${R()||""}-${S()??""}`),$n=$t(le,1,wn(Te),null,$n,{valid:h(O),connectingto:h(V),connectingfrom:h(D),source:!h(b),target:h(b),connectablestart:h(N),connectableend:h(N),connectable:h(N),connectionindicator:h(N)&&(!h(T)||h(A))}),de(le,"style",k())},[()=>Et(["svelte-flow__handle",`svelte-flow__handle-${M()}`,"nodrag","nopan",M(),F()])],ve),Ze("mousedown",le,dt),Ze("touchstart",le,dt),L(e,le);var En=ce({get id(){return R()},set id(Te){R(Te),y()},get type(){return S()},set type(Te){S(Te),y()},get position(){return M()},set position(Te){M(Te),y()},get style(){return k()},set style(Te){k(Te),y()},get isValidConnection(){return P()},set isValidConnection(Te){P(Te),y()},get onconnect(){return H()},set onconnect(Te){H(Te),y()},get ondisconnect(){return I()},set ondisconnect(Te){I(Te),y()},get isConnectable(){return B()},set isConnectable(Te){B(Te),y()},get class(){return F()},set class(Te){F(Te),y()}});return r(),En}ie(er,{id:{},type:{},position:{},style:{},isValidConnection:{},onconnect:{},ondisconnect:{},isConnectable:{},class:{}},["default"],[],!0);var Y0=ne("<!> <!>",1);function Ii(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["data","targetPosition","sourcePosition"]),ue(t,!1);let r=w(t,"data",28,()=>({label:"Node"})),o=w(t,"targetPosition",12,void 0),i=w(t,"sourcePosition",12,void 0);He();var s=Y0(),a=xe(s);const l=ve(()=>o()??$e.Top);er(a,{type:"target",get position(){return h(l)}});var u=z(a),c=z(u);const f=ve(()=>i()??$e.Bottom);return er(c,{type:"source",get position(){return h(f)}}),Ee(()=>{var d;return Bt(u,` ${((d=r())==null?void 0:d.label)??""} `)}),L(e,s),ce({get data(){return r()},set data(d){r(d),y()},get targetPosition(){return o()},set targetPosition(d){o(d),y()},get sourcePosition(){return i()},set sourcePosition(d){i(d),y()}})}ie(Ii,{data:{},targetPosition:{},sourcePosition:{}},[],[],!0);var Z0=ne(" <!>",1);function Uu(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["data","sourcePosition"]),ue(t,!1);let r=w(t,"data",28,()=>({label:"Node"})),o=w(t,"sourcePosition",12,void 0);He(),Pe();var i=Z0(),s=xe(i),a=z(s);const l=ve(()=>o()??$e.Bottom);return er(a,{type:"source",get position(){return h(l)}}),Ee(()=>{var u;return Bt(s,`${((u=r())==null?void 0:u.label)??""} `)}),L(e,i),ce({get data(){return r()},set data(u){r(u),y()},get sourcePosition(){return o()},set sourcePosition(u){o(u),y()}})}ie(Uu,{data:{},sourcePosition:{}},[],[],!0);var X0=ne(" <!>",1);function ju(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["data","targetPosition"]),ue(t,!1);let r=w(t,"data",28,()=>({label:"Node"})),o=w(t,"targetPosition",12,void 0);He(),Pe();var i=X0(),s=xe(i),a=z(s);const l=ve(()=>o()??$e.Top);return er(a,{type:"target",get position(){return h(l)}}),Ee(()=>{var u;return Bt(s,`${((u=r())==null?void 0:u.label)??""} `)}),L(e,i),ce({get data(){return r()},set data(u){r(u),y()},get targetPosition(){return o()},set targetPosition(u){o(u),y()}})}ie(ju,{data:{},targetPosition:{}},[],[],!0);function Ju(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,[])}ie(Ju,{},[],[],!0);function Qu(e,t,n){if(!t)return;const r=n?t.querySelector(n):t;r&&r.appendChild(e)}function Rr(e,{target:t,domNode:n}){return Qu(e,n,t),{async update({target:r,domNode:o}){Qu(e,o,r)},destroy(){e.parentNode&&e.parentNode.removeChild(e)}}}var F0=ne("<div><!></div>");function ec(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(i,"$domNode",n),{domNode:i}=Ue();He();var s=F0(),a=X(s);wt(a,t,"default",{}),Z(s),_t(s,(l,u)=>Rr==null?void 0:Rr(l,u),()=>({target:".svelte-flow__edgelabel-renderer",domNode:o()})),L(e,s),ce(),r()}ie(ec,{},["default"],[],!0);function tc(){const{edgeLookup:e,selectionRect:t,selectionRectMode:n,multiselectionKeyPressed:r,addSelectedEdges:o,unselectNodesAndEdges:i,elementsSelectable:s}=Ue();return a=>{const l=q(e).get(a);if(!l){console.warn("012",Dr.error012(a));return}(l.selectable||q(s)&&typeof l.selectable>"u")&&(t.set(null),n.set(null),l.selected?l.selected&&q(r)&&i({nodes:[],edges:[l]}):o([a]))}}var W0=ne('<div class="svelte-flow__edge-label" role="button" tabindex="-1"><!></div>');function nc(e,t){ue(t,!1);let n=w(t,"style",12,void 0),r=w(t,"x",12,void 0),o=w(t,"y",12,void 0);const i=tc(),s=ur("svelteflow__edge_id");return He(),ec(e,{children:(a,l)=>{var u=W0(),c=X(u);wt(c,t,"default",{}),Z(u),Ee(()=>{de(u,"style","pointer-events: all;"+n()),at(u,"transform",`translate(-50%, -50%) translate(${r()??""}px,${o()??""}px)`)}),Ze("keyup",u,()=>{}),Ze("click",u,()=>{s&&i(s)}),L(a,u)},$$slots:{default:!0}}),ce({get style(){return n()},set style(a){n(a),y()},get x(){return r()},set x(a){r(a),y()},get y(){return o()},set y(a){o(a),y()}})}ie(nc,{style:{},x:{},y:{}},["default"],[],!0);var K0=_e('<path fill="none" class="svelte-flow__edge-interaction"></path>'),q0=_e('<path fill="none"></path><!><!>',1);function Eo(e,t){ue(t,!1);let n=w(t,"id",12,void 0),r=w(t,"path",12),o=w(t,"label",12,void 0),i=w(t,"labelX",12,void 0),s=w(t,"labelY",12,void 0),a=w(t,"labelStyle",12,void 0),l=w(t,"markerStart",12,void 0),u=w(t,"markerEnd",12,void 0),c=w(t,"style",12,void 0),f=w(t,"interactionWidth",12,20),d=w(t,"class",12,void 0),g=f()===void 0?20:f();He();var p=q0(),x=xe(p),C=z(x);{var $=v=>{var b=K0();de(b,"stroke-opacity",0),de(b,"stroke-width",g),Ee(()=>de(b,"d",r())),L(v,b)};ke(C,v=>{g&&v($)})}var m=z(C);{var _=v=>{nc(v,{get x(){return i()},get y(){return s()},get style(){return a()},children:(b,N)=>{Pe();var E=Ae();Ee(()=>Bt(E,o())),L(b,E)},$$slots:{default:!0}})};ke(m,v=>{o()&&v(_)})}return Ee(v=>{de(x,"id",n()),de(x,"d",r()),$t(x,0,wn(v)),de(x,"marker-start",l()),de(x,"marker-end",u()),de(x,"style",c())},[()=>Et(["svelte-flow__edge-path",d()])],ve),L(e,p),ce({get id(){return n()},set id(v){n(v),y()},get path(){return r()},set path(v){r(v),y()},get label(){return o()},set label(v){o(v),y()},get labelX(){return i()},set labelX(v){i(v),y()},get labelY(){return s()},set labelY(v){s(v),y()},get labelStyle(){return a()},set labelStyle(v){a(v),y()},get markerStart(){return l()},set markerStart(v){l(v),y()},get markerEnd(){return u()},set markerEnd(v){u(v),y()},get style(){return c()},set style(v){c(v),y()},get interactionWidth(){return f()},set interactionWidth(v){f(v),y()},get class(){return d()},set class(v){d(v),y()}})}ie(Eo,{id:{},path:{},label:{},labelX:{},labelY:{},labelStyle:{},markerStart:{},markerEnd:{},style:{},interactionWidth:{},class:{}},[],[],!0);function zi(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["label","labelStyle","style","markerStart","markerEnd","interactionWidth","sourceX","sourceY","sourcePosition","targetX","targetY","targetPosition"]),ue(t,!1);const r=re(),o=re(),i=re();let s=w(t,"label",12,void 0),a=w(t,"labelStyle",12,void 0),l=w(t,"style",12,void 0),u=w(t,"markerStart",12,void 0),c=w(t,"markerEnd",12,void 0),f=w(t,"interactionWidth",12,void 0),d=w(t,"sourceX",12),g=w(t,"sourceY",12),p=w(t,"sourcePosition",12),x=w(t,"targetX",12),C=w(t,"targetY",12),$=w(t,"targetPosition",12);return ge(()=>(h(r),h(o),h(i),j(d()),j(g()),j(x()),j(C()),j(p()),j($())),()=>{(m=>(U(r,m[0]),U(o,m[1]),U(i,m[2])))(Tu({sourceX:d(),sourceY:g(),targetX:x(),targetY:C(),sourcePosition:p(),targetPosition:$()}))}),vt(),He(),Eo(e,{get path(){return h(r)},get labelX(){return h(o)},get labelY(){return h(i)},get label(){return s()},get labelStyle(){return a()},get markerStart(){return u()},get markerEnd(){return c()},get interactionWidth(){return f()},get style(){return l()}}),ce({get label(){return s()},set label(m){s(m),y()},get labelStyle(){return a()},set labelStyle(m){a(m),y()},get style(){return l()},set style(m){l(m),y()},get markerStart(){return u()},set markerStart(m){u(m),y()},get markerEnd(){return c()},set markerEnd(m){c(m),y()},get interactionWidth(){return f()},set interactionWidth(m){f(m),y()},get sourceX(){return d()},set sourceX(m){d(m),y()},get sourceY(){return g()},set sourceY(m){g(m),y()},get sourcePosition(){return p()},set sourcePosition(m){p(m),y()},get targetX(){return x()},set targetX(m){x(m),y()},get targetY(){return C()},set targetY(m){C(m),y()},get targetPosition(){return $()},set targetPosition(m){$(m),y()}})}ie(zi,{label:{},labelStyle:{},style:{},markerStart:{},markerEnd:{},interactionWidth:{},sourceX:{},sourceY:{},sourcePosition:{},targetX:{},targetY:{},targetPosition:{}},[],[],!0);function rc(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["label","labelStyle","style","markerStart","markerEnd","interactionWidth","sourceX","sourceY","sourcePosition","targetX","targetY","targetPosition"]),ue(t,!1);const r=re(),o=re(),i=re();let s=w(t,"label",12,void 0),a=w(t,"labelStyle",12,void 0),l=w(t,"style",12,void 0),u=w(t,"markerStart",12,void 0),c=w(t,"markerEnd",12,void 0),f=w(t,"interactionWidth",12,void 0),d=w(t,"sourceX",12),g=w(t,"sourceY",12),p=w(t,"sourcePosition",12),x=w(t,"targetX",12),C=w(t,"targetY",12),$=w(t,"targetPosition",12);return ge(()=>(h(r),h(o),h(i),j(d()),j(g()),j(x()),j(C()),j(p()),j($())),()=>{(m=>(U(r,m[0]),U(o,m[1]),U(i,m[2])))(Li({sourceX:d(),sourceY:g(),targetX:x(),targetY:C(),sourcePosition:p(),targetPosition:$()}))}),vt(),He(),Eo(e,{get path(){return h(r)},get labelX(){return h(o)},get labelY(){return h(i)},get label(){return s()},get labelStyle(){return a()},get markerStart(){return u()},get markerEnd(){return c()},get interactionWidth(){return f()},get style(){return l()}}),ce({get label(){return s()},set label(m){s(m),y()},get labelStyle(){return a()},set labelStyle(m){a(m),y()},get style(){return l()},set style(m){l(m),y()},get markerStart(){return u()},set markerStart(m){u(m),y()},get markerEnd(){return c()},set markerEnd(m){c(m),y()},get interactionWidth(){return f()},set interactionWidth(m){f(m),y()},get sourceX(){return d()},set sourceX(m){d(m),y()},get sourceY(){return g()},set sourceY(m){g(m),y()},get sourcePosition(){return p()},set sourcePosition(m){p(m),y()},get targetX(){return x()},set targetX(m){x(m),y()},get targetY(){return C()},set targetY(m){C(m),y()},get targetPosition(){return $()},set targetPosition(m){$(m),y()}})}ie(rc,{label:{},labelStyle:{},style:{},markerStart:{},markerEnd:{},interactionWidth:{},sourceX:{},sourceY:{},sourcePosition:{},targetX:{},targetY:{},targetPosition:{}},[],[],!0);function oc(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["label","labelStyle","style","markerStart","markerEnd","interactionWidth","sourceX","sourceY","targetX","targetY"]),ue(t,!1);const r=re(),o=re(),i=re();let s=w(t,"label",12,void 0),a=w(t,"labelStyle",12,void 0),l=w(t,"style",12,void 0),u=w(t,"markerStart",12,void 0),c=w(t,"markerEnd",12,void 0),f=w(t,"interactionWidth",12,void 0),d=w(t,"sourceX",12),g=w(t,"sourceY",12),p=w(t,"targetX",12),x=w(t,"targetY",12);return ge(()=>(h(r),h(o),h(i),j(d()),j(g()),j(p()),j(x())),()=>{(C=>(U(r,C[0]),U(o,C[1]),U(i,C[2])))(ea({sourceX:d(),sourceY:g(),targetX:p(),targetY:x()}))}),vt(),He(),Eo(e,{get path(){return h(r)},get labelX(){return h(o)},get labelY(){return h(i)},get label(){return s()},get labelStyle(){return a()},get markerStart(){return u()},get markerEnd(){return c()},get interactionWidth(){return f()},get style(){return l()}}),ce({get label(){return s()},set label(C){s(C),y()},get labelStyle(){return a()},set labelStyle(C){a(C),y()},get style(){return l()},set style(C){l(C),y()},get markerStart(){return u()},set markerStart(C){u(C),y()},get markerEnd(){return c()},set markerEnd(C){c(C),y()},get interactionWidth(){return f()},set interactionWidth(C){f(C),y()},get sourceX(){return d()},set sourceX(C){d(C),y()},get sourceY(){return g()},set sourceY(C){g(C),y()},get targetX(){return p()},set targetX(C){p(C),y()},get targetY(){return x()},set targetY(C){x(C),y()}})}ie(oc,{label:{},labelStyle:{},style:{},markerStart:{},markerEnd:{},interactionWidth:{},sourceX:{},sourceY:{},targetX:{},targetY:{}},[],[],!0);function ic(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]);it(n,["label","labelStyle","style","markerStart","markerEnd","interactionWidth","sourceX","sourceY","sourcePosition","targetX","targetY","targetPosition"]),ue(t,!1);const r=re(),o=re(),i=re();let s=w(t,"label",12,void 0),a=w(t,"labelStyle",12,void 0),l=w(t,"style",12,void 0),u=w(t,"markerStart",12,void 0),c=w(t,"markerEnd",12,void 0),f=w(t,"interactionWidth",12,void 0),d=w(t,"sourceX",12),g=w(t,"sourceY",12),p=w(t,"sourcePosition",12),x=w(t,"targetX",12),C=w(t,"targetY",12),$=w(t,"targetPosition",12);return ge(()=>(h(r),h(o),h(i),j(d()),j(g()),j(x()),j(C()),j(p()),j($())),()=>{(m=>(U(r,m[0]),U(o,m[1]),U(i,m[2])))(Li({sourceX:d(),sourceY:g(),targetX:x(),targetY:C(),sourcePosition:p(),targetPosition:$(),borderRadius:0}))}),vt(),He(),Eo(e,{get path(){return h(r)},get labelX(){return h(o)},get labelY(){return h(i)},get label(){return s()},get labelStyle(){return a()},get markerStart(){return u()},get markerEnd(){return c()},get interactionWidth(){return f()},get style(){return l()}}),ce({get label(){return s()},set label(m){s(m),y()},get labelStyle(){return a()},set labelStyle(m){a(m),y()},get style(){return l()},set style(m){l(m),y()},get markerStart(){return u()},set markerStart(m){u(m),y()},get markerEnd(){return c()},set markerEnd(m){c(m),y()},get interactionWidth(){return f()},set interactionWidth(m){f(m),y()},get sourceX(){return d()},set sourceX(m){d(m),y()},get sourceY(){return g()},set sourceY(m){g(m),y()},get sourcePosition(){return p()},set sourcePosition(m){p(m),y()},get targetX(){return x()},set targetX(m){x(m),y()},get targetY(){return C()},set targetY(m){C(m),y()},get targetPosition(){return $()},set targetPosition(m){$(m),y()}})}ie(ic,{label:{},labelStyle:{},style:{},markerStart:{},markerEnd:{},interactionWidth:{},sourceX:{},sourceY:{},sourcePosition:{},targetX:{},targetY:{},targetPosition:{}},[],[],!0);function G0(e,t){const n=e.set,r=t.set,o=q(e),i=q(t);let a=o.length===0&&i.length>0?i:o;e.set(a);const l=u=>{const c=n(u);return a=c,r(a),c};e.set=t.set=l,e.update=t.update=u=>l(u(a))}function U0(e,t){const n=e.set,r=t.set;let o=q(t);e.set(o);const i=s=>{n(s),r(s),o=s};e.set=t.set=i,e.update=t.update=s=>i(s(o))}const j0=(e,t,n)=>{if(!n)return;const r=q(e),o=t.set,i=n.set;let s=n?q(n):{x:0,y:0,zoom:1};t.set(s),t.set=a=>(o(a),i(a),s=a,a),n.set=a=>(r==null||r.syncViewport(a),o(a),i(a),s=a,a),t.update=a=>{t.set(a(s))},n.update=a=>{n.set(a(s))}},J0=(e,t,n,r=[0,0],o=Ni)=>{const{subscribe:i,set:s,update:a}=we([]);let l=e,u={},c=!0;const f=x=>(Ou(x,t,n,{elevateNodesOnSelect:c,nodeOrigin:r,nodeExtent:o,defaults:u,checkEquality:!1}),l=x,s(l),l),d=x=>f(x(l)),g=x=>{u=x},p=x=>{c=x.elevateNodesOnSelect??c};return f(l),{subscribe:i,set:f,update:d,setDefaultOptions:g,setOptions:p}},Q0=(e,t,n,r)=>{const{subscribe:o,set:i,update:s}=we([]);let a=e,l={};const u=d=>{const g=l?d.map(p=>({...l,...p})):d;Ru(t,n,g),a=g,i(a)},c=d=>u(d(a)),f=d=>{l=d};return u(a),{subscribe:o,set:u,update:c,setDefaultOptions:f}},sc={input:Uu,output:ju,default:Ii,group:Ju},ac={straight:oc,smoothstep:rc,default:zi,step:ic},e2=({nodes:e=[],edges:t=[],width:n,height:r,fitView:o,nodeOrigin:i,nodeExtent:s})=>{const a=new Map,l=new Map,u=new Map,c=new Map,f=i??[0,0],d=s??Ni;Ou(e,a,l,{nodeExtent:d,nodeOrigin:f,elevateNodesOnSelect:!1,checkEquality:!1}),Ru(u,c,t);let g={x:0,y:0,zoom:1};if(o&&n&&r){const p=bo(a,{filter:x=>!!((x.width||x.initialWidth)&&(x.height||x.initialHeight))});g=js(p,n,r,.5,2,.1)}return{flowId:we(null),nodes:J0(e,a,l,f,d),nodeLookup:Gt(a),parentLookup:Gt(l),edgeLookup:Gt(c),visibleNodes:Gt([]),edges:Q0(t,u,c),visibleEdges:Gt([]),connectionLookup:Gt(u),height:we(500),width:we(500),minZoom:we(.5),maxZoom:we(2),nodeOrigin:we(f),nodeDragThreshold:we(1),nodeExtent:we(d),translateExtent:we(Ni),autoPanOnNodeDrag:we(!0),autoPanOnConnect:we(!0),fitViewOnInit:we(!1),fitViewOnInitDone:we(!1),fitViewOptions:we(void 0),panZoom:we(null),snapGrid:we(null),dragging:we(!1),selectionRect:we(null),selectionKeyPressed:we(!1),multiselectionKeyPressed:we(!1),deleteKeyPressed:we(!1),panActivationKeyPressed:we(!1),zoomActivationKeyPressed:we(!1),selectionRectMode:we(null),selectionMode:we(Ti.Partial),nodeTypes:we(sc),edgeTypes:we(ac),viewport:we(g),connectionMode:we(hr.Strict),domNode:we(null),connection:Gt(Ws),connectionLineType:we(Ar.Bezier),connectionRadius:we(20),isValidConnection:we(()=>!0),nodesDraggable:we(!0),nodesConnectable:we(!0),elementsSelectable:we(!0),selectNodesOnDrag:we(!0),markers:Gt([]),defaultMarkerColor:we("#b1b1b7"),lib:Gt("svelte"),onlyRenderVisibleElements:we(!1),onerror:we(Qv),ondelete:we(void 0),onedgecreate:we(void 0),onconnect:we(void 0),onconnectstart:we(void 0),onconnectend:we(void 0),onbeforedelete:we(void 0),nodesInitialized:we(!1),edgesInitialized:we(!1),viewportInitialized:we(!1),initialized:Gt(!1)}};function t2(e){const t=Un([e.edges,e.nodes,e.nodeLookup,e.onlyRenderVisibleElements,e.viewport,e.width,e.height],([n,,r,o,i,s,a])=>o&&s&&a?n.filter(u=>{const c=r.get(u.source),f=r.get(u.target);return c&&f&&s0({sourceNode:c,targetNode:f,width:s,height:a,transform:[i.x,i.y,i.zoom]})}):n);return Un([t,e.nodes,e.nodeLookup,e.connectionMode,e.onerror],([n,,r,o,i])=>n.reduce((a,l)=>{const u=r.get(l.source),c=r.get(l.target);if(!u||!c)return a;const f=g0({id:l.id,sourceNode:u,targetNode:c,sourceHandle:l.sourceHandle||null,targetHandle:l.targetHandle||null,connectionMode:o,onError:i});return f&&a.push({...l,zIndex:i0({selected:l.selected,zIndex:l.zIndex,sourceNode:u,targetNode:c,elevateOnSelect:!1}),...f}),a},[]))}function n2(e){return Un([e.nodeLookup,e.onlyRenderVisibleElements,e.width,e.height,e.viewport,e.nodes],([t,n,r,o,i])=>{const s=[i.x,i.y,i.zoom];return n?pu(t,{x:0,y:0,width:r,height:o},s,!0):Array.from(t.values())})}const Ri=Symbol();function lc({nodes:e,edges:t,width:n,height:r,fitView:o,nodeOrigin:i,nodeExtent:s}){const a=e2({nodes:e,edges:t,width:n,height:r,fitView:o,nodeOrigin:i,nodeExtent:s});function l(k){a.nodeTypes.set({...sc,...k})}function u(k){a.edgeTypes.set({...ac,...k})}function c(k){const P=q(a.edges);a.edges.set(u0(k,P))}const f=(k,P=!1)=>{var I;const H=q(a.nodeLookup);for(const[B,F]of k){const K=(I=H.get(B))==null?void 0:I.internals.userNode;K&&(K.position=F.position,K.dragging=P)}a.nodes.update(B=>B)};function d(k){var F,K,se;const P=q(a.nodeLookup),H=q(a.parentLookup),{changes:I,updatedInternals:B}=x0(k,P,q(a.parentLookup),q(a.domNode),q(a.nodeOrigin));if(B){if(m0(P,H,{nodeOrigin:i,nodeExtent:s}),!q(a.fitViewOnInitDone)&&q(a.fitViewOnInit)){const ee=q(a.fitViewOptions),W=p({...ee,nodes:ee==null?void 0:ee.nodes});a.fitViewOnInitDone.set(W)}for(const ee of I){const W=(F=P.get(ee.id))==null?void 0:F.internals.userNode;if(W)switch(ee.type){case"dimensions":{const fe={...W.measured,...ee.dimensions};ee.setAttributes&&(W.width=((K=ee.dimensions)==null?void 0:K.width)??W.width,W.height=((se=ee.dimensions)==null?void 0:se.height)??W.height),W.measured=fe;break}case"position":W.position=ee.position??W.position;break}}a.nodes.update(ee=>ee),q(a.nodesInitialized)||a.nodesInitialized.set(!0)}}function g(k){const P=q(a.panZoom),H=q(a.domNode);if(!P||!H)return Promise.resolve(!1);const{width:I,height:B}=Qs(H),F=mu(q(a.nodeLookup),k);return yu({nodes:F,width:I,height:B,minZoom:q(a.minZoom),maxZoom:q(a.maxZoom),panZoom:P},k)}function p(k){const P=q(a.panZoom);if(!P)return!1;const H=mu(q(a.nodeLookup),k);return yu({nodes:H,width:q(a.width),height:q(a.height),minZoom:q(a.minZoom),maxZoom:q(a.maxZoom),panZoom:P},k),H.size>0}function x(k,P){const H=q(a.panZoom);return H?H.scaleBy(k,P):Promise.resolve(!1)}function C(k){return x(1.2,k)}function $(k){return x(1/1.2,k)}function m(k){const P=q(a.panZoom);P&&(P.setScaleExtent([k,q(a.maxZoom)]),a.minZoom.set(k))}function _(k){const P=q(a.panZoom);P&&(P.setScaleExtent([q(a.minZoom),k]),a.maxZoom.set(k))}function v(k){const P=q(a.panZoom);P&&(P.setTranslateExtent(k),a.translateExtent.set(k))}function b(k){let P=!1;return k.forEach(H=>{H.selected&&(H.selected=!1,P=!0)}),P}function N(k){var P;(P=q(a.panZoom))==null||P.setClickDistance(k)}function E(k){b((k==null?void 0:k.nodes)||q(a.nodes))&&a.nodes.set(q(a.nodes)),b((k==null?void 0:k.edges)||q(a.edges))&&a.edges.set(q(a.edges))}a.deleteKeyPressed.subscribe(async k=>{var P;if(k){const H=q(a.nodes),I=q(a.edges),B=H.filter(ee=>ee.selected),F=I.filter(ee=>ee.selected),{nodes:K,edges:se}=await wu({nodesToRemove:B,edgesToRemove:F,nodes:H,edges:I,onBeforeDelete:q(a.onbeforedelete)});(K.length||se.length)&&(a.nodes.update(ee=>ee.filter(W=>!K.some(fe=>fe.id===W.id))),a.edges.update(ee=>ee.filter(W=>!se.some(fe=>fe.id===W.id))),(P=q(a.ondelete))==null||P({nodes:K,edges:se}))}});function T(k){const P=q(a.multiselectionKeyPressed);a.nodes.update(H=>H.map(I=>{const B=k.includes(I.id),F=P&&I.selected||B;return I.selected=F,I})),P||a.edges.update(H=>H.map(I=>(I.selected=!1,I)))}function D(k){const P=q(a.multiselectionKeyPressed);a.edges.update(H=>H.map(I=>{const B=k.includes(I.id),F=P&&I.selected||B;return I.selected=F,I})),P||a.nodes.update(H=>H.map(I=>(I.selected=!1,I)))}function V(k){var H;const P=(H=q(a.nodes))==null?void 0:H.find(I=>I.id===k);if(!P){console.warn("012",Dr.error012(k));return}a.selectionRect.set(null),a.selectionRectMode.set(null),P.selected?P.selected&&q(a.multiselectionKeyPressed)&&E({nodes:[P],edges:[]}):T([k])}function A(k){const P=q(a.viewport);return b0({delta:k,panZoom:q(a.panZoom),transform:[P.x,P.y,P.zoom],translateExtent:q(a.translateExtent),width:q(a.width),height:q(a.height)})}const O=we(Ws),R=k=>{O.set({...k})};function S(){O.set(Ws)}function M(){a.fitViewOnInitDone.set(!1),a.selectionRect.set(null),a.selectionRectMode.set(null),a.snapGrid.set(null),a.isValidConnection.set(()=>!0),E(),S()}return{...a,visibleEdges:t2(a),visibleNodes:n2(a),connection:Un([O,a.viewport],([k,P])=>k.inProgress?{...k,to:ko(k.to,[P.x,P.y,P.zoom])}:{...k}),markers:Un([a.edges,a.defaultMarkerColor,a.flowId],([k,P,H])=>h0(k,{defaultColor:P,id:H})),initialized:(()=>{let k=!1;const P=q(a.nodes).length,H=q(a.edges).length;return Un([a.nodesInitialized,a.edgesInitialized,a.viewportInitialized],([I,B,F])=>k||(P===0?k=F:H===0?k=F&&I:k=F&&I&&B,k))})(),syncNodeStores:k=>G0(a.nodes,k),syncEdgeStores:k=>U0(a.edges,k),syncViewport:k=>j0(a.panZoom,a.viewport,k),setNodeTypes:l,setEdgeTypes:u,addEdge:c,updateNodePositions:f,updateNodeInternals:d,zoomIn:C,zoomOut:$,fitView:k=>g(k),setMinZoom:m,setMaxZoom:_,setTranslateExtent:v,setPaneClickDistance:N,unselectNodesAndEdges:E,addSelectedNodes:T,addSelectedEdges:D,handleNodeSelection:V,panBy:A,updateConnection:R,cancelConnection:S,reset:M}}function Ue(){const e=ur(Ri);if(!e)throw new Error("In order to use useStore you need to wrap your component in a <SvelteFlowProvider />");return e.getStore()}function r2({nodes:e,edges:t,width:n,height:r,fitView:o,nodeOrigin:i,nodeExtent:s}){const a=lc({nodes:e,edges:t,width:n,height:r,fitView:o,nodeOrigin:i,nodeExtent:s});return Sr(Ri,{getStore:()=>a}),a}function la(e,t){const{panZoom:n,minZoom:r,maxZoom:o,initialViewport:i,viewport:s,dragging:a,translateExtent:l,paneClickDistance:u}=t,c=R0({domNode:e,minZoom:r,maxZoom:o,translateExtent:l,viewport:i,paneClickDistance:u,onDraggingChange:a.set}),f=c.getViewport();return s.set(f),n.set(c),c.update(t),{update(d){c.update(d)}}}var o2=ne('<div class="svelte-flow__zoom svelte-4xkw84"><!></div>');const i2={hash:"svelte-4xkw84",code:".svelte-flow__zoom.svelte-4xkw84 {width:100%;height:100%;position:absolute;top:0;left:0;z-index:4;}"};function uc(e,t){ue(t,!1),et(e,i2);const[n,r]=nt(),o=()=>Q(H,"$panActivationKeyPressed",n),i=()=>Q(R,"$minZoom",n),s=()=>Q(S,"$maxZoom",n),a=()=>Q(I,"$zoomActivationKeyPressed",n),l=()=>Q(O,"$selectionRect",n),u=()=>Q(k,"$translateExtent",n),c=()=>Q(P,"$lib",n),f=re(),d=re(),g=re();let p=w(t,"initialViewport",12,void 0),x=w(t,"onMoveStart",12,void 0),C=w(t,"onMove",12,void 0),$=w(t,"onMoveEnd",12,void 0),m=w(t,"panOnScrollMode",12),_=w(t,"preventScrolling",12),v=w(t,"zoomOnScroll",12),b=w(t,"zoomOnDoubleClick",12),N=w(t,"zoomOnPinch",12),E=w(t,"panOnDrag",12),T=w(t,"panOnScroll",12),D=w(t,"paneClickDistance",12);const{viewport:V,panZoom:A,selectionRect:O,minZoom:R,maxZoom:S,dragging:M,translateExtent:k,lib:P,panActivationKeyPressed:H,zoomActivationKeyPressed:I,viewportInitialized:B}=Ue(),F=W=>V.set({x:W[0],y:W[1],zoom:W[2]});rn(()=>{ai(B,!0)}),ge(()=>j(p()),()=>{U(f,p()||{x:0,y:0,zoom:1})}),ge(()=>(o(),j(E())),()=>{U(d,o()||E())}),ge(()=>(o(),j(T())),()=>{U(g,o()||T())}),vt(),He();var K=o2(),se=X(K);wt(se,t,"default",{}),Z(K),_t(K,(W,fe)=>la==null?void 0:la(W,fe),()=>({viewport:V,minZoom:i(),maxZoom:s(),initialViewport:h(f),dragging:M,panZoom:A,onPanZoomStart:x(),onPanZoom:C(),onPanZoomEnd:$(),zoomOnScroll:v(),zoomOnDoubleClick:b(),zoomOnPinch:N(),panOnScroll:h(g),panOnDrag:h(d),panOnScrollSpeed:.5,panOnScrollMode:m()||Jn.Free,zoomActivationKeyPressed:a(),preventScrolling:typeof _()=="boolean"?_():!0,noPanClassName:"nopan",noWheelClassName:"nowheel",userSelectionActive:!!l(),translateExtent:u(),lib:c(),paneClickDistance:D(),onTransformChange:F})),L(e,K);var ee=ce({get initialViewport(){return p()},set initialViewport(W){p(W),y()},get onMoveStart(){return x()},set onMoveStart(W){x(W),y()},get onMove(){return C()},set onMove(W){C(W),y()},get onMoveEnd(){return $()},set onMoveEnd(W){$(W),y()},get panOnScrollMode(){return m()},set panOnScrollMode(W){m(W),y()},get preventScrolling(){return _()},set preventScrolling(W){_(W),y()},get zoomOnScroll(){return v()},set zoomOnScroll(W){v(W),y()},get zoomOnDoubleClick(){return b()},set zoomOnDoubleClick(W){b(W),y()},get zoomOnPinch(){return N()},set zoomOnPinch(W){N(W),y()},get panOnDrag(){return E()},set panOnDrag(W){E(W),y()},get panOnScroll(){return T()},set panOnScroll(W){T(W),y()},get paneClickDistance(){return D()},set paneClickDistance(W){D(W),y()}});return r(),ee}ie(uc,{initialViewport:{},onMoveStart:{},onMove:{},onMoveEnd:{},panOnScrollMode:{},preventScrolling:{},zoomOnScroll:{},zoomOnDoubleClick:{},zoomOnPinch:{},panOnDrag:{},panOnScroll:{},paneClickDistance:{}},["default"],[],!0);function cc(e,t){return n=>{n.target===t&&(e==null||e(n))}}function dc(e){return t=>{const n=e.includes(t.id);return t.selected!==n&&(t.selected=n),t}}var s2=ne("<div><!></div>");const a2={hash:"svelte-1esy7hx",code:".svelte-flow__pane.svelte-1esy7hx {position:absolute;top:0;left:0;width:100%;height:100%;}"};function fc(e,t){ue(t,!1),et(e,a2);const[n,r]=nt(),o=()=>Q(S,"$panActivationKeyPressed",n),i=()=>Q(O,"$selectionKeyPressed",n),s=()=>Q(V,"$selectionRect",n),a=()=>Q(D,"$elementsSelectable",n),l=()=>Q(A,"$selectionRectMode",n),u=()=>Q(N,"$edges",n),c=()=>Q(b,"$nodeLookup",n),f=()=>Q(E,"$viewport",n),d=()=>Q(R,"$selectionMode",n),g=()=>Q(T,"$dragging",n),p=re(),x=re(),C=re();let $=w(t,"panOnDrag",12,void 0),m=w(t,"selectionOnDrag",12,void 0);const _=ii(),{nodes:v,nodeLookup:b,edges:N,viewport:E,dragging:T,elementsSelectable:D,selectionRect:V,selectionRectMode:A,selectionKeyPressed:O,selectionMode:R,panActivationKeyPressed:S,unselectNodesAndEdges:M}=Ue();let k=re(),P=null,H=[],I=!1;function B(G){if(I){I=!1;return}_("paneclick",{event:G}),M(),A.set(null)}function F(G){var Le,Xe;if(P=h(k).getBoundingClientRect(),!D||!h(x)||G.button!==0||G.target!==h(k)||!P)return;(Xe=(Le=G.target)==null?void 0:Le.setPointerCapture)==null||Xe.call(Le,G.pointerId);const{x:ae,y:Me}=Rn(G,P);M(),V.set({width:0,height:0,startX:ae,startY:Me,x:ae,y:Me})}function K(G){if(!h(x)||!P||!s())return;I=!0;const ae=Rn(G,P),Me=s().startX??0,Le=s().startY??0,Xe={...s(),x:ae.x<Me?ae.x:Me,y:ae.y<Le?ae.y:Le,width:Math.abs(ae.x-Me),height:Math.abs(ae.y-Le)},te=H.map(oe=>oe.id),Fe=qs(H,u()).map(oe=>oe.id);H=pu(c(),Xe,[f().x,f().y,f().zoom],d()===Ti.Partial,!0);const Oe=qs(H,u()).map(oe=>oe.id),rt=H.map(oe=>oe.id);(te.length!==rt.length||rt.some(oe=>!te.includes(oe)))&&v.update(oe=>oe.map(dc(rt))),(Fe.length!==Oe.length||Oe.some(oe=>!Fe.includes(oe)))&&N.update(oe=>oe.map(dc(Oe))),A.set("user"),V.set(Xe)}function se(G){var ae,Me;G.button===0&&((Me=(ae=G.target)==null?void 0:ae.releasePointerCapture)==null||Me.call(ae,G.pointerId),!h(x)&&l()==="user"&&G.target===h(k)&&(B==null||B(G)),V.set(null),H.length>0&&ai(A,"nodes"),i()&&(I=!1))}const ee=G=>{var ae;if(Array.isArray(h(p))&&((ae=h(p))!=null&&ae.includes(2))){G.preventDefault();return}_("panecontextmenu",{event:G})};ge(()=>(o(),j($())),()=>{U(p,o()||$())}),ge(()=>(i(),s(),j(m()),h(p)),()=>{U(x,i()||s()||m()&&h(p)!==!0)}),ge(()=>(a(),h(x),l()),()=>{U(C,a()&&(h(x)||l()==="user"))}),vt(),He();var W=s2(),fe=Ne(()=>h(C)?void 0:cc(B,h(k))),me=Ne(()=>cc(ee,h(k)));let Ce;var he=X(W);wt(he,t,"default",{}),Z(W),An(W,G=>U(k,G),()=>h(k)),Ee(G=>Ce=$t(W,1,"svelte-flow__pane svelte-1esy7hx",null,Ce,{draggable:G,dragging:g(),selection:h(x)}),[()=>$()===!0||Array.isArray($())&&$().includes(0)],ve),Ze("click",W,function(...G){var ae;(ae=h(fe))==null||ae.apply(this,G)}),Ze("pointerdown",W,function(...G){var ae;(ae=h(C)?F:void 0)==null||ae.apply(this,G)}),Ze("pointermove",W,function(...G){var ae;(ae=h(C)?K:void 0)==null||ae.apply(this,G)}),Ze("pointerup",W,function(...G){var ae;(ae=h(C)?se:void 0)==null||ae.apply(this,G)}),Ze("contextmenu",W,function(...G){var ae;(ae=h(me))==null||ae.apply(this,G)}),L(e,W);var ze=ce({get panOnDrag(){return $()},set panOnDrag(G){$(G),y()},get selectionOnDrag(){return m()},set selectionOnDrag(G){m(G),y()}});return r(),ze}ie(fc,{panOnDrag:{},selectionOnDrag:{}},["default"],[],!0);var l2=ne('<div class="svelte-flow__viewport xyflow__viewport svelte-1floaup"><!></div>');const u2={hash:"svelte-1floaup",code:".svelte-flow__viewport.svelte-1floaup {width:100%;height:100%;position:absolute;top:0;left:0;}"};function gc(e,t){ue(t,!1),et(e,u2);const[n,r]=nt(),o=()=>Q(i,"$viewport",n),{viewport:i}=Ue();He();var s=l2(),a=X(s);wt(a,t,"default",{}),Z(s),Ee(()=>de(s,"style",`transform: translate(${o().x??""}px, ${o().y??""}px) scale(${o().zoom??""})`)),L(e,s),ce(),r()}ie(gc,{},["default"],[],!0);function Br(e,t){const{store:n,onDrag:r,onDragStart:o,onDragStop:i,onNodeMouseDown:s}=t,a=$0({onDrag:r,onDragStart:o,onDragStop:i,onNodeMouseDown:s,getStoreItems:()=>{const u=q(n.snapGrid),c=q(n.viewport);return{nodes:q(n.nodes),nodeLookup:q(n.nodeLookup),edges:q(n.edges),nodeExtent:q(n.nodeExtent),snapGrid:u||[0,0],snapToGrid:!!u,nodeOrigin:q(n.nodeOrigin),multiSelectionActive:q(n.multiselectionKeyPressed),domNode:q(n.domNode),transform:[c.x,c.y,c.zoom],autoPanOnNodeDrag:q(n.autoPanOnNodeDrag),nodesDraggable:q(n.nodesDraggable),selectNodesOnDrag:q(n.selectNodesOnDrag),nodeDragThreshold:q(n.nodeDragThreshold),unselectNodesAndEdges:n.unselectNodesAndEdges,updateNodePositions:n.updateNodePositions,panBy:n.panBy}}});function l(u,c){if(c.disabled){a.destroy();return}a.update({domNode:u,noDragClassName:c.noDragClass,handleSelector:c.handleSelector,nodeId:c.nodeId,isSelectable:c.isSelectable,nodeClickDistance:c.nodeClickDistance})}return l(e,t),{update(u){l(e,u)},destroy(){a.destroy()}}}function c2({width:e,height:t,initialWidth:n,initialHeight:r,measuredWidth:o,measuredHeight:i}){if(o===void 0&&i===void 0){const s=e??n,a=t??r;return{width:s?`width:${s}px;`:"",height:a?`height:${a}px;`:""}}return{width:e?`width:${e}px;`:"",height:t?`height:${t}px;`:""}}var d2=ne("<div><!></div>");function hc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(me,"$nodeTypes",n),i=()=>Q(ae,"$elementsSelectable",n),s=()=>Q(Me,"$nodesDraggable",n),a=()=>Q(Fe,"$connectableStore",n),l=re(void 0,!0),u=re(void 0,!0),c=re(void 0,!0),f=re(void 0,!0);let d=w(t,"node",13),g=w(t,"id",13),p=w(t,"data",29,()=>({})),x=w(t,"selected",13,!1),C=w(t,"draggable",13,void 0),$=w(t,"selectable",13,void 0),m=w(t,"connectable",13,!0),_=w(t,"deletable",13,!0),v=w(t,"hidden",13,!1),b=w(t,"dragging",13,!1),N=w(t,"resizeObserver",13,null),E=w(t,"style",13,void 0),T=w(t,"type",13,"default"),D=w(t,"isParent",13,!1),V=w(t,"positionX",13),A=w(t,"positionY",13),O=w(t,"sourcePosition",13,void 0),R=w(t,"targetPosition",13,void 0),S=w(t,"zIndex",13),M=w(t,"measuredWidth",13,void 0),k=w(t,"measuredHeight",13,void 0),P=w(t,"initialWidth",13,void 0),H=w(t,"initialHeight",13,void 0),I=w(t,"width",13,void 0),B=w(t,"height",13,void 0),F=w(t,"dragHandle",13,void 0),K=w(t,"initialized",13,!1),se=w(t,"parentId",13,void 0),ee=w(t,"nodeClickDistance",13,void 0),W=w(t,"class",13,"");const fe=Ue(),{nodeTypes:me,nodeDragThreshold:Ce,selectNodesOnDrag:he,handleNodeSelection:ze,updateNodeInternals:G,elementsSelectable:ae,nodesDraggable:Me}=fe;let Le=re(void 0,!0),Xe=re(null,!0);const te=ii(),Fe=we(m());let Oe=re(void 0,!0),rt=re(void 0,!0),oe=re(void 0,!0);Sr("svelteflow__node_id",g()),Sr("svelteflow__node_connectable",Fe),$s(()=>{var J;h(Xe)&&((J=N())==null||J.unobserve(h(Xe)))});function pe(J){$()&&(!q(he)||!C()||q(Ce)>0)&&ze(g()),te("nodeclick",{node:d().internals.userNode,event:J})}ge(()=>j(T()),()=>{U(l,T()||"default")}),ge(()=>(o(),h(l)),()=>{U(u,!!o()[h(l)])}),ge(()=>(o(),h(l),Ii),()=>{U(c,o()[h(l)]||Ii)}),ge(()=>(h(u),j(T())),()=>{h(u)||console.warn("003",Dr.error003(T()))}),ge(()=>(j(I()),j(B()),j(P()),j(H()),j(M()),j(k())),()=>{U(f,c2({width:I(),height:B(),initialWidth:P(),initialHeight:H(),measuredWidth:M(),measuredHeight:k()}))}),ge(()=>j(m()),()=>{Fe.set(!!m())}),ge(()=>(h(Oe),h(l),h(rt),j(O()),h(oe),j(R()),j(g()),h(Le)),()=>{(h(Oe)&&h(l)!==h(Oe)||h(rt)&&O()!==h(rt)||h(oe)&&R()!==h(oe))&&requestAnimationFrame(()=>G(new Map([[g(),{id:g(),nodeElement:h(Le),force:!0}]]))),U(Oe,h(l)),U(rt,O()),U(oe,R())}),ge(()=>(j(N()),h(Le),h(Xe),j(K())),()=>{N()&&(h(Le)!==h(Xe)||!K())&&(h(Xe)&&N().unobserve(h(Xe)),h(Le)&&N().observe(h(Le)),U(Xe,h(Le)))}),vt(),He(!0);var be=tt(),Ie=xe(be);{var ht=J=>{var Re=d2();let le;var $n=X(Re);const fn=ve(()=>x()??!1),En=ve(()=>$()??i()??!0),Te=ve(()=>_()??!0),st=ve(()=>C()??s()??!0);fl($n,()=>h(c),(ye,lt)=>{lt(ye,{get data(){return p()},get id(){return g()},get selected(){return h(fn)},get selectable(){return h(En)},get deletable(){return h(Te)},get sourcePosition(){return O()},get targetPosition(){return R()},get zIndex(){return S()},get dragging(){return b()},get draggable(){return h(st)},get dragHandle(){return F()},get parentId(){return se()},get type(){return h(l)},get isConnectable(){return a()},get positionAbsoluteX(){return V()},get positionAbsoluteY(){return A()},get width(){return I()},get height(){return B()}})}),Z(Re),_t(Re,(ye,lt)=>Br==null?void 0:Br(ye,lt),()=>({nodeId:g(),isSelectable:$(),disabled:!1,handleSelector:F(),noDragClass:"nodrag",nodeClickDistance:ee(),onNodeMouseDown:ze,onDrag:(ye,lt,ct,Jt)=>{te("nodedrag",{event:ye,targetNode:ct,nodes:Jt})},onDragStart:(ye,lt,ct,Jt)=>{te("nodedragstart",{event:ye,targetNode:ct,nodes:Jt})},onDragStop:(ye,lt,ct,Jt)=>{te("nodedragstop",{event:ye,targetNode:ct,nodes:Jt})},store:fe})),An(Re,ye=>U(Le,ye),()=>h(Le)),Rt(()=>Ze("click",Re,pe)),Rt(()=>Ze("mouseenter",Re,ye=>te("nodemouseenter",{node:d(),event:ye}))),Rt(()=>Ze("mouseleave",Re,ye=>te("nodemouseleave",{node:d(),event:ye}))),Rt(()=>Ze("mousemove",Re,ye=>te("nodemousemove",{node:d(),event:ye}))),Rt(()=>Ze("contextmenu",Re,ye=>te("nodecontextmenu",{node:d(),event:ye}))),Ee(ye=>{de(Re,"data-id",g()),le=$t(Re,1,wn(ye),null,le,{dragging:b(),selected:x(),draggable:C(),connectable:m(),selectable:$(),nopan:C(),parent:D()}),de(Re,"style",`${E()??""};${h(f).width??""}${h(f).height??""}`),at(Re,"z-index",S()),at(Re,"transform",`translate(${V()??""}px, ${A()??""}px)`),at(Re,"visibility",K()?"visible":"hidden")},[()=>Et(["svelte-flow__node",`svelte-flow__node-${h(l)}`,W()])],ve),L(J,Re)};ke(Ie,J=>{v()||J(ht)})}L(e,be);var dt=ce({get node(){return d()},set node(J){d(J),y()},get id(){return g()},set id(J){g(J),y()},get data(){return p()},set data(J){p(J),y()},get selected(){return x()},set selected(J){x(J),y()},get draggable(){return C()},set draggable(J){C(J),y()},get selectable(){return $()},set selectable(J){$(J),y()},get connectable(){return m()},set connectable(J){m(J),y()},get deletable(){return _()},set deletable(J){_(J),y()},get hidden(){return v()},set hidden(J){v(J),y()},get dragging(){return b()},set dragging(J){b(J),y()},get resizeObserver(){return N()},set resizeObserver(J){N(J),y()},get style(){return E()},set style(J){E(J),y()},get type(){return T()},set type(J){T(J),y()},get isParent(){return D()},set isParent(J){D(J),y()},get positionX(){return V()},set positionX(J){V(J),y()},get positionY(){return A()},set positionY(J){A(J),y()},get sourcePosition(){return O()},set sourcePosition(J){O(J),y()},get targetPosition(){return R()},set targetPosition(J){R(J),y()},get zIndex(){return S()},set zIndex(J){S(J),y()},get measuredWidth(){return M()},set measuredWidth(J){M(J),y()},get measuredHeight(){return k()},set measuredHeight(J){k(J),y()},get initialWidth(){return P()},set initialWidth(J){P(J),y()},get initialHeight(){return H()},set initialHeight(J){H(J),y()},get width(){return I()},set width(J){I(J),y()},get height(){return B()},set height(J){B(J),y()},get dragHandle(){return F()},set dragHandle(J){F(J),y()},get initialized(){return K()},set initialized(J){K(J),y()},get parentId(){return se()},set parentId(J){se(J),y()},get nodeClickDistance(){return ee()},set nodeClickDistance(J){ee(J),y()},get class(){return W()},set class(J){W(J),y()}});return r(),dt}ie(hc,{node:{},id:{},data:{},selected:{},draggable:{},selectable:{},connectable:{},deletable:{},hidden:{},dragging:{},resizeObserver:{},style:{},type:{},isParent:{},positionX:{},positionY:{},sourcePosition:{},targetPosition:{},zIndex:{},measuredWidth:{},measuredHeight:{},initialWidth:{},initialHeight:{},width:{},height:{},dragHandle:{},initialized:{},parentId:{},nodeClickDistance:{},class:{}},[],[],!0);var f2=ne('<div class="svelte-flow__nodes svelte-tf4uy4"></div>');const g2={hash:"svelte-tf4uy4",code:".svelte-flow__nodes.svelte-tf4uy4 {width:100%;height:100%;position:absolute;left:0;top:0;}"};function vc(e,t){ue(t,!1),et(e,g2);const[n,r]=nt(),o=()=>Q(c,"$visibleNodes",n),i=()=>Q(f,"$nodesDraggable",n),s=()=>Q(g,"$elementsSelectable",n),a=()=>Q(d,"$nodesConnectable",n),l=()=>Q(x,"$parentLookup",n);let u=w(t,"nodeClickDistance",12,0);const{visibleNodes:c,nodesDraggable:f,nodesConnectable:d,elementsSelectable:g,updateNodeInternals:p,parentLookup:x}=Ue(),C=typeof ResizeObserver>"u"?null:new ResizeObserver(_=>{const v=new Map;_.forEach(b=>{const N=b.target.getAttribute("data-id");v.set(N,{id:N,nodeElement:b.target,force:!0})}),p(v)});$s(()=>{C==null||C.disconnect()}),He();var $=f2();Yt($,5,o,_=>_.id,(_,v)=>{const b=ve(()=>!!h(v).selected),N=ve(()=>!!h(v).hidden),E=ve(()=>!!(h(v).draggable||i()&&typeof h(v).draggable>"u")),T=ve(()=>!!(h(v).selectable||s()&&typeof h(v).selectable>"u")),D=ve(()=>!!(h(v).connectable||a()&&typeof h(v).connectable>"u")),V=ve(()=>h(v).deletable??!0),A=ve(()=>l().has(h(v).id)),O=ve(()=>h(v).type??"default"),R=ve(()=>h(v).internals.z??0),S=ve(()=>Eu(h(v)));hc(_,{get node(){return h(v)},get id(){return h(v).id},get data(){return h(v).data},get selected(){return h(b)},get hidden(){return h(N)},get draggable(){return h(E)},get selectable(){return h(T)},get connectable(){return h(D)},get deletable(){return h(V)},get positionX(){return h(v).internals.positionAbsolute.x},get positionY(){return h(v).internals.positionAbsolute.y},get isParent(){return h(A)},get style(){return h(v).style},get class(){return h(v).class},get type(){return h(O)},get sourcePosition(){return h(v).sourcePosition},get targetPosition(){return h(v).targetPosition},get dragging(){return h(v).dragging},get zIndex(){return h(R)},get dragHandle(){return h(v).dragHandle},get initialized(){return h(S)},get width(){return h(v).width},get height(){return h(v).height},get initialWidth(){return h(v).initialWidth},get initialHeight(){return h(v).initialHeight},get measuredWidth(){return h(v).measured.width},get measuredHeight(){return h(v).measured.height},get parentId(){return h(v).parentId},resizeObserver:C,get nodeClickDistance(){return u()},$$events:{nodeclick(M){De.call(this,t,M)},nodemouseenter(M){De.call(this,t,M)},nodemousemove(M){De.call(this,t,M)},nodemouseleave(M){De.call(this,t,M)},nodedrag(M){De.call(this,t,M)},nodedragstart(M){De.call(this,t,M)},nodedragstop(M){De.call(this,t,M)},nodecontextmenu(M){De.call(this,t,M)}}})}),Z($),L(e,$);var m=ce({get nodeClickDistance(){return u()},set nodeClickDistance(_){u(_),y()}});return r(),m}ie(vc,{nodeClickDistance:{}},[],[],!0);var h2=_e('<svg><g role="img"><!></g></svg>');function pc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(W,"$edgeTypes",n),i=()=>Q(fe,"$flowId",n),s=()=>Q(me,"$elementsSelectable",n),a=()=>Q(ee,"$edgeLookup",n),l=re(void 0,!0),u=re(void 0,!0),c=re(void 0,!0),f=re(void 0,!0),d=re(void 0,!0);let g=w(t,"id",13),p=w(t,"type",13,"default"),x=w(t,"source",13,""),C=w(t,"target",13,""),$=w(t,"data",29,()=>({})),m=w(t,"style",13,void 0),_=w(t,"zIndex",13,void 0),v=w(t,"animated",13,!1),b=w(t,"selected",13,!1),N=w(t,"selectable",13,void 0),E=w(t,"deletable",13,void 0),T=w(t,"hidden",13,!1),D=w(t,"label",13,void 0),V=w(t,"labelStyle",13,void 0),A=w(t,"markerStart",13,void 0),O=w(t,"markerEnd",13,void 0),R=w(t,"sourceHandle",13,void 0),S=w(t,"targetHandle",13,void 0),M=w(t,"sourceX",13),k=w(t,"sourceY",13),P=w(t,"targetX",13),H=w(t,"targetY",13),I=w(t,"sourcePosition",13),B=w(t,"targetPosition",13),F=w(t,"ariaLabel",13,void 0),K=w(t,"interactionWidth",13,void 0),se=w(t,"class",13,"");Sr("svelteflow__edge_id",g());const{edgeLookup:ee,edgeTypes:W,flowId:fe,elementsSelectable:me}=Ue(),Ce=ii(),he=tc();function ze(te){const Fe=a().get(g());Fe&&(he(g()),Ce("edgeclick",{event:te,edge:Fe}))}function G(te,Fe){const Oe=a().get(g());Oe&&Ce(Fe,{event:te,edge:Oe})}ge(()=>j(p()),()=>{U(l,p()||"default")}),ge(()=>(o(),h(l),zi),()=>{U(u,o()[h(l)]||zi)}),ge(()=>(j(A()),i()),()=>{U(c,A()?`url('#${ta(A(),i())}')`:void 0)}),ge(()=>(j(O()),i()),()=>{U(f,O()?`url('#${ta(O(),i())}')`:void 0)}),ge(()=>(j(N()),s()),()=>{U(d,N()??s())}),vt(),He(!0);var ae=tt(),Me=xe(ae);{var Le=te=>{var Fe=h2(),Oe=X(Fe);let rt;var oe=X(Oe);const pe=ve(()=>E()??!0);fl(oe,()=>h(u),(be,Ie)=>{Ie(be,{get id(){return g()},get source(){return x()},get target(){return C()},get sourceX(){return M()},get sourceY(){return k()},get targetX(){return P()},get targetY(){return H()},get sourcePosition(){return I()},get targetPosition(){return B()},get animated(){return v()},get selected(){return b()},get label(){return D()},get labelStyle(){return V()},get data(){return $()},get style(){return m()},get interactionWidth(){return K()},get selectable(){return h(d)},get deletable(){return h(pe)},get type(){return h(l)},get sourceHandleId(){return R()},get targetHandleId(){return S()},get markerStart(){return h(c)},get markerEnd(){return h(f)}})}),Z(Oe),Z(Fe),Ee(be=>{at(Fe,"z-index",_()),rt=$t(Oe,0,wn(be),null,rt,{animated:v(),selected:b(),selectable:h(d)}),de(Oe,"data-id",g()),de(Oe,"aria-label",F()===null?void 0:F()?F():`Edge from ${x()} to ${C()}`)},[()=>Et(["svelte-flow__edge",se()])],ve),Ze("click",Oe,ze),Ze("contextmenu",Oe,be=>{G(be,"edgecontextmenu")}),Ze("mouseenter",Oe,be=>{G(be,"edgemouseenter")}),Ze("mouseleave",Oe,be=>{G(be,"edgemouseleave")}),L(te,Fe)};ke(Me,te=>{T()||te(Le)})}L(e,ae);var Xe=ce({get id(){return g()},set id(te){g(te),y()},get type(){return p()},set type(te){p(te),y()},get source(){return x()},set source(te){x(te),y()},get target(){return C()},set target(te){C(te),y()},get data(){return $()},set data(te){$(te),y()},get style(){return m()},set style(te){m(te),y()},get zIndex(){return _()},set zIndex(te){_(te),y()},get animated(){return v()},set animated(te){v(te),y()},get selected(){return b()},set selected(te){b(te),y()},get selectable(){return N()},set selectable(te){N(te),y()},get deletable(){return E()},set deletable(te){E(te),y()},get hidden(){return T()},set hidden(te){T(te),y()},get label(){return D()},set label(te){D(te),y()},get labelStyle(){return V()},set labelStyle(te){V(te),y()},get markerStart(){return A()},set markerStart(te){A(te),y()},get markerEnd(){return O()},set markerEnd(te){O(te),y()},get sourceHandle(){return R()},set sourceHandle(te){R(te),y()},get targetHandle(){return S()},set targetHandle(te){S(te),y()},get sourceX(){return M()},set sourceX(te){M(te),y()},get sourceY(){return k()},set sourceY(te){k(te),y()},get targetX(){return P()},set targetX(te){P(te),y()},get targetY(){return H()},set targetY(te){H(te),y()},get sourcePosition(){return I()},set sourcePosition(te){I(te),y()},get targetPosition(){return B()},set targetPosition(te){B(te),y()},get ariaLabel(){return F()},set ariaLabel(te){F(te),y()},get interactionWidth(){return K()},set interactionWidth(te){K(te),y()},get class(){return se()},set class(te){se(te),y()}});return r(),Xe}ie(pc,{id:{},type:{},source:{},target:{},data:{},style:{},zIndex:{},animated:{},selected:{},selectable:{},deletable:{},hidden:{},label:{},labelStyle:{},markerStart:{},markerEnd:{},sourceHandle:{},targetHandle:{},sourceX:{},sourceY:{},targetX:{},targetY:{},sourcePosition:{},targetPosition:{},ariaLabel:{},interactionWidth:{},class:{}},[],[],!0);function mc(e,t){ue(t,!1);let n=w(t,"onMount",12,void 0),r=w(t,"onDestroy",12,void 0);return rn(()=>{var o;return(o=n())==null||o(),r()}),He(),ce({get onMount(){return n()},set onMount(o){n(o),y()},get onDestroy(){return r()},set onDestroy(o){r(o),y()}})}ie(mc,{onMount:{},onDestroy:{}},[],[],!0);var v2=_e("<defs></defs>");function yc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(i,"$markers",n),{markers:i}=Ue();He();var s=v2();Yt(s,5,o,a=>a.id,(a,l)=>{wc(a,ft(()=>h(l)))}),Z(s),L(e,s),ce(),r()}ie(yc,{},[],[],!0);var p2=_e('<polyline stroke-linecap="round" stroke-linejoin="round" fill="none" points="-5,-4 0,0 -5,4"></polyline>'),m2=_e('<polyline stroke-linecap="round" stroke-linejoin="round" points="-5,-4 0,0 -5,4 -5,-4"></polyline>'),y2=_e('<marker class="svelte-flow__arrowhead" viewBox="-10 -10 20 20" refX="0" refY="0"><!></marker>');function wc(e,t){ue(t,!1);let n=w(t,"id",12),r=w(t,"type",12),o=w(t,"width",12,12.5),i=w(t,"height",12,12.5),s=w(t,"markerUnits",12,"strokeWidth"),a=w(t,"orient",12,"auto-start-reverse"),l=w(t,"color",12,void 0),u=w(t,"strokeWidth",12,void 0);He();var c=y2(),f=X(c);{var d=p=>{var x=p2();Ee(()=>{de(x,"stroke",l()),de(x,"stroke-width",u())}),L(p,x)},g=(p,x)=>{{var C=$=>{var m=m2();Ee(()=>{de(m,"stroke",l()),de(m,"stroke-width",u()),de(m,"fill",l())}),L($,m)};ke(p,$=>{r()===_o.ArrowClosed&&$(C)},x)}};ke(f,p=>{r()===_o.Arrow?p(d):p(g,!1)})}return Z(c),Ee(()=>{de(c,"id",n()),de(c,"markerWidth",`${o()}`),de(c,"markerHeight",`${i()}`),de(c,"markerUnits",s()),de(c,"orient",a())}),L(e,c),ce({get id(){return n()},set id(p){n(p),y()},get type(){return r()},set type(p){r(p),y()},get width(){return o()},set width(p){o(p),y()},get height(){return i()},set height(p){i(p),y()},get markerUnits(){return s()},set markerUnits(p){s(p),y()},get orient(){return a()},set orient(p){a(p),y()},get color(){return l()},set color(p){l(p),y()},get strokeWidth(){return u()},set strokeWidth(p){u(p),y()}})}ie(wc,{id:{},type:{},width:{},height:{},markerUnits:{},orient:{},color:{},strokeWidth:{}},[],[],!0);var w2=ne('<div class="svelte-flow__edges"><svg class="svelte-flow__marker"><!></svg> <!> <!></div>');function _c(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(a,"$visibleEdges",n),i=()=>Q(c,"$elementsSelectable",n);let s=w(t,"defaultEdgeOptions",12);const{visibleEdges:a,edgesInitialized:l,edges:{setDefaultOptions:u},elementsSelectable:c}=Ue();rn(()=>{s()&&u(s())}),He();var f=w2(),d=X(f),g=X(d);yc(g,{}),Z(d);var p=z(d,2);Yt(p,1,o,m=>m.id,(m,_)=>{const v=ve(()=>h(_).selectable??i()),b=ve(()=>h(_).type||"default");pc(m,{get id(){return h(_).id},get source(){return h(_).source},get target(){return h(_).target},get data(){return h(_).data},get style(){return h(_).style},get animated(){return h(_).animated},get selected(){return h(_).selected},get selectable(){return h(v)},get deletable(){return h(_).deletable},get hidden(){return h(_).hidden},get label(){return h(_).label},get labelStyle(){return h(_).labelStyle},get markerStart(){return h(_).markerStart},get markerEnd(){return h(_).markerEnd},get sourceHandle(){return h(_).sourceHandle},get targetHandle(){return h(_).targetHandle},get sourceX(){return h(_).sourceX},get sourceY(){return h(_).sourceY},get targetX(){return h(_).targetX},get targetY(){return h(_).targetY},get sourcePosition(){return h(_).sourcePosition},get targetPosition(){return h(_).targetPosition},get ariaLabel(){return h(_).ariaLabel},get interactionWidth(){return h(_).interactionWidth},get class(){return h(_).class},get type(){return h(b)},get zIndex(){return h(_).zIndex},$$events:{edgeclick(N){De.call(this,t,N)},edgecontextmenu(N){De.call(this,t,N)},edgemouseenter(N){De.call(this,t,N)},edgemouseleave(N){De.call(this,t,N)}}})});var x=z(p,2);{var C=m=>{mc(m,{onMount:()=>{ai(l,!0)},onDestroy:()=>{ai(l,!1)}})};ke(x,m=>{o().length>0&&m(C)})}Z(f),L(e,f);var $=ce({get defaultEdgeOptions(){return s()},set defaultEdgeOptions(m){s(m),y()}});return r(),$}ie(_c,{defaultEdgeOptions:{}},[],[],!0);var _2=ne('<div class="svelte-flow__selection svelte-1iugwpu"></div>');const x2={hash:"svelte-1iugwpu",code:".svelte-flow__selection.svelte-1iugwpu {position:absolute;top:0;left:0;}"};function ua(e,t){ue(t,!1),et(e,x2);let n=w(t,"x",12,0),r=w(t,"y",12,0),o=w(t,"width",12,0),i=w(t,"height",12,0),s=w(t,"isVisible",12,!0);var a=tt(),l=xe(a);{var u=c=>{var f=_2();Ee(()=>{at(f,"width",typeof o()=="string"?o():`${o()}px`),at(f,"height",typeof i()=="string"?i():`${i()}px`),at(f,"transform",`translate(${n()}px, ${r()}px)`)}),L(c,f)};ke(l,c=>{s()&&c(u)})}return L(e,a),ce({get x(){return n()},set x(c){n(c),y()},get y(){return r()},set y(c){r(c),y()},get width(){return o()},set width(c){o(c),y()},get height(){return i()},set height(c){i(c),y()},get isVisible(){return s()},set isVisible(c){s(c),y()}})}ie(ua,{x:{},y:{},width:{},height:{},isVisible:{}},[],[],!0);function xc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(s,"$selectionRect",n),i=()=>Q(a,"$selectionRectMode",n),{selectionRect:s,selectionRectMode:a}=Ue();He();const l=ve(()=>!!(o()&&i()==="user")),u=ve(()=>{var g;return(g=o())==null?void 0:g.width}),c=ve(()=>{var g;return(g=o())==null?void 0:g.height}),f=ve(()=>{var g;return(g=o())==null?void 0:g.x}),d=ve(()=>{var g;return(g=o())==null?void 0:g.y});ua(e,{get isVisible(){return h(l)},get width(){return h(u)},get height(){return h(c)},get x(){return h(f)},get y(){return h(d)}}),ce(),r()}ie(xc,{},[],[],!0);var b2=ne('<div class="selection-wrapper nopan svelte-5pxri" role="button" tabindex="-1"><!></div>');const C2={hash:"svelte-5pxri",code:".selection-wrapper.svelte-5pxri {position:absolute;top:0;left:0;z-index:7;pointer-events:all;}"};function bc(e,t){ue(t,!1),et(e,C2);const[n,r]=nt(),o=()=>Q(l,"$selectionRectMode",n),i=()=>Q(c,"$nodeLookup",n),s=()=>Q(u,"$nodes",n),a=Ue(),{selectionRectMode:l,nodes:u,nodeLookup:c}=a,f=ii();let d=re(null);function g(m){const _=s().filter(v=>v.selected);f("selectioncontextmenu",{nodes:_,event:m})}function p(m){const _=s().filter(v=>v.selected);f("selectionclick",{nodes:_,event:m})}ge(()=>(o(),i(),s()),()=>{o()==="nodes"&&(U(d,bo(i(),{filter:m=>!!m.selected})),s())}),vt(),He();var x=tt(),C=xe(x);{var $=m=>{var _=b2(),v=X(_);ua(v,{width:"100%",height:"100%",x:0,y:0}),Z(_),_t(_,(b,N)=>Br==null?void 0:Br(b,N),()=>({disabled:!1,store:a,onDrag:(b,N,E,T)=>{f("nodedrag",{event:b,targetNode:null,nodes:T})},onDragStart:(b,N,E,T)=>{f("nodedragstart",{event:b,targetNode:null,nodes:T})},onDragStop:(b,N,E,T)=>{f("nodedragstop",{event:b,targetNode:null,nodes:T})}})),Rt(()=>Ze("contextmenu",_,g)),Rt(()=>Ze("click",_,p)),Rt(()=>Ze("keyup",_,()=>{})),Ee(()=>de(_,"style",`width: ${h(d).width??""}px; height: ${h(d).height??""}px; transform: translate(${h(d).x??""}px, ${h(d).y??""}px)`)),L(m,_)};ke(C,m=>{o()==="nodes"&&h(d)&&zn(h(d).x)&&zn(h(d).y)&&m($)})}L(e,x),ce(),r()}ie(bc,{},[],[],!0);function qe(e,t){let{enabled:n=!0,trigger:r,type:o="keydown"}=t;function i(s){const a=Array.isArray(r)?r:[r],l={alt:s.altKey,ctrl:s.ctrlKey,shift:s.shiftKey,meta:s.metaKey};for(const u of a){const c={modifier:[],preventDefault:!1,enabled:!0,...u},{modifier:f,key:d,callback:g,preventDefault:p,enabled:x}=c;if(x){if(f.length&&!(Array.isArray(f)?f:[f]).map(m=>typeof m=="string"?[m]:m).some(m=>m.every(_=>l[_])))continue;if(s.key===d){p&&s.preventDefault();const C={node:e,trigger:c,originalEvent:s};e.dispatchEvent(new CustomEvent("shortcut",{detail:C})),g==null||g(C)}}}}return n&&e.addEventListener(o,i),{update:s=>{const{enabled:a=!0,type:l="keydown"}=s;n&&(!a||o!==l)?e.removeEventListener(o,i):!n&&a&&e.addEventListener(l,i),n=a,o=l,r=s.trigger},destroy:()=>{e.removeEventListener(o,i)}}}function Cc(e,t){ue(t,!1);let n=w(t,"selectionKey",12,"Shift"),r=w(t,"multiSelectionKey",28,()=>Di()?"Meta":"Control"),o=w(t,"deleteKey",12,"Backspace"),i=w(t,"panActivationKey",12," "),s=w(t,"zoomActivationKey",28,()=>Di()?"Meta":"Control");const{selectionKeyPressed:a,multiselectionKeyPressed:l,deleteKeyPressed:u,panActivationKeyPressed:c,zoomActivationKeyPressed:f,selectionRect:d}=Ue();function g(m){return m!==null&&typeof m=="object"}function p(m){return g(m)?m.modifier||[]:[]}function x(m){return m==null?"":g(m)?m.key:m}function C(m,_){return(Array.isArray(m)?m:[m]).map(b=>{const N=x(b);return{key:N,modifier:p(b),enabled:N!==null,callback:_}})}function $(){d.set(null),a.set(!1),l.set(!1),u.set(!1),c.set(!1),f.set(!1)}return He(),Ze("blur",Vt,$),Ze("contextmenu",Vt,$),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(n(),()=>a.set(!0)),type:"keydown"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(n(),()=>a.set(!1)),type:"keyup"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(r(),()=>l.set(!0)),type:"keydown"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(r(),()=>l.set(!1)),type:"keyup"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(o(),m=>{!(m.originalEvent.ctrlKey||m.originalEvent.metaKey||m.originalEvent.shiftKey)&&!r0(m.originalEvent)&&u.set(!0)}),type:"keydown"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(o(),()=>u.set(!1)),type:"keyup"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(i(),()=>c.set(!0)),type:"keydown"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(i(),()=>c.set(!1)),type:"keyup"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(s(),()=>f.set(!0)),type:"keydown"})),_t(Vt,(m,_)=>qe==null?void 0:qe(m,_),()=>({trigger:C(s(),()=>f.set(!1)),type:"keyup"})),ce({get selectionKey(){return n()},set selectionKey(m){n(m),y()},get multiSelectionKey(){return r()},set multiSelectionKey(m){r(m),y()},get deleteKey(){return o()},set deleteKey(m){o(m),y()},get panActivationKey(){return i()},set panActivationKey(m){i(m),y()},get zoomActivationKey(){return s()},set zoomActivationKey(m){s(m),y()}})}ie(Cc,{selectionKey:{},multiSelectionKey:{},deleteKey:{},panActivationKey:{},zoomActivationKey:{}},[],[],!0);var k2=_e('<path fill="none" class="svelte-flow__connection-path"></path>'),$2=_e('<svg class="svelte-flow__connectionline"><g><!><!></g></svg>');function kc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(g,"$connection",n),i=()=>Q(p,"$connectionLineType",n),s=()=>Q(f,"$width",n),a=()=>Q(d,"$height",n);let l=w(t,"containerStyle",12,""),u=w(t,"style",12,""),c=w(t,"isCustomComponent",12,!1);const{width:f,height:d,connection:g,connectionLineType:p}=Ue();let x=re(null);ge(()=>(o(),j(c()),i(),h(x),ea),()=>{if(o().inProgress&&!c()){const{from:v,to:b,fromPosition:N,toPosition:E}=o(),T={sourceX:v.x,sourceY:v.y,sourcePosition:N,targetX:b.x,targetY:b.y,targetPosition:E};switch(i()){case Ar.Bezier:(D=>U(x,D[0]))(Tu(T));break;case Ar.Step:(D=>U(x,D[0]))(Li({...T,borderRadius:0}));break;case Ar.SmoothStep:(D=>U(x,D[0]))(Li(T));break;default:(D=>U(x,D[0]))(ea(T))}}}),vt(),He();var C=tt(),$=xe(C);{var m=v=>{var b=$2(),N=X(b),E=X(N);wt(E,t,"connectionLine",{});var T=z(E);{var D=V=>{var A=k2();Ee(()=>{de(A,"d",h(x)),de(A,"style",u())}),L(V,A)};ke(T,V=>{c()||V(D)})}Z(N),Z(b),Ee(V=>{de(b,"width",s()),de(b,"height",a()),de(b,"style",l()),$t(N,0,wn(V))},[()=>Et(["svelte-flow__connection",qv(o().isValid)])],ve),L(v,b)};ke($,v=>{o().inProgress&&v(m)})}L(e,C);var _=ce({get containerStyle(){return l()},set containerStyle(v){l(v),y()},get style(){return u()},set style(v){u(v),y()},get isCustomComponent(){return c()},set isCustomComponent(v){c(v),y()}});return r(),_}ie(kc,{containerStyle:{},style:{},isCustomComponent:{}},["connectionLine"],[],!0);var E2=ne("<div><!></div>");function So(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]),r=it(n,["position","style","class"]);ue(t,!1);const[o,i]=nt(),s=()=>Q(f,"$selectionRectMode",o),a=re();let l=w(t,"position",12,"top-right"),u=w(t,"style",12,void 0),c=w(t,"class",12,void 0);const{selectionRectMode:f}=Ue();ge(()=>j(l()),()=>{U(a,`${l()}`.split("-"))}),vt(),He();var d=E2();let g;var p=X(d);wt(p,t,"default",{}),Z(d),Ee(C=>{g=nn(d,g,{class:C,style:u(),...r}),at(d,"pointer-events",s()?"none":"")},[()=>Et(["svelte-flow__panel",c(),...h(a)])],ve),L(e,d);var x=ce({get position(){return l()},set position(C){l(C),y()},get style(){return u()},set style(C){u(C),y()},get class(){return c()},set class(C){c(C),y()}});return i(),x}ie(So,{position:{},style:{},class:{}},["default"],[],!0);var S2=ne('<a href="https://svelteflow.dev" target="_blank" rel="noopener noreferrer" aria-label="Svelte Flow attribution">Svelte Flow</a>');function $c(e,t){ue(t,!1);let n=w(t,"proOptions",12,void 0),r=w(t,"position",12,"bottom-right");He();var o=tt(),i=xe(o);{var s=a=>{So(a,{get position(){return r()},class:"svelte-flow__attribution","data-message":"Feel free to remove the attribution or check out how you could support us: https://svelteflow.dev/support-us",children:(l,u)=>{var c=S2();L(l,c)},$$slots:{default:!0}})};ke(i,a=>{var l;(l=n())!=null&&l.hideAttribution||a(s)})}return L(e,o),ce({get proOptions(){return n()},set proOptions(a){n(a),y()},get position(){return r()},set position(a){r(a),y()}})}ie($c,{proOptions:{},position:{}},[],[],!0);function Ec(e,{nodeTypes:t,edgeTypes:n,minZoom:r,maxZoom:o,translateExtent:i,paneClickDistance:s}){t!==void 0&&e.setNodeTypes(t),n!==void 0&&e.setEdgeTypes(n),r!==void 0&&e.setMinZoom(r),o!==void 0&&e.setMaxZoom(o),i!==void 0&&e.setTranslateExtent(i),s!==void 0&&e.setPaneClickDistance(s)}const P2=e=>Object.keys(e);function Sc(e,t){P2(t).forEach(n=>{const r=t[n];r!==void 0&&e[n].set(r)})}function N2(){return typeof window>"u"||!window.matchMedia?null:window.matchMedia("(prefers-color-scheme: dark)")}function T2(e="light"){return Gt("light",n=>{if(e!=="system"){n(e);return}const r=N2(),o=()=>n(r!=null&&r.matches?"dark":"light");return n(r!=null&&r.matches?"dark":"light"),r==null||r.addEventListener("change",o),()=>{r==null||r.removeEventListener("change",o)}})}var M2=ne('<!> <!> <div class="svelte-flow__edgelabel-renderer"></div> <div class="svelte-flow__viewport-portal"></div> <!> <!>',1),H2=ne("<!> <!>",1),V2=ne("<div><!> <!> <!> <!></div>");const D2={hash:"svelte-12wlba6",code:".svelte-flow.svelte-12wlba6 {width:100%;height:100%;overflow:hidden;position:relative;z-index:0;background-color:var(--background-color, var(--background-color-default));}:root {--background-color-default: #fff;--background-pattern-color-default: #ddd;--minimap-mask-color-default: rgb(240, 240, 240, 0.6);--minimap-mask-stroke-color-default: none;--minimap-mask-stroke-width-default: 1;--controls-button-background-color-default: #fefefe;--controls-button-background-color-hover-default: #f4f4f4;--controls-button-color-default: inherit;--controls-button-color-hover-default: inherit;--controls-button-border-color-default: #eee;}"};function Pc(e,t){const n=e1(t),r=it(t,["children","$$slots","$$events","$$legacy","$$host"]),o=it(r,["id","nodes","edges","fitView","fitViewOptions","minZoom","maxZoom","initialViewport","viewport","nodeTypes","edgeTypes","selectionKey","selectionMode","panActivationKey","multiSelectionKey","zoomActivationKey","nodesDraggable","nodesConnectable","nodeDragThreshold","elementsSelectable","snapGrid","deleteKey","connectionRadius","connectionLineType","connectionMode","connectionLineStyle","connectionLineContainerStyle","onMoveStart","onMove","onMoveEnd","isValidConnection","translateExtent","nodeExtent","onlyRenderVisibleElements","panOnScrollMode","preventScrolling","zoomOnScroll","zoomOnDoubleClick","zoomOnPinch","panOnScroll","panOnDrag","selectionOnDrag","autoPanOnConnect","autoPanOnNodeDrag","onerror","ondelete","onedgecreate","attributionPosition","proOptions","defaultEdgeOptions","width","height","colorMode","onconnect","onconnectstart","onconnectend","onbeforedelete","oninit","nodeOrigin","paneClickDistance","nodeClickDistance","defaultMarkerColor","style","class"]);ue(t,!1),et(e,D2);const[i,s]=nt(),a=()=>Q(_(),"$viewport",i),l=()=>Q(xa,"$initialized",i),u=()=>Q(h(c),"$colorModeClass",i),c=re();let f=w(t,"id",12,"1"),d=w(t,"nodes",12),g=w(t,"edges",12),p=w(t,"fitView",12,void 0),x=w(t,"fitViewOptions",12,void 0),C=w(t,"minZoom",12,void 0),$=w(t,"maxZoom",12,void 0),m=w(t,"initialViewport",12,void 0),_=w(t,"viewport",12,void 0),v=w(t,"nodeTypes",12,void 0),b=w(t,"edgeTypes",12,void 0),N=w(t,"selectionKey",12,void 0),E=w(t,"selectionMode",12,void 0),T=w(t,"panActivationKey",12,void 0),D=w(t,"multiSelectionKey",12,void 0),V=w(t,"zoomActivationKey",12,void 0),A=w(t,"nodesDraggable",12,void 0),O=w(t,"nodesConnectable",12,void 0),R=w(t,"nodeDragThreshold",12,void 0),S=w(t,"elementsSelectable",12,void 0),M=w(t,"snapGrid",12,void 0),k=w(t,"deleteKey",12,void 0),P=w(t,"connectionRadius",12,void 0),H=w(t,"connectionLineType",12,void 0),I=w(t,"connectionMode",28,()=>hr.Strict),B=w(t,"connectionLineStyle",12,""),F=w(t,"connectionLineContainerStyle",12,""),K=w(t,"onMoveStart",12,void 0),se=w(t,"onMove",12,void 0),ee=w(t,"onMoveEnd",12,void 0),W=w(t,"isValidConnection",12,void 0),fe=w(t,"translateExtent",12,void 0),me=w(t,"nodeExtent",12,void 0),Ce=w(t,"onlyRenderVisibleElements",12,void 0),he=w(t,"panOnScrollMode",28,()=>Jn.Free),ze=w(t,"preventScrolling",12,!0),G=w(t,"zoomOnScroll",12,!0),ae=w(t,"zoomOnDoubleClick",12,!0),Me=w(t,"zoomOnPinch",12,!0),Le=w(t,"panOnScroll",12,!1),Xe=w(t,"panOnDrag",12,!0),te=w(t,"selectionOnDrag",12,void 0),Fe=w(t,"autoPanOnConnect",12,!0),Oe=w(t,"autoPanOnNodeDrag",12,!0),rt=w(t,"onerror",12,void 0),oe=w(t,"ondelete",12,void 0),pe=w(t,"onedgecreate",12,void 0),be=w(t,"attributionPosition",12,void 0),Ie=w(t,"proOptions",12,void 0),ht=w(t,"defaultEdgeOptions",12,void 0),dt=w(t,"width",12,void 0),J=w(t,"height",12,void 0),Re=w(t,"colorMode",12,"light"),le=w(t,"onconnect",12,void 0),$n=w(t,"onconnectstart",12,void 0),fn=w(t,"onconnectend",12,void 0),En=w(t,"onbeforedelete",12,void 0),Te=w(t,"oninit",12,void 0),st=w(t,"nodeOrigin",12,void 0),ye=w(t,"paneClickDistance",12,0),lt=w(t,"nodeClickDistance",12,0),ct=w(t,"defaultMarkerColor",12,"#b1b1b7"),Jt=w(t,"style",12,void 0),Oo=w(t,"class",12,void 0),Wt=re(),Ot=re(),Sn=re();const gn=a()||m(),mt=Df(Ri)?Ue():r2({nodes:q(d()),edges:q(g()),width:dt(),height:J(),fitView:p(),nodeOrigin:st(),nodeExtent:me()});rn(()=>(mt.width.set(h(Ot)),mt.height.set(h(Sn)),mt.domNode.set(h(Wt)),mt.syncNodeStores(d()),mt.syncEdgeStores(g()),mt.syncViewport(_()),p()!==void 0&&mt.fitViewOnInit.set(p()),x()&&mt.fitViewOptions.set(x()),Ec(mt,{nodeTypes:v(),edgeTypes:b(),minZoom:C(),maxZoom:$(),translateExtent:fe(),paneClickDistance:ye()}),()=>{mt.reset()}));const{initialized:xa}=mt;let yr=re(!1);ge(()=>(h(Ot),h(Sn)),()=>{h(Ot)!==void 0&&h(Sn)!==void 0&&(mt.width.set(h(Ot)),mt.height.set(h(Sn)))}),ge(()=>(h(yr),l(),j(Te())),()=>{var Y;!h(yr)&&l()&&((Y=Te())==null||Y(),U(yr,!0))}),ge(()=>(j(f()),j(H()),j(P()),j(E()),j(M()),j(ct()),j(A()),j(O()),j(S()),j(Ce()),j(W()),j(Fe()),j(Oe()),j(rt()),j(oe()),j(pe()),j(I()),j(R()),j(le()),j($n()),j(fn()),j(En()),j(st()),Sc),()=>{const Y={flowId:f(),connectionLineType:H(),connectionRadius:P(),selectionMode:E(),snapGrid:M(),defaultMarkerColor:ct(),nodesDraggable:A(),nodesConnectable:O(),elementsSelectable:S(),onlyRenderVisibleElements:Ce(),isValidConnection:W(),autoPanOnConnect:Fe(),autoPanOnNodeDrag:Oe(),onerror:rt(),ondelete:oe(),onedgecreate:pe(),connectionMode:I(),nodeDragThreshold:R(),onconnect:le(),onconnectstart:$n(),onconnectend:fn(),onbeforedelete:En(),nodeOrigin:st()};Sc(mt,Y)}),ge(()=>(j(v()),j(b()),j(C()),j($()),j(fe()),j(ye())),()=>{Ec(mt,{nodeTypes:v(),edgeTypes:b(),minZoom:C(),maxZoom:$(),translateExtent:fe(),paneClickDistance:ye()})}),ge(()=>j(Re()),()=>{l1(U(c,T2(Re())),"$colorModeClass",i)}),vt(),He();var hn=V2();let ji;var Ji=X(hn);Cc(Ji,{get selectionKey(){return N()},get deleteKey(){return k()},get panActivationKey(){return T()},get multiSelectionKey(){return D()},get zoomActivationKey(){return V()}});var Qi=z(Ji,2);const jy=ve(()=>he()===void 0?Jn.Free:he()),Jy=ve(()=>ze()===void 0?!0:ze()),Qy=ve(()=>G()===void 0?!0:G()),ew=ve(()=>ae()===void 0?!0:ae()),tw=ve(()=>Me()===void 0?!0:Me()),nw=ve(()=>Le()===void 0?!1:Le()),rw=ve(()=>Xe()===void 0?!0:Xe()),ow=ve(()=>ye()===void 0?0:ye());uc(Qi,{initialViewport:gn,get onMoveStart(){return K()},get onMove(){return se()},get onMoveEnd(){return ee()},get panOnScrollMode(){return h(jy)},get preventScrolling(){return h(Jy)},get zoomOnScroll(){return h(Qy)},get zoomOnDoubleClick(){return h(ew)},get zoomOnPinch(){return h(tw)},get panOnScroll(){return h(nw)},get panOnDrag(){return h(rw)},get paneClickDistance(){return h(ow)},children:(Y,pw)=>{const aw=ve(()=>Xe()===void 0?!0:Xe());fc(Y,{get panOnDrag(){return h(aw)},get selectionOnDrag(){return te()},$$events:{paneclick(Io){De.call(this,t,Io)},panecontextmenu(Io){De.call(this,t,Io)}},children:(Io,mw)=>{var Vd=H2(),Dd=xe(Vd);gc(Dd,{children:(uw,yw)=>{var Ad=M2(),Ld=xe(Ad);_c(Ld,{get defaultEdgeOptions(){return ht()},$$events:{edgeclick(Be){De.call(this,t,Be)},edgecontextmenu(Be){De.call(this,t,Be)},edgemouseenter(Be){De.call(this,t,Be)},edgemouseleave(Be){De.call(this,t,Be)}}});var Od=z(Ld,2);kc(Od,{get containerStyle(){return F()},get style(){return B()},isCustomComponent:n.connectionLine,$$slots:{connectionLine:(Be,ww)=>{var zd=tt(),dw=xe(zd);wt(dw,t,"connectionLine",{}),L(Be,zd)}}});var Id=z(Od,6);vc(Id,{get nodeClickDistance(){return lt()},$$events:{nodeclick(Be){De.call(this,t,Be)},nodemouseenter(Be){De.call(this,t,Be)},nodemousemove(Be){De.call(this,t,Be)},nodemouseleave(Be){De.call(this,t,Be)},nodedragstart(Be){De.call(this,t,Be)},nodedrag(Be){De.call(this,t,Be)},nodedragstop(Be){De.call(this,t,Be)},nodecontextmenu(Be){De.call(this,t,Be)}}});var cw=z(Id,2);bc(cw,{$$events:{selectionclick(Be){De.call(this,t,Be)},selectioncontextmenu(Be){De.call(this,t,Be)},nodedragstart(Be){De.call(this,t,Be)},nodedrag(Be){De.call(this,t,Be)},nodedragstop(Be){De.call(this,t,Be)}}}),L(uw,Ad)},$$slots:{default:!0}});var lw=z(Dd,2);xc(lw,{}),L(Io,Vd)},$$slots:{default:!0}})},$$slots:{default:!0}});var Hd=z(Qi,2);$c(Hd,{get proOptions(){return Ie()},get position(){return be()}});var iw=z(Hd,2);wt(iw,t,"default",{}),Z(hn),An(hn,Y=>U(Wt,Y),()=>h(Wt)),Ee(Y=>ji=nn(hn,ji,{style:Jt(),class:Y,"data-testid":"svelte-flow__wrapper",...o,role:"application"},"svelte-12wlba6"),[()=>Et(["svelte-flow",Oo(),u()])],ve),ml(hn,"clientWidth",Y=>U(Ot,Y)),ml(hn,"clientHeight",Y=>U(Sn,Y)),Ze("dragover",hn,function(Y){De.call(this,t,Y)}),Ze("drop",hn,function(Y){De.call(this,t,Y)}),L(e,hn);var sw=ce({get id(){return f()},set id(Y){f(Y),y()},get nodes(){return d()},set nodes(Y){d(Y),y()},get edges(){return g()},set edges(Y){g(Y),y()},get fitView(){return p()},set fitView(Y){p(Y),y()},get fitViewOptions(){return x()},set fitViewOptions(Y){x(Y),y()},get minZoom(){return C()},set minZoom(Y){C(Y),y()},get maxZoom(){return $()},set maxZoom(Y){$(Y),y()},get initialViewport(){return m()},set initialViewport(Y){m(Y),y()},get viewport(){return _()},set viewport(Y){_(Y),y()},get nodeTypes(){return v()},set nodeTypes(Y){v(Y),y()},get edgeTypes(){return b()},set edgeTypes(Y){b(Y),y()},get selectionKey(){return N()},set selectionKey(Y){N(Y),y()},get selectionMode(){return E()},set selectionMode(Y){E(Y),y()},get panActivationKey(){return T()},set panActivationKey(Y){T(Y),y()},get multiSelectionKey(){return D()},set multiSelectionKey(Y){D(Y),y()},get zoomActivationKey(){return V()},set zoomActivationKey(Y){V(Y),y()},get nodesDraggable(){return A()},set nodesDraggable(Y){A(Y),y()},get nodesConnectable(){return O()},set nodesConnectable(Y){O(Y),y()},get nodeDragThreshold(){return R()},set nodeDragThreshold(Y){R(Y),y()},get elementsSelectable(){return S()},set elementsSelectable(Y){S(Y),y()},get snapGrid(){return M()},set snapGrid(Y){M(Y),y()},get deleteKey(){return k()},set deleteKey(Y){k(Y),y()},get connectionRadius(){return P()},set connectionRadius(Y){P(Y),y()},get connectionLineType(){return H()},set connectionLineType(Y){H(Y),y()},get connectionMode(){return I()},set connectionMode(Y){I(Y),y()},get connectionLineStyle(){return B()},set connectionLineStyle(Y){B(Y),y()},get connectionLineContainerStyle(){return F()},set connectionLineContainerStyle(Y){F(Y),y()},get onMoveStart(){return K()},set onMoveStart(Y){K(Y),y()},get onMove(){return se()},set onMove(Y){se(Y),y()},get onMoveEnd(){return ee()},set onMoveEnd(Y){ee(Y),y()},get isValidConnection(){return W()},set isValidConnection(Y){W(Y),y()},get translateExtent(){return fe()},set translateExtent(Y){fe(Y),y()},get nodeExtent(){return me()},set nodeExtent(Y){me(Y),y()},get onlyRenderVisibleElements(){return Ce()},set onlyRenderVisibleElements(Y){Ce(Y),y()},get panOnScrollMode(){return he()},set panOnScrollMode(Y){he(Y),y()},get preventScrolling(){return ze()},set preventScrolling(Y){ze(Y),y()},get zoomOnScroll(){return G()},set zoomOnScroll(Y){G(Y),y()},get zoomOnDoubleClick(){return ae()},set zoomOnDoubleClick(Y){ae(Y),y()},get zoomOnPinch(){return Me()},set zoomOnPinch(Y){Me(Y),y()},get panOnScroll(){return Le()},set panOnScroll(Y){Le(Y),y()},get panOnDrag(){return Xe()},set panOnDrag(Y){Xe(Y),y()},get selectionOnDrag(){return te()},set selectionOnDrag(Y){te(Y),y()},get autoPanOnConnect(){return Fe()},set autoPanOnConnect(Y){Fe(Y),y()},get autoPanOnNodeDrag(){return Oe()},set autoPanOnNodeDrag(Y){Oe(Y),y()},get onerror(){return rt()},set onerror(Y){rt(Y),y()},get ondelete(){return oe()},set ondelete(Y){oe(Y),y()},get onedgecreate(){return pe()},set onedgecreate(Y){pe(Y),y()},get attributionPosition(){return be()},set attributionPosition(Y){be(Y),y()},get proOptions(){return Ie()},set proOptions(Y){Ie(Y),y()},get defaultEdgeOptions(){return ht()},set defaultEdgeOptions(Y){ht(Y),y()},get width(){return dt()},set width(Y){dt(Y),y()},get height(){return J()},set height(Y){J(Y),y()},get colorMode(){return Re()},set colorMode(Y){Re(Y),y()},get onconnect(){return le()},set onconnect(Y){le(Y),y()},get onconnectstart(){return $n()},set onconnectstart(Y){$n(Y),y()},get onconnectend(){return fn()},set onconnectend(Y){fn(Y),y()},get onbeforedelete(){return En()},set onbeforedelete(Y){En(Y),y()},get oninit(){return Te()},set oninit(Y){Te(Y),y()},get nodeOrigin(){return st()},set nodeOrigin(Y){st(Y),y()},get paneClickDistance(){return ye()},set paneClickDistance(Y){ye(Y),y()},get nodeClickDistance(){return lt()},set nodeClickDistance(Y){lt(Y),y()},get defaultMarkerColor(){return ct()},set defaultMarkerColor(Y){ct(Y),y()},get style(){return Jt()},set style(Y){Jt(Y),y()},get class(){return Oo()},set class(Y){Oo(Y),y()}});return s(),sw}ie(Pc,{id:{},nodes:{},edges:{},fitView:{},fitViewOptions:{},minZoom:{},maxZoom:{},initialViewport:{},viewport:{},nodeTypes:{},edgeTypes:{},selectionKey:{},selectionMode:{},panActivationKey:{},multiSelectionKey:{},zoomActivationKey:{},nodesDraggable:{},nodesConnectable:{},nodeDragThreshold:{},elementsSelectable:{},snapGrid:{},deleteKey:{},connectionRadius:{},connectionLineType:{},connectionMode:{},connectionLineStyle:{},connectionLineContainerStyle:{},onMoveStart:{},onMove:{},onMoveEnd:{},isValidConnection:{},translateExtent:{},nodeExtent:{},onlyRenderVisibleElements:{},panOnScrollMode:{},preventScrolling:{},zoomOnScroll:{},zoomOnDoubleClick:{},zoomOnPinch:{},panOnScroll:{},panOnDrag:{},selectionOnDrag:{},autoPanOnConnect:{},autoPanOnNodeDrag:{},onerror:{},ondelete:{},onedgecreate:{},attributionPosition:{},proOptions:{},defaultEdgeOptions:{},width:{},height:{},colorMode:{},onconnect:{},onconnectstart:{},onconnectend:{},onbeforedelete:{},oninit:{},nodeOrigin:{},paneClickDistance:{},nodeClickDistance:{},defaultMarkerColor:{},style:{},class:{}},["connectionLine","default"],[],!0);function Nc(e,t){ue(t,!1);let n=w(t,"initialNodes",12,void 0),r=w(t,"initialEdges",12,void 0),o=w(t,"initialWidth",12,void 0),i=w(t,"initialHeight",12,void 0),s=w(t,"fitView",12,void 0),a=w(t,"nodeOrigin",12,void 0);const l=lc({nodes:n(),edges:r(),width:o(),height:i(),nodeOrigin:a(),fitView:s()});Sr(Ri,{getStore:()=>l}),$s(()=>{l.reset()}),He();var u=tt(),c=xe(u);return wt(c,t,"default",{}),L(e,u),ce({get initialNodes(){return n()},set initialNodes(f){n(f),y()},get initialEdges(){return r()},set initialEdges(f){r(f),y()},get initialWidth(){return o()},set initialWidth(f){o(f),y()},get initialHeight(){return i()},set initialHeight(f){i(f),y()},get fitView(){return s()},set fitView(f){s(f),y()},get nodeOrigin(){return a()},set nodeOrigin(f){a(f),y()}})}ie(Nc,{initialNodes:{},initialEdges:{},initialWidth:{},initialHeight:{},fitView:{},nodeOrigin:{}},["default"],[],!0);var A2=ne("<button><!></button>");function Po(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]),r=it(n,["class","bgColor","bgColorHover","color","colorHover","borderColor"]);ue(t,!1);let o=w(t,"class",12,void 0),i=w(t,"bgColor",12,void 0),s=w(t,"bgColorHover",12,void 0),a=w(t,"color",12,void 0),l=w(t,"colorHover",12,void 0),u=w(t,"borderColor",12,void 0);He();var c=A2();let f;var d=X(c);return wt(d,t,"default",{class:"button-svg"}),Z(c),Ee(g=>{f=nn(c,f,{type:"button",class:g,...r}),at(c,"--xy-controls-button-background-color-props",i()),at(c,"--xy-controls-button-background-color-hover-props",s()),at(c,"--xy-controls-button-color-props",a()),at(c,"--xy-controls-button-color-hover-props",l()),at(c,"--xy-controls-button-border-color-props",u())},[()=>Et(["svelte-flow__controls-button",o()])],ve),Ze("click",c,function(g){De.call(this,t,g)}),L(e,c),ce({get class(){return o()},set class(g){o(g),y()},get bgColor(){return i()},set bgColor(g){i(g),y()},get bgColorHover(){return s()},set bgColorHover(g){s(g),y()},get color(){return a()},set color(g){a(g),y()},get colorHover(){return l()},set colorHover(g){l(g),y()},get borderColor(){return u()},set borderColor(g){u(g),y()}})}ie(Po,{class:{},bgColor:{},bgColorHover:{},color:{},colorHover:{},borderColor:{}},["default"],[],!0);var L2=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"></path></svg>');function Tc(e){var t=L2();L(e,t)}ie(Tc,{},[],[],!0);var O2=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5"><path d="M0 0h32v4.2H0z"></path></svg>');function Mc(e){var t=O2();L(e,t)}ie(Mc,{},[],[],!0);var I2=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30"><path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"></path></svg>');function Hc(e){var t=I2();L(e,t)}ie(Hc,{},[],[],!0);var z2=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32"><path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"></path></svg>');function Vc(e){var t=z2();L(e,t)}ie(Vc,{},[],[],!0);var R2=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32"><path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"></path></svg>');function Dc(e){var t=R2();L(e,t)}ie(Dc,{},[],[],!0);var B2=ne("<!> <!>",1),Y2=ne("<!> <!> <!> <!> <!> <!>",1);function Ac(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(H,"$nodesDraggable",n),i=()=>Q(I,"$nodesConnectable",n),s=()=>Q(B,"$elementsSelectable",n),a=()=>Q(M,"$viewport",n),l=()=>Q(k,"$minZoom",n),u=()=>Q(P,"$maxZoom",n),c=re(),f=re(),d=re(),g=re();let p=w(t,"position",12,"bottom-left"),x=w(t,"showZoom",12,!0),C=w(t,"showFitView",12,!0),$=w(t,"showLock",12,!0),m=w(t,"buttonBgColor",12,void 0),_=w(t,"buttonBgColorHover",12,void 0),v=w(t,"buttonColor",12,void 0),b=w(t,"buttonColorHover",12,void 0),N=w(t,"buttonBorderColor",12,void 0),E=w(t,"ariaLabel",12,void 0),T=w(t,"style",12,void 0),D=w(t,"orientation",12,"vertical"),V=w(t,"fitViewOptions",12,void 0),A=w(t,"class",12,"");const{zoomIn:O,zoomOut:R,fitView:S,viewport:M,minZoom:k,maxZoom:P,nodesDraggable:H,nodesConnectable:I,elementsSelectable:B}=Ue(),F={bgColor:m(),bgColorHover:_(),color:v(),colorHover:b(),borderColor:N()},K=()=>{O()},se=()=>{R()},ee=()=>{S(V())},W=()=>{U(c,!h(c)),H.set(h(c)),I.set(h(c)),B.set(h(c))};ge(()=>(o(),i(),s()),()=>{U(c,o()||i()||s())}),ge(()=>(a(),l()),()=>{U(f,a().zoom<=l())}),ge(()=>(a(),u()),()=>{U(d,a().zoom>=u())}),ge(()=>j(D()),()=>{U(g,D()==="horizontal"?"horizontal":"vertical")}),vt(),He();const fe=ve(()=>Et(["svelte-flow__controls",h(g),A()])),me=ve(()=>E()??"Svelte Flow controls");So(e,{get class(){return h(fe)},get position(){return p()},"data-testid":"svelte-flow__controls",get"aria-label"(){return h(me)},get style(){return T()},children:(he,ze)=>{var G=Y2(),ae=xe(G);wt(ae,t,"before",{});var Me=z(ae,2);{var Le=pe=>{var be=B2(),Ie=xe(be);Po(Ie,ft({class:"svelte-flow__controls-zoomin",title:"zoom in","aria-label":"zoom in",get disabled(){return h(d)}},F,{$$events:{click:K},children:(dt,J)=>{Tc(dt)},$$slots:{default:!0}}));var ht=z(Ie,2);Po(ht,ft({class:"svelte-flow__controls-zoomout",title:"zoom out","aria-label":"zoom out",get disabled(){return h(f)}},F,{$$events:{click:se},children:(dt,J)=>{Mc(dt)},$$slots:{default:!0}})),L(pe,be)};ke(Me,pe=>{x()&&pe(Le)})}var Xe=z(Me,2);{var te=pe=>{Po(pe,ft({class:"svelte-flow__controls-fitview",title:"fit view","aria-label":"fit view"},F,{$$events:{click:ee},children:(be,Ie)=>{Hc(be)},$$slots:{default:!0}}))};ke(Xe,pe=>{C()&&pe(te)})}var Fe=z(Xe,2);{var Oe=pe=>{Po(pe,ft({class:"svelte-flow__controls-interactive",title:"toggle interactivity","aria-label":"toggle interactivity"},F,{$$events:{click:W},children:(be,Ie)=>{var ht=tt(),dt=xe(ht);{var J=le=>{Dc(le)},Re=le=>{Vc(le)};ke(dt,le=>{h(c)?le(J):le(Re,!1)})}L(be,ht)},$$slots:{default:!0}}))};ke(Fe,pe=>{$()&&pe(Oe)})}var rt=z(Fe,2);wt(rt,t,"default",{});var oe=z(rt,2);wt(oe,t,"after",{}),L(he,G)},$$slots:{default:!0}});var Ce=ce({get position(){return p()},set position(he){p(he),y()},get showZoom(){return x()},set showZoom(he){x(he),y()},get showFitView(){return C()},set showFitView(he){C(he),y()},get showLock(){return $()},set showLock(he){$(he),y()},get buttonBgColor(){return m()},set buttonBgColor(he){m(he),y()},get buttonBgColorHover(){return _()},set buttonBgColorHover(he){_(he),y()},get buttonColor(){return v()},set buttonColor(he){v(he),y()},get buttonColorHover(){return b()},set buttonColorHover(he){b(he),y()},get buttonBorderColor(){return N()},set buttonBorderColor(he){N(he),y()},get ariaLabel(){return E()},set ariaLabel(he){E(he),y()},get style(){return T()},set style(he){T(he),y()},get orientation(){return D()},set orientation(he){D(he),y()},get fitViewOptions(){return V()},set fitViewOptions(he){V(he),y()},get class(){return A()},set class(he){A(he),y()}});return r(),Ce}ie(Ac,{position:{},showZoom:{},showFitView:{},showLock:{},buttonBgColor:{},buttonBgColorHover:{},buttonColor:{},buttonColorHover:{},buttonBorderColor:{},ariaLabel:{},style:{},orientation:{},fitViewOptions:{},class:{}},["before","default","after"],[],!0);var tr;(function(e){e.Lines="lines",e.Dots="dots",e.Cross="cross"})(tr||(tr={}));var Z2=_e("<circle></circle>");function Lc(e,t){ue(t,!1);let n=w(t,"radius",12,5),r=w(t,"class",12,"");He();var o=Z2();return Ee(i=>{de(o,"cx",n()),de(o,"cy",n()),de(o,"r",n()),$t(o,0,wn(i))},[()=>Et(["svelte-flow__background-pattern","dots",r()])],ve),L(e,o),ce({get radius(){return n()},set radius(i){n(i),y()},get class(){return r()},set class(i){r(i),y()}})}ie(Lc,{radius:{},class:{}},[],[],!0);var X2=_e("<path></path>");function Oc(e,t){ue(t,!1);let n=w(t,"lineWidth",12,1),r=w(t,"dimensions",12),o=w(t,"variant",12,void 0),i=w(t,"class",12,"");He();var s=X2();return Ee(a=>{de(s,"stroke-width",n()),de(s,"d",`M${r()[0]/2} 0 V${r()[1]} M0 ${r()[1]/2} H${r()[0]}`),$t(s,0,wn(a))},[()=>Et(["svelte-flow__background-pattern",o(),i()])],ve),L(e,s),ce({get lineWidth(){return n()},set lineWidth(a){n(a),y()},get dimensions(){return r()},set dimensions(a){r(a),y()},get variant(){return o()},set variant(a){o(a),y()},get class(){return i()},set class(a){i(a),y()}})}ie(Oc,{lineWidth:{},dimensions:{},variant:{},class:{}},[],[],!0);const F2={[tr.Dots]:1,[tr.Lines]:1,[tr.Cross]:6};var W2=_e('<svg data-testid="svelte-flow__background"><pattern patternUnits="userSpaceOnUse"><!></pattern><rect x="0" y="0" width="100%" height="100%"></rect></svg>');const K2={hash:"svelte-1r7pe8d",code:".svelte-flow__background.svelte-1r7pe8d {position:absolute;width:100%;height:100%;top:0;left:0;}"};function Ic(e,t){ue(t,!1),et(e,K2);const[n,r]=nt(),o=()=>Q(b,"$flowId",n),i=()=>Q(v,"$viewport",n),s=re(),a=re(),l=re(),u=re(),c=re();let f=w(t,"id",12,void 0),d=w(t,"variant",28,()=>tr.Dots),g=w(t,"gap",12,20),p=w(t,"size",12,1),x=w(t,"lineWidth",12,1),C=w(t,"bgColor",12,void 0),$=w(t,"patternColor",12,void 0),m=w(t,"patternClass",12,void 0),_=w(t,"class",12,"");const{viewport:v,flowId:b}=Ue(),N=p()||F2[d()],E=d()===tr.Dots,T=d()===tr.Cross,D=Array.isArray(g())?g():[g(),g()];ge(()=>(o(),j(f())),()=>{U(s,`background-pattern-${o()}-${f()?f():""}`)}),ge(()=>i(),()=>{U(a,[D[0]*i().zoom||1,D[1]*i().zoom||1])}),ge(()=>i(),()=>{U(l,N*i().zoom)}),ge(()=>(h(l),h(a)),()=>{U(u,T?[h(l),h(l)]:h(a))}),ge(()=>(h(l),h(u)),()=>{U(c,E?[h(l)/2,h(l)/2]:[h(u)[0]/2,h(u)[1]/2])}),vt(),He();var V=W2(),A=X(V),O=X(A);{var R=P=>{const H=ve(()=>h(l)/2);Lc(P,{get radius(){return h(H)},get class(){return m()}})},S=P=>{Oc(P,{get dimensions(){return h(u)},get variant(){return d()},get lineWidth(){return x()},get class(){return m()}})};ke(O,P=>{E?P(R):P(S,!1)})}Z(A);var M=z(A);Z(V),Ee(P=>{$t(V,0,wn(P),"svelte-1r7pe8d"),at(V,"--xy-background-color-props",C()),at(V,"--xy-background-pattern-color-props",$()),de(A,"id",h(s)),de(A,"x",i().x%h(a)[0]),de(A,"y",i().y%h(a)[1]),de(A,"width",h(a)[0]),de(A,"height",h(a)[1]),de(A,"patternTransform",`translate(-${h(c)[0]},-${h(c)[1]})`),de(M,"fill",`url(#${h(s)})`)},[()=>Et(["svelte-flow__background",_()])],ve),L(e,V);var k=ce({get id(){return f()},set id(P){f(P),y()},get variant(){return d()},set variant(P){d(P),y()},get gap(){return g()},set gap(P){g(P),y()},get size(){return p()},set size(P){p(P),y()},get lineWidth(){return x()},set lineWidth(P){x(P),y()},get bgColor(){return C()},set bgColor(P){C(P),y()},get patternColor(){return $()},set patternColor(P){$(P),y()},get patternClass(){return m()},set patternClass(P){m(P),y()},get class(){return _()},set class(P){_(P),y()}});return r(),k}ie(Ic,{id:{},variant:{},gap:{},size:{},lineWidth:{},bgColor:{},patternColor:{},patternClass:{},class:{}},[],[],!0);var q2=_e("<rect></rect>");function zc(e,t){ue(t,!1);let n=w(t,"x",12),r=w(t,"y",12),o=w(t,"width",12,0),i=w(t,"height",12,0),s=w(t,"borderRadius",12,5),a=w(t,"color",12,void 0),l=w(t,"shapeRendering",12),u=w(t,"strokeColor",12,void 0),c=w(t,"strokeWidth",12,2),f=w(t,"selected",12,!1),d=w(t,"class",12,"");He();var g=q2();let p;return Ee(x=>{p=$t(g,0,wn(x),null,p,{selected:f()}),de(g,"x",n()),de(g,"y",r()),de(g,"rx",s()),de(g,"ry",s()),de(g,"width",o()),de(g,"height",i()),de(g,"style",`${a()?`fill: ${a()};`:""}${u()?`stroke: ${u()};`:""}${c()?`stroke-width: ${c()};`:""}`),de(g,"shape-rendering",l())},[()=>Et(["svelte-flow__minimap-node",d()])],ve),L(e,g),ce({get x(){return n()},set x(x){n(x),y()},get y(){return r()},set y(x){r(x),y()},get width(){return o()},set width(x){o(x),y()},get height(){return i()},set height(x){i(x),y()},get borderRadius(){return s()},set borderRadius(x){s(x),y()},get color(){return a()},set color(x){a(x),y()},get shapeRendering(){return l()},set shapeRendering(x){l(x),y()},get strokeColor(){return u()},set strokeColor(x){u(x),y()},get strokeWidth(){return c()},set strokeWidth(x){c(x),y()},get selected(){return f()},set selected(x){f(x),y()},get class(){return d()},set class(x){d(x),y()}})}ie(zc,{x:{},y:{},width:{},height:{},borderRadius:{},color:{},shapeRendering:{},strokeColor:{},strokeWidth:{},selected:{},class:{}},[],[],!0);function ca(e,t){const n=H0({domNode:e,panZoom:t.panZoom,getTransform:()=>{const o=q(t.viewport);return[o.x,o.y,o.zoom]},getViewScale:t.getViewScale});function r(o){n.update({translateExtent:o.translateExtent,width:o.width,height:o.height,inversePan:o.inversePan,zoomStep:o.zoomStep,pannable:o.pannable,zoomable:o.zoomable})}return{update:r,destroy(){n.destroy()}}}const da=e=>e instanceof Function?e:()=>e;var G2=_e("<title> </title>"),U2=_e('<svg class="svelte-flow__minimap-svg" role="img"><!><!><path class="svelte-flow__minimap-mask" fill-rule="evenodd" pointer-events="none"></path></svg>');function Rc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(Xe,"$flowId",n),i=()=>Q(ae,"$viewport",n),s=()=>Q(Me,"$containerWidth",n),a=()=>Q(Le,"$containerHeight",n),l=()=>Q(G,"$nodeLookup",n),u=()=>Q(ze,"$nodes",n),c=()=>Q(te,"$panZoom",n),f=()=>Q(Fe,"$translateExtent",n),d=re(),g=re(),p=re(),x=re(),C=re(),$=re(),m=re(),_=re(),v=re(),b=re(),N=re(),E=re(),T=re();let D=w(t,"position",12,"bottom-right"),V=w(t,"ariaLabel",12,"Mini map"),A=w(t,"nodeStrokeColor",12,"transparent"),O=w(t,"nodeColor",12,void 0),R=w(t,"nodeClass",12,""),S=w(t,"nodeBorderRadius",12,5),M=w(t,"nodeStrokeWidth",12,2),k=w(t,"bgColor",12,void 0),P=w(t,"maskColor",12,void 0),H=w(t,"maskStrokeColor",12,void 0),I=w(t,"maskStrokeWidth",12,void 0),B=w(t,"width",12,void 0),F=w(t,"height",12,void 0),K=w(t,"pannable",12,!0),se=w(t,"zoomable",12,!0),ee=w(t,"inversePan",12,void 0),W=w(t,"zoomStep",12,void 0),fe=w(t,"style",12,""),me=w(t,"class",12,"");const Ce=200,he=150,{nodes:ze,nodeLookup:G,viewport:ae,width:Me,height:Le,flowId:Xe,panZoom:te,translateExtent:Fe}=Ue(),Oe=O()===void 0?void 0:da(O()),rt=da(A()),oe=da(R()),pe=typeof window>"u"||window.chrome?"crispEdges":"geometricPrecision",be=`svelte-flow__minimap-desc-${o()}`;let Ie=re(h(d));const ht=()=>h($);ge(()=>(i(),s(),a()),()=>{U(d,{x:-i().x/i().zoom,y:-i().y/i().zoom,width:s()/i().zoom,height:a()/i().zoom})}),ge(()=>(l(),h(d),u()),()=>{U(Ie,l().size>0?Cu(bo(l()),h(d)):h(d)),u()}),ge(()=>j(B()),()=>{U(g,B()??Ce)}),ge(()=>j(F()),()=>{U(p,F()??he)}),ge(()=>(h(Ie),h(g)),()=>{U(x,h(Ie).width/h(g))}),ge(()=>(h(Ie),h(p)),()=>{U(C,h(Ie).height/h(p))}),ge(()=>(h(x),h(C)),()=>{U($,Math.max(h(x),h(C)))}),ge(()=>(h($),h(g)),()=>{U(m,h($)*h(g))}),ge(()=>(h($),h(p)),()=>{U(_,h($)*h(p))}),ge(()=>h($),()=>{U(v,5*h($))}),ge(()=>(h(Ie),h(m),h(v)),()=>{U(b,h(Ie).x-(h(m)-h(Ie).width)/2-h(v))}),ge(()=>(h(Ie),h(_),h(v)),()=>{U(N,h(Ie).y-(h(_)-h(Ie).height)/2-h(v))}),ge(()=>(h(m),h(v)),()=>{U(E,h(m)+h(v)*2)}),ge(()=>(h(_),h(v)),()=>{U(T,h(_)+h(v)*2)}),vt(),He();const dt=ve(()=>fe()+(k()?`;--xy-minimap-background-color-props:${k()}`:"")),J=ve(()=>Et(["svelte-flow__minimap",me()]));So(e,{get position(){return D()},get style(){return h(dt)},get class(){return h(J)},"data-testid":"svelte-flow__minimap",children:(le,$n)=>{var fn=tt(),En=xe(fn);{var Te=st=>{var ye=U2();de(ye,"aria-labelledby",be);var lt=X(ye);{var ct=Wt=>{var Ot=G2();de(Ot,"id",be);var Sn=X(Ot,!0);Z(Ot),Ee(()=>Bt(Sn,V())),L(Wt,Ot)};ke(lt,Wt=>{V()&&Wt(ct)})}var Jt=z(lt);Yt(Jt,1,u,Wt=>Wt.id,(Wt,Ot)=>{var Sn=tt();const gn=ve(()=>l().get(h(Ot).id));var mt=xe(Sn);{var xa=yr=>{const hn=ve(()=>Qn(h(gn))),ji=ve(()=>Oe==null?void 0:Oe(h(gn))),Ji=ve(()=>rt(h(gn))),Qi=ve(()=>oe(h(gn)));zc(yr,ft({get x(){return h(gn).internals.positionAbsolute.x},get y(){return h(gn).internals.positionAbsolute.y}},()=>h(hn),{get selected(){return h(gn).selected},get color(){return h(ji)},get borderRadius(){return S()},get strokeColor(){return h(Ji)},get strokeWidth(){return M()},shapeRendering:pe,get class(){return h(Qi)}}))};ke(mt,yr=>{h(gn)&&Eu(h(gn))&&yr(xa)})}L(Wt,Sn)});var Oo=z(Jt);Z(ye),_t(ye,(Wt,Ot)=>ca==null?void 0:ca(Wt,Ot),()=>({panZoom:c(),viewport:ae,getViewScale:ht,translateExtent:f(),width:s(),height:a(),inversePan:ee(),zoomStep:W(),pannable:K(),zoomable:se()})),Ee(()=>{de(ye,"width",h(g)),de(ye,"height",h(p)),de(ye,"viewBox",`${h(b)??""} ${h(N)??""} ${h(E)??""} ${h(T)??""}`),at(ye,"--xy-minimap-mask-background-color-props",P()),at(ye,"--xy-minimap-mask-stroke-color-props",H()),at(ye,"--xy-minimap-mask-stroke-width-props",I()?I()*h($):void 0),de(Oo,"d",`M${h(b)-h(v)},${h(N)-h(v)}h${h(E)+h(v)*2}v${h(T)+h(v)*2}h${-h(E)-h(v)*2}z + M${h(d).x??""},${h(d).y??""}h${h(d).width??""}v${h(d).height??""}h${-h(d).width}z`)}),L(st,ye)};ke(En,st=>{c()&&st(Te)})}L(le,fn)},$$slots:{default:!0}});var Re=ce({get position(){return D()},set position(le){D(le),y()},get ariaLabel(){return V()},set ariaLabel(le){V(le),y()},get nodeStrokeColor(){return A()},set nodeStrokeColor(le){A(le),y()},get nodeColor(){return O()},set nodeColor(le){O(le),y()},get nodeClass(){return R()},set nodeClass(le){R(le),y()},get nodeBorderRadius(){return S()},set nodeBorderRadius(le){S(le),y()},get nodeStrokeWidth(){return M()},set nodeStrokeWidth(le){M(le),y()},get bgColor(){return k()},set bgColor(le){k(le),y()},get maskColor(){return P()},set maskColor(le){P(le),y()},get maskStrokeColor(){return H()},set maskStrokeColor(le){H(le),y()},get maskStrokeWidth(){return I()},set maskStrokeWidth(le){I(le),y()},get width(){return B()},set width(le){B(le),y()},get height(){return F()},set height(le){F(le),y()},get pannable(){return K()},set pannable(le){K(le),y()},get zoomable(){return se()},set zoomable(le){se(le),y()},get inversePan(){return ee()},set inversePan(le){ee(le),y()},get zoomStep(){return W()},set zoomStep(le){W(le),y()},get style(){return fe()},set style(le){fe(le),y()},get class(){return me()},set class(le){me(le),y()}});return r(),Re}ie(Rc,{position:{},ariaLabel:{},nodeStrokeColor:{},nodeColor:{},nodeClass:{},nodeBorderRadius:{},nodeStrokeWidth:{},bgColor:{},maskColor:{},maskStrokeColor:{},maskStrokeWidth:{},width:{},height:{},pannable:{},zoomable:{},inversePan:{},zoomStep:{},style:{},class:{}},[],[],!0);const Bc=e=>Uv(e);function Lt(){const{zoomIn:e,zoomOut:t,fitView:n,onbeforedelete:r,snapGrid:o,viewport:i,width:s,height:a,minZoom:l,maxZoom:u,panZoom:c,nodes:f,edges:d,domNode:g,nodeLookup:p,nodeOrigin:x,edgeLookup:C,connectionLookup:$}=Ue(),m=b=>{var V,A;const N=q(p),E=Bc(b)?b:N.get(b.id),T=E.parentId?e0(E.position,E.measured,E.parentId,N,q(x)):E.position,D={...E,position:T,width:((V=E.measured)==null?void 0:V.width)??E.width,height:((A=E.measured)==null?void 0:A.height)??E.height};return Or(D)},_=(b,N,E={replace:!1})=>{var V;const T=(V=q(p).get(b))==null?void 0:V.internals.userNode;if(!T)return;const D=typeof N=="function"?N(T):N;E.replace?f.update(A=>A.map(O=>O.id===b?Bc(D)?D:{...O,...D}:O)):(Object.assign(T,D),f.update(A=>A))},v=b=>q(p).get(b);return{zoomIn:e,zoomOut:t,getInternalNode:v,getNode:b=>{var N;return(N=v(b))==null?void 0:N.internals.userNode},getNodes:b=>b===void 0?q(f):Yc(q(p),b),getEdge:b=>q(C).get(b),getEdges:b=>b===void 0?q(d):Yc(q(C),b),setZoom:(b,N)=>{const E=q(c);return E?E.scaleTo(b,{duration:N==null?void 0:N.duration}):Promise.resolve(!1)},getZoom:()=>q(i).zoom,setViewport:async(b,N)=>{const E=q(i),T=q(c);return T?(await T.setViewport({x:b.x??E.x,y:b.y??E.y,zoom:b.zoom??E.zoom},{duration:N==null?void 0:N.duration}),Promise.resolve(!0)):Promise.resolve(!1)},getViewport:()=>q(i),setCenter:async(b,N,E)=>{const T=typeof(E==null?void 0:E.zoom)<"u"?E.zoom:q(u),D=q(c);return D?(await D.setViewport({x:q(s)/2-b*T,y:q(a)/2-N*T,zoom:T},{duration:E==null?void 0:E.duration}),Promise.resolve(!0)):Promise.resolve(!1)},fitView:n,fitBounds:async(b,N)=>{const E=q(c);if(!E)return Promise.resolve(!1);const T=js(b,q(s),q(a),q(l),q(u),(N==null?void 0:N.padding)??.1);return await E.setViewport(T,{duration:N==null?void 0:N.duration}),Promise.resolve(!0)},getIntersectingNodes:(b,N=!0,E)=>{const T=ku(b),D=T?b:m(b);return D?(E||q(f)).filter(V=>{const A=q(p).get(V.id);if(!A||!T&&V.id===b.id)return!1;const O=Or(A),R=Co(O,D);return N&&R>0||R>=D.width*D.height}):[]},isNodeIntersecting:(b,N,E=!0)=>{const D=ku(b)?b:m(b);if(!D)return!1;const V=Co(D,N);return E&&V>0||V>=D.width*D.height},deleteElements:async({nodes:b=[],edges:N=[]})=>{const{nodes:E,edges:T}=await wu({nodesToRemove:b,edgesToRemove:N,nodes:q(f),edges:q(d),onBeforeDelete:q(r)});return E&&f.update(D=>D.filter(V=>!E.some(({id:A})=>A===V.id))),T&&d.update(D=>D.filter(V=>!T.some(({id:A})=>A===V.id))),{deletedNodes:E,deletedEdges:T}},screenToFlowPosition:(b,N={snapToGrid:!0})=>{const E=q(g);if(!E)return b;const T=N.snapToGrid?q(o):!1,{x:D,y:V,zoom:A}=q(i),{x:O,y:R}=E.getBoundingClientRect(),S={x:b.x-O,y:b.y-R};return ko(S,[D,V,A],T!==null,T||[1,1])},flowToScreenPosition:b=>{const N=q(g);if(!N)return b;const{x:E,y:T,zoom:D}=q(i),{x:V,y:A}=N.getBoundingClientRect(),O=$u(b,[E,T,D]);return{x:O.x+V,y:O.y+A}},toObject:()=>({nodes:q(f).map(b=>({...b,position:{...b.position},data:{...b.data}})),edges:q(d).map(b=>({...b})),viewport:{...q(i)}}),updateNode:_,updateNodeData:(b,N,E)=>{var V;const T=(V=q(p).get(b))==null?void 0:V.internals.userNode;if(!T)return;const D=typeof N=="function"?N(T):N;T.data=E!=null&&E.replace?D:{...T.data,...D},f.update(A=>A)},getNodesBounds:b=>{const N=q(p),E=q(x);return jv(b,{nodeLookup:N,nodeOrigin:E})},getHandleConnections:({type:b,id:N,nodeId:E})=>{var T;return Array.from(((T=q($).get(`${E}-${b}-${N??null}`))==null?void 0:T.values())??[])},viewport:i}}function Yc(e,t){var r;const n=[];for(const o of t){const i=e.get(o);if(i){const s="internals"in i?(r=i.internals)==null?void 0:r.userNode:i;n.push(s)}}return n}var j2=ne('<div class="svelte-flow__node-toolbar"><!></div>');function Zc(e,t){ue(t,!1);const[n,r]=nt(),o=()=>Q(_,"$nodes",n),i=()=>Q(m,"$nodeLookup",n),s=()=>Q($,"$viewport",n),a=()=>Q(C,"$domNode",n),l=re(),u=re(),c=re();let f=w(t,"nodeId",12,void 0),d=w(t,"position",12,void 0),g=w(t,"align",12,void 0),p=w(t,"offset",12,void 0),x=w(t,"isVisible",12,void 0);const{domNode:C,viewport:$,nodeLookup:m,nodes:_}=Ue(),{getNodesBounds:v}=Lt(),b=ur("svelteflow__node_id");let N=re(),E=re([]),T=p()!==void 0?p():10,D=d()!==void 0?d():$e.Top,V=g()!==void 0?g():"center";ge(()=>(o(),j(f()),i()),()=>{o();const M=Array.isArray(f())?f():[f()||b];U(E,M.reduce((k,P)=>{const H=i().get(P);return H&&k.push(H),k},[]))}),ge(()=>(h(E),s()),()=>{const M=v(h(E));M&&U(N,v0(M,s(),D,T,V))}),ge(()=>h(E),()=>{U(l,h(E).length===0?1:Math.max(...h(E).map(M=>(M.internals.z||5)+1)))}),ge(()=>o(),()=>{U(u,o().filter(M=>M.selected).length)}),ge(()=>(j(x()),h(E),h(u)),()=>{U(c,typeof x()=="boolean"?x():h(E).length===1&&h(E)[0].selected&&h(u)===1)}),vt(),He();var A=tt(),O=xe(A);{var R=M=>{var k=j2(),P=X(k);wt(P,t,"default",{}),Z(k),_t(k,(H,I)=>Rr==null?void 0:Rr(H,I),()=>({domNode:a()})),Ee(H=>{de(k,"data-id",H),at(k,"position","absolute"),at(k,"transform",h(N)),at(k,"z-index",h(l))},[()=>h(E).reduce((H,I)=>`${H}${I.id} `,"").trim()],ve),L(M,k)};ke(O,M=>{a()&&h(c)&&h(E)&&M(R)})}L(e,A);var S=ce({get nodeId(){return f()},set nodeId(M){f(M),y()},get position(){return d()},set position(M){d(M),y()},get align(){return g()},set align(M){g(M),y()},get offset(){return p()},set offset(M){p(M),y()},get isVisible(){return x()},set isVisible(M){x(M),y()}});return r(),S}ie(Zc,{nodeId:{},position:{},align:{},offset:{},isVisible:{}},["default"],[],!0);function pr(e){const{nodes:t,nodeLookup:n}=Ue();let r=[],o=!0;return Un([t,n],([,i],s)=>{var c;const a=[],l=Array.isArray(e),u=l?e:[e];for(const f of u){const d=(c=i.get(f))==null?void 0:c.internals.userNode;d&&a.push({id:d.id,type:d.type,data:d.data})}(!C0(a,r)||o)&&(r=a,s(l?a:a[0]??null),o=!1)})}const Xc="tinyflow-component";class J2{constructor(t){Nt(this,"options");Nt(this,"rootEl");Nt(this,"svelteFlowInstance");if(typeof t.element!="string"&&!(t.element instanceof Element))throw new Error("element must be a string or Element");this._setOptions(t),this._init()}_init(){if(typeof this.options.element=="string"){if(this.rootEl=document.querySelector(this.options.element),!this.rootEl)throw new Error(`element not found by document.querySelector('${this.options.element}')`)}else if(this.options.element instanceof Element)this.rootEl=this.options.element;else throw new Error("element must be a string or Element");const t=document.createElement(Xc);t.style.display="block",t.style.width="100%",t.style.height="100%",t.classList.add("tf-theme-light"),t.options=this.options,t.onInit=n=>{this.svelteFlowInstance=n},this.rootEl.appendChild(t)}_setOptions(t){this.options={...t}}getOptions(){return this.options}getData(){return this.svelteFlowInstance.toObject()}setData(t){this.options.data=t;const n=document.createElement(Xc);n.style.display="block",n.style.width="100%",n.style.height="100%",n.classList.add("tf-theme-light"),n.options=this.options,n.onInit=r=>{this.svelteFlowInstance=r},this.destroy(),this.rootEl.appendChild(n)}destroy(){for(;this.rootEl.firstChild;)this.rootEl.removeChild(this.rootEl.firstChild)}}const Bi=(()=>{const e=we([]),t=we([]),n=we({x:250,y:100,zoom:1});return{nodes:e,edges:t,viewport:n,init:(r,o)=>{e.set(r),t.set(o)},addNode:r=>{e.update(o=>[...o,r])},removeNode:r=>{e.update(o=>o.filter(i=>i.id!==r))},updateNode:(r,o)=>{e.update(i=>i.map(s=>s.id===r?o:s))},updateNodeData:(r,o)=>{e.update(i=>i.map(s=>s.id===r?{...s,data:{...s.data,...o}}:s))},selectNodeOnly:r=>{e.update(o=>o.map(i=>i.id===r?{...i,selected:!0}:{...i,selected:!1}))},addEdge:r=>{t.update(o=>[...o,r])},removeEdge:r=>{t.update(o=>o.filter(i=>i.id!==r))},updateEdge:(r,o)=>{t.update(i=>i.map(s=>s.id===r?o:s))},updateEdgeData:(r,o)=>{t.update(i=>i.map(s=>s.id===r?{...s,data:o}:s))}}})();var Q2=ne("<button><!></button>");function Ge(e,t){ue(t,!0);const n=w(t,"children",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","children"]);var o=Q2();let i;var s=X(o);return cr(s,()=>n()??gt),Z(o),Ee(()=>i=nn(o,i,{type:"button",...r,class:`tf-btn nopan nodrag ${t.class??""}`})),L(e,o),ce({get children(){return n()},set children(a){n(a),y()}})}ie(Ge,{children:{}},[],[],!0);var ep=ne("<input>");function Fc(e,t){ue(t,!0);const n=xt(t,["$$slots","$$events","$$legacy","$$host"]);var r=ep();ao(r);let o;Ee(()=>o=nn(r,o,{type:"checkbox",...n,class:`tf-checkbox nopan nodrag ${t.class??""}`})),L(e,r),ce()}ie(Fc,{},[],[],!0);var tp=ne("<input>");function St(e,t){ue(t,!0);const n=xt(t,["$$slots","$$events","$$legacy","$$host"]);var r=tp();ao(r);let o;Ee(()=>o=nn(r,o,{type:"text",...n,class:`tf-input nopan nodrag ${t.class??""}`})),L(e,r),ce()}ie(St,{},[],[],!0);var np=ne("<textarea></textarea>");function Pt(e,t){ue(t,!0);const n=xt(t,["$$slots","$$events","$$legacy","$$host"]);var r=np();Wf(r);let o;Ee(()=>o=nn(r,o,{...n,class:`tf-textarea nodrag ${t.class??""}`})),L(e,r),ce()}ie(Pt,{},[],[],!0);var rp=ne('<div role="button"><!></div>'),op=ne("<div></div>");function Wc(e,t){const n=it(t,["children","$$slots","$$events","$$legacy","$$host"]),r=it(n,["items","onChange","activeIndex"]);ue(t,!1);let o=w(t,"items",28,()=>[]),i=w(t,"onChange",12,()=>{}),s=w(t,"activeIndex",12,0);function a(c,f){var d;s(f),(d=i())==null||d(c,f)}He();var l=op();let u;return Yt(l,5,o,oi,(c,f,d)=>{var g=rp();de(g,"tabindex",d),g.__click=()=>a(h(f),d),g.__keydown=$=>{($.key==="Enter"||$.key===" ")&&($.preventDefault(),a(h(f),d))};var p=X(g);{var x=$=>{var m=Ae();Ee(()=>Bt(m,h(f).label)),L($,m)},C=$=>{var m=tt(),_=xe(m);cr(_,()=>h(f).label??gt),L($,m)};ke(p,$=>{typeof h(f).label=="string"?$(x):$(C,!1)})}Z(g),Ee(()=>$t(g,1,`tf-tabs-item ${(d===s()?"active":"")??""}`)),L(c,g)}),Z(l),Ee(()=>u=nn(l,u,{...r,class:`tf-tabs ${r.class??""}`})),L(e,l),ce({get items(){return o()},set items(c){o(c),y()},get onChange(){return i()},set onChange(c){i(c),y()},get activeIndex(){return s()},set activeIndex(c){s(c),y()}})}ri(["click","keydown"]),ie(Wc,{items:{},onChange:{},activeIndex:{}},[],[],!0);var ip=(e,t,n)=>t(h(n)),sp=(e,t,n)=>{(e.key==="Enter"||e.key===" ")&&(e.preventDefault(),t(h(n)))},ap=ne('<span class="tf-collapse-item-title-icon"><!></span>'),lp=ne('<div class="tf-collapse-item-description"><!></div>'),up=ne('<div class="tf-collapse-item-content"><!></div>'),cp=ne('<div class="tf-collapse-item"><div class="tf-collapse-item-title" role="button"><!> <!> <span><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13.1717 12.0007L8.22192 7.05093L9.63614 5.63672L16.0001 12.0007L9.63614 18.3646L8.22192 16.9504L13.1717 12.0007Z"></path></svg></span></div> <!> <!></div>'),dp=ne("<div></div>");const fp={hash:"svelte-1jfktzw",code:`\r + /* 定义旋转的 CSS 类 */.rotate-90.svelte-1jfktzw {transform:rotate(90deg);transition:transform 0.3s ease;}`};function Kc(e,t){ue(t,!0),et(e,fp);let n=w(t,"items",7),r=w(t,"onChange",7),o=w(t,"activeKeys",31,()=>Ht([]));function i(a){var l;o().includes(a.key)?o(o().filter(u=>u!==a.key)):(o().push(a.key),o(o())),(l=r())==null||l(a,o())}var s=dp();return Yt(s,21,n,oi,(a,l,u)=>{var c=cp(),f=X(c);de(f,"tabindex",u),f.__click=[ip,i,l],f.__keydown=[sp,i,l];var d=X(f);{var g=v=>{var b=ap(),N=X(b);nr(N,{get target(){return h(l).icon}}),Z(b),L(v,b)};ke(d,v=>{h(l).icon&&v(g)})}var p=z(d,2);nr(p,{get target(){return h(l).title}});var x=z(p,2);Z(f);var C=z(f,2);{var $=v=>{var b=lp(),N=X(b);nr(N,{get target(){return h(l).description}}),Z(b),L(v,b)};ke(C,v=>{h(l).description&&v($)})}var m=z(C,2);{var _=v=>{var b=up(),N=X(b);nr(N,{get target(){return h(l).content}}),Z(b),L(v,b)};ke(m,v=>{o().includes(h(l).key)&&v(_)})}Z(c),Ee(v=>$t(x,1,`tf-collapse-item-title-arrow ${v??""}`,"svelte-1jfktzw"),[()=>o().includes(h(l).key)?"rotate-90":""]),L(a,c)}),Z(s),Ee(()=>{de(s,"style",t.style),$t(s,1,`tf-collapse ${t.class??""}`,"svelte-1jfktzw")}),L(e,s),ce({get items(){return n()},set items(a){n(a),y()},get onChange(){return r()},set onChange(a){r(a),y()},get activeKeys(){return o()},set activeKeys(a=[]){o(a),y()}})}ri(["click","keydown"]),ie(Kc,{items:{},onChange:{},activeKeys:{}},[],[],!0);function nr(e,t){ue(t,!0);let n=w(t,"target",7);typeof n()>"u"&&n("undefined");var r=tt(),o=xe(r);{var i=a=>{var l=tt(),u=xe(l);cr(u,()=>n()??gt),L(a,l)},s=a=>{var l=tt(),u=xe(l);dl(u,n),L(a,l)};ke(o,a=>{typeof n()=="function"?a(i):a(s,!1)})}return L(e,r),ce({get target(){return n()},set target(a){n(a),y()}})}ie(nr,{target:{}},[],[],!0);var gp=(e,t,n)=>t(h(n)),hp=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 14L8 10H16L12 14Z"></path></svg>'),vp=ne('<div class="tf-select-content-children"><!></div>'),pp=ne('<button class="tf-select-content-item"><span><!></span> <!></button> <!>',1),mp=ne('<div class="tf-select-content nopan nodrag"><!></div>'),yp=ne("<!> <!>",1),wp=ne('<div class="tf-select-input-placeholder"> </div>'),_p=ne('<button><div class="tf-select-input-value"></div> <div class="tf-select-input-arrow"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11.9999 13.1714L16.9497 8.22168L18.3639 9.63589L11.9999 15.9999L5.63599 9.63589L7.0502 8.22168L11.9999 13.1714Z"></path></svg></div></button>'),xp=ne("<div><!></div>");function ln(e,t){ue(t,!0);const n=(_,v=gt)=>{var b=tt(),N=xe(b);Yt(N,19,v,(E,T)=>`${T}_${E.value}`,(E,T)=>{var D=pp(),V=xe(D);V.__click=[gp,x,T];var A=X(V),O=X(A);{var R=P=>{var H=hp();L(P,H)};ke(O,P=>{h(T).children&&h(T).children.length>0&&P(R)})}Z(A);var S=z(A,2);nr(S,{get target(){return h(T).label}}),Z(V);var M=z(V,2);{var k=P=>{var H=vp(),I=X(H);n(I,()=>h(T).children),Z(H),L(P,H)};ke(M,P=>{h(T).children&&h(T).children.length>0&&(l()||c().includes(h(T).value))&&P(k)})}L(E,D)}),L(_,b)};let r=w(t,"items",7),o=w(t,"onExpand",7),i=w(t,"onSelect",7),s=w(t,"value",23,()=>[]),a=w(t,"defaultValue",23,()=>[]),l=w(t,"expandAll",7,!0),u=w(t,"multiple",7,!1),c=w(t,"expandValue",23,()=>[]),f=w(t,"placeholder",7),d=xt(t,["$$slots","$$events","$$legacy","$$host","items","onExpand","onSelect","value","defaultValue","expandAll","multiple","expandValue","placeholder"]),g=Ne(()=>{const _=[],v=b=>{for(let N of b)s().length>0?s().includes(N.value)&&_.push(N):a().includes(N.value)&&_.push(N),N.children&&N.children.length>0&&v(N.children)};return v(r()),_}),p;function x(_){var v,b;if(_.children&&_.children.length>0){(v=o())==null||v(_);return}else p==null||p.hide(),(b=i())==null||b(_)}var C=xp();let $;var m=X(C);return An(Do(m,{floating:v=>{var b=mp(),N=X(b);n(N,r),Z(b),L(v,b)},children:(v,b)=>{var N=_p();let E;var T=X(N);Yt(T,23,()=>h(g),(D,V)=>`${V}_${D.value}`,(D,V,A)=>{var O=tt(),R=xe(O);{var S=k=>{var P=tt(),H=xe(P);{var I=B=>{nr(B,{get target(){return h(V).label}})};ke(H,B=>{h(A)===0&&B(I)})}L(k,P)},M=k=>{var P=yp(),H=xe(P);nr(H,{get target(){return h(V).label}});var I=z(H,2);{var B=F=>{var K=Ae(",");L(F,K)};ke(I,F=>{h(A)<h(g).length-1&&F(B)})}L(k,P)};ke(R,k=>{u()?k(M,!1):k(S)})}L(D,O)},D=>{var V=wp(),A=X(V,!0);Z(V),Ee(()=>Bt(A,f())),L(D,V)}),Z(T),Pe(2),Z(N),Ee(()=>E=nn(N,E,{class:"tf-select-input nopan nodrag",...d})),L(v,N)},$$slots:{floating:!0,default:!0}}),v=>p=v,()=>p),Z(C),Ee(()=>$=nn(C,$,{...d,class:`tf-select ${d.class??""}`})),L(e,C),ce({get items(){return r()},set items(_){r(_),y()},get onExpand(){return o()},set onExpand(_){o(_),y()},get onSelect(){return i()},set onSelect(_){i(_),y()},get value(){return s()},set value(_=[]){s(_),y()},get defaultValue(){return a()},set defaultValue(_=[]){a(_),y()},get expandAll(){return l()},set expandAll(_=!0){l(_),y()},get multiple(){return u()},set multiple(_=!1){u(_),y()},get expandValue(){return c()},set expandValue(_=[]){c(_),y()},get placeholder(){return f()},set placeholder(_){f(_),y()}})}ri(["click"]),ie(ln,{items:{},onExpand:{},onSelect:{},value:{},defaultValue:{},expandAll:{},multiple:{},expandValue:{},placeholder:{}},[],[],!0);const No=Math.min,Yr=Math.max,Yi=Math.round,bn=e=>({x:e,y:e}),bp={left:"right",right:"left",bottom:"top",top:"bottom"},Cp={start:"end",end:"start"};function fa(e,t,n){return Yr(e,No(t,n))}function To(e,t){return typeof e=="function"?e(t):e}function mr(e){return e.split("-")[0]}function Mo(e){return e.split("-")[1]}function qc(e){return e==="x"?"y":"x"}function ga(e){return e==="y"?"height":"width"}function Zr(e){return["top","bottom"].includes(mr(e))?"y":"x"}function ha(e){return qc(Zr(e))}function kp(e,t,n){n===void 0&&(n=!1);const r=Mo(e),o=ha(e),i=ga(o);let s=o==="x"?r===(n?"end":"start")?"right":"left":r==="start"?"bottom":"top";return t.reference[i]>t.floating[i]&&(s=Zi(s)),[s,Zi(s)]}function $p(e){const t=Zi(e);return[va(e),t,va(t)]}function va(e){return e.replace(/start|end/g,t=>Cp[t])}function Ep(e,t,n){const r=["left","right"],o=["right","left"],i=["top","bottom"],s=["bottom","top"];switch(e){case"top":case"bottom":return n?t?o:r:t?r:o;case"left":case"right":return t?i:s;default:return[]}}function Sp(e,t,n,r){const o=Mo(e);let i=Ep(mr(e),n==="start",r);return o&&(i=i.map(s=>s+"-"+o),t&&(i=i.concat(i.map(va)))),i}function Zi(e){return e.replace(/left|right|bottom|top/g,t=>bp[t])}function Pp(e){return{top:0,right:0,bottom:0,left:0,...e}}function Gc(e){return typeof e!="number"?Pp(e):{top:e,right:e,bottom:e,left:e}}function Xi(e){const{x:t,y:n,width:r,height:o}=e;return{width:r,height:o,top:n,left:t,right:t+r,bottom:n+o,x:t,y:n}}function Uc(e,t,n){let{reference:r,floating:o}=e;const i=Zr(t),s=ha(t),a=ga(s),l=mr(t),u=i==="y",c=r.x+r.width/2-o.width/2,f=r.y+r.height/2-o.height/2,d=r[a]/2-o[a]/2;let g;switch(l){case"top":g={x:c,y:r.y-o.height};break;case"bottom":g={x:c,y:r.y+r.height};break;case"right":g={x:r.x+r.width,y:f};break;case"left":g={x:r.x-o.width,y:f};break;default:g={x:r.x,y:r.y}}switch(Mo(t)){case"start":g[s]-=d*(n&&u?-1:1);break;case"end":g[s]+=d*(n&&u?-1:1);break}return g}const Np=async(e,t,n)=>{const{placement:r="bottom",strategy:o="absolute",middleware:i=[],platform:s}=n,a=i.filter(Boolean),l=await(s.isRTL==null?void 0:s.isRTL(t));let u=await s.getElementRects({reference:e,floating:t,strategy:o}),{x:c,y:f}=Uc(u,r,l),d=r,g={},p=0;for(let x=0;x<a.length;x++){const{name:C,fn:$}=a[x],{x:m,y:_,data:v,reset:b}=await $({x:c,y:f,initialPlacement:r,placement:d,strategy:o,middlewareData:g,rects:u,platform:s,elements:{reference:e,floating:t}});c=m??c,f=_??f,g={...g,[C]:{...g[C],...v}},b&&p<=50&&(p++,typeof b=="object"&&(b.placement&&(d=b.placement),b.rects&&(u=b.rects===!0?await s.getElementRects({reference:e,floating:t,strategy:o}):b.rects),{x:c,y:f}=Uc(u,d,l)),x=-1)}return{x:c,y:f,placement:d,strategy:o,middlewareData:g}};async function jc(e,t){var n;t===void 0&&(t={});const{x:r,y:o,platform:i,rects:s,elements:a,strategy:l}=e,{boundary:u="clippingAncestors",rootBoundary:c="viewport",elementContext:f="floating",altBoundary:d=!1,padding:g=0}=To(t,e),p=Gc(g),C=a[d?f==="floating"?"reference":"floating":f],$=Xi(await i.getClippingRect({element:(n=await(i.isElement==null?void 0:i.isElement(C)))==null||n?C:C.contextElement||await(i.getDocumentElement==null?void 0:i.getDocumentElement(a.floating)),boundary:u,rootBoundary:c,strategy:l})),m=f==="floating"?{x:r,y:o,width:s.floating.width,height:s.floating.height}:s.reference,_=await(i.getOffsetParent==null?void 0:i.getOffsetParent(a.floating)),v=await(i.isElement==null?void 0:i.isElement(_))?await(i.getScale==null?void 0:i.getScale(_))||{x:1,y:1}:{x:1,y:1},b=Xi(i.convertOffsetParentRelativeRectToViewportRelativeRect?await i.convertOffsetParentRelativeRectToViewportRelativeRect({elements:a,rect:m,offsetParent:_,strategy:l}):m);return{top:($.top-b.top+p.top)/v.y,bottom:(b.bottom-$.bottom+p.bottom)/v.y,left:($.left-b.left+p.left)/v.x,right:(b.right-$.right+p.right)/v.x}}const Tp=e=>({name:"arrow",options:e,async fn(t){const{x:n,y:r,placement:o,rects:i,platform:s,elements:a,middlewareData:l}=t,{element:u,padding:c=0}=To(e,t)||{};if(u==null)return{};const f=Gc(c),d={x:n,y:r},g=ha(o),p=ga(g),x=await s.getDimensions(u),C=g==="y",$=C?"top":"left",m=C?"bottom":"right",_=C?"clientHeight":"clientWidth",v=i.reference[p]+i.reference[g]-d[g]-i.floating[p],b=d[g]-i.reference[g],N=await(s.getOffsetParent==null?void 0:s.getOffsetParent(u));let E=N?N[_]:0;(!E||!await(s.isElement==null?void 0:s.isElement(N)))&&(E=a.floating[_]||i.floating[p]);const T=v/2-b/2,D=E/2-x[p]/2-1,V=No(f[$],D),A=No(f[m],D),O=V,R=E-x[p]-A,S=E/2-x[p]/2+T,M=fa(O,S,R),k=!l.arrow&&Mo(o)!=null&&S!==M&&i.reference[p]/2-(S<O?V:A)-x[p]/2<0,P=k?S<O?S-O:S-R:0;return{[g]:d[g]+P,data:{[g]:M,centerOffset:S-M-P,...k&&{alignmentOffset:P}},reset:k}}}),Mp=function(e){return e===void 0&&(e={}),{name:"flip",options:e,async fn(t){var n,r;const{placement:o,middlewareData:i,rects:s,initialPlacement:a,platform:l,elements:u}=t,{mainAxis:c=!0,crossAxis:f=!0,fallbackPlacements:d,fallbackStrategy:g="bestFit",fallbackAxisSideDirection:p="none",flipAlignment:x=!0,...C}=To(e,t);if((n=i.arrow)!=null&&n.alignmentOffset)return{};const $=mr(o),m=Zr(a),_=mr(a)===a,v=await(l.isRTL==null?void 0:l.isRTL(u.floating)),b=d||(_||!x?[Zi(a)]:$p(a)),N=p!=="none";!d&&N&&b.push(...Sp(a,x,p,v));const E=[a,...b],T=await jc(t,C),D=[];let V=((r=i.flip)==null?void 0:r.overflows)||[];if(c&&D.push(T[$]),f){const S=kp(o,s,v);D.push(T[S[0]],T[S[1]])}if(V=[...V,{placement:o,overflows:D}],!D.every(S=>S<=0)){var A,O;const S=(((A=i.flip)==null?void 0:A.index)||0)+1,M=E[S];if(M)return{data:{index:S,overflows:V},reset:{placement:M}};let k=(O=V.filter(P=>P.overflows[0]<=0).sort((P,H)=>P.overflows[1]-H.overflows[1])[0])==null?void 0:O.placement;if(!k)switch(g){case"bestFit":{var R;const P=(R=V.filter(H=>{if(N){const I=Zr(H.placement);return I===m||I==="y"}return!0}).map(H=>[H.placement,H.overflows.filter(I=>I>0).reduce((I,B)=>I+B,0)]).sort((H,I)=>H[1]-I[1])[0])==null?void 0:R[0];P&&(k=P);break}case"initialPlacement":k=a;break}if(o!==k)return{reset:{placement:k}}}return{}}}};async function Hp(e,t){const{placement:n,platform:r,elements:o}=e,i=await(r.isRTL==null?void 0:r.isRTL(o.floating)),s=mr(n),a=Mo(n),l=Zr(n)==="y",u=["left","top"].includes(s)?-1:1,c=i&&l?-1:1,f=To(t,e);let{mainAxis:d,crossAxis:g,alignmentAxis:p}=typeof f=="number"?{mainAxis:f,crossAxis:0,alignmentAxis:null}:{mainAxis:f.mainAxis||0,crossAxis:f.crossAxis||0,alignmentAxis:f.alignmentAxis};return a&&typeof p=="number"&&(g=a==="end"?p*-1:p),l?{x:g*c,y:d*u}:{x:d*u,y:g*c}}const Vp=function(e){return e===void 0&&(e=0),{name:"offset",options:e,async fn(t){var n,r;const{x:o,y:i,placement:s,middlewareData:a}=t,l=await Hp(t,e);return s===((n=a.offset)==null?void 0:n.placement)&&(r=a.arrow)!=null&&r.alignmentOffset?{}:{x:o+l.x,y:i+l.y,data:{...l,placement:s}}}}},Dp=function(e){return e===void 0&&(e={}),{name:"shift",options:e,async fn(t){const{x:n,y:r,placement:o}=t,{mainAxis:i=!0,crossAxis:s=!1,limiter:a={fn:C=>{let{x:$,y:m}=C;return{x:$,y:m}}},...l}=To(e,t),u={x:n,y:r},c=await jc(t,l),f=Zr(mr(o)),d=qc(f);let g=u[d],p=u[f];if(i){const C=d==="y"?"top":"left",$=d==="y"?"bottom":"right",m=g+c[C],_=g-c[$];g=fa(m,g,_)}if(s){const C=f==="y"?"top":"left",$=f==="y"?"bottom":"right",m=p+c[C],_=p-c[$];p=fa(m,p,_)}const x=a.fn({...t,[d]:g,[f]:p});return{...x,data:{x:x.x-n,y:x.y-r,enabled:{[d]:i,[f]:s}}}}}};function Fi(){return typeof window<"u"}function Xr(e){return Jc(e)?(e.nodeName||"").toLowerCase():"#document"}function Xt(e){var t;return(e==null||(t=e.ownerDocument)==null?void 0:t.defaultView)||window}function Bn(e){var t;return(t=(Jc(e)?e.ownerDocument:e.document)||window.document)==null?void 0:t.documentElement}function Jc(e){return Fi()?e instanceof Node||e instanceof Xt(e).Node:!1}function un(e){return Fi()?e instanceof Element||e instanceof Xt(e).Element:!1}function Cn(e){return Fi()?e instanceof HTMLElement||e instanceof Xt(e).HTMLElement:!1}function Qc(e){return!Fi()||typeof ShadowRoot>"u"?!1:e instanceof ShadowRoot||e instanceof Xt(e).ShadowRoot}function Ho(e){const{overflow:t,overflowX:n,overflowY:r,display:o}=cn(e);return/auto|scroll|overlay|hidden|clip/.test(t+r+n)&&!["inline","contents"].includes(o)}function Ap(e){return["table","td","th"].includes(Xr(e))}function Wi(e){return[":popover-open",":modal"].some(t=>{try{return e.matches(t)}catch{return!1}})}function pa(e){const t=ma(),n=un(e)?cn(e):e;return["transform","translate","scale","rotate","perspective"].some(r=>n[r]?n[r]!=="none":!1)||(n.containerType?n.containerType!=="normal":!1)||!t&&(n.backdropFilter?n.backdropFilter!=="none":!1)||!t&&(n.filter?n.filter!=="none":!1)||["transform","translate","scale","rotate","perspective","filter"].some(r=>(n.willChange||"").includes(r))||["paint","layout","strict","content"].some(r=>(n.contain||"").includes(r))}function Lp(e){let t=rr(e);for(;Cn(t)&&!Fr(t);){if(pa(t))return t;if(Wi(t))return null;t=rr(t)}return null}function ma(){return typeof CSS>"u"||!CSS.supports?!1:CSS.supports("-webkit-backdrop-filter","none")}function Fr(e){return["html","body","#document"].includes(Xr(e))}function cn(e){return Xt(e).getComputedStyle(e)}function Ki(e){return un(e)?{scrollLeft:e.scrollLeft,scrollTop:e.scrollTop}:{scrollLeft:e.scrollX,scrollTop:e.scrollY}}function rr(e){if(Xr(e)==="html")return e;const t=e.assignedSlot||e.parentNode||Qc(e)&&e.host||Bn(e);return Qc(t)?t.host:t}function ed(e){const t=rr(e);return Fr(t)?e.ownerDocument?e.ownerDocument.body:e.body:Cn(t)&&Ho(t)?t:ed(t)}function td(e,t,n){var r;t===void 0&&(t=[]);const o=ed(e),i=o===((r=e.ownerDocument)==null?void 0:r.body),s=Xt(o);return i?(ya(s),t.concat(s,s.visualViewport||[],Ho(o)?o:[],[])):t.concat(o,td(o,[]))}function ya(e){return e.parent&&Object.getPrototypeOf(e.parent)?e.frameElement:null}function nd(e){const t=cn(e);let n=parseFloat(t.width)||0,r=parseFloat(t.height)||0;const o=Cn(e),i=o?e.offsetWidth:n,s=o?e.offsetHeight:r,a=Yi(n)!==i||Yi(r)!==s;return a&&(n=i,r=s),{width:n,height:r,$:a}}function rd(e){return un(e)?e:e.contextElement}function Wr(e){const t=rd(e);if(!Cn(t))return bn(1);const n=t.getBoundingClientRect(),{width:r,height:o,$:i}=nd(t);let s=(i?Yi(n.width):n.width)/r,a=(i?Yi(n.height):n.height)/o;return(!s||!Number.isFinite(s))&&(s=1),(!a||!Number.isFinite(a))&&(a=1),{x:s,y:a}}const Op=bn(0);function od(e){const t=Xt(e);return!ma()||!t.visualViewport?Op:{x:t.visualViewport.offsetLeft,y:t.visualViewport.offsetTop}}function Ip(e,t,n){return t===void 0&&(t=!1),!n||t&&n!==Xt(e)?!1:t}function Vo(e,t,n,r){t===void 0&&(t=!1),n===void 0&&(n=!1);const o=e.getBoundingClientRect(),i=rd(e);let s=bn(1);t&&(r?un(r)&&(s=Wr(r)):s=Wr(e));const a=Ip(i,n,r)?od(i):bn(0);let l=(o.left+a.x)/s.x,u=(o.top+a.y)/s.y,c=o.width/s.x,f=o.height/s.y;if(i){const d=Xt(i),g=r&&un(r)?Xt(r):r;let p=d,x=ya(p);for(;x&&r&&g!==p;){const C=Wr(x),$=x.getBoundingClientRect(),m=cn(x),_=$.left+(x.clientLeft+parseFloat(m.paddingLeft))*C.x,v=$.top+(x.clientTop+parseFloat(m.paddingTop))*C.y;l*=C.x,u*=C.y,c*=C.x,f*=C.y,l+=_,u+=v,p=Xt(x),x=ya(p)}}return Xi({width:c,height:f,x:l,y:u})}function wa(e,t){const n=Ki(e).scrollLeft;return t?t.left+n:Vo(Bn(e)).left+n}function id(e,t,n){n===void 0&&(n=!1);const r=e.getBoundingClientRect(),o=r.left+t.scrollLeft-(n?0:wa(e,r)),i=r.top+t.scrollTop;return{x:o,y:i}}function zp(e){let{elements:t,rect:n,offsetParent:r,strategy:o}=e;const i=o==="fixed",s=Bn(r),a=t?Wi(t.floating):!1;if(r===s||a&&i)return n;let l={scrollLeft:0,scrollTop:0},u=bn(1);const c=bn(0),f=Cn(r);if((f||!f&&!i)&&((Xr(r)!=="body"||Ho(s))&&(l=Ki(r)),Cn(r))){const g=Vo(r);u=Wr(r),c.x=g.x+r.clientLeft,c.y=g.y+r.clientTop}const d=s&&!f&&!i?id(s,l,!0):bn(0);return{width:n.width*u.x,height:n.height*u.y,x:n.x*u.x-l.scrollLeft*u.x+c.x+d.x,y:n.y*u.y-l.scrollTop*u.y+c.y+d.y}}function Rp(e){return Array.from(e.getClientRects())}function Bp(e){const t=Bn(e),n=Ki(e),r=e.ownerDocument.body,o=Yr(t.scrollWidth,t.clientWidth,r.scrollWidth,r.clientWidth),i=Yr(t.scrollHeight,t.clientHeight,r.scrollHeight,r.clientHeight);let s=-n.scrollLeft+wa(e);const a=-n.scrollTop;return cn(r).direction==="rtl"&&(s+=Yr(t.clientWidth,r.clientWidth)-o),{width:o,height:i,x:s,y:a}}function Yp(e,t){const n=Xt(e),r=Bn(e),o=n.visualViewport;let i=r.clientWidth,s=r.clientHeight,a=0,l=0;if(o){i=o.width,s=o.height;const u=ma();(!u||u&&t==="fixed")&&(a=o.offsetLeft,l=o.offsetTop)}return{width:i,height:s,x:a,y:l}}function Zp(e,t){const n=Vo(e,!0,t==="fixed"),r=n.top+e.clientTop,o=n.left+e.clientLeft,i=Cn(e)?Wr(e):bn(1),s=e.clientWidth*i.x,a=e.clientHeight*i.y,l=o*i.x,u=r*i.y;return{width:s,height:a,x:l,y:u}}function sd(e,t,n){let r;if(t==="viewport")r=Yp(e,n);else if(t==="document")r=Bp(Bn(e));else if(un(t))r=Zp(t,n);else{const o=od(e);r={x:t.x-o.x,y:t.y-o.y,width:t.width,height:t.height}}return Xi(r)}function ad(e,t){const n=rr(e);return n===t||!un(n)||Fr(n)?!1:cn(n).position==="fixed"||ad(n,t)}function Xp(e,t){const n=t.get(e);if(n)return n;let r=td(e,[]).filter(a=>un(a)&&Xr(a)!=="body"),o=null;const i=cn(e).position==="fixed";let s=i?rr(e):e;for(;un(s)&&!Fr(s);){const a=cn(s),l=pa(s);!l&&a.position==="fixed"&&(o=null),(i?!l&&!o:!l&&a.position==="static"&&!!o&&["absolute","fixed"].includes(o.position)||Ho(s)&&!l&&ad(e,s))?r=r.filter(c=>c!==s):o=a,s=rr(s)}return t.set(e,r),r}function Fp(e){let{element:t,boundary:n,rootBoundary:r,strategy:o}=e;const s=[...n==="clippingAncestors"?Wi(t)?[]:Xp(t,this._c):[].concat(n),r],a=s[0],l=s.reduce((u,c)=>{const f=sd(t,c,o);return u.top=Yr(f.top,u.top),u.right=No(f.right,u.right),u.bottom=No(f.bottom,u.bottom),u.left=Yr(f.left,u.left),u},sd(t,a,o));return{width:l.right-l.left,height:l.bottom-l.top,x:l.left,y:l.top}}function Wp(e){const{width:t,height:n}=nd(e);return{width:t,height:n}}function Kp(e,t,n){const r=Cn(t),o=Bn(t),i=n==="fixed",s=Vo(e,!0,i,t);let a={scrollLeft:0,scrollTop:0};const l=bn(0);if(r||!r&&!i)if((Xr(t)!=="body"||Ho(o))&&(a=Ki(t)),r){const d=Vo(t,!0,i,t);l.x=d.x+t.clientLeft,l.y=d.y+t.clientTop}else o&&(l.x=wa(o));const u=o&&!r&&!i?id(o,a):bn(0),c=s.left+a.scrollLeft-l.x-u.x,f=s.top+a.scrollTop-l.y-u.y;return{x:c,y:f,width:s.width,height:s.height}}function _a(e){return cn(e).position==="static"}function ld(e,t){if(!Cn(e)||cn(e).position==="fixed")return null;if(t)return t(e);let n=e.offsetParent;return Bn(e)===n&&(n=n.ownerDocument.body),n}function ud(e,t){const n=Xt(e);if(Wi(e))return n;if(!Cn(e)){let o=rr(e);for(;o&&!Fr(o);){if(un(o)&&!_a(o))return o;o=rr(o)}return n}let r=ld(e,t);for(;r&&Ap(r)&&_a(r);)r=ld(r,t);return r&&Fr(r)&&_a(r)&&!pa(r)?n:r||Lp(e)||n}const qp=async function(e){const t=this.getOffsetParent||ud,n=this.getDimensions,r=await n(e.floating);return{reference:Kp(e.reference,await t(e.floating),e.strategy),floating:{x:0,y:0,width:r.width,height:r.height}}};function Gp(e){return cn(e).direction==="rtl"}const Up={convertOffsetParentRelativeRectToViewportRelativeRect:zp,getDocumentElement:Bn,getClippingRect:Fp,getOffsetParent:ud,getElementRects:qp,getClientRects:Rp,getDimensions:Wp,getScale:Wr,isElement:un,isRTL:Gp},jp=Vp,Jp=Dp,Qp=Mp,em=Tp,tm=(e,t,n)=>{const r=new Map,o={platform:Up,...n},i={...o.platform,_c:r};return Np(e,t,{...o,platform:i})},nm=({trigger:e,triggerEvent:t,floatContent:n,placement:r="bottom",offsetOptions:o,flipOptions:i,shiftOptions:s,interactive:a,showArrow:l})=>{if(typeof e=="string"){const $=document.querySelector(e);if($)e=$;else throw new Error("element not found by document.querySelector('"+e+"')")}let u;if(typeof n=="string"){const $=document.querySelector(n);if($)u=$;else throw new Error("element not found by document.querySelector('"+n+"')")}else u=n;let c;l&&(c=document.createElement("div"),c.style.position="absolute",c.style.backgroundColor="#222",c.style.width="8px",c.style.height="8px",c.style.transform="rotate(45deg)",c.style.display="none",u.firstElementChild.before(c));function f(){tm(e,u,{placement:r,middleware:[jp(o),Qp(i),Jp(s),...l?[em({element:c})]:[]]}).then(({x:$,y:m,placement:_,middlewareData:v})=>{if(Object.assign(u.style,{left:`${$}px`,top:`${m}px`}),l){const{x:b,y:N}=v.arrow,E={top:"bottom",right:"left",bottom:"top",left:"right"}[_.split("-")[0]];Object.assign(c.style,{zIndex:-1,left:b!=null?`${b}px`:"",top:N!=null?`${N}px`:"",right:"",bottom:"",[E]:"2px"})}})}let d=!1;function g(){u.style.display="block",u.style.visibility="block",u.style.position="absolute",l&&(c.style.display="block"),d=!0,f()}function p(){u.style.display="none",l&&(c.style.display="none"),d=!1}function x($){$.stopPropagation(),d?p():g()}function C($){u.contains($.target)||p()}return(!t||t.length==0)&&(t=["click"]),t.forEach($=>{e.addEventListener($,x)}),document.addEventListener("click",C),{destroy(){t.forEach($=>{e.removeEventListener($,x)}),document.removeEventListener("click",C)},hide(){p()},isVisible(){return d}}};var rm=ne('<div style="position: relative"><div><!></div> <div style="display: none; width: 100%;z-index: 9999"><!></div></div>');function Do(e,t){ue(t,!0);const n=w(t,"children",7),r=w(t,"floating",7),o=w(t,"placement",7,"bottom");let i,s,a;rn(()=>(a=nm({trigger:i,floatContent:s,interactive:!0,placement:o()}),()=>{a.destroy()}));function l(){a.hide()}var u=rm(),c=X(u),f=X(c);cr(f,n),Z(c),An(c,p=>i=p,()=>i);var d=z(c,2),g=X(d);return cr(g,r),Z(d),An(d,p=>s=p,()=>s),Z(u),L(e,u),ce({hide:l,get children(){return n()},set children(p){n(p),y()},get floating(){return r()},set floating(p){r(p),y()},get placement(){return o()},set placement(p="bottom"){o(p),y()}})}ie(Do,{children:{},floating:{},placement:{}},[],["hide"],!0);function je(e,t){ue(t,!0);const n=w(t,"children",7),r=w(t,"level",7,1),o=w(t,"mt",7),i=w(t,"mb",7);var s=tt(),a=xe(s);return t1(a,()=>`h${r()}`,!1,(l,u)=>{let c;Ee(()=>c=nn(l,c,{class:"tf-heading",style:`margin-top:${o()||"0"};margin-bottom:${i()||"0"}`},void 0,l.namespaceURI===Ea,l.nodeName.includes("-")));var f=tt(),d=xe(f);cr(d,()=>n()??gt),L(u,f)}),L(e,s),ce({get children(){return n()},set children(l){n(l),y()},get level(){return r()},set level(l=1){r(l),y()},get mt(){return o()},set mt(l){o(l),y()},get mb(){return i()},set mb(l){i(l),y()}})}ie(je,{children:{},level:{},mt:{},mb:{}},[],[],!0);var om=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="svelte-1rvn4a8"><path d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z" class="svelte-1rvn4a8"></path></svg>');const im={hash:"svelte-1rvn4a8",code:".input-btn-more {border:1px solid transparent;padding:3px;&:hover {background:#eee;border:1px solid transparent;}}"};function qi(e,t){ue(t,!0),et(e,im);const n=xt(t,["$$slots","$$events","$$legacy","$$host"]);Ge(e,ft(()=>n,{get class(){return`input-btn-more ${t.class??""}`},children:(r,o)=>{var i=om();L(r,i)},$$slots:{default:!0}})),ce()}ie(qi,{},[],[],!0);const sm=()=>{const e=Ue();return{deleteNode:n=>{e.nodes.update(r=>r.filter(o=>o.id!==n)),e.edges.update(r=>r.filter(o=>o.source!==n&&o.target!==n))}}},Kr=(e=16)=>{const t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",n=new Uint8Array(e);return crypto.getRandomValues(n),Array.from(n,r=>t[r%t.length]).join("")},am=()=>{const{nodes:e,nodeLookup:t}=Ue();return{copyNode:r=>{var s;const i=(s=q(t).get(r))==null?void 0:s.internals.userNode;if(i){const a=Kr(),l={...i,id:a,position:{x:i.position.x+50,y:i.position.y+50}};e.update(u=>[...u,l]),e.update(u=>u.map(c=>c.id===a?{...c,selected:!0}:{...c,selected:!1}))}}}};var lm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M8 18.3915V5.60846L18.2264 12L8 18.3915ZM6 3.80421V20.1957C6 20.9812 6.86395 21.46 7.53 21.0437L20.6432 12.848C21.2699 12.4563 21.2699 11.5436 20.6432 11.152L7.53 2.95621C6.86395 2.53993 6 3.01878 6 3.80421Z"></path></svg>'),um=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM5.00242 8L5.00019 20H14.9998V8H5.00242ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z"></path></svg>'),cm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M17 6H22V8H20V21C20 21.5523 19.5523 22 19 22H5C4.44772 22 4 21.5523 4 21V8H2V6H7V3C7 2.44772 7.44772 2 8 2H16C16.5523 2 17 2.44772 17 3V6ZM18 8H6V20H18V8ZM9 11H11V17H9V11ZM13 11H15V17H13V11ZM9 4V6H15V4H9Z"></path></svg>'),dm=ne('<div class="tf-node-toolbar svelte-44dmwv"><!> <!> <!></div>'),fm=ne('<!> <div class="tf-node-wrapper"><div class="tf-node-wrapper-title">TinyFlow.ai</div> <div class="tf-node-wrapper-body"><!></div></div> <!> <!> <!>',1);const gm={hash:"svelte-44dmwv",code:".tf-node-toolbar.svelte-44dmwv {display:flex;gap:5px;padding:5px;border-radius:5px;background:#fff;border:1px solid #eee;box-shadow:0 0 5px rgba(0, 0, 0, 0.1);}.tf-node-toolbar-item {border:1px solid transparent;}"};function dn(e,t){ue(t,!0),et(e,gm);const n=w(t,"data",7),r=w(t,"id",7,""),o=w(t,"icon",7),i=w(t,"handle",7),s=w(t,"children",7),a=w(t,"allowExecute",7,!0),l=w(t,"allowCopy",7,!0),u=w(t,"allowDelete",7,!0),c=w(t,"showSourceHandle",7,!0),f=w(t,"showTargetHandle",7,!0);let d=n().expand?["key"]:[];const{updateNodeData:g}=Lt(),p=[{key:"key",icon:o(),title:n().title,description:n().description,content:s()}],{deleteNode:x}=sm(),{copyNode:C}=am();var $=fm(),m=xe($);{var _=O=>{Zc(O,{get position(){return $e.Top},align:"end",children:(R,S)=>{var M=dm(),k=X(M);{var P=K=>{Ge(K,{class:"tf-node-toolbar-item",children:(se,ee)=>{var W=lm();L(se,W)},$$slots:{default:!0}})};ke(k,K=>{a()&&K(P)})}var H=z(k,2);{var I=K=>{Ge(K,{class:"tf-node-toolbar-item",onclick:()=>{C(r())},children:(se,ee)=>{var W=um();L(se,W)},$$slots:{default:!0}})};ke(H,K=>{l()&&K(I)})}var B=z(H,2);{var F=K=>{Ge(K,{class:"tf-node-toolbar-item",onclick:()=>{x(r())},children:(se,ee)=>{var W=cm();L(se,W)},$$slots:{default:!0}})};ke(B,K=>{u()&&K(F)})}Z(M),L(R,M)},$$slots:{default:!0}})};ke(m,O=>{(a()||l()||u())&&O(_)})}var v=z(m,2),b=z(X(v),2),N=X(b);Kc(N,{items:p,activeKeys:d,onChange:(O,R)=>{g(r(),{expand:R==null?void 0:R.includes("key")})}}),Z(b),Z(v);var E=z(v,2);{var T=O=>{er(O,{type:"target",get position(){return $e.Left},style:" left: -12px;top: 20px"})};ke(E,O=>{f()&&O(T)})}var D=z(E,2);{var V=O=>{er(O,{type:"source",get position(){return $e.Right},style:"right: -12px;top: 20px"})};ke(D,O=>{c()&&O(V)})}var A=z(D,2);return cr(A,()=>i()??gt),L(e,$),ce({get data(){return n()},set data(O){n(O),y()},get id(){return r()},set id(O=""){r(O),y()},get icon(){return o()},set icon(O){o(O),y()},get handle(){return i()},set handle(O){i(O),y()},get children(){return s()},set children(O){s(O),y()},get allowExecute(){return a()},set allowExecute(O=!0){a(O),y()},get allowCopy(){return l()},set allowCopy(O=!0){l(O),y()},get allowDelete(){return u()},set allowDelete(O=!0){u(O),y()},get showSourceHandle(){return c()},set showSourceHandle(O=!0){c(O),y()},get showTargetHandle(){return f()},set showTargetHandle(O=!0){f(O),y()}})}ie(dn,{data:{},id:{},icon:{},handle:{},children:{},allowExecute:{},allowCopy:{},allowDelete:{},showSourceHandle:{},showTargetHandle:{}},[],[],!0);function pt(){return ur("svelteflow__node_id")}const cd=[{value:"String",label:"String"},{value:"Number",label:"Number"},{value:"Boolean",label:"Boolean"},{value:"File",label:"File"},{value:"Object",label:"Object"},{value:"Array",label:"Array"}],hm=[{value:"ref",label:"引用"},{value:"input",label:"固定值"}];var vm=ne('<div class="input-more-setting svelte-laou7w"><div class="input-more-item svelte-laou7w">参数类型: <!></div> <div class="input-more-item svelte-laou7w">默认值: <!></div> <div class="input-more-item svelte-laou7w">参数描述: <!></div> <div class="input-more-item svelte-laou7w"><!></div></div>'),pm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.5 10.5C3.675 10.5 3 11.175 3 12C3 12.825 3.675 13.5 4.5 13.5C5.325 13.5 6 12.825 6 12C6 11.175 5.325 10.5 4.5 10.5ZM19.5 10.5C18.675 10.5 18 11.175 18 12C18 12.825 18.675 13.5 19.5 13.5C20.325 13.5 21 12.825 21 12C21 11.175 20.325 10.5 19.5 10.5ZM12 10.5C11.175 10.5 10.5 11.175 10.5 12C10.5 12.825 11.175 13.5 12 13.5C12.825 13.5 13.5 12.825 13.5 12C13.5 11.175 12.825 10.5 12 10.5Z"></path></svg>'),mm=ne('<div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div>',1);const ym={hash:"svelte-laou7w",code:".input-item.svelte-laou7w {display:flex;align-items:center;}.input-more-setting.svelte-laou7w {display:flex;flex-direction:column;gap:10px;padding:10px;background:#fff;border:1px solid #ddd;border-radius:5px;width:200px;box-shadow:0 0 10px 2px rgba(0, 0, 0, 0.1);}.input-more-setting.svelte-laou7w .input-more-item:where(.svelte-laou7w) {display:flex;flex-direction:column;gap:3px;font-size:12px;color:#666;}"};function dd(e,t){ue(t,!0),et(e,ym);const[n,r]=nt(),o=()=>Q(h(l),"$node",n),i=w(t,"parameter",7),s=w(t,"index",7);let a=pt(),l=Ne(()=>pr(a)),u=Ne(()=>{var T,D;return{...i(),...(D=(T=o())==null?void 0:T.data)==null?void 0:D.parameters[s()]}});const{updateNodeData:c}=Lt(),f=T=>{const D=T.target.value;c(a,V=>{let A=V.data.parameters;return A[s()].name=D,{parameters:A}})},d=T=>{const D=T.target.checked;c(a,V=>{let A=V.data.parameters;return A[s()].required=D,{parameters:A}})},g=T=>{const D=T.value;D&&c(a,V=>{let A=V.data.parameters;return A[s()].dataType=D,{parameters:A}})};let p;const x=()=>{c(a,T=>{let D=T.data.parameters;return D.splice(s(),1),{parameters:[...D]}}),p==null||p.hide()};var C=mm(),$=xe(C),m=X($);St(m,{style:"width: 100%;",get value(){return h(u).name},placeholder:"请输入参数名称",oninput:f}),Z($);var _=z($,2),v=X(_);Fc(v,{get checked(){return h(u).required},onchange:d}),Z(_);var b=z(_,2),N=X(b);An(Do(N,{placement:"bottom",floating:D=>{var V=vm(),A=X(V),O=z(X(A));const R=Ne(()=>h(u).dataType?[h(u).dataType]:["String"]);ln(O,{items:cd,style:"width: 100%",onSelect:g,get value(){return h(R)}}),Z(A);var S=z(A,2),M=z(X(S));Pt(M,{rows:1,style:"width: 100%;"}),Z(S);var k=z(S,2),P=z(X(k));Pt(P,{rows:3,style:"width: 100%;"}),Z(k);var H=z(k,2),I=X(H);Ge(I,{onclick:x,children:(B,F)=>{Pe();var K=Ae("删除");L(B,K)},$$slots:{default:!0}}),Z(H),Z(V),L(D,V)},children:(D,V)=>{Ge(D,{class:"input-btn-more",children:(A,O)=>{var R=pm();L(A,R)},$$slots:{default:!0}})},$$slots:{floating:!0,default:!0}}),D=>p=D,()=>p),Z(b),L(e,C);var E=ce({get parameter(){return i()},set parameter(T){i(T),y()},get index(){return s()},set index(T){s(T),y()}});return r(),E}ie(dd,{parameter:{},index:{}},[],[],!0);var wm=ne('<div class="input-header svelte-3n0wca">参数名称</div> <div class="input-header svelte-3n0wca">必填</div> <div class="input-header svelte-3n0wca"></div>',1),_m=ne('<div class="none-params svelte-3n0wca">无输入参数</div>'),xm=ne('<div class="input-container svelte-3n0wca"><!> <!></div>');const bm={hash:"svelte-3n0wca",code:`.input-container.svelte-3n0wca {display:grid;grid-template-columns:80% 10% 10%;row-gap:5px;column-gap:3px;}.input-container.svelte-3n0wca .none-params:where(.svelte-3n0wca) {font-size:12px;background:#f8f8f8;height:40px;display:flex;justify-content:center;align-items:center;border-radius:5px;width:calc(100% - 5px);grid-column:1 / -1; + /* 从第一列开始到最后一列结束 */}.input-container.svelte-3n0wca .input-header:where(.svelte-3n0wca) {font-size:12px;color:#666;}`};function fd(e,t){ue(t,!0),et(e,bm);const[n,r]=nt(),o=()=>Q(h(s),"$node",n);let i=pt(),s=Ne(()=>pr(i)),a=Ne(()=>{var d,g;return[...((g=(d=o())==null?void 0:d.data)==null?void 0:g.parameters)||[]]});var l=xm(),u=X(l);{var c=d=>{var g=wm();Pe(4),L(d,g)};ke(u,d=>{h(a).length!==0&&d(c)})}var f=z(u,2);Yt(f,19,()=>h(a),d=>d.id,(d,g,p)=>{dd(d,{get parameter(){return h(g)},get index(){return h(p)}})},d=>{var g=_m();L(d,g)}),Z(l),L(e,l),ce(),r()}ie(fd,{},[],[],!0);const gd=e=>{!e||e.length==0||e.forEach(t=>{t.id||(t.id=Kr()),gd(t.children)})},kn=()=>{const{updateNodeData:e}=Lt();return{addParameter:(t,n="parameters",r)=>{gd(r==null?void 0:r.children);const o={...r,id:Kr()};e(t,i=>{let s=i.data[n];return s?s.push(o):s=[o],{[n]:[...s]}})}}};var Cm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12C15 13.6569 13.6569 15 12 15Z"></path></svg>'),km=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),$m=ne('<div class="heading svelte-r5g35l"><!> <!></div> <!>',1);const Em={hash:"svelte-r5g35l",code:".heading.svelte-r5g35l {display:flex;margin-bottom:10px;}.input-btn-more {border:1px solid transparent;padding:3px;}.input-btn-more:hover {background:#eee;border:1px solid transparent;}"};function hd(e,t){ue(t,!0),et(e,Em);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn();return dn(e,ft(()=>r,{get data(){return n()},allowExecute:!1,showTargetHandle:!1,icon:a=>{var l=Cm();L(a,l)},children:(a,l)=>{var u=$m(),c=xe(u),f=X(c);je(f,{level:3,children:(p,x)=>{Pe();var C=Ae("输入参数");L(p,C)},$$slots:{default:!0}});var d=z(f,2);Ge(d,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(p,x)=>{var C=km();L(p,C)},$$slots:{default:!0}}),Z(c);var g=z(c,2);fd(g,{}),L(a,u)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(s){n(s),y()}})}ie(hd,{data:{}},[],[],!0);const vd=(e,t,n)=>{for(let r of n)r.target===t&&r.source&&(e.push(r.source),vd(e,r.source,n))},pd=(e,t)=>{if(e.type==="startNode"){const n=e.data.parameters,r=[];if(n)for(const o of n)r.push({label:o.name+(t?` (Array<${o.dataType||"String"}>)`:` (${o.dataType||"String"})`),value:e.id+"."+o.name});return{label:e.data.title,value:e.id,children:r}}else{if(e.type==="loopNode"&&t)return{label:e.data.title,value:e.id,children:[{label:"loopItem",value:e.id+".loop"},{label:"index (Number)",value:e.id+".index"}]};{const n=e.data.outputDefs;if(n){const r=(o,i)=>!o||o.length===0?[]:o.map(s=>({label:s.name+(t?` (Array<${s.dataType||"String"}>)`:` (${s.dataType||"String"})`),value:i+"."+s.name,children:r(s.children,i+"."+s.name)}));return{label:e.data.title,value:e.id,children:r(n,e.id)}}}}},Sm=(e=!1)=>{const t=pt(),n=pr(t),{nodes:r,edges:o}=Ue();return Un([n,r,o],([i,s,a])=>{const l=[];if(e){for(let u of s)if(u.parentId===i.id){const c=pd(u,u.parentId===i.id);c&&l.push(c)}}else{const u=[];vd(u,t,a);for(let c of s)if(u.includes(c.id)){const f=pd(c,c.parentId===i.id);f&&l.push(f)}}return l})};var Pm=ne('<div class="input-more-setting svelte-laou7w"><div class="input-more-item svelte-laou7w">数据来源: <!></div> <div class="input-more-item svelte-laou7w">默认值: <!></div> <div class="input-more-item svelte-laou7w">参数描述: <!></div> <div class="input-more-item svelte-laou7w"><!></div></div>'),Nm=ne('<div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div> <div class="input-item svelte-laou7w"><!></div>',1);const Tm={hash:"svelte-laou7w",code:".input-item.svelte-laou7w {display:flex;align-items:center;}.input-more-setting.svelte-laou7w {display:flex;flex-direction:column;gap:10px;padding:10px;background:#fff;border:1px solid #ddd;border-radius:5px;width:200px;box-shadow:0 0 10px 2px rgba(0, 0, 0, 0.1);}.input-more-setting.svelte-laou7w .input-more-item:where(.svelte-laou7w) {display:flex;flex-direction:column;gap:3px;font-size:12px;color:#666;}"};function md(e,t){ue(t,!0),et(e,Tm);const[n,r]=nt(),o=()=>Q(h(c),"$node",n),i=()=>Q(v,"$selectItems",n),s=w(t,"parameter",7),a=w(t,"index",7),l=w(t,"dataKeyName",7);let u=pt(),c=Ne(()=>pr(u)),f=Ne(()=>{var M;return{...s(),...(M=o())==null?void 0:M.data[l()][a()]}});const{updateNodeData:d}=Lt(),g=(M,k)=>{d(u,P=>{let H=P.data[l()];return H[a()]={...H[a()],[M]:k},{[l()]:H}})},p=M=>{const k=M.target.value;g("name",k)},x=M=>{const k=M.target.value;g("value",k)},C=M=>{const k=M.value;g("ref",k)},$=M=>{const k=M.value;g("refType",k)};let m;const _=()=>{d(u,M=>{let k=M.data[l()];return k.splice(a(),1),{[l()]:[...k]}}),m==null||m.hide()},v=Sm();var b=Nm(),N=xe(b),E=X(N);St(E,{style:"width: 100%;",get value(){return h(f).name},placeholder:"请输入参数名称",oninput:p}),Z(N);var T=z(N,2),D=X(T);{var V=M=>{St(M,{get value(){return h(f).value},placeholder:"请输入参数值",oninput:x})},A=M=>{const k=Ne(()=>[h(f).ref]);ln(M,{get items(){return i()},style:"width: 100%",defaultValue:["ref"],get value(){return h(k)},expandAll:!0,onSelect:C})};ke(D,M=>{h(f).refType==="input"?M(V):M(A,!1)})}Z(T);var O=z(T,2),R=X(O);An(Do(R,{placement:"bottom",floating:k=>{var P=Pm(),H=X(P),I=z(X(H));const B=Ne(()=>h(f).refType?[h(f).refType]:[]);ln(I,{items:hm,style:"width: 100%",defaultValue:["ref"],get value(){return h(B)},onSelect:$}),Z(H);var F=z(H,2),K=z(X(F));Pt(K,{rows:1,style:"width: 100%;",onchange:me=>{const Ce=me.target.value;g("defaultValue",Ce)}}),Z(F);var se=z(F,2),ee=z(X(se));Pt(ee,{rows:3,style:"width: 100%;",onchange:me=>{const Ce=me.target.value;g("description",Ce)}}),Z(se);var W=z(se,2),fe=X(W);Ge(fe,{onclick:_,children:(me,Ce)=>{Pe();var he=Ae("删除");L(me,he)},$$slots:{default:!0}}),Z(W),Z(P),L(k,P)},children:(k,P)=>{qi(k,{})},$$slots:{floating:!0,default:!0}}),k=>m=k,()=>m),Z(O),L(e,b);var S=ce({get parameter(){return s()},set parameter(M){s(M),y()},get index(){return a()},set index(M){a(M),y()},get dataKeyName(){return l()},set dataKeyName(M){l(M),y()}});return r(),S}ie(md,{parameter:{},index:{},dataKeyName:{}},[],[],!0);var Mm=ne('<div class="input-header svelte-1sm1mgi">参数名称</div> <div class="input-header svelte-1sm1mgi">参数值</div> <div class="input-header svelte-1sm1mgi"></div>',1),Hm=ne('<div class="none-params svelte-1sm1mgi"> </div>'),Vm=ne('<div class="input-container svelte-1sm1mgi"><!> <!></div>');const Dm={hash:"svelte-1sm1mgi",code:`.input-container.svelte-1sm1mgi {display:grid;grid-template-columns:40% 50% 10%;row-gap:5px;column-gap:3px;}.input-container.svelte-1sm1mgi .none-params:where(.svelte-1sm1mgi) {font-size:12px;background:#f8f8f8;height:40px;display:flex;justify-content:center;align-items:center;border-radius:5px;width:calc(100% - 5px);grid-column:1 / -1; + /* 从第一列开始到最后一列结束 */}.input-container.svelte-1sm1mgi .input-header:where(.svelte-1sm1mgi) {font-size:12px;color:#666;}`};function Ft(e,t){ue(t,!0),et(e,Dm);const[n,r]=nt(),o=()=>Q(h(l),"$node",n),i=w(t,"noneParameterText",7,"无输入参数"),s=w(t,"dataKeyName",7,"parameters");let a=pt(),l=Ne(()=>pr(a)),u=Ne(()=>{var x;return[...((x=o())==null?void 0:x.data[s()])||[]]});var c=Vm(),f=X(c);{var d=x=>{var C=Mm();Pe(4),L(x,C)};ke(f,x=>{h(u).length!==0&&x(d)})}var g=z(f,2);Yt(g,19,()=>h(u),x=>x.id,(x,C,$)=>{md(x,{get parameter(){return h(C)},get index(){return h($)},get dataKeyName(){return s()}})},x=>{var C=Hm(),$=X(C,!0);Z(C),Ee(()=>Bt($,i())),L(x,C)}),Z(c),L(e,c);var p=ce({get noneParameterText(){return i()},set noneParameterText(x="无输入参数"){i(x),y()},get dataKeyName(){return s()},set dataKeyName(x="parameters"){s(x),y()}});return r(),p}ie(Ft,{noneParameterText:{},dataKeyName:{}},[],[],!0);var Am=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6 5.1438V16.0002H18.3391L6 5.1438ZM4 2.932C4 2.07155 5.01456 1.61285 5.66056 2.18123L21.6501 16.2494C22.3423 16.8584 21.9116 18.0002 20.9896 18.0002H6V22H4V2.932Z"></path></svg>'),Lm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),Om=ne('<div class="heading svelte-11h445j"><!> <!></div> <!>',1);const Im={hash:"svelte-11h445j",code:".heading.svelte-11h445j {display:flex;margin-bottom:10px;}"};function yd(e,t){ue(t,!0),et(e,Im);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn();return dn(e,ft({get data(){return n()}},()=>r,{allowExecute:!1,showSourceHandle:!1,icon:a=>{var l=Am();L(a,l)},children:(a,l)=>{var u=Om(),c=xe(u),f=X(c);je(f,{level:3,children:(p,x)=>{Pe();var C=Ae("输出参数");L(p,C)},$$slots:{default:!0}});var d=z(f,2);Ge(d,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o,"outputDefs")},children:(p,x)=>{var C=Lm();L(p,C)},$$slots:{default:!0}}),Z(c);var g=z(c,2);Ft(g,{noneParameterText:"无输出参数",dataKeyName:"outputDefs"}),L(a,u)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(s){n(s),y()}})}ie(yd,{data:{}},[],[],!0);const Ao=()=>ur("tinyflow_options");var zm=_e('<svg style="transform: scaleY(-1)" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M13 8V16C13 17.6569 11.6569 19 10 19H7.82929C7.41746 20.1652 6.30622 21 5 21C3.34315 21 2 19.6569 2 18C2 16.3431 3.34315 15 5 15C6.30622 15 7.41746 15.8348 7.82929 17H10C10.5523 17 11 16.5523 11 16V8C11 6.34315 12.3431 5 14 5H17V2L22 6L17 10V7H14C13.4477 7 13 7.44772 13 8ZM5 19C5.55228 19 6 18.5523 6 18C6 17.4477 5.55228 17 5 17C4.44772 17 4 17.4477 4 18C4 18.5523 4.44772 19 5 19Z"></path></svg>'),Rm=ne('<div class="input-more-item svelte-1cfeest"><!></div>'),Bm=ne('<div class="input-more-setting svelte-1cfeest"><div class="input-more-item svelte-1cfeest">默认值: <!></div> <div class="input-more-item svelte-1cfeest">参数描述: <!></div> <!></div>'),Ym=ne('<div class="input-item svelte-1cfeest"><!> <!></div> <div class="input-item svelte-1cfeest"><!> <!></div> <div class="input-item svelte-1cfeest"><!></div>',1);const Zm={hash:"svelte-1cfeest",code:".input-item.svelte-1cfeest {display:flex;align-items:center;gap:2px;}.input-more-setting.svelte-1cfeest {display:flex;flex-direction:column;gap:10px;padding:10px;background:#fff;border:1px solid #ddd;border-radius:5px;width:200px;box-shadow:0 0 10px 2px rgba(0, 0, 0, 0.1);}.input-more-setting.svelte-1cfeest .input-more-item:where(.svelte-1cfeest) {display:flex;flex-direction:column;gap:3px;font-size:12px;color:#666;}"};function wd(e,t){ue(t,!0),et(e,Zm);const[n,r]=nt(),o=()=>Q(h(u),"$node",n),i=w(t,"parameter",7),s=w(t,"position",7),a=w(t,"dataKeyName",7);let l=pt(),u=Ne(()=>pr(l)),c=Ne(()=>{var I;let P=(I=o())==null?void 0:I.data[a()],H;if(P&&s().length>0){let B=P;for(let F=0;F<s().length;F++){const K=s()[F];F==s().length-1?H=B[K]:B=B[K].children}}return{...i(),...H}});const{updateNodeData:f}=Lt(),d=(P,H)=>{f(l,I=>{const B=I.data[a()];if(B&&s().length>0){let F=B;for(let K=0;K<s().length;K++){const se=s()[K];K==s().length-1?F[se]={...F[se],[P]:H}:F=B[se].children}}return{[a()]:B}})},g=P=>{const H=P.target.value;d("name",H)},p=P=>{const H=P.value;d("dataType",H)};let x;const C=()=>{f(l,P=>{let H=P.data[a()];if(H&&s().length>0){let I=H;for(let B=0;B<s().length;B++){const F=s()[B];B==s().length-1?I.splice(F,1):I=I[F].children}}return{[a()]:[...H]}}),x==null||x.hide()},$=()=>{f(l,P=>{let H=P.data[a()];if(H&&s().length>0){let I=H;for(let B=0;B<s().length;B++){const F=s()[B];B==s().length-1?I[F].children?I[F].children.push({id:Kr(),name:"newParam",dataType:"String"}):I[F].children=[{id:Kr(),name:"newParam",dataType:"String"}]:I=I[F].children}}return{[a()]:[...H]}})};var m=Ym(),_=xe(m),v=X(_);{var b=P=>{var H=tt(),I=xe(H);Yt(I,17,s,oi,(B,F)=>{Pe();var K=Ae(" ");L(B,K)}),L(P,H)};ke(v,P=>{s().length>1&&P(b)})}var N=z(v,2);const E=Ne(()=>h(c).nameDisabled===!0);St(N,{style:"width: 100%;",get value(){return h(c).name},placeholder:"请输入参数名称",oninput:g,get disabled(){return h(E)}}),Z(_);var T=z(_,2),D=X(T);const V=Ne(()=>h(c).dataType?[h(c).dataType]:[]),A=Ne(()=>h(c).dataTypeDisabled===!0);ln(D,{items:cd,style:"width: 100%",defaultValue:["String"],get value(){return h(V)},get disabled(){return h(A)},onSelect:p});var O=z(D,2);{var R=P=>{Ge(P,{class:"input-btn-more",style:"margin-left: auto",onclick:$,children:(H,I)=>{var B=zm();L(H,B)},$$slots:{default:!0}})};ke(O,P=>{(h(c).dataType==="Object"||h(c).dataType==="Array")&&h(c).addChildDisabled!==!0&&P(R)})}Z(T);var S=z(T,2),M=X(S);An(Do(M,{placement:"bottom",floating:H=>{var I=Bm(),B=X(I),F=z(X(B));Pt(F,{rows:1,style:"width: 100%;",onchange:fe=>{const me=fe.target.value;d("defaultValue",me)}}),Z(B);var K=z(B,2),se=z(X(K));Pt(se,{rows:3,style:"width: 100%;",onchange:fe=>{const me=fe.target.value;d("description",me)}}),Z(K);var ee=z(K,2);{var W=fe=>{var me=Rm(),Ce=X(me);Ge(Ce,{onclick:C,children:(he,ze)=>{Pe();var G=Ae("删除");L(he,G)},$$slots:{default:!0}}),Z(me),L(fe,me)};ke(ee,fe=>{h(c).deleteDisabled!==!0&&fe(W)})}Z(I),L(H,I)},children:(H,I)=>{qi(H,{})},$$slots:{floating:!0,default:!0}}),H=>x=H,()=>x),Z(S),L(e,m);var k=ce({get parameter(){return i()},set parameter(P){i(P),y()},get position(){return s()},set position(P){s(P),y()},get dataKeyName(){return a()},set dataKeyName(P){a(P),y()}});return r(),k}ie(wd,{parameter:{},position:{},dataKeyName:{}},[],[],!0);var Xm=ne("<!> <!>",1),Fm=ne('<div class="none-params svelte-1sm1mgi"> </div>'),Wm=ne('<div class="input-header svelte-1sm1mgi">参数名称</div> <div class="input-header svelte-1sm1mgi">参数类型</div> <div class="input-header svelte-1sm1mgi"></div>',1),Km=ne('<div class="input-container svelte-1sm1mgi"><!> <!></div>');const qm={hash:"svelte-1sm1mgi",code:`.input-container.svelte-1sm1mgi {display:grid;grid-template-columns:40% 50% 10%;row-gap:5px;column-gap:3px;}.input-container.svelte-1sm1mgi .none-params:where(.svelte-1sm1mgi) {font-size:12px;background:#f8f8f8;height:40px;display:flex;justify-content:center;align-items:center;border-radius:5px;width:calc(100% - 5px);grid-column:1 / -1; + /* 从第一列开始到最后一列结束 */}.input-container.svelte-1sm1mgi .input-header:where(.svelte-1sm1mgi) {font-size:12px;color:#666;}`};function Yn(e,t){ue(t,!0),et(e,qm);const[n,r]=nt(),o=()=>Q(h(u),"$node",n),i=(C,$=gt,m=gt)=>{var _=tt(),v=xe(_);Yt(v,19,$,b=>`${b.id}_${b.children?b.children.length:0}`,(b,N,E)=>{var T=Xm(),D=xe(T);const V=Ne(()=>[...m(),h(E)]);wd(D,{get parameter(){return h(N)},get position(){return h(V)},get dataKeyName(){return a()}});var A=z(D,2);{var O=R=>{var S=ve(()=>[...m(),h(E)]);i(R,()=>h(N).children,()=>h(S))};ke(A,R=>{h(N).children&&R(O)})}L(b,T)},b=>{var N=tt(),E=xe(N);{var T=D=>{var V=Fm(),A=X(V,!0);Z(V),Ee(()=>Bt(A,s())),L(D,V)};ke(E,D=>{m().length===0&&D(T)})}L(b,N)}),L(C,_)},s=w(t,"noneParameterText",7,"无输出参数"),a=w(t,"dataKeyName",7,"outputDefs");let l=pt(),u=Ne(()=>pr(l)),c=Ne(()=>{var C;return[...((C=o())==null?void 0:C.data[a()])||[]]});var f=Km(),d=X(f);{var g=C=>{var $=Wm();Pe(4),L(C,$)};ke(d,C=>{h(c).length!==0&&C(g)})}var p=z(d,2);i(p,()=>h(c)||[],()=>[]),Z(f),L(e,f);var x=ce({get noneParameterText(){return s()},set noneParameterText(C="无输出参数"){s(C),y()},get dataKeyName(){return a()},set dataKeyName(C="outputDefs"){a(C),y()}});return r(),x}ie(Yn,{noneParameterText:{},dataKeyName:{}},[],[],!0);var Gm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M20.7134 7.12811L20.4668 7.69379C20.2864 8.10792 19.7136 8.10792 19.5331 7.69379L19.2866 7.12811C18.8471 6.11947 18.0555 5.31641 17.0677 4.87708L16.308 4.53922C15.8973 4.35653 15.8973 3.75881 16.308 3.57612L17.0252 3.25714C18.0384 2.80651 18.8442 1.97373 19.2761 0.930828L19.5293 0.319534C19.7058 -0.106511 20.2942 -0.106511 20.4706 0.319534L20.7238 0.930828C21.1558 1.97373 21.9616 2.80651 22.9748 3.25714L23.6919 3.57612C24.1027 3.75881 24.1027 4.35653 23.6919 4.53922L22.9323 4.87708C21.9445 5.31641 21.1529 6.11947 20.7134 7.12811ZM9 2C13.0675 2 16.426 5.03562 16.9337 8.96494L19.1842 12.5037C19.3324 12.7367 19.3025 13.0847 18.9593 13.2317L17 14.071V17C17 18.1046 16.1046 19 15 19H13.001L13 22H4L4.00025 18.3061C4.00033 17.1252 3.56351 16.0087 2.7555 15.0011C1.65707 13.6313 1 11.8924 1 10C1 5.58172 4.58172 2 9 2ZM9 4C5.68629 4 3 6.68629 3 10C3 11.3849 3.46818 12.6929 4.31578 13.7499C5.40965 15.114 6.00036 16.6672 6.00025 18.3063L6.00013 20H11.0007L11.0017 17H15V12.7519L16.5497 12.0881L15.0072 9.66262L14.9501 9.22118C14.5665 6.25141 12.0243 4 9 4ZM19.4893 16.9929L21.1535 18.1024C22.32 16.3562 23 14.2576 23 12.0001C23 11.317 22.9378 10.6486 22.8186 10L20.8756 10.5C20.9574 10.9878 21 11.489 21 12.0001C21 13.8471 20.4436 15.5642 19.4893 16.9929Z"></path></svg>'),Um=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),jm=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),Jm=ne('<div class="heading svelte-wn2kra"><!> <!></div> <!> <!> <div class="setting-title svelte-wn2kra">模型</div> <div class="setting-item svelte-wn2kra"><!> <!></div> <div class="setting-title svelte-wn2kra">采样参数</div> <div class="setting-item svelte-wn2kra"><div class="slider-container svelte-wn2kra"><label class="svelte-wn2kra"> </label> <input type="range" min="0" max="1" step="0.1" class="svelte-wn2kra"></div></div> <div class="setting-item svelte-wn2kra"><div class="slider-container svelte-wn2kra"><label class="svelte-wn2kra"> </label> <input type="range" min="0" max="1" step="0.1" class="svelte-wn2kra"></div></div> <div class="setting-item svelte-wn2kra"><div class="slider-container svelte-wn2kra"><label class="svelte-wn2kra"> </label> <input type="range" min="0" max="100" step="1" class="svelte-wn2kra"></div></div> <div class="setting-title svelte-wn2kra">系统提示词</div> <div class="setting-item svelte-wn2kra"><!></div> <div class="setting-title svelte-wn2kra">用户提示词</div> <div class="setting-item svelte-wn2kra"><!></div> <div class="heading svelte-wn2kra"><!> <!></div> <!>',1);const Qm={hash:"svelte-wn2kra",code:`.heading.svelte-wn2kra {display:flex;margin-bottom:10px;}.setting-title.svelte-wn2kra {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-wn2kra {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}\r + /* 新增样式 */.slider-container.svelte-wn2kra {width:100%;display:flex;flex-direction:column;gap:4px;}.slider-container.svelte-wn2kra label:where(.svelte-wn2kra) {font-size:12px;color:#666;display:flex;justify-content:space-between;align-items:center;}input[type="range"].svelte-wn2kra {width:100%;height:4px;background:#ddd;border-radius:2px;outline:none;-webkit-appearance:none;}input[type="range"].svelte-wn2kra::-webkit-slider-thumb {-webkit-appearance:none;width:14px;height:14px;background:#007bff;border-radius:50%;cursor:pointer;}`};function _d(e,t){ue(t,!0),et(e,Qm);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),s=Ao();let a=Fn(Ht([]));rn(async()=>{var c,f;const u=await((f=(c=s.provider)==null?void 0:c.llm)==null?void 0:f.call(c));h(a).push(...u||[])});const{updateNodeData:l}=Lt();return dn(e,ft({get data(){return n()}},()=>r,{icon:c=>{var f=Gm();L(c,f)},children:(c,f)=>{var d=Jm(),g=xe(d),p=X(g);je(p,{level:3,children:(G,ae)=>{Pe();var Me=Ae("输入参数");L(G,Me)},$$slots:{default:!0}});var x=z(p,2);Ge(x,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(G,ae)=>{var Me=Um();L(G,Me)},$$slots:{default:!0}}),Z(g);var C=z(g,2);Ft(C,{});var $=z(C,2);je($,{level:3,mt:"10px",children:(G,ae)=>{Pe();var Me=Ae("模型设置");L(G,Me)},$$slots:{default:!0}});var m=z($,4),_=X(m);const v=Ne(()=>n().llmId?[n().llmId]:[]);ln(_,{get items(){return h(a)},style:"width: 100%",placeholder:"请选择模型",onSelect:G=>{const ae=G.value;l(o,()=>({llmId:ae}))},get value(){return h(v)}});var b=z(_,2);qi(b,{}),Z(m);var N=z(m,4),E=X(N),T=X(E),D=X(T);Z(T);var V=z(T,2);ao(V),Z(E),Z(N);var A=z(N,2),O=X(A),R=X(O),S=X(R);Z(R);var M=z(R,2);ao(M),Z(O),Z(A);var k=z(A,2),P=X(k),H=X(P),I=X(H);Z(H);var B=z(H,2);ao(B),Z(P),Z(k);var F=z(k,4),K=X(F);const se=Ne(()=>n().systemPrompt||"");Pt(K,{rows:5,placeholder:"请输入系统提示词",style:"width: 100%",get value(){return h(se)},oninput:G=>{l(o,{systemPrompt:G.target.value})}}),Z(F);var ee=z(F,4),W=X(ee);const fe=Ne(()=>n().userPrompt||"");Pt(W,{rows:5,placeholder:"请输入用户提示词",style:"width: 100%",get value(){return h(fe)},oninput:G=>{l(o,{userPrompt:G.target.value})}}),Z(ee);var me=z(ee,2),Ce=X(me);je(Ce,{level:3,mt:"10px",children:(G,ae)=>{Pe();var Me=Ae("输出参数");L(G,Me)},$$slots:{default:!0}});var he=z(Ce,2);Ge(he,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o,"outputDefs")},children:(G,ae)=>{var Me=jm();L(G,Me)},$$slots:{default:!0}}),Z(me);var ze=z(me,2);Yn(ze,{}),Ee(()=>{Bt(D,`Temperature: ${n().temperature??.5}`),bs(V,n().temperature??.5),Bt(S,`Top P: ${n().topP??.9}`),bs(M,n().topP??.9),Bt(I,`Top K: ${n().topK??50}`),bs(B,n().topK??50)}),Ze("mousedown",V,ks(function(G){De.call(this,t,G)})),Ze("input",V,G=>l(o,{temperature:parseFloat(G.target.value)})),Ze("mousedown",M,ks(function(G){De.call(this,t,G)})),Ze("input",M,G=>l(o,{topP:parseFloat(G.target.value)})),Ze("mousedown",B,ks(function(G){De.call(this,t,G)})),Ze("input",B,G=>l(o,{topK:parseInt(G.target.value)})),L(c,d)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(u){n(u),y()}})}ie(_d,{data:{}},[],[],!0);var ey=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M23 12L15.9289 19.0711L14.5147 17.6569L20.1716 12L14.5147 6.34317L15.9289 4.92896L23 12ZM3.82843 12L9.48528 17.6569L8.07107 19.0711L1 12L8.07107 4.92896L9.48528 6.34317L3.82843 12Z"></path></svg>'),ty=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),ny=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),ry=ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">执行引擎</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">执行代码</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!> <!></div> <!>',1);const oy={hash:"svelte-15t2v24",code:".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}"};function xd(e,t){ue(t,!0),et(e,oy);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),{updateNodeData:s}=Lt(),a=[{label:"QLExpress",value:"qlexpress"},{label:"Groovy",value:"groovy"},{label:"JavaScript",value:"js"}];return dn(e,ft({get data(){return n()}},()=>r,{icon:u=>{var c=ey();L(u,c)},children:(u,c)=>{var f=ry(),d=xe(f),g=X(d);je(g,{level:3,children:(A,O)=>{Pe();var R=Ae("输入参数");L(A,R)},$$slots:{default:!0}});var p=z(g,2);Ge(p,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(A,O)=>{var R=ty();L(A,R)},$$slots:{default:!0}}),Z(d);var x=z(d,2);Ft(x,{});var C=z(x,2);je(C,{level:3,mt:"10px",children:(A,O)=>{Pe();var R=Ae("代码");L(A,R)},$$slots:{default:!0}});var $=z(C,4),m=X($);const _=Ne(()=>n().engine?[n().engine]:["qlexpress"]);ln(m,{items:a,style:"width: 100%",placeholder:"请选择执行引擎",onSelect:A=>{const O=A.value;s(o,()=>({engine:O}))},get value(){return h(_)}}),Z($);var v=z($,4),b=X(v);const N=Ne(()=>n().code||"");Pt(b,{rows:10,placeholder:"请输入执行代码,注:输出内容需添加到_result中,如:_result.put(key, value)",style:"width: 100%",onchange:A=>{s(o,()=>({code:A.target.value}))},get value(){return h(N)}}),Z(v);var E=z(v,2),T=X(E);je(T,{level:3,mt:"10px",children:(A,O)=>{Pe();var R=Ae("输出参数");L(A,R)},$$slots:{default:!0}});var D=z(T,2);Ge(D,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o,"outputDefs")},children:(A,O)=>{var R=ny();L(A,R)},$$slots:{default:!0}}),Z(E);var V=z(E,2);Yn(V,{}),L(u,f)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(l){n(l),y()}})}ie(xd,{data:{}},[],[],!0);var iy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 4C2 3.44772 2.44772 3 3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4ZM4 5V19H20V5H4ZM7 8H17V11H15V10H13V14H14.5V16H9.5V14H11V10H9V11H7V8Z"></path></svg>'),sy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),ay=ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">执行代码</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!></div> <!>',1);const ly={hash:"svelte-15t2v24",code:".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}"};function bd(e,t){ue(t,!0),et(e,ly);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),{updateNodeData:s}=Lt();return kr(()=>{(!n().outputDefs||n().outputDefs.length===0)&&i(o,"outputDefs",{name:"output",dataType:"String",dataTypeDisabled:!0,deleteDisabled:!0})}),dn(e,ft({get data(){return n()}},()=>r,{icon:l=>{var u=iy();L(l,u)},children:(l,u)=>{var c=ay(),f=xe(c),d=X(f);je(d,{level:3,children:(N,E)=>{Pe();var T=Ae("输入参数");L(N,T)},$$slots:{default:!0}});var g=z(d,2);Ge(g,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(N,E)=>{var T=sy();L(N,T)},$$slots:{default:!0}}),Z(f);var p=z(f,2);Ft(p,{});var x=z(p,2);je(x,{level:3,mt:"10px",children:(N,E)=>{Pe();var T=Ae("代码");L(N,T)},$$slots:{default:!0}});var C=z(x,4),$=X(C);const m=Ne(()=>n().template||"");Pt($,{rows:10,placeholder:"请输入执行代码",style:"width: 100%",onchange:N=>{s(o,()=>({template:N.target.value}))},get value(){return h(m)}}),Z(C);var _=z(C,2),v=X(_);je(v,{level:3,mt:"10px",children:(N,E)=>{Pe();var T=Ae("输出参数");L(N,T)},$$slots:{default:!0}}),Z(_);var b=z(_,2);Yn(b,{}),L(l,c)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(a){n(a),y()}})}ie(bd,{data:{}},[],[],!0);var uy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.23509 6.45329C4.85101 7.89148 4 9.84636 4 12C4 16.4183 7.58172 20 12 20C13.0808 20 14.1116 19.7857 15.0521 19.3972C15.1671 18.6467 14.9148 17.9266 14.8116 17.6746C14.582 17.115 13.8241 16.1582 12.5589 14.8308C12.2212 14.4758 12.2429 14.2035 12.3636 13.3943L12.3775 13.3029C12.4595 12.7486 12.5971 12.4209 14.4622 12.1248C15.4097 11.9746 15.6589 12.3533 16.0043 12.8777C16.0425 12.9358 16.0807 12.9928 16.1198 13.0499C16.4479 13.5297 16.691 13.6394 17.0582 13.8064C17.2227 13.881 17.428 13.9751 17.7031 14.1314C18.3551 14.504 18.3551 14.9247 18.3551 15.8472V15.9518C18.3551 16.3434 18.3168 16.6872 18.2566 16.9859C19.3478 15.6185 20 13.8854 20 12C20 8.70089 18.003 5.8682 15.1519 4.64482C14.5987 5.01813 13.8398 5.54726 13.575 5.91C13.4396 6.09538 13.2482 7.04166 12.6257 7.11976C12.4626 7.14023 12.2438 7.12589 12.012 7.11097C11.3905 7.07058 10.5402 7.01606 10.268 7.75495C10.0952 8.2232 10.0648 9.49445 10.6239 10.1543C10.7134 10.2597 10.7307 10.4547 10.6699 10.6735C10.59 10.9608 10.4286 11.1356 10.3783 11.1717C10.2819 11.1163 10.0896 10.8931 9.95938 10.7412C9.64554 10.3765 9.25405 9.92233 8.74797 9.78176C8.56395 9.73083 8.36166 9.68867 8.16548 9.64736C7.6164 9.53227 6.99443 9.40134 6.84992 9.09302C6.74442 8.8672 6.74488 8.55621 6.74529 8.22764C6.74529 7.8112 6.74529 7.34029 6.54129 6.88256C6.46246 6.70541 6.35689 6.56446 6.23509 6.45329ZM12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22Z"></path></svg>'),cy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),dy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),fy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),gy=ne('<div class="heading svelte-1vtcqdz" style="padding-top: 10px"><!> <!></div> <!>',1),hy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),vy=ne('<div class="heading svelte-1vtcqdz" style="padding-top: 10px"><!> <!></div> <!>',1),py=ne('<div style="width: 100%"><!></div>'),my=ne('<div style="width: 100%"><!></div>'),yy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),wy=ne('<div style="display: flex;gap: 2px;width: 100%;padding: 10px 0"><div><!></div> <div style="width: 100%"><!></div></div> <div class="heading svelte-1vtcqdz"><!> <!></div> <!> <div class="heading svelte-1vtcqdz" style="padding-top: 10px"><!> <!></div> <!> <!> <div class="radio-group svelte-1vtcqdz"><label class="svelte-1vtcqdz"><!>none</label> <label class="svelte-1vtcqdz"><!>form-data</label> <label class="svelte-1vtcqdz"><!>x-www-form-urlencoded</label> <label class="svelte-1vtcqdz"><!>json</label> <label class="svelte-1vtcqdz"><!>raw</label></div> <!> <!> <!> <!> <div class="heading svelte-1vtcqdz"><!> <!></div> <!>',1);const _y={hash:"svelte-1vtcqdz",code:".heading.svelte-1vtcqdz {display:flex;margin-bottom:10px;}.radio-group.svelte-1vtcqdz {display:flex;margin:10px 0;}.radio-group.svelte-1vtcqdz label:where(.svelte-1vtcqdz) {display:flex;font-size:14px;}"};function Cd(e,t){ue(t,!0),et(e,_y);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=[{value:"get",label:"GET"},{value:"post",label:"POST"},{value:"put",label:"PUT"},{value:"delete",label:"DELETE"},{value:"head",label:"HEAD"},{value:"patch",label:"PATCH"}],i=pt(),{addParameter:s}=kn(),{updateNodeData:a}=Lt();return dn(e,ft({get data(){return n()}},()=>r,{icon:u=>{var c=uy();L(u,c)},children:(u,c)=>{var f=wy(),d=xe(f),g=X(d),p=X(g);const x=Ne(()=>n().method?[n().method]:["get"]);ln(p,{items:o,style:"width: 100%",placeholder:"请选择请求方式",onSelect:oe=>{const pe=oe.value;a(i,()=>({method:pe}))},get value(){return h(x)}}),Z(g);var C=z(g,2),$=X(C);const m=Ne(()=>n().url||"");St($,{placeholder:"请输入url",style:"width: 100%",onchange:oe=>{a(i,()=>({url:oe.target.value}))},get value(){return h(m)}}),Z(C),Z(d);var _=z(d,2),v=X(_);je(v,{level:3,children:(oe,pe)=>{Pe();var be=Ae("Http 头信息");L(oe,be)},$$slots:{default:!0}});var b=z(v,2);Ge(b,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{s(i,"headers")},children:(oe,pe)=>{var be=cy();L(oe,be)},$$slots:{default:!0}}),Z(_);var N=z(_,2);Ft(N,{dataKeyName:"headers"});var E=z(N,2),T=X(E);je(T,{level:3,children:(oe,pe)=>{Pe();var be=Ae("参数");L(oe,be)},$$slots:{default:!0}});var D=z(T,2);Ge(D,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{s(i,"urlParameters")},children:(oe,pe)=>{var be=dy();L(oe,be)},$$slots:{default:!0}}),Z(E);var V=z(E,2);Ft(V,{dataKeyName:"urlParameters"});var A=z(V,2);je(A,{level:3,mt:"10px",children:(oe,pe)=>{Pe();var be=Ae("Body");L(oe,be)},$$slots:{default:!0}});var O=z(A,2),R=X(O),S=X(R);const M=Ne(()=>!n().bodyType);St(S,{type:"radio",name:"bodyType",value:"",get checked(){return h(M)},onchange:oe=>{var pe;(pe=oe.target)!=null&&pe.checked&&a(i,{bodyType:""})}}),Pe(),Z(R);var k=z(R,2),P=X(k);const H=Ne(()=>n().bodyType==="form-data");St(P,{type:"radio",name:"bodyType",value:"form-data",get checked(){return h(H)},onchange:oe=>{var pe;(pe=oe.target)!=null&&pe.checked&&a(i,{bodyType:"form-data"})}}),Pe(),Z(k);var I=z(k,2),B=X(I);const F=Ne(()=>n().bodyType==="x-www-form-urlencoded");St(B,{type:"radio",name:"bodyType",value:"x-www-form-urlencoded",get checked(){return h(F)},onchange:oe=>{var pe;(pe=oe.target)!=null&&pe.checked&&a(i,{bodyType:"x-www-form-urlencoded"})}}),Pe(),Z(I);var K=z(I,2),se=X(K);const ee=Ne(()=>n().bodyType==="json");St(se,{type:"radio",name:"bodyType",value:"json",get checked(){return h(ee)},onchange:oe=>{var pe;(pe=oe.target)!=null&&pe.checked&&a(i,{bodyType:"json"})}}),Pe(),Z(K);var W=z(K,2),fe=X(W);const me=Ne(()=>n().bodyType==="raw");St(fe,{type:"radio",name:"bodyType",value:"raw",get checked(){return h(me)},onchange:oe=>{var pe;(pe=oe.target)!=null&&pe.checked&&a(i,{bodyType:"raw"})}}),Pe(),Z(W),Z(O);var Ce=z(O,2);{var he=oe=>{var pe=gy(),be=xe(pe),Ie=X(be);je(Ie,{level:3,children:(J,Re)=>{Pe();var le=Ae("参数");L(J,le)},$$slots:{default:!0}});var ht=z(Ie,2);Ge(ht,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{s(i,"fromData")},children:(J,Re)=>{var le=fy();L(J,le)},$$slots:{default:!0}}),Z(be);var dt=z(be,2);Ft(dt,{dataKeyName:"fromData"}),L(oe,pe)};ke(Ce,oe=>{n().bodyType==="form-data"&&oe(he)})}var ze=z(Ce,2);{var G=oe=>{var pe=vy(),be=xe(pe),Ie=X(be);je(Ie,{level:3,children:(J,Re)=>{Pe();var le=Ae("参数");L(J,le)},$$slots:{default:!0}});var ht=z(Ie,2);Ge(ht,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{s(i,"fromUrlencoded")},children:(J,Re)=>{var le=hy();L(J,le)},$$slots:{default:!0}}),Z(be);var dt=z(be,2);Ft(dt,{dataKeyName:"fromUrlencoded"}),L(oe,pe)};ke(ze,oe=>{n().bodyType==="x-www-form-urlencoded"&&oe(G)})}var ae=z(ze,2);{var Me=oe=>{var pe=py(),be=X(pe);Pt(be,{rows:"5",style:"width: 100%",placeholder:"请输入 json 信息",get value(){return n().bodyJson},oninput:Ie=>{a(i,{bodyJson:Ie.target.value})}}),Z(pe),L(oe,pe)};ke(ae,oe=>{n().bodyType==="json"&&oe(Me)})}var Le=z(ae,2);{var Xe=oe=>{var pe=my(),be=X(pe);Pt(be,{rows:"5",style:"width: 100%",placeholder:"请输入请求信息",get value(){return n().bodyRaw},oninput:Ie=>{a(i,{bodyRaw:Ie.target.value})}}),Z(pe),L(oe,pe)};ke(Le,oe=>{n().bodyType==="raw"&&oe(Xe)})}var te=z(Le,2),Fe=X(te);je(Fe,{level:3,mt:"10px",children:(oe,pe)=>{Pe();var be=Ae("输出参数");L(oe,be)},$$slots:{default:!0}});var Oe=z(Fe,2);Ge(Oe,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{s(i,"outputDefs")},children:(oe,pe)=>{var be=yy();L(oe,be)},$$slots:{default:!0}}),Z(te);var rt=z(te,2);Yn(rt,{}),L(u,f)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(l){n(l),y()}})}ie(Cd,{data:{}},[],[],!0);var xy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 5C13.567 5 12 6.567 12 8.5C12 10.433 13.567 12 15.5 12C17.433 12 19 10.433 19 8.5C19 6.567 17.433 5 15.5 5ZM10 8.5C10 5.46243 12.4624 3 15.5 3C18.5376 3 21 5.46243 21 8.5C21 9.6575 20.6424 10.7315 20.0317 11.6175L22.7071 14.2929L21.2929 15.7071L18.6175 13.0317C17.7315 13.6424 16.6575 14 15.5 14C12.4624 14 10 11.5376 10 8.5ZM3 4H8V6H3V4ZM3 11H8V13H3V11ZM21 18V20H3V18H21Z"></path></svg>'),by=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),Cy=ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">知识库</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">获取数据量</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!></div> <!>',1);const ky={hash:"svelte-15t2v24",code:".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}"};function kd(e,t){ue(t,!0),et(e,ky);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),s=Ao();let a=Fn(Ht([]));rn(async()=>{var c,f;const u=await((f=(c=s.provider)==null?void 0:c.knowledge)==null?void 0:f.call(c));h(a).push(...u||[])});const{updateNodeData:l}=Lt();return kr(()=>{(!n().outputDefs||n().outputDefs.length===0)&&i(o,"outputDefs",{name:"documents",dataType:"Array",nameDisabled:!0,dataTypeDisabled:!0,addChildDisabled:!0,children:[{name:"title",dataType:"String",nameDisabled:!0,dataTypeDisabled:!0},{name:"content",dataType:"String",nameDisabled:!0,dataTypeDisabled:!0},{name:"documentId",dataType:"Number",nameDisabled:!0,dataTypeDisabled:!0},{name:"knowledgeId",dataType:"Number",nameDisabled:!0,dataTypeDisabled:!0}]})}),dn(e,ft({get data(){return n()}},()=>r,{icon:c=>{var f=xy();L(c,f)},children:(c,f)=>{var d=Cy(),g=xe(d),p=X(g);je(p,{level:3,children:(V,A)=>{Pe();var O=Ae("输入参数");L(V,O)},$$slots:{default:!0}});var x=z(p,2);Ge(x,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(V,A)=>{var O=by();L(V,O)},$$slots:{default:!0}}),Z(g);var C=z(g,2);Ft(C,{});var $=z(C,2);je($,{level:3,mt:"10px",children:(V,A)=>{Pe();var O=Ae("知识库设置");L(V,O)},$$slots:{default:!0}});var m=z($,4),_=X(m);const v=Ne(()=>n().knowledgeId?[n().knowledgeId]:[]);ln(_,{get items(){return h(a)},style:"width: 100%",placeholder:"请选择知识库",onSelect:V=>{const A=V.value;l(o,()=>({knowledgeId:A}))},get value(){return h(v)}}),Z(m);var b=z(m,4),N=X(b);St(N,{placeholder:"搜索的数据条数",style:"width: 100%"}),Z(b);var E=z(b,2),T=X(E);je(T,{level:3,mt:"10px",children:(V,A)=>{Pe();var O=Ae("输出参数");L(V,O)},$$slots:{default:!0}}),Z(E);var D=z(E,2);Yn(D,{}),L(c,d)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(u){n(u),y()}})}ie(kd,{data:{}},[],[],!0);var $y=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"></path></svg>'),Ey=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),Sy=ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">API 服务商</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">API Key</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">关键字</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">数据量</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="setting-title svelte-15t2v24">其他参数</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!></div> <!>',1);const Py={hash:"svelte-15t2v24",code:".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}"};function $d(e,t){ue(t,!0),et(e,Py);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),s=Ao();let a=Fn(Ht([]));rn(async()=>{var c;const u=await((c=s.provider)==null?void 0:c.knowledge());h(a).push(...u||[])});const{updateNodeData:l}=Lt();return kr(()=>{(!n().outputDefs||n().outputDefs.length===0)&&i(o,"outputDefs",{name:"documents",dataType:"Array",nameDisabled:!0,dataTypeDisabled:!0,addChildDisabled:!0,children:[{name:"title",dataType:"String",nameDisabled:!0,dataTypeDisabled:!0},{name:"content",dataType:"String",nameDisabled:!0,dataTypeDisabled:!0},{name:"documentId",dataType:"Number",nameDisabled:!0,dataTypeDisabled:!0},{name:"knowledgeId",dataType:"Number",nameDisabled:!0,dataTypeDisabled:!0}]})}),dn(e,ft({get data(){return n()}},()=>r,{icon:c=>{var f=$y();L(c,f)},children:(c,f)=>{var d=Sy(),g=xe(d),p=X(g);je(p,{level:3,children:(k,P)=>{Pe();var H=Ae("输入参数");L(k,H)},$$slots:{default:!0}});var x=z(p,2);Ge(x,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(k,P)=>{var H=Ey();L(k,H)},$$slots:{default:!0}}),Z(g);var C=z(g,2);Ft(C,{});var $=z(C,2);je($,{level:3,mt:"10px",children:(k,P)=>{Pe();var H=Ae("搜索引擎设置");L(k,H)},$$slots:{default:!0}});var m=z($,4),_=X(m);const v=Ne(()=>n().knowledgeId?[n().knowledgeId]:[]);ln(_,{get items(){return h(a)},style:"width: 100%",placeholder:"请选择 API 服务商",onSelect:k=>{const P=k.value;l(o,()=>({knowledgeId:P}))},get value(){return h(v)}}),Z(m);var b=z(m,4),N=X(b);St(N,{placeholder:"请输入 API Key",style:"width: 100%"}),Z(b);var E=z(b,4),T=X(E);St(T,{placeholder:"请输入关键字",style:"width: 100%"}),Z(E);var D=z(E,4),V=X(D);St(V,{placeholder:"搜索的数据条数",style:"width: 100%"}),Z(D);var A=z(D,4),O=X(A);Pt(O,{rows:3,placeholder:"请输入其他参数(Property 格式)",style:"width: 100%"}),Z(A);var R=z(A,2),S=X(R);je(S,{level:3,mt:"10px",children:(k,P)=>{Pe();var H=Ae("输出参数");L(k,H)},$$slots:{default:!0}}),Z(R);var M=z(R,2);Yn(M,{}),L(c,d)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(u){n(u),y()}})}ie($d,{data:{}},[],[],!0);var Ny=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5.46257 4.43262C7.21556 2.91688 9.5007 2 12 2C17.5228 2 22 6.47715 22 12C22 14.1361 21.3302 16.1158 20.1892 17.7406L17 12H20C20 7.58172 16.4183 4 12 4C9.84982 4 7.89777 4.84827 6.46023 6.22842L5.46257 4.43262ZM18.5374 19.5674C16.7844 21.0831 14.4993 22 12 22C6.47715 22 2 17.5228 2 12C2 9.86386 2.66979 7.88416 3.8108 6.25944L7 12H4C4 16.4183 7.58172 20 12 20C14.1502 20 16.1022 19.1517 17.5398 17.7716L18.5374 19.5674Z"></path></svg>'),Ty=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),My=ne('<div class="heading svelte-md8tgj"><!> <!></div> <!> <div class="heading svelte-md8tgj"><!></div> <!>',1);const Hy={hash:"svelte-md8tgj",code:".heading.svelte-md8tgj {display:flex;margin-bottom:10px;}.loop_handle_wrapper ::after {content:'循环体';width:100px;height:20px;background:#000;color:#fff;display:flex;justify-content:center;align-items:center;}"};function Ed(e,t){ue(t,!0),et(e,Hy);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),s=Ao();let a=Fn(Ht([]));return rn(async()=>{var u;const l=await((u=s.provider)==null?void 0:u.knowledge());h(a).push(...l||[])}),dn(e,ft({get data(){return n()}},()=>r,{icon:c=>{var f=Ny();L(c,f)},handle:c=>{er(c,{type:"source",get position(){return $e.Bottom},id:"loop_handle",style:"bottom: -12px;width: 100px",class:"loop_handle_wrapper"})},children:(c,f)=>{var d=My(),g=xe(d),p=X(g);je(p,{level:3,children:(v,b)=>{Pe();var N=Ae("循环变量");L(v,N)},$$slots:{default:!0}});var x=z(p,2);Ge(x,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(v,b)=>{var N=Ty();L(v,N)},$$slots:{default:!0}}),Z(g);var C=z(g,2);Ft(C,{});var $=z(C,2),m=X($);je(m,{level:3,mt:"10px",children:(v,b)=>{Pe();var N=Ae("输出参数");L(v,N)},$$slots:{default:!0}}),Z($);var _=z($,2);Yn(_,{}),L(c,d)},$$slots:{icon:!0,handle:!0,default:!0}})),ce({get data(){return n()},set data(l){n(l),y()}})}ie(Ed,{data:{}},[],[],!0);var Vy=_e('<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor" p-id="2577" width="200" height="200"><path d="M312.096 408.576l67.84 67.84 45.312-45.216a32 32 0 0 1 45.248 45.248l-45.28 45.248 90.496 90.496 45.28-45.216a32 32 0 0 1 45.248 45.248l-45.248 45.248 67.904 67.872-90.528 90.528a224.064 224.064 0 0 1-292.544 21.024L176.32 906.368a32 32 0 0 1-45.248-45.248l69.504-69.472a224.064 224.064 0 0 1 21.024-292.576l90.496-90.496z m0 90.496L266.848 544.32a160 160 0 0 0-4.8 221.28l4.8 4.992a160 160 0 0 0 221.248 4.8l5.024-4.8 45.248-45.248-226.272-226.24z m610.272-384a32 32 0 0 1 0 45.248l-69.44 69.504a224.064 224.064 0 0 1-21.056 292.544l-90.528 90.528-316.8-316.8 90.56-90.496a224.064 224.064 0 0 1 292.544-21.024l69.44-69.504a32 32 0 0 1 45.28 0zM565.344 246.08l-5.024 4.8-45.248 45.248 226.272 226.272 45.248-45.248a160 160 0 0 0 4.8-221.28l-4.8-4.992a160 160 0 0 0-221.248-4.8z" p-id="2578"></path></svg>'),Dy=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),Ay=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M11 11V5H13V11H19V13H13V19H11V13H5V11H11Z"></path></svg>'),Ly=ne('<div class="heading svelte-15t2v24"><!> <!></div> <!> <!> <div class="setting-title svelte-15t2v24">选择内部接口</div> <div class="setting-item svelte-15t2v24"><!></div> <div class="heading svelte-15t2v24"><!> <!></div> <!>',1);const Oy={hash:"svelte-15t2v24",code:".heading.svelte-15t2v24 {display:flex;margin-bottom:10px;}.setting-title.svelte-15t2v24 {font-size:12px;color:#999;margin-bottom:4px;margin-top:10px;}.setting-item.svelte-15t2v24 {display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;gap:10px;}"};function Sd(e,t){ue(t,!0),et(e,Oy);const n=w(t,"data",7),r=xt(t,["$$slots","$$events","$$legacy","$$host","data"]),o=pt(),{addParameter:i}=kn(),{updateNodeData:s}=Lt(),a=Ao();let l=Fn(Ht([]));return rn(async()=>{var c,f;const u=await((f=(c=a.provider)==null?void 0:c.internal)==null?void 0:f.call(c));h(l).push(...u||[])}),dn(e,ft({get data(){return n()}},()=>r,{icon:c=>{var f=Vy();L(c,f)},children:(c,f)=>{var d=Ly(),g=xe(d),p=X(g);je(p,{level:3,children:(D,V)=>{Pe();var A=Ae("输入参数");L(D,A)},$$slots:{default:!0}});var x=z(p,2);Ge(x,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o)},children:(D,V)=>{var A=Dy();L(D,A)},$$slots:{default:!0}}),Z(g);var C=z(g,2);Ft(C,{});var $=z(C,2);je($,{level:3,mt:"10px",children:(D,V)=>{Pe();var A=Ae("接口");L(D,A)},$$slots:{default:!0}});var m=z($,4),_=X(m);const v=Ne(()=>n().method?[n().method]:[""]);ln(_,{get items(){return h(l)},style:"width: 100%",placeholder:"请选择内部接口",onSelect:D=>{const V=D.value;s(o,()=>({method:V}))},get value(){return h(v)}}),Z(m);var b=z(m,2),N=X(b);je(N,{level:3,mt:"10px",children:(D,V)=>{Pe();var A=Ae("输出参数");L(D,A)},$$slots:{default:!0}});var E=z(N,2);Ge(E,{class:"input-btn-more",style:"margin-left: auto",onclick:()=>{i(o,"outputDefs")},children:(D,V)=>{var A=Ay();L(D,A)},$$slots:{default:!0}}),Z(b);var T=z(b,2);Yn(T,{}),L(c,d)},$$slots:{icon:!0,default:!0}})),ce({get data(){return n()},set data(u){n(u),y()}})}ie(Sd,{data:{}},[],[],!0);const Iy={startNode:hd,codeNode:xd,llmNode:_d,templateNode:bd,httpNode:Cd,knowledgeNode:kd,searchEngineNode:$d,loopNode:Ed,internalNode:Sd,endNode:yd};var zy=ne("<!> ",1);function Pd(e,t){ue(t,!0);const n=w(t,"icon",7),r=w(t,"title",7),o=w(t,"type",7),i=w(t,"description",7),s=w(t,"extra",7);return Ge(e,{draggable:!0,ondragstart:l=>{if(!l.dataTransfer)return null;const u={type:o(),data:{title:r(),description:i(),systemPrompt:"",userPrompt:"",...s()}};l.dataTransfer.setData("application/tinyflow",JSON.stringify(u)),l.dataTransfer.effectAllowed="move"},children:(l,u)=>{var c=zy(),f=xe(c);dl(f,n);var d=z(f);Ee(()=>Bt(d,` ${r()??""}`)),L(l,c)},$$slots:{default:!0}}),ce({get icon(){return n()},set icon(l){n(l),y()},get title(){return r()},set title(l){r(l),y()},get type(){return o()},set type(l){o(l),y()},get description(){return i()},set description(l){i(l),y()},get extra(){return s()},set extra(l){s(l),y()}})}ie(Pd,{icon:{},title:{},type:{},description:{},extra:{}},[],[],!0);var Ry=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M4.83582 12L11.0429 18.2071L12.4571 16.7929L7.66424 12L12.4571 7.20712L11.0429 5.79291L4.83582 12ZM10.4857 12L16.6928 18.2071L18.107 16.7929L13.3141 12L18.107 7.20712L16.6928 5.79291L10.4857 12Z"></path></svg>'),By=_e('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M19.1642 12L12.9571 5.79291L11.5429 7.20712L16.3358 12L11.5429 16.7929L12.9571 18.2071L19.1642 12ZM13.5143 12L7.30722 5.79291L5.89301 7.20712L10.6859 12L5.89301 16.7929L7.30722 18.2071L13.5143 12Z"></path></svg>'),Yy=ne('<div><div class="tf-toolbar-container "><div class="tf-toolbar-container-header"><!></div> <div class="tf-toolbar-container-body"><div class="tf-toolbar-container-base"></div> <div class="tf-toolbar-container-tools"><!></div></div></div> <!></div>');function Nd(e){let t=Fn("base"),n=Fn("show");const r=[{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM12 15C10.3431 15 9 13.6569 9 12C9 10.3431 10.3431 9 12 9C13.6569 9 15 10.3431 15 12C15 13.6569 13.6569 15 12 15Z"></path></svg>',title:"开始节点",type:"startNode",description:"开始定义输入参数"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M5.46257 4.43262C7.21556 2.91688 9.5007 2 12 2C17.5228 2 22 6.47715 22 12C22 14.1361 21.3302 16.1158 20.1892 17.7406L17 12H20C20 7.58172 16.4183 4 12 4C9.84982 4 7.89777 4.84827 6.46023 6.22842L5.46257 4.43262ZM18.5374 19.5674C16.7844 21.0831 14.4993 22 12 22C6.47715 22 2 17.5228 2 12C2 9.86386 2.66979 7.88416 3.8108 6.25944L7 12H4C4 16.4183 7.58172 20 12 20C14.1502 20 16.1022 19.1517 17.5398 17.7716L18.5374 19.5674Z"></path></svg>',title:"循环",type:"loopNode",description:"用于循环执行任务"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M20.7134 7.12811L20.4668 7.69379C20.2864 8.10792 19.7136 8.10792 19.5331 7.69379L19.2866 7.12811C18.8471 6.11947 18.0555 5.31641 17.0677 4.87708L16.308 4.53922C15.8973 4.35653 15.8973 3.75881 16.308 3.57612L17.0252 3.25714C18.0384 2.80651 18.8442 1.97373 19.2761 0.930828L19.5293 0.319534C19.7058 -0.106511 20.2942 -0.106511 20.4706 0.319534L20.7238 0.930828C21.1558 1.97373 21.9616 2.80651 22.9748 3.25714L23.6919 3.57612C24.1027 3.75881 24.1027 4.35653 23.6919 4.53922L22.9323 4.87708C21.9445 5.31641 21.1529 6.11947 20.7134 7.12811ZM9 2C13.0675 2 16.426 5.03562 16.9337 8.96494L19.1842 12.5037C19.3324 12.7367 19.3025 13.0847 18.9593 13.2317L17 14.071V17C17 18.1046 16.1046 19 15 19H13.001L13 22H4L4.00025 18.3061C4.00033 17.1252 3.56351 16.0087 2.7555 15.0011C1.65707 13.6313 1 11.8924 1 10C1 5.58172 4.58172 2 9 2ZM9 4C5.68629 4 3 6.68629 3 10C3 11.3849 3.46818 12.6929 4.31578 13.7499C5.40965 15.114 6.00036 16.6672 6.00025 18.3063L6.00013 20H11.0007L11.0017 17H15V12.7519L16.5497 12.0881L15.0072 9.66262L14.9501 9.22118C14.5665 6.25141 12.0243 4 9 4ZM19.4893 16.9929L21.1535 18.1024C22.32 16.3562 23 14.2576 23 12.0001C23 11.317 22.9378 10.6486 22.8186 10L20.8756 10.5C20.9574 10.9878 21 11.489 21 12.0001C21 13.8471 20.4436 15.5642 19.4893 16.9929Z"></path></svg>',title:"大模型",type:"llmNode",description:"使用大模型处理问题"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M15.5 5C13.567 5 12 6.567 12 8.5C12 10.433 13.567 12 15.5 12C17.433 12 19 10.433 19 8.5C19 6.567 17.433 5 15.5 5ZM10 8.5C10 5.46243 12.4624 3 15.5 3C18.5376 3 21 5.46243 21 8.5C21 9.6575 20.6424 10.7315 20.0317 11.6175L22.7071 14.2929L21.2929 15.7071L18.6175 13.0317C17.7315 13.6424 16.6575 14 15.5 14C12.4624 14 10 11.5376 10 8.5ZM3 4H8V6H3V4ZM3 11H8V13H3V11ZM21 18V20H3V18H21Z"></path></svg>',title:"知识库",type:"knowledgeNode",description:"通过知识库获取内容"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M18.031 16.6168L22.3137 20.8995L20.8995 22.3137L16.6168 18.031C15.0769 19.263 13.124 20 11 20C6.032 20 2 15.968 2 11C2 6.032 6.032 2 11 2C15.968 2 20 6.032 20 11C20 13.124 19.263 15.0769 18.031 16.6168ZM16.0247 15.8748C17.2475 14.6146 18 12.8956 18 11C18 7.1325 14.8675 4 11 4C7.1325 4 4 7.1325 4 11C4 14.8675 7.1325 18 11 18C12.8956 18 14.6146 17.2475 15.8748 16.0247L16.0247 15.8748Z"></path></svg>',title:"搜索引擎",type:"searchEngineNode",description:"通过搜索引擎搜索内容"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6.23509 6.45329C4.85101 7.89148 4 9.84636 4 12C4 16.4183 7.58172 20 12 20C13.0808 20 14.1116 19.7857 15.0521 19.3972C15.1671 18.6467 14.9148 17.9266 14.8116 17.6746C14.582 17.115 13.8241 16.1582 12.5589 14.8308C12.2212 14.4758 12.2429 14.2035 12.3636 13.3943L12.3775 13.3029C12.4595 12.7486 12.5971 12.4209 14.4622 12.1248C15.4097 11.9746 15.6589 12.3533 16.0043 12.8777C16.0425 12.9358 16.0807 12.9928 16.1198 13.0499C16.4479 13.5297 16.691 13.6394 17.0582 13.8064C17.2227 13.881 17.428 13.9751 17.7031 14.1314C18.3551 14.504 18.3551 14.9247 18.3551 15.8472V15.9518C18.3551 16.3434 18.3168 16.6872 18.2566 16.9859C19.3478 15.6185 20 13.8854 20 12C20 8.70089 18.003 5.8682 15.1519 4.64482C14.5987 5.01813 13.8398 5.54726 13.575 5.91C13.4396 6.09538 13.2482 7.04166 12.6257 7.11976C12.4626 7.14023 12.2438 7.12589 12.012 7.11097C11.3905 7.07058 10.5402 7.01606 10.268 7.75495C10.0952 8.2232 10.0648 9.49445 10.6239 10.1543C10.7134 10.2597 10.7307 10.4547 10.6699 10.6735C10.59 10.9608 10.4286 11.1356 10.3783 11.1717C10.2819 11.1163 10.0896 10.8931 9.95938 10.7412C9.64554 10.3765 9.25405 9.92233 8.74797 9.78176C8.56395 9.73083 8.36166 9.68867 8.16548 9.64736C7.6164 9.53227 6.99443 9.40134 6.84992 9.09302C6.74442 8.8672 6.74488 8.55621 6.74529 8.22764C6.74529 7.8112 6.74529 7.34029 6.54129 6.88256C6.46246 6.70541 6.35689 6.56446 6.23509 6.45329ZM12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22Z"></path></svg>',title:"Http 请求",type:"httpNode",description:"通过 HTTP 请求获取数据"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M23 12L15.9289 19.0711L14.5147 17.6569L20.1716 12L14.5147 6.34317L15.9289 4.92896L23 12ZM3.82843 12L9.48528 17.6569L8.07107 19.0711L1 12L8.07107 4.92896L9.48528 6.34317L3.82843 12Z"></path></svg>',title:"动态代码",type:"codeNode",description:"动态执行代码"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 4C2 3.44772 2.44772 3 3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4ZM4 5V19H20V5H4ZM7 8H17V11H15V10H13V14H14.5V16H9.5V14H11V10H9V11H7V8Z"></path></svg>',title:"内容模板",type:"templateNode",description:"通过模板引擎生成内容"},{icon:'<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" fill="currentColor" p-id="2577" width="200" height="200"><path d="M312.096 408.576l67.84 67.84 45.312-45.216a32 32 0 0 1 45.248 45.248l-45.28 45.248 90.496 90.496 45.28-45.216a32 32 0 0 1 45.248 45.248l-45.248 45.248 67.904 67.872-90.528 90.528a224.064 224.064 0 0 1-292.544 21.024L176.32 906.368a32 32 0 0 1-45.248-45.248l69.504-69.472a224.064 224.064 0 0 1 21.024-292.576l90.496-90.496z m0 90.496L266.848 544.32a160 160 0 0 0-4.8 221.28l4.8 4.992a160 160 0 0 0 221.248 4.8l5.024-4.8 45.248-45.248-226.272-226.24z m610.272-384a32 32 0 0 1 0 45.248l-69.44 69.504a224.064 224.064 0 0 1-21.056 292.544l-90.528 90.528-316.8-316.8 90.56-90.496a224.064 224.064 0 0 1 292.544-21.024l69.44-69.504a32 32 0 0 1 45.28 0zM565.344 246.08l-5.024 4.8-45.248 45.248 226.272 226.272 45.248-45.248a160 160 0 0 0 4.8-221.28l-4.8-4.992a160 160 0 0 0-221.248-4.8z" p-id="2578"></path></svg>',title:"内部接口",type:"internalNode",description:"执行内部提供接口"},{icon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M6 5.1438V16.0002H18.3391L6 5.1438ZM4 2.932C4 2.07155 5.01456 1.61285 5.66056 2.18123L21.6501 16.2494C22.3423 16.8584 21.9116 18.0002 20.9896 18.0002H6V22H4V2.932Z"></path></svg>',title:"结束节点",type:"endNode",description:"结束定义输出参数"}],o=[{label:"基础节点",value:"base"},{label:"业务工具",value:"tools"}];var i=Yy(),s=X(i),a=X(s),l=X(a);Wc(l,{style:"width: 100%",items:o,onChange:p=>{U(t,Ht(p.value.toString()))}}),Z(a);var u=z(a,2),c=X(u);Yt(c,21,()=>r,oi,(p,x)=>{Pd(p,ft(()=>h(x)))}),Z(c);var f=z(c,2),d=X(f);Ge(d,{children:(p,x)=>{Pe();var C=Ae("测试业务按钮");L(p,C)},$$slots:{default:!0}}),Z(f),Z(u),Z(s);var g=z(s,2);Ge(g,{onclick:()=>{U(n,Ht(h(n)?"":"show"))},children:(p,x)=>{var C=tt(),$=xe(C);{var m=v=>{var b=Ry();L(v,b)},_=v=>{var b=By();L(v,b)};ke($,v=>{h(n)==="show"?v(m):v(_,!1)})}L(p,C)},$$slots:{default:!0}}),Z(i),Ee(()=>{$t(i,1,`tf-toolbar ${h(n)??""}`),de(c,"style",`display: ${(h(t)==="base"?"flex":"none")??""}`),de(f,"style",`display: ${(h(t)!=="base"?"flex":"none")??""}`)}),L(e,i)}ie(Nd,{},[],[],!0);const Zy=()=>{const{nodeLookup:e}=Ue();return{getNode:n=>{var o;return(o=q(e).get(n))==null?void 0:o.internals.userNode}}},Xy=()=>{const{nodes:e}=Ue();return{ensureParentInNodesBefore:(n,r)=>{e.update(o=>{let i=-1;for(let l=0;l<o.length;l++)if(o[l].id===n){i=l;break}if(i<=0)return o;let s=-1;for(let l=0;l<i;l++)if(o[l].parentId===n||o[l].id===r){s=l;break}if(s==-1)return o;const a=o[i];for(let l=i;l>s;l--)o[l]=o[l-1];return o[s]=a,o})}}},Fy=()=>{const{edges:e}=Ue();return{getEdgesByTarget:n=>q(e).filter(o=>o.target===n)}};var Wy=ne('<div class="panel-content svelte-1oe15vw"><div>边属性设置</div> <div class="setting-title svelte-1oe15vw">边条件设置</div> <div class="setting-item"><!></div></div>'),Ky=ne("<!> <!> <!> <!>",1),qy=ne('<div style="position: relative; height: 100%; width: 100%"><!> <!></div>');const Gy={hash:"svelte-1oe15vw",code:".panel-content.svelte-1oe15vw {padding:10px;background-color:#fff;border-radius:5px;box-shadow:0 2px 4px rgba(0, 0, 0, 0.1);width:200px;border:1px solid #efefef;}.setting-title.svelte-1oe15vw {margin:10px 0;font-size:12px;color:#999;}"};function Td(e,t){ue(t,!0),et(e,Gy);const n=w(t,"onInit",7),r=Lt();n()(r);let o=Fn(!1);const i=_=>{_.preventDefault(),_.dataTransfer&&(_.dataTransfer.dropEffect="move")},s=_=>{var T;_.preventDefault();const v=r.screenToFlowPosition({x:_.clientX-250,y:_.clientY-100}),b=(T=_.dataTransfer)==null?void 0:T.getData("application/tinyflow"),N=b?JSON.parse(b):{},E={id:`node_${Kr()}`,position:v,data:{},...N};Bi.addNode(E),Bi.selectNodeOnly(E.id)},{getNode:a}=Zy(),l=_=>{const v=a(_.source),b=a(_.target);if(_.sourceHandle==="loop_handle"||v.parentId){const N=r.getEdges();for(let E of N)if(E.target===_.target){const T=a(E.source);if(_.sourceHandle==="loop_handle"&&T.parentId!==v.id||v.parentId&&T.parentId!==v.parentId)return!1}}return!(!v.parentId&&b.parentId&&b.parentId!==v.id)},{ensureParentInNodesBefore:u}=Xy(),c=(_,v)=>{if(!v.isValid)return;const b=v.toNode;if(b.parentId)return;const N=v.fromNode,E=v.fromHandle,T={position:{...b.position}};if(E.id==="loop_handle"?T.parentId=N.id:N.parentId&&(T.parentId=N.parentId),T.parentId){const D=a(T.parentId);T.position={x:b.position.x-D.position.x,y:b.position.y-D.position.y},u(T.parentId,b.id),r.updateNode(b.id,T)}},{getEdgesByTarget:f}=Fy(),d=_=>{_.edges.forEach(b=>{const N=a(b.target);if(N.parentId){const E=f(b.target),T=a(N.parentId);if(E.length===0)r.updateNode(N.id,{parentId:void 0,position:{x:N.position.x+T.position.x,y:N.position.y+T.position.y}});else{let D=!1;for(let V=0;V<E.length;V++){const A=E[V],O=a(A.source);if(O.parentId||O.type==="loopNode"){D=!0;break}}D||r.updateNode(N.id,{parentId:void 0,position:{x:N.position.x+T.position.x,y:N.position.y+T.position.y}})}}})},g=(_,v)=>{console.log("onconnectstart: ",_,v)},p=_=>{console.log("onconnect: ",_)};var x=qy(),C=X(x);Nd(C);var $=z(C,2);const m=Ne(()=>({markerEnd:{type:_o.ArrowClosed,width:20,height:20}}));return Pc($,ft({nodeTypes:Iy},Bi,{class:"tinyflow-logo",isValidConnection:l,onconnectend:c,onconnectstart:g,onconnect:p,connectionRadius:50,ondelete:d,onclick:_=>{const v=_.target;v.classList.contains("svelte-flow__edge-interaction")||v.classList.contains("panel-content")||v.closest(".panel-content")||U(o,!1)},get defaultEdgeOptions(){return h(m)},$$events:{drop:s,dragover:i,edgeclick:()=>{U(o,!0)}},children:(_,v)=>{var b=Ky(),N=xe(b);Ic(N,{});var E=z(N,2);Ac(E,{});var T=z(E,2);Rc(T,{});var D=z(T,2);{var V=A=>{So(A,{children:(O,R)=>{var S=Wy(),M=z(X(S),4),k=X(M);Pt(k,{rows:3,placeholder:"请输入边条件",style:"width: 100%",oninput:P=>{}}),Z(M),Z(S),L(O,S)},$$slots:{default:!0}})};ke(D,A=>{h(o)&&A(V)})}L(_,b)},$$slots:{default:!0}})),Z(x),L(e,x),ce({get onInit(){return n()},set onInit(_){n(_),y()}})}ie(Td,{onInit:{}},[],[],!0);function Uy(e,t){ue(t,!0);const n=w(t,"options",7),r=w(t,"onInit",7),{data:o}=n();return Bi.init((o==null?void 0:o.nodes)||[],(o==null?void 0:o.edges)||[]),Sr("tinyflow_options",n()),Nc(e,{fitView:!0,children:(i,s)=>{Td(i,{get onInit(){return r()}})},$$slots:{default:!0}}),ce({get options(){return n()},set options(i){n(i),y()},get onInit(){return r()},set onInit(i){r(i),y()}})}customElements.define("tinyflow-component",ie(Uy,{options:{},onInit:{}},[],[],!1)),We.Tinyflow=J2,Object.defineProperty(We,Symbol.toStringTag,{value:"Module"})}); +//# sourceMappingURL=index.umd.js.map diff --git a/src/router/modules/remaining.ts b/src/router/modules/remaining.ts index 6c21655..2e808c4 100644 --- a/src/router/modules/remaining.ts +++ b/src/router/modules/remaining.ts @@ -1,6 +1,8 @@ import {Layout} from '@/utils/routerHelper' +import {meta} from "eslint-plugin-prettier"; const { t } = useI18n() + /** * redirect: noredirect 当设置 noredirect 的时候该路由在面包屑导航中不可被点击 * name:'router-name' 设定路由的名字,一定要填写不然使用<keep-alive>时会出现各种问题 @@ -471,6 +473,118 @@ ] }, { + path: '/ai', + component: Layout, + name: 'Ai', + meta: { + hidden: true + }, + children: [ + { + path: 'image/square', + component: () => import('@/views/ai/image/square/index.vue'), + name: 'AiImageSquare', + meta: { + title: '绘图作品', + icon: 'ep:home-filled', + noCache: false + } + }, + { + path: 'knowledge/document', + component: () => import('@/views/ai/knowledge/document/index.vue'), + name: 'AiKnowledgeDocument', + meta: { + title: '知识库文档', + icon: 'ep:document', + noCache: false, + activeMenu: '/ai/knowledge' + } + }, + { + path: 'knowledge/document/create', + component: () => import('@/views/ai/knowledge/document/form/index.vue'), + name: 'AiKnowledgeDocumentCreate', + meta: { + title: '创建文档', + icon: 'ep:plus', + noCache: true, + hidden: true, + activeMenu: '/ai/knowledge' + } + }, + { + path: 'knowledge/document/update', + component: () => import('@/views/ai/knowledge/document/form/index.vue'), + name: 'AiKnowledgeDocumentUpdate', + meta: { + title: '修改文档', + icon: 'ep:edit', + noCache: true, + hidden: true, + activeMenu: '/ai/knowledge' + } + }, + { + path: 'knowledge/retrieval', + component: () => import('@/views/ai/knowledge/knowledge/retrieval/index.vue'), + name: 'AiKnowledgeRetrieval', + meta: { + title: '文档召回测试', + icon: 'ep:search', + noCache: true, + hidden: true, + activeMenu: '/ai/knowledge' + } + }, + { + path: 'knowledge/segment', + component: () => import('@/views/ai/knowledge/segment/index.vue'), + name: 'AiKnowledgeSegment', + meta: { + title: '知识库分段', + icon: 'ep:tickets', + noCache: true, + hidden: true, + activeMenu: '/ai/knowledge' + } + }, + { + path: 'console/workflow/create', + component: () => import('@/views/ai/workflow/form/index.vue'), + name: 'AiWorkflowCreate', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '设计 AI 工作流', + activeMenu: '/ai/console/workflow' + } + }, + { + path: 'console/workflow/:type/:id', + component: () => import('@/views/ai/workflow/form/index.vue'), + name: 'AiWorkflowUpdate', + meta: { + noCache: true, + hidden: true, + canTo: true, + title: '设计 AI 工作流', + activeMenu: '/ai/console/workflow' + } + } + ] + }, + { + path: '/ai/zhuanlu', + name: 'Zhuanlu', + component: () => import('@/views/ai/dashboard/zhuanlu/index.vue'), + meta: { + hidden: true, + noTagsView: true, + } + }, + { path: '/model/analysis', name: 'AnalysisformDataThird', component: () => import('@/views/model/pre/analysis/third.vue'), diff --git a/src/utils/dict.ts b/src/utils/dict.ts index 20b54e4..13efa8f 100644 --- a/src/utils/dict.ts +++ b/src/utils/dict.ts @@ -202,4 +202,16 @@ ADJUST_TRIGGER_RULE = 'adjust_trigger_rule', ADJUST_VALUE_RULE = 'adjust_value_rule', INFLUENCE_FACTOR_PATTERN = 'influence_factor_pattern', + + // ========== AI - 人工智能模块 ========== + AI_PLATFORM = 'ai_platform', // AI 平台 + AI_MODEL_TYPE = 'ai_model_type', // AI 模型类型 + AI_IMAGE_STATUS = 'ai_image_status', // AI 图片状态 + AI_MUSIC_STATUS = 'ai_music_status', // AI 音乐状态 + AI_GENERATE_MODE = 'ai_generate_mode', // AI 生成模式 + AI_WRITE_TYPE = 'ai_write_type', // AI 写作类型 + AI_WRITE_LENGTH = 'ai_write_length', // AI 写作长度 + AI_WRITE_FORMAT = 'ai_write_format', // AI 写作格式 + AI_WRITE_TONE = 'ai_write_tone', // AI 写作语气 + AI_WRITE_LANGUAGE = 'ai_write_language', // AI 写作语言 } diff --git a/src/utils/index.ts b/src/utils/index.ts index 2c2fbbd..37d6a52 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -117,6 +117,64 @@ } /** + * 根据支持的文件类型生成 accept 属性值 + * + * @param supportedFileTypes 支持的文件类型数组,如 ['PDF', 'DOC', 'DOCX'] + * @returns 用于文件上传组件 accept 属性的字符串 + */ +export const generateAcceptedFileTypes = (supportedFileTypes: string[]): string => { + const allowedExtensions = supportedFileTypes.map((ext) => ext.toLowerCase()) + const mimeTypes: string[] = [] + + // 添加常见的 MIME 类型映射 + if (allowedExtensions.includes('txt')) { + mimeTypes.push('text/plain') + } + if (allowedExtensions.includes('pdf')) { + mimeTypes.push('application/pdf') + } + if (allowedExtensions.includes('html') || allowedExtensions.includes('htm')) { + mimeTypes.push('text/html') + } + if (allowedExtensions.includes('csv')) { + mimeTypes.push('text/csv') + } + if (allowedExtensions.includes('xlsx') || allowedExtensions.includes('xls')) { + mimeTypes.push('application/vnd.ms-excel') + mimeTypes.push('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') + } + if (allowedExtensions.includes('docx') || allowedExtensions.includes('doc')) { + mimeTypes.push('application/msword') + mimeTypes.push('application/vnd.openxmlformats-officedocument.wordprocessingml.document') + } + if (allowedExtensions.includes('pptx') || allowedExtensions.includes('ppt')) { + mimeTypes.push('application/vnd.ms-powerpoint') + mimeTypes.push('application/vnd.openxmlformats-officedocument.presentationml.presentation') + } + if (allowedExtensions.includes('xml')) { + mimeTypes.push('application/xml') + mimeTypes.push('text/xml') + } + if (allowedExtensions.includes('md') || allowedExtensions.includes('markdown')) { + mimeTypes.push('text/markdown') + } + if (allowedExtensions.includes('epub')) { + mimeTypes.push('application/epub+zip') + } + if (allowedExtensions.includes('eml')) { + mimeTypes.push('message/rfc822') + } + if (allowedExtensions.includes('msg')) { + mimeTypes.push('application/vnd.ms-outlook') + } + + // 添加文件扩展名 + const extensions = allowedExtensions.map((ext) => `.${ext}`) + + return [...mimeTypes, ...extensions].join(',') +} + +/** * 首字母大写 */ export function firstUpperCase(str: string) { diff --git a/src/utils/rem.ts b/src/utils/rem.ts new file mode 100644 index 0000000..00be2a1 --- /dev/null +++ b/src/utils/rem.ts @@ -0,0 +1,9 @@ +// utils/rem.js +const baseWidth = 1920 // 设计稿基准宽度 +const baseFontSize = 16 // 基准字体大小 + +export const setRem = () => { + const scale = document.documentElement.clientWidth / baseWidth + document.documentElement.style.fontSize = + `${Math.min(scale, 1.5) * baseFontSize}px` // 限制最大缩放比例 +} diff --git a/src/views/ai/chat/index/components/conversation/ConversationList.vue b/src/views/ai/chat/index/components/conversation/ConversationList.vue new file mode 100644 index 0000000..54940f8 --- /dev/null +++ b/src/views/ai/chat/index/components/conversation/ConversationList.vue @@ -0,0 +1,472 @@ +<!-- AI 对话 --> +<template> + <el-aside width="260px" class="conversation-container h-100%"> + <!-- 左顶部:对话 --> + <div class="h-100%"> + <el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation"> + <Icon icon="ep:plus" class="mr-5px" /> + 新建对话 + </el-button> + + <!-- 左顶部:搜索对话 --> + <el-input + v-model="searchName" + size="large" + class="mt-10px search-input" + placeholder="搜索历史记录" + @keyup="searchConversation" + > + <template #prefix> + <Icon icon="ep:search" /> + </template> + </el-input> + + <!-- 左中间:对话列表 --> + <div class="conversation-list"> + <!-- 情况一:加载中 --> + <el-empty v-if="loading" description="." :v-loading="loading" /> + <!-- 情况二:按照 group 分组,展示聊天会话 list 列表 --> + <div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey"> + <div + class="conversation-item classify-title" + v-if="conversationMap[conversationKey].length" + > + <el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text> + </div> + <div + class="conversation-item" + v-for="conversation in conversationMap[conversationKey]" + :key="conversation.id" + @click="handleConversationClick(conversation.id)" + @mouseover="hoverConversationId = conversation.id" + @mouseout="hoverConversationId = ''" + > + <div + :class=" + conversation.id === activeConversationId ? 'conversation active' : 'conversation' + " + > + <div class="title-wrapper"> + <img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" /> + <span class="title">{{ conversation.title }}</span> + </div> + <div class="button-wrapper" v-show="hoverConversationId === conversation.id"> + <el-button class="btn" link @click.stop="handleTop(conversation)"> + <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon> + <el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon> + </el-button> + <el-button class="btn" link @click.stop="updateConversationTitle(conversation)"> + <el-icon title="编辑"> + <Icon icon="ep:edit" /> + </el-icon> + </el-button> + <el-button class="btn" link @click.stop="deleteChatConversation(conversation)"> + <el-icon title="删除对话"> + <Icon icon="ep:delete" /> + </el-icon> + </el-button> + </div> + </div> + </div> + </div> + <!-- 底部占位 --> + <div class="h-160px w-100%"></div> + </div> + </div> + + <!-- 左底部:工具栏 --> + <div class="tool-box"> + <div @click="handleRoleRepository"> + <Icon icon="ep:user" /> + <el-text size="small">角色仓库</el-text> + </div> + <div @click="handleClearConversation"> + <Icon icon="ep:delete" /> + <el-text size="small">清空未置顶对话</el-text> + </div> + </div> + + <!-- 角色仓库抽屉 --> + <el-drawer v-model="roleRepositoryOpen" title="角色仓库" size="754px"> + <RoleRepository /> + </el-drawer> + </el-aside> +</template> + +<script setup lang="ts"> +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import RoleRepository from '../role/RoleRepository.vue' +import { Bottom, Top } from '@element-plus/icons-vue' +import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' + +const message = useMessage() // 消息弹窗 + +// 定义属性 +const searchName = ref<string>('') // 对话搜索 +const activeConversationId = ref<number | null>(null) // 选中的对话,默认为 null +const hoverConversationId = ref<number | null>(null) // 悬浮上去的对话 +const conversationList = ref([] as ChatConversationVO[]) // 对话列表 +const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前) +const loading = ref<boolean>(false) // 加载中 +const loadingTime = ref<any>() // 加载中定时器 + +// 定义组件 props +const props = defineProps({ + activeId: { + type: String || null, + required: true + } +}) + +// 定义钩子 +const emits = defineEmits([ + 'onConversationCreate', + 'onConversationClick', + 'onConversationClear', + 'onConversationDelete' +]) + +/** 搜索对话 */ +const searchConversation = async (e) => { + // 恢复数据 + if (!searchName.value.trim().length) { + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } else { + // 过滤 + const filterValues = conversationList.value.filter((item) => { + return item.title.includes(searchName.value.trim()) + }) + conversationMap.value = await getConversationGroupByCreateTime(filterValues) + } +} + +/** 点击对话 */ +const handleConversationClick = async (id: number) => { + // 过滤出选中的对话 + const filterConversation = conversationList.value.filter((item) => { + return item.id === id + }) + // 回调 onConversationClick + // noinspection JSVoidFunctionReturnValueUsed + const success = emits('onConversationClick', filterConversation[0]) + // 切换对话 + if (success) { + activeConversationId.value = id + } +} + +/** 获取对话列表 */ +const getChatConversationList = async () => { + try { + // 加载中 + loadingTime.value = setTimeout(() => { + loading.value = true + }, 50) + + // 1.1 获取 对话数据 + conversationList.value = await ChatConversationApi.getChatConversationMyList() + // 1.2 排序 + conversationList.value.sort((a, b) => { + return b.createTime - a.createTime + }) + // 1.3 没有任何对话情况 + if (conversationList.value.length === 0) { + activeConversationId.value = null + conversationMap.value = {} + return + } + + // 2. 对话根据时间分组(置顶、今天、一天前、三天前、七天前、30 天前) + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } finally { + // 清理定时器 + if (loadingTime.value) { + clearTimeout(loadingTime.value) + } + // 加载完成 + loading.value = false + } +} + +/** 按照 creteTime 创建时间,进行分组 */ +const getConversationGroupByCreateTime = async (list: ChatConversationVO[]) => { + // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前) + // noinspection NonAsciiCharacters + const groupMap = { + 置顶: [], + 今天: [], + 一天前: [], + 三天前: [], + 七天前: [], + 三十天前: [] + } + // 当前时间的时间戳 + const now = Date.now() + // 定义时间间隔常量(单位:毫秒) + const oneDay = 24 * 60 * 60 * 1000 + const threeDays = 3 * oneDay + const sevenDays = 7 * oneDay + const thirtyDays = 30 * oneDay + for (const conversation of list) { + // 置顶 + if (conversation.pinned) { + groupMap['置顶'].push(conversation) + continue + } + // 计算时间差(单位:毫秒) + const diff = now - conversation.createTime + // 根据时间间隔判断 + if (diff < oneDay) { + groupMap['今天'].push(conversation) + } else if (diff < threeDays) { + groupMap['一天前'].push(conversation) + } else if (diff < sevenDays) { + groupMap['三天前'].push(conversation) + } else if (diff < thirtyDays) { + groupMap['七天前'].push(conversation) + } else { + groupMap['三十天前'].push(conversation) + } + } + return groupMap +} + +/** 新建对话 */ +const createConversation = async () => { + // 1. 新建对话 + const conversationId = await ChatConversationApi.createChatConversationMy( + {} as unknown as ChatConversationVO + ) + // 2. 获取对话内容 + await getChatConversationList() + // 3. 选中对话 + await handleConversationClick(conversationId) + // 4. 回调 + emits('onConversationCreate') +} + +/** 修改对话的标题 */ +const updateConversationTitle = async (conversation: ChatConversationVO) => { + // 1. 二次确认 + const { value } = await ElMessageBox.prompt('修改标题', { + inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格 + inputErrorMessage: '标题不能为空', + inputValue: conversation.title + }) + // 2. 发起修改 + await ChatConversationApi.updateChatConversationMy({ + id: conversation.id, + title: value + } as ChatConversationVO) + message.success('重命名成功') + // 3. 刷新列表 + await getChatConversationList() + // 4. 过滤当前切换的 + const filterConversationList = conversationList.value.filter((item) => { + return item.id === conversation.id + }) + if (filterConversationList.length > 0) { + // tip:避免切换对话 + if (activeConversationId.value === filterConversationList[0].id) { + emits('onConversationClick', filterConversationList[0]) + } + } +} + +/** 删除聊天对话 */ +const deleteChatConversation = async (conversation: ChatConversationVO) => { + try { + // 删除的二次确认 + await message.delConfirm(`是否确认删除对话 - ${conversation.title}?`) + // 发起删除 + await ChatConversationApi.deleteChatConversationMy(conversation.id) + message.success('对话已删除') + // 刷新列表 + await getChatConversationList() + // 回调 + emits('onConversationDelete', conversation) + } catch {} +} + +/** 清空对话 */ +const handleClearConversation = async () => { + try { + await message.confirm('确认后对话会全部清空,置顶的对话除外。') + await ChatConversationApi.deleteChatConversationMyByUnpinned() + ElMessage({ + message: '操作成功!', + type: 'success' + }) + // 清空 对话 和 对话内容 + activeConversationId.value = null + // 获取 对话列表 + await getChatConversationList() + // 回调 方法 + emits('onConversationClear') + } catch {} +} + +/** 对话置顶 */ +const handleTop = async (conversation: ChatConversationVO) => { + // 更新对话置顶 + conversation.pinned = !conversation.pinned + await ChatConversationApi.updateChatConversationMy(conversation) + // 刷新对话 + await getChatConversationList() +} + +// ============ 角色仓库 ============ + +/** 角色仓库抽屉 */ +const roleRepositoryOpen = ref<boolean>(false) // 角色仓库是否打开 +const handleRoleRepository = async () => { + roleRepositoryOpen.value = !roleRepositoryOpen.value +} + +/** 监听选中的对话 */ +const { activeId } = toRefs(props) +watch(activeId, async (newValue, oldValue) => { + activeConversationId.value = newValue as string +}) + +// 定义 public 方法 +defineExpose({ createConversation }) + +/** 初始化 */ +onMounted(async () => { + // 获取 对话列表 + await getChatConversationList() + // 默认选中 + if (props.activeId) { + activeConversationId.value = props.activeId + } else { + // 首次默认选中第一个 + if (conversationList.value.length) { + activeConversationId.value = conversationList.value[0].id + // 回调 onConversationClick + await emits('onConversationClick', conversationList.value[0]) + } + } +}) +</script> + +<style scoped lang="scss"> +.conversation-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px 10px 0; + overflow: hidden; + + .btn-new-conversation { + padding: 18px 0; + } + + .search-input { + margin-top: 20px; + } + + .conversation-list { + overflow: auto; + height: 100%; + + .classify-title { + padding-top: 10px; + } + + .conversation-item { + margin-top: 5px; + } + + .conversation { + display: flex; + flex-direction: row; + justify-content: space-between; + flex: 1; + padding: 0 5px; + cursor: pointer; + border-radius: 5px; + align-items: center; + line-height: 30px; + + &.active { + background-color: #e6e6e6; + + .button { + display: inline-block; + } + } + + .title-wrapper { + display: flex; + flex-direction: row; + align-items: center; + } + + .title { + padding: 2px 10px; + max-width: 220px; + font-size: 14px; + font-weight: 400; + color: rgba(0, 0, 0, 0.77); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .avatar { + width: 25px; + height: 25px; + border-radius: 5px; + display: flex; + flex-direction: row; + justify-items: center; + } + + // 对话编辑、删除 + .button-wrapper { + right: 2px; + display: flex; + flex-direction: row; + justify-items: center; + color: #606266; + + .btn { + margin: 0; + } + } + } + } + + // 角色仓库、清空未设置对话 + .tool-box { + position: absolute; + bottom: 0; + left: 0; + right: 0; + //width: 100%; + padding: 0 20px; + background-color: #f4f4f4; + box-shadow: 0 0 1px 1px rgba(228, 228, 228, 0.8); + line-height: 35px; + display: flex; + justify-content: space-between; + align-items: center; + color: var(--el-text-color); + + > div { + display: flex; + align-items: center; + color: #606266; + padding: 0; + margin: 0; + cursor: pointer; + + > span { + margin-left: 5px; + } + } + } +} +</style> diff --git a/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue b/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue new file mode 100644 index 0000000..90f68c6 --- /dev/null +++ b/src/views/ai/chat/index/components/conversation/ConversationUpdateForm.vue @@ -0,0 +1,148 @@ +<template> + <Dialog title="设定" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="130px" + v-loading="formLoading" + > + <el-form-item label="角色设定" prop="systemMessage"> + <el-input + type="textarea" + v-model="formData.systemMessage" + :rows="4" + placeholder="请输入角色设定" + /> + </el-form-item> + <el-form-item label="模型" prop="modelId"> + <el-select v-model="formData.modelId" placeholder="请选择模型"> + <el-option + v-for="model in models" + :key="model.id" + :label="model.name" + :value="model.id" + /> + </el-select> + </el-form-item> + <el-form-item label="温度参数" prop="temperature"> + <el-input-number + v-model="formData.temperature" + placeholder="请输入温度参数" + :min="0" + :max="2" + :precision="2" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="回复数 Token 数" prop="maxTokens"> + <el-input-number + v-model="formData.maxTokens" + placeholder="请输入回复数 Token 数" + :min="0" + :max="8192" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="上下文数量" prop="maxContexts"> + <el-input-number + v-model="formData.maxContexts" + placeholder="请输入上下文数量" + :min="0" + :max="20" + class="!w-1/1" + /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import { AiModelTypeEnum } from '@/views/ai/utils/constants' + +/** AI 聊天对话的更新表单 */ +defineOptions({ name: 'ChatConversationUpdateForm' }) + +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formData = ref({ + id: undefined, + systemMessage: undefined, + modelId: undefined, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined +}) +const formRules = reactive({ + modelId: [{ required: true, message: '模型不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'blur' }], + temperature: [{ required: true, message: '温度参数不能为空', trigger: 'blur' }], + maxTokens: [{ required: true, message: '回复数 Token 数不能为空', trigger: 'blur' }], + maxContexts: [{ required: true, message: '上下文数量不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const models = ref([] as ModelVO[]) // 聊天模型列表 + +/** 打开弹窗 */ +const open = async (id: number) => { + dialogVisible.value = true + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + const data = await ChatConversationApi.getChatConversationMy(id) + formData.value = Object.keys(formData.value).reduce((obj, key) => { + if (data.hasOwnProperty(key)) { + obj[key] = data[key] + } + return obj + }, {}) + } finally { + formLoading.value = false + } + } + // 获得下拉数据 + models.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.CHAT) +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ChatConversationVO + await ChatConversationApi.updateChatConversationMy(data) + message.success('对话配置已更新') + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + systemMessage: undefined, + modelId: undefined, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/chat/index/components/message/MessageKnowledge.vue b/src/views/ai/chat/index/components/message/MessageKnowledge.vue new file mode 100644 index 0000000..5ff3f63 --- /dev/null +++ b/src/views/ai/chat/index/components/message/MessageKnowledge.vue @@ -0,0 +1,104 @@ +<!-- 知识引用组件 --> +<template> + <!-- 知识引用列表 --> + <div v-if="segments && segments.length > 0" class="mt-10px p-10px rounded-8px bg-[#f5f5f5]"> + <div class="text-14px text-[#666] mb-8px flex items-center"> + <Icon icon="ep:document" class="mr-5px" /> 知识引用 + </div> + <div class="flex flex-wrap gap-8px"> + <div + v-for="(doc, index) in documentList" + :key="index" + class="p-8px px-12px bg-white rounded-6px cursor-pointer transition-all hover:bg-[#e6f4ff]" + @click="handleClick(doc)" + > + <div class="text-14px text-[#333] mb-4px"> + {{ doc.title }} + <span class="text-12px text-[#999] ml-4px">({{ doc.segments.length }} 条)</span> + </div> + </div> + </div> + </div> + + <!-- 知识引用详情弹窗 --> + <el-popover + v-model:visible="dialogVisible" + :width="600" + trigger="click" + placement="top-start" + :offset="55" + popper-class="knowledge-popover" + > + <template #reference> + <div ref="documentRef"></div> + </template> + <template #default> + <div class="text-16px font-bold mb-12px">{{ document?.title }}</div> + <div class="max-h-[60vh] overflow-y-auto"> + <div + v-for="(segment, index) in document?.segments" + :key="index" + class="p-12px border-b-solid border-b-[#eee] last:border-b-0" + > + <div + class="block mb-8px px-8px py-2px bg-[#f5f5f5] rounded-4px text-12px text-[#666] w-fit" + > + 分段 {{ segment.id }} + </div> + <div class="text-14px leading-[1.6] text-[#333] mt-[10px]"> + {{ segment.content }} + </div> + </div> + </div> + </template> + </el-popover> +</template> + +<script setup lang="ts"> +const props = defineProps<{ + segments: { + id: number + documentId: number + documentName: string + content: string + }[] +}>() + +const document = ref<{ + id: number + title: string + segments: { + id: number + content: string + }[] +} | null>(null) // 知识库文档列表 +const dialogVisible = ref(false) // 知识引用详情弹窗 +const documentRef = ref<HTMLElement>() // 知识引用详情弹窗 Ref + +/** 按照 document 聚合 segments */ +const documentList = computed(() => { + if (!props.segments) return [] + + const docMap = new Map() + props.segments.forEach((segment) => { + if (!docMap.has(segment.documentId)) { + docMap.set(segment.documentId, { + id: segment.documentId, + title: segment.documentName, + segments: [] + }) + } + docMap.get(segment.documentId).segments.push({ + id: segment.id, + content: segment.content + }) + }) + return Array.from(docMap.values()) +}) + +/** 点击 document 处理 */ +const handleClick = (doc: any) => { + document.value = doc + dialogVisible.value = true +} +</script> diff --git a/src/views/ai/chat/index/components/message/MessageList.vue b/src/views/ai/chat/index/components/message/MessageList.vue new file mode 100644 index 0000000..2840bf9 --- /dev/null +++ b/src/views/ai/chat/index/components/message/MessageList.vue @@ -0,0 +1,284 @@ +<template> + <div ref="messageContainer" class="h-100% overflow-y-auto relative"> + <div class="chat-list" v-for="(item, index) in list" :key="index"> + <!-- 靠左 message:system、assistant 类型 --> + <div class="left-message message-item" v-if="item.type !== 'user'"> + <div class="avatar"> + <el-avatar :src="roleAvatar" /> + </div> + <div class="message"> + <div> + <el-text class="time">{{ formatDate(item.createTime) }}</el-text> + </div> + <div class="left-text-container" ref="markdownViewRef"> + <MarkdownView class="left-text" :content="item.content" /> + <MessageKnowledge v-if="item.segments" :segments="item.segments" /> + </div> + <div class="left-btns"> + <el-button class="btn-cus" link @click="copyContent(item.content)"> + <img class="btn-image" src="@/assets/ai/copy.svg" /> + </el-button> + <el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)"> + <img class="btn-image h-17px" src="@/assets/ai/delete.svg" /> + </el-button> + </div> + </div> + </div> + <!-- 靠右 message:user 类型 --> + <div class="right-message message-item" v-if="item.type === 'user'"> + <div class="avatar"> + <el-avatar :src="userAvatar" /> + </div> + <div class="message"> + <div> + <el-text class="time">{{ formatDate(item.createTime) }}</el-text> + </div> + <div class="right-text-container"> + <div class="right-text">{{ item.content }}</div> + </div> + <div class="right-btns"> + <el-button class="btn-cus" link @click="copyContent(item.content)"> + <img class="btn-image" src="@/assets/ai/copy.svg" /> + </el-button> + <el-button class="btn-cus" link @click="onDelete(item.id)"> + <img class="btn-image h-17px mr-12px" src="@/assets/ai/delete.svg" /> + </el-button> + <el-button class="btn-cus" link @click="onRefresh(item)"> + <el-icon size="17"><RefreshRight /></el-icon> + </el-button> + <el-button class="btn-cus" link @click="onEdit(item)"> + <el-icon size="17"><Edit /></el-icon> + </el-button> + </div> + </div> + </div> + </div> + </div> + <!-- 回到底部 --> + <div v-if="isScrolling" class="to-bottom" @click="handleGoBottom"> + <el-button :icon="ArrowDownBold" circle /> + </div> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' +import { formatDate } from '@/utils/formatTime' +import MarkdownView from '@/components/MarkdownView/index.vue' +import MessageKnowledge from './MessageKnowledge.vue' +import { useClipboard } from '@vueuse/core' +import { ArrowDownBold, Edit, RefreshRight } from '@element-plus/icons-vue' +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import { ChatConversationVO } from '@/api/ai/chat/conversation' +import { useUserStore } from '@/store/modules/user' +import userAvatarDefaultImg from '@/assets/imgs/avatar.gif' +import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' + +const message = useMessage() // 消息弹窗 +const { copy } = useClipboard() // 初始化 copy 到粘贴板 +const userStore = useUserStore() + +// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方) +const messageContainer: any = ref(null) +const isScrolling = ref(false) //用于判断用户是否在滚动 + +const userAvatar = computed(() => userStore.user.avatar || userAvatarDefaultImg) +const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg) + +// 定义 props +const props = defineProps({ + conversation: { + type: Object as PropType<ChatConversationVO>, + required: true + }, + list: { + type: Array as PropType<ChatMessageVO[]>, + required: true + } +}) + +const { list } = toRefs(props) // 消息列表 + +const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // 定义 emits + +// ============ 处理对话滚动 ============== + +/** 滚动到底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + // 注意要使用 nextTick 以免获取不到 dom + await nextTick() + if (isIgnore || !isScrolling.value) { + messageContainer.value.scrollTop = + messageContainer.value.scrollHeight - messageContainer.value.offsetHeight + } +} + +function handleScroll() { + const scrollContainer = messageContainer.value + const scrollTop = scrollContainer.scrollTop + const scrollHeight = scrollContainer.scrollHeight + const offsetHeight = scrollContainer.offsetHeight + if (scrollTop + offsetHeight < scrollHeight - 100) { + // 用户开始滚动并在最底部之上,取消保持在最底部的效果 + isScrolling.value = true + } else { + // 用户停止滚动并滚动到最底部,开启保持到最底部的效果 + isScrolling.value = false + } +} + +/** 回到底部 */ +const handleGoBottom = async () => { + const scrollContainer = messageContainer.value + scrollContainer.scrollTop = scrollContainer.scrollHeight +} + +/** 回到顶部 */ +const handlerGoTop = async () => { + const scrollContainer = messageContainer.value + scrollContainer.scrollTop = 0 +} + +defineExpose({ scrollToBottom, handlerGoTop }) // 提供方法给 parent 调用 + +// ============ 处理消息操作 ============== + +/** 复制 */ +const copyContent = async (content) => { + await copy(content) + message.success('复制成功!') +} + +/** 删除 */ +const onDelete = async (id) => { + // 删除 message + await ChatMessageApi.deleteChatMessage(id) + message.success('删除成功!') + // 回调 + emits('onDeleteSuccess') +} + +/** 刷新 */ +const onRefresh = async (message: ChatMessageVO) => { + emits('onRefresh', message) +} + +/** 编辑 */ +const onEdit = async (message: ChatMessageVO) => { + emits('onEdit', message) +} + +/** 初始化 */ +onMounted(async () => { + messageContainer.value.addEventListener('scroll', handleScroll) +}) +</script> + +<style scoped lang="scss"> +.message-container { + position: relative; + overflow-y: scroll; +} + +// 中间 +.chat-list { + display: flex; + flex-direction: column; + overflow-y: hidden; + padding: 0 20px; + .message-item { + margin-top: 50px; + } + + .left-message { + display: flex; + flex-direction: row; + } + + .right-message { + display: flex; + flex-direction: row-reverse; + justify-content: flex-start; + } + + .message { + display: flex; + flex-direction: column; + text-align: left; + margin: 0 15px; + + .time { + text-align: left; + line-height: 30px; + } + + .left-text-container { + position: relative; + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background-color: rgba(228, 228, 228, 0.8); + box-shadow: 0 0 0 1px rgba(228, 228, 228, 0.8); + border-radius: 10px; + padding: 10px 10px 5px 10px; + + .left-text { + color: #393939; + font-size: 0.95rem; + } + } + + .right-text-container { + display: flex; + flex-direction: row-reverse; + + .right-text { + font-size: 0.95rem; + color: #fff; + display: inline; + background-color: #267fff; + box-shadow: 0 0 0 1px #267fff; + border-radius: 10px; + padding: 10px; + width: auto; + overflow-wrap: break-word; + white-space: pre-wrap; + } + } + + .left-btns { + display: flex; + flex-direction: row; + margin-top: 8px; + } + + .right-btns { + display: flex; + flex-direction: row-reverse; + margin-top: 8px; + } + } + + // 复制、删除按钮 + .btn-cus { + display: flex; + background-color: transparent; + align-items: center; + + .btn-image { + height: 20px; + } + } + + .btn-cus:hover { + cursor: pointer; + background-color: #f6f6f6; + } +} + +// 回到底部 +.to-bottom { + position: absolute; + z-index: 1000; + bottom: 0; + right: 50%; +} +</style> diff --git a/src/views/ai/chat/index/components/message/MessageListEmpty.vue b/src/views/ai/chat/index/components/message/MessageListEmpty.vue new file mode 100644 index 0000000..e4d4539 --- /dev/null +++ b/src/views/ai/chat/index/components/message/MessageListEmpty.vue @@ -0,0 +1,83 @@ +<!-- 消息列表为空时,展示 prompt 列表 --> +<template> + <div class="chat-empty"> + <!-- title --> + <div class="center-container"> + <div class="title">工业互联网平台 AI</div> + <div class="role-list"> + <div + class="role-item" + v-for="prompt in promptList" + :key="prompt.prompt" + @click="handlerPromptClick(prompt)" + > + {{ prompt.prompt }} + </div> + </div> + </div> + </div> +</template> +<script setup lang="ts"> +const promptList = [ + { + prompt: '今天气怎么样?' + }, + { + prompt: '写一首好听的诗歌?' + } +] // prompt 列表 + +const emits = defineEmits(['onPrompt']) + +/** 选中 prompt 点击 */ +const handlerPromptClick = async ({ prompt }) => { + emits('onPrompt', prompt) +} +</script> +<style scoped lang="scss"> +.chat-empty { + position: relative; + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + height: 100%; + + .center-container { + display: flex; + flex-direction: column; + justify-content: center; + + .title { + font-size: 28px; + font-weight: bold; + text-align: center; + } + + .role-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + justify-content: center; + width: 460px; + margin-top: 20px; + + .role-item { + display: flex; + justify-content: center; + width: 180px; + line-height: 50px; + border: 1px solid #e4e4e4; + border-radius: 10px; + margin: 10px; + cursor: pointer; + } + + .role-item:hover { + background-color: rgba(243, 243, 243, 0.73); + } + } + } +} +</style> diff --git a/src/views/ai/chat/index/components/message/MessageLoading.vue b/src/views/ai/chat/index/components/message/MessageLoading.vue new file mode 100644 index 0000000..f3198cb --- /dev/null +++ b/src/views/ai/chat/index/components/message/MessageLoading.vue @@ -0,0 +1,15 @@ +<!-- message 加载页面 --> +<template> + <div class="message-loading" > + <el-skeleton animated /> + </div> +</template> + +<script setup lang="ts"> + +</script> +<style scoped lang="scss"> +.message-loading { + padding: 30px 30px; +} +</style> diff --git a/src/views/ai/chat/index/components/message/MessageNewConversation.vue b/src/views/ai/chat/index/components/message/MessageNewConversation.vue new file mode 100644 index 0000000..40c3107 --- /dev/null +++ b/src/views/ai/chat/index/components/message/MessageNewConversation.vue @@ -0,0 +1,46 @@ +<!-- 无聊天对话时,在 message 区域,可以新增对话 --> +<template> + <div class="new-chat"> + <div class="box-center"> + <div class="tip">点击下方按钮,开始你的对话吧</div> + <div class="btns"> + <el-button type="primary" round @click="handlerNewChat">新建对话</el-button> + </div> + </div> + </div> +</template> +<script setup lang="ts"> +const emits = defineEmits(['onNewConversation']) + +/** 新建 conversation 聊天对话 */ +const handlerNewChat = () => { + emits('onNewConversation') +} +</script> +<style scoped lang="scss"> +.new-chat { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + height: 100%; + + .box-center { + display: flex; + flex-direction: column; + justify-content: center; + + .tip { + font-size: 14px; + color: #858585; + } + + .btns { + display: flex; + flex-direction: row; + justify-content: center; + margin-top: 20px; + } + } +} +</style> diff --git a/src/views/ai/chat/index/components/role/RoleCategoryList.vue b/src/views/ai/chat/index/components/role/RoleCategoryList.vue new file mode 100644 index 0000000..c02126d --- /dev/null +++ b/src/views/ai/chat/index/components/role/RoleCategoryList.vue @@ -0,0 +1,53 @@ +<template> + <div class="category-list"> + <div class="category" v-for="category in categoryList" :key="category"> + <el-button + plain + round + size="small" + :type="category === active ? 'primary' : ''" + @click="handleCategoryClick(category)" + > + {{ category }} + </el-button> + </div> + </div> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' + +// 定义属性 +defineProps({ + categoryList: { + type: Array as PropType<string[]>, + required: true + }, + active: { + type: String, + required: false, + default: '全部' + } +}) + +// 定义回调 +const emits = defineEmits(['onCategoryClick']) + +/** 处理分类点击事件 */ +const handleCategoryClick = async (category: string) => { + emits('onCategoryClick', category) +} +</script> +<style scoped lang="scss"> +.category-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + + .category { + display: flex; + flex-direction: row; + margin-right: 10px; + } +} +</style> diff --git a/src/views/ai/chat/index/components/role/RoleHeader.vue b/src/views/ai/chat/index/components/role/RoleHeader.vue new file mode 100644 index 0000000..17b1693 --- /dev/null +++ b/src/views/ai/chat/index/components/role/RoleHeader.vue @@ -0,0 +1,48 @@ +<!-- header --> +<template> + <el-header class="chat-header"> + <div class="title"> + {{ title }} + </div> + <div class="title-right"> + <slot></slot> + </div> + </el-header> +</template> + +<script setup lang="ts"> +// 设置组件属性 +defineProps({ + title: { + type: String, + required: true + } +}) +</script> + +<style scoped lang="scss"> +.chat-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0 10px; + white-space: nowrap; + text-overflow: ellipsis; + background-color: #ececec; + width: 100%; + + .title { + font-size: 20px; + font-weight: bold; + overflow: hidden; + color: #3e3e3e; + max-width: 220px; + } + + .title-right { + display: flex; + flex-direction: row; + } +} +</style> diff --git a/src/views/ai/chat/index/components/role/RoleList.vue b/src/views/ai/chat/index/components/role/RoleList.vue new file mode 100644 index 0000000..e0388d0 --- /dev/null +++ b/src/views/ai/chat/index/components/role/RoleList.vue @@ -0,0 +1,174 @@ +<template> + <div class="card-list" ref="tabsRef" @scroll="handleTabsScroll"> + <div class="card-item" v-for="role in roleList" :key="role.id"> + <el-card class="card" body-class="card-body"> + <!-- 更多操作 --> + <div class="more-container" v-if="showMore"> + <el-dropdown @command="handleMoreClick"> + <span class="el-dropdown-link"> + <el-button type="text"> + <el-icon><More /></el-icon> + </el-button> + </span> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item :command="['edit', role]"> + <Icon icon="ep:edit" color="#787878" />编辑 + </el-dropdown-item> + <el-dropdown-item :command="['delete', role]" style="color: red"> + <Icon icon="ep:delete" color="red" />删除 + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + </div> + <!-- 角色信息 --> + <div> + <img class="avatar" :src="role.avatar" /> + </div> + <div class="right-container"> + <div class="content-container"> + <div class="title">{{ role.name }}</div> + <div class="description">{{ role.description }}</div> + </div> + <div class="btn-container"> + <el-button type="primary" size="small" @click="handleUseClick(role)">使用</el-button> + </div> + </div> + </el-card> + </div> + </div> +</template> + +<script setup lang="ts"> +import { ChatRoleVO } from '@/api/ai/model/chatRole' +import { PropType, ref } from 'vue' +import { More } from '@element-plus/icons-vue' + +const tabsRef = ref<any>() // tabs ref + +// 定义属性 +const props = defineProps({ + loading: { + type: Boolean, + required: true + }, + roleList: { + type: Array as PropType<ChatRoleVO[]>, + required: true + }, + showMore: { + type: Boolean, + required: false, + default: false + } +}) + +// 定义钩子 +const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage']) + +/** 操作:编辑、删除 */ +const handleMoreClick = async (data) => { + const type = data[0] + const role = data[1] + if (type === 'delete') { + emits('onDelete', role) + } else { + emits('onEdit', role) + } +} + +/** 选中 */ +const handleUseClick = (role) => { + emits('onUse', role) +} + +/** 滚动 */ +const handleTabsScroll = async () => { + if (tabsRef.value) { + const { scrollTop, scrollHeight, clientHeight } = tabsRef.value + if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) { + await emits('onPage') + } + } +} +</script> + +<style lang="scss"> +// 重写 card 组件 body 样式 +.card-body { + max-width: 240px; + width: 240px; + padding: 15px 15px 10px 15px; + + display: flex; + flex-direction: row; + justify-content: flex-start; + position: relative; +} +</style> +<style scoped lang="scss"> +// 卡片列表 +.card-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + position: relative; + height: 100%; + overflow: auto; + padding: 0px 25px; + padding-bottom: 140px; + align-items: start; + align-content: flex-start; + justify-content: start; + + .card { + display: inline-block; + margin-right: 20px; + border-radius: 10px; + margin-bottom: 20px; + position: relative; + + .more-container { + position: absolute; + top: 0; + right: 12px; + } + + .avatar { + width: 40px; + height: 40px; + border-radius: 10px; + overflow: hidden; + } + + .right-container { + margin-left: 10px; + width: 100%; + //height: 100px; + + .content-container { + height: 85px; + + .title { + font-size: 18px; + font-weight: bold; + color: #3e3e3e; + } + + .description { + margin-top: 10px; + font-size: 14px; + color: #6a6a6a; + } + } + + .btn-container { + display: flex; + flex-direction: row-reverse; + margin-top: 2px; + } + } + } +} +</style> diff --git a/src/views/ai/chat/index/components/role/RoleRepository.vue b/src/views/ai/chat/index/components/role/RoleRepository.vue new file mode 100644 index 0000000..246dcb4 --- /dev/null +++ b/src/views/ai/chat/index/components/role/RoleRepository.vue @@ -0,0 +1,289 @@ +<!-- chat 角色仓库 --> +<template> + <el-container class="role-container"> + <ChatRoleForm ref="formRef" @success="handlerAddRoleSuccess" /> + <!-- header --> + <RoleHeader title="角色仓库" class="relative" /> + <!-- main --> + <el-main class="role-main"> + <div class="search-container"> + <!-- 搜索按钮 --> + <el-input + :loading="loading" + v-model="search" + class="search-input" + size="default" + placeholder="请输入搜索的内容" + :suffix-icon="Search" + @change="getActiveTabsRole" + /> + <el-button + v-if="activeTab == 'my-role'" + type="primary" + @click="handlerAddRole" + class="ml-20px" + > + <Icon icon="ep:user" style="margin-right: 5px;" /> + 添加角色 + </el-button> + </div> + <!-- tabs --> + <el-tabs v-model="activeTab" class="tabs" @tab-click="handleTabsClick"> + <el-tab-pane class="role-pane" label="我的角色" name="my-role"> + <RoleList + :loading="loading" + :role-list="myRoleList" + :show-more="true" + @on-delete="handlerCardDelete" + @on-edit="handlerCardEdit" + @on-use="handlerCardUse" + @on-page="handlerCardPage('my')" + class="mt-20px" + /> + </el-tab-pane> + <el-tab-pane label="公共角色" name="public-role"> + <RoleCategoryList + class="role-category-list" + :category-list="categoryList" + :active="activeCategory" + @on-category-click="handlerCategoryClick" + /> + <RoleList + :role-list="publicRoleList" + @on-delete="handlerCardDelete" + @on-edit="handlerCardEdit" + @on-use="handlerCardUse" + @on-page="handlerCardPage('public')" + class="mt-20px" + loading + /> + </el-tab-pane> + </el-tabs> + </el-main> + </el-container> +</template> + +<script setup lang="ts"> +import {ref} from 'vue' +import RoleHeader from './RoleHeader.vue' +import RoleList from './RoleList.vue' +import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue' +import RoleCategoryList from './RoleCategoryList.vue' +import {ChatRoleApi, ChatRolePageReqVO, ChatRoleVO} from '@/api/ai/model/chatRole' +import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' +import {Search} from '@element-plus/icons-vue' +import {TabsPaneContext} from 'element-plus' + +const router = useRouter() // 路由对象 + +// 属性定义 +const loading = ref<boolean>(false) // 加载中 +const activeTab = ref<string>('my-role') // 选中的角色 Tab +const search = ref<string>('') // 加载中 +const myRoleParams = reactive({ + pageNo: 1, + pageSize: 50 +}) +const myRoleList = ref<ChatRoleVO[]>([]) // my 分页大小 +const publicRoleParams = reactive({ + pageNo: 1, + pageSize: 50 +}) +const publicRoleList = ref<ChatRoleVO[]>([]) // public 分页大小 +const activeCategory = ref<string>('全部') // 选择中的分类 +const categoryList = ref<string[]>([]) // 角色分类类别 + +/** tabs 点击 */ +const handleTabsClick = async (tab: TabsPaneContext) => { + // 设置切换状态 + activeTab.value = tab.paneName + '' + // 切换的时候重新加载数据 + await getActiveTabsRole() +} + +/** 获取 my role 我的角色 */ +const getMyRole = async (append?: boolean) => { + const params: ChatRolePageReqVO = { + ...myRoleParams, + name: search.value, + publicStatus: false + } + const { list } = await ChatRoleApi.getMyPage(params) + if (append) { + myRoleList.value.push.apply(myRoleList.value, list) + } else { + myRoleList.value = list + } +} + +/** 获取 public role 公共角色 */ +const getPublicRole = async (append?: boolean) => { + const params: ChatRolePageReqVO = { + ...publicRoleParams, + category: activeCategory.value === '全部' ? '' : activeCategory.value, + name: search.value, + publicStatus: true + } + const { total, list } = await ChatRoleApi.getMyPage(params) + if (append) { + publicRoleList.value.push.apply(publicRoleList.value, list) + } else { + publicRoleList.value = list + } +} + +/** 获取选中的 tabs 角色 */ +const getActiveTabsRole = async () => { + if (activeTab.value === 'my-role') { + myRoleParams.pageNo = 1 + await getMyRole() + } else { + publicRoleParams.pageNo = 1 + await getPublicRole() + } +} + +/** 获取角色分类列表 */ +const getRoleCategoryList = async () => { + categoryList.value = ['全部', ...(await ChatRoleApi.getCategoryList())] +} + +/** 处理分类点击 */ +const handlerCategoryClick = async (category: string) => { + // 切换选择的分类 + activeCategory.value = category + // 筛选 + await getActiveTabsRole() +} + +/** 添加/修改操作 */ +const formRef = ref() +const handlerAddRole = async () => { + formRef.value.open('my-create', null, '添加角色') +} +/** 编辑角色 */ +const handlerCardEdit = async (role) => { + formRef.value.open('my-update', role.id, '编辑角色') +} + +/** 添加角色成功 */ +const handlerAddRoleSuccess = async (e) => { + // 刷新数据 + await getActiveTabsRole() +} + +/** 删除角色 */ +const handlerCardDelete = async (role) => { + await ChatRoleApi.deleteMy(role.id) + // 刷新数据 + await getActiveTabsRole() +} + +/** 角色分页:获取下一页 */ +const handlerCardPage = async (type) => { + try { + loading.value = true + if (type === 'public') { + publicRoleParams.pageNo++ + await getPublicRole(true) + } else { + myRoleParams.pageNo++ + await getMyRole(true) + } + } finally { + loading.value = false + } +} + +/** 选择 card 角色:新建聊天对话 */ +const handlerCardUse = async (role) => { + // 1. 创建对话 + const data: ChatConversationVO = { + roleId: role.id + } as unknown as ChatConversationVO + const conversationId = await ChatConversationApi.createChatConversationMy(data) + + // 2. 跳转页面 + await router.push({ + name: 'AiChat', + query: { + conversationId: conversationId + } + }) +} + +/** 初始化 **/ +onMounted(async () => { + // 获取分类 + await getRoleCategoryList() + // 获取 role 数据 + await getActiveTabsRole() +}) +</script> +<!-- 覆盖 element ui css --> +<style lang="scss"> +.el-tabs__content { + position: relative; + height: 100%; + overflow: hidden; +} +.el-tabs__nav-scroll { + margin: 10px 20px; +} +</style> +<!-- 样式 --> +<style scoped lang="scss"> +// 跟容器 +.role-container { + position: absolute; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + left: 0; + right: 0; + top: 0; + bottom: 0; + background-color: #ffffff; + overflow: hidden; + display: flex; + flex-direction: column; + + .role-main { + flex: 1; + overflow: hidden; + margin: 0; + padding: 0; + position: relative; + + .search-container { + margin: 20px 20px 0px 20px; + position: absolute; + right: 0; + top: -5px; + z-index: 100; + } + + .search-input { + width: 240px; + } + + .tabs { + position: relative; + height: 100%; + + .role-category-list { + margin: 0 27px; + } + } + + .role-pane { + display: flex; + flex-direction: column; + height: 100%; + overflow-y: auto; + position: relative; + } + } +} +</style> diff --git a/src/views/ai/chat/index/index.vue b/src/views/ai/chat/index/index.vue new file mode 100644 index 0000000..e6efef0 --- /dev/null +++ b/src/views/ai/chat/index/index.vue @@ -0,0 +1,773 @@ +<template> + <el-container class="ai-layout"> + <!-- 左侧:对话列表 --> + <ConversationList + :active-id="activeConversationId" + ref="conversationListRef" + @on-conversation-create="handleConversationCreateSuccess" + @on-conversation-click="handleConversationClick" + @on-conversation-clear="handleConversationClear" + @on-conversation-delete="handlerConversationDelete" + /> + <!-- 右侧:对话详情 --> + <el-container class="detail-container"> + <el-header class="header"> + <div class="title"> + {{ activeConversation?.title ? activeConversation?.title : '对话' }} + <span v-if="activeMessageList.length">({{ activeMessageList.length }})</span> + </div> + <div class="btns" v-if="activeConversation"> + <el-button type="primary" bg plain size="small" @click="openChatConversationUpdateForm"> + <span v-html="activeConversation?.modelName"></span> + <Icon icon="ep:setting" class="ml-10px" /> + </el-button> + <el-button size="small" class="btn" @click="handlerMessageClear"> + <Icon icon="heroicons-outline:archive-box-x-mark" color="#787878" /> + </el-button> + <el-button size="small" class="btn"> + <Icon icon="ep:download" color="#787878" /> + </el-button> + <el-button size="small" class="btn" @click="handleGoTopMessage"> + <Icon icon="ep:top" color="#787878" /> + </el-button> + </div> + </el-header> + + <!-- main:消息列表 --> + <el-main class="main-container"> + <div> + <div class="message-container"> + <!-- 情况一:消息加载中 --> + <MessageLoading v-if="activeMessageListLoading" /> + <!-- 情况二:无聊天对话时 --> + <MessageNewConversation + v-if="!activeConversation" + @on-new-conversation="handleConversationCreate" + /> + <!-- 情况三:消息列表为空 --> + <MessageListEmpty + v-if="!activeMessageListLoading && messageList.length === 0 && activeConversation" + @on-prompt="doSendMessage" + /> + <!-- 情况四:消息列表不为空 --> + <MessageList + v-if="!activeMessageListLoading && messageList.length > 0" + ref="messageRef" + :conversation="activeConversation" + :list="messageList" + @on-delete-success="handleMessageDelete" + @on-edit="handleMessageEdit" + @on-refresh="handleMessageRefresh" + /> + </div> + </div> + </el-main> + + <!-- 底部 --> + <el-footer class="footer-container"> + <form class="prompt-from"> + <textarea + class="prompt-input" + v-model="prompt" + @keydown="handleSendByKeydown" + @input="handlePromptInput" + @compositionstart="onCompositionstart" + @compositionend="onCompositionend" + placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)" + ></textarea> + <div class="prompt-btns"> + <div> + <el-switch v-model="enableContext" /> + <span class="ml-5px text-14px text-#8f8f8f">上下文</span> + </div> + <el-button + type="primary" + size="default" + @click="handleSendByButton" + :loading="conversationInProgress" + v-if="conversationInProgress == false" + > + {{ conversationInProgress ? '进行中' : '发送' }} + </el-button> + <el-button + type="danger" + size="default" + @click="stopStream()" + v-if="conversationInProgress == true" + > + 停止 + </el-button> + </div> + </form> + </el-footer> + </el-container> + + <!-- 更新对话 Form --> + <ConversationUpdateForm + ref="conversationUpdateFormRef" + @success="handleConversationUpdateSuccess" + /> + </el-container> +</template> + +<script setup lang="ts"> +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import ConversationList from './components/conversation/ConversationList.vue' +import ConversationUpdateForm from './components/conversation/ConversationUpdateForm.vue' +import MessageList from './components/message/MessageList.vue' +import MessageListEmpty from './components/message/MessageListEmpty.vue' +import MessageLoading from './components/message/MessageLoading.vue' +import MessageNewConversation from './components/message/MessageNewConversation.vue' + +/** AI 聊天对话 列表 */ +defineOptions({ name: 'AiChat' }) + +const route = useRoute() // 路由 +const message = useMessage() // 消息弹窗 + +// 聊天对话 +const conversationListRef = ref() +const activeConversationId = ref<number | null>(null) // 选中的对话编号 +const activeConversation = ref<ChatConversationVO | null>(null) // 选中的 Conversation +const conversationInProgress = ref(false) // 对话是否正在进行中。目前只有【发送】消息时,会更新为 true,避免切换对话、删除对话等操作 + +// 消息列表 +const messageRef = ref() +const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表 +const activeMessageListLoading = ref<boolean>(false) // activeMessageList 是否正在加载中 +const activeMessageListLoadingTimer = ref<any>() // activeMessageListLoading Timer 定时器。如果加载速度很快,就不进入加载中 +// 消息滚动 +const textSpeed = ref<number>(50) // Typing speed in milliseconds +const textRoleRunning = ref<boolean>(false) // Typing speed in milliseconds + +// 发送消息输入框 +const isComposing = ref(false) // 判断用户是否在输入 +const conversationInAbortController = ref<any>() // 对话进行中 abort 控制器(控制 stream 对话) +const inputTimeout = ref<any>() // 处理输入中回车的定时器 +const prompt = ref<string>() // prompt +const enableContext = ref<boolean>(true) // 是否开启上下文 +// 接收 Stream 消息 +const receiveMessageFullText = ref('') +const receiveMessageDisplayedText = ref('') + + +// =========== 【聊天对话】相关 =========== + +/** 获取对话信息 */ +const getConversation = async (id: number | null) => { + if (!id) { + return + } + const conversation: ChatConversationVO = await ChatConversationApi.getChatConversationMy(id) + if (!conversation) { + return + } + activeConversation.value = conversation + activeConversationId.value = conversation.id +} + +/** + * 点击某个对话 + * + * @param conversation 选中的对话 + * @return 是否切换成功 + */ +const handleConversationClick = async (conversation: ChatConversationVO) => { + // 对话进行中,不允许切换 + if (conversationInProgress.value) { + message.alert('对话中,不允许切换!') + return false + } + + // 更新选中的对话 id + activeConversationId.value = conversation.id + activeConversation.value = conversation + // 刷新 message 列表 + await getMessageList() + // 滚动底部 + scrollToBottom(true) + // 清空输入框 + prompt.value = '' + return true +} + +/** 删除某个对话*/ +const handlerConversationDelete = async (delConversation: ChatConversationVO) => { + // 删除的对话如果是当前选中的,那么就重置 + if (activeConversationId.value === delConversation.id) { + await handleConversationClear() + } +} +/** 清空选中的对话 */ +const handleConversationClear = async () => { + // 对话进行中,不允许切换 + if (conversationInProgress.value) { + message.alert('对话中,不允许切换!') + return false + } + activeConversationId.value = null + activeConversation.value = null + activeMessageList.value = [] +} + +/** 修改聊天对话 */ +const conversationUpdateFormRef = ref() +const openChatConversationUpdateForm = async () => { + conversationUpdateFormRef.value.open(activeConversationId.value) +} +const handleConversationUpdateSuccess = async () => { + // 对话更新成功,刷新最新信息 + await getConversation(activeConversationId.value) +} + +/** 处理聊天对话的创建成功 */ +const handleConversationCreate = async () => { + // 创建对话 + await conversationListRef.value.createConversation() +} +/** 处理聊天对话的创建成功 */ +const handleConversationCreateSuccess = async () => { + // 创建新的对话,清空输入框 + prompt.value = '' +} + +// =========== 【消息列表】相关 =========== + +/** 获取消息 message 列表 */ +const getMessageList = async () => { + try { + if (activeConversationId.value === null) { + return + } + // Timer 定时器,如果加载速度很快,就不进入加载中 + activeMessageListLoadingTimer.value = setTimeout(() => { + activeMessageListLoading.value = true + }, 60) + + // 获取消息列表 + activeMessageList.value = await ChatMessageApi.getChatMessageListByConversationId( + activeConversationId.value + ) + + // 滚动到最下面 + await nextTick() + await scrollToBottom() + } finally { + // time 定时器,如果加载速度很快,就不进入加载中 + if (activeMessageListLoadingTimer.value) { + clearTimeout(activeMessageListLoadingTimer.value) + } + // 加载结束 + activeMessageListLoading.value = false + } +} + +/** + * 消息列表 + * + * 和 {@link #getMessageList()} 的差异是,把 systemMessage 考虑进去 + */ +const messageList = computed(() => { + if (activeMessageList.value.length > 0) { + return activeMessageList.value + } + // 没有消息时,如果有 systemMessage 则展示它 + if (activeConversation.value?.systemMessage) { + return [ + { + id: 0, + type: 'system', + content: activeConversation.value.systemMessage + } + ] + } + return [] +}) + +/** 处理删除 message 消息 */ +const handleMessageDelete = () => { + if (conversationInProgress.value) { + message.alert('回答中,不能删除!') + return + } + // 刷新 message 列表 + getMessageList() +} + +/** 处理 message 清空 */ +const handlerMessageClear = async () => { + if (!activeConversationId.value) { + return + } + try { + // 确认提示 + await message.delConfirm('确认清空对话消息?') + // 清空对话 + await ChatMessageApi.deleteByConversationId(activeConversationId.value) + // 刷新 message 列表 + activeMessageList.value = [] + } catch {} +} + +/** 回到 message 列表的顶部 */ +const handleGoTopMessage = () => { + messageRef.value.handlerGoTop() +} + +// =========== 【发送消息】相关 =========== + +/** 处理来自 keydown 的发送消息 */ +const handleSendByKeydown = async (event) => { + // 判断用户是否在输入 + if (isComposing.value) { + return + } + // 进行中不允许发送 + if (conversationInProgress.value) { + return + } + const content = prompt.value?.trim() as string + if (event.key === 'Enter') { + if (event.shiftKey) { + // 插入换行 + prompt.value += '\r\n' + event.preventDefault() // 防止默认的换行行为 + } else { + // 发送消息 + await doSendMessage(content) + event.preventDefault() // 防止默认的提交行为 + } + } +} + +/** 处理来自【发送】按钮的发送消息 */ +const handleSendByButton = () => { + doSendMessage(prompt.value?.trim() as string) +} + +/** 处理 prompt 输入变化 */ +const handlePromptInput = (event) => { + // 非输入法 输入设置为 true + if (!isComposing.value) { + // 回车 event data 是 null + if (event.data == null) { + return + } + isComposing.value = true + } + // 清理定时器 + if (inputTimeout.value) { + clearTimeout(inputTimeout.value) + } + // 重置定时器 + inputTimeout.value = setTimeout(() => { + isComposing.value = false + }, 400) +} +// TODO @芋艿:是不是可以通过 @keydown.enter、@keydown.shift.enter 来实现,回车发送、shift+回车换行;主要看看,是不是可以简化 isComposing 相关的逻辑 +const onCompositionstart = () => { + isComposing.value = true +} +const onCompositionend = () => { + // console.log('输入结束...') + setTimeout(() => { + isComposing.value = false + }, 200) +} + +/** 真正执行【发送】消息操作 */ +const doSendMessage = async (content: string) => { + // 校验 + if (content.length < 1) { + message.error('发送失败,原因:内容为空!') + return + } + if (activeConversationId.value == null) { + message.error('还没创建对话,不能发送!') + return + } + // 清空输入框 + prompt.value = '' + // 执行发送 + await doSendMessageStream({ + conversationId: activeConversationId.value, + content: content + } as ChatMessageVO) +} + +/** 真正执行【发送】消息操作 */ +const doSendMessageStream = async (userMessage: ChatMessageVO) => { + // 创建 AbortController 实例,以便中止请求 + conversationInAbortController.value = new AbortController() + // 标记对话进行中 + conversationInProgress.value = true + // 设置为空 + receiveMessageFullText.value = '' + + try { + // 1.1 先添加两个假数据,等 stream 返回再替换 + activeMessageList.value.push({ + id: -1, + conversationId: activeConversationId.value, + type: 'user', + content: userMessage.content, + createTime: new Date() + } as ChatMessageVO) + activeMessageList.value.push({ + id: -2, + conversationId: activeConversationId.value, + type: 'assistant', + content: '思考中...', + createTime: new Date() + } as ChatMessageVO) + // 1.2 滚动到最下面 + await nextTick() + await scrollToBottom() // 底部 + // 1.3 开始滚动 + textRoll() + + // 2. 发送 event stream + let isFirstChunk = true // 是否是第一个 chunk 消息段 + await ChatMessageApi.sendChatMessageStream( + userMessage.conversationId, + userMessage.content, + conversationInAbortController.value, + enableContext.value, + async (res) => { + const { code, data, msg } = JSON.parse(res.data) + if (code !== 0) { + message.alert(`对话异常! ${msg}`) + return + } + + // 如果内容为空,就不处理。 + if (data.receive.content === '') { + return + } + // 首次返回需要添加一个 message 到页面,后面的都是更新 + if (isFirstChunk) { + isFirstChunk = false + // 弹出两个假数据 + activeMessageList.value.pop() + activeMessageList.value.pop() + // 更新返回的数据 + activeMessageList.value.push(data.send) + activeMessageList.value.push(data.receive) + } + // debugger + receiveMessageFullText.value = receiveMessageFullText.value + data.receive.content + // 滚动到最下面 + await scrollToBottom() + }, + (error) => { + message.alert(`对话异常! ${error}`) + stopStream() + }, + () => { + stopStream() + } + ) + } catch {} +} + +/** 停止 stream 流式调用 */ +const stopStream = async () => { + // tip:如果 stream 进行中的 message,就需要调用 controller 结束 + if (conversationInAbortController.value) { + conversationInAbortController.value.abort() + } + // 设置为 false + conversationInProgress.value = false +} + +/** 编辑 message:设置为 prompt,可以再次编辑 */ +const handleMessageEdit = (message: ChatMessageVO) => { + prompt.value = message.content +} + +/** 刷新 message:基于指定消息,再次发起对话 */ +const handleMessageRefresh = (message: ChatMessageVO) => { + doSendMessage(message.content) +} + +// ============== 【消息滚动】相关 ============= + +/** 滚动到 message 底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + await nextTick() + if (messageRef.value) { + messageRef.value.scrollToBottom(isIgnore) + } +} + +/** 自提滚动效果 */ +const textRoll = async () => { + let index = 0 + try { + // 只能执行一次 + if (textRoleRunning.value) { + return + } + // 设置状态 + textRoleRunning.value = true + receiveMessageDisplayedText.value = '' + const task = async () => { + // 调整速度 + const diff = + (receiveMessageFullText.value.length - receiveMessageDisplayedText.value.length) / 10 + if (diff > 5) { + textSpeed.value = 10 + } else if (diff > 2) { + textSpeed.value = 30 + } else if (diff > 1.5) { + textSpeed.value = 50 + } else { + textSpeed.value = 100 + } + // 对话结束,就按 30 的速度 + if (!conversationInProgress.value) { + textSpeed.value = 10 + } + + if (index < receiveMessageFullText.value.length) { + receiveMessageDisplayedText.value += receiveMessageFullText.value[index] + index++ + + // 更新 message + const lastMessage = activeMessageList.value[activeMessageList.value.length - 1] + lastMessage.content = receiveMessageDisplayedText.value + // 滚动到住下面 + await scrollToBottom() + // 重新设置任务 + timer = setTimeout(task, textSpeed.value) + } else { + // 不是对话中可以结束 + if (!conversationInProgress.value) { + textRoleRunning.value = false + clearTimeout(timer) + } else { + // 重新设置任务 + timer = setTimeout(task, textSpeed.value) + } + } + } + let timer = setTimeout(task, textSpeed.value) + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + // 如果有 conversationId 参数,则默认选中 + if (route.query.conversationId) { + const id = route.query.conversationId as unknown as number + activeConversationId.value = id + await getConversation(id) + } + + // 获取列表数据 + activeMessageListLoading.value = true + await getMessageList() +}) +</script> + +<style lang="scss" scoped> +.ai-layout { + position: absolute; + flex: 1; + top: 0; + left: 0; + height: 100%; + width: 100%; +} + +.conversation-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px 10px 0; + + .btn-new-conversation { + padding: 18px 0; + } + + .search-input { + margin-top: 20px; + } + + .conversation-list { + margin-top: 20px; + + .conversation { + display: flex; + flex-direction: row; + justify-content: space-between; + flex: 1; + padding: 0 5px; + margin-top: 10px; + cursor: pointer; + border-radius: 5px; + align-items: center; + line-height: 30px; + + &.active { + background-color: #e6e6e6; + + .button { + display: inline-block; + } + } + + .title-wrapper { + display: flex; + flex-direction: row; + align-items: center; + } + + .title { + padding: 5px 10px; + max-width: 220px; + font-size: 14px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .avatar { + width: 28px; + height: 28px; + display: flex; + flex-direction: row; + justify-items: center; + } + + // 对话编辑、删除 + .button-wrapper { + right: 2px; + display: flex; + flex-direction: row; + justify-items: center; + color: #606266; + + .el-icon { + margin-right: 5px; + } + } + } + } + + // 角色仓库、清空未设置对话 + .tool-box { + line-height: 35px; + display: flex; + justify-content: space-between; + align-items: center; + color: var(--el-text-color); + + > div { + display: flex; + align-items: center; + color: #606266; + padding: 0; + margin: 0; + cursor: pointer; + + > span { + margin-left: 5px; + } + } + } +} + +// 头部 +.detail-container { + background: #ffffff; + + .header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + background: #fbfbfb; + box-shadow: 0 0 0 0 #dcdfe6; + + .title { + font-size: 18px; + font-weight: bold; + } + + .btns { + display: flex; + width: 300px; + flex-direction: row; + justify-content: flex-end; + //justify-content: space-between; + + .btn { + padding: 10px; + } + } + } +} + +// main 容器 +.main-container { + margin: 0; + padding: 0; + position: relative; + height: 100%; + width: 100%; + + .message-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: hidden; + padding: 0; + margin: 0; + } +} + +// 底部 +.footer-container { + display: flex; + flex-direction: column; + height: auto; + margin: 0; + padding: 0; + + .prompt-from { + display: flex; + flex-direction: column; + height: auto; + border: 1px solid #e3e3e3; + border-radius: 10px; + margin: 10px 20px 20px 20px; + padding: 9px 10px; + } + + .prompt-input { + height: 80px; + //box-shadow: none; + border: none; + box-sizing: border-box; + resize: none; + padding: 0 2px; + overflow: auto; + } + + .prompt-input:focus { + outline: none; + } + + .prompt-btns { + display: flex; + justify-content: space-between; + padding-bottom: 0; + padding-top: 5px; + } +} +</style> diff --git a/src/views/ai/chat/manager/ChatConversationList.vue b/src/views/ai/chat/manager/ChatConversationList.vue new file mode 100644 index 0000000..23933f0 --- /dev/null +++ b/src/views/ai/chat/manager/ChatConversationList.vue @@ -0,0 +1,163 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户编号" prop="userId"> + <el-select + v-model="queryParams.userId" + clearable + placeholder="请输入用户编号" + class="!w-240px" + > + <el-option + v-for="item in userList" + :key="item.id" + :label="item.nickname" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="聊天编号" prop="title"> + <el-input + v-model="queryParams.title" + placeholder="请输入聊天编号" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="对话编号" align="center" prop="id" width="180" fixed="left" /> + <el-table-column label="对话标题" align="center" prop="title" width="180" fixed="left" /> + <el-table-column label="用户" align="center" prop="userId" width="180"> + <template #default="scope"> + <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span> + </template> + </el-table-column> + <el-table-column label="角色" align="center" prop="roleName" width="180" /> + <el-table-column label="模型标识" align="center" prop="model" width="180" /> + <el-table-column label="消息数" align="center" prop="messageCount" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="温度参数" align="center" prop="temperature" /> + <el-table-column label="回复 Token 数" align="center" prop="maxTokens" width="120" /> + <el-table-column label="上下文数量" align="center" prop="maxContexts" width="120" /> + <el-table-column label="操作" align="center" width="180" fixed="right"> + <template #default="scope"> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:chat-conversation:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import * as UserApi from '@/api/system/user' + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ChatConversationVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + userId: undefined, + title: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const userList = ref<UserApi.UserVO[]>([]) // 用户列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ChatConversationApi.getChatConversationPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ChatConversationApi.deleteChatConversationByAdmin(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + getList() + // 获得用户列表 + userList.value = await UserApi.getSimpleUserList() +}) +</script> diff --git a/src/views/ai/chat/manager/ChatMessageList.vue b/src/views/ai/chat/manager/ChatMessageList.vue new file mode 100644 index 0000000..0d84184 --- /dev/null +++ b/src/views/ai/chat/manager/ChatMessageList.vue @@ -0,0 +1,175 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="对话编号" prop="conversationId"> + <el-input + v-model="queryParams.conversationId" + placeholder="请输入对话编号" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="用户编号" prop="userId"> + <el-select + v-model="queryParams.userId" + clearable + placeholder="请输入用户编号" + class="!w-240px" + > + <el-option + v-for="item in userList" + :key="item.id" + :label="item.nickname" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="消息编号" align="center" prop="id" width="180" fixed="left" /> + <el-table-column + label="对话编号" + align="center" + prop="conversationId" + width="180" + fixed="left" + /> + <el-table-column label="用户" align="center" prop="userId" width="180"> + <template #default="scope"> + <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span> + </template> + </el-table-column> + <el-table-column label="角色" align="center" prop="roleName" width="180" /> + <el-table-column label="消息类型" align="center" prop="type" width="100" /> + <el-table-column label="模型标识" align="center" prop="model" width="180" /> + <el-table-column label="消息内容" align="center" prop="content" width="300" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="回复消息编号" align="center" prop="replyId" width="180" /> + <el-table-column label="携带上下文" align="center" prop="useContext" width="100"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.useContext" /> + </template> + </el-table-column> + <el-table-column label="操作" align="center" fixed="right"> + <template #default="scope"> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:chat-message:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import * as UserApi from '@/api/system/user' +import { DICT_TYPE } from '@/utils/dict' + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ChatMessageVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + conversationId: undefined, + userId: undefined, + content: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const userList = ref<UserApi.UserVO[]>([]) // 用户列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ChatMessageApi.getChatMessagePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ChatMessageApi.deleteChatMessageByAdmin(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + getList() + // 获得用户列表 + userList.value = await UserApi.getSimpleUserList() +}) +</script> diff --git a/src/views/ai/chat/manager/index.vue b/src/views/ai/chat/manager/index.vue new file mode 100644 index 0000000..984961b --- /dev/null +++ b/src/views/ai/chat/manager/index.vue @@ -0,0 +1,21 @@ +<template> + + <ContentWrap> + <el-tabs> + <el-tab-pane label="对话列表"> + <ChatConversationList /> + </el-tab-pane> + <el-tab-pane label="消息列表"> + <ChatMessageList /> + </el-tab-pane> + </el-tabs> + </ContentWrap> +</template> + +<script setup lang="ts"> +import ChatConversationList from './ChatConversationList.vue' +import ChatMessageList from './ChatMessageList.vue' + +/** AI 聊天对话 列表 */ +defineOptions({ name: 'AiChatManager' }) +</script> diff --git a/src/views/ai/dashboard/components/conversation/CommonConversation.vue b/src/views/ai/dashboard/components/conversation/CommonConversation.vue new file mode 100644 index 0000000..2de4e77 --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/CommonConversation.vue @@ -0,0 +1,964 @@ +<template> + <!-- 整体容器添加相对定位 --> + <div class="container-wrapper" ref="containerRef"> + <!-- 折叠按钮 --> + <div + class="sidebar-toggle" + :style="{ left: toggleLeft }" + @click.stop="toggleSidebar" + ref="toggleRef"> + <Icon :icon="isCollapsed ? 'ep:caret-right' : 'ep:caret-left'" /> + </div> + + <!-- 左侧对话列表 --> + <div + class="conversation-wrapper" + :class="{ collapsed: isCollapsed }" + :style="{ width: sidebarWidth }" + ref="sidebarRef"> + <ConversationList + :active-id="activeConversationId" + ref="conversationListRef" + @on-conversation-create="handleConversationCreateSuccess" + @on-conversation-click="handleConversationClick" + @on-conversation-clear="handleConversationClear" + @on-conversation-delete="handlerConversationDelete" + /> + </div> + <!-- 右侧:对话详情 --> + <el-container class="detail-container"> + <el-header class="header"> + <div class="title"> + {{ activeConversation?.title ? activeConversation?.title : '对话' }} + <span v-if="activeMessageList.length">({{ activeMessageList.length }})</span> + </div> + <div class="btns" v-if="activeConversation"> + <el-button type="primary" bg plain size="small" @click="openChatConversationUpdateForm"> + <span v-html="activeConversation?.modelName"></span> + <Icon icon="ep:setting" class="ml-10px" /> + </el-button> + <el-button size="small" class="btn" @click="handlerMessageClear"> + <Icon icon="heroicons-outline:archive-box-x-mark" color="#73C4FF" /> + </el-button> + <el-button size="small" class="btn" @click="handleGoBottomMessage"> + <Icon icon="ep:download" color="#73C4FF" /> + </el-button> + <el-button size="small" class="btn" @click="handleGoTopMessage"> + <Icon icon="ep:top" color="#73C4FF" /> + </el-button> + </div> + </el-header> + + <!-- main:消息列表 --> + <el-main class="main-container"> + <div class="message-container"> + <!-- 情况一:消息加载中 --> + <MessageLoading v-if="activeMessageListLoading" /> + <!-- 情况二:无聊天对话时 --> + <MessageNewConversation + v-if="!activeConversation" + @on-new-conversation="handleConversationCreate" + /> + <!-- 情况三:消息列表为空 --> + <MessageListEmpty + v-if="!activeMessageListLoading && messageList.length === 0 && activeConversation" + @on-prompt="doSendMessage" + /> + <!-- 情况四:消息列表不为空 --> + <MessageList + v-if="!activeMessageListLoading && messageList.length > 0" + ref="messageRef" + :conversation="activeConversation" + :list="messageList" + @on-delete-success="handleMessageDelete" + @on-edit="handleMessageEdit" + @on-refresh="handleMessageRefresh" + /> + </div> + </el-main> + + <!-- 底部 --> + <el-footer class="footer-container"> + <!-- 输入框 --> + <div class="input-container"> + <form class="prompt-from"> + <textarea + class="prompt-input" + v-model="prompt" + @keydown="handleSendByKeydown" + @input="handlePromptInput" + @compositionstart="onCompositionstart" + @compositionend="onCompositionend" + placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)" + ></textarea> + <div class="prompt-btns"> + <div class="content"> + <el-button + :class="{ 'active-button': enableContext }" + @click="enableContext = !enableContext" + > + <el-icon class="content-icon" /> + 上下文 + </el-button> + </div> + <div class="message"> + <el-button + type="primary" + size="default" + @click="handleSendByButton" + :loading="conversationInProgress" + v-if="conversationInProgress == false" + > + {{ conversationInProgress ? '进行中' : '发消息' }} + </el-button> + <el-button + type="danger" + size="default" + @click="stopStream()" + v-if="conversationInProgress == true" + > + 停止 + </el-button> + </div> + </div> + </form> + </div> + </el-footer> + </el-container> + </div> + <!-- 更新对话 Form --> + <ConversationUpdateForm + ref="conversationUpdateFormRef" + @success="handleConversationUpdateSuccess" + /> +</template> + +<script setup lang="ts"> +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import ConversationList from './CommonConversationList.vue' +import ConversationUpdateForm from './CommonConversationUpdateForm.vue' +import MessageList from '../message/MessageList.vue' +import MessageListEmpty from '../message/MessageListEmpty.vue' +import MessageLoading from '../message/MessageLoading.vue' +import MessageNewConversation from '../message/MessageNewConversation.vue' +import { onClickOutside } from '@vueuse/core' +import * as authUtil from "@/utils/auth"; +import {refreshToken} from "@/api/login"; + +/** AI 聊天对话 列表 */ +defineOptions({ name: 'NormalConversation' }) + +const route = useRoute() // 路由 +const message = useMessage() // 消息弹窗 + +const isCollapsed = ref(true) +const sidebarWidth = ref('270px') +const toggleLeft = computed(() => isCollapsed.value ? '0' : sidebarWidth.value) + +// 新增DOM引用用于会话列表的展开和收缩 +const containerRef = ref<HTMLElement>() +const sidebarRef = ref<HTMLElement>() +const toggleRef = ref<HTMLElement>() + +// 点击外部区域处理 +onClickOutside(sidebarRef, (event) => { + if (!isCollapsed.value && + !sidebarRef.value?.contains(event.target) && + !toggleRef.value?.contains(event.target)) { + isCollapsed.value = true + } +}) + +// 切换侧边栏 +const toggleSidebar = () => { + isCollapsed.value = !isCollapsed.value +} + +// 聊天对话 +const conversationListRef = ref() +const activeConversationId = ref<number | null>(null) // 选中的对话编号 +const activeConversation = ref<ChatConversationVO | null>(null) // 选中的 Conversation +const conversationInProgress = ref(false) // 对话是否正在进行中。目前只有【发送】消息时,会更新为 true,避免切换对话、删除对话等操作 + +// 消息列表 +const messageRef = ref() +const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表 +const activeMessageListLoading = ref<boolean>(false) // activeMessageList 是否正在加载中 +const activeMessageListLoadingTimer = ref<any>() // activeMessageListLoading Timer 定时器。如果加载速度很快,就不进入加载中 +// 消息滚动 +const textSpeed = ref<number>(50) // Typing speed in milliseconds +const textRoleRunning = ref<boolean>(false) // Typing speed in milliseconds + +// 发送消息输入框 +const isComposing = ref(false) // 判断用户是否在输入 +const conversationInAbortController = ref<any>() // 对话进行中 abort 控制器(控制 stream 对话) +const inputTimeout = ref<any>() // 处理输入中回车的定时器 +const prompt = ref<string>() // prompt +const enableContext = ref<boolean>(true) // 是否开启上下文 +// 接收 Stream 消息 +const receiveMessageFullText = ref('') +const receiveMessageDisplayedText = ref('') + + +// =========== 【聊天对话】相关 =========== + +/** 获取对话信息 */ +const getConversation = async (id: number | null) => { + if (!id) { + return + } + const conversation: ChatConversationVO = await ChatConversationApi.getChatConversationMy(id) + if (!conversation) { + return + } + activeConversation.value = conversation + activeConversationId.value = conversation.id +} + +/** + * 点击某个对话 + * + * @param conversation 选中的对话 + * @return 是否切换成功 + */ +const handleConversationClick = async (conversation: ChatConversationVO) => { + // 对话进行中,不允许切换 + if (conversationInProgress.value) { + message.alert('对话中,不允许切换!') + return false + } + + // 更新选中的对话 id + activeConversationId.value = conversation.id + activeConversation.value = conversation + // 刷新 message 列表 + await getMessageList() + // 滚动底部 + scrollToBottom(true) + // 清空输入框 + prompt.value = '' + return true +} + +/** 删除某个对话*/ +const handlerConversationDelete = async (delConversation: ChatConversationVO) => { + // 删除的对话如果是当前选中的,那么就重置 + if (activeConversationId.value === delConversation.id) { + await handleConversationClear() + } +} +/** 清空选中的对话 */ +const handleConversationClear = async () => { + // 对话进行中,不允许切换 + if (conversationInProgress.value) { + message.alert('对话中,不允许切换!') + return false + } + activeConversationId.value = null + activeConversation.value = null + activeMessageList.value = [] +} + +/** 修改聊天对话 */ +const conversationUpdateFormRef = ref() +const openChatConversationUpdateForm = async () => { + conversationUpdateFormRef.value.open(activeConversationId.value) +} +const handleConversationUpdateSuccess = async () => { + // 对话更新成功,刷新最新信息 + await getConversation(activeConversationId.value) +} + +/** 处理聊天对话的创建成功 */ +const handleConversationCreate = async () => { + // 创建对话 + await conversationListRef.value.createConversation() +} +/** 处理聊天对话的创建成功 */ +const handleConversationCreateSuccess = async () => { + // 创建新的对话,清空输入框 + prompt.value = '' +} + +// =========== 【消息列表】相关 =========== + +/** 获取消息 message 列表 */ +const getMessageList = async () => { + try { + if (activeConversationId.value === null) { + return + } + // Timer 定时器,如果加载速度很快,就不进入加载中 + activeMessageListLoadingTimer.value = setTimeout(() => { + activeMessageListLoading.value = true + }, 60) + + // 获取消息列表 + activeMessageList.value = await ChatMessageApi.getChatMessageListByConversationId( + activeConversationId.value + ) + + // 滚动到最下面 + await nextTick() + await scrollToBottom() + } finally { + // time 定时器,如果加载速度很快,就不进入加载中 + if (activeMessageListLoadingTimer.value) { + clearTimeout(activeMessageListLoadingTimer.value) + } + // 加载结束 + activeMessageListLoading.value = false + } +} + +/** + * 消息列表 + * + * 和 {@link #getMessageList()} 的差异是,把 systemMessage 考虑进去 + */ +const messageList = computed(() => { + if (activeMessageList.value.length > 0) { + dealResult(activeMessageList.value) + return activeMessageList.value + } + // 没有消息时,如果有 systemMessage 则展示它 + if (activeConversation.value?.systemMessage) { + return [ + { + id: 0, + type: 'system', + content: activeConversation.value.systemMessage + } + ] + } + return [] +}) + +//处理调度推理结论 +const dealResult = (conversations: any) => { + const regex = /<think>(\n*)([\s\S]*?)(\n*)<\/think>(\n*)([\s\S]*)/; + conversations.forEach((conversation) => { + if(conversation.content.includes('<\/think>')) { + conversation.thinkingFlag = false + } else { + conversation.thinkingFlag = true + } + const match = conversation.content.match(regex); + if(match) { + conversation.thinking = match[2]; + conversation.conclusion = match[5] + } + }) +} + +/** 处理删除 message 消息 */ +const handleMessageDelete = () => { + if (conversationInProgress.value) { + message.alert('回答中,不能删除!') + return + } + // 刷新 message 列表 + getMessageList() +} + +/** 处理 message 清空 */ +const handlerMessageClear = async () => { + if (!activeConversationId.value) { + return + } + try { + // 确认提示 + await message.delConfirm('确认清空对话消息?') + // 清空对话 + await ChatMessageApi.deleteByConversationId(activeConversationId.value) + // 刷新 message 列表 + activeMessageList.value = [] + } catch {} +} + +/** 回到 message 列表的顶部 */ +const handleGoTopMessage = () => { + messageRef.value.handlerGoTop() +} + +/** 回到 message 列表的底部 */ +const handleGoBottomMessage = () => { + messageRef.value.handleGoBottom() +} + +// =========== 【发送消息】相关 =========== + +/** 处理来自 keydown 的发送消息 */ +const handleSendByKeydown = async (event) => { + // 判断用户是否在输入 + if (isComposing.value) { + return + } + // 进行中不允许发送 + if (conversationInProgress.value) { + return + } + const content = prompt.value?.trim() as string + if (event.key === 'Enter') { + if (event.shiftKey) { + // 插入换行 + prompt.value += '\r\n' + event.preventDefault() // 防止默认的换行行为 + } else { + // 发送消息 + await doSendMessage(content) + event.preventDefault() // 防止默认的提交行为 + } + } +} + +/** 处理来自【发送】按钮的发送消息 */ +const handleSendByButton = () => { + doSendMessage(prompt.value?.trim() as string) +} + +/** 处理 prompt 输入变化 */ +const handlePromptInput = (event) => { + // 非输入法 输入设置为 true + if (!isComposing.value) { + // 回车 event data 是 null + if (event.data == null) { + return + } + isComposing.value = true + } + // 清理定时器 + if (inputTimeout.value) { + clearTimeout(inputTimeout.value) + } + // 重置定时器 + inputTimeout.value = setTimeout(() => { + isComposing.value = false + }, 400) +} +// TODO @芋艿:是不是可以通过 @keydown.enter、@keydown.shift.enter 来实现,回车发送、shift+回车换行;主要看看,是不是可以简化 isComposing 相关的逻辑 +const onCompositionstart = () => { + isComposing.value = true +} +const onCompositionend = () => { + // console.log('输入结束...') + setTimeout(() => { + isComposing.value = false + }, 200) +} + +/** 真正执行【发送】消息操作 */ +const doSendMessage = async (content: string) => { + // 校验 + if (content.length < 1) { + message.error('发送失败,原因:内容为空!') + return + } + if (activeConversationId.value == null) { + message.error('还没创建对话,不能发送!') + return + } + // 清空输入框 + prompt.value = '' + // 发送请求时如果accessToken过期,无法中断请求,暂时增加请求前刷新token + authUtil.setToken(await refreshToken()) + // 执行发送 + await doSendMessageStream({ + conversationId: activeConversationId.value, + content: content + } as ChatMessageVO) +} + +/** 真正执行【发送】消息操作 */ +const doSendMessageStream = async (userMessage: ChatMessageVO) => { + // 创建 AbortController 实例,以便中止请求 + conversationInAbortController.value = new AbortController() + // 标记对话进行中 + conversationInProgress.value = true + // 设置为空 + receiveMessageFullText.value = '' + + try { + // 1.1 先添加两个假数据,等 stream 返回再替换 + activeMessageList.value.push({ + id: -1, + conversationId: activeConversationId.value, + type: 'user', + content: userMessage.content, + createTime: new Date() + } as ChatMessageVO) + activeMessageList.value.push({ + id: -2, + conversationId: activeConversationId.value, + type: 'assistant', + content: '思考中...', + createTime: new Date() + } as ChatMessageVO) + // 1.2 滚动到最下面 + await nextTick() + await scrollToBottom() // 底部 + // 1.3 开始滚动 + textRoll() + + // 2. 发送 event stream + let isFirstChunk = true // 是否是第一个 chunk 消息段 + await ChatMessageApi.sendChatMessageStream( + userMessage.conversationId, + userMessage.content, + conversationInAbortController.value, + enableContext.value, + async (res) => { + const { code, data, msg } = JSON.parse(res.data) + if (code !== 0) { + message.alert(`对话异常! ${msg}`) + return + } + + // 如果内容为空,就不处理。 + if (data.receive.content === '') { + return + } + // 首次返回需要添加一个 message 到页面,后面的都是更新 + if (isFirstChunk) { + isFirstChunk = false + // 弹出两个假数据 + activeMessageList.value.pop() + activeMessageList.value.pop() + // 更新返回的数据 + activeMessageList.value.push(data.send) + activeMessageList.value.push(data.receive) + } + // debugger + receiveMessageFullText.value = receiveMessageFullText.value + data.receive.content + // 滚动到最下面 + await scrollToBottom() + }, + (error) => { + message.alert(`对话异常! ${error}`) + stopStream() + }, + () => { + stopStream() + } + ) + } catch {} +} + +/** 停止 stream 流式调用 */ +const stopStream = async () => { + // tip:如果 stream 进行中的 message,就需要调用 controller 结束 + if (conversationInAbortController.value) { + conversationInAbortController.value.abort() + } + // 设置为 false + conversationInProgress.value = false +} + +/** 编辑 message:设置为 prompt,可以再次编辑 */ +const handleMessageEdit = (message: ChatMessageVO) => { + prompt.value = message.content +} + +/** 刷新 message:基于指定消息,再次发起对话 */ +const handleMessageRefresh = (message: ChatMessageVO) => { + doSendMessage(message.content) +} + +// ============== 【消息滚动】相关 ============= + +/** 滚动到 message 底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + await nextTick() + if (messageRef.value) { + messageRef.value.scrollToBottom(isIgnore) + } +} + +/** 自提滚动效果 */ +const textRoll = async () => { + let index = 0 + try { + // 只能执行一次 + if (textRoleRunning.value) { + return + } + // 设置状态 + textRoleRunning.value = true + receiveMessageDisplayedText.value = '' + const task = async () => { + // 调整速度 + const diff = + (receiveMessageFullText.value.length - receiveMessageDisplayedText.value.length) / 10 + if (diff > 5) { + textSpeed.value = 10 + } else if (diff > 2) { + textSpeed.value = 30 + } else if (diff > 1.5) { + textSpeed.value = 50 + } else { + textSpeed.value = 100 + } + // 对话结束,就按 30 的速度 + if (!conversationInProgress.value) { + textSpeed.value = 10 + } + + if (index < receiveMessageFullText.value.length) { + receiveMessageDisplayedText.value += receiveMessageFullText.value[index] + index++ + + // 更新 message + const lastMessage = activeMessageList.value[activeMessageList.value.length - 1] + lastMessage.content = receiveMessageDisplayedText.value + // 滚动到住下面 + await scrollToBottom() + // 重新设置任务 + timer = setTimeout(task, textSpeed.value) + } else { + // 不是对话中可以结束 + if (!conversationInProgress.value) { + textRoleRunning.value = false + clearTimeout(timer) + } else { + // 重新设置任务 + timer = setTimeout(task, textSpeed.value) + } + } + } + let timer = setTimeout(task, textSpeed.value) + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + // 如果有 conversationId 参数,则默认选中 + if (route.query.conversationId) { + const id = route.query.conversationId as unknown as number + activeConversationId.value = id + await getConversation(id) + } + + // 获取列表数据 + activeMessageListLoading.value = true + await getMessageList() +}) +</script> + +<style lang="scss" scoped> + +.container-wrapper { + position: relative; // 关键定位容器 + width: 100%; + height: 100%; +} + +.sidebar-toggle { + position: absolute; + left: 300px; // 初始展开位置 + top: 40%; + z-index: 1000; + width: 20px; + height: 80px; + background: rgba(115, 196, 255, 0.5); + border-radius: 0 8px 8px 0; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + color: rgba(255,215,0); + transition: left 0.3s ease, background 0.2s ease; + + &:hover { + background: #409EFF; + left: 304px; // 悬停微调 + transition: left 0.3s ease, background 0.2s ease; + } +} + +.conversation-wrapper { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 300px; + background: rgba(13,28,58,0.9); + box-shadow: 2px 0 8px rgba(0,0,0,0.1); + transition: transform 0.3s ease, opacity 0.2s ease; + z-index: 999; + overflow: hidden; + + &.collapsed { + transform: translateX(-100%); + opacity: 0; + pointer-events: none; + } +} + +// 头部 +.detail-container { + width: 100%; + height: 885px; + margin-left: 5px; + background-color: rgba(0, 0, 0, 0); /* 透明背景 */ + transition: margin 0.3s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 1; + .header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + box-shadow: 0 0 0 0 #dcdfe6; + + .title { + font-size: 18px; + font-weight: bold; + color: gold; + } + + .btns { + display: flex; + width: 300px; + flex-direction: row; + justify-content: flex-end; + + .btn { + padding: 10px; + } + + /* 所有状态通用透明背景 */ + :deep(.el-button) { + background: transparent !important; + border-color: currentColor; /* 保持与文字同色 */ + color: #409EFF; /* 蓝色文字 */ + } + + /* 悬停状态 */ + :deep(.el-button:hover) { + background: rgba(0, 0, 0, 0.05) !important; /* 轻微悬停反馈 */ + } + + /* 点击状态 */ + :deep(.el-button:active) { + background: rgba(0, 0, 0, 0.1) !important; + } + + /* 禁用状态 */ + :deep(.el-button.is-disabled) { + opacity: 0.6; + background: transparent !important; + } + } + } + + &[style*="0"] { + margin-left: 0 !important; + } +} + +// main 容器 +.main-container { + margin-left: 10px; + padding: 0; + position: relative; + height: 500px; + width: 100%; + + .message-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: hidden; + padding: 0; + margin: 0; + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.15) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } + } +} + +// 底部 +.footer-container { + display: flex; + flex-direction: column; + height: 114px; + margin-left: 10px; + padding: 0; + + // 输入框 + .input-container { + display: flex; + flex-direction: column; + height: auto; + width: 876px; + margin: 0; + padding: 0; + overflow-y: auto; /* 垂直方向溢出时显示滚动条 */ + overflow-x: hidden; /* 水平方向隐藏滚动条 */ + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.15) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } + + .prompt-from { + display: flex; + flex-direction: column; + padding: 9px 10px; + width: 876px; + height: 114px; + background: rgba(115,196,255,0.05); + border-radius: 4px 4px 4px 4px; + border: 1px solid #73C4FF; + } + + textarea::placeholder { + color: #DBEEFF; + } + + .prompt-input { + width: 876px; + height: 113.55px; + font-weight: 400; + font-size: 14px; + background-color: rgba(219,238,255,0); + line-height: 21px; + text-align: left; + font-style: normal; + text-transform: none; + border: 0; + color: #73C4FF; + } + + .prompt-input:focus { + outline: none; + } + + .prompt-btns { + display: flex; + justify-content: space-between; + padding-bottom: 0; + padding-top: 5px; + + .content { + /* 默认状态 */ + .el-button { + background: transparent !important; + border-color: rgba(115, 196, 255, 0.5); + color: #73C4FF; + border-radius: 15px !important; + } + + /* 上下文图标处理 */ + .content-icon { + color: blue; /* 图标颜色 */ + font-size: 18px; + margin-right: 10px; + background: url("@/assets/ai/zhuanlu/content.png"); + vertical-align: middle; + } + + /* 选中状态 */ + .active-button { + background: #409eff !important; + border-color: #409eff !important; + color: white !important; + .content-icon { + background: url("@/assets/ai/zhuanlu/content_select.png"); + vertical-align: middle; + } + } + + /* 按钮组间距处理 */ + .button-group .el-button { + margin-left: 0; + border-radius: 4px; + } + + /* 悬停效果 */ + .el-button:not(.active-button):hover { + border-color: rgba(115,196,255,0.5); + color: #409eff; + } + } + .message { + /* 所有状态通用透明背景 */ + :deep(.el-button) { + background: rgba(73, 254, 210, 0.8) !important; + border-color: currentColor; /* 保持与文字同色 */ + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 16px; + color: #123C4E; + clip-path: polygon( + 0 0, + 100% 0, + 100% 100%, + 10px 100%, /* 右下方向留出10px */ + 0 calc(100% - 10px) /* 左上方向留出10px */ + ); + position: relative; + padding-left: 15px; /* 增加右侧留白 */ + } + + /* 悬停状态 */ + :deep(.el-button:hover) { + background: rgba(73, 254, 210, 0.6) !important; /* 轻微悬停反馈 */ + } + + /* 点击状态 */ + :deep(.el-button:active) { + background: rgba(73, 254, 210, 1) !important; + } + + /* 禁用状态 */ + :deep(.el-button.is-disabled) { + opacity: 0.6; + background: transparent !important; + } + + /* 核心样式覆盖 */ + :deep(.el-switch__core) { + background: transparent !important; + border-radius: 0 0 15px 0 !important; + border: none !important; + height: 40px !important; + } + + /* 按钮内容容器 */ + .button-content { + display: flex; + align-items: center; + padding: 0 15px; + height: 100%; + } + } + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/conversation/CommonConversationList.vue b/src/views/ai/dashboard/components/conversation/CommonConversationList.vue new file mode 100644 index 0000000..df1c641 --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/CommonConversationList.vue @@ -0,0 +1,526 @@ +<!-- AI 对话 --> +<template> + <el-aside width="260px" class="conversation-container h-100%"> + <!-- 左顶部:对话 --> + <div class="h-100%"> + <div class="conversation-title"> + <img + src="@/assets/ai/zhuanlu/conversation_big.png" + class="mr-3px w-[1.2em] h-[1.2em]" + alt="icon" + /> + 对话列表 + </div> +<!-- <hr class="line"/>--> + <el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation"> + <img + src="@/assets/ai/zhuanlu/conversation_big.png" + class="mr-8px w-[1.5em] h-[1.5em]" + alt="icon" + /> + 开始新对话 + </el-button> + + <!-- 左顶部:搜索对话 --> + <el-input + v-model="searchName" + size="large" + class="mt-10px search-input" + placeholder="搜索历史记录" + @keyup="searchConversation" + > + <template #prefix> + <Icon icon="ep:search" /> + </template> + </el-input> + + <!-- 左中间:对话列表 --> + <div class="conversation-list"> + <!-- 情况一:加载中 --> + <el-empty v-if="loading" description="." :v-loading="loading" /> + <!-- 情况二:按照 group 分组,展示聊天会话 list 列表 --> + <div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey"> + <div + class="conversation-item classify-title" + v-if="conversationMap[conversationKey].length" + > + <el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text> + </div> + <div + class="conversation-item" + v-for="conversation in conversationMap[conversationKey]" + :key="conversation.id" + @click="handleConversationClick(conversation.id)" + @mouseover="hoverConversationId = conversation.id" + @mouseout="hoverConversationId = ''" + > + <div + :class=" + conversation.id === activeConversationId ? 'conversation active' : 'conversation' + " + > + <div class="title-wrapper"> + <img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" /> + <span class="title">{{ conversation.title }}</span> + </div> + <div class="button-wrapper" v-show="hoverConversationId === conversation.id"> + <el-button class="btn" link @click.stop="handleTop(conversation)"> + <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon> + <el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon> + </el-button> + <el-button class="btn" link @click.stop="updateConversationTitle(conversation)"> + <el-icon title="编辑"> + <Icon icon="ep:edit" /> + </el-icon> + </el-button> + <el-button class="btn" link @click.stop="deleteChatConversation(conversation)"> + <el-icon title="删除对话"> + <Icon icon="ep:delete" /> + </el-icon> + </el-button> + </div> + </div> + </div> + </div> + <!-- 底部占位 --> + <div class="h-160px w-100%"></div> + </div> + </div> + + <!-- 左底部:工具栏 --> + <div class="tool-box"> + <div @click="handleClearConversation"> + <Icon icon="ep:delete" /> + <el-text size="small">清空未置顶对话</el-text> + </div> + </div> + + </el-aside> +</template> + +<script setup lang="ts"> +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import { Bottom, Top } from '@element-plus/icons-vue' +import roleAvatarDefaultImg from '@/assets/ai/zhuanlu/assistant.png' + +const message = useMessage() // 消息弹窗 + +// 定义属性 +const searchName = ref<string>('') // 对话搜索 +const modelName = ref<string>('common') // 对话搜索 +const activeConversationId = ref<number | null>(null) // 选中的对话,默认为 null +const hoverConversationId = ref<number | null>(null) // 悬浮上去的对话 +const conversationList = ref([] as ChatConversationVO[]) // 对话列表 +const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前) +const loading = ref<boolean>(false) // 加载中 +const loadingTime = ref<any>() // 加载中定时器 + +// 定义组件 props +const props = defineProps({ + activeId: { + type: String || null, + required: true + } +}) + +// 定义钩子 +const emits = defineEmits([ + 'onConversationCreate', + 'onConversationClick', + 'onConversationClear', + 'onConversationDelete' +]) + +/** 搜索对话 */ +const searchConversation = async (e) => { + // 恢复数据 + if (!searchName.value.trim().length) { + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } else { + // 过滤 + const filterValues = conversationList.value.filter((item) => { + return item.title.includes(searchName.value.trim()) + }) + conversationMap.value = await getConversationGroupByCreateTime(filterValues) + } +} + +/** 点击对话 */ +const handleConversationClick = async (id: number) => { + // 过滤出选中的对话 + const filterConversation = conversationList.value.filter((item) => { + return item.id === id + }) + // 回调 onConversationClick + // noinspection JSVoidFunctionReturnValueUsed + const success = emits('onConversationClick', filterConversation[0]) + // 切换对话 + if (success) { + activeConversationId.value = id + } +} + +/** 获取对话列表 */ +const getChatConversationList = async () => { + try { + // 加载中 + loadingTime.value = setTimeout(() => { + loading.value = true + }, 50) + + // 1.1 获取 对话数据 + conversationList.value = await ChatConversationApi.getChatConversationEnergyList(modelName.value) + if(conversationList.value.length == 0) { + await createConversation() + } + // 1.2 排序 + conversationList.value.sort((a, b) => { + return b.createTime - a.createTime + }) + // 1.3 没有任何对话情况 + if (conversationList.value.length === 0) { + activeConversationId.value = null + conversationMap.value = {} + return + } + + // 2. 对话根据时间分组(置顶、今天、一天前、三天前、七天前、30 天前) + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } finally { + // 清理定时器 + if (loadingTime.value) { + clearTimeout(loadingTime.value) + } + // 加载完成 + loading.value = false + } +} + +/** 按照 creteTime 创建时间,进行分组 */ +const getConversationGroupByCreateTime = async (list: ChatConversationVO[]) => { + // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前) + // noinspection NonAsciiCharacters + const groupMap = { + 置顶: [], + 今天: [], + 一天前: [], + 三天前: [], + 七天前: [], + 三十天前: [] + } + // 当前时间的时间戳 + const now = Date.now() + // 定义时间间隔常量(单位:毫秒) + const oneDay = 24 * 60 * 60 * 1000 + const threeDays = 3 * oneDay + const sevenDays = 7 * oneDay + const thirtyDays = 30 * oneDay + for (const conversation of list) { + // 置顶 + if (conversation.pinned) { + groupMap['置顶'].push(conversation) + continue + } + // 计算时间差(单位:毫秒) + const diff = now - conversation.createTime + // 根据时间间隔判断 + if (diff < oneDay) { + groupMap['今天'].push(conversation) + } else if (diff < threeDays) { + groupMap['一天前'].push(conversation) + } else if (diff < sevenDays) { + groupMap['三天前'].push(conversation) + } else if (diff < thirtyDays) { + groupMap['七天前'].push(conversation) + } else { + groupMap['三十天前'].push(conversation) + } + } + return groupMap +} + +/** 新建对话 */ +const createConversation = async () => { + // 1. 新建对话 + const conversationId = await ChatConversationApi.createChatConversationEnergy( + {modelName: modelName.value} as unknown as ChatConversationVO + ) + // 2. 获取对话内容 + await getChatConversationList() + // 3. 选中对话 + await handleConversationClick(conversationId) + // 4. 回调 + emits('onConversationCreate') +} + +/** 修改对话的标题 */ +const updateConversationTitle = async (conversation: ChatConversationVO) => { + // 1. 二次确认 + const { value } = await ElMessageBox.prompt('修改标题', { + inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格 + inputErrorMessage: '标题不能为空', + inputValue: conversation.title + }) + // 2. 发起修改 + await ChatConversationApi.updateChatConversationMy({ + id: conversation.id, + title: value + } as ChatConversationVO) + message.success('重命名成功') + // 3. 刷新列表 + await getChatConversationList() + // 4. 过滤当前切换的 + const filterConversationList = conversationList.value.filter((item) => { + return item.id === conversation.id + }) + if (filterConversationList.length > 0) { + // tip:避免切换对话 + if (activeConversationId.value === filterConversationList[0].id) { + emits('onConversationClick', filterConversationList[0]) + } + } +} + +/** 删除聊天对话 */ +const deleteChatConversation = async (conversation: ChatConversationVO) => { + try { + // 删除的二次确认 + await message.delConfirm(`是否确认删除对话 - ${conversation.title}?`) + // 发起删除 + await ChatConversationApi.deleteChatConversationMy(conversation.id) + message.success('对话已删除') + // 刷新列表 + await getChatConversationList() + // 回调 + emits('onConversationDelete', conversation) + } catch {} +} + +/** 清空对话 */ +const handleClearConversation = async () => { + try { + await message.confirm('确认后对话会全部清空,置顶的对话除外。') + await ChatConversationApi.deleteChatConversationMyByUnpinned() + ElMessage({ + message: '操作成功!', + type: 'success' + }) + // 清空 对话 和 对话内容 + activeConversationId.value = null + // 获取 对话列表 + await getChatConversationList() + // 回调 方法 + emits('onConversationClear') + } catch {} +} + +/** 对话置顶 */ +const handleTop = async (conversation: ChatConversationVO) => { + // 更新对话置顶 + conversation.pinned = !conversation.pinned + await ChatConversationApi.updateChatConversationMy(conversation) + // 刷新对话 + await getChatConversationList() +} + +// ============ 角色仓库 ============ + +/** 角色仓库抽屉 */ +const roleRepositoryOpen = ref<boolean>(false) // 角色仓库是否打开 +const handleRoleRepository = async () => { + roleRepositoryOpen.value = !roleRepositoryOpen.value +} + +/** 监听选中的对话 */ +const { activeId } = toRefs(props) +watch(activeId, async (newValue, oldValue) => { + activeConversationId.value = newValue as string +}) + +// 定义 public 方法 +defineExpose({ createConversation }) + +/** 初始化 */ +onMounted(async () => { + // 获取 对话列表 + await getChatConversationList() + // 默认选中 + if (props.activeId) { + activeConversationId.value = props.activeId + } else { + // 首次默认选中第一个 + if (conversationList.value.length) { + activeConversationId.value = conversationList.value[0].id + // 回调 onConversationClick + await emits('onConversationClick', conversationList.value[0]) + } + } +}) +</script> + +<style scoped lang="scss"> +.conversation-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px 10px 0; + overflow: hidden; + + .conversation-title { + color: #73C4FF; + line-height: 25px; + margin-bottom: 8px; + border-bottom: 1px solid rgba(69,133,255,0.2); + img { + padding: 6px 0 0 5px; + } + } + .line { + margin: 5px 0; + } + + .btn-new-conversation { + border-radius: 4px; + border: 1px solid rgba(69,133,255,0.6); + background: rgba(69,133,255,0.4); + color: #73C4FF; + } + + .search-input { + height: 30px; + border-radius: 4px; + border: 1px solid rgba(69,133,255,0.6); + background: rgba(69,133,255,0.4); + } + + .conversation-list { + overflow: auto; + height: 100%; + + .classify-title { + padding-top: 10px; + b { + color: white; + } + } + + .conversation-item { + margin-top: 5px; + } + + .conversation { + display: flex; + flex-direction: row; + justify-content: space-between; + flex: 1; + padding: 0 5px; + cursor: pointer; + border-radius: 5px; + align-items: center; + line-height: 30px; + background-color: rgba(69,133,255,0.1); + &.active { + background-color: rgba(69,133,255,0.5); + .button { + display: inline-block; + } + .title-wrapper { + > span { + font-weight: bold; + color: rgba(115, 196, 255); + } + } + } + + .title-wrapper { + display: flex; + flex-direction: row; + align-items: center; + > span { + color: rgba(115, 196, 255, 0.5); + } + } + + .title { + padding: 2px 10px; + max-width: 220px; + font-size: 14px; + font-weight: 400; + color: rgba(0, 0, 0, 0.77); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .avatar { + width: 25px; + height: 25px; + border-radius: 5px; + display: flex; + flex-direction: row; + justify-items: center; + } + + // 对话编辑、删除 + .button-wrapper { + right: 2px; + display: flex; + flex-direction: row; + justify-items: center; + color: #606266; + + .btn { + margin: 0; + color: #73C4FF; + } + } + } + } + + // 角色仓库、清空未设置对话 + .tool-box { + bottom: 0; + padding: 0 20px; + background-color: rgba(69,133,255,0.2); + box-shadow: 0 0 1px 1px rgba(69,133,255,0.4); + line-height: 35px; + justify-content: space-between; + align-items: center; + color: var(--el-text-color); + + div { + display: flex; + margin-left: 20%; + align-items: center; + color: #73C4FF; + padding: 0; + cursor: pointer; + > span { + color: #73C4FF; + margin-left: 5px; + } + } + } +} + +/* 移除所有输入框边框 */ +:deep(.el-form-item .el-input__wrapper) { + border: none !important; + box-shadow: none !important; + background: rgba(255,255,255,0.1) !important; /* 保留浅色背景 */ +} + +/* 移除输入框边框 */ +:deep(.el-input .el-input__wrapper) { + border: 1px solid #1E5A86 !important; + box-shadow: none !important; +} + +:deep(.el-input__inner) { + color: #73C4FF; +} +:deep(.el-input__wrapper) { + background: rgba(0,194,255,0.08) !important; +} +</style> diff --git a/src/views/ai/dashboard/components/conversation/CommonConversationUpdateForm.vue b/src/views/ai/dashboard/components/conversation/CommonConversationUpdateForm.vue new file mode 100644 index 0000000..afa5279 --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/CommonConversationUpdateForm.vue @@ -0,0 +1,219 @@ +<template> + <DialogDashboard title="模型设定" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="130px" + v-loading="formLoading" + > + <el-form-item label="模型" prop="modelId"> + <el-select v-model="formData.modelId" disabled> + <el-option + v-for="model in models" + :key="model.id" + :label="model.name" + :value="model.id" + /> + </el-select> + </el-form-item> + <el-form-item label="温度参数" prop="temperature"> + <el-input-number + v-model="formData.temperature" + placeholder="请输入温度参数" + :min="0" + :max="2" + :precision="2" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="回复数 Token 数" prop="maxTokens"> + <el-input-number + v-model="formData.maxTokens" + placeholder="请输入回复数 Token 数" + :min="0" + :max="8192" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="上下文数量" prop="maxContexts"> + <el-input-number + v-model="formData.maxContexts" + placeholder="请输入上下文数量" + :min="0" + :max="20" + class="!w-1/1" + /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </DialogDashboard> +</template> +<script setup lang="ts"> +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import { AiModelTypeEnum } from '@/views/ai/utils/constants' + +/** AI 聊天对话的更新表单 */ +defineOptions({ name: 'ChatConversationUpdateForm' }) + +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formData = ref({ + id: undefined, + systemMessage: undefined, + modelId: undefined, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined +}) +const formRules = reactive({ + modelId: [{ required: true, message: '模型不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'blur' }], + temperature: [{ required: true, message: '温度参数不能为空', trigger: 'blur' }], + maxTokens: [{ required: true, message: '回复数 Token 数不能为空', trigger: 'blur' }], + maxContexts: [{ required: true, message: '上下文数量不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const models = ref([] as ModelVO[]) // 聊天模型列表 + +/** 打开弹窗 */ +const open = async (id: number) => { + dialogVisible.value = true + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + const data = await ChatConversationApi.getChatConversationMy(id) + formData.value = Object.keys(formData.value).reduce((obj, key) => { + if (data.hasOwnProperty(key)) { + obj[key] = data[key] + } + return obj + }, {}) + } finally { + formLoading.value = false + } + } + // 获得下拉数据 + models.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.CHAT) +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ChatConversationVO + await ChatConversationApi.updateChatConversationMy(data) + message.success('对话配置已更新') + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + systemMessage: undefined, + modelId: undefined, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined + } + formRef.value?.resetFields() +} +</script> + +<style lang="scss" scoped> + +:deep(.el-form-item__label) { + color: #73C4FF; +} +:deep(.el-form-item__content) { + .el-input-number { + border: black solid 1px !important; + } +} +/* 移除所有输入框边框 */ +:deep(.el-form-item .el-input__wrapper) { + border: none !important; + box-shadow: none !important; + background: rgba(255,255,255,0.1) !important; /* 保留浅色背景 */ +} + +/* 移除数字输入框边框 */ +:deep(.el-input-number .el-input__wrapper) { + border: 1px solid #1E5A86 !important; + box-shadow: none !important; +} + +/* 下拉组件 */ +:deep(.el-select) { + /* 下拉箭头 */ + .el-select__caret { + color: #73C4FF !important; /* 匹配图中的浅蓝箭头 */ + font-size: 16px !important; + } +} + +/* 深度选择器调整边框细节 */ +:deep(.el-select__wrapper) { + border-radius: 6px; /* 圆角大小 */ + border-width: 1.5px; /* 边框粗细 */ + box-shadow: 0 0 0 1px #1E5A86 !important; /* 聚焦阴影 */ +} + +/* 移除按钮组边框(增减按钮) */ +:deep(.el-input-number__decrease), +:deep(.el-input-number__increase) { + border: 1px solid #1E5A86; + background: transparent !important; + i { + color: #73C4FF; + } +} +:deep(.el-loading-mask) { + background: black !important; + border-radius: 10px; +} +:deep(.el-input__inner) { + color: #73C4FF; +} +:deep(.el-select__selected-item ) { + color: #73C4FF !important; +} +:deep(.el-select__wrapper) { + background: rgba(0,194,255,0.08) !important; +} +:deep(.el-input-number span) { + background: rgba(0,194,255,0.08) !important; +} +:deep(.el-input__wrapper) { + background: rgba(0,194,255,0.08) !important; +} +.el-dialog__footer { + button { + background: rgba(0,194,255,0.08) !important; + border-color: rgba(0,194,255,0.8) !important; + } + button:first-child { + color: #73C4FF; + } +} + +</style> diff --git a/src/views/ai/dashboard/components/conversation/ConversationList.vue b/src/views/ai/dashboard/components/conversation/ConversationList.vue new file mode 100644 index 0000000..c66ff0a --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/ConversationList.vue @@ -0,0 +1,459 @@ +<!-- AI 对话 --> +<template> + <el-aside width="260px" class="conversation-container h-100%"> + <!-- 左顶部:对话 --> + <div class="h-100%"> + <el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation"> + <Icon icon="ep:plus" class="mr-5px" /> + 新建对话 + </el-button> + + <!-- 左顶部:搜索对话 --> + <el-input + v-model="searchName" + size="large" + class="mt-10px search-input" + placeholder="搜索历史记录" + @keyup="searchConversation" + > + <template #prefix> + <Icon icon="ep:search" /> + </template> + </el-input> + + <!-- 左中间:对话列表 --> + <div class="conversation-list"> + <!-- 情况一:加载中 --> + <el-empty v-if="loading" description="." :v-loading="loading" /> + <!-- 情况二:按照 group 分组,展示聊天会话 list 列表 --> + <div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey"> + <div + class="conversation-item classify-title" + v-if="conversationMap[conversationKey].length" + > + <el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text> + </div> + <div + class="conversation-item" + v-for="conversation in conversationMap[conversationKey]" + :key="conversation.id" + @click="handleConversationClick(conversation.id)" + @mouseover="hoverConversationId = conversation.id" + @mouseout="hoverConversationId = ''" + > + <div + :class=" + conversation.id === activeConversationId ? 'conversation active' : 'conversation' + " + > + <div class="title-wrapper"> + <img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" /> + <span class="title">{{ conversation.title }}</span> + </div> + <div class="button-wrapper" v-show="hoverConversationId === conversation.id"> + <el-button class="btn" link @click.stop="handleTop(conversation)"> + <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon> + <el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon> + </el-button> + <el-button class="btn" link @click.stop="updateConversationTitle(conversation)"> + <el-icon title="编辑"> + <Icon icon="ep:edit" /> + </el-icon> + </el-button> + <el-button class="btn" link @click.stop="deleteChatConversation(conversation)"> + <el-icon title="删除对话"> + <Icon icon="ep:delete" /> + </el-icon> + </el-button> + </div> + </div> + </div> + </div> + <!-- 底部占位 --> + <div class="h-160px w-100%"></div> + </div> + </div> + + </el-aside> +</template> + +<script setup lang="ts"> +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import { Bottom, Top } from '@element-plus/icons-vue' +import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' + +const message = useMessage() // 消息弹窗 + +// 定义属性 +const searchName = ref<string>('') // 对话搜索 +const modelName = ref<string>('zhuanlu') // 对话搜索 +const activeConversationId = ref<number | null>(null) // 选中的对话,默认为 null +const hoverConversationId = ref<number | null>(null) // 悬浮上去的对话 +const conversationList = ref([] as ChatConversationVO[]) // 对话列表 +const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前) +const loading = ref<boolean>(false) // 加载中 +const loadingTime = ref<any>() // 加载中定时器 + +// 定义组件 props +const props = defineProps({ + activeId: { + type: String || null, + required: true + } +}) + +// 定义钩子 +const emits = defineEmits([ + 'onConversationCreate', + 'onConversationClick', + 'onConversationClear', + 'onConversationDelete' +]) + +/** 搜索对话 */ +const searchConversation = async (e) => { + // 恢复数据 + if (!searchName.value.trim().length) { + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } else { + // 过滤 + const filterValues = conversationList.value.filter((item) => { + return item.title.includes(searchName.value.trim()) + }) + conversationMap.value = await getConversationGroupByCreateTime(filterValues) + } +} + +/** 点击对话 */ +const handleConversationClick = async (id: number) => { + // 过滤出选中的对话 + const filterConversation = conversationList.value.filter((item) => { + return item.id === id + }) + // 回调 onConversationClick + // noinspection JSVoidFunctionReturnValueUsed + const success = emits('onConversationClick', filterConversation[0]) + // 切换对话 + if (success) { + activeConversationId.value = id + } +} + +/** 获取对话列表 */ +const getChatConversationList = async () => { + try { + // 加载中 + loadingTime.value = setTimeout(() => { + loading.value = true + }, 50) + + // 1.1 获取 对话数据 + conversationList.value = await ChatConversationApi.getChatConversationEnergyList(modelName.value) + if(conversationList.value.length == 0) { + await createConversation() + } + // 1.2 排序 + conversationList.value.sort((a, b) => { + return b.createTime - a.createTime + }) + // 1.3 没有任何对话情况 + if (conversationList.value.length === 0) { + activeConversationId.value = null + conversationMap.value = {} + return + } + + // 2. 对话根据时间分组(置顶、今天、一天前、三天前、七天前、30 天前) + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } finally { + // 清理定时器 + if (loadingTime.value) { + clearTimeout(loadingTime.value) + } + // 加载完成 + loading.value = false + } +} + +/** 按照 creteTime 创建时间,进行分组 */ +const getConversationGroupByCreateTime = async (list: ChatConversationVO[]) => { + // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前) + // noinspection NonAsciiCharacters + const groupMap = { + 置顶: [], + 今天: [], + 一天前: [], + 三天前: [], + 七天前: [], + 三十天前: [] + } + // 当前时间的时间戳 + const now = Date.now() + // 定义时间间隔常量(单位:毫秒) + const oneDay = 24 * 60 * 60 * 1000 + const threeDays = 3 * oneDay + const sevenDays = 7 * oneDay + const thirtyDays = 30 * oneDay + for (const conversation of list) { + // 置顶 + if (conversation.pinned) { + groupMap['置顶'].push(conversation) + continue + } + // 计算时间差(单位:毫秒) + const diff = now - conversation.createTime + // 根据时间间隔判断 + if (diff < oneDay) { + groupMap['今天'].push(conversation) + } else if (diff < threeDays) { + groupMap['一天前'].push(conversation) + } else if (diff < sevenDays) { + groupMap['三天前'].push(conversation) + } else if (diff < thirtyDays) { + groupMap['七天前'].push(conversation) + } else { + groupMap['三十天前'].push(conversation) + } + } + return groupMap +} + +/** 新建对话 */ +const createConversation = async () => { + // 1. 新建对话 + const conversationId = await ChatConversationApi.createChatConversationEnergy( + {modelName: modelName.value} as unknown as ChatConversationVO + ) + // 2. 获取对话内容 + await getChatConversationList() + // 3. 选中对话 + await handleConversationClick(conversationId) + // 4. 回调 + emits('onConversationCreate') +} + +/** 修改对话的标题 */ +const updateConversationTitle = async (conversation: ChatConversationVO) => { + // 1. 二次确认 + const { value } = await ElMessageBox.prompt('修改标题', { + inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格 + inputErrorMessage: '标题不能为空', + inputValue: conversation.title + }) + // 2. 发起修改 + await ChatConversationApi.updateChatConversationMy({ + id: conversation.id, + title: value + } as ChatConversationVO) + message.success('重命名成功') + // 3. 刷新列表 + await getChatConversationList() + // 4. 过滤当前切换的 + const filterConversationList = conversationList.value.filter((item) => { + return item.id === conversation.id + }) + if (filterConversationList.length > 0) { + // tip:避免切换对话 + if (activeConversationId.value === filterConversationList[0].id) { + emits('onConversationClick', filterConversationList[0]) + } + } +} + +/** 删除聊天对话 */ +const deleteChatConversation = async (conversation: ChatConversationVO) => { + try { + // 删除的二次确认 + await message.delConfirm(`是否确认删除对话 - ${conversation.title}?`) + // 发起删除 + await ChatConversationApi.deleteChatConversationMy(conversation.id) + message.success('对话已删除') + // 刷新列表 + await getChatConversationList() + // 回调 + emits('onConversationDelete', conversation) + } catch {} +} + +/** 清空对话 */ +const handleClearConversation = async () => { + try { + await message.confirm('确认后对话会全部清空,置顶的对话除外。') + await ChatConversationApi.deleteChatConversationMyByUnpinned() + ElMessage({ + message: '操作成功!', + type: 'success' + }) + // 清空 对话 和 对话内容 + activeConversationId.value = null + // 获取 对话列表 + await getChatConversationList() + // 回调 方法 + emits('onConversationClear') + } catch {} +} + +/** 对话置顶 */ +const handleTop = async (conversation: ChatConversationVO) => { + // 更新对话置顶 + conversation.pinned = !conversation.pinned + await ChatConversationApi.updateChatConversationMy(conversation) + // 刷新对话 + await getChatConversationList() +} + +// ============ 角色仓库 ============ + +/** 角色仓库抽屉 */ +const roleRepositoryOpen = ref<boolean>(false) // 角色仓库是否打开 +const handleRoleRepository = async () => { + roleRepositoryOpen.value = !roleRepositoryOpen.value +} + +/** 监听选中的对话 */ +const { activeId } = toRefs(props) +watch(activeId, async (newValue, oldValue) => { + activeConversationId.value = newValue as string +}) + +// 定义 public 方法 +defineExpose({ createConversation }) + +/** 初始化 */ +onMounted(async () => { + // 获取 对话列表 + await getChatConversationList() + // 默认选中 + if (props.activeId) { + activeConversationId.value = props.activeId + } else { + // 首次默认选中第一个 + if (conversationList.value.length) { + activeConversationId.value = conversationList.value[0].id + // 回调 onConversationClick + await emits('onConversationClick', conversationList.value[0]) + } + } +}) +</script> + +<style scoped lang="scss"> +.conversation-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px 10px 0; + overflow: hidden; + + .btn-new-conversation { + padding: 18px 0; + } + + .search-input { + margin-top: 20px; + } + + .conversation-list { + overflow: auto; + height: 100%; + + .classify-title { + padding-top: 10px; + } + + .conversation-item { + margin-top: 5px; + } + + .conversation { + display: flex; + flex-direction: row; + justify-content: space-between; + flex: 1; + padding: 0 5px; + cursor: pointer; + border-radius: 5px; + align-items: center; + line-height: 30px; + + &.active { + background-color: #e6e6e6; + + .button { + display: inline-block; + } + } + + .title-wrapper { + display: flex; + flex-direction: row; + align-items: center; + } + + .title { + padding: 2px 10px; + max-width: 220px; + font-size: 14px; + font-weight: 400; + color: rgba(0, 0, 0, 0.77); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .avatar { + width: 25px; + height: 25px; + border-radius: 5px; + display: flex; + flex-direction: row; + justify-items: center; + } + + // 对话编辑、删除 + .button-wrapper { + right: 2px; + display: flex; + flex-direction: row; + justify-items: center; + color: #606266; + + .btn { + margin: 0; + } + } + } + } + + // 角色仓库、清空未设置对话 + .tool-box { + position: absolute; + bottom: 0; + left: 0; + right: 0; + //width: 100%; + padding: 0 20px; + background-color: #f4f4f4; + box-shadow: 0 0 1px 1px rgba(228, 228, 228, 0.8); + line-height: 35px; + display: flex; + justify-content: space-between; + align-items: center; + color: var(--el-text-color); + + > div { + display: flex; + align-items: center; + color: #606266; + padding: 0; + margin: 0; + cursor: pointer; + + > span { + margin-left: 5px; + } + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/conversation/ConversationListEmpty.vue b/src/views/ai/dashboard/components/conversation/ConversationListEmpty.vue new file mode 100644 index 0000000..d7fef8c --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/ConversationListEmpty.vue @@ -0,0 +1,78 @@ +<!-- 无聊天对话时,在 message 区域--> +<template> + <div class="conversation-empty"> + <div class="center-container"> + <div class="title no-data">暂无数据</div> + <div class="title">欢迎来到转炉煤气调度大模型</div> + </div> + </div> +</template> +<script setup lang="ts"> +const emits = defineEmits(['onNewConversation']) + +import roleAvatarDefaultImg from '@/assets/ai/zhuanlu/assistant.png' + +const roleAvatar = roleAvatarDefaultImg + +</script> +<style scoped lang="scss"> +.conversation-empty { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + height: 100%; + + .box-center { + margin-top: 35%; + margin-left: 12%; + display: inline-block; + + .tip { + width: 120px; + height: 40px; + padding: 2px 0 0 10px; + border-radius: 2px; + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 24px; + text-align: left; + font-style: normal; + text-transform: none; + background: linear-gradient(0deg, #49FFD3 0%, #4585FF 100%); + } + } + + .conversation-empty { + position: relative; + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + height: 100%; + + .center-container { + display: flex; + flex-direction: column; + justify-content: center; + + .title { + width: 120px; + height: 40px; + padding: 2px 0 0 10px; + border-radius: 2px; + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 24px; + text-align: left; + font-style: normal; + text-transform: none; + background: linear-gradient(0deg, #49FFD3 0%, #4585FF 100%); + } + .no-data { + color: rgba(143,214,254,0.8) !important; + } + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/conversation/ConversationUpdateForm.vue b/src/views/ai/dashboard/components/conversation/ConversationUpdateForm.vue new file mode 100644 index 0000000..90f68c6 --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/ConversationUpdateForm.vue @@ -0,0 +1,148 @@ +<template> + <Dialog title="设定" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="130px" + v-loading="formLoading" + > + <el-form-item label="角色设定" prop="systemMessage"> + <el-input + type="textarea" + v-model="formData.systemMessage" + :rows="4" + placeholder="请输入角色设定" + /> + </el-form-item> + <el-form-item label="模型" prop="modelId"> + <el-select v-model="formData.modelId" placeholder="请选择模型"> + <el-option + v-for="model in models" + :key="model.id" + :label="model.name" + :value="model.id" + /> + </el-select> + </el-form-item> + <el-form-item label="温度参数" prop="temperature"> + <el-input-number + v-model="formData.temperature" + placeholder="请输入温度参数" + :min="0" + :max="2" + :precision="2" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="回复数 Token 数" prop="maxTokens"> + <el-input-number + v-model="formData.maxTokens" + placeholder="请输入回复数 Token 数" + :min="0" + :max="8192" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="上下文数量" prop="maxContexts"> + <el-input-number + v-model="formData.maxContexts" + placeholder="请输入上下文数量" + :min="0" + :max="20" + class="!w-1/1" + /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import { AiModelTypeEnum } from '@/views/ai/utils/constants' + +/** AI 聊天对话的更新表单 */ +defineOptions({ name: 'ChatConversationUpdateForm' }) + +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formData = ref({ + id: undefined, + systemMessage: undefined, + modelId: undefined, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined +}) +const formRules = reactive({ + modelId: [{ required: true, message: '模型不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'blur' }], + temperature: [{ required: true, message: '温度参数不能为空', trigger: 'blur' }], + maxTokens: [{ required: true, message: '回复数 Token 数不能为空', trigger: 'blur' }], + maxContexts: [{ required: true, message: '上下文数量不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const models = ref([] as ModelVO[]) // 聊天模型列表 + +/** 打开弹窗 */ +const open = async (id: number) => { + dialogVisible.value = true + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + const data = await ChatConversationApi.getChatConversationMy(id) + formData.value = Object.keys(formData.value).reduce((obj, key) => { + if (data.hasOwnProperty(key)) { + obj[key] = data[key] + } + return obj + }, {}) + } finally { + formLoading.value = false + } + } + // 获得下拉数据 + models.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.CHAT) +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ChatConversationVO + await ChatConversationApi.updateChatConversationMy(data) + message.success('对话配置已更新') + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + systemMessage: undefined, + modelId: undefined, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/dashboard/components/conversation/HistoryConversationList.vue b/src/views/ai/dashboard/components/conversation/HistoryConversationList.vue new file mode 100644 index 0000000..fd1ec4f --- /dev/null +++ b/src/views/ai/dashboard/components/conversation/HistoryConversationList.vue @@ -0,0 +1,459 @@ +<!-- AI 对话 --> +<template> + <el-aside width="260px" class="conversation-container h-100%"> + <!-- 左顶部:对话 --> + <div class="h-100%"> + <el-button class="w-1/1 btn-new-conversation" type="primary" @click="createConversation"> + <Icon icon="ep:plus" class="mr-5px" /> + 新建对话 + </el-button> + + <!-- 左顶部:搜索对话 --> + <el-input + v-model="searchName" + size="large" + class="mt-10px search-input" + placeholder="搜索历史记录" + @keyup="searchConversation" + > + <template #prefix> + <Icon icon="ep:search" /> + </template> + </el-input> + + <!-- 左中间:对话列表 --> + <div class="conversation-list"> + <!-- 情况一:加载中 --> + <el-empty v-if="loading" description="." :v-loading="loading" /> + <!-- 情况二:按照 group 分组,展示聊天会话 list 列表 --> + <div v-for="conversationKey in Object.keys(conversationMap)" :key="conversationKey"> + <div + class="conversation-item classify-title" + v-if="conversationMap[conversationKey].length" + > + <el-text class="mx-1" size="small" tag="b">{{ conversationKey }}</el-text> + </div> + <div + class="conversation-item" + v-for="conversation in conversationMap[conversationKey]" + :key="conversation.id" + @click="handleConversationClick(conversation.id)" + @mouseover="hoverConversationId = conversation.id" + @mouseout="hoverConversationId = ''" + > + <div + :class=" + conversation.id === activeConversationId ? 'conversation active' : 'conversation' + " + > + <div class="title-wrapper"> + <img class="avatar" :src="conversation.roleAvatar || roleAvatarDefaultImg" /> + <span class="title">{{ conversation.title }}</span> + </div> + <div class="button-wrapper" v-show="hoverConversationId === conversation.id"> + <el-button class="btn" link @click.stop="handleTop(conversation)"> + <el-icon title="置顶" v-if="!conversation.pinned"><Top /></el-icon> + <el-icon title="置顶" v-if="conversation.pinned"><Bottom /></el-icon> + </el-button> + <el-button class="btn" link @click.stop="updateConversationTitle(conversation)"> + <el-icon title="编辑"> + <Icon icon="ep:edit" /> + </el-icon> + </el-button> + <el-button class="btn" link @click.stop="deleteChatConversation(conversation)"> + <el-icon title="删除对话"> + <Icon icon="ep:delete" /> + </el-icon> + </el-button> + </div> + </div> + </div> + </div> + <!-- 底部占位 --> + <div class="h-160px w-100%"></div> + </div> + </div> + + </el-aside> +</template> + +<script setup lang="ts"> +import { ChatConversationApi, ChatConversationVO } from '@/api/ai/chat/conversation' +import { Bottom, Top } from '@element-plus/icons-vue' +import roleAvatarDefaultImg from '@/assets/ai/gpt.svg' + +const message = useMessage() // 消息弹窗 + +// 定义属性 +const searchName = ref<string>('') // 对话搜索 +const modelName = ref<string>('common') // 对话搜索 +const activeConversationId = ref<number | null>(null) // 选中的对话,默认为 null +const hoverConversationId = ref<number | null>(null) // 悬浮上去的对话 +const conversationList = ref([] as ChatConversationVO[]) // 对话列表 +const conversationMap = ref<any>({}) // 对话分组 (置顶、今天、三天前、一星期前、一个月前) +const loading = ref<boolean>(false) // 加载中 +const loadingTime = ref<any>() // 加载中定时器 + +// 定义组件 props +const props = defineProps({ + activeId: { + type: String || null, + required: true + } +}) + +// 定义钩子 +const emits = defineEmits([ + 'onConversationCreate', + 'onConversationClick', + 'onConversationClear', + 'onConversationDelete' +]) + +/** 搜索对话 */ +const searchConversation = async (e) => { + // 恢复数据 + if (!searchName.value.trim().length) { + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } else { + // 过滤 + const filterValues = conversationList.value.filter((item) => { + return item.title.includes(searchName.value.trim()) + }) + conversationMap.value = await getConversationGroupByCreateTime(filterValues) + } +} + +/** 点击对话 */ +const handleConversationClick = async (id: number) => { + // 过滤出选中的对话 + const filterConversation = conversationList.value.filter((item) => { + return item.id === id + }) + // 回调 onConversationClick + // noinspection JSVoidFunctionReturnValueUsed + const success = emits('onConversationClick', filterConversation[0]) + // 切换对话 + if (success) { + activeConversationId.value = id + } +} + +/** 获取对话列表 */ +const getChatConversationList = async () => { + try { + // 加载中 + loadingTime.value = setTimeout(() => { + loading.value = true + }, 50) + + // 1.1 获取 对话数据 + conversationList.value = await ChatConversationApi.getChatConversationEnergyList(modelName.value) + if(conversationList.value.length == 0) { + await createConversation() + } + // 1.2 排序 + conversationList.value.sort((a, b) => { + return b.createTime - a.createTime + }) + // 1.3 没有任何对话情况 + if (conversationList.value.length === 0) { + activeConversationId.value = null + conversationMap.value = {} + return + } + + // 2. 对话根据时间分组(置顶、今天、一天前、三天前、七天前、30 天前) + conversationMap.value = await getConversationGroupByCreateTime(conversationList.value) + } finally { + // 清理定时器 + if (loadingTime.value) { + clearTimeout(loadingTime.value) + } + // 加载完成 + loading.value = false + } +} + +/** 按照 creteTime 创建时间,进行分组 */ +const getConversationGroupByCreateTime = async (list: ChatConversationVO[]) => { + // 排序、指定、时间分组(今天、一天前、三天前、七天前、30天前) + // noinspection NonAsciiCharacters + const groupMap = { + 置顶: [], + 今天: [], + 一天前: [], + 三天前: [], + 七天前: [], + 三十天前: [] + } + // 当前时间的时间戳 + const now = Date.now() + // 定义时间间隔常量(单位:毫秒) + const oneDay = 24 * 60 * 60 * 1000 + const threeDays = 3 * oneDay + const sevenDays = 7 * oneDay + const thirtyDays = 30 * oneDay + for (const conversation of list) { + // 置顶 + if (conversation.pinned) { + groupMap['置顶'].push(conversation) + continue + } + // 计算时间差(单位:毫秒) + const diff = now - conversation.createTime + // 根据时间间隔判断 + if (diff < oneDay) { + groupMap['今天'].push(conversation) + } else if (diff < threeDays) { + groupMap['一天前'].push(conversation) + } else if (diff < sevenDays) { + groupMap['三天前'].push(conversation) + } else if (diff < thirtyDays) { + groupMap['七天前'].push(conversation) + } else { + groupMap['三十天前'].push(conversation) + } + } + return groupMap +} + +/** 新建对话 */ +const createConversation = async () => { + // 1. 新建对话 + const conversationId = await ChatConversationApi.createChatConversationEnergy( + {modelName: modelName.value} as unknown as ChatConversationVO + ) + // 2. 获取对话内容 + await getChatConversationList() + // 3. 选中对话 + await handleConversationClick(conversationId) + // 4. 回调 + emits('onConversationCreate') +} + +/** 修改对话的标题 */ +const updateConversationTitle = async (conversation: ChatConversationVO) => { + // 1. 二次确认 + const { value } = await ElMessageBox.prompt('修改标题', { + inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格 + inputErrorMessage: '标题不能为空', + inputValue: conversation.title + }) + // 2. 发起修改 + await ChatConversationApi.updateChatConversationMy({ + id: conversation.id, + title: value + } as ChatConversationVO) + message.success('重命名成功') + // 3. 刷新列表 + await getChatConversationList() + // 4. 过滤当前切换的 + const filterConversationList = conversationList.value.filter((item) => { + return item.id === conversation.id + }) + if (filterConversationList.length > 0) { + // tip:避免切换对话 + if (activeConversationId.value === filterConversationList[0].id) { + emits('onConversationClick', filterConversationList[0]) + } + } +} + +/** 删除聊天对话 */ +const deleteChatConversation = async (conversation: ChatConversationVO) => { + try { + // 删除的二次确认 + await message.delConfirm(`是否确认删除对话 - ${conversation.title}?`) + // 发起删除 + await ChatConversationApi.deleteChatConversationMy(conversation.id) + message.success('对话已删除') + // 刷新列表 + await getChatConversationList() + // 回调 + emits('onConversationDelete', conversation) + } catch {} +} + +/** 清空对话 */ +const handleClearConversation = async () => { + try { + await message.confirm('确认后对话会全部清空,置顶的对话除外。') + await ChatConversationApi.deleteChatConversationMyByUnpinned() + ElMessage({ + message: '操作成功!', + type: 'success' + }) + // 清空 对话 和 对话内容 + activeConversationId.value = null + // 获取 对话列表 + await getChatConversationList() + // 回调 方法 + emits('onConversationClear') + } catch {} +} + +/** 对话置顶 */ +const handleTop = async (conversation: ChatConversationVO) => { + // 更新对话置顶 + conversation.pinned = !conversation.pinned + await ChatConversationApi.updateChatConversationMy(conversation) + // 刷新对话 + await getChatConversationList() +} + +// ============ 角色仓库 ============ + +/** 角色仓库抽屉 */ +const roleRepositoryOpen = ref<boolean>(false) // 角色仓库是否打开 +const handleRoleRepository = async () => { + roleRepositoryOpen.value = !roleRepositoryOpen.value +} + +/** 监听选中的对话 */ +const { activeId } = toRefs(props) +watch(activeId, async (newValue, oldValue) => { + activeConversationId.value = newValue as string +}) + +// 定义 public 方法 +defineExpose({ createConversation }) + +/** 初始化 */ +onMounted(async () => { + // 获取 对话列表 + await getChatConversationList() + // 默认选中 + if (props.activeId) { + activeConversationId.value = props.activeId + } else { + // 首次默认选中第一个 + if (conversationList.value.length) { + activeConversationId.value = conversationList.value[0].id + // 回调 onConversationClick + await emits('onConversationClick', conversationList.value[0]) + } + } +}) +</script> + +<style scoped lang="scss"> +.conversation-container { + position: relative; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px 10px 0; + overflow: hidden; + + .btn-new-conversation { + padding: 18px 0; + } + + .search-input { + margin-top: 20px; + } + + .conversation-list { + overflow: auto; + height: 100%; + + .classify-title { + padding-top: 10px; + } + + .conversation-item { + margin-top: 5px; + } + + .conversation { + display: flex; + flex-direction: row; + justify-content: space-between; + flex: 1; + padding: 0 5px; + cursor: pointer; + border-radius: 5px; + align-items: center; + line-height: 30px; + + &.active { + background-color: #e6e6e6; + + .button { + display: inline-block; + } + } + + .title-wrapper { + display: flex; + flex-direction: row; + align-items: center; + } + + .title { + padding: 2px 10px; + max-width: 220px; + font-size: 14px; + font-weight: 400; + color: rgba(0, 0, 0, 0.77); + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + .avatar { + width: 25px; + height: 25px; + border-radius: 5px; + display: flex; + flex-direction: row; + justify-items: center; + } + + // 对话编辑、删除 + .button-wrapper { + right: 2px; + display: flex; + flex-direction: row; + justify-items: center; + color: #606266; + + .btn { + margin: 0; + } + } + } + } + + // 角色仓库、清空未设置对话 + .tool-box { + position: absolute; + bottom: 0; + left: 0; + right: 0; + //width: 100%; + padding: 0 20px; + background-color: #f4f4f4; + box-shadow: 0 0 1px 1px rgba(228, 228, 228, 0.8); + line-height: 35px; + display: flex; + justify-content: space-between; + align-items: center; + color: var(--el-text-color); + + > div { + display: flex; + align-items: center; + color: #606266; + padding: 0; + margin: 0; + cursor: pointer; + + > span { + margin-left: 5px; + } + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/message/HistoryMessageDialog.vue b/src/views/ai/dashboard/components/message/HistoryMessageDialog.vue new file mode 100644 index 0000000..68b1a73 --- /dev/null +++ b/src/views/ai/dashboard/components/message/HistoryMessageDialog.vue @@ -0,0 +1,375 @@ +<template> + <DialogHistory title="历史建议" v-model="dialogVisible" width="1200"> + <!-- 左侧:对话列表 --> + <ConversationList + v-show="false" + :active-id="activeConversationId" + ref="conversationListRef" + /> + <!-- 右侧:对话详情 --> + <el-container class="detail-container"> + <el-header class="header"> + <div class="title"> + {{ activeConversation?.title ? activeConversation?.title : '' }} + <span v-if="activeMessageList.length">({{ activeMessageList.length }})</span> + </div> + <div class="btns" v-if="activeConversation"> + <el-button size="small" class="btn" @click="handlerMessageClear"> + <Icon icon="heroicons-outline:archive-box-x-mark" color="#73C4FF" /> + </el-button> + <el-button size="small" class="btn" @click="handleGoBottomMessage"> + <Icon icon="ep:download" color="#73C4FF" /> + </el-button> + <el-button size="small" class="btn" @click="handleGoTopMessage"> + <Icon icon="ep:top" color="#73C4FF" /> + </el-button> + </div> + </el-header> + + <!-- main:消息列表 --> + <el-main class="main-container"> + <div class="message-container"> + <!-- 情况一:无聊天对话时 --> + <ConversationListEmpty + v-if="!activeConversation" + /> + <!-- 情况二:消息列表为空 --> + <MessageListEmpty + v-if="activeMessageList.length === 0 && activeConversation" + /> + <!-- 情况三:消息列表不为空 --> + <HistoryMessageList + v-if="activeMessageList.length > 0" + ref="messageRef" + :conversation="activeConversation" + :list="activeMessageList" + /> + </div> + </el-main> + </el-container> + + </DialogHistory> +</template> + +<script setup lang="ts"> +import {ChatMessageApi, ChatMessageVO} from '@/api/ai/chat/message' +import { ChatConversationVO } from '@/api/ai/chat/conversation' +import ConversationList from '../conversation/HistoryConversationList.vue' +import HistoryMessageList from './HistoryMessageList.vue' +import MessageListEmpty from './MessageListEmpty.vue' +import ConversationListEmpty from '../conversation/ConversationListEmpty.vue' + +/** AI 聊天对话 列表 */ +defineOptions({ name: 'HistoryMessageDialog' }) + +const route = useRoute() // 路由 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 + +// 聊天对话 +const conversationListRef = ref() +const activeConversation = ref<ChatConversationVO | null>(null) // 选中的 Conversation + +// 消息列表 +const messageRef = ref() +const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表 + + +/** 打开弹窗 */ +const open = async (messages: ChatMessageVO[], conversation: ChatConversationVO) => { + dialogVisible.value = true + await nextTick() // 等待弹窗DOM挂载 + activeMessageList.value = messages + activeConversation.value = conversation +} + +defineExpose({ open }) // 提供方法给 parent 调用 + +/** 回到 message 列表的顶部 */ +const handleGoTopMessage = () => { + messageRef.value.handlerGoTop() +} + +/** 回到 message 列表的底部 */ +const handleGoBottomMessage = () => { + messageRef.value.handleGoBottom() +} + +/** 处理 message 清空 */ +const handlerMessageClear = async () => { + if (!activeConversation.value) { + return + } + try { + // 确认提示 + await message.delConfirm('确认清空对话消息?') + // 清空对话 + await ChatMessageApi.deleteByConversationId(activeConversation.value.id) + // 刷新 message 列表 + activeMessageList.value = [] + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { +}) +</script> + +<style lang="scss" scoped> + +// 头部 +.detail-container { + display: flex; + flex-direction: column; + width: 100%; + height: 820px; + background-color: rgba(0, 0, 0, 0); /* 透明背景 */ + z-index: 1; + .header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + box-shadow: 0 0 0 0 #dcdfe6; + + .title { + font-size: 18px; + font-weight: bold; + color: gold; + } + + .btns { + display: flex; + width: 300px; + flex-direction: row; + justify-content: flex-end; + + .btn { + padding: 10px; + } + + /* 所有状态通用透明背景 */ + :deep(.el-button) { + background: transparent !important; + border-color: currentColor; /* 保持与文字同色 */ + color: #409EFF; /* 蓝色文字 */ + } + + /* 悬停状态 */ + :deep(.el-button:hover) { + background: rgba(0, 0, 0, 0.05) !important; /* 轻微悬停反馈 */ + } + + /* 点击状态 */ + :deep(.el-button:active) { + background: rgba(0, 0, 0, 0.1) !important; + } + + /* 禁用状态 */ + :deep(.el-button.is-disabled) { + opacity: 0.6; + background: transparent !important; + } + } + } +} + +// main 容器 +.main-container { + padding: 0; + position: relative; + overflow: hidden; /* 隐藏外层滚动条 */ + width: 100%; + + .message-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: hidden; + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.15) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } + } +} + +// 底部 +.footer-container { + display: flex; + flex-direction: column; + height: 114px; + margin-left: 10px; + padding: 0; + + // 输入框 + .input-container { + display: flex; + flex-direction: column; + height: auto; + width: 876px; + margin: 0; + padding: 0; + overflow-y: auto; /* 垂直方向溢出时显示滚动条 */ + overflow-x: hidden; /* 水平方向隐藏滚动条 */ + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.15) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } + + .prompt-from { + display: flex; + flex-direction: column; + padding: 9px 10px; + width: 876px; + height: 114px; + background: rgba(115,196,255,0.05); + border-radius: 4px 4px 4px 4px; + border: 1px solid #73C4FF; + } + + .prompt-input { + width: 876px; + height: 113.55px; + font-weight: 400; + font-size: 14px; + background-color: rgba(219,238,255,0); + line-height: 21px; + text-align: left; + font-style: normal; + text-transform: none; + border: 0; + color: rgba(219,238,255,0.6); + } + + .prompt-input:focus { + outline: none; + } + + .prompt-btns { + display: flex; + justify-content: space-between; + padding-bottom: 0; + padding-top: 5px; + + .content { + /* 默认状态 */ + .el-button { + background: transparent !important; + border-color: rgba(115, 196, 255, 0.5); + color: #73C4FF; + border-radius: 15px !important; + } + + /* 上下文图标处理 */ + .content-icon { + color: blue; /* 图标颜色 */ + font-size: 18px; + margin-right: 10px; + background: url("@/assets/ai/zhuanlu/content.png"); + vertical-align: middle; + } + + /* 选中状态 */ + .active-button { + background: #409eff !important; + border-color: #409eff !important; + color: white !important; + .content-icon { + background: url("@/assets/ai/zhuanlu/content_select.png"); + vertical-align: middle; + } + } + + /* 按钮组间距处理 */ + .button-group .el-button { + margin-left: 0; + border-radius: 4px; + } + + /* 悬停效果 */ + .el-button:not(.active-button):hover { + border-color: rgba(115,196,255,0.5); + color: #409eff; + } + } + .message { + /* 所有状态通用透明背景 */ + :deep(.el-button) { + background: rgba(73, 254, 210, 0.8) !important; + border-color: currentColor; /* 保持与文字同色 */ + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 16px; + color: #123C4E; + clip-path: polygon( + 0 0, + 100% 0, + 100% 100%, + 10px 100%, /* 右下方向留出10px */ + 0 calc(100% - 10px) /* 左上方向留出10px */ + ); + position: relative; + padding-left: 15px; /* 增加右侧留白 */ + } + + /* 悬停状态 */ + :deep(.el-button:hover) { + background: rgba(73, 254, 210, 0.6) !important; /* 轻微悬停反馈 */ + } + + /* 点击状态 */ + :deep(.el-button:active) { + background: rgba(73, 254, 210, 1) !important; + } + + /* 禁用状态 */ + :deep(.el-button.is-disabled) { + opacity: 0.6; + background: transparent !important; + } + + /* 核心样式覆盖 */ + :deep(.el-switch__core) { + background: transparent !important; + border-radius: 0 0 15px 0 !important; + border: none !important; + height: 40px !important; + } + + /* 按钮内容容器 */ + .button-content { + display: flex; + align-items: center; + padding: 0 15px; + height: 100%; + } + } + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/message/HistoryMessageList.vue b/src/views/ai/dashboard/components/message/HistoryMessageList.vue new file mode 100644 index 0000000..91371e1 --- /dev/null +++ b/src/views/ai/dashboard/components/message/HistoryMessageList.vue @@ -0,0 +1,290 @@ +<template> + <div ref="messageContainer" class="h-100% overflow-y-auto relative"> + <div class="chat-list" v-for="(item, index) in list" :key="index"> + <!-- 靠左 message:system、assistant 类型 --> + <div class="left-message message-item" v-if="item.type !== 'user'"> + <div class="avatar"> + <el-avatar :src="roleAvatar" /> + </div> + <div class="message"> + <div> + <el-text class="time">{{ formatDate(item.createTime) }}</el-text> + </div> + <div v-if="item.thinkingFlag" class="left-text-container-thinking" ref="markdownViewRef"> + <MarkdownView v-if="item.thinking" class="left-text thinking" :content="item.thinking" /> + <MarkdownView v-else class="left-text thinking" :content="item.content" /> + </div> + <div v-else-if="item.thinking" class="left-text-container-thinking" ref="markdownViewRef"> + <MarkdownView class="left-text thinking" :content="item.thinking" /> + </div> + <div v-else class="left-text-container-conclusion" ref="markdownViewRef"> + <MarkdownView class="left-text" :content="item.content" /> + </div> + <div class="left-text-container-conclusion" ref="markdownViewRef"> + <MarkdownView class="left-text" :content="item.conclusion" /> + </div> + <div class="right-btns"> + <el-button class="btn-cus" link @click="copyContent(item.content)"> + <img class="btn-image" src="@/assets/ai/zhuanlu/copy.png" /> + </el-button> + <!-- 暂时不能删除,随意删除会影响首页echarts图表展示 --> +<!-- <el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)">--> +<!-- <img class="btn-image h-17px" src="@/assets/ai/zhuanlu/delete.png" />--> +<!-- </el-button>--> + </div> + </div> + </div> + <!-- 靠右 message:user 类型 --> + <div class="left-message message-item" v-if="item.type === 'user'"> + <div class="avatar"> + <el-avatar :src="userAvatar" /> + </div> + <div class="message"> + <div> + <el-text class="time">{{ formatDate(item.createTime) }}</el-text> + </div> + <div class="right-text-container"> + <div class="right-text">{{ item.content }}</div> + </div> + <div class="right-btns"> + <el-button class="btn-cus" link @click="copyContent(item.content)"> + <img class="btn-image" src="@/assets/ai/zhuanlu/copy.png" /> + </el-button> + <el-button class="btn-cus" link @click="onDelete(item.id)"> + <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/delete.png" /> + </el-button> + </div> + </div> + </div> + </div> + </div> + <!-- 回到底部 --> + <div v-if="isScrolling" class="to-bottom" @click="handleGoBottom"> + <el-button :icon="ArrowDownBold" circle /> + </div> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' +import { formatDate } from '@/utils/formatTime' +import MarkdownView from '@/components/MarkdownView/index.vue' +import { useClipboard } from '@vueuse/core' +import { ArrowDownBold } from '@element-plus/icons-vue' +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import { ChatConversationVO } from '@/api/ai/chat/conversation' +import { useUserStore } from '@/store/modules/user' +import userAvatarDefaultImg from '@/assets/ai/zhuanlu/user.png' +import roleAvatarDefaultImg from '@/assets/ai/zhuanlu/assistant.png' + +const dialogVisible = ref(false) // 弹窗的是否展示 + +const message = useMessage() // 消息弹窗 +const { copy } = useClipboard() // 初始化 copy 到粘贴板 +const userStore = useUserStore() + +// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方) +const messageContainer: any = ref(null) +const isScrolling = ref(false) //用于判断用户是否在滚动 + +const userAvatar = computed(() => userAvatarDefaultImg) +const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg) + +// 定义 props +const props = defineProps({ + conversation: { + type: Object as PropType<ChatConversationVO>, + required: true + }, + list: { + type: Array as PropType<ChatMessageVO[]>, + required: true + } +}) + +const { list } = toRefs(props) // 消息列表 + +const emits = defineEmits(['onDeleteSuccess']) // 定义 emits + +// ============ 处理对话滚动 ============== + +/** 滚动到底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + // 注意要使用 nextTick 以免获取不到 dom + await nextTick() + if (isIgnore || !isScrolling.value) { + messageContainer.value.scrollTop = + messageContainer.value.scrollHeight - messageContainer.value.offsetHeight + } +} + +function handleScroll() { + const scrollContainer = messageContainer.value + const scrollTop = scrollContainer.scrollTop + const scrollHeight = scrollContainer.scrollHeight + const offsetHeight = scrollContainer.offsetHeight + if (scrollTop + offsetHeight < scrollHeight - 100) { + // 用户开始滚动并在最底部之上,取消保持在最底部的效果 + isScrolling.value = true + } else { + // 用户停止滚动并滚动到最底部,开启保持到最底部的效果 + isScrolling.value = false + } +} + +/** 回到底部 */ +const handleGoBottom = async () => { + const scrollContainer = messageContainer.value + scrollContainer.scrollTop = scrollContainer.scrollHeight +} + +/** 回到顶部 */ +const handlerGoTop = async () => { + const scrollContainer = messageContainer.value + scrollContainer.scrollTop = 0 +} + +defineExpose({ scrollToBottom, handlerGoTop, handleGoBottom }) // 提供方法给 parent 调用 + +// ============ 处理消息操作 ============== + +/** 复制 */ +const copyContent = async (content) => { + await copy(content) + message.success('复制成功!') +} + +/** 删除 */ +const onDelete = async (id) => { + // 删除 message + await ChatMessageApi.deleteChatMessage(id) + message.success('删除成功!') + // 回调 + emits('onDeleteSuccess') +} + +/** 初始化 */ +onMounted(async () => { + messageContainer.value.addEventListener('scroll', handleScroll) +}) +</script> + +<style scoped lang="scss"> +/* 添加或修改以下样式 */ +div[ref="messageContainer"] { + height: 100%; /* 继承父容器高度 */ + overflow-y: auto; /* 启用垂直滚动 */ + max-height: 720px; /* 或根据实际需求调整 */ + padding-bottom: 20px; /* 避免底部内容被截断 */ +} +// 中间 +.chat-list { + display: flex; + flex-direction: column; + height: auto; + padding: 0 20px; + + .left-message { + display: flex; + flex-direction: row; + } + + .message { + display: flex; + flex-direction: column; + text-align: left; + margin: 0 15px; + + .time { + text-align: left; + line-height: 30px; + } + + .left-text-container-thinking { + position: relative; + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background: rgba(115,196,255,0.05); + border-radius: 4px 4px 4px 4px; + border-left: 1px solid #73C4FF; + padding: 10px 10px 5px 10px; + .left-text { + color: rgba(219,238,255,0.5); + font-size: 0.85rem; + } + } + + .left-text-container-conclusion { + position: relative; + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background: rgba(115,196,255,0); + border-radius: 4px 4px 4px 4px; + padding: 0 10px 5px 0; + .left-text { + color: rgba(219,238,255,0.8); + font-size: 1rem; + } + } + + .right-text-container { + display: flex; + flex-direction: row-reverse; + + .right-text { + font-size: 0.95rem; + color: #DBEEFF; + display: inline; + background: rgba(40,139,255,0.1); + box-shadow: 0 0 0 0 rgba(40,139,255,0.3); + border-radius: 10px; + padding: 10px; + width: auto; + overflow-wrap: break-word; + white-space: pre-wrap; + } + } + + .left-btns { + display: flex; + flex-direction: row; + margin-top: 8px; + } + + .right-btns { + display: flex; + flex-direction: row-reverse; + margin-top: 8px; + } + } + + // 复制、删除按钮 + .btn-cus { + display: flex; + background-color: transparent; + align-items: center; + + .btn-image { + height: 20px; + } + } + + .btn-cus:hover { + cursor: pointer; + background-color: #f6f6f6; + } +} + +// 回到底部 +.to-bottom { + position: absolute; + z-index: 1000; + bottom: 0; + right: 50%; + .el-button { + background: rgba(255,215,0,0.2); + border: solid 1px rgba(255,215,0,0.8); + color: rgba(255,215,0,0.8); + } +} +</style> diff --git a/src/views/ai/dashboard/components/message/MessageKnowledge.vue b/src/views/ai/dashboard/components/message/MessageKnowledge.vue new file mode 100644 index 0000000..5ff3f63 --- /dev/null +++ b/src/views/ai/dashboard/components/message/MessageKnowledge.vue @@ -0,0 +1,104 @@ +<!-- 知识引用组件 --> +<template> + <!-- 知识引用列表 --> + <div v-if="segments && segments.length > 0" class="mt-10px p-10px rounded-8px bg-[#f5f5f5]"> + <div class="text-14px text-[#666] mb-8px flex items-center"> + <Icon icon="ep:document" class="mr-5px" /> 知识引用 + </div> + <div class="flex flex-wrap gap-8px"> + <div + v-for="(doc, index) in documentList" + :key="index" + class="p-8px px-12px bg-white rounded-6px cursor-pointer transition-all hover:bg-[#e6f4ff]" + @click="handleClick(doc)" + > + <div class="text-14px text-[#333] mb-4px"> + {{ doc.title }} + <span class="text-12px text-[#999] ml-4px">({{ doc.segments.length }} 条)</span> + </div> + </div> + </div> + </div> + + <!-- 知识引用详情弹窗 --> + <el-popover + v-model:visible="dialogVisible" + :width="600" + trigger="click" + placement="top-start" + :offset="55" + popper-class="knowledge-popover" + > + <template #reference> + <div ref="documentRef"></div> + </template> + <template #default> + <div class="text-16px font-bold mb-12px">{{ document?.title }}</div> + <div class="max-h-[60vh] overflow-y-auto"> + <div + v-for="(segment, index) in document?.segments" + :key="index" + class="p-12px border-b-solid border-b-[#eee] last:border-b-0" + > + <div + class="block mb-8px px-8px py-2px bg-[#f5f5f5] rounded-4px text-12px text-[#666] w-fit" + > + 分段 {{ segment.id }} + </div> + <div class="text-14px leading-[1.6] text-[#333] mt-[10px]"> + {{ segment.content }} + </div> + </div> + </div> + </template> + </el-popover> +</template> + +<script setup lang="ts"> +const props = defineProps<{ + segments: { + id: number + documentId: number + documentName: string + content: string + }[] +}>() + +const document = ref<{ + id: number + title: string + segments: { + id: number + content: string + }[] +} | null>(null) // 知识库文档列表 +const dialogVisible = ref(false) // 知识引用详情弹窗 +const documentRef = ref<HTMLElement>() // 知识引用详情弹窗 Ref + +/** 按照 document 聚合 segments */ +const documentList = computed(() => { + if (!props.segments) return [] + + const docMap = new Map() + props.segments.forEach((segment) => { + if (!docMap.has(segment.documentId)) { + docMap.set(segment.documentId, { + id: segment.documentId, + title: segment.documentName, + segments: [] + }) + } + docMap.get(segment.documentId).segments.push({ + id: segment.id, + content: segment.content + }) + }) + return Array.from(docMap.values()) +}) + +/** 点击 document 处理 */ +const handleClick = (doc: any) => { + document.value = doc + dialogVisible.value = true +} +</script> diff --git a/src/views/ai/dashboard/components/message/MessageList.vue b/src/views/ai/dashboard/components/message/MessageList.vue new file mode 100644 index 0000000..616b9f4 --- /dev/null +++ b/src/views/ai/dashboard/components/message/MessageList.vue @@ -0,0 +1,305 @@ +<template> + <div ref="messageContainer" class="h-100% overflow-y-auto relative"> + <div class="chat-list" v-for="(item, index) in list" :key="index"> + <!-- 靠左 message:system、assistant 类型 --> + <div class="left-message message-item" v-if="item.type !== 'user'"> + <div class="avatar"> + <el-avatar :src="roleAvatar" /> + </div> + <div class="message"> + <div> + <el-text class="time">{{ formatDate(item.createTime) }}</el-text> + </div> + <div v-if="item.thinkingFlag" class="left-text-container-thinking" ref="markdownViewRef"> + <MarkdownView v-if="item.thinking" class="left-text thinking" :content="item.thinking" /> + <MarkdownView v-else class="left-text thinking" :content="item.content" /> + </div> + <div v-else-if="item.thinking" class="left-text-container-thinking" ref="markdownViewRef"> + <MarkdownView class="left-text thinking" :content="item.thinking" /> + </div> + <div class="left-text-container-conclusion" ref="markdownViewRef"> + <MarkdownView class="left-text" :content="item.conclusion" /> + </div> + <div class="left-btns"> + <el-button class="btn-cus" link @click="copyContent(item.content)"> + <img class="btn-image" src="@/assets/ai/zhuanlu/copy.png" /> + </el-button> + <el-button v-if="item.id > 0" class="btn-cus" link @click="onDelete(item.id)"> + <img class="btn-image h-17px" src="@/assets/ai/zhuanlu/delete.png" /> + </el-button> + </div> + </div> + </div> + <!-- 靠右 message:user 类型 --> + <div class="right-message message-item" v-if="item.type === 'user'"> + <div class="avatar"> + <el-avatar :src="userAvatar" /> + </div> + <div class="message"> + <div> + <el-text class="time">{{ formatDate(item.createTime) }}</el-text> + </div> + <div class="right-text-container"> + <div class="right-text">{{ item.content }}</div> + </div> + <div class="right-btns"> + <el-button class="btn-cus" link @click="copyContent(item.content)"> + <img class="btn-image" src="@/assets/ai/zhuanlu/copy.png" /> + </el-button> + <el-button class="btn-cus" link @click="onDelete(item.id)"> + <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/delete.png" /> + </el-button> + <el-button class="btn-cus" link @click="onRefresh(item)"> + <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/refresh.png" /> + </el-button> + <el-button class="btn-cus" link @click="onEdit(item)"> + <img class="btn-image h-17px mr-12px" src="@/assets/ai/zhuanlu/edit.png" /> + </el-button> + </div> + </div> + </div> + </div> + </div> + <!-- 回到底部 --> + <div v-if="isScrolling" class="to-bottom" @click="handleGoBottom"> + <el-button :icon="ArrowDownBold" circle /> + </div> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' +import { formatDate } from '@/utils/formatTime' +import MarkdownView from '@/components/MarkdownView/index.vue' +import { useClipboard } from '@vueuse/core' +import { ArrowDownBold} from '@element-plus/icons-vue' +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import { ChatConversationVO } from '@/api/ai/chat/conversation' +import { useUserStore } from '@/store/modules/user' +import userAvatarDefaultImg from '@/assets/ai/zhuanlu/user.png' +import roleAvatarDefaultImg from '@/assets/ai/zhuanlu/assistant.png' + +const message = useMessage() // 消息弹窗 +const { copy } = useClipboard() // 初始化 copy 到粘贴板 +const userStore = useUserStore() + +// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方) +const messageContainer: any = ref(null) +const isScrolling = ref(false) //用于判断用户是否在滚动 + +const userAvatar = computed(() => userAvatarDefaultImg) +// const userAvatar = computed(() => userStore.user.avatar || userAvatarDefaultImg) +const roleAvatar = computed(() => props.conversation.roleAvatar ?? roleAvatarDefaultImg) + +// 定义 props +const props = defineProps({ + conversation: { + type: Object as PropType<ChatConversationVO>, + required: true + }, + list: { + type: Array as PropType<ChatMessageVO[]>, + required: true + } +}) + +const { list } = toRefs(props) // 消息列表 + +const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // 定义 emits + +// ============ 处理对话滚动 ============== + +/** 滚动到底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + // 注意要使用 nextTick 以免获取不到 dom + await nextTick() + if (isIgnore || !isScrolling.value) { + messageContainer.value.scrollTop = + messageContainer.value.scrollHeight - messageContainer.value.offsetHeight + } +} + +function handleScroll() { + const scrollContainer = messageContainer.value + const scrollTop = scrollContainer.scrollTop + const scrollHeight = scrollContainer.scrollHeight + const offsetHeight = scrollContainer.offsetHeight + if (scrollTop + offsetHeight < scrollHeight - 100) { + // 用户开始滚动并在最底部之上,取消保持在最底部的效果 + isScrolling.value = true + } else { + // 用户停止滚动并滚动到最底部,开启保持到最底部的效果 + isScrolling.value = false + } +} + +/** 回到底部 */ +const handleGoBottom = async () => { + const scrollContainer = messageContainer.value + console.log(scrollContainer.scrollHeight) + scrollContainer.scrollTop = scrollContainer.scrollHeight +} + +/** 回到顶部 */ +const handlerGoTop = async () => { + const scrollContainer = messageContainer.value + console.log(scrollContainer.scrollHeight) + scrollContainer.scrollTop = 0 +} + +defineExpose({ scrollToBottom, handlerGoTop, handleGoBottom }) // 提供方法给 parent 调用 + +// ============ 处理消息操作 ============== + +/** 复制 */ +const copyContent = async (content) => { + await copy(content) + message.success('复制成功!') +} + +/** 删除 */ +const onDelete = async (id) => { + // 删除 message + await ChatMessageApi.deleteChatMessage(id) + message.success('删除成功!') + // 回调 + emits('onDeleteSuccess') +} + +/** 刷新 */ +const onRefresh = async (message: ChatMessageVO) => { + emits('onRefresh', message) +} + +/** 编辑 */ +const onEdit = async (message: ChatMessageVO) => { + emits('onEdit', message) +} + +/** 初始化 */ +onMounted(async () => { + messageContainer.value.addEventListener('scroll', handleScroll) +}) +</script> + +<style scoped lang="scss"> +.message-container { + position: relative; + overflow-y: scroll; +} + +// 中间 +.chat-list { + display: flex; + flex-direction: column; + overflow-y: hidden; + padding: 0 20px; + .message-item { + margin-top: 50px; + } + + .left-message { + display: flex; + flex-direction: row; + } + + .right-message { + display: flex; + flex-direction: row-reverse; + justify-content: flex-start; + } + + .message { + display: flex; + flex-direction: column; + text-align: left; + margin: 0 15px; + + .time { + text-align: left; + line-height: 30px; + } + + .left-text-container-thinking { + position: relative; + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background: rgba(115,196,255,0.05); + border-radius: 4px 4px 4px 4px; + border-left: 1px solid #73C4FF; + padding: 10px 10px 5px 10px; + .left-text { + color: rgba(219,238,255,0.5); + font-size: 0.85rem; + } + } + + .left-text-container-conclusion { + position: relative; + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background: rgba(115,196,255,0); + border-radius: 4px 4px 4px 4px; + padding: 20px 10px 5px 0; + .left-text { + color: rgba(219,238,255,0.8); + font-size: 1rem; + } + } + + .right-text-container { + display: flex; + flex-direction: row-reverse; + + .right-text { + font-size: 0.95rem; + color: #DBEEFF; + display: inline; + background: rgba(40,139,255,0.1); + box-shadow: 0 0 0 1px rgba(40,139,255,0.3); + border-radius: 10px; + padding: 10px; + width: auto; + overflow-wrap: break-word; + white-space: pre-wrap; + } + } + + .left-btns { + display: flex; + flex-direction: row; + margin-top: 8px; + } + + .right-btns { + display: flex; + flex-direction: row-reverse; + margin-top: 8px; + } + } + + // 复制、删除按钮 + .btn-cus { + display: flex; + background-color: transparent; + align-items: center; + + .btn-image { + height: 20px; + } + } + + .btn-cus:hover { + cursor: pointer; + background-color: #f6f6f6; + } +} + +// 回到底部 +.to-bottom { + position: absolute; + z-index: 1000; + bottom: 0; + right: 50%; +} +</style> diff --git a/src/views/ai/dashboard/components/message/MessageListEmpty.vue b/src/views/ai/dashboard/components/message/MessageListEmpty.vue new file mode 100644 index 0000000..e746f74 --- /dev/null +++ b/src/views/ai/dashboard/components/message/MessageListEmpty.vue @@ -0,0 +1,50 @@ +<!-- 消息列表为空时,展示 prompt 列表 --> +<template> + <div class="chat-empty"> + <!-- title --> + <div class="center-container"> + <div class="avatar"><img src="@/assets/ai/zhuanlu/assistant.png" /></div> + <div class="gradient-text">欢迎来到转炉煤气调度大模型</div> + </div> + </div> +</template> +<script setup lang="ts"> + +const emits = defineEmits(['onPrompt']) + +</script> +<style scoped lang="scss"> +.chat-empty { + position: relative; + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + height: 100%; + .center-container { + margin-top: 30%; + display: inline-block; + + div { + float: left; + } + + .avatar { + background: transparent; + width: 50px; + height: 50px; + position: relative; + } + /* 渐变文字样式 */ + .gradient-text { + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 24px; + background: linear-gradient(0deg, #49FFD3 0%, #4585FF 100%); + -webkit-background-clip: text; + color: transparent; + margin: 4px 0 0 10px; + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/message/MessageLoading.vue b/src/views/ai/dashboard/components/message/MessageLoading.vue new file mode 100644 index 0000000..f3198cb --- /dev/null +++ b/src/views/ai/dashboard/components/message/MessageLoading.vue @@ -0,0 +1,15 @@ +<!-- message 加载页面 --> +<template> + <div class="message-loading" > + <el-skeleton animated /> + </div> +</template> + +<script setup lang="ts"> + +</script> +<style scoped lang="scss"> +.message-loading { + padding: 30px 30px; +} +</style> diff --git a/src/views/ai/dashboard/components/message/MessageNewConversation.vue b/src/views/ai/dashboard/components/message/MessageNewConversation.vue new file mode 100644 index 0000000..40c3107 --- /dev/null +++ b/src/views/ai/dashboard/components/message/MessageNewConversation.vue @@ -0,0 +1,46 @@ +<!-- 无聊天对话时,在 message 区域,可以新增对话 --> +<template> + <div class="new-chat"> + <div class="box-center"> + <div class="tip">点击下方按钮,开始你的对话吧</div> + <div class="btns"> + <el-button type="primary" round @click="handlerNewChat">新建对话</el-button> + </div> + </div> + </div> +</template> +<script setup lang="ts"> +const emits = defineEmits(['onNewConversation']) + +/** 新建 conversation 聊天对话 */ +const handlerNewChat = () => { + emits('onNewConversation') +} +</script> +<style scoped lang="scss"> +.new-chat { + display: flex; + flex-direction: row; + justify-content: center; + width: 100%; + height: 100%; + + .box-center { + display: flex; + flex-direction: column; + justify-content: center; + + .tip { + font-size: 14px; + color: #858585; + } + + .btns { + display: flex; + flex-direction: row; + justify-content: center; + margin-top: 20px; + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/message/ModelMessageList.vue b/src/views/ai/dashboard/components/message/ModelMessageList.vue new file mode 100644 index 0000000..f46b93e --- /dev/null +++ b/src/views/ai/dashboard/components/message/ModelMessageList.vue @@ -0,0 +1,193 @@ +<template> + <div ref="messageContainer" class="h-100%"> + <div class="chat-list" v-for="(item, index) in list" :key="index"> + <!-- 靠左 message:system、assistant 类型 --> + <div class="left-message message-item" v-if="item.type !== 'user'"> + <div class="message"> + <div class="left-text-container" ref="markdownViewRef"> + <MarkdownView class="left-text" :content="item.thinking" /> + </div> + </div> + </div> + </div> + </div> + <!-- 回到底部 --> + <div v-if="isScrolling" class="to-bottom" @click="handleGoBottom"> + <el-button :icon="ArrowDownBold" circle /> + </div> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' +import MarkdownView from '@/components/MarkdownView/index.vue' +import { useClipboard } from '@vueuse/core' +import { ArrowDownBold} from '@element-plus/icons-vue' +import { ChatMessageApi, ChatMessageVO } from '@/api/ai/chat/message' +import { ChatConversationVO } from '@/api/ai/chat/conversation' +import { useUserStore } from '@/store/modules/user' +import {formatDate} from "@/utils/formatTime"; + +const message = useMessage() // 消息弹窗 +const { copy } = useClipboard() // 初始化 copy 到粘贴板 +const userStore = useUserStore() + +// 判断“消息列表”滚动的位置(用于判断是否需要滚动到消息最下方) +const messageContainer: any = ref(null) +const isScrolling = ref(false) //用于判断用户是否在滚动 + +// 定义 props +const props = defineProps({ + conversation: { + type: Object as PropType<ChatConversationVO>, + required: true + }, + list: { + type: Array as PropType<ChatMessageVO[]>, + required: true + } +}) + +const { list } = toRefs(props) // 消息列表 + +const emits = defineEmits(['onDeleteSuccess', 'onRefresh', 'onEdit']) // 定义 emits + +// ============ 处理对话滚动 ============== + +/** 滚动到底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + // 注意要使用 nextTick 以免获取不到 dom + await nextTick() + if (isIgnore || !isScrolling.value) { + messageContainer.value.scrollTop = + messageContainer.value.scrollHeight - messageContainer.value.offsetHeight + } +} + +function handleScroll() { + const scrollContainer = messageContainer.value + const scrollTop = scrollContainer.scrollTop + const scrollHeight = scrollContainer.scrollHeight + const offsetHeight = scrollContainer.offsetHeight + if (scrollTop + offsetHeight < scrollHeight - 100) { + // 用户开始滚动并在最底部之上,取消保持在最底部的效果 + isScrolling.value = true + } else { + // 用户停止滚动并滚动到最底部,开启保持到最底部的效果 + isScrolling.value = false + } +} + +/** 回到底部 */ +const handleGoBottom = async () => { + const scrollContainer = messageContainer.value + scrollContainer.scrollTop = scrollContainer.scrollHeight +} + +/** 回到顶部 */ +const handlerGoTop = async () => { + const scrollContainer = messageContainer.value + scrollContainer.scrollTop = 0 +} + +defineExpose({ scrollToBottom, handlerGoTop }) // 提供方法给 parent 调用 + +// ============ 处理消息操作 ============== + +/** 复制 */ +const copyContent = async (content) => { + await copy(content) + message.success('复制成功!') +} + +/** 删除 */ +const onDelete = async (id) => { + // 删除 message + await ChatMessageApi.deleteChatMessage(id) + message.success('删除成功!') + // 回调 + emits('onDeleteSuccess') +} + +/** 刷新 */ +const onRefresh = async (message: ChatMessageVO) => { + emits('onRefresh', message) +} + +/** 编辑 */ +const onEdit = async (message: ChatMessageVO) => { + emits('onEdit', message) +} + +/** 初始化 */ +onMounted(async () => { + messageContainer.value.addEventListener('scroll', handleScroll) +}) +</script> + +<style scoped lang="scss"> +.message-container { + position: relative; + overflow-y: scroll; +} + +// 中间 +.chat-list { + display: flex; + flex-direction: column; + overflow-y: hidden; + padding: 0 20px; + .message-item { + margin-top: 30px; + } + + .message { + display: flex; + flex-direction: column; + text-align: left; + height: 480px; + + .left-text-container { + width: 855px; + height: 450px; + position: relative; + display: flex; + flex-direction: column; + overflow-wrap: break-word; + background: rgba(115,196,255,0.05); + border-radius: 4px 4px 4px 4px; + border-left: 1px solid #73C4FF; + padding: 10px 10px 5px 10px; + + .left-text { + font-weight: 400; + font-size: 14px; + color: rgba(219,238,255,0.6); + } + } + + } + + // 复制、删除按钮 + .btn-cus { + display: flex; + background-color: transparent; + align-items: center; + + .btn-image { + height: 20px; + } + } + + .btn-cus:hover { + cursor: pointer; + background-color: #f6f6f6; + } +} + +// 回到底部 +.to-bottom { + position: absolute; + z-index: 1000; + bottom: 0; + right: 50%; +} +</style> diff --git a/src/views/ai/dashboard/components/role/RoleCategoryList.vue b/src/views/ai/dashboard/components/role/RoleCategoryList.vue new file mode 100644 index 0000000..c02126d --- /dev/null +++ b/src/views/ai/dashboard/components/role/RoleCategoryList.vue @@ -0,0 +1,53 @@ +<template> + <div class="category-list"> + <div class="category" v-for="category in categoryList" :key="category"> + <el-button + plain + round + size="small" + :type="category === active ? 'primary' : ''" + @click="handleCategoryClick(category)" + > + {{ category }} + </el-button> + </div> + </div> +</template> +<script setup lang="ts"> +import { PropType } from 'vue' + +// 定义属性 +defineProps({ + categoryList: { + type: Array as PropType<string[]>, + required: true + }, + active: { + type: String, + required: false, + default: '全部' + } +}) + +// 定义回调 +const emits = defineEmits(['onCategoryClick']) + +/** 处理分类点击事件 */ +const handleCategoryClick = async (category: string) => { + emits('onCategoryClick', category) +} +</script> +<style scoped lang="scss"> +.category-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-items: center; + + .category { + display: flex; + flex-direction: row; + margin-right: 10px; + } +} +</style> diff --git a/src/views/ai/dashboard/components/role/RoleHeader.vue b/src/views/ai/dashboard/components/role/RoleHeader.vue new file mode 100644 index 0000000..17b1693 --- /dev/null +++ b/src/views/ai/dashboard/components/role/RoleHeader.vue @@ -0,0 +1,48 @@ +<!-- header --> +<template> + <el-header class="chat-header"> + <div class="title"> + {{ title }} + </div> + <div class="title-right"> + <slot></slot> + </div> + </el-header> +</template> + +<script setup lang="ts"> +// 设置组件属性 +defineProps({ + title: { + type: String, + required: true + } +}) +</script> + +<style scoped lang="scss"> +.chat-header { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 0 10px; + white-space: nowrap; + text-overflow: ellipsis; + background-color: #ececec; + width: 100%; + + .title { + font-size: 20px; + font-weight: bold; + overflow: hidden; + color: #3e3e3e; + max-width: 220px; + } + + .title-right { + display: flex; + flex-direction: row; + } +} +</style> diff --git a/src/views/ai/dashboard/components/role/RoleList.vue b/src/views/ai/dashboard/components/role/RoleList.vue new file mode 100644 index 0000000..e0388d0 --- /dev/null +++ b/src/views/ai/dashboard/components/role/RoleList.vue @@ -0,0 +1,174 @@ +<template> + <div class="card-list" ref="tabsRef" @scroll="handleTabsScroll"> + <div class="card-item" v-for="role in roleList" :key="role.id"> + <el-card class="card" body-class="card-body"> + <!-- 更多操作 --> + <div class="more-container" v-if="showMore"> + <el-dropdown @command="handleMoreClick"> + <span class="el-dropdown-link"> + <el-button type="text"> + <el-icon><More /></el-icon> + </el-button> + </span> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item :command="['edit', role]"> + <Icon icon="ep:edit" color="#787878" />编辑 + </el-dropdown-item> + <el-dropdown-item :command="['delete', role]" style="color: red"> + <Icon icon="ep:delete" color="red" />删除 + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + </div> + <!-- 角色信息 --> + <div> + <img class="avatar" :src="role.avatar" /> + </div> + <div class="right-container"> + <div class="content-container"> + <div class="title">{{ role.name }}</div> + <div class="description">{{ role.description }}</div> + </div> + <div class="btn-container"> + <el-button type="primary" size="small" @click="handleUseClick(role)">使用</el-button> + </div> + </div> + </el-card> + </div> + </div> +</template> + +<script setup lang="ts"> +import { ChatRoleVO } from '@/api/ai/model/chatRole' +import { PropType, ref } from 'vue' +import { More } from '@element-plus/icons-vue' + +const tabsRef = ref<any>() // tabs ref + +// 定义属性 +const props = defineProps({ + loading: { + type: Boolean, + required: true + }, + roleList: { + type: Array as PropType<ChatRoleVO[]>, + required: true + }, + showMore: { + type: Boolean, + required: false, + default: false + } +}) + +// 定义钩子 +const emits = defineEmits(['onDelete', 'onEdit', 'onUse', 'onPage']) + +/** 操作:编辑、删除 */ +const handleMoreClick = async (data) => { + const type = data[0] + const role = data[1] + if (type === 'delete') { + emits('onDelete', role) + } else { + emits('onEdit', role) + } +} + +/** 选中 */ +const handleUseClick = (role) => { + emits('onUse', role) +} + +/** 滚动 */ +const handleTabsScroll = async () => { + if (tabsRef.value) { + const { scrollTop, scrollHeight, clientHeight } = tabsRef.value + if (scrollTop + clientHeight >= scrollHeight - 20 && !props.loading) { + await emits('onPage') + } + } +} +</script> + +<style lang="scss"> +// 重写 card 组件 body 样式 +.card-body { + max-width: 240px; + width: 240px; + padding: 15px 15px 10px 15px; + + display: flex; + flex-direction: row; + justify-content: flex-start; + position: relative; +} +</style> +<style scoped lang="scss"> +// 卡片列表 +.card-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + position: relative; + height: 100%; + overflow: auto; + padding: 0px 25px; + padding-bottom: 140px; + align-items: start; + align-content: flex-start; + justify-content: start; + + .card { + display: inline-block; + margin-right: 20px; + border-radius: 10px; + margin-bottom: 20px; + position: relative; + + .more-container { + position: absolute; + top: 0; + right: 12px; + } + + .avatar { + width: 40px; + height: 40px; + border-radius: 10px; + overflow: hidden; + } + + .right-container { + margin-left: 10px; + width: 100%; + //height: 100px; + + .content-container { + height: 85px; + + .title { + font-size: 18px; + font-weight: bold; + color: #3e3e3e; + } + + .description { + margin-top: 10px; + font-size: 14px; + color: #6a6a6a; + } + } + + .btn-container { + display: flex; + flex-direction: row-reverse; + margin-top: 2px; + } + } + } +} +</style> diff --git a/src/views/ai/dashboard/components/role/RoleRepository.vue b/src/views/ai/dashboard/components/role/RoleRepository.vue new file mode 100644 index 0000000..246dcb4 --- /dev/null +++ b/src/views/ai/dashboard/components/role/RoleRepository.vue @@ -0,0 +1,289 @@ +<!-- chat 角色仓库 --> +<template> + <el-container class="role-container"> + <ChatRoleForm ref="formRef" @success="handlerAddRoleSuccess" /> + <!-- header --> + <RoleHeader title="角色仓库" class="relative" /> + <!-- main --> + <el-main class="role-main"> + <div class="search-container"> + <!-- 搜索按钮 --> + <el-input + :loading="loading" + v-model="search" + class="search-input" + size="default" + placeholder="请输入搜索的内容" + :suffix-icon="Search" + @change="getActiveTabsRole" + /> + <el-button + v-if="activeTab == 'my-role'" + type="primary" + @click="handlerAddRole" + class="ml-20px" + > + <Icon icon="ep:user" style="margin-right: 5px;" /> + 添加角色 + </el-button> + </div> + <!-- tabs --> + <el-tabs v-model="activeTab" class="tabs" @tab-click="handleTabsClick"> + <el-tab-pane class="role-pane" label="我的角色" name="my-role"> + <RoleList + :loading="loading" + :role-list="myRoleList" + :show-more="true" + @on-delete="handlerCardDelete" + @on-edit="handlerCardEdit" + @on-use="handlerCardUse" + @on-page="handlerCardPage('my')" + class="mt-20px" + /> + </el-tab-pane> + <el-tab-pane label="公共角色" name="public-role"> + <RoleCategoryList + class="role-category-list" + :category-list="categoryList" + :active="activeCategory" + @on-category-click="handlerCategoryClick" + /> + <RoleList + :role-list="publicRoleList" + @on-delete="handlerCardDelete" + @on-edit="handlerCardEdit" + @on-use="handlerCardUse" + @on-page="handlerCardPage('public')" + class="mt-20px" + loading + /> + </el-tab-pane> + </el-tabs> + </el-main> + </el-container> +</template> + +<script setup lang="ts"> +import {ref} from 'vue' +import RoleHeader from './RoleHeader.vue' +import RoleList from './RoleList.vue' +import ChatRoleForm from '@/views/ai/model/chatRole/ChatRoleForm.vue' +import RoleCategoryList from './RoleCategoryList.vue' +import {ChatRoleApi, ChatRolePageReqVO, ChatRoleVO} from '@/api/ai/model/chatRole' +import {ChatConversationApi, ChatConversationVO} from '@/api/ai/chat/conversation' +import {Search} from '@element-plus/icons-vue' +import {TabsPaneContext} from 'element-plus' + +const router = useRouter() // 路由对象 + +// 属性定义 +const loading = ref<boolean>(false) // 加载中 +const activeTab = ref<string>('my-role') // 选中的角色 Tab +const search = ref<string>('') // 加载中 +const myRoleParams = reactive({ + pageNo: 1, + pageSize: 50 +}) +const myRoleList = ref<ChatRoleVO[]>([]) // my 分页大小 +const publicRoleParams = reactive({ + pageNo: 1, + pageSize: 50 +}) +const publicRoleList = ref<ChatRoleVO[]>([]) // public 分页大小 +const activeCategory = ref<string>('全部') // 选择中的分类 +const categoryList = ref<string[]>([]) // 角色分类类别 + +/** tabs 点击 */ +const handleTabsClick = async (tab: TabsPaneContext) => { + // 设置切换状态 + activeTab.value = tab.paneName + '' + // 切换的时候重新加载数据 + await getActiveTabsRole() +} + +/** 获取 my role 我的角色 */ +const getMyRole = async (append?: boolean) => { + const params: ChatRolePageReqVO = { + ...myRoleParams, + name: search.value, + publicStatus: false + } + const { list } = await ChatRoleApi.getMyPage(params) + if (append) { + myRoleList.value.push.apply(myRoleList.value, list) + } else { + myRoleList.value = list + } +} + +/** 获取 public role 公共角色 */ +const getPublicRole = async (append?: boolean) => { + const params: ChatRolePageReqVO = { + ...publicRoleParams, + category: activeCategory.value === '全部' ? '' : activeCategory.value, + name: search.value, + publicStatus: true + } + const { total, list } = await ChatRoleApi.getMyPage(params) + if (append) { + publicRoleList.value.push.apply(publicRoleList.value, list) + } else { + publicRoleList.value = list + } +} + +/** 获取选中的 tabs 角色 */ +const getActiveTabsRole = async () => { + if (activeTab.value === 'my-role') { + myRoleParams.pageNo = 1 + await getMyRole() + } else { + publicRoleParams.pageNo = 1 + await getPublicRole() + } +} + +/** 获取角色分类列表 */ +const getRoleCategoryList = async () => { + categoryList.value = ['全部', ...(await ChatRoleApi.getCategoryList())] +} + +/** 处理分类点击 */ +const handlerCategoryClick = async (category: string) => { + // 切换选择的分类 + activeCategory.value = category + // 筛选 + await getActiveTabsRole() +} + +/** 添加/修改操作 */ +const formRef = ref() +const handlerAddRole = async () => { + formRef.value.open('my-create', null, '添加角色') +} +/** 编辑角色 */ +const handlerCardEdit = async (role) => { + formRef.value.open('my-update', role.id, '编辑角色') +} + +/** 添加角色成功 */ +const handlerAddRoleSuccess = async (e) => { + // 刷新数据 + await getActiveTabsRole() +} + +/** 删除角色 */ +const handlerCardDelete = async (role) => { + await ChatRoleApi.deleteMy(role.id) + // 刷新数据 + await getActiveTabsRole() +} + +/** 角色分页:获取下一页 */ +const handlerCardPage = async (type) => { + try { + loading.value = true + if (type === 'public') { + publicRoleParams.pageNo++ + await getPublicRole(true) + } else { + myRoleParams.pageNo++ + await getMyRole(true) + } + } finally { + loading.value = false + } +} + +/** 选择 card 角色:新建聊天对话 */ +const handlerCardUse = async (role) => { + // 1. 创建对话 + const data: ChatConversationVO = { + roleId: role.id + } as unknown as ChatConversationVO + const conversationId = await ChatConversationApi.createChatConversationMy(data) + + // 2. 跳转页面 + await router.push({ + name: 'AiChat', + query: { + conversationId: conversationId + } + }) +} + +/** 初始化 **/ +onMounted(async () => { + // 获取分类 + await getRoleCategoryList() + // 获取 role 数据 + await getActiveTabsRole() +}) +</script> +<!-- 覆盖 element ui css --> +<style lang="scss"> +.el-tabs__content { + position: relative; + height: 100%; + overflow: hidden; +} +.el-tabs__nav-scroll { + margin: 10px 20px; +} +</style> +<!-- 样式 --> +<style scoped lang="scss"> +// 跟容器 +.role-container { + position: absolute; + width: 100%; + height: 100%; + margin: 0; + padding: 0; + left: 0; + right: 0; + top: 0; + bottom: 0; + background-color: #ffffff; + overflow: hidden; + display: flex; + flex-direction: column; + + .role-main { + flex: 1; + overflow: hidden; + margin: 0; + padding: 0; + position: relative; + + .search-container { + margin: 20px 20px 0px 20px; + position: absolute; + right: 0; + top: -5px; + z-index: 100; + } + + .search-input { + width: 240px; + } + + .tabs { + position: relative; + height: 100%; + + .role-category-list { + margin: 0 27px; + } + } + + .role-pane { + display: flex; + flex-direction: column; + height: 100%; + overflow-y: auto; + position: relative; + } + } +} +</style> diff --git a/src/views/ai/dashboard/zhuanlu/index.vue b/src/views/ai/dashboard/zhuanlu/index.vue new file mode 100644 index 0000000..0d0be46 --- /dev/null +++ b/src/views/ai/dashboard/zhuanlu/index.vue @@ -0,0 +1,2347 @@ +<template> + <div class="gas-scheduling-container"> + <el-button size="small" class="fullscreen-btn" @click="toggleFullscreen"> + <Icon + class="is-hover mr-12px cursor-pointer" + :icon="isFullscreen ? 'radix-icons:exit-full-screen' : 'radix-icons:enter-full-screen'" + color="#8FD6FE" + hover-color="var(--el-color-primary)" + /> + {{ isFullscreen ? '退出全屏' : '全屏' }} + </el-button> + <div class="gas-scheduling-left"> + <div id="mqhsssxx"> + <div class="title"></div> + <div class="data1-item" v-for="(item, index) in mqhsList" :key="`dynamics-${index}`"> + <div class="content"> + <div class="value"> + <span>{{item.value}}</span> <span>{{item.unit}}</span> + </div> + <div class="name"> + {{item.name}} + </div> + </div> + </div> + </div> + <div id="tsxx"> + <div class="title"></div> + <div class="data1-item" v-for="(item, index) in tsxxList" :key="`dynamics-${index}`"> + <div class="content"> + <div class="value"> + <span>{{item.value}}</span> <span>{{item.unit}}</span> + </div> + <div class="name"> + {{item.name}} + </div> + </div> + </div> + </div> + <div id="zlxx"> + <div class="title"></div> + <el-table :data="zlxxList" class="transparent-table"> + <el-table-column prop="name" label="控制器名称" header-class-name="custom-header" width="150"/> + <el-table-column prop="zl1" label="1#转炉" header-class-name="custom-header" /> + <el-table-column prop="zl2" label="2#转炉" header-class-name="custom-header" /> + <el-table-column prop="zl3" label="3#转炉" header-class-name="custom-header" /> + </el-table> + </div> + <div id="mqxhssxx"> + <div class="title"></div> + <div class="data2-item" v-for="(item, index) in mqxhssxxList" :key="`dynamics-${index}`"> + <div class="content2"> + <div class="name"> + {{item.name}} + </div> + <div class="value"> + <span>{{item.value}}</span> <span>{{item.unit}}</span> + </div> + </div> + </div> + </div> + </div> + + <div class="gas-scheduling-center"> + <div class="mode-switch"> + <el-radio-group v-model="tabPosition" class="custom-radio-group"> + <el-radio-button label="model">大模型模式</el-radio-button> + <el-radio-button label="conversation">对话模式</el-radio-button> + </el-radio-group> + </div> + + <div v-if="tabPosition === 'model'"> + <!-- 对话列表 --> + <ConversationList + v-show="false" + :active-id="activeConversationId" + ref="conversationListRef" + @on-conversation-click="handleConversationClick" + @on-conversation-clear="handleConversationClear" + @on-conversation-delete="handlerConversationDelete" + /> + <div class="detail-container"> + <!-- 输入框 --> + <div class="input-container"> + <form class="prompt-from"> + <textarea + class="prompt-input" + v-model="prompt" + @keydown="handleSendByKeydown" + @input="handlePromptInput" + @compositionstart="onCompositionstart" + @compositionend="onCompositionend" + placeholder="问我任何问题...(Shift+Enter 换行,按下 Enter 发送)" + ></textarea> + <div class="prompt-btns"> + <div class="content"> + <el-button + :class="{ 'active-button': enableContext }" + @click="enableContext = !enableContext" + > + <el-icon class="content-icon" /> + 上下文 + </el-button> + </div> + <div class="message"> + <el-button + type="primary" + size="default" + @click="handleSendByButton" + :loading="conversationInProgress" + v-if="conversationInProgress == false" + > + {{ conversationInProgress ? '进行中' : '发消息' }} + </el-button> + <el-button + type="danger" + size="default" + @click="stopStream()" + v-if="conversationInProgress == true" + > + 停止 + </el-button> + </div> + </div> + </form> + </div> + + <!-- main:消息列表 --> + <el-main class="main-container"> + <div class="title"> + <span>工业能源大模型思考</span> + </div> + <div> + <div class="message-container"> + <!-- 情况一:消息加载中 --> + <MessageLoading v-if="activeMessageListLoading" /> + <!-- 情况二:消息列表为空 --> + <MessageListEmpty + v-if="!activeMessageListLoading && messageList.length === 0 && activeConversation" + @on-prompt="doSendMessage" + /> + <!-- 情况三:消息列表不为空 --> + <ModelMessageList + v-if="!activeMessageListLoading && messageList.length > 0" + ref="messageRef" + :conversation="activeConversation" + :list="messageList" + @on-delete-success="handleMessageDelete" + @on-edit="handleMessageEdit" + @on-refresh="handleMessageRefresh" + /> + </div> + </div> + </el-main> + <!-- main:调度推理结论 --> + <div class="result-container-title"> + <span>调度推理结论</span><el-button @click="openHistoryMessage" size="small" class="history-button" :icon="ArrowUpBold">历史建议</el-button> + </div> + <el-main class="result-container"> + <div class="result"> + <textarea class="result-content" v-model="ddtlResult"></textarea> + </div> + </el-main> + </div> + <!-- 历史建议 --> + <HistoryMessageDialog + ref="historyMessageRef" + :conversation="activeConversation" + /> + </div> + + <div v-else> + <NormalConversation /> + </div> + </div> + + <div class="gas-scheduling-right"> + <div id="ldghslyc"> + <div class="title"></div> + <div ref="LDGHSLYCEhartContainer" style="width: 100%; height: 180px"></div> + </div> + <div id="ldggrqsyc"> + <div class="title"></div> + <div ref="LDGGRYCEhartContainer" style="width: 100%; height: 180px"></div> + </div> + <div id="mqhsjhxx"> + <div class="title"></div> + <div class="time-content" v-for="(item, index) in mqhsjhTimeList" :key="`dynamics-${index}`"> + <div class="time-content-item"> + <div class="name"> + <span>{{item.name}}</span> + </div> + <div class="time"> + <div class="in-pot"></div><div class="in">装入{{item.inTime}}</div> + <div class="out-pot"></div><div class="out">结束{{item.outTime}}</div> + </div> + </div> + </div> + <div class="data2-item" v-for="(item, index) in mqhsjhxxList" :key="`dynamics-${index}`"> + <div class="content"> + <div class="name"> + {{item.name}} + </div> + <div class="value"> + <span>{{item.value}}</span> <span>{{item.unit}}</span> + </div> + </div> + </div> + </div> + <div id="scmbyyxzb"> + <div class="title"></div> + <div class="little-title">生产目标/班</div> + <div class="data3-item" v-for="(item, index) in scmbList" :key="`dynamics-${index}`"> + <div class="content2"> + <div class="name"> + {{item.name}} + </div> + <el-progress + :percentage="percentage(item)" + :stroke-width="12" + :text-inside="true" + :color="customColor" + :show-text="false" + /> + <div class="value"> + <span>{{item.current}}/{{item.total}}</span> + </div> + <div class="value-content"> + <span>已吹炼/总炉数</span> + </div> + </div> + </div> + <div class="little-title">运行指标/天</div> + <div class="zb-content"> + <div class="item left-label"></div> + <div class="item data4-item" v-for="(item, index) in yxzbList" :key="`dynamics-${index}`"> + <div class="content"> + <div class="value"> + <span>{{item.value}}</span> <span>{{item.unit}}</span> + </div> + <div class="name"> + {{item.name}} + </div> + </div> + </div> + <div class="item right-label"></div> + </div> + </div> + </div> + </div> +</template> + +<script setup lang="ts"> +import { ref, onMounted, reactive } from 'vue' +import {ChatConversationApi, ChatConversationVO} from "@/api/ai/chat/conversation"; +import {ChatMessageApi, ChatMessageVO} from "@/api/ai/chat/message"; +import ModelMessageList from '../components/message/ModelMessageList.vue' +import NormalConversation from '../components/conversation/CommonConversation.vue' +import MessageListEmpty from '../components/message/MessageListEmpty.vue' +import MessageLoading from '../components/message/MessageLoading.vue' +import ConversationList from "../components/conversation/ConversationList.vue"; +import HistoryMessageDialog from "../components/message/HistoryMessageDialog.vue" +import * as echarts from "echarts"; +import {formatToDateTime} from "@/utils/dateUtil"; +import {refreshToken} from "@/api/login"; +import {round} from "lodash-es"; +import {ArrowUpBold} from "@element-plus/icons-vue"; +import * as authUtil from "@/utils/auth"; + +const mqhsList = ref([ + { + name: '单转炉煤气回收流量', + value: 130, + unit: 'km³/h' + }, + { + name: '转炉煤气 O 含量', + value: 618, + unit: '%' + }, + { + name: '转炉煤气 CO 含量', + value: 15, + unit: '%' + }, + { + name: '转炉铁水碳含量', + value: 20, + unit: '%' + }, + { + name: '三通阀信号', + value: 0, + unit: '' + }, + { + name: '单转炉吹氧流量', + value: 400, + unit: 'kNm³/h' + } +]) + +const tsxxList = ref([ + { + name: '各高炉出铁水信号', + value: '进行', + unit: '' + }, + { + name: '各高炉出铁量', + value: 5000, + unit: '吨' + }, + { + name: '各高炉铁水装入鱼雷罐车信号', + value: '进行', + unit: 'm³/h' + }, + { + name: '鱼雷罐车等待信号', + value: '进行', + unit: 'm³/h' + }, + { + name: '铁水倒入铁水包信号', + value: '不进行', + unit: 'm³/h' + }, + { + name: '铁产量计划', + value: 6000, + unit: '吨' + }, +]) + +const zlxxList = ref([ + { + name: '吹炼状态', + zl1: '正在吹炼', + zl2: '正在吹炼', + zl3: '正在吹炼' + }, + { + name: '当前状态持续时间', + zl1: '10min', + zl2: '10min', + zl3: '10min' + }, + { + name: '当前炉吹炼开始时刻', + zl1: '18:40', + zl2: '18:40', + zl3: '18:40' + }, + { + name: '当前炉吹炼结束时刻', + zl1: '18:40', + zl2: '18:40', + zl3: '18:40' + }, + { + name: '前一炉吹炼开始时刻', + zl1: '18:40', + zl2: '18:40', + zl3: '18:40' + }, + { + name: '前一炉吹炼结束时刻', + zl1: '18:40', + zl2: '18:40', + zl3: '18:40' + } +]) + +const mqxhssxxList = ref([ + { + name: '去棒三混合站', + value: 57.1, + unit: 'km³/h' + }, + { + name: '东区掺混 LDG', + value: 49.4, + unit: 'km³/h' + }, + { + name: '去焦化方向', + value: 67.4, + unit: 'm³/h' + }, + { + name: '西区掺混 LDG', + value: 70, + unit: 'm³/h' + }, + { + name: '送 BFG 管网', + value: 50.1, + unit: 'm³/h' + }, + { + name: '去热卷二', + value: 72.2, + unit: 'km³/h' + }, + { + name: '热卷一', + value: 13.9, + unit: 'km³/h' + }, + { + name: '转底炉 1', + value: 7.0, + unit: 'km³/h' + }, + { + name: '超薄带', + value: 67.4, + unit: 'km³/h' + }, + { + name: '转底炉 2', + value: 13.5, + unit: 'km³/h' + }, + { + name: '135MW 1', + value: 45.3, + unit: 'km³/h' + }, + { + name: '135MW 2', + value: 36.2, + unit: 'km³/h' + }, +]) + +const mqhsjhxxList = ref([ + { + name: '转炉总炉数\n' + + '日计划', + value: 567, + unit: '炉' + }, + { + name: '转炉入炉铁水量\n' + + '日计划', + value: 200, + unit: '吨' + }, + { + name: '转炉检修计划', + value: '未进行', + unit: '' + }, + { + name: '钢产量日计划', + value: 300, + unit: '吨' + }, + { + name: '转炉加入废钢总量', + value: 500, + unit: '吨' + }, + { + name: '转炉实绩钢产量', + value: 100, + unit: '吨' + } +]) + +const mqhsjhTimeList = ref([ + { + name: '兑铁', + inTime: '04-23 03:19', + outTime: '04-28 14:54', + }, + { + name: '吹炼', + inTime: '04-23 03:19', + outTime: '04-28 14:54', + }, + { + name: '出钢', + inTime: '04-23 03:19', + outTime: '04-28 14:54', + } +]) + +const scmbList = ref([ + { + id: 1, + name: '1#转炉', + current: 20, + total: 30 + }, + { + id: 2, + name: '2#转炉', + current: 25, + total: 100 + }, + { + id: 3, + name: '3#转炉', + current: 4, + total: 29 + } +]) + +// 自定义进度条颜色(可选) +const customColor = '#409EFF'; + +const percentage = (item) => { + return Math.round((item.current / item.total) * 100); +}; + +const yxzbList = ref([ + { + name: '昨日LDG拒收时间', + value: 0.00, + unit: 'min' + }, + { + name: '昨日吨钢回收量', + value: 86.08, + unit: 'm³/t' + },{ + name: '昨日LDG混入累积量', + value: 2687.25, + unit: 'km³' + } + +]) + +const ddtlResult = ref('') + +const route = useRoute() // 路由 +const message = useMessage() // 消息弹窗 + +// 聊天对话 +const conversationListRef = ref() +const activeConversationId = ref<number | null>(null) // 选中的对话编号 +const activeConversation = ref<ChatConversationVO | null>(null) // 选中的 Conversation +const conversationInProgress = ref(false) // 对话是否正在进行中。目前只有【发送】消息时,会更新为 true,避免切换对话、删除对话等操作 + +// 消息列表 +const messageRef = ref() +const activeMessageList = ref<ChatMessageVO[]>([]) // 选中对话的消息列表 +const activeHistoryMessageList = ref<ChatMessageVO[]>([]) // 历史建议列表 +const activeMessageListLoading = ref<boolean>(false) // activeMessageList 是否正在加载中 +const activeMessageListLoadingTimer = ref<any>() // activeMessageListLoading Timer 定时器。如果加载速度很快,就不进入加载中 +// 消息滚动 +const textSpeed = ref<number>(50) // Typing speed in milliseconds +const textRoleRunning = ref<boolean>(false) // Typing speed in milliseconds + +// 发送消息输入框 +const isComposing = ref(false) // 判断用户是否在输入 +const conversationInAbortController = ref<any>() // 对话进行中 abort 控制器(控制 stream 对话) +const inputTimeout = ref<any>() // 处理输入中回车的定时器 +const prompt = ref<string>() // prompt +const enableContext = ref<boolean>(false) // 是否开启上下文 +// 接收 Stream 消息 +const receiveMessageFullText = ref('') +const receiveMessageDisplayedText = ref('') + +const tabPosition = ref('model') + +// 模型数据 +const modelData = ref() + +/** 历史建议 */ +const historyMessageRef = ref() +const openHistoryMessage = async () => { + // 刷新 message 列表 + await getHistoryMessageList() + historyMessageRef.value.open(activeHistoryMessageList.value, activeConversation.value) +} + +// =========== 【聊天对话】相关 =========== + +/** 获取对话信息 */ +const getConversation = async (id: number | null) => { + if (!id) { + return + } + const conversation: ChatConversationVO = await ChatConversationApi.getChatConversationMy(id) + if (!conversation) { + return + } + activeConversation.value = conversation + activeConversationId.value = conversation.id +} + +/** + * 点击某个对话 + * + * @param conversation 选中的对话 + * @return 是否切换成功 + */ +const handleConversationClick = async (conversation: ChatConversationVO) => { + // 对话进行中,不允许切换 + if (conversationInProgress.value) { + message.alert('对话中,不允许切换!') + return false + } + + // 更新选中的对话 id + activeConversationId.value = conversation.id + activeConversation.value = conversation + // 刷新 message 列表 + await getMessageList() + // 滚动底部 + scrollToBottom(true) + // 清空输入框 + // prompt.value = '' + return true +} + +/** 删除某个对话*/ +const handlerConversationDelete = async (delConversation: ChatConversationVO) => { + // 删除的对话如果是当前选中的,那么就重置 + if (activeConversationId.value === delConversation.id) { + await handleConversationClear() + } +} +/** 清空选中的对话 */ +const handleConversationClear = async () => { + // 对话进行中,不允许切换 + if (conversationInProgress.value) { + message.alert('对话中,不允许切换!') + return false + } + activeConversationId.value = null + activeConversation.value = null + activeMessageList.value = [] +} + +// =========== 【消息列表】相关 =========== + +/** 获取消息 message 列表 */ +const getMessageList = async () => { + try { + if (activeConversationId.value === null) { + return + } + // Timer 定时器,如果加载速度很快,就不进入加载中 + activeMessageListLoadingTimer.value = setTimeout(() => { + activeMessageListLoading.value = true + }, 60) + + // 获取消息列表 + activeMessageList.value = await ChatMessageApi.getEnergyChatMessageListByConversationId( + activeConversationId.value + ) + if(activeMessageList.value.length != 0) { + prompt.value = activeMessageList.value[0].content + } + + // 滚动到最下面 + await nextTick() + await scrollToBottom() + } finally { + // time 定时器,如果加载速度很快,就不进入加载中 + if (activeMessageListLoadingTimer.value) { + clearTimeout(activeMessageListLoadingTimer.value) + } + // 加载结束 + activeMessageListLoading.value = false + } +} + +/** 获取消息 message 列表 */ +const getHistoryMessageList = async () => { + if (activeConversationId.value === null) { + return + } + // 获取消息列表 + activeHistoryMessageList.value = await ChatMessageApi.getChatMessageListByConversationId( + activeConversationId.value + ) + if (activeHistoryMessageList.value.length > 0) { + activeHistoryMessageList.value.forEach((message: ChatMessageVO) => { + if(message.type != 'user') { + dealResult(message) + } + }) + return activeHistoryMessageList.value + } +} +//处理调度推理结论 +const dealResult = (message: any) => { + const spliceText = message.content.includes("总结:") ? "总结:" : "结论:"; + const regex = new RegExp('(\\n*)([\\s\\S]*?)(\\n*)' + spliceText + '([\\s\\S]*)'); + const match = message.content.match(regex); + if(match) { + message.thinking = match[2]; + message.conclusion = match[4] + } + return message +} + + +/** + * 消息列表 + * + * 和 {@link #getMessageList()} 的差异是,把 systemMessage 考虑进去 + */ +const messageList = computed(() => { + if (activeMessageList.value.length > 0) { + activeMessageList.value[1].thinking = dealResultAndData(activeMessageList.value[1].content) + console.log(activeMessageList.value) + return activeMessageList.value + } + // 没有消息时,如果有 systemMessage 则展示它 + if (activeConversation.value?.systemMessage) { + return [ + { + id: 0, + type: 'system', + content: activeConversation.value.systemMessage + } + ] + } + return [] +}) + +//处理调度推理结论及数据 +const dealResultAndData = (content: string) => { + const spliceText = content.includes("总结:") ? "总结:" : "结论:"; +// 创建同时捕获前后内容的正则表达式 + const regex = new RegExp(`^([\\s\\S]*?)${spliceText}([\\s\\S]*)$`); + const match = content.match(regex); + +// 获取前面段落(优先返回匹配结果,若无匹配返回全文) + content = match ? match[1].trim() : content; +// 已存在的后面段落获取方式 + const result = match ? match[2].trim() : ''; + ddtlResult.value = result + const dataRegex = /转炉煤气回收情况:\s*((?:.*?)(?=\n\d\.|\n|$))/s; + const dataMatch = content.match(dataRegex); + const dataContent = dataMatch ? dataMatch[1] : ''; + modelData.value = extractRecoveryDetails(dataContent, 78, 90); + if(modelData.value.schedule.length === 3) { + initLDGHSLYCChart() + } + initLDGGRQSYCChart() + return content +} + +const extractRecoveryDetails = (text, consume, gui, totalMinutes = 60) => { + // 正则表达式匹配转炉数据块 + const furnaceBlocks = Array.from(text.matchAll(/(\d+#转炉.*?)(?=\d+#转炉|$)/gs)) + .map(match => match[0]); + + // 初始化数据结构 + const state = reactive({ + allSchedule: [], + result: {}, + totalRecovery: Array.from({length: totalMinutes}, () => [0]), + tankLevels: Array.from({length: totalMinutes + 1}, () => [0]) + }); + + // 解析每个转炉的数据 + furnaceBlocks.forEach(block => { + // 提取转炉编号 + const furnaceNum = parseInt(block.match(/(\d+)#/)[1]); + + // 解析时间段和回收量 + const periods = Array.from(block.matchAll(/第(\d+)-(\d+)[^\d]+?(\d+\.?\d*)km³/gs)) + .map(match => [ + parseInt(match[1]), + parseInt(match[2]), + parseFloat(match[3]) + ]); + + // 存储到结果 + state.result[furnaceNum] = periods; + + // 计算每分钟回收量 + periods.forEach(([start, end, amount]) => { + const duration = end - start; + const perMin = amount / duration; + for(let t = start; t < end; t++) { + state.totalRecovery[t][0] += perMin; + } + }); + }); + + // 生成0/1序列 + furnaceBlocks.forEach(block => { + const schedule = new Array(totalMinutes).fill(0); + Array.from(block.matchAll(/第(\d+)-(\d+)/gs)).forEach(match => { + const start = parseInt(match[1]) - 1; + const end = parseInt(match[2]) - 1; + for(let i = start; i <= end; i++) { + if(i >= 0 && i < totalMinutes) schedule[i] = 1; + } + }); + state.allSchedule.push(schedule); + }); + + // 计算柜位 + let cumulative = 0; + const consumptionRate = consume / 60; + state.tankLevels = Array.from({length: totalMinutes + 1}, (_, t) => { + if(t > 0) cumulative += state.totalRecovery[t-1][0]; + const consumed = consumptionRate * t; + return [Number((gui + cumulative - consumed).toFixed(2))]; + }); + + // 格式化输出 + return { + schedule: state.allSchedule, + result: state.result, + totalRecovery: state.totalRecovery.map(v => [Number(v[0].toFixed(2))]), + tankLevels: state.tankLevels + } +} + +/** 处理删除 message 消息 */ +const handleMessageDelete = () => { + if (conversationInProgress.value) { + message.alert('回答中,不能删除!') + return + } + // 刷新 message 列表 + getMessageList() +} + +/** 处理 message 清空 */ +const handlerMessageClear = async () => { + if (!activeConversationId.value) { + return + } + try { + // 刷新 message 列表 + activeMessageList.value = [] + } catch {} +} + +// =========== 【发送消息】相关 =========== + +/** 处理来自 keydown 的发送消息 */ +const handleSendByKeydown = async (event) => { + // 判断用户是否在输入 + if (isComposing.value) { + return + } + // 进行中不允许发送 + if (conversationInProgress.value) { + return + } + const content = prompt.value?.trim() as string + if (event.key === 'Enter') { + if (event.shiftKey) { + // 插入换行 + prompt.value += '\r\n' + event.preventDefault() // 防止默认的换行行为 + } else { + // 发送消息 + await doSendMessage(content) + event.preventDefault() // 防止默认的提交行为 + } + } +} + +/** 处理来自【发送】按钮的发送消息 */ +const handleSendByButton = () => { + doSendMessage(prompt.value?.trim() as string) +} + +/** 处理 prompt 输入变化 */ +const handlePromptInput = (event) => { + // 非输入法 输入设置为 true + if (!isComposing.value) { + // 回车 event data 是 null + if (event.data == null) { + return + } + isComposing.value = true + } + // 清理定时器 + if (inputTimeout.value) { + clearTimeout(inputTimeout.value) + } + // 重置定时器 + inputTimeout.value = setTimeout(() => { + isComposing.value = false + }, 400) +} +// TODO :是不是可以通过 @keydown.enter、@keydown.shift.enter 来实现,回车发送、shift+回车换行;主要看看,是不是可以简化 isComposing 相关的逻辑 +const onCompositionstart = () => { + isComposing.value = true +} +const onCompositionend = () => { + // console.log('输入结束...') + setTimeout(() => { + isComposing.value = false + }, 200) +} + +/** 真正执行【发送】消息操作 */ +const doSendMessage = async (content: string) => { + // 校验 + if (content.length < 1) { + message.error('发送失败,原因:内容为空!') + return + } + if (activeConversationId.value == null) { + message.error('还没创建对话,不能发送!') + return + } + // 发送请求时如果accessToken过期,无法中断请求,暂时增加请求前刷新token + authUtil.setToken(await refreshToken()) + // 执行发送 + await doSendMessageStream({ + conversationId: activeConversationId.value, + content: content + } as ChatMessageVO) +} + +/** 真正执行【发送】消息操作 */ +const doSendMessageStream = async (userMessage: ChatMessageVO) => { + // 创建 AbortController 实例,以便中止请求 + conversationInAbortController.value = new AbortController() + // 标记对话进行中 + conversationInProgress.value = true + // 设置为空 + receiveMessageFullText.value = '' + + try { + // 1.0 每次发送消息前先将消息记录chat message清空 + await handlerMessageClear() + // 1.1 先添加两个假数据,等 stream 返回再替换 + activeMessageList.value.push({ + id: -1, + conversationId: activeConversationId.value, + type: 'user', + content: userMessage.content, + createTime: new Date() + } as ChatMessageVO) + activeMessageList.value.push({ + id: -2, + conversationId: activeConversationId.value, + type: 'assistant', + content: '思考中...', + createTime: new Date() + } as ChatMessageVO) + // 1.2 滚动到最下面 + await nextTick() + await scrollToBottom() // 底部 + // 1.3 开始滚动 + textRoll() + + // 2. 发送 event stream + let isFirstChunk = true // 是否是第一个 chunk 消息段 + await ChatMessageApi.sendChatMessageStream( + userMessage.conversationId, + userMessage.content, + conversationInAbortController.value, + enableContext.value, + async (res) => { + const { code, data, msg } = JSON.parse(res.data) + if (code !== 0) { + message.alert(`对话异常! ${msg}`) + } + + // 如果内容为空,就不处理。 + if (data.receive.content === '') { + return + } + // 首次返回需要添加一个 message 到页面,后面的都是更新 + if (isFirstChunk) { + isFirstChunk = false + // 弹出两个假数据 + activeMessageList.value.pop() + activeMessageList.value.pop() + // 更新返回的数据 + activeMessageList.value.push(data.send) + activeMessageList.value.push(data.receive) + } + // debugger + receiveMessageFullText.value = receiveMessageFullText.value + data.receive.content + // 滚动到最下面 + await scrollToBottom() + }, + (error) => { + message.alert(`对话异常! ${error}`) + stopStream() + }, + () => { + stopStream() + } + ) + } catch {} +} + +/** 停止 stream 流式调用 */ +const stopStream = async () => { + // tip:如果 stream 进行中的 message,就需要调用 controller 结束 + if (conversationInAbortController.value) { + conversationInAbortController.value.abort() + } + // 设置为 false + conversationInProgress.value = false +} + +/** 编辑 message:设置为 prompt,可以再次编辑 */ +const handleMessageEdit = (message: ChatMessageVO) => { + prompt.value = message.content +} + +/** 刷新 message:基于指定消息,再次发起对话 */ +const handleMessageRefresh = (message: ChatMessageVO) => { + doSendMessage(message.content) +} + +// ============== 【消息滚动】相关 ============= + +/** 滚动到 message 底部 */ +const scrollToBottom = async (isIgnore?: boolean) => { + await nextTick() + if (messageRef.value) { + messageRef.value.scrollToBottom(isIgnore) + } +} + +/** 自提滚动效果 */ +const textRoll = async () => { + let index = 0 + try { + // 只能执行一次 + if (textRoleRunning.value) { + return + } + // 设置状态 + textRoleRunning.value = true + receiveMessageDisplayedText.value = '' + const task = async () => { + // 调整速度 + const diff = + (receiveMessageFullText.value.length - receiveMessageDisplayedText.value.length) / 10 + if (diff > 5) { + textSpeed.value = 10 + } else if (diff > 2) { + textSpeed.value = 30 + } else if (diff > 1.5) { + textSpeed.value = 50 + } else { + textSpeed.value = 100 + } + // 对话结束,就按 30 的速度 + if (!conversationInProgress.value) { + textSpeed.value = 10 + } + + if (index < receiveMessageFullText.value.length) { + receiveMessageDisplayedText.value += receiveMessageFullText.value[index] + index++ + + // 更新 message + const lastMessage = activeMessageList.value[activeMessageList.value.length - 1] + lastMessage.content = receiveMessageDisplayedText.value + // 滚动到住下面 + await scrollToBottom() + // 重新设置任务 + timer = setTimeout(task, textSpeed.value) + } else { + // 不是对话中可以结束 + if (!conversationInProgress.value) { + textRoleRunning.value = false + clearTimeout(timer) + } else { + // 重新设置任务 + timer = setTimeout(task, textSpeed.value) + } + } + } + let timer = setTimeout(task, textSpeed.value) + } catch {} +} + +const LDGHSLYCEhartContainer = ref(); + + // 生成未来60秒的时间标签(LDG回收量预测) +const generateLDGHSLYCTimeLabels = () => { + const labels = []; + for (let i = 0; i < 60; i++) { + labels.push(i); + } + return labels; + }; + +// 生成未来60秒和过去60秒的时间标签 +const generateLDGGRQSYCTimeLabels = () => { + const labels = []; + const now = new Date(); + // 补零函数 + const padZero = num => num.toString().padStart(2, '0') + for (let i = 0; i < 121; i++) { + const date = new Date(now.getTime() + (i-60) * 1000); + const formatted = `${date.getFullYear()}-${padZero(date.getMonth() + 1)}-${padZero(date.getDate())} ` + + `${padZero(date.getHours())}:${padZero(date.getMinutes())}:${padZero(date.getSeconds())}`; + labels.push(formatted); + } + return labels; +}; + +// 数据格式转换示例(LDG回收量预测) +const seriesHSLYCDataConverter = () => { + const recovery = modelData.value.totalRecovery + const totalRecovery = [] + recovery.forEach(item => { + totalRecovery.push(item[0]) + }) + return totalRecovery; +}; + +const seriesHSLYCDataSchedule = (type) => { + const max = LDGMaxTotalValue() + const schedule = modelData.value.schedule[type] + // 返回对象格式数据,包含原始值和基准值 + const baseline = round(max, 0) + (6 - 2 * type) + return schedule.map(item => ({ + value: item + baseline, // 显示值 = 原始值 + 基准值 + original: item // 原始值 + })); +}; + +// 计算总回收量的最大值 +const LDGMaxTotalValue = () => { + const total = modelData.value.totalRecovery + let returnValue = computed(() => { + return Math.max(...total) + }) + return returnValue.value +}; + +// 计算柜容预测趋势的最大值和最小值,用于上下界限显示 +const LDGComputedValue = (type) => { + const tank = modelData.value.tankLevels + let returnValue = 0; + if(type == 'max') { + returnValue = computed(() => { + return Math.max(...tank) + 20 + }) + } else if(type == 'min') { + returnValue = computed(() => { + return Math.min(...tank) - 60 + }) + } else if(type == 'average') { + returnValue = computed(() => { + let sum = 0 + tank.forEach((item) => { + sum += item[0] + }) + return (sum / tank.length).toFixed(0); + }) + } + return returnValue.value +}; + +// 数据格式转换示例(LDG柜容趋势预测) +const seriesGRQSYCDataConverter = () => { + const tank = modelData.value.tankLevels + const tankLevels = [] + tank.forEach(item => { + tankLevels.push(item[0]) + }) + return tankLevels; +}; + +// 图表配置 +const initLDGHSLYCChart = () => { + const LDGHSLYCChart = echarts.init(LDGHSLYCEhartContainer.value); + const option = { + tooltip: { + trigger: 'axis', + formatter: function (params) { + let tooltipContent = params[0].name + '<br/>'; // 时间标签 + params.forEach(param => { + const seriesName = param.seriesName; + let originalValue; + + // 判断是否为转炉系列 + if (seriesName.includes('转炉')) { + // 直接从数据项中获取原始值 + originalValue = param.data.original; + tooltipContent += ` + ${param.marker} + ${seriesName}: ${originalValue.toFixed(0)}<br/> + `; + } else { + // 总回收量直接显示值 + originalValue = param.value; + tooltipContent += ` + ${param.marker} + ${seriesName}: ${originalValue.toFixed(2)}<br/> + `; + } + }); + return tooltipContent; + } + }, + grid: { + left: 25, + right: 25, + bottom: 10, + top: 30, + containLabel: true + }, + legend: { + top: 10, + right: 10, + data: ['1#转炉', '2#转炉', '3#转炉', '总回收量'], + textStyle: { + color: '#8FD6FE' + }, + itemWidth: 20, // 图例标记的宽度 + itemHeight: 0, // 图例标记的高度,设为较小值使其更像线条 + }, + xAxis: { + type: 'category', + boundaryGap: false, + data: generateLDGHSLYCTimeLabels(), + axisTick: { + show: false + }, + axisLine: { + lineStyle: { + color: '#C7E7FF' + } + }, + axisLabel: { + color: '#fff' + } + }, + yAxis: { + type: 'value', + min: 0, + axisLine: { + show: true, + lineStyle: { + color: '#C7E7FF', + } + }, + axisTick: { + show: false + }, + axisLabel: { + show: false + }, + splitLine: { + show: false // Y轴网格线 + } + }, + series: [ + { + name: '1#转炉', + type: 'line', + step: 'start', + data: seriesHSLYCDataSchedule(0), + showSymbol: false, // 取消数据点 + emphasis: { + focus: 'series' + }, + lineStyle: { + color: '#FF7686' // 粉色 + } + }, + { + name: '2#转炉', + type: 'line', + step: 'start', + data: seriesHSLYCDataSchedule(1), + showSymbol: false, // 取消数据点 + emphasis: { + focus: 'series' + }, + lineStyle: { + color: '#49FFD3' // 绿色 + } + }, + { + name: '3#转炉', + type: 'line', + step: 'start', + showSymbol: false, // 取消数据点 + data: seriesHSLYCDataSchedule(2), + color: '#FAC858', + emphasis: { + focus: 'series' + }, + lineStyle: { + color: '#FFAE81' // 橙色 + }, + }, + { + name: '总回收量', + type: 'line', + step: 'start', + showSymbol: false, // 取消数据点 + data: seriesHSLYCDataConverter(), + emphasis: { + focus: 'series' + }, + lineStyle: { + color: 'white' + }, + } + ] + }; + LDGHSLYCChart.setOption(option); +}; + +const LDGGRYCEhartContainer = ref(); + +/** 带预测的转炉数据阶梯图配置 */ +const initLDGGRQSYCChart = () => { + const labels = generateLDGGRQSYCTimeLabels() + const tankLevels = seriesGRQSYCDataConverter() + const fullData = []; + for(let i = 0; i < 121; i ++ ) { + let value = 90 + if(i >= 60) { + value = tankLevels[i - 60] + } + fullData.push([labels[i], value]); + } + + const splitTime = formatToDateTime(new Date()); // 分割时间点(当前时间) + const upperLimit = LDGComputedValue('max'); + const lowerLimit = LDGComputedValue('min'); + const averageValue = LDGComputedValue('average'); + + // 分割真实数据和预测数据 + const splitIndex = fullData.findIndex(item => item[0] === splitTime); + const realData = fullData.slice(0, splitIndex + 1); + const predictData = fullData.slice(splitIndex); + + // 创建纯净的上下限数据数组 + const upperLimitData = [ + [labels[0], upperLimit], + [labels[labels.length - 1], upperLimit] + ]; + + const lowerLimitData = [ + [labels[0], lowerLimit], + [labels[labels.length - 1], lowerLimit] + ]; + + const LDGGRQSYCChart = echarts.init(LDGGRYCEhartContainer.value); + const option = { + grid: { + left: 0, + right: 0, + bottom: 10, + top: 20, + containLabel: true + }, + tooltip: { + trigger: 'axis' + }, + xAxis: { + type: 'time', + axisLabel: { + formatter: (value) => { + return echarts.time.format(value, '{mm}:{ss}', false) + }, + color: '#C7E7FF' + }, + axisLine: { + show: true, + lineStyle: { + color: '#C7E7FF' + } + }, + axisTick: { + show: false + }, + splitLine: { + show: false + } + }, + yAxis: { + type: 'value', + min: 0, + max: LDGComputedValue('max') + 30, + axisLine: { + show: true, + lineStyle: { + color: '#C7E7FF' + } + }, + axisTick: { + show: false + }, + splitLine: { + show: false + } + }, + series: [ + // 真实数据(实线) + { + type: 'line', + data: realData, + smooth: true, + symbol: 'none', + lineStyle: { color: '#95E6FF' }, + markLine: { + symbol: ['none', 'none'], + label: { + show: false + }, + data: [{ + xAxis: splitTime, + lineStyle: { + type: 'solid', + color: '#5DFF9E', + width: 1 + } + }] + } + }, + // 预测数据(虚线) + { + type: 'line', + data: predictData, + smooth: true, + symbol: 'none', + lineStyle: { + type: 'dashed', + color: '#E76666', + dashOffset: 5 + } + }, + // 上限填充 + { + type: 'line', + data: upperLimitData, + lineStyle: { width: 0 }, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: 'rgba(255,0,0,0.2)' }, // 顶部颜色 + { offset: 1, color: 'rgba(255,0,0,0.5)' } // 底部颜色(到upperLimit) + ]), + origin: 'end' // 关键:从线条向上填充 + }, + silent: true + }, + // 下限填充 + { + type: 'line', + data: lowerLimitData, + lineStyle: { width: 0 }, + areaStyle: { + color: new echarts.graphic.LinearGradient(0, 1, 0, 0, [ + { offset: 0, color: 'rgba(0,0,255,0.2)' }, + { offset: 1, color: 'rgba(0,0,255,0.5)' } + ]) + }, + silent: true + }, + // 辅助线系列 + { + type: 'line', + symbol: 'none', // 关闭辅助线系列自身的数据点 + markLine: { + symbol: ['none', 'none'], // 全局隐藏标记点 + data: [ + // 上限辅助线配置部分 + { + yAxis: upperLimit, + symbol: 'none', + label: { show: false }, + emphasis: { + // 这里添加让高亮时标记点也不显示 + itemStyle: { + borderWidth: 0, + borderColor: 'transparent', + color: 'transparent' + }, + label: { show: false } + }, + lineStyle: { color: 'rgba(255,0,0)', width: 1, type: 'solid' } + }, + // 下限辅助线配置部分 + { + yAxis: lowerLimit, + symbol: 'none', + label: { show: false }, + emphasis: { + // 这里添加让高亮时标记点也不显示 + itemStyle: { + borderWidth: 0, + borderColor: 'transparent', + color: 'transparent' + }, + label: { show: false } + }, + lineStyle: { color: 'rgba(0,0,255)', width: 1, type: 'solid' } + }, + { + yAxis: averageValue, + label: { show: false }, + lineStyle: { type: 'dashed', color: 'rgba(0,194,255,0.52)', width: 1 } + } + ] + } + } + ] + }; + LDGGRQSYCChart.setOption(option); +} + +const isFullscreen = ref(false); + +// 核心:统一检测全屏状态 +const updateFullscreenStatus = () => { + // 同时检测 API 全屏和 F11 全屏(近似) + isFullscreen.value = !!document.fullscreenElement || window.outerHeight === screen.height; +}; + +// 监听全屏 API 变化 +const handleFullscreenChange = () => { + updateFullscreenStatus(); +}; + +// 监听 F11 按键(兜底) +const handleKeyPress = (e) => { + if (e.key === 'F11') { + e.preventDefault(); // 尝试阻止默认行为(部分浏览器允许) + setTimeout(updateFullscreenStatus, 100); // 延迟确保状态更新 + } +}; + +// 监听窗口大小变化(F11 全屏会触发) +const handleResize = () => { + updateFullscreenStatus(); +}; + +// 切换全屏(API 方式) +const toggleFullscreen = async () => { + if (!document.fullscreenElement) { + await document.documentElement.requestFullscreen(); + } else { + await document.exitFullscreen(); + } +}; + +//初始化全屏信息 +const initFullscreen = async () => { + // Fullscreen API 事件 + const events = ['fullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange']; + events.forEach(event => { + document.addEventListener(event, handleFullscreenChange); + }); + + // 窗口变化 + 键盘事件兜底 + window.addEventListener('resize', handleResize); + window.addEventListener('keydown', handleKeyPress); + + // 初始状态检测 + updateFullscreenStatus(); +} + +/** 初始化 **/ +onMounted(async () => { + await initFullscreen() + // 如果有 conversationId 参数,则默认选中 + if (route.query.conversationId) { + const id = route.query.conversationId as unknown as number + activeConversationId.value = id + await getConversation(id) + } + + // 获取列表数据 + activeMessageListLoading.value = true + await getMessageList() +}) + +// 清理监听 +onUnmounted(() => { + const events = ['fullscreenchange', 'webkitfullscreenchange', 'msfullscreenchange']; + events.forEach(event => { + document.removeEventListener(event, handleFullscreenChange); + }); + window.removeEventListener('resize', handleResize); + window.removeEventListener('keydown', handleKeyPress); +}); + +</script> + +<style lang="scss" scoped> +.gas-scheduling-container { + display: flex; + font-family: Microsoft YaHei, Microsoft YaHei; + .fullscreen-btn { + background-color: transparent; + border: 1px solid rgba(115, 196, 255, 0.5); + position: fixed; + margin-top: 3.5%; + margin-left: 28%; + color: rgba(115, 196, 255, 0.8); + font-size: 12px; + } + /* 背景层容器 */ + &::before { + content: ''; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; /* 置于内容层下方 */ + background: + url("@/assets/ai/zhuanlu/bg.png") center/cover no-repeat, + linear-gradient(to bottom, #0a1633dd, #0a1633dd); /* 叠加深色遮罩 */ + pointer-events: none; /* 防止遮挡交互 */ + } + .gas-scheduling-left { + width: 23%; + height: 89%; + margin: 2rem 2rem 0 1.8rem; + z-index: 1; + background-color: rgba(0, 0, 0, 0); /* 透明背景 */ + .data1-item { + height: 2.6rem; + width: 42%; + display: inline-block; + margin: 8px 10px ; + background: url("@/assets/ai/zhuanlu/data_bg1.png") center/cover no-repeat; + } + .data2-item { + height: 30px; + width: 42%; + display: inline-block; + margin: 6px 8px; + background: url("@/assets/ai/zhuanlu/data_bg2.png") center/cover no-repeat; + } + .content { + margin-left: 16px; + .value { + span:nth-child(1){ + height: 19px; + font-weight: bold; + font-size: 16px; + color: #FFAE81; + line-height: 19px; + } + span:nth-child(2) { + height: 16px; + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + } + } + .name { + height: 16px; + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + } + } + .content2 { + display: flex; + width: 11rem; + margin-left: 10px; + .name { + width: 95px; + height: 18px; + font-weight: 400; + font-size: 14px; + color: #C7E7FF; + } + .value { + margin-left: auto; + margin-right: 5px; + span:nth-child(1){ + height: 15px; + font-weight: bold; + font-size: 15px; + color: #FFAE81; + line-height: 15px; + } + span:nth-child(2) { + height: 15px; + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + } + } + } + #mqhsssxx { + .title { + height: 2rem; + background: + url("@/assets/ai/zhuanlu/mqhsssxx_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + } + #tsxx { + .title { + margin-top: 5px; + height: 2rem; + background: + url("@/assets/ai/zhuanlu/tsxx_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + } + #zlxx { + .title { + margin-top: 5px; + height: 2rem; + background: + url("@/assets/ai/zhuanlu/zlxx_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + :deep(.el-table) { + font-weight: 400; + font-size: 14px; + color: #DBEEFF; + text-align: left; + background-color: transparent !important; + } + + .transparent-table { + margin-top: 14px; + } + + /* 行样式 */ + :deep(.el-table .el-table__body tr) { + border: 1px solid rgba(16, 198, 255, 0.3); + margin-bottom: 4px; + border-radius: 4px; + overflow: hidden; + background-color: transparent; + } + + /* 行内容样式 */ + :deep(.el-table .el-table__body td) { + padding: 4px 0; + color: #FFAA5D; + } + + /* 列头样式 */ + :deep(.el-table th) { + color: #8FD6FE; + background: + url("@/assets/ai/zhuanlu/table_header_bg.png") center/cover no-repeat !important; /* 叠加深色遮罩 */ + border: none; + } + + :deep(.el-table tr) { + background: transparent; + } + + /* 行头样式 */ + :deep(.el-table .el-table__body td:first-child) { + color: #8FD6FE; + font-weight: 500; + background-color: transparent; + } + + :deep(.el-table .el-table__body tr:nth-child(even) td) { + background-color: rgba(0, 194, 255, 0.1); + } + + :deep(.el-table .el-table__body tr:nth-child(odd) td) { + background-color: rgba(0, 194, 255, 0.2); + } + + /* 移除表格内部边框线 */ + :deep(.el-table td, .el-table th.is-leaf) { + border-bottom: none; + } + + :deep(.el-table .el-table__inner-wrapper:before) { + background-color: transparent !important; + } + } + #mqxhssxx { + .title { + height: 30px; + margin-top: 15px; + background: + url("@/assets/ai/zhuanlu/mqxhssxx_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + } + } + + .gas-scheduling-center { + margin-top: 2.6rem; + width: 55.5rem !important; + .mode-switch { + margin-top: 20px; + margin-left: 43rem; + font-weight: 400; + font-size: 14px; + color: #73C4FF; + width: 40%; + /* 必须穿透到组件内部层级 */ + :deep(.custom-radio-group) { + --el-color-primary: red !important; /* 强制修改主题色变量 */ + } + + /* 所有按钮基础样式 */ + :deep(.custom-radio-group .el-radio-button__inner) { + background: black !important; /* 未选中黑色背景 */ + border: 1px solid rgba(173, 216, 230, 0.3) !important; /* 蓝色边框 */ + font-weight: 300; + font-size: 14px; + color: #DBEEFF; + transition: all 0.3s; + } + + /* 选中状态 */ + :deep(.custom-radio-group .el-radio-button.is-active .el-radio-button__inner) { + background: #b92220 !important; + color: gold !important; + font-weight: bolder; + } + + /* 强制覆盖原生选中状态 */ + :deep(.custom-radio-group .el-radio-button__orig-radio:checked + .el-radio-button__inner) { + background: inherit !important; /* 继承上层样式 */ + } + } + + // 头部 + .detail-container { + margin-left: 5px; + background-color: rgba(0, 0, 0, 0); /* 透明背景 */ + z-index: 1; + .header { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + box-shadow: 0 0 0 0 #dcdfe6; + + .title { + font-size: 18px; + font-weight: bold; + } + + .btns { + display: flex; + width: 300px; + flex-direction: row; + justify-content: flex-end; + //justify-content: space-between; + + .btn { + padding: 10px; + } + } + } + } + + // main 容器 + .main-container { + margin: 0; + padding: 0; + position: relative; + height: 30rem; + + .message-container { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + overflow-y: hidden; + padding: 0; + margin: 0; + } + .title { + background: url("@/assets/ai/zhuanlu/think_bg.png") center/cover no-repeat; + width: auto; + height: 1.8rem; + font-weight: 400; + font-size: 14px; + color: #8FD6FE; + text-align: left; + font-style: normal; + text-transform: none; + span { + margin-left: 30px; + } + } + } + + .result-container-title { + margin-top: 15px; + background: url("@/assets/ai/zhuanlu/ddtljl_result_title.png") center/cover no-repeat; + width: auto; + height: 1.8rem; + font-weight: 400; + font-size: 14px; + color: #8FD6FE; + text-align: left; + font-style: normal; + text-transform: none; + span { + margin-left: 30px; + } + .history-button { + color: rgba(143, 214, 254); + font-weight: bold; + float: right; + margin-right: 5px; + background-color: rgba(0, 255, 255, 0.1); + border-radius: 3px; + padding: 0 5px; + border: none; + cursor: pointer + } + .history-button:hover { + color: rgba(143, 214, 254, 0.5); + } + } + // main 容器 + .result-container { + padding: 0; + position: relative; + width: 100%; + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } + .result { + margin-top: 10px; + margin-left: 18px; + border-left: 1px solid #73C4FF; + .result-content { + width: 53rem; + height: 6rem; + margin-left: 10px; + font-weight: 400; + font-size: 14px; + background-color: rgba(219,238,255,0); + line-height: 21px; + text-align: left; + font-style: normal; + text-transform: none; + border: 0; + color: rgba(219,238,255,0.6); + } + .result-content:focus { + outline: none; + } + } + } + + // 输入框 + .input-container { + display: flex; + flex-direction: column; + height: auto; + margin: 0; + padding: 0; + overflow-y: auto; /* 垂直方向溢出时显示滚动条 */ + overflow-x: hidden; /* 水平方向隐藏滚动条 */ + /* Firefox */ + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.15) transparent; + + /* WebKit */ + &::-webkit-scrollbar { + width: 6px; + background: transparent; + } + &::-webkit-scrollbar-thumb { + border-radius: 4px; + background: rgba(0, 0, 0, 0.15); + transition: background 0.3s; + &:hover { background: rgba(0, 0, 0, 0.25); } + } + + .prompt-from { + display: flex; + flex-direction: column; + margin: 10px 20px 20px 20px; + padding: 9px 10px; + width: 53.9rem; + background: rgba(115,196,255,0.05); + border-radius: 4px 4px 4px 4px; + border: 1px solid #73C4FF; + } + + .prompt-input { + width: 53rem; + height: 11rem; + font-weight: 400; + font-size: 14px; + background-color: rgba(219,238,255,0); + line-height: 21px; + text-align: left; + font-style: normal; + text-transform: none; + border: 0; + color: rgba(219,238,255,0.6); + } + + .prompt-input:focus { + outline: none; + } + + .prompt-btns { + display: flex; + justify-content: space-between; + padding-bottom: 0; + padding-top: 5px; + .content { + /* 默认状态 */ + .el-button { + background: transparent !important; + border-color: rgba(115, 196, 255, 0.5); + color: #73C4FF; + border-radius: 15px !important; + } + + /* 上下文图标处理 */ + .content-icon { + color: blue; /* 图标颜色 */ + font-size: 18px; + margin-right: 10px; + background: url("@/assets/ai/zhuanlu/content.png"); + vertical-align: middle; + } + + /* 选中状态 */ + .active-button { + background: #409eff !important; + border-color: #409eff !important; + color: white !important; + .content-icon { + background: url("@/assets/ai/zhuanlu/content_select.png"); + vertical-align: middle; + } + } + + /* 按钮组间距处理 */ + .button-group .el-button { + margin-left: 0; + border-radius: 4px; + } + + /* 悬停效果 */ + .el-button:not(.active-button):hover { + border-color: rgba(115,196,255,0.5); + color: #409eff; + } + } + .message { + /* 所有状态通用透明背景 */ + :deep(.el-button) { + background: rgba(73, 254, 210, 0.8) !important; + border-color: currentColor; /* 保持与文字同色 */ + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 16px; + color: #123C4E; + clip-path: polygon( + 0 0, + 100% 0, + 100% 100%, + 10px 100%, /* 右下方向留出10px */ + 0 calc(100% - 10px) /* 左上方向留出10px */ + ); + position: relative; + padding-left: 15px; /* 增加右侧留白 */ + } + + /* 悬停状态 */ + :deep(.el-button:hover) { + background: rgba(73, 254, 210, 0.6) !important; /* 轻微悬停反馈 */ + } + + /* 点击状态 */ + :deep(.el-button:active) { + background: rgba(73, 254, 210, 1) !important; + } + + /* 禁用状态 */ + :deep(.el-button.is-disabled) { + opacity: 0.6; + background: transparent !important; + } + + /* 核心样式覆盖 */ + :deep(.el-switch__core) { + background: transparent !important; + border-radius: 0 0 15px 0 !important; + border: none !important; + height: 40px !important; + } + + /* 按钮内容容器 */ + .button-content { + display: flex; + align-items: center; + padding: 0 15px; + height: 100%; + } + } + } + } + } + + .gas-scheduling-right { + width: 22%; + height: 89%; + margin-left: 4.3rem; + margin-top: 2.8rem; + z-index: 1; + background-color: rgba(0, 0, 0, 0); /* 透明背景 */ + + #ldghslyc { + .title { + height: 1.8rem; + background: + url("@/assets/ai/zhuanlu/ldghslyc_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + } + #ldggrqsyc { + .title { + height: 1.8rem; + background: + url("@/assets/ai/zhuanlu/ldggrqsyc_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + } + #mqhsjhxx { + .title { + height: 1.8rem; + background: + url("@/assets/ai/zhuanlu/mqhsjhxx_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + .time-content { + display: inline-block; + width: 32%; + height: 2.9rem; + margin: 10px 0 5px 5px; + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + .time-content-item { + display: flex; + .name { + width: 1.6rem; + height: 3rem; + padding: 8px 3px; + background: linear-gradient( 180deg, rgba(115,196,255,0.1) 0%, rgba(255,136,69,0.1) 100%); + border-radius: 2px 2px 2px 2px; + border: 1px solid rgba(255,255,255,0.15); + opacity: 0.9; + } + .name span { + height: 1.8rem; + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 14px; + color: #C7E7FF; + line-height: 15px; + text-align: left; + font-style: normal; + text-transform: none; + } + .time { + width: 105px; + height: 38px; + margin: 4px; + display: inline-block; + .in-pot{ + width: 4px; + height: 4px; + background: #49FFD3; + border-radius: 80px 80px 80px 80px; + margin-top: 7px; + margin-right: 3px; + } + .out-pot{ + width: 4px; + height: 4px; + background: #FFAE81; + border-radius: 80px 80px 80px 80px; + margin-top: 7px; + margin-right: 3px; + } + } + .time > div { + float: left; + height: 25px; + } + } + } + .data2-item { + height: 2.8rem; + width: 45%; + display: inline-block; + margin: 6px 8px; + background: url("@/assets/ai/zhuanlu/data_bg3.png") no-repeat; + } + .content { + display: flex; + width: 192px; + margin-left: 10px; + + .name { + width: 95px; + height: 18px; + font-weight: 400; + font-size: 14px; + color: #C7E7FF; + } + + .value { + margin-top: 10px; + margin-left: auto; + margin-right: 5px; + span:nth-child(1) { + height: 15px; + font-weight: bold; + font-size: 15px; + color: #FFAE81; + line-height: 15px; + } + + span:nth-child(2) { + height: 15px; + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + } + } + } + } + #scmbyyxzb { + margin-top: 10px; + .title { + height: 1.8rem; + background: + url("@/assets/ai/zhuanlu/scmbyyxzb_title.png") center/cover no-repeat; /* 叠加深色遮罩 */ + } + .little-title { + font-size: 14px; + color: #8FD6FE; + margin: 10px; + } + .data3-item { + height: 5.2rem; + width: 30%; + display: inline-block; + margin: 0 6px 6px 6px; + background: url("@/assets/ai/zhuanlu/data_bg4.png") center/cover no-repeat; + .name { + font-family: Alimama ShuHeiTi, Alimama ShuHeiTi; + font-weight: bold; + font-size: 16px; + color: #FFFFFF; + margin-left: 3px; + } + .value { + color: #DBEEFF; + font-size: 14px; + margin-left: 3px; + } + .value-content { + color: #8FD6FE; + font-size: 12px; + margin-left: 3px; + } + } + .zb-content { + display: inline-block; + .item { + float: left; + } + .data4-item { + height: 2.2rem; + width: 7.8rem; + display: inline-block; + margin: 5px 0; + .content { + margin-left: 16px; + .value { + text-align: center; + span:nth-child(1){ + height: 19px; + font-weight: bold; + font-size: 16px; + color: #FFAE81; + line-height: 19px; + } + span:nth-child(2) { + height: 16px; + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + } + } + .name { + font-weight: 400; + font-size: 12px; + color: #C7E7FF; + } + } + .content div { + height: 25px; + } + } + .left-label { + width: 1.2rem; + height: 3.5rem; + background: url("@/assets/ai/zhuanlu/left_label.png") center/cover no-repeat; + } + .right-label { + width: 1.2rem; + height: 3.5rem; + background: url("@/assets/ai/zhuanlu/right_label.png") center/cover no-repeat; + } + } + } + } +} + +/* 背景颜色修改 */ +:deep(.el-progress .el-progress-bar .el-progress-bar__outer) { + width: 90%; + margin: 5px 0 2px 5px; + background-color: rgba(64, 158, 255, 0.3) !important; + border-radius: 2px; +} + +/* 进度条填充颜色 */ +:deep(.el-progress-bar__inner) { + border-radius: 2px; + transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1); +} + +</style> diff --git a/src/views/ai/image/index/components/ImageCard.vue b/src/views/ai/image/index/components/ImageCard.vue new file mode 100644 index 0000000..4ba78ca --- /dev/null +++ b/src/views/ai/image/index/components/ImageCard.vue @@ -0,0 +1,162 @@ +<template> + <el-card body-class="" class="image-card"> + <div class="image-operation"> + <div> + <el-button type="primary" text bg v-if="detail?.status === AiImageStatusEnum.IN_PROGRESS"> + 生成中 + </el-button> + <el-button text bg v-else-if="detail?.status === AiImageStatusEnum.SUCCESS"> + 已完成 + </el-button> + <el-button type="danger" text bg v-else-if="detail?.status === AiImageStatusEnum.FAIL"> + 异常 + </el-button> + </div> + <!-- 操作区 --> + <div> + <el-button + class="btn" + text + :icon="Download" + @click="handleButtonClick('download', detail)" + /> + <el-button + class="btn" + text + :icon="RefreshRight" + @click="handleButtonClick('regeneration', detail)" + /> + <el-button class="btn" text :icon="Delete" @click="handleButtonClick('delete', detail)" /> + <el-button class="btn" text :icon="More" @click="handleButtonClick('more', detail)" /> + </div> + </div> + <div class="image-wrapper" ref="cardImageRef"> + <el-image + class="image" + :src="detail?.picUrl" + :preview-src-list="[detail.picUrl]" + preview-teleported + /> + <div v-if="detail?.status === AiImageStatusEnum.FAIL"> + {{ detail?.errorMessage }} + </div> + </div> + <!-- Midjourney 专属操作 --> + <div class="image-mj-btns"> + <el-button + size="small" + v-for="button in detail?.buttons" + :key="button" + class="min-w-40px ml-0 mr-10px mt-5px" + @click="handleMidjourneyBtnClick(button)" + > + {{ button.label }}{{ button.emoji }} + </el-button> + </div> + </el-card> +</template> +<script setup lang="ts"> +import { Delete, Download, More, RefreshRight } from '@element-plus/icons-vue' +import { ImageVO, ImageMidjourneyButtonsVO } from '@/api/ai/image' +import { PropType } from 'vue' +import { ElLoading, LoadingOptionsResolved } from 'element-plus' +import { AiImageStatusEnum } from '@/views/ai/utils/constants' + +const message = useMessage() // 消息 + +const props = defineProps({ + detail: { + type: Object as PropType<ImageVO>, + require: true + } +}) + +const cardImageRef = ref<any>() // 卡片 image ref +const cardImageLoadingInstance = ref<any>() // 卡片 image ref + +/** 处理点击事件 */ +const handleButtonClick = async (type, detail: ImageVO) => { + emits('onBtnClick', type, detail) +} + +/** 处理 Midjourney 按钮点击事件 */ +const handleMidjourneyBtnClick = async (button: ImageMidjourneyButtonsVO) => { + // 确认窗体 + await message.confirm(`确认操作 "${button.label} ${button.emoji}" ?`) + emits('onMjBtnClick', button, props.detail) +} + +const emits = defineEmits(['onBtnClick', 'onMjBtnClick']) // emits + +/** 监听详情 */ +const { detail } = toRefs(props) +watch(detail, async (newVal, oldVal) => { + await handleLoading(newVal.status as string) +}) + +/** 处理加载状态 */ +const handleLoading = async (status: number) => { + // 情况一:如果是生成中,则设置加载中的 loading + if (status === AiImageStatusEnum.IN_PROGRESS) { + cardImageLoadingInstance.value = ElLoading.service({ + target: cardImageRef.value, + text: '生成中...' + } as LoadingOptionsResolved) + // 情况二:如果已经生成结束,则移除 loading + } else { + if (cardImageLoadingInstance.value) { + cardImageLoadingInstance.value.close() + cardImageLoadingInstance.value = null + } + } +} + +/** 初始化 */ +onMounted(async () => { + await handleLoading(props.detail.status as string) +}) +</script> + +<style scoped lang="scss"> +.image-card { + width: 320px; + height: auto; + border-radius: 10px; + position: relative; + display: flex; + flex-direction: column; + + .image-operation { + display: flex; + flex-direction: row; + justify-content: space-between; + + .btn { + //border: 1px solid red; + padding: 10px; + margin: 0; + } + } + + .image-wrapper { + overflow: hidden; + margin-top: 20px; + height: 280px; + flex: 1; + + .image { + width: 100%; + border-radius: 10px; + } + } + + .image-mj-btns { + margin-top: 5px; + width: 100%; + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: flex-start; + } +} +</style> diff --git a/src/views/ai/image/index/components/ImageDetail.vue b/src/views/ai/image/index/components/ImageDetail.vue new file mode 100644 index 0000000..ad15aa8 --- /dev/null +++ b/src/views/ai/image/index/components/ImageDetail.vue @@ -0,0 +1,224 @@ +<template> + <el-drawer + v-model="showDrawer" + title="图片详细" + @close="handleDrawerClose" + custom-class="drawer-class" + > + <!-- 图片 --> + <div class="item"> + <div class="body"> + <el-image + class="image" + :src="detail?.picUrl" + :preview-src-list="[detail.picUrl]" + preview-teleported + /> + </div> + </div> + <!-- 时间 --> + <div class="item"> + <div class="tip">时间</div> + <div class="body"> + <div>提交时间:{{ formatTime(detail.createTime, 'yyyy-MM-dd HH:mm:ss') }}</div> + <div>生成时间:{{ formatTime(detail.finishTime, 'yyyy-MM-dd HH:mm:ss') }}</div> + </div> + </div> + <!-- 模型 --> + <div class="item"> + <div class="tip">模型</div> + <div class="body"> {{ detail.model }}({{ detail.height }}x{{ detail.width }}) </div> + </div> + <!-- 提示词 --> + <div class="item"> + <div class="tip">提示词</div> + <div class="body"> + {{ detail.prompt }} + </div> + </div> + <!-- 地址 --> + <div class="item"> + <div class="tip">图片地址</div> + <div class="body"> + {{ detail.picUrl }} + </div> + </div> + <!-- StableDiffusion 专属区域 --> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.sampler" + > + <div class="tip">采样方法</div> + <div class="body"> + {{ + StableDiffusionSamplers.find( + (item: ImageModelVO) => item.key === detail?.options?.sampler + )?.name + }} + </div> + </div> + <div + class="item" + v-if=" + detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.clipGuidancePreset + " + > + <div class="tip">CLIP</div> + <div class="body"> + {{ + StableDiffusionClipGuidancePresets.find( + (item: ImageModelVO) => item.key === detail?.options?.clipGuidancePreset + )?.name + }} + </div> + </div> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.stylePreset" + > + <div class="tip">风格</div> + <div class="body"> + {{ + StableDiffusionStylePresets.find( + (item: ImageModelVO) => item.key === detail?.options?.stylePreset + )?.name + }} + </div> + </div> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.steps" + > + <div class="tip">迭代步数</div> + <div class="body"> + {{ detail?.options?.steps }} + </div> + </div> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.scale" + > + <div class="tip">引导系数</div> + <div class="body"> + {{ detail?.options?.scale }} + </div> + </div> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.STABLE_DIFFUSION && detail?.options?.seed" + > + <div class="tip">随机因子</div> + <div class="body"> + {{ detail?.options?.seed }} + </div> + </div> + <!-- Dall3 专属区域 --> + <div class="item" v-if="detail.platform === AiPlatformEnum.OPENAI && detail?.options?.style"> + <div class="tip">风格选择</div> + <div class="body"> + {{ Dall3StyleList.find((item: ImageModelVO) => item.key === detail?.options?.style)?.name }} + </div> + </div> + <!-- Midjourney 专属区域 --> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.version" + > + <div class="tip">模型版本</div> + <div class="body"> + {{ detail?.options?.version }} + </div> + </div> + <div + class="item" + v-if="detail.platform === AiPlatformEnum.MIDJOURNEY && detail?.options?.referImageUrl" + > + <div class="tip">参考图</div> + <div class="body"> + <el-image :src="detail.options.referImageUrl" /> + </div> + </div> + </el-drawer> +</template> + +<script setup lang="ts"> +import { ImageApi, ImageVO } from '@/api/ai/image' +import { + AiPlatformEnum, + Dall3StyleList, + ImageModelVO, + StableDiffusionClipGuidancePresets, + StableDiffusionSamplers, + StableDiffusionStylePresets +} from '@/views/ai/utils/constants' +import { formatTime } from '@/utils' + +const showDrawer = ref<boolean>(false) // 是否显示 +const detail = ref<ImageVO>({} as ImageVO) // 图片详细信息 + +const props = defineProps({ + show: { + type: Boolean, + require: true, + default: false + }, + id: { + type: Number, + required: true + } +}) + +/** 关闭抽屉 */ +const handleDrawerClose = async () => { + emits('handleDrawerClose') +} + +/** 监听 drawer 是否打开 */ +const { show } = toRefs(props) +watch(show, async (newValue, oldValue) => { + showDrawer.value = newValue as boolean +}) + +/** 获取图片详情 */ +const getImageDetail = async (id: number) => { + detail.value = await ImageApi.getImageMy(id) +} + +/** 监听 id 变化,加载最新图片详情 */ +const { id } = toRefs(props) +watch(id, async (newVal, oldVal) => { + if (newVal) { + await getImageDetail(newVal) + } +}) + +const emits = defineEmits(['handleDrawerClose']) +</script> +<style scoped lang="scss"> +.item { + margin-bottom: 20px; + width: 100%; + overflow: hidden; + word-wrap: break-word; + + .header { + display: flex; + flex-direction: row; + justify-content: space-between; + } + + .tip { + font-weight: bold; + font-size: 16px; + } + + .body { + margin-top: 10px; + color: #616161; + + .taskImage { + border-radius: 10px; + } + } +} +</style> diff --git a/src/views/ai/image/index/components/ImageList.vue b/src/views/ai/image/index/components/ImageList.vue new file mode 100644 index 0000000..ced006f --- /dev/null +++ b/src/views/ai/image/index/components/ImageList.vue @@ -0,0 +1,245 @@ +<template> + <el-card class="dr-task" body-class="task-card" shadow="never"> + <template #header> + 绘画任务 + <!-- TODO @fan:看看,怎么优化下这个样子哈。 --> + <el-button @click="handleViewPublic">绘画作品</el-button> + </template> + <!-- 图片列表 --> + <div class="task-image-list" ref="imageListRef"> + <ImageCard + v-for="image in imageList" + :key="image.id" + :detail="image" + @on-btn-click="handleImageButtonClick" + @on-mj-btn-click="handleImageMidjourneyButtonClick" + /> + </div> + <div class="task-image-pagination"> + <Pagination + :total="pageTotal" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getImageList" + /> + </div> + </el-card> + + <!-- 图片详情 --> + <ImageDetail + :show="isShowImageDetail" + :id="showImageDetailId" + @handle-drawer-close="handleDetailClose" + /> +</template> +<script setup lang="ts"> +import { + ImageApi, + ImageVO, + ImageMidjourneyActionVO, + ImageMidjourneyButtonsVO +} from '@/api/ai/image' +import ImageDetail from './ImageDetail.vue' +import ImageCard from './ImageCard.vue' +import { ElLoading, LoadingOptionsResolved } from 'element-plus' +import { AiImageStatusEnum } from '@/views/ai/utils/constants' +import download from '@/utils/download' + +const message = useMessage() // 消息弹窗 +const router = useRouter() // 路由 + +// 图片分页相关的参数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10 +}) +const pageTotal = ref<number>(0) // page size +const imageList = ref<ImageVO[]>([]) // image 列表 +const imageListLoadingInstance = ref<any>() // image 列表是否正在加载中 +const imageListRef = ref<any>() // ref +// 图片轮询相关的参数(正在生成中的) +const inProgressImageMap = ref<{}>({}) // 监听的 image 映射,一般是生成中(需要轮询),key 为 image 编号,value 为 image +const inProgressTimer = ref<any>() // 生成中的 image 定时器,轮询生成进展 +// 图片详情相关的参数 +const isShowImageDetail = ref<boolean>(false) // 图片详情是否展示 +const showImageDetailId = ref<number>(0) // 图片详情的图片编号 + +/** 处理查看绘图作品 */ +const handleViewPublic = () => { + router.push({ + name: 'AiImageSquare' + }) +} + +/** 查看图片的详情 */ +const handleDetailOpen = async () => { + isShowImageDetail.value = true +} + +/** 关闭图片的详情 */ +const handleDetailClose = async () => { + isShowImageDetail.value = false +} + +/** 获得 image 图片列表 */ +const getImageList = async () => { + try { + // 1. 加载图片列表 + imageListLoadingInstance.value = ElLoading.service({ + target: imageListRef.value, + text: '加载中...' + } as LoadingOptionsResolved) + const { list, total } = await ImageApi.getImagePageMy(queryParams) + imageList.value = list + pageTotal.value = total + + // 2. 计算需要轮询的图片 + const newWatImages = {} + imageList.value.forEach((item) => { + if (item.status === AiImageStatusEnum.IN_PROGRESS) { + newWatImages[item.id] = item + } + }) + inProgressImageMap.value = newWatImages + } finally { + // 关闭正在“加载中”的 Loading + if (imageListLoadingInstance.value) { + imageListLoadingInstance.value.close() + imageListLoadingInstance.value = null + } + } +} + +/** 轮询生成中的 image 列表 */ +const refreshWatchImages = async () => { + const imageIds = Object.keys(inProgressImageMap.value).map(Number) + if (imageIds.length == 0) { + return + } + const list = (await ImageApi.getImageListMyByIds(imageIds)) as ImageVO[] + const newWatchImages = {} + list.forEach((image) => { + if (image.status === AiImageStatusEnum.IN_PROGRESS) { + newWatchImages[image.id] = image + } else { + const index = imageList.value.findIndex((oldImage) => image.id === oldImage.id) + if (index >= 0) { + // 更新 imageList + imageList.value[index] = image + } + } + }) + inProgressImageMap.value = newWatchImages +} + +/** 图片的点击事件 */ +const handleImageButtonClick = async (type: string, imageDetail: ImageVO) => { + // 详情 + if (type === 'more') { + showImageDetailId.value = imageDetail.id + await handleDetailOpen() + return + } + // 删除 + if (type === 'delete') { + await message.confirm(`是否删除照片?`) + await ImageApi.deleteImageMy(imageDetail.id) + await getImageList() + message.success('删除成功!') + return + } + // 下载 + if (type === 'download') { + await download.image({ url: imageDetail.picUrl }) + return + } + // 重新生成 + if (type === 'regeneration') { + await emits('onRegeneration', imageDetail) + return + } +} + +/** 处理 Midjourney 按钮点击事件 */ +const handleImageMidjourneyButtonClick = async ( + button: ImageMidjourneyButtonsVO, + imageDetail: ImageVO +) => { + // 1. 构建 params 参数 + const data = { + id: imageDetail.id, + customId: button.customId + } as ImageMidjourneyActionVO + // 2. 发送 action + await ImageApi.midjourneyAction(data) + // 3. 刷新列表 + await getImageList() +} + +defineExpose({ getImageList }) // 暴露组件方法 + +const emits = defineEmits(['onRegeneration']) + +/** 组件挂在的时候 */ +onMounted(async () => { + // 获取 image 列表 + await getImageList() + // 自动刷新 image 列表 + inProgressTimer.value = setInterval(async () => { + await refreshWatchImages() + }, 1000 * 3) +}) + +/** 组件取消挂在的时候 */ +onUnmounted(async () => { + if (inProgressTimer.value) { + clearInterval(inProgressTimer.value) + } +}) +</script> +<style lang="scss"> +.dr-task { + width: 100%; + height: 100%; +} +.task-card { + margin: 0; + padding: 0; + height: 100%; + position: relative; +} + +.task-image-list { + position: relative; + display: flex; + flex-direction: row; + flex-wrap: wrap; + align-content: flex-start; + height: 100%; + overflow: auto; + padding: 20px 20px 140px; + box-sizing: border-box; /* 确保内边距不会增加高度 */ + + > div { + margin-right: 20px; + margin-bottom: 20px; + } + > div:last-of-type { + //margin-bottom: 100px; + } +} + +.task-image-pagination { + position: absolute; + bottom: 60px; + height: 50px; + line-height: 90px; + width: 100%; + z-index: 999; + background-color: #ffffff; + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; +} +</style> diff --git a/src/views/ai/image/index/components/common/index.vue b/src/views/ai/image/index/components/common/index.vue new file mode 100644 index 0000000..57c47b0 --- /dev/null +++ b/src/views/ai/image/index/components/common/index.vue @@ -0,0 +1,224 @@ +<!-- dall3 --> +<template> + <div class="prompt"> + <el-text tag="b">画面描述</el-text> + <el-text tag="p">建议使用“形容词 + 动词 + 风格”的格式,使用“,”隔开</el-text> + <el-input + v-model="prompt" + maxlength="1024" + :rows="5" + class="w-100% mt-15px" + input-style="border-radius: 7px;" + placeholder="例如:童话里的小屋应该是什么样子?" + show-word-limit + type="textarea" + /> + </div> + <div class="hot-words"> + <div> + <el-text tag="b">随机热词</el-text> + </div> + <el-space wrap class="word-list"> + <el-button + round + class="btn" + :type="selectHotWord === hotWord ? 'primary' : 'default'" + v-for="hotWord in ImageHotWords" + :key="hotWord" + @click="handleHotWordClick(hotWord)" + > + {{ hotWord }} + </el-button> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">平台</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-select + v-model="otherPlatform" + placeholder="Select" + size="large" + class="!w-350px" + @change="handlerPlatformChange" + > + <el-option + v-for="item in OtherPlatformEnum" + :key="item.key" + :label="item.name" + :value="item.key" + /> + </el-select> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">模型</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-select v-model="modelId" placeholder="Select" size="large" class="!w-350px"> + <el-option + v-for="item in platformModels" + :key="item.id" + :label="item.name" + :value="item.id" + /> + </el-select> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">图片尺寸</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-input v-model="width" type="number" class="w-170px" placeholder="图片宽度" /> + <el-input v-model="height" type="number" class="w-170px" placeholder="图片高度" /> + </el-space> + </div> + <div class="btns"> + <el-button + type="primary" + size="large" + round + :loading="drawIn" + :disabled="prompt.length === 0" + @click="handleGenerateImage" + > + {{ drawIn ? '生成中' : '生成内容' }} + </el-button> + </div> +</template> +<script setup lang="ts"> +import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image' +import { AiPlatformEnum, ImageHotWords, OtherPlatformEnum } from '@/views/ai/utils/constants' +import { ModelVO } from '@/api/ai/model/model' + +const message = useMessage() // 消息弹窗 + +// 接收父组件传入的模型列表 +const props = defineProps({ + models: { + type: Array<ModelVO>, + default: () => [] as ModelVO[] + } +}) +const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits + +// 定义属性 +const drawIn = ref<boolean>(false) // 生成中 +const selectHotWord = ref<string>('') // 选中的热词 +// 表单 +const prompt = ref<string>('') // 提示词 +const width = ref<number>(512) // 图片宽度 +const height = ref<number>(512) // 图片高度 +const otherPlatform = ref<string>(AiPlatformEnum.TONG_YI) // 平台 +const platformModels = ref<ModelVO[]>([]) // 模型列表 +const modelId = ref<number>() // 选中的模型 + +/** 选择热词 */ +const handleHotWordClick = async (hotWord: string) => { + // 情况一:取消选中 + if (selectHotWord.value == hotWord) { + selectHotWord.value = '' + return + } + + // 情况二:选中 + selectHotWord.value = hotWord // 选中 + prompt.value = hotWord // 替换提示词 +} + +/** 图片生成 */ +const handleGenerateImage = async () => { + // 二次确认 + await message.confirm(`确认生成内容?`) + try { + // 加载中 + drawIn.value = true + // 回调 + emits('onDrawStart', otherPlatform.value) + // 发送请求 + const form = { + platform: otherPlatform.value, + modelId: modelId.value, // 模型 + prompt: prompt.value, // 提示词 + width: width.value, // 图片宽度 + height: height.value, // 图片高度 + options: {} + } as unknown as ImageDrawReqVO + await ImageApi.drawImage(form) + } finally { + // 回调 + emits('onDrawComplete', otherPlatform.value) + // 加载结束 + drawIn.value = false + } +} + +/** 填充值 */ +const settingValues = async (detail: ImageVO) => { + prompt.value = detail.prompt + width.value = detail.width + height.value = detail.height +} + +/** 平台切换 */ +const handlerPlatformChange = async (platform: string) => { + // 根据选择的平台筛选模型 + platformModels.value = props.models.filter((item: ModelVO) => item.platform === platform) + + // 切换平台,默认选择一个模型 + if (platformModels.value.length > 0) { + modelId.value = platformModels.value[0].id // 使用 model 属性作为值 + } else { + modelId.value = undefined + } +} + +/** 监听 models 变化 */ +watch( + () => props.models, + () => { + handlerPlatformChange(otherPlatform.value) + }, + { immediate: true, deep: true } +) +/** 暴露组件方法 */ +defineExpose({ settingValues }) +</script> +<style scoped lang="scss"> +.hot-words { + display: flex; + flex-direction: column; + margin-top: 30px; + + .word-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: start; + margin-top: 15px; + + .btn { + margin: 0; + } + } +} + +// 模型 +.group-item { + margin-top: 30px; + + .group-item-body { + margin-top: 15px; + width: 100%; + } +} + +.btns { + display: flex; + justify-content: center; + margin-top: 50px; +} +</style> diff --git a/src/views/ai/image/index/components/dall3/index.vue b/src/views/ai/image/index/components/dall3/index.vue new file mode 100644 index 0000000..efa41ed --- /dev/null +++ b/src/views/ai/image/index/components/dall3/index.vue @@ -0,0 +1,363 @@ +<!-- dall3 --> +<template> + <div class="prompt"> + <el-text tag="b">画面描述</el-text> + <el-text tag="p">建议使用"形容词 + 动词 + 风格"的格式,使用","隔开</el-text> + <el-input + v-model="prompt" + maxlength="1024" + :rows="5" + class="w-100% mt-15px" + input-style="border-radius: 7px;" + placeholder="例如:童话里的小屋应该是什么样子?" + show-word-limit + type="textarea" + /> + </div> + <div class="hot-words"> + <div> + <el-text tag="b">随机热词</el-text> + </div> + <el-space wrap class="word-list"> + <el-button + round + class="btn" + :type="selectHotWord === hotWord ? 'primary' : 'default'" + v-for="hotWord in ImageHotWords" + :key="hotWord" + @click="handleHotWordClick(hotWord)" + > + {{ hotWord }} + </el-button> + </el-space> + </div> + <div class="model"> + <div> + <el-text tag="b">模型选择</el-text> + </div> + <el-space wrap class="model-list"> + <div + :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'" + v-for="model in Dall3Models" + :key="model.key" + > + <el-image :src="model.image" fit="contain" @click="handleModelClick(model)" /> + <div class="model-font">{{ model.name }}</div> + </div> + </el-space> + </div> + <div class="image-style"> + <div> + <el-text tag="b">风格选择</el-text> + </div> + <el-space wrap class="image-style-list"> + <div + :class="style === imageStyle.key ? 'image-style-item selectImageStyle' : 'image-style-item'" + v-for="imageStyle in Dall3StyleList" + :key="imageStyle.key" + > + <el-image :src="imageStyle.image" fit="contain" @click="handleStyleClick(imageStyle)" /> + <div class="style-font">{{ imageStyle.name }}</div> + </div> + </el-space> + </div> + <div class="image-size"> + <div> + <el-text tag="b">画面比例</el-text> + </div> + <el-space wrap class="size-list"> + <div + class="size-item" + v-for="imageSize in Dall3SizeList" + :key="imageSize.key" + @click="handleSizeClick(imageSize)" + > + <div + :class="selectSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'" + > + <div :style="imageSize.style"></div> + </div> + <div class="size-font">{{ imageSize.name }}</div> + </div> + </el-space> + </div> + <div class="btns"> + <el-button + type="primary" + size="large" + round + :loading="drawIn" + :disabled="prompt.length === 0" + @click="handleGenerateImage" + > + {{ drawIn ? '生成中' : '生成内容' }} + </el-button> + </div> +</template> +<script setup lang="ts"> +import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image' +import { + Dall3Models, + Dall3StyleList, + ImageHotWords, + Dall3SizeList, + ImageModelVO, + AiPlatformEnum, + ImageSizeVO +} from '@/views/ai/utils/constants' +import { ModelVO } from '@/api/ai/model/model' + +const message = useMessage() // 消息弹窗 + +// 接收父组件传入的模型列表 +const props = defineProps({ + models: { + type: Array<ModelVO>, + default: () => [] as ModelVO[] + } +}) +const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits + +// 定义属性 +const prompt = ref<string>('') // 提示词 +const drawIn = ref<boolean>(false) // 生成中 +const selectHotWord = ref<string>('') // 选中的热词 +const selectModel = ref<string>('dall-e-3') // 模型 +const selectSize = ref<string>('1024x1024') // 选中 size +const style = ref<string>('vivid') // style 样式 + +/** 选择热词 */ +const handleHotWordClick = async (hotWord: string) => { + // 情况一:取消选中 + if (selectHotWord.value == hotWord) { + selectHotWord.value = '' + return + } + + // 情况二:选中 + selectHotWord.value = hotWord + prompt.value = hotWord +} + +/** 选择 model 模型 */ +const handleModelClick = async (model: ImageModelVO) => { + selectModel.value = model.key + // 可以在这里添加模型特定的处理逻辑 + // 例如,如果未来需要根据不同模型设置不同参数 + if (model.key === 'dall-e-3') { + // DALL-E-3 模型特定的处理 + style.value = 'vivid' // 默认设置vivid风格 + } else if (model.key === 'dall-e-2') { + // DALL-E-2 模型特定的处理 + style.value = 'natural' // 如果有其他DALL-E-2适合的默认风格 + } + + // 更新其他相关参数 + // 例如可以默认选择最适合当前模型的尺寸 + const recommendedSize = Dall3SizeList.find( + (size) => + (model.key === 'dall-e-3' && size.key === '1024x1024') || + (model.key === 'dall-e-2' && size.key === '512x512') + ) + + if (recommendedSize) { + selectSize.value = recommendedSize.key + } +} + +/** 选择 style 样式 */ +const handleStyleClick = async (imageStyle: ImageModelVO) => { + style.value = imageStyle.key +} + +/** 选择 size 大小 */ +const handleSizeClick = async (imageSize: ImageSizeVO) => { + selectSize.value = imageSize.key +} + +/** 图片生产 */ +const handleGenerateImage = async () => { + // 从 models 中查找匹配的模型 + const matchedModel = props.models.find( + (item) => item.model === selectModel.value && item.platform === AiPlatformEnum.OPENAI + ) + if (!matchedModel) { + message.error('该模型不可用,请选择其它模型') + return + } + + // 二次确认 + await message.confirm(`确认生成内容?`) + try { + // 加载中 + drawIn.value = true + // 回调 + emits('onDrawStart', AiPlatformEnum.OPENAI) + const imageSize = Dall3SizeList.find((item) => item.key === selectSize.value) as ImageSizeVO + const form = { + platform: AiPlatformEnum.OPENAI, + prompt: prompt.value, // 提示词 + modelId: matchedModel.id, // 使用匹配到的模型 + style: style.value, // 图像生成的风格 + width: imageSize.width, // size 不能为空 + height: imageSize.height, // size 不能为空 + options: { + style: style.value // 图像生成的风格 + } + } as ImageDrawReqVO + // 发送请求 + await ImageApi.drawImage(form) + } finally { + // 回调 + emits('onDrawComplete', AiPlatformEnum.OPENAI) + // 加载结束 + drawIn.value = false + } +} + +/** 填充值 */ +const settingValues = async (detail: ImageVO) => { + prompt.value = detail.prompt + selectModel.value = detail.model + style.value = detail.options?.style + const imageSize = Dall3SizeList.find( + (item) => item.key === `${detail.width}x${detail.height}` + ) as ImageSizeVO + await handleSizeClick(imageSize) +} + +/** 暴露组件方法 */ +defineExpose({ settingValues }) +</script> +<style scoped lang="scss"> +// 热词 +.hot-words { + display: flex; + flex-direction: column; + margin-top: 30px; + + .word-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: start; + margin-top: 15px; + + .btn { + margin: 0; + } + } +} + +// 模型 +.model { + margin-top: 30px; + + .model-list { + margin-top: 15px; + + .modal-item { + width: 110px; + //outline: 1px solid blue; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + border: 3px solid transparent; + cursor: pointer; + + .model-font { + font-size: 14px; + color: #3e3e3e; + font-weight: bold; + } + } + + .selectModel { + border: 3px solid #1293ff; + border-radius: 5px; + } + } +} + +// 样式 style +.image-style { + margin-top: 30px; + + .image-style-list { + margin-top: 15px; + + .image-style-item { + width: 110px; + //outline: 1px solid blue; + overflow: hidden; + display: flex; + flex-direction: column; + align-items: center; + border: 3px solid transparent; + cursor: pointer; + + .style-font { + font-size: 14px; + color: #3e3e3e; + font-weight: bold; + } + } + + .selectImageStyle { + border: 3px solid #1293ff; + border-radius: 5px; + } + } +} + +// 尺寸 +.image-size { + width: 100%; + margin-top: 30px; + + .size-list { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + margin-top: 20px; + + .size-item { + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + + .size-wrapper { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 7px; + padding: 4px; + width: 50px; + height: 50px; + background-color: #fff; + border: 1px solid #fff; + } + + .size-font { + font-size: 14px; + color: #3e3e3e; + font-weight: bold; + } + } + } + + .selectImageSize { + border: 1px solid #1293ff !important; + } +} + +.btns { + display: flex; + justify-content: center; + margin-top: 50px; +} +</style> diff --git a/src/views/ai/image/index/components/midjourney/index.vue b/src/views/ai/image/index/components/midjourney/index.vue new file mode 100644 index 0000000..adfcb90 --- /dev/null +++ b/src/views/ai/image/index/components/midjourney/index.vue @@ -0,0 +1,350 @@ +<!-- dall3 --> +<template> + <div class="prompt"> + <el-text tag="b">画面描述</el-text> + <el-text tag="p">建议使用“形容词+动词+风格”的格式,使用“,”隔开.</el-text> + <el-input + v-model="prompt" + maxlength="1024" + :rows="5" + class="w-100% mt-15px" + input-style="border-radius: 7px;" + placeholder="例如:童话里的小屋应该是什么样子?" + show-word-limit + type="textarea" + /> + </div> + <div class="hot-words"> + <div> + <el-text tag="b">随机热词</el-text> + </div> + <el-space wrap class="word-list"> + <el-button + round + class="btn" + :type="selectHotWord === hotWord ? 'primary' : 'default'" + v-for="hotWord in ImageHotWords" + :key="hotWord" + @click="handleHotWordClick(hotWord)" + > + {{ hotWord }} + </el-button> + </el-space> + </div> + <div class="image-size"> + <div> + <el-text tag="b">尺寸</el-text> + </div> + <el-space wrap class="size-list"> + <div + class="size-item" + v-for="imageSize in MidjourneySizeList" + :key="imageSize.key" + @click="handleSizeClick(imageSize)" + > + <div + :class="selectSize === imageSize.key ? 'size-wrapper selectImageSize' : 'size-wrapper'" + > + <div :style="imageSize.style"></div> + </div> + <div class="size-font">{{ imageSize.key }}</div> + </div> + </el-space> + </div> + <div class="model"> + <div> + <el-text tag="b">模型</el-text> + </div> + <el-space wrap class="model-list"> + <div + :class="selectModel === model.key ? 'modal-item selectModel' : 'modal-item'" + v-for="model in MidjourneyModels" + :key="model.key" + > + <el-image :src="model.image" fit="contain" @click="handleModelClick(model)" /> + <div class="model-font">{{ model.name }}</div> + </div> + </el-space> + </div> + <div class="version"> + <div> + <el-text tag="b">版本</el-text> + </div> + <el-space wrap class="version-list"> + <el-select + v-model="selectVersion" + class="version-select !w-350px" + clearable + placeholder="请选择版本" + > + <el-option + v-for="item in versionList" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </el-space> + </div> + <div class="model"> + <div> + <el-text tag="b">参考图</el-text> + </div> + <el-space wrap class="model-list"> + <UploadImg v-model="referImageUrl" height="120px" width="120px" /> + </el-space> + </div> + <div class="btns"> + <el-button + type="primary" + size="large" + round + :disabled="prompt.length === 0" + @click="handleGenerateImage" + > + {{ drawIn ? '生成中' : '生成内容' }} + </el-button> + </div> +</template> +<script setup lang="ts"> +import { ImageApi, ImageMidjourneyImagineReqVO, ImageVO } from '@/api/ai/image' +import { + AiPlatformEnum, + ImageHotWords, + ImageSizeVO, + ImageModelVO, + MidjourneyModels, + MidjourneySizeList, + MidjourneyVersions, + NijiVersionList +} from '@/views/ai/utils/constants' +import { ModelVO } from '@/api/ai/model/model' + +const message = useMessage() // 消息弹窗 + +// 接收父组件传入的模型列表 +const props = defineProps({ + models: { + type: Array<ModelVO>, + default: () => [] as ModelVO[] + } +}) +const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits + +// 定义属性 +const drawIn = ref<boolean>(false) // 生成中 +const selectHotWord = ref<string>('') // 选中的热词 +// 表单 +const prompt = ref<string>('') // 提示词 +const referImageUrl = ref<any>() // 参考图 +const selectModel = ref<string>('midjourney') // 选中的模型 +const selectSize = ref<string>('1:1') // 选中 size +const selectVersion = ref<any>('6.0') // 选中的 version +const versionList = ref<any>(MidjourneyVersions) // version 列表 + +/** 选择热词 */ +const handleHotWordClick = async (hotWord: string) => { + // 情况一:取消选中 + if (selectHotWord.value == hotWord) { + selectHotWord.value = '' + return + } + + // 情况二:选中 + selectHotWord.value = hotWord // 选中 + prompt.value = hotWord // 设置提示次 +} + +/** 点击 size 尺寸 */ +const handleSizeClick = async (imageSize: ImageSizeVO) => { + selectSize.value = imageSize.key +} + +/** 点击 model 模型 */ +const handleModelClick = async (model: ImageModelVO) => { + selectModel.value = model.key + if (model.key === 'niji') { + versionList.value = NijiVersionList // 默认选择 niji + } else { + versionList.value = MidjourneyVersions // 默认选择 midjourney + } + selectVersion.value = versionList.value[0].value +} + +/** 图片生成 */ +const handleGenerateImage = async () => { + // 从 models 中查找匹配的模型 + const matchedModel = props.models.find( + (item) => item.model === selectModel.value && item.platform === AiPlatformEnum.MIDJOURNEY + ) + if (!matchedModel) { + message.error('该模型不可用,请选择其它模型') + return + } + + // 二次确认 + await message.confirm(`确认生成内容?`) + try { + // 加载中 + drawIn.value = true + // 回调 + emits('onDrawStart', AiPlatformEnum.MIDJOURNEY) + // 发送请求 + const imageSize = MidjourneySizeList.find( + (item) => selectSize.value === item.key + ) as ImageSizeVO + const req = { + prompt: prompt.value, + modelId: matchedModel.id, + width: imageSize.width, + height: imageSize.height, + version: selectVersion.value, + referImageUrl: referImageUrl.value + } as ImageMidjourneyImagineReqVO + await ImageApi.midjourneyImagine(req) + } finally { + // 回调 + emits('onDrawComplete', AiPlatformEnum.MIDJOURNEY) + // 加载结束 + drawIn.value = false + } +} + +/** 填充值 */ +const settingValues = async (detail: ImageVO) => { + // 提示词 + prompt.value = detail.prompt + // image size + const imageSize = MidjourneySizeList.find( + (item) => item.key === `${detail.width}:${detail.height}` + ) as ImageSizeVO + selectSize.value = imageSize.key + // 选中模型 + const model = MidjourneyModels.find((item) => item.key === detail.options?.model) as ImageModelVO + await handleModelClick(model) + // 版本 + selectVersion.value = versionList.value.find( + (item) => item.value === detail.options?.version + ).value + // image + referImageUrl.value = detail.options.referImageUrl +} + +/** 暴露组件方法 */ +defineExpose({ settingValues }) +</script> +<style scoped lang="scss"> +// 提示词 +.prompt { +} + +// 热词 +.hot-words { + display: flex; + flex-direction: column; + margin-top: 30px; + + .word-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: start; + margin-top: 15px; + + .btn { + margin: 0; + } + } +} + +// version +.version { + margin-top: 20px; + + .version-list { + margin-top: 20px; + width: 100%; + } +} + +// 模型 +.model { + margin-top: 30px; + + .model-list { + margin-top: 15px; + + .modal-item { + display: flex; + flex-direction: column; + align-items: center; + width: 150px; + //outline: 1px solid blue; + overflow: hidden; + border: 3px solid transparent; + cursor: pointer; + + .model-font { + font-size: 14px; + color: #3e3e3e; + font-weight: bold; + } + } + + .selectModel { + border: 3px solid #1293ff; + border-radius: 5px; + } + } +} + +// 尺寸 +.image-size { + width: 100%; + margin-top: 30px; + + .size-list { + display: flex; + flex-direction: row; + justify-content: space-between; + width: 100%; + margin-top: 20px; + + .size-item { + display: flex; + flex-direction: column; + align-items: center; + cursor: pointer; + + .size-wrapper { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + border-radius: 7px; + padding: 4px; + width: 50px; + height: 50px; + background-color: #fff; + border: 1px solid #fff; + } + + .size-font { + font-size: 14px; + color: #3e3e3e; + font-weight: bold; + } + } + } + + .selectImageSize { + border: 1px solid #1293ff !important; + } +} + +.btns { + display: flex; + justify-content: center; + margin-top: 50px; +} +</style> diff --git a/src/views/ai/image/index/components/stableDiffusion/index.vue b/src/views/ai/image/index/components/stableDiffusion/index.vue new file mode 100644 index 0000000..cf5e5bd --- /dev/null +++ b/src/views/ai/image/index/components/stableDiffusion/index.vue @@ -0,0 +1,296 @@ +<!-- dall3 --> +<template> + <div class="prompt"> + <el-text tag="b">画面描述</el-text> + <el-text tag="p">建议使用“形容词 + 动词 + 风格”的格式,使用“,”隔开</el-text> + <el-input + v-model="prompt" + maxlength="1024" + :rows="5" + class="w-100% mt-15px" + input-style="border-radius: 7px;" + placeholder="例如:童话里的小屋应该是什么样子?" + show-word-limit + type="textarea" + /> + </div> + <div class="hot-words"> + <div> + <el-text tag="b">随机热词</el-text> + </div> + <el-space wrap class="word-list"> + <el-button + round + class="btn" + :type="selectHotWord === hotWord ? 'primary' : 'default'" + v-for="hotWord in ImageHotEnglishWords" + :key="hotWord" + @click="handleHotWordClick(hotWord)" + > + {{ hotWord }} + </el-button> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">采样方法</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-select v-model="sampler" placeholder="Select" size="large" class="!w-350px"> + <el-option + v-for="item in StableDiffusionSamplers" + :key="item.key" + :label="item.name" + :value="item.key" + /> + </el-select> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">CLIP</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-select v-model="clipGuidancePreset" placeholder="Select" size="large" class="!w-350px"> + <el-option + v-for="item in StableDiffusionClipGuidancePresets" + :key="item.key" + :label="item.name" + :value="item.key" + /> + </el-select> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">风格</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-select v-model="stylePreset" placeholder="Select" size="large" class="!w-350px"> + <el-option + v-for="item in StableDiffusionStylePresets" + :key="item.key" + :label="item.name" + :value="item.key" + /> + </el-select> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">图片尺寸</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-input v-model="width" class="w-170px" placeholder="图片宽度" /> + <el-input v-model="height" class="w-170px" placeholder="图片高度" /> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">迭代步数</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-input + v-model="steps" + type="number" + size="large" + class="!w-350px" + placeholder="Please input" + /> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">引导系数</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-input + v-model="scale" + type="number" + size="large" + class="!w-350px" + placeholder="Please input" + /> + </el-space> + </div> + <div class="group-item"> + <div> + <el-text tag="b">随机因子</el-text> + </div> + <el-space wrap class="group-item-body"> + <el-input + v-model="seed" + type="number" + size="large" + class="!w-350px" + placeholder="Please input" + /> + </el-space> + </div> + <div class="btns"> + <el-button + type="primary" + size="large" + round + :loading="drawIn" + :disabled="prompt.length === 0" + @click="handleGenerateImage" + > + {{ drawIn ? '生成中' : '生成内容' }} + </el-button> + </div> +</template> +<script setup lang="ts"> +import { ImageApi, ImageDrawReqVO, ImageVO } from '@/api/ai/image' +import { hasChinese } from '@/views/ai/utils/utils' +import { + AiPlatformEnum, + ImageHotEnglishWords, + StableDiffusionClipGuidancePresets, + StableDiffusionSamplers, + StableDiffusionStylePresets +} from '@/views/ai/utils/constants' +import { ModelVO } from '@/api/ai/model/model' + +const message = useMessage() // 消息弹窗 + +// 接收父组件传入的模型列表 +const props = defineProps({ + models: { + type: Array<ModelVO>, + default: () => [] as ModelVO[] + } +}) +const emits = defineEmits(['onDrawStart', 'onDrawComplete']) // 定义 emits + +// 定义属性 +const drawIn = ref<boolean>(false) // 生成中 +const selectHotWord = ref<string>('') // 选中的热词 +// 表单 +const prompt = ref<string>('') // 提示词 +const width = ref<number>(512) // 图片宽度 +const height = ref<number>(512) // 图片高度 +const sampler = ref<string>('DDIM') // 采样方法 +const steps = ref<number>(20) // 迭代步数 +const seed = ref<number>(42) // 控制生成图像的随机性 +const scale = ref<number>(7.5) // 引导系数 +const clipGuidancePreset = ref<string>('NONE') // 文本提示相匹配的图像(clip_guidance_preset) 简称 CLIP +const stylePreset = ref<string>('3d-model') // 风格 + +/** 选择热词 */ +const handleHotWordClick = async (hotWord: string) => { + // 情况一:取消选中 + if (selectHotWord.value == hotWord) { + selectHotWord.value = '' + return + } + + // 情况二:选中 + selectHotWord.value = hotWord // 选中 + prompt.value = hotWord // 替换提示词 +} + +/** 图片生成 */ +const handleGenerateImage = async () => { + // 从 models 中查找匹配的模型 + const selectModel = 'stable-diffusion-v1-6' + const matchedModel = props.models.find( + (item) => item.model === selectModel && item.platform === AiPlatformEnum.STABLE_DIFFUSION + ) + if (!matchedModel) { + message.error('该模型不可用,请选择其它模型') + return + } + + // 二次确认 + if (hasChinese(prompt.value)) { + message.alert('暂不支持中文!') + return + } + await message.confirm(`确认生成内容?`) + + try { + // 加载中 + drawIn.value = true + // 回调 + emits('onDrawStart', AiPlatformEnum.STABLE_DIFFUSION) + // 发送请求 + const form = { + modelId: matchedModel.id, + prompt: prompt.value, // 提示词 + width: width.value, // 图片宽度 + height: height.value, // 图片高度 + options: { + seed: seed.value, // 随机种子 + steps: steps.value, // 图片生成步数 + scale: scale.value, // 引导系数 + sampler: sampler.value, // 采样算法 + clipGuidancePreset: clipGuidancePreset.value, // 文本提示相匹配的图像 CLIP + stylePreset: stylePreset.value // 风格 + } + } as ImageDrawReqVO + await ImageApi.drawImage(form) + } finally { + // 回调 + emits('onDrawComplete', AiPlatformEnum.STABLE_DIFFUSION) + // 加载结束 + drawIn.value = false + } +} + +/** 填充值 */ +const settingValues = async (detail: ImageVO) => { + prompt.value = detail.prompt + width.value = detail.width + height.value = detail.height + seed.value = detail.options?.seed + steps.value = detail.options?.steps + scale.value = detail.options?.scale + sampler.value = detail.options?.sampler + clipGuidancePreset.value = detail.options?.clipGuidancePreset + stylePreset.value = detail.options?.stylePreset +} + +/** 暴露组件方法 */ +defineExpose({ settingValues }) +</script> +<style scoped lang="scss"> +// 提示词 +.prompt { +} + +// 热词 +.hot-words { + display: flex; + flex-direction: column; + margin-top: 30px; + + .word-list { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: start; + margin-top: 15px; + + .btn { + margin: 0; + } + } +} + +// 模型 +.group-item { + margin-top: 30px; + + .group-item-body { + margin-top: 15px; + width: 100%; + } +} + +.btns { + display: flex; + justify-content: center; + margin-top: 50px; +} +</style> diff --git a/src/views/ai/image/index/index.vue b/src/views/ai/image/index/index.vue new file mode 100644 index 0000000..2181fab --- /dev/null +++ b/src/views/ai/image/index/index.vue @@ -0,0 +1,155 @@ +<!-- image --> +<template> + <div class="ai-image"> + <div class="left"> + <div class="segmented"> + <el-segmented v-model="selectPlatform" :options="platformOptions" /> + </div> + <div class="modal-switch-container"> + <Common + v-if="selectPlatform === 'common'" + ref="commonRef" + :models="models" + @on-draw-complete="handleDrawComplete" + /> + <Dall3 + v-if="selectPlatform === AiPlatformEnum.OPENAI" + ref="dall3Ref" + :models="models" + @on-draw-start="handleDrawStart" + @on-draw-complete="handleDrawComplete" + /> + <Midjourney + v-if="selectPlatform === AiPlatformEnum.MIDJOURNEY" + ref="midjourneyRef" + :models="models" + /> + <StableDiffusion + v-if="selectPlatform === AiPlatformEnum.STABLE_DIFFUSION" + ref="stableDiffusionRef" + :models="models" + @on-draw-complete="handleDrawComplete" + /> + </div> + </div> + <div class="main"> + <ImageList ref="imageListRef" @on-regeneration="handleRegeneration" /> + </div> + </div> +</template> + +<script setup lang="ts"> +import ImageList from './components/ImageList.vue' +import { AiPlatformEnum } from '@/views/ai/utils/constants' +import { ImageVO } from '@/api/ai/image' +import Dall3 from './components/dall3/index.vue' +import Midjourney from './components/midjourney/index.vue' +import StableDiffusion from './components/stableDiffusion/index.vue' +import Common from './components/common/index.vue' +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { AiModelTypeEnum } from '@/views/ai/utils/constants' + +const imageListRef = ref<any>() // image 列表 ref +const dall3Ref = ref<any>() // dall3(openai) ref +const midjourneyRef = ref<any>() // midjourney ref +const stableDiffusionRef = ref<any>() // stable diffusion ref +const commonRef = ref<any>() // stable diffusion ref + +// 定义属性 +const selectPlatform = ref('common') // 选中的平台 +const platformOptions = [ + { + label: '通用', + value: 'common' + }, + { + label: 'DALL3 绘画', + value: AiPlatformEnum.OPENAI + }, + { + label: 'MJ 绘画', + value: AiPlatformEnum.MIDJOURNEY + }, + { + label: 'SD 绘图', + value: AiPlatformEnum.STABLE_DIFFUSION + } +] + +const models = ref<ModelVO[]>([]) // 模型列表 + +/** 绘画 start */ +const handleDrawStart = async (platform: string) => {} + +/** 绘画 complete */ +const handleDrawComplete = async (platform: string) => { + await imageListRef.value.getImageList() +} + +/** 重新生成:将画图详情填充到对应平台 */ +const handleRegeneration = async (image: ImageVO) => { + // 切换平台 + selectPlatform.value = image.platform + // 根据不同平台填充 image + await nextTick() + if (image.platform === AiPlatformEnum.MIDJOURNEY) { + midjourneyRef.value.settingValues(image) + } else if (image.platform === AiPlatformEnum.OPENAI) { + dall3Ref.value.settingValues(image) + } else if (image.platform === AiPlatformEnum.STABLE_DIFFUSION) { + stableDiffusionRef.value.settingValues(image) + } + // TODO @fan:貌似 other 重新设置不行? +} + +/** 组件挂载的时候 */ +onMounted(async () => { + // 获取模型列表 + models.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.IMAGE) +}) +</script> + +<style scoped lang="scss"> +.ai-image { + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + + display: flex; + flex-direction: row; + height: 100%; + width: 100%; + + .left { + display: flex; + flex-direction: column; + padding: 20px; + width: 390px; + + .segmented .el-segmented { + --el-border-radius-base: 16px; + --el-segmented-item-selected-color: #fff; + background-color: #ececec; + width: 350px; + } + + .modal-switch-container { + height: 100%; + overflow-y: auto; + margin-top: 30px; + } + } + + .main { + flex: 1; + background-color: #fff; + } + + .right { + width: 350px; + background-color: #f7f8fa; + } +} +</style> diff --git a/src/views/ai/image/manager/index.vue b/src/views/ai/image/manager/index.vue new file mode 100644 index 0000000..db19b63 --- /dev/null +++ b/src/views/ai/image/manager/index.vue @@ -0,0 +1,252 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户编号" prop="userId"> + <el-select + v-model="queryParams.userId" + clearable + placeholder="请输入用户编号" + class="!w-240px" + > + <el-option + v-for="item in userList" + :key="item.id" + :label="item.nickname" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="平台" prop="platform"> + <el-select v-model="queryParams.status" placeholder="请选择平台" clearable class="!w-240px"> + <el-option + v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="绘画状态" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择绘画状态" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.AI_IMAGE_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="是否发布" prop="publicStatus"> + <el-select + v-model="queryParams.publicStatus" + placeholder="请选择是否发布" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" width="180" fixed="left" /> + <el-table-column label="图片" align="center" prop="picUrl" width="110px" fixed="left"> + <template #default="{ row }"> + <el-image + class="h-80px w-80px" + lazy + :src="row.picUrl" + :preview-src-list="[row.picUrl]" + preview-teleported + fit="cover" + v-if="row.picUrl?.length > 0" + /> + </template> + </el-table-column> + <el-table-column label="用户" align="center" prop="userId" width="180"> + <template #default="scope"> + <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span> + </template> + </el-table-column> + <el-table-column label="平台" align="center" prop="platform" width="120"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" /> + </template> + </el-table-column> + <el-table-column label="模型" align="center" prop="model" width="180" /> + <el-table-column label="绘画状态" align="center" prop="status" width="100"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_IMAGE_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="是否发布" align="center" prop="publicStatus"> + <template #default="scope"> + <el-switch + v-model="scope.row.publicStatus" + :active-value="true" + :inactive-value="false" + @change="handleUpdatePublicStatusChange(scope.row)" + :disabled="scope.row.status !== AiImageStatusEnum.SUCCESS" + /> + </template> + </el-table-column> + <el-table-column label="提示词" align="center" prop="prompt" width="180" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="宽度" align="center" prop="width" /> + <el-table-column label="高度" align="center" prop="height" /> + <el-table-column label="错误信息" align="center" prop="errorMessage" /> + <el-table-column label="任务编号" align="center" prop="taskId" /> + <el-table-column label="操作" align="center" width="100" fixed="right"> + <template #default="scope"> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:image:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE, getStrDictOptions, getBoolDictOptions } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { ImageApi, ImageVO } from '@/api/ai/image' +import * as UserApi from '@/api/system/user' +import { AiImageStatusEnum } from '@/views/ai/utils/constants' + +/** AI 绘画 列表 */ +defineOptions({ name: 'AiImageManager' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ImageVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + userId: undefined, + platform: undefined, + status: undefined, + publicStatus: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const userList = ref<UserApi.UserVO[]>([]) // 用户列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ImageApi.getImagePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ImageApi.deleteImage(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 修改是否发布 */ +const handleUpdatePublicStatusChange = async (row: ImageVO) => { + try { + // 修改状态的二次确认 + const text = row.publicStatus ? '公开' : '私有' + await message.confirm('确认要"' + text + '"该图片吗?') + // 发起修改状态 + await ImageApi.updateImage({ + id: row.id, + publicStatus: row.publicStatus + }) + await getList() + } catch { + row.publicStatus = !row.publicStatus + } +} + +/** 初始化 **/ +onMounted(async () => { + getList() + // 获得用户列表 + userList.value = await UserApi.getSimpleUserList() +}) +</script> diff --git a/src/views/ai/image/square/index.vue b/src/views/ai/image/square/index.vue new file mode 100644 index 0000000..3da6cde --- /dev/null +++ b/src/views/ai/image/square/index.vue @@ -0,0 +1,104 @@ +<template> + <div class="square-container"> + <!-- TODO @fan:style 建议换成 unocss --> + <!-- TODO @fan:Search 可以换成 Icon 组件么? --> + <el-input + v-model="queryParams.prompt" + style="width: 100%; margin-bottom: 20px" + size="large" + placeholder="请输入要搜索的内容" + :suffix-icon="Search" + @keyup.enter="handleQuery" + /> + <div class="gallery"> + <!-- TODO @fan:这个图片的风格,要不和 ImageCard.vue 界面一致?(只有卡片,没有操作);因为看着更有相框的感觉~~~ --> + <div v-for="item in list" :key="item.id" class="gallery-item"> + <img :src="item.picUrl" class="img" /> + </div> + </div> + <!-- TODO @fan:缺少翻页 --> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </div> +</template> +<script setup lang="ts"> +import { ImageApi, ImageVO } from '@/api/ai/image' +import { Search } from '@element-plus/icons-vue' + +// TODO @fan:加个 loading 加载中的状态 +const loading = ref(true) // 列表的加载中 +const list = ref<ImageVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + publicStatus: true, + prompt: undefined +}) + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ImageApi.getImagePageMy(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 初始化 */ +onMounted(async () => { + await getList() +}) +</script> +<style scoped lang="scss"> +.square-container { + background-color: #fff; + padding: 20px; + + .gallery { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 10px; + //max-width: 1000px; + background-color: #fff; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + + .gallery-item { + position: relative; + overflow: hidden; + background: #f0f0f0; + cursor: pointer; + transition: transform 0.3s; + } + + .gallery-item img { + width: 100%; + height: auto; + display: block; + transition: transform 0.3s; + } + + .gallery-item:hover img { + transform: scale(1.1); + } + + .gallery-item:hover { + transform: scale(1.05); + } +} +</style> diff --git a/src/views/ai/knowledge/document/form/ProcessStep.vue b/src/views/ai/knowledge/document/form/ProcessStep.vue new file mode 100644 index 0000000..4b290ea --- /dev/null +++ b/src/views/ai/knowledge/document/form/ProcessStep.vue @@ -0,0 +1,146 @@ +<template> + <div> + <!-- 文件处理列表 --> + <div class="mt-15px grid grid-cols-1 gap-2"> + <div + v-for="(file, index) in modelValue.list" + :key="index" + class="flex items-center py-4px px-12px border-l-4 border-l-[#409eff] rounded-sm shadow-sm hover:bg-[#ecf5ff] transition-all duration-300" + > + <!-- 文件图标和名称 --> + <div class="flex items-center min-w-[200px] mr-10px"> + <Icon icon="ep:document" class="mr-8px text-[#409eff]" /> + <span class="text-[13px] text-[#303133] break-all">{{ file.name }}</span> + </div> + + <!-- 处理进度 --> + <div class="flex-1"> + <el-progress + :percentage="file.progress || 0" + :stroke-width="10" + :status="isProcessComplete(file) ? 'success' : ''" + /> + </div> + + <!-- 分段数量 --> + <div class="ml-10px text-[13px] text-[#606266]"> + 分段数量:{{ file.count ? file.count : '-' }} + </div> + </div> + </div> + + <!-- 底部完成按钮 --> + <div class="flex justify-end mt-20px"> + <el-button + :type="allProcessComplete ? 'success' : 'primary'" + :disabled="!allProcessComplete" + @click="handleComplete" + > + 完成 + </el-button> + </div> + </div> +</template> + +<script setup lang="ts"> +import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment' + +const props = defineProps({ + modelValue: { + type: Object, + required: true + } +}) + +const emit = defineEmits(['update:modelValue']) +const parent = inject('parent') as any +const pollingTimer = ref<number | null>(null) // 轮询定时器 ID,用于跟踪和清除轮询进程 + +/** 判断文件处理是否完成 */ +const isProcessComplete = (file) => { + return file.progress === 100 +} + +/** 判断所有文件是否都处理完成 */ +const allProcessComplete = computed(() => { + return props.modelValue.list.every((file) => isProcessComplete(file)) +}) + +/** 完成按钮点击事件处理 */ +const handleComplete = () => { + if (parent?.exposed?.handleBack) { + parent.exposed.handleBack() + } +} + +/** 获取文件处理进度 */ +const getProcessList = async () => { + try { + // 1. 调用 API 获取处理进度 + const documentIds = props.modelValue.list.filter((item) => item.id).map((item) => item.id) + if (documentIds.length === 0) { + return + } + const result = await KnowledgeSegmentApi.getKnowledgeSegmentProcessList(documentIds) + + // 2.1更新进度 + const updatedList = props.modelValue.list.map((file) => { + const processInfo = result.find((item) => item.documentId === file.id) + if (processInfo) { + // 计算进度百分比:已嵌入数量 / 总数量 * 100 + const progress = + processInfo.embeddingCount && processInfo.count + ? Math.floor((processInfo.embeddingCount / processInfo.count) * 100) + : 0 + return { + ...file, + progress: progress, + count: processInfo.count || 0 + } + } + return file + }) + + // 2.2 更新数据 + emit('update:modelValue', { + ...props.modelValue, + list: updatedList + }) + + // 3. 如果未完成,继续轮询 + if (!updatedList.every((file) => isProcessComplete(file))) { + pollingTimer.value = window.setTimeout(getProcessList, 3000) + } + } catch (error) { + // 出错后也继续轮询 + console.error('获取处理进度失败:', error) + pollingTimer.value = window.setTimeout(getProcessList, 5000) + } +} + +/** 组件挂载时开始轮询 */ +onMounted(() => { + // 1. 初始化进度为 0 + const initialList = props.modelValue.list.map((file) => ({ + ...file, + progress: 0 + })) + + emit('update:modelValue', { + ...props.modelValue, + list: initialList + }) + + // 2. 开始轮询获取进度 + getProcessList() +}) + +/** 组件卸载前清除轮询 */ +onBeforeUnmount(() => { + // 1. 清除定时器 + if (pollingTimer.value) { + clearTimeout(pollingTimer.value) + pollingTimer.value = null + } +}) +</script> diff --git a/src/views/ai/knowledge/document/form/SplitStep.vue b/src/views/ai/knowledge/document/form/SplitStep.vue new file mode 100644 index 0000000..5b28ce3 --- /dev/null +++ b/src/views/ai/knowledge/document/form/SplitStep.vue @@ -0,0 +1,238 @@ +<template> + <div> + <!-- 上部分段设置部分 --> + <div class="mb-20px"> + <div class="mb-20px flex justify-between items-center"> + <div class="text-16px font-bold flex items-center"> + 分段设置 + <el-tooltip + content="系统会自动将文档内容分割成多个段落,您可以根据需要调整分段方式和内容。" + placement="top" + > + <Icon icon="ep:warning" class="ml-5px text-gray-400" /> + </el-tooltip> + </div> + <div> + <el-button type="primary" plain size="small" @click="handleAutoSegment"> + 预览分段 + </el-button> + </div> + </div> + + <div class="segment-settings mb-20px"> + <el-form label-width="120px"> + <el-form-item label="最大 Token 数"> + <el-input-number v-model="modelData.segmentMaxTokens" :min="1" :max="2048" /> + </el-form-item> + </el-form> + </div> + </div> + + <!-- 下部文件预览部分 --> + <div class="mb-10px"> + <div class="text-16px font-bold mb-10px">分段预览</div> + + <!-- 文件选择器 --> + <div class="file-selector mb-10px"> + <el-dropdown v-if="modelData.list && modelData.list.length > 0" trigger="click"> + <div class="flex items-center cursor-pointer"> + <Icon icon="ep:document" class="text-danger mr-5px" /> + <span>{{ currentFile?.name || '请选择文件' }}</span> + <span v-if="currentFile?.segments" class="ml-5px text-gray-500 text-12px"> + ({{ currentFile.segments.length }}个分片) + </span> + <Icon icon="ep:arrow-down" class="ml-5px" /> + </div> + <template #dropdown> + <el-dropdown-menu> + <el-dropdown-item + v-for="(file, index) in modelData.list" + :key="index" + @click="selectFile(index)" + > + {{ file.name }} + <span v-if="file.segments" class="ml-5px text-gray-500 text-12px"> + ({{ file.segments.length }}个分片) + </span> + </el-dropdown-item> + </el-dropdown-menu> + </template> + </el-dropdown> + <div v-else class="text-gray-400">暂无上传文件</div> + </div> + + <!-- 文件内容预览 --> + <div class="file-preview bg-gray-50 p-15px rounded-md max-h-600px overflow-y-auto"> + <div v-if="splitLoading" class="flex justify-center items-center py-20px"> + <Icon icon="ep:loading" class="is-loading" /> + <span class="ml-10px">正在加载分段内容...</span> + </div> + <template + v-else-if="currentFile && currentFile.segments && currentFile.segments.length > 0" + > + <div v-for="(segment, index) in currentFile.segments" :key="index" class="mb-10px"> + <div class="text-gray-500 text-12px mb-5px"> + 分片-{{ index + 1 }} · {{ segment.contentLength || 0 }} 字符数 · + {{ segment.tokens || 0 }} Token + </div> + <div class="bg-white p-10px rounded-md">{{ segment.content }}</div> + </div> + </template> + <el-empty v-else description="暂无预览内容" /> + </div> + </div> + + <!-- 添加底部按钮 --> + <div class="mt-20px flex justify-between"> + <div> + <el-button v-if="!modelData.id" @click="handlePrevStep">上一步</el-button> + </div> + <div> + <el-button type="primary" :loading="submitLoading" @click="handleSave"> + 保存并处理 + </el-button> + </div> + </div> + </div> +</template> + +<script lang="ts" setup> +import { computed, getCurrentInstance, inject, onMounted, PropType, ref } from 'vue' +import { Icon } from '@/components/Icon' +import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment' +import { useMessage } from '@/hooks/web/useMessage' +import { KnowledgeDocumentApi } from '@/api/ai/knowledge/document' + +const props = defineProps({ + modelValue: { + type: Object as PropType<any>, + required: true + } +}) + +const emit = defineEmits(['update:modelValue']) +const message = useMessage() // 消息提示 +const parent = inject('parent', null) // 获取父组件实例 + +const modelData = computed({ + get: () => props.modelValue, + set: (val) => emit('update:modelValue', val) +}) // 表单数据 + +const splitLoading = ref(false) // 分段加载状态 +const currentFile = ref<any>(null) // 当前选中的文件 +const submitLoading = ref(false) // 提交按钮加载状态 + +/** 选择文件 */ +const selectFile = async (index: number) => { + currentFile.value = modelData.value.list[index] + await splitContent(currentFile.value) +} + +/** 获取文件分段内容 */ +const splitContent = async (file: any) => { + if (!file || !file.url) { + message.warning('文件 URL 不存在') + return + } + + splitLoading.value = true + try { + // 调用后端分段接口,获取文档的分段内容、字符数和 Token 数 + file.segments = await KnowledgeSegmentApi.splitContent( + file.url, + modelData.value.segmentMaxTokens + ) + } catch (error) { + console.error('获取分段内容失败:', file, error) + } finally { + splitLoading.value = false + } +} + +/** 处理预览分段 */ +const handleAutoSegment = async () => { + // 如果没有选中文件,默认选中第一个 + if (!currentFile.value && modelData.value.list && modelData.value.list.length > 0) { + currentFile.value = modelData.value.list[0] + } + // 如果没有选中文件,提示请先选择文件 + if (!currentFile.value) { + message.warning('请先选择文件') + return + } + + // 获取分段内容 + await splitContent(currentFile.value) +} + +/** 上一步按钮处理 */ +const handlePrevStep = () => { + const parentEl = parent || getCurrentInstance()?.parent + if (parentEl && typeof parentEl.exposed?.goToPrevStep === 'function') { + parentEl.exposed.goToPrevStep() + } +} + +/** 保存操作 */ +const handleSave = async () => { + // 保存前验证 + if (!currentFile?.value?.segments || currentFile.value.segments.length === 0) { + message.warning('请先预览分段内容') + return + } + + // 设置按钮加载状态 + submitLoading.value = true + try { + if (modelData.value.id) { + // 修改场景 + await KnowledgeDocumentApi.updateKnowledgeDocument({ + id: modelData.value.id, + segmentMaxTokens: modelData.value.segmentMaxTokens + }) + } else { + // 新增场景 + const data = await KnowledgeDocumentApi.createKnowledgeDocumentList({ + knowledgeId: modelData.value.knowledgeId, + segmentMaxTokens: modelData.value.segmentMaxTokens, + list: modelData.value.list.map((item: any) => ({ + name: item.name, + url: item.url + })) + }) + modelData.value.list.forEach((document: any, index: number) => { + document.id = data[index] + }) + } + + // 进入下一步 + const parentEl = parent || getCurrentInstance()?.parent + if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') { + parentEl.exposed.goToNextStep() + } + } catch (error: any) { + console.error('保存失败:', modelData.value, error) + } finally { + // 关闭按钮加载状态 + submitLoading.value = false + } +} + +/** 初始化 */ +onMounted(async () => { + // 确保 segmentMaxTokens 存在 + if (!modelData.value.segmentMaxTokens) { + modelData.value.segmentMaxTokens = 500 + } + // 如果没有选中文件,默认选中第一个 + if (!currentFile.value && modelData.value.list && modelData.value.list.length > 0) { + currentFile.value = modelData.value.list[0] + } + + // 如果有选中的文件,获取分段内容 + if (currentFile.value) { + await splitContent(currentFile.value) + } +}) +</script> diff --git a/src/views/ai/knowledge/document/form/UploadStep.vue b/src/views/ai/knowledge/document/form/UploadStep.vue new file mode 100644 index 0000000..5a4d700 --- /dev/null +++ b/src/views/ai/knowledge/document/form/UploadStep.vue @@ -0,0 +1,273 @@ +<template> + <el-form ref="formRef" :model="modelData" label-width="0" class="mt-20px"> + <el-form-item class="mb-20px"> + <div class="w-full"> + <div + class="w-full border-2 border-dashed border-[#dcdfe6] rounded-md p-20px text-center hover:border-[#409eff]" + > + <el-upload + ref="uploadRef" + class="upload-demo" + drag + :action="uploadUrl" + :auto-upload="true" + :on-success="handleUploadSuccess" + :on-error="handleUploadError" + :on-change="handleFileChange" + :on-remove="handleFileRemove" + :before-upload="beforeUpload" + :http-request="httpRequest" + :file-list="fileList" + :multiple="true" + :show-file-list="false" + :accept="acceptedFileTypes" + > + <div class="flex flex-col items-center justify-center py-20px"> + <Icon icon="ep:upload-filled" class="text-[48px] text-[#c0c4cc] mb-10px" /> + <div class="el-upload__text text-[16px] text-[#606266]"> + 拖拽文件至此,或者 + <em class="text-[#409eff] not-italic cursor-pointer">选择文件</em> + </div> + <div class="el-upload__tip mt-10px text-[#909399] text-[12px]"> + 已支持 {{ supportedFileTypes.join('、') }},每个文件不超过 {{ maxFileSize }} MB。 + </div> + </div> + </el-upload> + </div> + + <div + v-if="modelData.list && modelData.list.length > 0" + class="mt-15px grid grid-cols-1 gap-2" + > + <div + v-for="(file, index) in modelData.list" + :key="index" + class="flex justify-between items-center py-4px px-12px border-l-4 border-l-[#409eff] rounded-sm shadow-sm hover:bg-[#ecf5ff] transition-all duration-300" + > + <div class="flex items-center"> + <Icon icon="ep:document" class="mr-8px text-[#409eff]" /> + <span class="text-[13px] text-[#303133] break-all">{{ file.name }}</span> + </div> + <el-button type="danger" link @click="removeFile(index)" class="ml-2"> + <Icon icon="ep:delete" /> + </el-button> + </div> + </div> + </div> + </el-form-item> + + <!-- 添加下一步按钮 --> + <el-form-item> + <div class="flex justify-end w-full"> + <el-button type="primary" @click="handleNextStep" :disabled="!isAllUploaded"> + 下一步 + </el-button> + </div> + </el-form-item> + </el-form> +</template> + +<script lang="ts" setup> +import { PropType, ref, computed, inject, getCurrentInstance, onMounted } from 'vue' +import { useMessage } from '@/hooks/web/useMessage' +import { useUpload } from '@/components/UploadFile/src/useUpload' +import { generateAcceptedFileTypes } from '@/utils' +import { Icon } from '@/components/Icon' + +const props = defineProps({ + modelValue: { + type: Object as PropType<any>, + required: true + } +}) + +const emit = defineEmits(['update:modelValue']) + +const formRef = ref() // 表单引用 +const uploadRef = ref() // 上传组件引用 +const parent = inject('parent', null) // 获取父组件实例 +const { uploadUrl, httpRequest } = useUpload() // 使用上传组件的钩子 +const message = useMessage() // 消息弹窗 +const fileList = ref([]) // 文件列表 +const uploadingCount = ref(0) // 上传中的文件数量 + +// 支持的文件类型和大小限制 +const supportedFileTypes = [ + 'TXT', + 'MARKDOWN', + 'MDX', + 'PDF', + 'HTML', + 'XLSX', + 'XLS', + 'DOC', + 'DOCX', + 'CSV', + 'EML', + 'MSG', + 'PPTX', + 'XML', + 'EPUB', + 'PPT', + 'MD', + 'HTM' +] +const allowedExtensions = supportedFileTypes.map((ext) => ext.toLowerCase()) // 小写的扩展名列表 +const maxFileSize = 15 // 最大文件大小(MB) + +// 构建 accept 属性值,用于限制文件选择对话框中可见的文件类型 +const acceptedFileTypes = computed(() => generateAcceptedFileTypes(supportedFileTypes)) + +/** 表单数据 */ +const modelData = computed({ + get: () => { + return props.modelValue + }, + set: (val) => emit('update:modelValue', val) +}) + +/** 确保 list 属性存在 */ +const ensureListExists = () => { + if (!props.modelValue.list) { + emit('update:modelValue', { + ...props.modelValue, + list: [] + }) + } +} + +/** 是否所有文件都已上传完成 */ +const isAllUploaded = computed(() => { + return modelData.value.list && modelData.value.list.length > 0 && uploadingCount.value === 0 +}) + +/** + * 上传前检查文件类型和大小 + * + * @param file 待上传的文件 + * @returns 是否允许上传 + */ +const beforeUpload = (file) => { + // 1.1 检查文件扩展名 + const fileName = file.name.toLowerCase() + const fileExtension = fileName.substring(fileName.lastIndexOf('.') + 1) + if (!allowedExtensions.includes(fileExtension)) { + message.error('不支持的文件类型!') + return false + } + // 1.2 检查文件大小 + if (!(file.size / 1024 / 1024 < maxFileSize)) { + message.error(`文件大小不能超过 ${maxFileSize} MB!`) + return false + } + + // 2. 增加上传中的文件计数 + uploadingCount.value++ + return true +} + +/** + * 文件上传成功处理 + * + * @param response 上传响应 + * @param file 上传的文件 + */ +const handleUploadSuccess = (response, file) => { + // 添加到文件列表 + if (response && response.data) { + ensureListExists() + emit('update:modelValue', { + ...props.modelValue, + list: [ + ...props.modelValue.list, + { + name: file.name, + url: response.data + } + ] + }) + } else { + message.error(`文件 ${file.name} 上传失败`) + } + + // 减少上传中的文件计数 + uploadingCount.value = Math.max(0, uploadingCount.value - 1) +} + +/** + * 文件上传失败处理 + * + * @param error 错误信息 + * @param file 上传的文件 + */ +const handleUploadError = (error, file) => { + message.error(`文件 ${file.name} 上传失败: ${error}`) + // 减少上传中的文件计数 + uploadingCount.value = Math.max(0, uploadingCount.value - 1) +} + +/** + * 文件变更处理 + * + * @param file 变更的文件 + */ +const handleFileChange = (file) => { + if (file.status === 'success' || file.status === 'fail') { + uploadingCount.value = Math.max(0, uploadingCount.value - 1) + } +} + +/** + * 文件移除处理 + * + * @param file 被移除的文件 + */ +const handleFileRemove = (file) => { + if (file.status === 'uploading') { + uploadingCount.value = Math.max(0, uploadingCount.value - 1) + } +} + +/** + * 从列表中移除文件 + * + * @param index 要移除的文件索引 + */ +const removeFile = (index: number) => { + // 从列表中移除文件 + const newList = [...props.modelValue.list] + newList.splice(index, 1) + // 更新表单数据 + emit('update:modelValue', { + ...props.modelValue, + list: newList + }) +} + +/** 下一步按钮处理 */ +const handleNextStep = () => { + // 1.1 检查是否有文件上传 + if (!modelData.value.list || modelData.value.list.length === 0) { + message.warning('请上传至少一个文件') + return + } + // 1.2 检查是否有文件正在上传 + if (uploadingCount.value > 0) { + message.warning('请等待所有文件上传完成') + return + } + + // 2. 获取父组件的goToNextStep方法 + const parentEl = parent || getCurrentInstance()?.parent + if (parentEl && typeof parentEl.exposed?.goToNextStep === 'function') { + parentEl.exposed.goToNextStep() + } +} + +/** 初始化 */ +onMounted(() => { + ensureListExists() +}) +</script> + +<style lang="scss" scoped></style> diff --git a/src/views/ai/knowledge/document/form/index.vue b/src/views/ai/knowledge/document/form/index.vue new file mode 100644 index 0000000..722f57c --- /dev/null +++ b/src/views/ai/knowledge/document/form/index.vue @@ -0,0 +1,193 @@ +<template> + <ContentWrap> + <div class="mx-auto"> + <!-- 头部导航栏 --> + <div + class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px" + > + <!-- 左侧标题 --> + <div class="w-200px flex items-center overflow-hidden"> + <Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" /> + <span class="ml-10px text-16px truncate"> + {{ formData.id ? '编辑知识库文档' : '创建知识库文档' }} + </span> + </div> + + <!-- 步骤条 --> + <div class="flex-1 flex items-center justify-center h-full"> + <div class="w-400px flex items-center justify-between h-full"> + <div + v-for="(step, index) in steps" + :key="index" + class="flex items-center mx-15px relative h-full" + :class="[ + currentStep === index + ? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid' + : 'text-gray-500' + ]" + > + <div + class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px" + :class="[ + currentStep === index + ? 'bg-[#3473ff] text-white border-[#3473ff]' + : 'border-gray-300 bg-white text-gray-500' + ]" + > + {{ index + 1 }} + </div> + <span class="text-16px font-bold whitespace-nowrap">{{ step.title }}</span> + </div> + </div> + </div> + + <!-- 右侧按钮 - 已移除 --> + <div class="w-200px flex items-center justify-end gap-2"> </div> + </div> + + <!-- 主体内容 --> + <div class="mt-50px"> + <!-- 第一步:上传文档 --> + <div v-if="currentStep === 0" class="mx-auto w-560px"> + <UploadStep v-model="formData" ref="uploadDocumentRef" /> + </div> + + <!-- 第二步:文档分段 --> + <div v-if="currentStep === 1" class="mx-auto w-560px"> + <SplitStep v-model="formData" ref="documentSegmentRef" /> + </div> + + <!-- 第三步:处理并完成 --> + <div v-if="currentStep === 2" class="mx-auto w-560px"> + <ProcessStep v-model="formData" ref="processCompleteRef" /> + </div> + </div> + </div> + </ContentWrap> +</template> + +<script lang="ts" setup> +import { useRoute, useRouter } from 'vue-router' +import { useTagsViewStore } from '@/store/modules/tagsView' +import UploadStep from './UploadStep.vue' +import SplitStep from './SplitStep.vue' +import ProcessStep from './ProcessStep.vue' +import { KnowledgeDocumentApi } from '@/api/ai/knowledge/document' + +const { delView } = useTagsViewStore() // 视图操作 +const route = useRoute() // 路由 +const router = useRouter() // 路由 + +// 组件引用 +const uploadDocumentRef = ref() +const documentSegmentRef = ref() +const processCompleteRef = ref() +const currentStep = ref(0) // 步骤控制 +const steps = [{ title: '上传文档' }, { title: '文档分段' }, { title: '处理并完成' }] +const formData = ref({ + knowledgeId: undefined, // 知识库编号 + id: undefined, // 编辑的文档编号(documentId) + segmentMaxTokens: 500, // 分段最大 token 数 + list: [] as Array<{ + id: number // 文档编号 + name: string // 文档名称 + url: string // 文档 URL + segments: Array<{ + content?: string + contentLength?: number + tokens?: number + }> + count?: number // 段落数量 + process?: number // 处理进度 + }> // 用于存储上传的文件列表 +}) // 表单数据 + +provide('parent', getCurrentInstance()) // 提供 parent 给子组件使用 + +/** 初始化数据 */ +const initData = async () => { + // 【新增场景】从路由参数中获取知识库 ID + if (route.query.knowledgeId) { + formData.value.knowledgeId = route.query.knowledgeId as any + } + + // 【修改场景】从路由参数中获取文档 ID + const documentId = route.query.id + if (documentId) { + // 获取文档信息 + formData.value.id = documentId as any + const document = await KnowledgeDocumentApi.getKnowledgeDocument(documentId as any) + formData.value.segmentMaxTokens = document.segmentMaxTokens + formData.value.list = [ + { + id: document.id, + name: document.name, + url: document.url, + segments: [] + } + ] + // 进入下一步 + goToNextStep() + } +} + +/** 切换到下一步 */ +const goToNextStep = () => { + if (currentStep.value < steps.length - 1) { + currentStep.value++ + } +} + +/** 切换到上一步 */ +const goToPrevStep = () => { + if (currentStep.value > 0) { + currentStep.value-- + } +} + +/** 返回列表页 */ +const handleBack = () => { + // 先删除当前页签 + delView(unref(router.currentRoute)) + // 跳转到列表页 + router.push({ name: 'AiKnowledgeDocument', query: { knowledgeId: formData.value.knowledgeId } }) +} + +/** 初始化 */ +onMounted(async () => { + await initData() +}) + +/** 添加组件卸载前的清理代码 */ +onBeforeUnmount(() => { + // 清理所有的引用 + uploadDocumentRef.value = null + documentSegmentRef.value = null + processCompleteRef.value = null +}) + +/** 暴露方法给子组件使用 */ +defineExpose({ + goToNextStep, + goToPrevStep, + handleBack +}) +</script> + +<style lang="scss" scoped> +.border-bottom { + border-bottom: 1px solid #dcdfe6; +} + +.text-primary { + color: #3473ff; +} + +.bg-primary { + background-color: #3473ff; +} + +.border-primary { + border-color: #3473ff; +} +</style> diff --git a/src/views/ai/knowledge/document/index.vue b/src/views/ai/knowledge/document/index.vue new file mode 100644 index 0000000..e8ba711 --- /dev/null +++ b/src/views/ai/knowledge/document/index.vue @@ -0,0 +1,236 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="文件名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入文件名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="是否启用" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择是否启用" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button type="primary" plain @click="handleCreate" v-hasPermi="['ai:knowledge:create']"> + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="文档编号" align="center" prop="id" /> + <el-table-column label="文件名称" align="center" prop="name" /> + <el-table-column label="字符数" align="center" prop="contentLength" /> + <el-table-column label="Token 数" align="center" prop="tokens" /> + <el-table-column label="分片最大 Token 数" align="center" prop="segmentMaxTokens" /> + <el-table-column label="召回次数" align="center" prop="retrievalCount" /> + <el-table-column label="是否启用" align="center" prop="status"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + :active-value="0" + :inactive-value="1" + @change="handleStatusChange(scope.row)" + :disabled="!checkPermi(['ai:knowledge:update'])" + /> + </template> + </el-table-column> + <el-table-column + label="上传时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center" min-width="120px"> + <template #default="scope"> + <el-button + link + type="primary" + @click="handleUpdate(scope.row.id)" + v-hasPermi="['ai:knowledge:update']" + > + 编辑 + </el-button> + <el-button + link + type="primary" + @click="handleSegment(scope.row.id)" + v-hasPermi="['ai:knowledge:query']" + > + 分段 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:knowledge:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <!-- <KnowledgeDocumentForm ref="formRef" @success="getList" /> --> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { KnowledgeDocumentApi, KnowledgeDocumentVO } from '@/api/ai/knowledge/document' +import { useRoute, useRouter } from 'vue-router' +import { checkPermi } from '@/utils/permission' +import { CommonStatusEnum } from '@/utils/constants' +// import KnowledgeDocumentForm from './KnowledgeDocumentForm.vue' + +/** AI 知识库文档 列表 */ +defineOptions({ name: 'KnowledgeDocument' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 +const route = useRoute() // 路由 +const router = useRouter() // 路由 + +const loading = ref(true) // 列表的加载中 +const list = ref<KnowledgeDocumentVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + status: undefined, + knowledgeId: undefined +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await KnowledgeDocumentApi.getKnowledgeDocumentPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 跳转到创建文档页面 */ +const handleCreate = () => { + router.push({ + name: 'AiKnowledgeDocumentCreate', + query: { knowledgeId: queryParams.knowledgeId } + }) +} + +/** 跳转到更新文档页面 */ +const handleUpdate = (id: number) => { + router.push({ + name: 'AiKnowledgeDocumentUpdate', + query: { id, knowledgeId: queryParams.knowledgeId } + }) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await KnowledgeDocumentApi.deleteKnowledgeDocument(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 修改状态操作 */ +const handleStatusChange = async (row: KnowledgeDocumentVO) => { + try { + // 修改状态的二次确认 + const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '禁用' + await message.confirm('确认要"' + text + '""' + row.name + '"文档吗?') + // 发起修改状态 + await KnowledgeDocumentApi.updateKnowledgeDocumentStatus({ id: row.id, status: row.status }) + message.success(t('common.updateSuccess')) + // 刷新列表 + await getList() + } catch { + // 取消后,进行恢复按钮 + row.status = + row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE + } +} + +/** 跳转到知识库分段页面 */ +const handleSegment = (id: number) => { + router.push({ + name: 'AiKnowledgeSegment', + query: { documentId: id } + }) +} + +/** 初始化 **/ +onMounted(() => { + // 如果知识库 ID 不存在,显示错误提示并关闭页面 + if (!route.query.knowledgeId) { + message.error('知识库 ID 不存在,无法查看文档列表') + // 关闭当前路由,返回到知识库列表页面 + router.push({ name: 'AiKnowledge' }) + return + } + + // 从路由参数中获取知识库 ID + queryParams.knowledgeId = route.query.knowledgeId as any + getList() +}) +</script> diff --git a/src/views/ai/knowledge/knowledge/KnowledgeForm.vue b/src/views/ai/knowledge/knowledge/KnowledgeForm.vue new file mode 100644 index 0000000..d72f37c --- /dev/null +++ b/src/views/ai/knowledge/knowledge/KnowledgeForm.vue @@ -0,0 +1,162 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="130px" + v-loading="formLoading" + > + <el-form-item label="知识库名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入知识库名称" /> + </el-form-item> + <el-form-item label="知识库描述" prop="description"> + <el-input + v-model="formData.description" + type="textarea" + :rows="3" + placeholder="请输入知识库描述" + /> + </el-form-item> + <el-form-item label="向量模型" prop="embeddingModelId"> + <el-select + v-model="formData.embeddingModelId" + placeholder="请选择向量模型" + clearable + class="!w-full" + > + <el-option v-for="item in modelList" :key="item.id" :label="item.name" :value="item.id" /> + </el-select> + </el-form-item> + <el-form-item label="检索 topK" prop="topK"> + <el-input-number + v-model="formData.topK" + placeholder="请输入检索 topK" + :min="0" + :max="10" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="检索相似度阈值" prop="similarityThreshold"> + <el-input-number + v-model="formData.similarityThreshold" + placeholder="请输入检索相似度阈值" + :min="0" + :max="1" + :step="0.01" + :precision="2" + class="!w-1/1" + /> + </el-form-item> + <el-form-item label="是否启用" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { KnowledgeApi, KnowledgeVO } from '@/api/ai/knowledge/knowledge' +import { CommonStatusEnum } from '@/utils/constants' +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { AiModelTypeEnum } from '../../utils/constants' + +/** AI 知识库表单 */ +defineOptions({ name: 'KnowledgeForm' }) + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + description: undefined, + embeddingModelId: undefined, + topK: undefined, + similarityThreshold: undefined, + status: CommonStatusEnum.ENABLE // 默认开启 +}) +const formRules = reactive({ + name: [{ required: true, message: '请输入知识库名称', trigger: 'blur' }], + embeddingModelId: [{ required: true, message: '请输入向量模型', trigger: 'blur' }], + topK: [{ required: true, message: '请输入检索 topK', trigger: 'blur' }], + similarityThreshold: [{ required: true, message: '请输入检索相似度阈值', trigger: 'blur' }], + status: [{ required: true, message: '请选择是否启用', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const modelList = ref<ModelVO[]>([]) // 向量模型选项 + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 获取向量模型列表 + modelList.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.EMBEDDING) + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await KnowledgeApi.getKnowledge(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as KnowledgeVO + if (formType.value === 'create') { + await KnowledgeApi.createKnowledge(data) + message.success(t('common.createSuccess')) + } else { + await KnowledgeApi.updateKnowledge(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + description: undefined, + embeddingModelId: undefined, + topK: undefined, + similarityThreshold: undefined, + status: CommonStatusEnum.ENABLE // 默认开启 + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/knowledge/knowledge/index.vue b/src/views/ai/knowledge/knowledge/index.vue new file mode 100644 index 0000000..8e48549 --- /dev/null +++ b/src/views/ai/knowledge/knowledge/index.vue @@ -0,0 +1,220 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="95px" + > + <el-form-item label="知识库名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入知识库名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="是否启用" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择是否启用" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-220px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['ai:knowledge:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="知识库名称" align="center" prop="name" /> + <el-table-column label="知识库描述" align="center" prop="description" /> + <el-table-column label="向量化模型" align="center" prop="embeddingModel" /> + <el-table-column label="是否启用" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center" min-width="120px"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:knowledge:update']" + > + 编辑 + </el-button> + <el-button + link + type="primary" + @click="handleDocument(scope.row.id)" + v-hasPermi="['ai:knowledge:query']" + > + 文档 + </el-button> + <el-button + link + type="primary" + @click="handleRetrieval(scope.row.id)" + v-hasPermi="['ai:knowledge:query']" + > + 召回测试 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:knowledge:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <KnowledgeForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { KnowledgeApi, KnowledgeVO } from '@/api/ai/knowledge/knowledge' +import KnowledgeForm from './KnowledgeForm.vue' +import { useRouter } from 'vue-router' + +/** AI 知识库列表 */ +defineOptions({ name: 'Knowledge' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<KnowledgeVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + status: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await KnowledgeApi.getKnowledgePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await KnowledgeApi.deleteKnowledge(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 文档按钮操作 */ +const router = useRouter() +const handleDocument = (id: number) => { + router.push({ + name: 'AiKnowledgeDocument', + query: { knowledgeId: id } + }) +} + +/** 跳转到文档召回测试页面 */ +const handleRetrieval = (id: number) => { + router.push({ + name: 'AiKnowledgeRetrieval', + query: { id } + }) +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/ai/knowledge/knowledge/retrieval/index.vue b/src/views/ai/knowledge/knowledge/retrieval/index.vue new file mode 100644 index 0000000..aca521a --- /dev/null +++ b/src/views/ai/knowledge/knowledge/retrieval/index.vue @@ -0,0 +1,163 @@ +<template> + <div class="flex gap-20px w-full"> + <!-- 左侧输入区域 --> + <ContentWrap class="flex-1 min-w-300px"> + <div class="mb-15px"> + <h3 class="m-0 mb-5px">召回测试</h3> + <div class="text-gray-500 text-14px">根据给定的查询文本测试召回效果。</div> + </div> + <div> + <div class="relative mb-10px"> + <el-input + v-model="queryParams.content" + type="textarea" + :rows="8" + placeholder="请输入文本" + /> + <div class="absolute bottom-10px right-10px text-gray-400 text-12px"> + {{ queryParams.content?.length }} / 200 + </div> + </div> + <div class="flex items-center mb-10px"> + <span class="w-60px text-gray-500">topK:</span> + <el-input-number v-model="queryParams.topK" :min="1" :max="20" /> + </div> + <div class="flex items-center mb-15px"> + <span class="w-60px text-gray-500">相似度:</span> + <el-input-number + v-model="queryParams.similarityThreshold" + :min="0" + :max="1" + :precision="2" + :step="0.01" + /> + </div> + <div class="flex justify-end"> + <el-button type="primary" @click="getRetrievalResult" :loading="loading">测试</el-button> + </div> + </div> + </ContentWrap> + + <!-- 右侧召回结果区域 --> + <ContentWrap class="flex-1 min-w-300px"> + <el-empty v-if="loading" description="正在检索中..." /> + <div v-else-if="segments.length > 0" class="font-bold mb-15px"> + {{ segments.length }} 个召回段落 + </div> + <el-empty v-else description="暂无召回结果" /> + <div> + <div + v-for="(segment, index) in segments" + :key="index" + class="mb-20px border border-solid border-gray-200 rounded p-15px" + > + <div class="flex justify-between text-12px text-gray-500 mb-5px"> + <span> + 分段({{ segment.id }}) · {{ segment.contentLength }} 字符数 · + {{ segment.tokens }} Token + </span> + <span class="px-8px py-4px bg-blue-50 text-blue-500 rounded-full text-12px font-bold"> + score: {{ segment.score }} + </span> + </div> + <div + class="bg-gray-50 p-10px rounded mb-10px whitespace-pre-wrap overflow-hidden transition-all duration-100 text-13px" + :class="{ + 'line-clamp-2 max-h-50px': !segment.expanded, + 'max-h-500px': segment.expanded + }" + > + {{ segment.content }} + </div> + <div class="flex justify-between items-center"> + <div class="flex items-center text-gray-500 text-13px"> + <Icon icon="ep:document" class="mr-5px" /> + <span>{{ segment.documentName || '未知文档' }}</span> + </div> + <el-button size="small" @click="toggleExpand(segment)"> + {{ segment.expanded ? '收起' : '展开' }} + <Icon :icon="segment.expanded ? 'ep:arrow-up' : 'ep:arrow-down'" /> + </el-button> + </div> + </div> + </div> + </ContentWrap> + </div> +</template> + +<script setup lang="ts"> +import { useMessage } from '@/hooks/web/useMessage' +import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment' +import { KnowledgeApi } from '@/api/ai/knowledge/knowledge' +/** 文档召回测试 */ +defineOptions({ name: 'KnowledgeDocumentRetrieval' }) + +const message = useMessage() // 消息弹窗 +const route = useRoute() // 路由 +const router = useRouter() // 路由 + +const loading = ref(false) // 加载状态 +const segments = ref<any[]>([]) // 召回结果 +const queryParams = reactive({ + id: undefined, + content: '', + topK: 10, + similarityThreshold: 0.5 +}) + +/** 调用文档召回测试接口 */ +const getRetrievalResult = async () => { + if (!queryParams.content) { + message.warning('请输入查询文本') + return + } + + loading.value = true + segments.value = [] + + try { + const data = await KnowledgeSegmentApi.searchKnowledgeSegment({ + knowledgeId: queryParams.id, + content: queryParams.content, + topK: queryParams.topK, + similarityThreshold: queryParams.similarityThreshold + }) + segments.value = data || [] + } catch (error) { + console.error(error) + } finally { + loading.value = false + } +} + +/** 展开/收起段落内容 */ +const toggleExpand = (segment: any) => { + segment.expanded = !segment.expanded +} + +/** 获取知识库信息 */ +const getKnowledgeInfo = async (id: number) => { + try { + const knowledge = await KnowledgeApi.getKnowledge(id) + if (knowledge) { + queryParams.topK = knowledge.topK || queryParams.topK + queryParams.similarityThreshold = + knowledge.similarityThreshold || queryParams.similarityThreshold + } + } catch (error) {} +} + +/** 初始化 **/ +onMounted(() => { + // 如果知识库 ID 不存在,显示错误提示并关闭页面 + if (!route.query.id) { + message.error('知识库 ID 不存在,无法进行召回测试') + router.back() + return + } + queryParams.id = route.query.id as any + + // 获取知识库信息并设置默认值 + getKnowledgeInfo(queryParams.id as any) +}) +</script> diff --git a/src/views/ai/knowledge/segment/KnowledgeSegmentForm.vue b/src/views/ai/knowledge/segment/KnowledgeSegmentForm.vue new file mode 100644 index 0000000..4818de0 --- /dev/null +++ b/src/views/ai/knowledge/segment/KnowledgeSegmentForm.vue @@ -0,0 +1,101 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="切片内容" prop="content"> + <el-input + v-model="formData.content" + type="textarea" + :rows="6" + placeholder="请输入切片内容" + /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { KnowledgeSegmentApi, KnowledgeSegmentVO } from '@/api/ai/knowledge/segment' + +/** AI 知识库分段表单 */ +defineOptions({ name: 'KnowledgeSegmentForm' }) + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + documentId: undefined, + content: undefined +}) +const formRules = reactive({ + content: [{ required: true, message: '切片内容不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number, documentId?: any) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + formData.value.documentId = documentId as any + + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await KnowledgeSegmentApi.getKnowledgeSegment(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as KnowledgeSegmentVO + if (formType.value === 'create') { + await KnowledgeSegmentApi.createKnowledgeSegment(data) + message.success(t('common.createSuccess')) + } else { + await KnowledgeSegmentApi.updateKnowledgeSegment(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + documentId: undefined, + content: undefined + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/knowledge/segment/index.vue b/src/views/ai/knowledge/segment/index.vue new file mode 100644 index 0000000..e2f8a67 --- /dev/null +++ b/src/views/ai/knowledge/segment/index.vue @@ -0,0 +1,242 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="文档编号" prop="documentId"> + <el-input + v-model="queryParams.documentId" + placeholder="请输入文档编号" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="是否启用" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择是否启用" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['ai:knowledge:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="分段编号" align="center" prop="id" /> + <el-table-column type="expand"> + <template #default="props"> + <div + class="content-expand" + style=" + padding: 10px 20px; + white-space: pre-wrap; + line-height: 1.5; + background-color: #f9f9f9; + border-radius: 4px; + border-left: 3px solid #409eff; + " + > + <div + class="content-title" + style="margin-bottom: 8px; color: #606266; font-size: 14px; font-weight: bold" + > + 完整内容: + </div> + {{ props.row.content }} + </div> + </template> + </el-table-column> + <el-table-column + label="切片内容" + align="center" + prop="content" + min-width="250px" + :show-overflow-tooltip="true" + /> + <el-table-column label="字符数" align="center" prop="contentLength" /> + <el-table-column label="token 数量" align="center" prop="tokens" /> + <el-table-column label="召回次数" align="center" prop="retrievalCount" /> + <el-table-column label="是否启用" align="center" prop="status"> + <template #default="scope"> + <el-switch + v-model="scope.row.status" + :active-value="0" + :inactive-value="1" + @change="handleStatusChange(scope.row)" + :disabled="!checkPermi(['ai:knowledge:update'])" + /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center" min-width="120px"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:knowledge:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:knowledge:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <KnowledgeSegmentForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { KnowledgeSegmentApi, KnowledgeSegmentVO } from '@/api/ai/knowledge/segment' +import KnowledgeSegmentForm from './KnowledgeSegmentForm.vue' +import { CommonStatusEnum } from '@/utils/constants' +import { checkPermi } from '@/utils/permission' + +/** AI 知识库分段 列表 */ +defineOptions({ name: 'KnowledgeSegment' }) + +const message = useMessage() // 消息弹窗 +const router = useRouter() // 路由 +const route = useRoute() // 路由 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<KnowledgeSegmentVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + documentId: undefined, + content: undefined, + status: undefined +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await KnowledgeSegmentApi.getKnowledgeSegmentPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id, queryParams.documentId) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await KnowledgeSegmentApi.deleteKnowledgeSegment(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 修改状态操作 */ +const handleStatusChange = async (row: KnowledgeSegmentVO) => { + try { + // 修改状态的二次确认 + const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '禁用' + await message.confirm('确认要"' + text + '"该分段吗?') + // 发起修改状态 + await KnowledgeSegmentApi.updateKnowledgeSegmentStatus({ id: row.id, status: row.status }) + message.success(t('common.updateSuccess')) + // 刷新列表 + await getList() + } catch { + // 取消后,进行恢复按钮 + row.status = + row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE + } +} + +/** 初始化 **/ +onMounted(() => { + // 如果文档 ID 不存在,显示错误提示并关闭页面 + if (!route.query.documentId) { + message.error('文档 ID 不存在,无法查看分段列表') + // 关闭当前路由,返回到文档列表页面 + router.push({ name: 'AiKnowledgeDocument' }) + return + } + + // 从路由参数中获取文档 ID + queryParams.documentId = route.query.documentId as any + getList() +}) +</script> diff --git a/src/views/ai/mindmap/index/components/Left.vue b/src/views/ai/mindmap/index/components/Left.vue new file mode 100644 index 0000000..5c3dbf0 --- /dev/null +++ b/src/views/ai/mindmap/index/components/Left.vue @@ -0,0 +1,78 @@ +<template> + <div class="w-[350px] p-5 flex flex-col bg-[#f5f7f9]"> + <h3 class="w-full h-full h-7 text-5 text-center leading-[28px] title">思维导图创作中心</h3> + <!--下面表单部分--> + <div class="flex-grow overflow-y-auto"> + <div class="mt-[30ppx]"> + <el-text tag="b">您的需求?</el-text> + <el-input + v-model="formData.prompt" + maxlength="1024" + :rows="5" + class="w-100% mt-15px" + input-style="border-radius: 7px;" + placeholder="请输入提示词,让AI帮你完善" + show-word-limit + type="textarea" + /> + <el-button + class="!w-full mt-[15px]" + type="primary" + :loading="isGenerating" + @click="emits('submit', formData)" + > + 智能生成思维导图 + </el-button> + </div> + <div class="mt-[30px]"> + <el-text tag="b">使用已有内容生成?</el-text> + <el-input + v-model="generatedContent" + maxlength="1024" + :rows="5" + class="w-100% mt-15px" + input-style="border-radius: 7px;" + placeholder="例如:童话里的小屋应该是什么样子?" + show-word-limit + type="textarea" + /> + <el-button + class="!w-full mt-[15px]" + type="primary" + @click="emits('directGenerate', generatedContent)" + :disabled="isGenerating" + > + 直接生成 + </el-button> + </div> + </div> + </div> +</template> + +<script setup lang="ts"> +import { MindMapContentExample } from '@/views/ai/utils/constants' + +const emits = defineEmits(['submit', 'directGenerate']) +defineProps<{ + isGenerating: boolean +}>() +// 提交的提示词字段 +const formData = reactive({ + prompt: '' +}) + +const generatedContent = ref(MindMapContentExample) // 已有的内容 + +defineExpose({ + setGeneratedContent(newContent: string) { + // 设置已有的内容,在生成结束的时候将结果赋值给该值 + generatedContent.value = newContent + } +}) +</script> + +<style lang="scss" scoped> +.title { + color: var(--el-color-primary); +} +</style> diff --git a/src/views/ai/mindmap/index/components/Right.vue b/src/views/ai/mindmap/index/components/Right.vue new file mode 100644 index 0000000..b1d04de --- /dev/null +++ b/src/views/ai/mindmap/index/components/Right.vue @@ -0,0 +1,167 @@ +<template> + <el-card class="my-card h-full flex-grow"> + <template #header> + <h3 class="m-0 px-7 shrink-0 flex items-center justify-between"> + <span>思维导图预览</span> + <!-- 展示在右上角 --> + <el-button v-show="isEnd" size="small" type="primary" @click="downloadImage"> + <template #icon> + <Icon icon="ph:copy-bold" /> + </template> + 下载图片 + </el-button> + </h3> + </template> + + <div ref="contentRef" class="hide-scroll-bar h-full box-border"> + <!--展示 markdown 的容器,最终生成的是 html 字符串,直接用 v-html 嵌入--> + <div v-if="isGenerating" ref="mdContainerRef" class="wh-full overflow-y-auto"> + <div class="flex flex-col items-center justify-center" v-html="html"></div> + </div> + + <div ref="mindMapRef" class="wh-full"> + <svg ref="svgRef" :style="{ height: `${contentAreaHeight}px` }" class="w-full" /> + <div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div> + </div> + </div> + </el-card> +</template> + +<script lang="ts" setup> +import { Markmap } from 'markmap-view' +import { Transformer } from 'markmap-lib' +import { Toolbar } from 'markmap-toolbar' +import markdownit from 'markdown-it' +import download from '@/utils/download' + +const md = markdownit() +const message = useMessage() // 消息弹窗 + +const props = defineProps<{ + generatedContent: string // 生成结果 + isEnd: boolean // 是否结束 + isGenerating: boolean // 是否正在生成 + isStart: boolean // 开始状态,开始时需要清除 html +}>() +const contentRef = ref<HTMLDivElement>() // 右侧出来 header 以下的区域 +const mdContainerRef = ref<HTMLDivElement>() // markdown 的容器,用来滚动到底下的 +const mindMapRef = ref<HTMLDivElement>() // 思维导图的容器 +const svgRef = ref<SVGElement>() // 思维导图的渲染 svg +const toolBarRef = ref<HTMLDivElement>() // 思维导图右下角的工具栏,缩放等 +const html = ref('') // 生成过程中的文本 +const contentAreaHeight = ref(0) // 生成区域的高度,出去 header 部分 +let markMap: Markmap | null = null +const transformer = new Transformer() + +onMounted(() => { + contentAreaHeight.value = contentRef.value?.clientHeight || 0 // 获取区域高度 + /** 初始化思维导图 **/ + try { + markMap = Markmap.create(svgRef.value!) + const { el } = Toolbar.create(markMap) + toolBarRef.value?.append(el) + nextTick(update) + } catch (e) { + message.error('思维导图初始化失败') + } +}) + +watch(props, ({ generatedContent, isGenerating, isEnd, isStart }) => { + // 开始生成的时候清空一下 markdown 的内容 + if (isStart) { + html.value = '' + } + // 生成内容的时候使用 markdown 来渲染 + if (isGenerating) { + html.value = md.render(generatedContent) + } + // 生成结束时更新思维导图 + if (isEnd) { + update() + } +}) + +/** 更新思维导图的展示 */ +const update = () => { + try { + const { root } = transformer.transform(processContent(props.generatedContent)) + markMap?.setData(root) + markMap?.fit() + } catch (e) { + console.error(e) + } +} + +/** 处理内容 */ +const processContent = (text: string) => { + const arr: string[] = [] + const lines = text.split('\n') + for (let line of lines) { + if (line.indexOf('```') !== -1) { + continue + } + line = line.replace(/([*_~`>])|(\d+\.)\s/g, '') + arr.push(line) + } + return arr.join('\n') +} + +/** 下载图片:download SVG to png file */ +const downloadImage = () => { + const svgElement = mindMapRef.value + // 将 SVG 渲染到图片对象 + const serializer = new XMLSerializer() + const source = `<?xml version="1.0" standalone="no"?>\r\n${serializer.serializeToString(svgRef.value!)}` + const base64Url = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(source)}` + download.image({ + url: base64Url, + canvasWidth: svgElement?.offsetWidth, + canvasHeight: svgElement?.offsetHeight, + drawWithImageSize: false + }) +} + +defineExpose({ + scrollBottom() { + mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight) + } +}) +</script> +<style lang="scss" scoped> +.hide-scroll-bar { + -ms-overflow-style: none; + scrollbar-width: none; + + &::-webkit-scrollbar { + width: 0; + height: 0; + } +} + +.my-card { + display: flex; + flex-direction: column; + + :deep(.el-card__body) { + box-sizing: border-box; + flex-grow: 1; + overflow-y: auto; + padding: 0; + @extend .hide-scroll-bar; + } +} + +// markmap的tool样式覆盖 +:deep(.markmap) { + width: 100%; +} + +:deep(.mm-toolbar-brand) { + display: none; +} + +:deep(.mm-toolbar) { + display: flex; + flex-direction: row; +} +</style> diff --git a/src/views/ai/mindmap/index/index.vue b/src/views/ai/mindmap/index/index.vue new file mode 100644 index 0000000..b85f20b --- /dev/null +++ b/src/views/ai/mindmap/index/index.vue @@ -0,0 +1,92 @@ +<template> + <div class="absolute top-0 left-0 right-0 bottom-0 flex"> + <!--表单区域--> + <Left + ref="leftRef" + :is-generating="isGenerating" + @submit="submit" + @direct-generate="directGenerate" + /> + <!--右边生成思维导图区域--> + <Right + ref="rightRef" + :generatedContent="generatedContent" + :isEnd="isEnd" + :isGenerating="isGenerating" + :isStart="isStart" + /> + </div> +</template> + +<script lang="ts" setup> +import Left from './components/Left.vue' +import Right from './components/Right.vue' +import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap' +import { MindMapContentExample } from '@/views/ai/utils/constants' + +defineOptions({ + name: 'AiMindMap' +}) +const ctrl = ref<AbortController>() // 请求控制 +const isGenerating = ref(false) // 是否正在生成思维导图 +const isStart = ref(false) // 开始生成,用来清空思维导图 +const isEnd = ref(true) // 用来判断结束的时候渲染思维导图 +const message = useMessage() // 消息提示 + +const generatedContent = ref('') // 生成思维导图结果 + +const leftRef = ref<InstanceType<typeof Left>>() // 左边组件 +const rightRef = ref<InstanceType<typeof Right>>() // 右边组件 + +/** 使用已有内容直接生成 **/ +const directGenerate = (existPrompt: string) => { + isEnd.value = false // 先设置为 false 再设置为 true,让子组建的 watch 能够监听到 + generatedContent.value = existPrompt + isEnd.value = true +} + +/** 停止 stream 生成 */ +const stopStream = () => { + isGenerating.value = false + isStart.value = false + ctrl.value?.abort() +} + +/** 提交生成 */ +const submit = (data: AiMindMapGenerateReqVO) => { + isGenerating.value = true + isStart.value = true + isEnd.value = false + ctrl.value = new AbortController() // 请求控制赋值 + generatedContent.value = '' // 清空生成数据 + AiMindMapApi.generateMindMap({ + data, + onMessage: async (res) => { + const { code, data, msg } = JSON.parse(res.data) + if (code !== 0) { + message.alert(`生成思维导图异常! ${msg}`) + stopStream() + return + } + generatedContent.value = generatedContent.value + data + await nextTick() + rightRef.value?.scrollBottom() + }, + onClose() { + isEnd.value = true + leftRef.value?.setGeneratedContent(generatedContent.value) + stopStream() + }, + onError(err) { + console.error('生成思维导图失败', err) + stopStream() + }, + ctrl: ctrl.value + }) +} + +/** 初始化 */ +onMounted(() => { + generatedContent.value = MindMapContentExample +}) +</script> diff --git a/src/views/ai/mindmap/manager/index.vue b/src/views/ai/mindmap/manager/index.vue new file mode 100644 index 0000000..ba79b8c --- /dev/null +++ b/src/views/ai/mindmap/manager/index.vue @@ -0,0 +1,190 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户编号" prop="userId"> + <el-select + v-model="queryParams.userId" + clearable + placeholder="请输入用户编号" + class="!w-240px" + > + <el-option + v-for="item in userList" + :key="item.id" + :label="item.nickname" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="提示词" prop="prompt"> + <el-input + v-model="queryParams.prompt" + placeholder="请输入提示词" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" width="180" fixed="left" /> + <el-table-column label="用户" align="center" prop="userId" width="180"> + <template #default="scope"> + <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span> + </template> + </el-table-column> + <el-table-column label="提示词" align="center" prop="prompt" width="180" /> + <el-table-column label="思维导图" align="center" prop="generatedContent" min-width="300" /> + <el-table-column label="模型" align="center" prop="model" width="180" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="错误信息" align="center" prop="errorMessage" /> + <el-table-column label="操作" align="center" width="120" fixed="right"> + <template #default="scope"> + <el-button link type="primary" @click="openPreview(scope.row)"> 预览 </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:mind-map:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 思维导图的预览 --> + <el-drawer v-model="previewVisible" :with-header="false" size="800px"> + <Right + v-if="previewVisible2" + :generatedContent="previewContent" + :isEnd="true" + :isGenerating="false" + :isStart="false" + /> + </el-drawer> +</template> + +<script setup lang="ts"> +import { dateFormatter } from '@/utils/formatTime' +import { AiMindMapApi, MindMapVO } from '@/api/ai/mindmap' +import * as UserApi from '@/api/system/user' +import Right from '@/views/ai/mindmap/index/components/Right.vue' + +/** AI 思维导图 列表 */ +defineOptions({ name: 'AiMindMapManager' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<MindMapVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + userId: undefined, + prompt: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const userList = ref<UserApi.UserVO[]>([]) // 用户列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await AiMindMapApi.getMindMapPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await AiMindMapApi.deleteMindMap(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 预览操作按钮 */ +const previewVisible = ref(false) // drawer 的显示隐藏 +const previewVisible2 = ref(false) // right 的显示隐藏 +const previewContent = ref('') +const openPreview = async (row: MindMapVO) => { + previewVisible2.value = false + previewVisible.value = true + // 在 drawer 渲染完后,再渲染 right 预览,不然会报错,需要保证 width 宽度先出来 + await nextTick() + previewVisible2.value = true + previewContent.value = row.generatedContent +} + +/** 初始化 **/ +onMounted(async () => { + getList() + // 获得用户列表 + userList.value = await UserApi.getSimpleUserList() +}) +</script> diff --git a/src/views/ai/model/apiKey/ApiKeyForm.vue b/src/views/ai/model/apiKey/ApiKeyForm.vue new file mode 100644 index 0000000..2d3d4bf --- /dev/null +++ b/src/views/ai/model/apiKey/ApiKeyForm.vue @@ -0,0 +1,132 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="120px" + v-loading="formLoading" + > + <el-form-item label="所属平台" prop="platform"> + <el-select v-model="formData.platform" placeholder="请输入平台" clearable> + <el-option + v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入名称" /> + </el-form-item> + <el-form-item label="密钥" prop="apiKey"> + <el-input v-model="formData.apiKey" placeholder="请输入密钥" /> + </el-form-item> + <el-form-item label="自定义 API URL" prop="url"> + <el-input v-model="formData.url" placeholder="请输入自定义 API URL" /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :value="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict' +import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey' +import { CommonStatusEnum } from '@/utils/constants' + +/** AI API 密钥 表单 */ +defineOptions({ name: 'ApiKeyForm' }) + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + apiKey: undefined, + platform: undefined, + url: undefined, + status: CommonStatusEnum.ENABLE +}) +const formRules = reactive({ + name: [{ required: true, message: '名称不能为空', trigger: 'blur' }], + apiKey: [{ required: true, message: '密钥不能为空', trigger: 'blur' }], + platform: [{ required: true, message: '平台不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await ApiKeyApi.getApiKey(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ApiKeyVO + if (formType.value === 'create') { + await ApiKeyApi.createApiKey(data) + message.success(t('common.createSuccess')) + } else { + await ApiKeyApi.updateApiKey(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + apiKey: undefined, + platform: undefined, + url: undefined, + status: CommonStatusEnum.ENABLE + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/model/apiKey/index.vue b/src/views/ai/model/apiKey/index.vue new file mode 100644 index 0000000..8ac7002 --- /dev/null +++ b/src/views/ai/model/apiKey/index.vue @@ -0,0 +1,181 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="平台" prop="platform"> + <el-select + v-model="queryParams.platform" + placeholder="请输入平台" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['ai:api-key:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="所属平台" align="center" prop="platform"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" /> + </template> + </el-table-column> + <el-table-column label="名称" align="center" prop="name" /> + <el-table-column label="密钥" align="center" prop="apiKey" /> + <el-table-column label="自定义 API URL" align="center" prop="url" /> + <el-table-column label="状态" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:api-key:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:api-key:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <ApiKeyForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE, getStrDictOptions } from '@/utils/dict' +import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey' +import ApiKeyForm from './ApiKeyForm.vue' + +/** AI API 密钥 列表 */ +defineOptions({ name: 'AiApiKey' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ApiKeyVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + platform: undefined, + status: undefined +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ApiKeyApi.getApiKeyPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ApiKeyApi.deleteApiKey(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/ai/model/chatRole/ChatRoleForm.vue b/src/views/ai/model/chatRole/ChatRoleForm.vue new file mode 100644 index 0000000..ebfd600 --- /dev/null +++ b/src/views/ai/model/chatRole/ChatRoleForm.vue @@ -0,0 +1,211 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="角色名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入角色名称" /> + </el-form-item> + <el-form-item label="角色头像" prop="avatar"> + <UploadImg v-model="formData.avatar" height="60px" width="60px" /> + </el-form-item> + <el-form-item label="绑定模型" prop="modelId" v-if="!isUser"> + <el-select v-model="formData.modelId" placeholder="请选择模型" clearable> + <el-option + v-for="model in models" + :key="model.id" + :label="model.name" + :value="model.id" + /> + </el-select> + </el-form-item> + <el-form-item label="角色类别" prop="category" v-if="!isUser"> + <el-input v-model="formData.category" placeholder="请输入角色类别" /> + </el-form-item> + <el-form-item label="角色描述" prop="description"> + <el-input type="textarea" v-model="formData.description" placeholder="请输入角色描述" /> + </el-form-item> + <el-form-item label="角色设定" prop="systemMessage"> + <el-input type="textarea" v-model="formData.systemMessage" placeholder="请输入角色设定" /> + </el-form-item> + <el-form-item label="引用知识库" prop="knowledgeIds"> + <el-select v-model="formData.knowledgeIds" placeholder="请选择知识库" clearable multiple> + <el-option + v-for="item in knowledgeList" + :key="item.id" + :label="item.name" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="引用工具" prop="toolIds"> + <el-select v-model="formData.toolIds" placeholder="请选择工具" clearable multiple> + <el-option v-for="item in toolList" :key="item.id" :label="item.name" :value="item.id" /> + </el-select> + </el-form-item> + <el-form-item label="是否公开" prop="publicStatus" v-if="!isUser"> + <el-radio-group v-model="formData.publicStatus"> + <el-radio + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" + :key="dict.value" + :value="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="角色排序" prop="sort" v-if="!isUser"> + <el-input-number v-model="formData.sort" placeholder="请输入角色排序" class="!w-1/1" /> + </el-form-item> + <el-form-item label="开启状态" prop="status" v-if="!isUser"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :value="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' +import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole' +import { CommonStatusEnum } from '@/utils/constants' +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { FormRules } from 'element-plus' +import { AiModelTypeEnum } from '@/views/ai/utils/constants' +import { KnowledgeApi, KnowledgeVO } from '@/api/ai/knowledge/knowledge' +import { ToolApi, ToolVO } from '@/api/ai/model/tool' + +/** AI 聊天角色 表单 */ +defineOptions({ name: 'ChatRoleForm' }) + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + modelId: undefined, + name: undefined, + avatar: undefined, + category: undefined, + sort: undefined, + description: undefined, + systemMessage: undefined, + publicStatus: true, + status: CommonStatusEnum.ENABLE, + knowledgeIds: [] as number[], + toolIds: [] as number[] +}) +const formRef = ref() // 表单 Ref +const models = ref([] as ModelVO[]) // 聊天模型列表 +const knowledgeList = ref([] as KnowledgeVO[]) // 知识库列表 +const toolList = ref([] as ToolVO[]) // 工具列表 + +/** 是否【我】自己创建,私有角色 */ +const isUser = computed(() => { + return formType.value === 'my-create' || formType.value === 'my-update' +}) + +const formRules = reactive<FormRules>({ + name: [{ required: true, message: '角色名称不能为空', trigger: 'blur' }], + avatar: [{ required: true, message: '角色头像不能为空', trigger: 'blur' }], + category: [{ required: true, message: '角色类别不能为空', trigger: 'blur' }], + sort: [{ required: true, message: '角色排序不能为空', trigger: 'blur' }], + description: [{ required: true, message: '角色描述不能为空', trigger: 'blur' }], + systemMessage: [{ required: true, message: '角色设定不能为空', trigger: 'blur' }], + publicStatus: [{ required: true, message: '是否公开不能为空', trigger: 'blur' }] +}) + +/** 打开弹窗 */ +// TODO @fan:title 是不是收敛到 type 判断生成 title,会更合理 +const open = async (type: string, id?: number, title?: string) => { + dialogVisible.value = true + dialogTitle.value = title || t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await ChatRoleApi.getChatRole(id) + } finally { + formLoading.value = false + } + } + // 获得下拉数据 + models.value = await ModelApi.getModelSimpleList(AiModelTypeEnum.CHAT) + // 获取知识库列表 + knowledgeList.value = await KnowledgeApi.getSimpleKnowledgeList() + // 获取工具列表 + toolList.value = await ToolApi.getToolSimpleList() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ChatRoleVO + // tip: my-create、my-update 是 chat 角色仓库调用 + // tip: create、else 是后台管理调用 + if (formType.value === 'my-create') { + await ChatRoleApi.createMy(data) + message.success(t('common.createSuccess')) + } else if (formType.value === 'my-update') { + await ChatRoleApi.updateMy(data) + message.success(t('common.updateSuccess')) + } else if (formType.value === 'create') { + await ChatRoleApi.createChatRole(data) + message.success(t('common.createSuccess')) + } else { + await ChatRoleApi.updateChatRole(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + modelId: undefined, + name: undefined, + avatar: undefined, + category: undefined, + sort: undefined, + description: undefined, + systemMessage: undefined, + publicStatus: true, + status: CommonStatusEnum.ENABLE, + knowledgeIds: [], + toolIds: [] + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/model/chatRole/index.vue b/src/views/ai/model/chatRole/index.vue new file mode 100644 index 0000000..85e491d --- /dev/null +++ b/src/views/ai/model/chatRole/index.vue @@ -0,0 +1,200 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="角色名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入角色名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="角色类别" prop="category"> + <el-input + v-model="queryParams.category" + placeholder="请输入角色类别" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="是否公开" prop="publicStatus"> + <el-select + v-model="queryParams.publicStatus" + placeholder="请选择是否公开" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['ai:chat-role:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="角色名称" align="center" prop="name" /> + <el-table-column label="绑定模型" align="center" prop="modelName" /> + <el-table-column label="角色头像" align="center" prop="avatar"> + <template #default="scope"> + <el-image :src="scope?.row.avatar" class="w-32px h-32px" /> + </template> + </el-table-column> + <el-table-column label="角色类别" align="center" prop="category" /> + <el-table-column label="角色描述" align="center" prop="description" /> + <el-table-column label="角色设定" align="center" prop="systemMessage" /> + <el-table-column label="知识库" align="center" prop="knowledgeIds"> + <template #default="scope"> + <span v-if="!scope.row.knowledgeIds || scope.row.knowledgeIds.length === 0">-</span> + <span v-else>引用 {{ scope.row.knowledgeIds.length }} 个</span> + </template> + </el-table-column> + <el-table-column label="工具" align="center" prop="toolIds"> + <template #default="scope"> + <span v-if="!scope.row.toolIds || scope.row.toolIds.length === 0">-</span> + <span v-else>引用 {{ scope.row.toolIds.length }} 个</span> + </template> + </el-table-column> + <el-table-column label="是否公开" align="center" prop="publicStatus"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.publicStatus" /> + </template> + </el-table-column> + <el-table-column label="状态" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="角色排序" align="center" prop="sort" /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:chat-role:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:chat-role:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <ChatRoleForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getBoolDictOptions, DICT_TYPE } from '@/utils/dict' +import { ChatRoleApi, ChatRoleVO } from '@/api/ai/model/chatRole' +import ChatRoleForm from './ChatRoleForm.vue' + +/** AI 聊天角色 列表 */ +defineOptions({ name: 'AiChatRole' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ChatRoleVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + category: undefined, + publicStatus: true +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ChatRoleApi.getChatRolePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ChatRoleApi.deleteChatRole(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/ai/model/model/ModelForm.vue b/src/views/ai/model/model/ModelForm.vue new file mode 100644 index 0000000..a27ca0f --- /dev/null +++ b/src/views/ai/model/model/ModelForm.vue @@ -0,0 +1,220 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="120px" + v-loading="formLoading" + > + <el-form-item label="所属平台" prop="platform"> + <el-select v-model="formData.platform" placeholder="请输入平台" clearable> + <el-option + v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="模型类型" prop="type"> + <el-select + v-model="formData.type" + placeholder="请输入模型类型" + clearable + :disabled="formData.id" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.AI_MODEL_TYPE)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="API 秘钥" prop="keyId"> + <el-select v-model="formData.keyId" placeholder="请选择 API 秘钥" clearable> + <el-option + v-for="apiKey in apiKeyList" + :key="apiKey.id" + :label="apiKey.name" + :value="apiKey.id" + /> + </el-select> + </el-form-item> + <el-form-item label="模型名字" prop="name"> + <el-input v-model="formData.name" placeholder="请输入模型名字" /> + </el-form-item> + <el-form-item label="模型标识" prop="model"> + <el-input v-model="formData.model" placeholder="请输入模型标识" /> + </el-form-item> + <el-form-item label="模型排序" prop="sort"> + <el-input-number v-model="formData.sort" placeholder="请输入模型排序" class="!w-1/1" /> + </el-form-item> + <el-form-item label="开启状态" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :value="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + <el-form-item + label="温度参数" + prop="temperature" + v-if="formData.type === AiModelTypeEnum.CHAT" + > + <el-input-number + v-model="formData.temperature" + placeholder="请输入温度参数" + :min="0" + :max="2" + :precision="2" + class="!w-1/1" + /> + </el-form-item> + <el-form-item + label="回复数 Token 数" + prop="maxTokens" + v-if="formData.type === AiModelTypeEnum.CHAT" + > + <el-input-number + v-model="formData.maxTokens" + placeholder="请输入回复数 Token 数" + :min="0" + :max="8192" + class="!w-1/1" + /> + </el-form-item> + <el-form-item + label="上下文数量" + prop="maxContexts" + v-if="formData.type === AiModelTypeEnum.CHAT" + > + <el-input-number + v-model="formData.maxContexts" + placeholder="请输入上下文数量" + :min="0" + :max="20" + class="!w-1/1" + /> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey' +import { CommonStatusEnum } from '@/utils/constants' +import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict' +import { AiModelTypeEnum } from '@/views/ai/utils/constants' + +/** API 模型的表单 */ +defineOptions({ name: 'ModelForm' }) + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + keyId: undefined, + name: undefined, + model: undefined, + platform: undefined, + type: undefined, + sort: undefined, + status: CommonStatusEnum.ENABLE, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined +}) +const formRules = reactive({ + keyId: [{ required: true, message: 'API 秘钥不能为空', trigger: 'blur' }], + name: [{ required: true, message: '模型名字不能为空', trigger: 'blur' }], + model: [{ required: true, message: '模型标识不能为空', trigger: 'blur' }], + platform: [{ required: true, message: '所属平台不能为空', trigger: 'blur' }], + type: [{ required: true, message: '模型类型不能为空', trigger: 'blur' }], + sort: [{ required: true, message: '排序不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref +const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表 + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await ModelApi.getModel(id) + } finally { + formLoading.value = false + } + } + // 获得下拉数据 + apiKeyList.value = await ApiKeyApi.getApiKeySimpleList() +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ModelVO + if (data.type !== AiModelTypeEnum.CHAT) { + delete data.temperature + delete data.maxTokens + delete data.maxContexts + } + if (formType.value === 'create') { + await ModelApi.createModel(data) + message.success(t('common.createSuccess')) + } else { + await ModelApi.updateModel(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + keyId: undefined, + name: undefined, + model: undefined, + platform: undefined, + type: undefined, + sort: undefined, + status: CommonStatusEnum.ENABLE, + temperature: undefined, + maxTokens: undefined, + maxContexts: undefined + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/model/model/index.vue b/src/views/ai/model/model/index.vue new file mode 100644 index 0000000..9d33020 --- /dev/null +++ b/src/views/ai/model/model/index.vue @@ -0,0 +1,190 @@ +<template> + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="模型名字" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入模型名字" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="模型标识" prop="model"> + <el-input + v-model="queryParams.model" + placeholder="请输入模型标识" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="模型平台" prop="platform"> + <el-input + v-model="queryParams.platform" + placeholder="请输入模型平台" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['ai:model:create']" + > + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="所属平台" align="center" prop="platform" min-width="100"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" /> + </template> + </el-table-column> + <el-table-column label="模型类型" align="center" prop="platform" min-width="100"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_MODEL_TYPE" :value="scope.row.type" /> + </template> + </el-table-column> + <el-table-column label="模型名字" align="center" prop="name" min-width="180" /> + <el-table-column label="模型标识" align="center" prop="model" min-width="180" /> + <el-table-column label="API 秘钥" align="center" prop="keyId" min-width="140"> + <template #default="scope"> + <span>{{ apiKeyList.find((item) => item.id === scope.row.keyId)?.name }}</span> + </template> + </el-table-column> + <el-table-column label="排序" align="center" prop="sort" min-width="80" /> + <el-table-column label="状态" align="center" prop="status" min-width="80"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="温度参数" align="center" prop="temperature" min-width="80" /> + <el-table-column label="回复数 Token 数" align="center" prop="maxTokens" min-width="140" /> + <el-table-column label="上下文数量" align="center" prop="maxContexts" min-width="100" /> + <el-table-column label="操作" align="center" width="180" fixed="right"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:model:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:model:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <ModelForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { ModelApi, ModelVO } from '@/api/ai/model/model' +import ModelForm from './ModelForm.vue' +import { DICT_TYPE } from '@/utils/dict' +import { ApiKeyApi, ApiKeyVO } from '@/api/ai/model/apiKey' + +/** API 模型列表 */ +defineOptions({ name: 'AiModel' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ModelVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + model: undefined, + platform: undefined +}) +const queryFormRef = ref() // 搜索的表单 +const apiKeyList = ref([] as ApiKeyVO[]) // API 密钥列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ModelApi.getModelPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ModelApi.deleteModel(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + await getList() + // 获得下拉数据 + apiKeyList.value = await ApiKeyApi.getApiKeySimpleList() +}) +</script> diff --git a/src/views/ai/model/tool/ToolForm.vue b/src/views/ai/model/tool/ToolForm.vue new file mode 100644 index 0000000..bec5961 --- /dev/null +++ b/src/views/ai/model/tool/ToolForm.vue @@ -0,0 +1,112 @@ +<template> + <Dialog :title="dialogTitle" v-model="dialogVisible"> + <el-form + ref="formRef" + :model="formData" + :rules="formRules" + label-width="100px" + v-loading="formLoading" + > + <el-form-item label="工具名称" prop="name"> + <el-input v-model="formData.name" placeholder="请输入工具名称" /> + </el-form-item> + <el-form-item label="工具描述" prop="description"> + <el-input type="textarea" v-model="formData.description" placeholder="请输入工具描述" /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-radio-group v-model="formData.status"> + <el-radio + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.value" + > + {{ dict.label }} + </el-radio> + </el-radio-group> + </el-form-item> + </el-form> + <template #footer> + <el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </template> + </Dialog> +</template> +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { ToolApi, ToolVO } from '@/api/ai/model/tool' +import { CommonStatusEnum } from '@/utils/constants' + +/** AI 工具表单 */ +defineOptions({ name: 'ToolForm' }) + +const { t } = useI18n() // 国际化 +const message = useMessage() // 消息弹窗 + +const dialogVisible = ref(false) // 弹窗的是否展示 +const dialogTitle = ref('') // 弹窗的标题 +const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 +const formType = ref('') // 表单的类型:create - 新增;update - 修改 +const formData = ref({ + id: undefined, + name: undefined, + description: undefined, + status: CommonStatusEnum.ENABLE +}) +const formRules = reactive({ + name: [{ required: true, message: '工具名称不能为空', trigger: 'blur' }] +}) +const formRef = ref() // 表单 Ref + +/** 打开弹窗 */ +const open = async (type: string, id?: number) => { + dialogVisible.value = true + dialogTitle.value = t('action.' + type) + formType.value = type + resetForm() + // 修改时,设置数据 + if (id) { + formLoading.value = true + try { + formData.value = await ToolApi.getTool(id) + } finally { + formLoading.value = false + } + } +} +defineExpose({ open }) // 提供 open 方法,用于打开弹窗 + +/** 提交表单 */ +const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调 +const submitForm = async () => { + // 校验表单 + await formRef.value.validate() + // 提交请求 + formLoading.value = true + try { + const data = formData.value as unknown as ToolVO + if (formType.value === 'create') { + await ToolApi.createTool(data) + message.success(t('common.createSuccess')) + } else { + await ToolApi.updateTool(data) + message.success(t('common.updateSuccess')) + } + dialogVisible.value = false + // 发送操作成功的事件 + emit('success') + } finally { + formLoading.value = false + } +} + +/** 重置表单 */ +const resetForm = () => { + formData.value = { + id: undefined, + name: undefined, + description: undefined, + status: CommonStatusEnum.ENABLE + } + formRef.value?.resetFields() +} +</script> diff --git a/src/views/ai/model/tool/index.vue b/src/views/ai/model/tool/index.vue new file mode 100644 index 0000000..e0c0b14 --- /dev/null +++ b/src/views/ai/model/tool/index.vue @@ -0,0 +1,177 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="工具名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入工具名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="请选择状态" clearable class="!w-240px"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-220px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['ai:tool:create']"> + <Icon icon="ep:plus" class="mr-5px" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="工具编号" align="center" prop="id" /> + <el-table-column label="工具名称" align="center" prop="name" /> + <el-table-column label="工具描述" align="center" prop="description" /> + <el-table-column label="状态" align="center" prop="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center" min-width="120px"> + <template #default="scope"> + <el-button + link + type="primary" + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:tool:update']" + > + 编辑 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:tool:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 表单弹窗:添加/修改 --> + <ToolForm ref="formRef" @success="getList" /> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { ToolApi, ToolVO } from '@/api/ai/model/tool' +import ToolForm from './ToolForm.vue' + +/** AI 工具 列表 */ +defineOptions({ name: 'AiTool' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<ToolVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + name: undefined, + description: undefined, + status: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 +const exportLoading = ref(false) // 导出的加载中 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await ToolApi.getToolPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 添加/修改操作 */ +const formRef = ref() +const openForm = (type: string, id?: number) => { + formRef.value.open(type, id) +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await ToolApi.deleteTool(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/ai/music/index/index.vue b/src/views/ai/music/index/index.vue new file mode 100644 index 0000000..413792a --- /dev/null +++ b/src/views/ai/music/index/index.vue @@ -0,0 +1,26 @@ +<template> +<div class="flex h-full items-stretch"> + <!-- 模式 --> + <Mode class="flex-none" @generate-music="generateMusic"/> + <!-- 音频列表 --> + <List ref="listRef" class="flex-auto"/> + </div> +</template> + +<script lang="ts" setup> +import Mode from './mode/index.vue' +import List from './list/index.vue' + +defineOptions({ name: 'Index' }) + +const listRef = ref<Nullable<{generateMusic: (...args) => void}>>(null) + +/* + *@Description: 拿到左侧配置信息调用右侧音乐生成的方法 + *@MethodAuthor: xiaohong + *@Date: 2024-07-19 11:13:38 +*/ +function generateMusic (args: {formData: Recordable}) { + unref(listRef)?.generateMusic(args.formData) +} +</script> diff --git a/src/views/ai/music/index/list/audioBar/index.vue b/src/views/ai/music/index/list/audioBar/index.vue new file mode 100644 index 0000000..db7f767 --- /dev/null +++ b/src/views/ai/music/index/list/audioBar/index.vue @@ -0,0 +1,70 @@ +<template> + <div class="flex items-center justify-between px-2 h-72px bg-[var(--el-bg-color-overlay)] b-solid b-1 b-[var(--el-border-color)] b-l-none"> + <!-- 歌曲信息 --> + <div class="flex gap-[10px]"> + <el-image src="https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png" class="w-[45px]"/> + <div> + <div>{{currentSong.name}}</div> + <div class="text-[12px] text-gray-400">{{currentSong.singer}}</div> + </div> + </div> + + <!-- 音频controls --> + <div class="flex gap-[12px] items-center"> + <Icon icon="majesticons:back-circle" :size="20" class="text-gray-300 cursor-pointer"/> + <Icon :icon="audioProps.paused ? 'mdi:arrow-right-drop-circle' : 'solar:pause-circle-bold'" :size="30" class=" cursor-pointer" @click="toggleStatus('paused')"/> + <Icon icon="majesticons:next-circle" :size="20" class="text-gray-300 cursor-pointer"/> + <div class="flex gap-[16px] items-center"> + <span>{{audioProps.currentTime}}</span> + <el-slider v-model="audioProps.duration" color="#409eff" class="w-[160px!important] "/> + <span>{{ audioProps.duration }}</span> + </div> + <!-- 音频 --> + <audio v-bind="audioProps" ref="audioRef" controls v-show="!audioProps" @timeupdate="audioTimeUpdate"> + <source :src="audioUrl"/> + </audio> + </div> + + <!-- 音量控制器 --> + <div class="flex gap-[16px] items-center"> + <Icon :icon="audioProps.muted ? 'tabler:volume-off' : 'tabler:volume'" :size="20" class="cursor-pointer" @click="toggleStatus('muted')"/> + <el-slider v-model="audioProps.volume" color="#409eff" class="w-[160px!important] "/> + </div> + </div> +</template> + +<script lang="ts" setup> +import { formatPast } from '@/utils/formatTime' +import audioUrl from '@/assets/audio/response.mp3' + +defineOptions({ name: 'Index' }) + +const currentSong = inject('currentSong', {}) + +const audioRef = ref<Nullable<HTMLElement>>(null) + // 音频相关属性https://www.runoob.com/tags/ref-av-dom.html +const audioProps = reactive({ + autoplay: true, + paused: false, + currentTime: '00:00', + duration: '00:00', + muted: false, + volume: 50, +}) + +function toggleStatus (type: string) { + audioProps[type] = !audioProps[type] + if (type === 'paused' && audioRef.value) { + if (audioProps[type]) { + audioRef.value.pause() + } else { + audioRef.value.play() + } + } +} + +// 更新播放位置 +function audioTimeUpdate (args) { + audioProps.currentTime = formatPast(new Date(args.timeStamp), 'mm:ss') +} +</script> diff --git a/src/views/ai/music/index/list/index.vue b/src/views/ai/music/index/list/index.vue new file mode 100644 index 0000000..6c33f56 --- /dev/null +++ b/src/views/ai/music/index/list/index.vue @@ -0,0 +1,108 @@ +<template> + <div class="flex flex-col"> + <div class="flex-auto flex overflow-hidden"> + <el-tabs v-model="currentType" class="flex-auto px-[var(--app-content-padding)]"> + <!-- 我的创作 --> + <el-tab-pane v-loading="loading" label="我的创作" name="mine"> + <el-row v-if="mySongList.length" :gutter="12"> + <el-col v-for="song in mySongList" :key="song.id" :span="24"> + <songCard :songInfo="song" @play="setCurrentSong(song)"/> + </el-col> + </el-row> + <el-empty v-else description="暂无音乐"/> + </el-tab-pane> + + <!-- 试听广场 --> + <el-tab-pane v-loading="loading" label="试听广场" name="square"> + <el-row v-if="squareSongList.length" v-loading="loading" :gutter="12"> + <el-col v-for="song in squareSongList" :key="song.id" :span="24"> + <songCard :songInfo="song" @play="setCurrentSong(song)"/> + </el-col> + </el-row> + <el-empty v-else description="暂无音乐"/> + </el-tab-pane> + </el-tabs> + <!-- songInfo --> + <songInfo class="flex-none"/> + </div> + <audioBar class="flex-none"/> + </div> +</template> + +<script lang="ts" setup> +import songCard from './songCard/index.vue' +import songInfo from './songInfo/index.vue' +import audioBar from './audioBar/index.vue' + +defineOptions({ name: 'Index' }) + + +const currentType = ref('mine') +// loading 状态 +const loading = ref(false) +// 当前音乐 +const currentSong = ref({}) + +const mySongList = ref<Recordable[]>([]) +const squareSongList = ref<Recordable[]>([]) + +provide('currentSong', currentSong) + +/* + *@Description: 调接口生成音乐列表 + *@MethodAuthor: xiaohong + *@Date: 2024-06-27 17:06:44 +*/ +function generateMusic (formData: Recordable) { + console.log(formData); + loading.value = true + setTimeout(() => { + mySongList.value = Array.from({ length: 20 }, (_, index) => { + return { + id: index, + audioUrl: '', + videoUrl: '', + title: '我走后' + index, + imageUrl: 'https://www.carsmp3.com/data/attachment/forum/201909/19/091020q5kgre20fidreqyt.jpg', + desc: 'Metal, symphony, film soundtrack, grand, majesticMetal, dtrack, grand, majestic', + date: '2024年04月30日 14:02:57', + lyric: `<div class="_words_17xen_66"><div>大江东去,浪淘尽,千古风流人物。 + </div><div>故垒西边,人道是,三国周郎赤壁。 + </div><div>乱石穿空,惊涛拍岸,卷起千堆雪。 + </div><div>江山如画,一时多少豪杰。 + </div><div> + </div><div>遥想公瑾当年,小乔初嫁了,雄姿英发。 + </div><div>羽扇纶巾,谈笑间,樯橹灰飞烟灭。 + </div><div>故国神游,多情应笑我,早生华发。 + </div><div>人生如梦,一尊还酹江月。</div></div>` + } + }) + loading.value = false + }, 3000) +} + +/* + *@Description: 设置当前播放的音乐 + *@MethodAuthor: xiaohong + *@Date: 2024-07-19 11:22:33 +*/ +function setCurrentSong (music: Recordable) { + currentSong.value = music +} + +defineExpose({ + generateMusic +}) +</script> + + +<style lang="scss" scoped> +:deep(.el-tabs) { + display: flex; + flex-direction: column; + .el-tabs__content { + padding: 0 7px; + overflow: auto; + } +} +</style> diff --git a/src/views/ai/music/index/list/songCard/index.vue b/src/views/ai/music/index/list/songCard/index.vue new file mode 100644 index 0000000..0534251 --- /dev/null +++ b/src/views/ai/music/index/list/songCard/index.vue @@ -0,0 +1,36 @@ +<template> + <div class="flex bg-[var(--el-bg-color-overlay)] p-12px mb-12px rounded-1"> + <div class="relative" @click="playSong"> + <el-image :src="songInfo.imageUrl" class="flex-none w-80px"/> + <div class="bg-black bg-op-40 absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer"> + <Icon :icon="currentSong.id === songInfo.id ? 'solar:pause-circle-bold':'mdi:arrow-right-drop-circle'" :size="30" /> + </div> + </div> + <div class="ml-8px"> + <div>{{ songInfo.title }}</div> + <div class="mt-8px text-12px text-[var(--el-text-color-secondary)] line-clamp-2"> + {{ songInfo.desc }} + </div> + </div> + </div> +</template> + +<script lang="ts" setup> + +defineOptions({ name: 'Index' }) + +defineProps({ + songInfo: { + type: Object, + default: () => ({}) + } +}) + +const emits = defineEmits(['play']) + +const currentSong = inject('currentSong', {}) + +function playSong () { + emits('play') +} +</script> diff --git a/src/views/ai/music/index/list/songInfo/index.vue b/src/views/ai/music/index/list/songInfo/index.vue new file mode 100644 index 0000000..8d67c4d --- /dev/null +++ b/src/views/ai/music/index/list/songInfo/index.vue @@ -0,0 +1,22 @@ +<template> + <ContentWrap class="w-300px mb-[0!important] line-height-24px"> + <el-image :src="currentSong.imageUrl"/> + <div class="">{{ currentSong.title }}</div> + <div class="text-[var(--el-text-color-secondary)] text-12px line-clamp-1"> + {{ currentSong.desc }} + </div> + <div class="text-[var(--el-text-color-secondary)] text-12px"> + {{ currentSong.date }} + </div> + <el-button size="small" round class="my-6px">信息复用</el-button> + <div class="text-[var(--el-text-color-secondary)] text-12px" v-html="currentSong.lyric"></div> + </ContentWrap> +</template> + +<script lang="ts" setup> + +defineOptions({ name: 'Index' }) + +const currentSong = inject('currentSong', {}) + +</script> diff --git a/src/views/ai/music/index/mode/desc.vue b/src/views/ai/music/index/mode/desc.vue new file mode 100644 index 0000000..4488461 --- /dev/null +++ b/src/views/ai/music/index/mode/desc.vue @@ -0,0 +1,55 @@ +<template> + <div> + <Title title="音乐/歌词说明" desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲"> + <el-input + v-model="formData.desc" + :autosize="{ minRows: 6, maxRows: 6}" + resize="none" + type="textarea" + maxlength="1200" + show-word-limit + placeholder="一首关于糟糕分手的欢快歌曲" + /> + </Title> + + <Title title="纯音乐" desc="创建一首没有歌词的歌曲"> + <template #extra> + <el-switch v-model="formData.pure" size="small"/> + </template> + </Title> + + <Title title="版本" desc="描述您想要的音乐风格和主题,使用流派和氛围而不是特定的艺术家和歌曲"> + <el-select v-model="formData.version" placeholder="请选择"> + <el-option + v-for="item in [{ + value: '3', + label: 'V3' + }, { + value: '2', + label: 'V2' + }]" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </Title> + </div> +</template> + +<script lang="ts" setup> +import Title from '../title/index.vue' + +defineOptions({ name: 'Desc' }) + +const formData = reactive({ + desc: '', + pure: false, + version: '3' +}) + +defineExpose({ + formData +}) + +</script> diff --git a/src/views/ai/music/index/mode/index.vue b/src/views/ai/music/index/mode/index.vue new file mode 100644 index 0000000..32cad7e --- /dev/null +++ b/src/views/ai/music/index/mode/index.vue @@ -0,0 +1,35 @@ +<template> + <ContentWrap class="w-300px h-full mb-[0!important]"> + <el-radio-group v-model="generateMode" class="mb-15px"> + <el-radio-button value="desc"> 描述模式 </el-radio-button> + <el-radio-button value="lyric"> 歌词模式 </el-radio-button> + </el-radio-group> + + <!-- 描述模式/歌词模式 切换 --> + <component :is="generateMode === 'desc' ? desc : lyric" ref="modeRef" /> + + <el-button type="primary" round class="w-full" @click="generateMusic"> 创作音乐 </el-button> + </ContentWrap> +</template> + +<script lang="ts" setup> +import desc from './desc.vue' +import lyric from './lyric.vue' + +defineOptions({ name: 'Index' }) + +const emits = defineEmits(['generate-music']) + +const generateMode = ref('lyric') + +const modeRef = ref<Nullable<{ formData: Recordable }>>(null) + +/* + *@Description: 根据信息生成音乐 + *@MethodAuthor: xiaohong + *@Date: 2024-06-27 16:40:16 + */ +function generateMusic() { + emits('generate-music', { formData: unref(modeRef)?.formData }) +} +</script> diff --git a/src/views/ai/music/index/mode/lyric.vue b/src/views/ai/music/index/mode/lyric.vue new file mode 100644 index 0000000..f774003 --- /dev/null +++ b/src/views/ai/music/index/mode/lyric.vue @@ -0,0 +1,83 @@ +<template> + <div class=""> + <Title title="歌词" desc="自己编写歌词或使用Ai生成歌词,两节/8行效果最佳"> + <el-input + v-model="formData.lyric" + :autosize="{ minRows: 6, maxRows: 6}" + resize="none" + type="textarea" + maxlength="1200" + show-word-limit + placeholder="请输入您自己的歌词" + /> + </Title> + + <Title title="音乐风格"> + <el-space class="flex-wrap"> + <el-tag v-for="tag in tags" :key="tag" round class="mb-8px">{{tag}}</el-tag> + </el-space> + + <el-button + :type="showCustom ? 'primary': 'default'" + round + size="small" + class="mb-6px" + @click="showCustom = !showCustom" + >自定义风格 + </el-button> + </Title> + + <Title v-show="showCustom" desc="描述您想要的音乐风格,Suno无法识别艺术家的名字,但可以理解流派和氛围" class="-mt-12px"> + <el-input + v-model="formData.style" + :autosize="{ minRows: 4, maxRows: 4}" + resize="none" + type="textarea" + maxlength="256" + show-word-limit + placeholder="输入音乐风格(英文)" + /> + </Title> + + <Title title="音乐/歌曲名称"> + <el-input v-model="formData.name" placeholder="请输入音乐/歌曲名称"/> + </Title> + + <Title title="版本"> + <el-select v-model="formData.version" placeholder="请选择"> + <el-option + v-for="item in [{ + value: '3', + label: 'V3' + }, { + value: '2', + label: 'V2' + }]" + :key="item.value" + :label="item.label" + :value="item.value" + /> + </el-select> + </Title> + </div> +</template> + +<script lang="ts" setup> +import Title from '../title/index.vue' +defineOptions({ name: 'Lyric' }) + +const tags = ['rock', 'punk', 'jazz', 'soul', 'country', 'kidsmusic', 'pop'] + +const showCustom = ref(false) + +const formData = reactive({ + lyric: '', + style: '', + name: '', + version: '' +}) + +defineExpose({ + formData +}) +</script> diff --git a/src/views/ai/music/index/title/index.vue b/src/views/ai/music/index/title/index.vue new file mode 100644 index 0000000..a065802 --- /dev/null +++ b/src/views/ai/music/index/title/index.vue @@ -0,0 +1,25 @@ +<template> + <div class="mb-12px"> + <div class="flex text-[var(--el-text-color-primary)] justify-between items-center"> + <span>{{title}}</span> + <slot name="extra"></slot> + </div> + <div class="text-[var(--el-text-color-secondary)] text-12px my-8px"> + {{desc}} + </div> + <slot></slot> + </div> +</template> + +<script lang="ts" setup> +defineOptions({ name: 'Index' }) + +defineProps({ + title: { + type: String + }, + desc: { + type: String + } +}) +</script> diff --git a/src/views/ai/music/manager/index.vue b/src/views/ai/music/manager/index.vue new file mode 100644 index 0000000..e2ad911 --- /dev/null +++ b/src/views/ai/music/manager/index.vue @@ -0,0 +1,293 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户编号" prop="userId"> + <el-select + v-model="queryParams.userId" + clearable + placeholder="请输入用户编号" + class="!w-240px" + > + <el-option + v-for="item in userList" + :key="item.id" + :label="item.nickname" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="音乐名称" prop="title"> + <el-input + v-model="queryParams.title" + placeholder="请输入音乐名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="音乐状态" prop="status"> + <el-select + v-model="queryParams.status" + placeholder="请选择音乐状态" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.AI_MUSIC_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="生成模式" prop="generateMode"> + <el-select + v-model="queryParams.generateMode" + placeholder="请选择生成模式" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.AI_GENERATE_MODE)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="是否发布" prop="publicStatus"> + <el-select + v-model="queryParams.publicStatus" + placeholder="请选择是否发布" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" width="180" fixed="left" /> + <el-table-column label="音乐名称" align="center" prop="title" width="180px" fixed="left" /> + <el-table-column label="用户" align="center" prop="userId" width="180"> + <template #default="scope"> + <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span> + </template> + </el-table-column> + <el-table-column label="音乐状态" align="center" prop="status" width="100"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_MUSIC_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="模型" align="center" prop="model" width="180" /> + <el-table-column label="内容" align="center" width="180"> + <template #default="{ row }"> + <el-link + v-if="row.audioUrl?.length > 0" + type="primary" + :href="row.audioUrl" + target="_blank" + > + 音乐 + </el-link> + <el-link + v-if="row.videoUrl?.length > 0" + type="primary" + :href="row.videoUrl" + target="_blank" + class="!pl-5px" + > + 视频 + </el-link> + <el-link + v-if="row.imageUrl?.length > 0" + type="primary" + :href="row.imageUrl" + target="_blank" + class="!pl-5px" + > + 封面 + </el-link> + </template> + </el-table-column> + <el-table-column label="时长(秒)" align="center" prop="duration" width="100" /> + <el-table-column label="提示词" align="center" prop="prompt" width="180" /> + <el-table-column label="歌词" align="center" prop="lyric" width="180" /> + <el-table-column label="描述" align="center" prop="gptDescriptionPrompt" width="180" /> + <el-table-column label="生成模式" align="center" prop="generateMode" width="100"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_GENERATE_MODE" :value="scope.row.generateMode" /> + </template> + </el-table-column> + <el-table-column label="风格标签" align="center" prop="tags" width="180"> + <template #default="scope"> + <el-tag v-for="tag in scope.row.tags" :key="tag" round class="ml-2px"> + {{ tag }} + </el-tag> + </template> + </el-table-column> + <el-table-column label="是否发布" align="center" prop="publicStatus"> + <template #default="scope"> + <el-switch + v-model="scope.row.publicStatus" + :active-value="true" + :inactive-value="false" + @change="handleUpdatePublicStatusChange(scope.row)" + :disabled="scope.row.status !== AiMusicStatusEnum.SUCCESS" + /> + </template> + </el-table-column> + <el-table-column label="任务编号" align="center" prop="taskId" width="180" /> + <el-table-column label="错误信息" align="center" prop="errorMessage" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="操作" align="center" width="100" fixed="right"> + <template #default="scope"> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:music:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { getIntDictOptions, getBoolDictOptions, DICT_TYPE } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { MusicApi, MusicVO } from '@/api/ai/music' +import * as UserApi from '@/api/system/user' +import { AiMusicStatusEnum } from '@/views/ai/utils/constants' + +/** AI 音乐 列表 */ +defineOptions({ name: 'AiMusicManager' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 + +const loading = ref(true) // 列表的加载中 +const list = ref<MusicVO[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + userId: undefined, + title: undefined, + status: undefined, + generateMode: undefined, + createTime: [], + publicStatus: undefined +}) +const queryFormRef = ref() // 搜索的表单 +const userList = ref<UserApi.UserVO[]>([]) // 用户列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await MusicApi.getMusicPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await MusicApi.deleteMusic(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 修改是否发布 */ +const handleUpdatePublicStatusChange = async (row: MusicVO) => { + try { + // 修改状态的二次确认 + const text = row.publicStatus ? '公开' : '私有' + await message.confirm('确认要"' + text + '"该音乐吗?') + // 发起修改状态 + await MusicApi.updateMusic({ + id: row.id, + publicStatus: row.publicStatus + }) + await getList() + } catch { + row.publicStatus = !row.publicStatus + } +} + +/** 初始化 **/ +onMounted(async () => { + getList() + // 获得用户列表 + userList.value = await UserApi.getSimpleUserList() +}) +</script> diff --git a/src/views/ai/questionparamsetting/QuestionParamSettingForm.vue b/src/views/ai/questionparamsetting/QuestionParamSettingForm.vue new file mode 100644 index 0000000..869dc3f --- /dev/null +++ b/src/views/ai/questionparamsetting/QuestionParamSettingForm.vue @@ -0,0 +1,117 @@ +<template> + <div class="app-container"> + <!-- 对话框(添加 / 修改) --> + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag + append-to-body> + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" + label-width="100px"> + <el-form-item label="问题模板id" prop="templateId"> + <el-input v-model="formData.templateId" placeholder="请输入问题模板id"/> + </el-form-item> + <el-form-item label="key" prop="settingKey"> + <el-input v-model="formData.settingKey" placeholder="请输入key"/> + </el-form-item> + <el-form-item label="参数名称" prop="settingName"> + <el-input v-model="formData.settingName" placeholder="请输入参数名称"/> + </el-form-item> + <el-form-item label="参数默认值" prop="settingValue"> + <el-input v-model="formData.settingValue" placeholder="请输入参数默认值"/> + </el-form-item> + <el-form-item label="排序" prop="sort"> + <el-input v-model="formData.sort" placeholder="请输入排序"/> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import * as QuestionParamSettingApi from '@/api/ai/questionparamsetting'; + +export default { + name: "QuestionParamSettingForm", + components: {}, + data() { + return { + // 弹出层标题 + dialogTitle: "", + // 是否显示弹出层 + dialogVisible: false, + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 + formLoading: false, + // 表单参数 + formData: { + id: undefined, + templateId: undefined, + settingKey: undefined, + settingName: undefined, + settingValue: undefined, + sort: undefined, + }, + // 表单校验 + formRules: { + templateId: [{required: true, message: '问题模板id不能为空', trigger: 'blur'}], + }, + }; + }, + methods: { + /** 打开弹窗 */ + async open(id) { + this.dialogVisible = true; + this.reset(); + // 修改时,设置数据 + if (id) { + this.formLoading = true; + try { + const res = await QuestionParamSettingApi.getQuestionParamSetting(id); + this.formData = res.data; + this.title = "修改大模型问题设置参数"; + } finally { + this.formLoading = false; + } + } + this.title = "新增大模型问题设置参数"; + }, + /** 提交按钮 */ + async submitForm() { + // 校验主表 + await this.$refs["formRef"].validate(); + this.formLoading = true; + try { + const data = this.formData; + // 修改的提交 + if (data.id) { + await QuestionParamSettingApi.updateQuestionParamSetting(data); + this.$modal.msgSuccess("修改成功"); + this.dialogVisible = false; + this.$emit('success'); + return; + } + // 添加的提交 + await QuestionParamSettingApi.createQuestionParamSetting(data); + this.$modal.msgSuccess("新增成功"); + this.dialogVisible = false; + this.$emit('success'); + } finally { + this.formLoading = false; + } + }, + /** 表单重置 */ + reset() { + this.formData = { + id: undefined, + templateId: undefined, + settingKey: undefined, + settingName: undefined, + settingValue: undefined, + sort: undefined, + }; + this.resetForm("formRef"); + } + } +}; +</script> diff --git a/src/views/ai/questionparamsetting/index.vue b/src/views/ai/questionparamsetting/index.vue new file mode 100644 index 0000000..e71a3e2 --- /dev/null +++ b/src/views/ai/questionparamsetting/index.vue @@ -0,0 +1,148 @@ +<template> + <div class="app-container"> + <!-- 搜索工作栏 --> + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" + label-width="68px"> + <el-form-item label="问题模板id" prop="templateId"> + <el-input v-model="queryParams.templateId" placeholder="请输入问题模板id" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="key" prop="settingKey"> + <el-input v-model="queryParams.settingKey" placeholder="请输入key" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="参数名称" prop="settingName"> + <el-input v-model="queryParams.settingName" placeholder="请输入参数名称" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="参数默认值" prop="settingValue"> + <el-input v-model="queryParams.settingValue" placeholder="请输入参数默认值" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="排序" prop="sort"> + <el-input v-model="queryParams.sort" placeholder="请输入排序" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button> + </el-form-item> + </el-form> + + <!-- 操作工具栏 --> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" + v-hasPermi="['ai:question-param-setting:create']">新增 + </el-button> + </el-col> + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="id" align="center" prop="id"/> + <el-table-column label="问题模板id" align="center" prop="templateId"/> + <el-table-column label="key" align="center" prop="settingKey"/> + <el-table-column label="参数名称" align="center" prop="settingName"/> + <el-table-column label="参数默认值" align="center" prop="settingValue"/> + <el-table-column label="排序" align="center" prop="sort"/> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <template v-slot="scope"> + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" + v-hasPermi="['ai:question-param-setting:update']">修改 + </el-button> + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" + v-hasPermi="['ai:question-param-setting:delete']">删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页组件 --> + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" + :limit.sync="queryParams.pageSize" + @pagination="getList"/> + <!-- 对话框(添加 / 修改) --> + <QuestionParamSettingForm ref="formRef" @success="getList"/> + </div> +</template> + +<script> +import * as QuestionParamSettingApi from '@/api/ai/questionparamsetting'; +import QuestionParamSettingForm from './QuestionParamSettingForm.vue'; + +export default { + name: "QuestionParamSetting", + components: { + QuestionParamSettingForm, + }, + data() { + return { + // 遮罩层 + loading: true, + // 显示搜索条件 + showSearch: true, + // 总条数 + total: 0, + // 大模型问题设置参数列表 + list: [], + // 是否展开,默认全部展开 + isExpandAll: true, + // 重新渲染表格状态 + refreshTable: true, + // 选中行 + currentRow: {}, + // 查询参数 + queryParams: { + pageNo: 1, + pageSize: 10, + templateId: null, + settingKey: null, + settingName: null, + settingValue: null, + sort: null, + }, + }; + }, + created() { + this.getList(); + }, + methods: { + /** 查询列表 */ + async getList() { + try { + this.loading = true; + const res = await QuestionParamSettingApi.getQuestionParamSettingPage(this.queryParams); + this.list = res.data.list; + this.total = res.data.total; + } finally { + this.loading = false; + } + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNo = 1; + this.getList(); + }, + /** 重置按钮操作 */ + resetQuery() { + this.resetForm("queryForm"); + this.handleQuery(); + }, + /** 添加/修改操作 */ + openForm(id) { + this.$refs["formRef"].open(id); + }, + /** 删除按钮操作 */ + async handleDelete(row) { + const id = row.id; + await this.$modal.confirm('是否确认删除大模型问题设置参数编号为"' + id + '"的数据项?') + try { + await QuestionParamSettingApi.deleteQuestionParamSetting(id); + await this.getList(); + this.$modal.msgSuccess("删除成功"); + } catch { + } + }, + } +}; +</script> diff --git a/src/views/ai/questiontemplate/QuestionTemplateForm.vue b/src/views/ai/questiontemplate/QuestionTemplateForm.vue new file mode 100644 index 0000000..acc6048 --- /dev/null +++ b/src/views/ai/questiontemplate/QuestionTemplateForm.vue @@ -0,0 +1,148 @@ +<template> + <div class="app-container"> + <!-- 对话框(添加 / 修改) --> + <el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="45%" v-dialogDrag + append-to-body> + <el-form ref="formRef" :model="formData" :rules="formRules" v-loading="formLoading" + label-width="100px"> + <el-form-item label="模型id" prop="modelId"> + <el-input v-model="formData.modelId" placeholder="请输入模型id"/> + </el-form-item> + <el-form-item label="问题编号" prop="questionCode"> + <el-input v-model="formData.questionCode" placeholder="请输入问题编号"/> + </el-form-item> + <el-form-item label="问题名称" prop="questionName"> + <el-input v-model="formData.questionName" placeholder="请输入问题名称"/> + </el-form-item> + <el-form-item label="问题内容"> + <Editor v-model="formData.questionContent" :min-height="192"/> + </el-form-item> + <el-form-item label="输入个数" prop="dataLength"> + <el-input v-model="formData.dataLength" placeholder="请输入输入个数"/> + </el-form-item> + <el-form-item label="是否启用(0禁用 1启用)" prop="isEnable"> + <el-input v-model="formData.isEnable" placeholder="请输入是否启用(0禁用 1启用)"/> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="formData.remark" placeholder="请输入备注"/> + </el-form-item> + <el-form-item label="创建时间" prop="createDate"> + <el-date-picker clearable v-model="formData.createDate" type="date" + value-format="timestamp" placeholder="选择创建时间"/> + </el-form-item> + <el-form-item label="更新者" prop="updator"> + <el-input v-model="formData.updator" placeholder="请输入更新者"/> + </el-form-item> + <el-form-item label="更新时间" prop="updateDate"> + <el-date-picker clearable v-model="formData.updateDate" type="date" + value-format="timestamp" placeholder="选择更新时间"/> + </el-form-item> + </el-form> + <div slot="footer" class="dialog-footer"> + <el-button type="primary" @click="submitForm" :disabled="formLoading">确 定</el-button> + <el-button @click="dialogVisible = false">取 消</el-button> + </div> + </el-dialog> + </div> +</template> + +<script> +import * as QuestionTemplateApi from '@/api/ai/questiontemplate'; +import Editor from '@/components/Editor'; + +export default { + name: "QuestionTemplateForm", + components: { + Editor, + }, + data() { + return { + // 弹出层标题 + dialogTitle: "", + // 是否显示弹出层 + dialogVisible: false, + // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用 + formLoading: false, + // 表单参数 + formData: { + id: undefined, + modelId: undefined, + questionCode: undefined, + questionName: undefined, + questionContent: undefined, + dataLength: undefined, + isEnable: undefined, + remark: undefined, + createDate: undefined, + updator: undefined, + updateDate: undefined, + }, + // 表单校验 + formRules: { + modelId: [{required: true, message: '模型id不能为空', trigger: 'blur'}], + questionCode: [{required: true, message: '问题编号不能为空', trigger: 'blur'}], + }, + }; + }, + methods: { + /** 打开弹窗 */ + async open(id) { + this.dialogVisible = true; + this.reset(); + // 修改时,设置数据 + if (id) { + this.formLoading = true; + try { + const res = await QuestionTemplateApi.getQuestionTemplate(id); + this.formData = res.data; + this.title = "修改大模型问题模板"; + } finally { + this.formLoading = false; + } + } + this.title = "新增大模型问题模板"; + }, + /** 提交按钮 */ + async submitForm() { + // 校验主表 + await this.$refs["formRef"].validate(); + this.formLoading = true; + try { + const data = this.formData; + // 修改的提交 + if (data.id) { + await QuestionTemplateApi.updateQuestionTemplate(data); + this.$modal.msgSuccess("修改成功"); + this.dialogVisible = false; + this.$emit('success'); + return; + } + // 添加的提交 + await QuestionTemplateApi.createQuestionTemplate(data); + this.$modal.msgSuccess("新增成功"); + this.dialogVisible = false; + this.$emit('success'); + } finally { + this.formLoading = false; + } + }, + /** 表单重置 */ + reset() { + this.formData = { + id: undefined, + modelId: undefined, + questionCode: undefined, + questionName: undefined, + questionContent: undefined, + dataLength: undefined, + isEnable: undefined, + remark: undefined, + createDate: undefined, + updator: undefined, + updateDate: undefined, + }; + this.resetForm("formRef"); + } + } +}; +</script> diff --git a/src/views/ai/questiontemplate/index.vue b/src/views/ai/questiontemplate/index.vue new file mode 100644 index 0000000..d8078ea --- /dev/null +++ b/src/views/ai/questiontemplate/index.vue @@ -0,0 +1,186 @@ +<template> + <div class="app-container"> + <!-- 搜索工作栏 --> + <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" + label-width="68px"> + <el-form-item label="模型id" prop="modelId"> + <el-input v-model="queryParams.modelId" placeholder="请输入模型id" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="问题编号" prop="questionCode"> + <el-input v-model="queryParams.questionCode" placeholder="请输入问题编号" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="问题名称" prop="questionName"> + <el-input v-model="queryParams.questionName" placeholder="请输入问题名称" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="输入个数" prop="dataLength"> + <el-input v-model="queryParams.dataLength" placeholder="请输入输入个数" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="是否启用(0禁用 1启用)" prop="isEnable"> + <el-input v-model="queryParams.isEnable" placeholder="请输入是否启用(0禁用 1启用)" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="备注" prop="remark"> + <el-input v-model="queryParams.remark" placeholder="请输入备注" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="创建时间" prop="createDate"> + <el-date-picker v-model="queryParams.createDate" style="width: 240px" + value-format="yyyy-MM-dd HH:mm:ss" type="daterange" + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" + :default-time="['00:00:00', '23:59:59']"/> + </el-form-item> + <el-form-item label="更新者" prop="updator"> + <el-input v-model="queryParams.updator" placeholder="请输入更新者" clearable + @keyup.enter.native="handleQuery"/> + </el-form-item> + <el-form-item label="更新时间" prop="updateDate"> + <el-date-picker v-model="queryParams.updateDate" style="width: 240px" + value-format="yyyy-MM-dd HH:mm:ss" type="daterange" + range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" + :default-time="['00:00:00', '23:59:59']"/> + </el-form-item> + <el-form-item> + <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button> + <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button> + </el-form-item> + </el-form> + + <!-- 操作工具栏 --> + <el-row :gutter="10" class="mb8"> + <el-col :span="1.5"> + <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="openForm(undefined)" + v-hasPermi="['ai:question-template:create']">新增 + </el-button> + </el-col> + <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> + </el-row> + + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="id" align="center" prop="id"/> + <el-table-column label="模型id" align="center" prop="modelId"/> + <el-table-column label="问题编号" align="center" prop="questionCode"/> + <el-table-column label="问题名称" align="center" prop="questionName"/> + <el-table-column label="问题内容" align="center" prop="questionContent"/> + <el-table-column label="输入个数" align="center" prop="dataLength"/> + <el-table-column label="是否启用(0禁用 1启用)" align="center" prop="isEnable"/> + <el-table-column label="备注" align="center" prop="remark"/> + <el-table-column label="创建时间" align="center" prop="createDate" width="180"> + <template v-slot="scope"> + <span>{{ parseTime(scope.row.createDate) }}</span> + </template> + </el-table-column> + <el-table-column label="更新者" align="center" prop="updator"/> + <el-table-column label="更新时间" align="center" prop="updateDate" width="180"> + <template v-slot="scope"> + <span>{{ parseTime(scope.row.updateDate) }}</span> + </template> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> + <template v-slot="scope"> + <el-button size="mini" type="text" icon="el-icon-edit" @click="openForm(scope.row.id)" + v-hasPermi="['ai:question-template:update']">修改 + </el-button> + <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" + v-hasPermi="['ai:question-template:delete']">删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页组件 --> + <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" + :limit.sync="queryParams.pageSize" + @pagination="getList"/> + <!-- 对话框(添加 / 修改) --> + <QuestionTemplateForm ref="formRef" @success="getList"/> + </div> +</template> + +<script> +import * as QuestionTemplateApi from '@/api/ai/questiontemplate'; +import QuestionTemplateForm from './QuestionTemplateForm.vue'; + +export default { + name: "QuestionTemplate", + components: { + QuestionTemplateForm, + }, + data() { + return { + // 遮罩层 + loading: true, + // 显示搜索条件 + showSearch: true, + // 总条数 + total: 0, + // 大模型问题模板列表 + list: [], + // 是否展开,默认全部展开 + isExpandAll: true, + // 重新渲染表格状态 + refreshTable: true, + // 选中行 + currentRow: {}, + // 查询参数 + queryParams: { + pageNo: 1, + pageSize: 10, + modelId: null, + questionCode: null, + questionName: null, + questionContent: null, + dataLength: null, + isEnable: null, + remark: null, + createDate: [], + updator: null, + updateDate: [], + }, + }; + }, + created() { + this.getList(); + }, + methods: { + /** 查询列表 */ + async getList() { + try { + this.loading = true; + const res = await QuestionTemplateApi.getQuestionTemplatePage(this.queryParams); + this.list = res.data.list; + this.total = res.data.total; + } finally { + this.loading = false; + } + }, + /** 搜索按钮操作 */ + handleQuery() { + this.queryParams.pageNo = 1; + this.getList(); + }, + /** 重置按钮操作 */ + resetQuery() { + this.resetForm("queryForm"); + this.handleQuery(); + }, + /** 添加/修改操作 */ + openForm(id) { + this.$refs["formRef"].open(id); + }, + /** 删除按钮操作 */ + async handleDelete(row) { + const id = row.id; + await this.$modal.confirm('是否确认删除大模型问题模板编号为"' + id + '"的数据项?') + try { + await QuestionTemplateApi.deleteQuestionTemplate(id); + await this.getList(); + this.$modal.msgSuccess("删除成功"); + } catch { + } + }, + } +}; +</script> diff --git a/src/views/ai/utils/constants.ts b/src/views/ai/utils/constants.ts new file mode 100644 index 0000000..580c676 --- /dev/null +++ b/src/views/ai/utils/constants.ts @@ -0,0 +1,470 @@ +/** + * Created by 工业互联网平台 + * + * AI 枚举类 + * + * 问题:为什么不放在 src/utils/constants.ts 呢? + * 回答:主要 AI 是可选模块,考虑到独立、解耦,所以放在了 /views/ai/utils/constants.ts + */ + +/** + * AI 平台的枚举 + */ +export const AiPlatformEnum = { + TONG_YI: 'TongYi', // 阿里 + YI_YAN: 'YiYan', // 百度 + DEEP_SEEK: 'DeepSeek', // DeepSeek + ZHI_PU: 'ZhiPu', // 智谱 AI + XING_HUO: 'XingHuo', // 讯飞 + SiliconFlow: 'SiliconFlow', // 硅基流动 + OPENAI: 'OpenAI', + Ollama: 'Ollama', + STABLE_DIFFUSION: 'StableDiffusion', // Stability AI + MIDJOURNEY: 'Midjourney', // Midjourney + SUNO: 'Suno' // Suno AI +} + +export const AiModelTypeEnum = { + CHAT: 1, // 聊天 + IMAGE: 2, // 图像 + VOICE: 3, // 音频 + VIDEO: 4, // 视频 + EMBEDDING: 5, // 向量 + RERANK: 6 // 重排 +} + +export const OtherPlatformEnum: ImageModelVO[] = [ + { + key: AiPlatformEnum.TONG_YI, + name: '通义万相' + }, + { + key: AiPlatformEnum.YI_YAN, + name: '百度千帆' + }, + { + key: AiPlatformEnum.ZHI_PU, + name: '智谱 AI' + }, + { + key: AiPlatformEnum.SiliconFlow, + name: '硅基流动' + } +] + +/** + * AI 图像生成状态的枚举 + */ +export const AiImageStatusEnum = { + IN_PROGRESS: 10, // 进行中 + SUCCESS: 20, // 已完成 + FAIL: 30 // 已失败 +} + +/** + * AI 音乐生成状态的枚举 + */ +export const AiMusicStatusEnum = { + IN_PROGRESS: 10, // 进行中 + SUCCESS: 20, // 已完成 + FAIL: 30 // 已失败 +} + +/** + * AI 写作类型的枚举 + */ +export enum AiWriteTypeEnum { + WRITING = 1, // 撰写 + REPLY // 回复 +} + +// 表格展示对照map +export const AiWriteTypeTableRender = { + [AiWriteTypeEnum.WRITING]: '撰写', + [AiWriteTypeEnum.REPLY]: '回复' +} + +// ========== 【图片 UI】相关的枚举 ========== + +export const ImageHotWords = [ + '中国旗袍', + '古装美女', + '卡通头像', + '机甲战士', + '童话小屋', + '中国长城' +] // 图片热词 + +export const ImageHotEnglishWords = [ + 'Chinese Cheongsam', + 'Ancient Beauty', + 'Cartoon Avatar', + 'Mech Warrior', + 'Fairy Tale Cottage', + 'The Great Wall of China' +] // 图片热词(英文) + +export interface ImageModelVO { + key: string + name: string + image?: string +} + +export const StableDiffusionSamplers: ImageModelVO[] = [ + { + key: 'DDIM', + name: 'DDIM' + }, + { + key: 'DDPM', + name: 'DDPM' + }, + { + key: 'K_DPMPP_2M', + name: 'K_DPMPP_2M' + }, + { + key: 'K_DPMPP_2S_ANCESTRAL', + name: 'K_DPMPP_2S_ANCESTRAL' + }, + { + key: 'K_DPM_2', + name: 'K_DPM_2' + }, + { + key: 'K_DPM_2_ANCESTRAL', + name: 'K_DPM_2_ANCESTRAL' + }, + { + key: 'K_EULER', + name: 'K_EULER' + }, + { + key: 'K_EULER_ANCESTRAL', + name: 'K_EULER_ANCESTRAL' + }, + { + key: 'K_HEUN', + name: 'K_HEUN' + }, + { + key: 'K_LMS', + name: 'K_LMS' + } +] + +export const StableDiffusionStylePresets: ImageModelVO[] = [ + { + key: '3d-model', + name: '3d-model' + }, + { + key: 'analog-film', + name: 'analog-film' + }, + { + key: 'anime', + name: 'anime' + }, + { + key: 'cinematic', + name: 'cinematic' + }, + { + key: 'comic-book', + name: 'comic-book' + }, + { + key: 'digital-art', + name: 'digital-art' + }, + { + key: 'enhance', + name: 'enhance' + }, + { + key: 'fantasy-art', + name: 'fantasy-art' + }, + { + key: 'isometric', + name: 'isometric' + }, + { + key: 'line-art', + name: 'line-art' + }, + { + key: 'low-poly', + name: 'low-poly' + }, + { + key: 'modeling-compound', + name: 'modeling-compound' + }, + // neon-punk origami photographic pixel-art tile-texture + { + key: 'neon-punk', + name: 'neon-punk' + }, + { + key: 'origami', + name: 'origami' + }, + { + key: 'photographic', + name: 'photographic' + }, + { + key: 'pixel-art', + name: 'pixel-art' + }, + { + key: 'tile-texture', + name: 'tile-texture' + } +] + +export const StableDiffusionClipGuidancePresets: ImageModelVO[] = [ + { + key: 'NONE', + name: 'NONE' + }, + { + key: 'FAST_BLUE', + name: 'FAST_BLUE' + }, + { + key: 'FAST_GREEN', + name: 'FAST_GREEN' + }, + { + key: 'SIMPLE', + name: 'SIMPLE' + }, + { + key: 'SLOW', + name: 'SLOW' + }, + { + key: 'SLOWER', + name: 'SLOWER' + }, + { + key: 'SLOWEST', + name: 'SLOWEST' + } +] + +export const Dall3Models: ImageModelVO[] = [ + { + key: 'dall-e-3', + name: 'DALL·E 3', + image: `/src/assets/ai/dall2.jpg` + }, + { + key: 'dall-e-2', + name: 'DALL·E 2', + image: `/src/assets/ai/dall3.jpg` + } +] + +export const Dall3StyleList: ImageModelVO[] = [ + { + key: 'vivid', + name: '清晰', + image: `/src/assets/ai/qingxi.jpg` + }, + { + key: 'natural', + name: '自然', + image: `/src/assets/ai/ziran.jpg` + } +] + +export interface ImageSizeVO { + key: string + name?: string + style: string + width: string + height: string +} + +export const Dall3SizeList: ImageSizeVO[] = [ + { + key: '1024x1024', + name: '1:1', + width: '1024', + height: '1024', + style: 'width: 30px; height: 30px;background-color: #dcdcdc;' + }, + { + key: '1024x1792', + name: '3:5', + width: '1024', + height: '1792', + style: 'width: 30px; height: 50px;background-color: #dcdcdc;' + }, + { + key: '1792x1024', + name: '5:3', + width: '1792', + height: '1024', + style: 'width: 50px; height: 30px;background-color: #dcdcdc;' + } +] + +export const MidjourneyModels: ImageModelVO[] = [ + { + key: 'midjourney', + name: 'MJ', + image: 'https://bigpt8.com/pc/_nuxt/mj.34a61377.png' + }, + { + key: 'niji', + name: 'NIJI', + image: 'https://bigpt8.com/pc/_nuxt/nj.ca79b143.png' + } +] + +export const MidjourneySizeList: ImageSizeVO[] = [ + { + key: '1:1', + width: '1', + height: '1', + style: 'width: 30px; height: 30px;background-color: #dcdcdc;' + }, + { + key: '3:4', + width: '3', + height: '4', + style: 'width: 30px; height: 40px;background-color: #dcdcdc;' + }, + { + key: '4:3', + width: '4', + height: '3', + style: 'width: 40px; height: 30px;background-color: #dcdcdc;' + }, + { + key: '9:16', + width: '9', + height: '16', + style: 'width: 30px; height: 50px;background-color: #dcdcdc;' + }, + { + key: '16:9', + width: '16', + height: '9', + style: 'width: 50px; height: 30px;background-color: #dcdcdc;' + } +] + +export const MidjourneyVersions = [ + { + value: '6.0', + label: 'v6.0' + }, + { + value: '5.2', + label: 'v5.2' + }, + { + value: '5.1', + label: 'v5.1' + }, + { + value: '5.0', + label: 'v5.0' + }, + { + value: '4.0', + label: 'v4.0' + } +] + +export const NijiVersionList = [ + { + value: '5', + label: 'v5' + } +] + +// ========== 【写作 UI】相关的枚举 ========== + +/** 写作点击示例时的数据 **/ +export const WriteExample = { + write: { + prompt: 'vue', + data: 'Vue.js 是一种用于构建用户界面的渐进式 JavaScript 框架。它的核心库只关注视图层,易于上手,同时也便于与其他库或已有项目整合。\n\nVue.js 的特点包括:\n- 响应式的数据绑定:Vue.js 会自动将数据与 DOM 同步,使得状态管理变得更加简单。\n- 组件化:Vue.js 允许开发者通过小型、独立和通常可复用的组件构建大型应用。\n- 虚拟 DOM:Vue.js 使用虚拟 DOM 实现快速渲染,提高了性能。\n\n在 Vue.js 中,一个典型的应用结构可能包括:\n1. 根实例:每个 Vue 应用都需要一个根实例作为入口点。\n2. 组件系统:可以创建自定义的可复用组件。\n3. 指令:特殊的带有前缀 v- 的属性,为 DOM 元素提供特殊的行为。\n4. 插值:用于文本内容,将数据动态地插入到 HTML。\n5. 计算属性和侦听器:用于处理数据的复杂逻辑和响应数据变化。\n6. 条件渲染:根据条件决定元素的渲染。\n7. 列表渲染:用于显示列表数据。\n8. 事件处理:响应用户交互。\n9. 表单输入绑定:处理表单输入和验证。\n10. 组件生命周期钩子:在组件的不同阶段执行特定的函数。\n\nVue.js 还提供了官方的路由器 Vue Router 和状态管理库 Vuex,以支持构建复杂的单页应用(SPA)。\n\n在开发过程中,开发者通常会使用 Vue CLI,这是一个强大的命令行工具,用于快速生成 Vue 项目脚手架,集成了诸如 Babel、Webpack 等现代前端工具,以及热重载、代码检测等开发体验优化功能。\n\nVue.js 的生态系统还包括大量的第三方库和插件,如 Vuetify(UI 组件库)、Vue Test Utils(测试工具)等,这些都极大地丰富了 Vue.js 的开发生态。\n\n总的来说,Vue.js 是一个灵活、高效的前端框架,适合从小型项目到大型企业级应用的开发。它的易用性、灵活性和强大的社区支持使其成为许多开发者的首选框架之一。' + }, + reply: { + originalContent: '领导,我想请假', + prompt: '不批', + data: '您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务,请及时与我联系。\n\n祝工作顺利。\n\n谢谢。' + } +} + +// ========== 【思维导图 UI】相关的枚举 ========== + +/** 思维导图已有内容生成示例 **/ +export const MindMapContentExample = `# Java 技术栈 + +## 核心技术 +### Java SE +### Java EE + +## 框架 +### Spring +#### Spring Boot +#### Spring MVC +#### Spring Data +### Hibernate +### MyBatis + +## 构建工具 +### Maven +### Gradle + +## 版本控制 +### Git +### SVN + +## 测试工具 +### JUnit +### Mockito +### Selenium + +## 应用服务器 +### Tomcat +### Jetty +### WildFly + +## 数据库 +### MySQL +### PostgreSQL +### Oracle +### MongoDB + +## 消息队列 +### Kafka +### RabbitMQ +### ActiveMQ + +## 微服务 +### Spring Cloud +### Dubbo + +## 容器化 +### Docker +### Kubernetes + +## 云服务 +### AWS +### Azure +### Google Cloud + +## 开发工具 +### IntelliJ IDEA +### Eclipse +### Visual Studio Code` diff --git a/src/views/ai/utils/utils.ts b/src/views/ai/utils/utils.ts new file mode 100644 index 0000000..ef36350 --- /dev/null +++ b/src/views/ai/utils/utils.ts @@ -0,0 +1,13 @@ +/** + * Created by 工业互联网平台 + * + * AI 枚举类 + * + * 问题:为什么不放在 src/utils/common-utils.ts 呢? + * 回答:主要 AI 是可选模块,考虑到独立、解耦,所以放在了 /views/ai/utils/common-utils.ts + */ + +/** 判断字符串是否包含中文 */ +export const hasChinese = (str: string) => { + return /[\u4e00-\u9fa5]/.test(str) +} diff --git a/src/views/ai/workflow/form/BasicInfo.vue b/src/views/ai/workflow/form/BasicInfo.vue new file mode 100644 index 0000000..6b5426c --- /dev/null +++ b/src/views/ai/workflow/form/BasicInfo.vue @@ -0,0 +1,54 @@ +<template> + <el-form ref="formRef" :model="modelData" :rules="formRules" label-width="120px"> + <el-row> + <el-col :span="24"> + <el-form-item label="流程标识" prop="code"> + <el-input v-model="modelData.code" placeholder="请输入流程标识" /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="流程名称" prop="name"> + <el-input v-model="modelData.name" placeholder="请输入流程名称" /> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="状态" prop="status"> + <el-select v-model="modelData.status" placeholder="请选择状态"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + </el-col> + <el-col :span="24"> + <el-form-item label="备注" prop="remark"> + <el-input v-model="modelData.remark" :rows="2" type="textarea" placeholder="请输入备注" /> + </el-form-item> + </el-col> + </el-row> + </el-form> +</template> +<script lang="ts" setup> +import { FormRules } from 'element-plus' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' + +const modelData = defineModel<any>() + +const formRef = ref() // 表单 Ref +const formRules = reactive<FormRules>({ + code: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }], + name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }], + status: [{ required: true, message: '状态不能为空', trigger: 'change' }] +}) + +/** 表单校验 */ +const validate = async () => { + await formRef.value?.validate() +} +defineExpose({ + validate +}) +</script> diff --git a/src/views/ai/workflow/form/WorkflowDesign.vue b/src/views/ai/workflow/form/WorkflowDesign.vue new file mode 100644 index 0000000..3697319 --- /dev/null +++ b/src/views/ai/workflow/form/WorkflowDesign.vue @@ -0,0 +1,49 @@ +<template> + <div class="relative" style="width: 100%; height: 700px"> + <Tinyflow + v-if="workflowData" + ref="tinyflowRef" + :className="'custom-class'" + :style="{ width: '100%', height: '100%' }" + :data="workflowData" + :provider="provider" + /> + <div class="absolute top-30px right-30px"> + <el-button @click="testWorkflowModel" type="primary" v-hasPermi="['ai:workflow:test']"> + 测试 + </el-button> + </div> + </div> +</template> + +<script setup lang="ts"> +import Tinyflow from '@/components/Tinyflow/Tinyflow.vue' + +defineProps<{ + provider: any +}>() + +const tinyflowRef = ref() +const workflowData = inject('workflowData') as Ref + +const testWorkflowModel = () => { + // TODO @lesan 测试 +} + +/** 表单校验 */ +const validate = async () => { + try { + // 获取最新的流程数据 + if (!workflowData.value) { + throw new Error('请设计流程') + } + workflowData.value = tinyflowRef.value.getData() + return true + } catch (error) { + throw error + } +} +defineExpose({ + validate +}) +</script> diff --git a/src/views/ai/workflow/form/index.vue b/src/views/ai/workflow/form/index.vue new file mode 100644 index 0000000..ee2bd56 --- /dev/null +++ b/src/views/ai/workflow/form/index.vue @@ -0,0 +1,235 @@ +<template> + <ContentWrap> + <div class="mx-auto"> + <!-- 头部导航栏 --> + <div + class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px" + > + <!-- 左侧标题 --> + <div class="w-200px flex items-center overflow-hidden"> + <Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="handleBack" /> + <span class="ml-10px text-16px truncate" :title="formData.name || '创建流程'"> + {{ formData.name || '创建流程' }} + </span> + </div> + + <!-- 步骤条 --> + <div class="flex-1 flex items-center justify-center h-full"> + <div class="w-400px flex items-center justify-between h-full"> + <div + v-for="(step, index) in steps" + :key="index" + class="flex items-center cursor-pointer mx-15px relative h-full" + :class="[ + currentStep === index + ? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid' + : 'text-gray-500' + ]" + @click="handleStepClick(index)" + > + <div + class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px" + :class="[ + currentStep === index + ? 'bg-[#3473ff] text-white border-[#3473ff]' + : 'border-gray-300 bg-white text-gray-500' + ]" + > + {{ index + 1 }} + </div> + <span class="text-16px font-bold whitespace-nowrap">{{ step.title }}</span> + </div> + </div> + </div> + + <!-- 右侧按钮 --> + <div class="w-200px flex items-center justify-end gap-2"> + <el-button type="primary" @click="handleSave"> 保 存 </el-button> + </div> + </div> + + <!-- 主体内容 --> + <div class="mt-50px"> + <!-- 第一步:基本信息 --> + <div v-if="currentStep === 0" class="mx-auto w-560px"> + <BasicInfo v-model="formData" ref="basicInfoRef" /> + </div> + + <!-- 第二步:工作流设计 --> + <WorkflowDesign + v-if="currentStep === 1" + v-model="formData" + :provider="provider" + ref="workflowDesignRef" + /> + </div> + </div> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { useTagsViewStore } from '@/store/modules/tagsView' +import { CommonStatusEnum } from '@/utils/constants' +import * as WorkflowApi from '@/api/ai/workflow' +import BasicInfo from './BasicInfo.vue' +import WorkflowDesign from './WorkflowDesign.vue' +import { ApiKeyApi } from '@/api/ai/model/apiKey' + +const router = useRouter() +const { delView } = useTagsViewStore() +const route = useRoute() +const message = useMessage() + +const basicInfoRef = ref() +const workflowDesignRef = ref() + +const validateBasic = async () => { + await basicInfoRef.value?.validate() +} +const validateWorkflow = async () => { + await workflowDesignRef.value?.validate() +} + +const currentStep = ref(-1) +const steps = [ + { title: '基本信息', validator: validateBasic }, + { title: '工作流设计', validator: validateWorkflow } +] + +const formData: any = ref({ + id: undefined, + name: '', + code: '', + remark: '', + graph: '', + status: CommonStatusEnum.ENABLE +}) +// TODO @lesan:待接入 +const provider = ref<any>() +const workflowData = ref<any>({}) +provide('workflowData', workflowData) + +/** 初始化数据 */ +const actionType = route.params.type as string +const initData = async () => { + if (actionType === 'update') { + const workflowId = route.params.id as string + formData.value = await WorkflowApi.getWorkflow(workflowId) + workflowData.value = JSON.parse(formData.value.graph) + } + + const apiKeys = await ApiKeyApi.getApiKeySimpleList() + provider.value = { + llm: () => + apiKeys.map(({ id, name }) => ({ + value: id, + label: name + })), + knowledge: () => [], + internal: () => [] + } + + currentStep.value = 0 +} + +/** 校验所有步骤数据是否完整 */ +const validateAllSteps = async () => { + try { + // 基本信息校验 + try { + await validateBasic() + } catch (error) { + currentStep.value = 0 + throw new Error('请完善基本信息') + } + + // 工作流设计校验 + try { + await validateWorkflow() + } catch (error) { + currentStep.value = 1 + throw new Error('请完善工作流信息') + } + return true + } catch (error) { + throw error + } +} + +/** 保存操作 */ +const handleSave = async () => { + try { + // 保存前校验所有步骤的数据 + await validateAllSteps() + + // 更新表单数据 + const data = { + ...formData.value + } + + data.graph = JSON.stringify(workflowData.value) + + if (actionType === 'update') { + await WorkflowApi.updateWorkflow(data) + } else { + await WorkflowApi.createWorkflow(data) + } + + delView(unref(router.currentRoute)) + await router.push({ name: 'AiWorkflow' }) + } catch (error: any) { + console.error('保存失败:', error) + message.warning(error.message || '请完善所有步骤的必填信息') + } +} + +/** 步骤切换处理 */ +const handleStepClick = async (index: number) => { + try { + if (index !== 0) { + await validateBasic() + } + if (index !== 1) { + await validateWorkflow() + } + + // 切换步骤 + currentStep.value = index + } catch (error) { + console.error('步骤切换失败:', error) + message.warning('请先完善当前步骤必填信息') + } +} + +/** 返回列表页 */ +const handleBack = () => { + // 先删除当前页签 + delView(unref(router.currentRoute)) + // 跳转到列表页 + router.push({ name: 'AiWorkflow' }) +} + +/** 初始化 */ +onMounted(async () => { + await initData() +}) +</script> + +<!-- TODO @lesan:可以用 cursor 搞成 unocss 哈 --> +<style lang="scss" scoped> +.border-bottom { + border-bottom: 1px solid #dcdfe6; +} + +.text-primary { + color: #3473ff; +} + +.bg-primary { + background-color: #3473ff; +} + +.border-primary { + border-color: #3473ff; +} +</style> diff --git a/src/views/ai/workflow/index.vue b/src/views/ai/workflow/index.vue new file mode 100644 index 0000000..6a874c7 --- /dev/null +++ b/src/views/ai/workflow/index.vue @@ -0,0 +1,193 @@ +<template> + <!-- 搜索工作栏 --> + <ContentWrap> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="流程标识" prop="code"> + <el-input + v-model="queryParams.code" + placeholder="请输入流程标识" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="流程名称" prop="name"> + <el-input + v-model="queryParams.name" + placeholder="请输入流程名称" + clearable + @keyup.enter="handleQuery" + class="!w-240px" + /> + </el-form-item> + <el-form-item label="状态" prop="status"> + <el-select v-model="queryParams.status" placeholder="状态" clearable class="!w-240px"> + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['ai:workflow:create']" + > + <Icon icon="ep:plus" /> 新增 + </el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" /> + <el-table-column label="流程标识" align="center" prop="code" /> + <el-table-column label="流程名称" align="center" prop="name" /> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + /> + <el-table-column label="备注" align="center" prop="remark" /> + <el-table-column label="状态" align="center" key="status"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" /> + </template> + </el-table-column> + <el-table-column label="操作" align="center" fixed="right"> + <template #default="scope"> + <el-button + type="primary" + link + @click="openForm('update', scope.row.id)" + v-hasPermi="['ai:workflow:update']" + > + 修改 + </el-button> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:workflow:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> + + <!-- 添加或修改工作流对话框 --> +</template> + +<script setup lang="ts"> +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import * as WorkflowApi from '@/api/ai/workflow' +import { dateFormatter } from '@/utils/formatTime' + +defineOptions({ name: 'AiWorkflow' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 +const { push } = useRouter() // 路由 + +const loading = ref(true) // 列表的加载中 +const list = ref([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive({ + pageNo: 1, + pageSize: 10, + code: '', + name: '', + status: undefined, + createTime: [] +}) +const queryFormRef = ref() // 搜索的表单 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await WorkflowApi.getWorkflowPage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await WorkflowApi.deleteWorkflow(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 添加/修改操作 */ +const openForm = async (type: string, id?: number) => { + if (type === 'create') { + await push({ name: 'AiWorkflowCreate' }) + } else { + await push({ + name: 'AiWorkflowUpdate', + params: { id, type } + }) + } +} + +/** 初始化 **/ +onMounted(() => { + getList() +}) +</script> diff --git a/src/views/ai/write/index/components/Left.vue b/src/views/ai/write/index/components/Left.vue new file mode 100644 index 0000000..74e5d58 --- /dev/null +++ b/src/views/ai/write/index/components/Left.vue @@ -0,0 +1,213 @@ +<template> + <!-- 定义 tab 组件:撰写/回复等 --> + <DefineTab v-slot="{ active, text, itemClick }"> + <span + :class="active ? 'text-black shadow-md' : 'hover:bg-[#DDDFE3]'" + class="inline-block w-1/2 rounded-full cursor-pointer text-center leading-[30px] relative z-1 text-[5C6370] hover:text-black" + @click="itemClick" + > + {{ text }} + </span> + </DefineTab> + <!-- 定义 label 组件:长度/格式/语气/语言等 --> + <DefineLabel v-slot="{ label, hint, hintClick }"> + <h3 class="mt-5 mb-3 flex items-center justify-between text-[14px]"> + <span>{{ label }}</span> + <span + v-if="hint" + class="flex items-center text-[12px] text-[#846af7] cursor-pointer select-none" + @click="hintClick" + > + <Icon icon="ep:question-filled" /> + {{ hint }} + </span> + </h3> + </DefineLabel> + + <div class="flex flex-col" v-bind="$attrs"> + <!-- tab --> + <div class="w-full pt-2 bg-[#f5f7f9] flex justify-center"> + <div class="w-[303px] rounded-full bg-[#DDDFE3] p-1 z-10"> + <div + :class=" + selectedTab === AiWriteTypeEnum.REPLY && 'after:transform after:translate-x-[100%]' + " + class="flex items-center relative after:content-[''] after:block after:bg-white after:h-[30px] after:w-1/2 after:absolute after:top-0 after:left-0 after:transition-transform after:rounded-full" + > + <ReuseTab + v-for="tab in tabs" + :key="tab.value" + :active="tab.value === selectedTab" + :itemClick="() => switchTab(tab.value)" + :text="tab.text" + /> + </div> + </div> + </div> + <div + class="px-7 pb-2 flex-grow overflow-y-auto lg:block w-[380px] box-border bg-[#f5f7f9] h-full" + > + <div> + <template v-if="selectedTab === 1"> + <ReuseLabel :hint-click="() => example('write')" hint="示例" label="写作内容" /> + <el-input + v-model="formData.prompt" + :maxlength="500" + :rows="5" + placeholder="请输入写作内容" + showWordLimit + type="textarea" + /> + </template> + + <template v-else> + <ReuseLabel :hint-click="() => example('reply')" hint="示例" label="原文" /> + <el-input + v-model="formData.originalContent" + :maxlength="500" + :rows="5" + placeholder="请输入原文" + showWordLimit + type="textarea" + /> + + <ReuseLabel label="回复内容" /> + <el-input + v-model="formData.prompt" + :maxlength="500" + :rows="5" + placeholder="请输入回复内容" + showWordLimit + type="textarea" + /> + </template> + + <ReuseLabel label="长度" /> + <Tag v-model="formData.length" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_LENGTH)" /> + <ReuseLabel label="格式" /> + <Tag v-model="formData.format" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_FORMAT)" /> + <ReuseLabel label="语气" /> + <Tag v-model="formData.tone" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_TONE)" /> + <ReuseLabel label="语言" /> + <Tag v-model="formData.language" :tags="getIntDictOptions(DICT_TYPE.AI_WRITE_LANGUAGE)" /> + + <div class="flex items-center justify-center mt-3"> + <el-button :disabled="isWriting" @click="reset">重置</el-button> + <el-button :loading="isWriting" color="#846af7" @click="submit">生成</el-button> + </div> + </div> + </div> + </div> +</template> + +<script lang="ts" setup> +import { createReusableTemplate } from '@vueuse/core' +import { ref } from 'vue' +import Tag from './Tag.vue' +import { WriteVO } from '@/api/ai/write' +import { omit } from 'lodash-es' +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' +import { AiWriteTypeEnum, WriteExample } from '@/views/ai/utils/constants' + +type TabType = WriteVO['type'] + +const message = useMessage() // 消息弹窗 + +defineProps<{ + isWriting: boolean +}>() + +const emits = defineEmits<{ + (e: 'submit', params: Partial<WriteVO>) + (e: 'example', param: 'write' | 'reply') + (e: 'reset') +}>() + +/** 点击示例的时候,将定义好的文章作为示例展示出来 **/ +const example = (type: 'write' | 'reply') => { + formData.value = { + ...initData, + ...omit(WriteExample[type], ['data']) + } + emits('example', type) +} + +/** 重置,将表单值作为初选值 **/ +const reset = () => { + formData.value = { ...initData } + emits('reset') +} + +const selectedTab = ref<TabType>(AiWriteTypeEnum.WRITING) +const tabs: { + text: string + value: TabType +}[] = [ + { text: '撰写', value: AiWriteTypeEnum.WRITING }, + { text: '回复', value: AiWriteTypeEnum.REPLY } +] +const [DefineTab, ReuseTab] = createReusableTemplate<{ + active?: boolean + text: string + itemClick: () => void +}>() + +/** + * 可以在 template 里边定义可复用的组件,DefineLabel,ReuseLabel 是采用的解构赋值,都是 Vue 组件 + * + * 直接通过组件的形式使用,<DefineLabel v-slot="{ label, hint, hintClick }"> 中间是需要复用的组件代码 <DefineLabel />,通过 <ReuseLabel /> 来使用定义的组件 + * DefineLabel 里边的 v-slot="{ label, hint, hintClick }"相当于是解构了组件的 prop,需要注意的是 boolean 类型,需要显式的赋值比如 <ReuseLabel :flag="true" /> + * 事件也得以 prop 形式传入,不能是 @event的形式,比如下面的 hintClick 需要<ReuseLabel :hintClick="() => { doSomething }"/> + * + * @see https://vueuse.org/createReusableTemplate + */ +const [DefineLabel, ReuseLabel] = createReusableTemplate<{ + label: string + class?: string + hint?: string + hintClick?: () => void +}>() + +const initData: WriteVO = { + type: 1, + prompt: '', + originalContent: '', + tone: 1, + language: 1, + length: 1, + format: 1 +} +const formData = ref<WriteVO>({ ...initData }) + +/** 用来记录切换之前所填写的数据,切换的时候给赋值回来 **/ +const recordFormData = {} as Record<AiWriteTypeEnum, WriteVO> + +/** 切换tab **/ +const switchTab = (value: TabType) => { + if (value !== selectedTab.value) { + // 保存之前的久数据 + recordFormData[selectedTab.value] = formData.value + selectedTab.value = value + // 将之前的旧数据赋值回来 + formData.value = { ...initData, ...recordFormData[value] } + } +} + +/** 提交写作 */ +const submit = () => { + if (selectedTab.value === 2 && !formData.value.originalContent) { + message.warning('请输入原文') + return + } + if (!formData.value.prompt) { + message.warning(`请输入${selectedTab.value === 1 ? '写作' : '回复'}内容`) + return + } + emits('submit', { + /** 撰写的时候没有 originalContent 字段**/ + ...(selectedTab.value === 1 ? omit(formData.value, ['originalContent']) : formData.value), + /** 使用选中 tab 值覆盖当前的 type 类型 **/ + type: selectedTab.value + }) +} +</script> diff --git a/src/views/ai/write/index/components/Right.vue b/src/views/ai/write/index/components/Right.vue new file mode 100644 index 0000000..d0aada5 --- /dev/null +++ b/src/views/ai/write/index/components/Right.vue @@ -0,0 +1,120 @@ +<template> + <el-card class="my-card h-full"> + <template #header> + <h3 class="m-0 px-7 shrink-0 flex items-center justify-between"> + <span>预览</span> + <!-- 展示在右上角 --> + <el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small"> + <template #icon> + <Icon icon="ph:copy-bold" /> + </template> + 复制 + </el-button> + </h3> + </template> + + <div ref="contentRef" class="hide-scroll-bar h-full box-border overflow-y-auto"> + <div class="w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7"> + <!-- 终止生成内容的按钮 --> + <el-button + v-show="isWriting" + class="absolute bottom-2 sm:bottom-5 left-1/2 -translate-x-1/2 z-36" + @click="emits('stopStream')" + size="small" + > + <template #icon> + <Icon icon="material-symbols:stop" /> + </template> + 终止生成 + </el-button> + <el-input + id="inputId" + type="textarea" + v-model="compContent" + autosize + :input-style="{ boxShadow: 'none' }" + resize="none" + placeholder="生成的内容……" + /> + </div> + </div> + </el-card> +</template> + +<script setup lang="ts"> +import { useClipboard } from '@vueuse/core' + +const message = useMessage() // 消息弹窗 +const { copied, copy } = useClipboard() // 粘贴板 + +const props = defineProps({ + content: { + // 生成的结果 + type: String, + default: '' + }, + isWriting: { + // 是否正在生成文章 + type: Boolean, + default: false + } +}) + +const emits = defineEmits(['update:content', 'stopStream']) + +/** 通过计算属性,双向绑定,更改生成的内容,考虑到用户想要更改生成文章的情况 */ +const compContent = computed({ + get() { + return props.content + }, + set(val) { + emits('update:content', val) + } +}) + +/** 滚动 */ +const contentRef = ref<HTMLDivElement>() +defineExpose({ + scrollToBottom() { + contentRef.value?.scrollTo(0, contentRef.value?.scrollHeight) + } +}) + +/** 点击复制的时候复制内容 */ +const showCopy = computed(() => props.content && !props.isWriting) // 是否展示复制按钮,在生成内容完成的时候展示 +const copyContent = () => { + copy(props.content) +} + +/** 复制成功的时候 copied.value 为 true */ +watch(copied, (val) => { + if (val) { + message.success('复制成功') + } +}) +</script> + +<style lang="scss" scoped> +.hide-scroll-bar { + -ms-overflow-style: none; + scrollbar-width: none; + + &::-webkit-scrollbar { + width: 0; + height: 0; + } +} + +.my-card { + display: flex; + flex-direction: column; + + :deep(.el-card__body) { + box-sizing: border-box; + flex-grow: 1; + overflow-y: auto; + padding: 0; + @extend .hide-scroll-bar; + } +} +</style> diff --git a/src/views/ai/write/index/components/Tag.vue b/src/views/ai/write/index/components/Tag.vue new file mode 100644 index 0000000..3d616be --- /dev/null +++ b/src/views/ai/write/index/components/Tag.vue @@ -0,0 +1,32 @@ +<!-- 标签选项 --> +<template> + <div class="flex flex-wrap gap-[8px]"> + <span + v-for="tag in props.tags" + :key="tag.value" + class="tag mb-2 border-[2px] border-solid border-[#DDDFE3] px-2 leading-6 text-[12px] bg-[#DDDFE3] rounded-[4px] cursor-pointer" + :class="modelValue === tag.value && '!border-[#846af7] text-[#846af7]'" + @click="emits('update:modelValue', tag.value)" + > + {{ tag.label }} + </span> + </div> +</template> + +<script setup lang="ts"> +const props = withDefaults( + defineProps<{ + tags: { label: string; value: string }[] + modelValue: string + [k: string]: any + }>(), + { + tags: () => [] + } +) + +const emits = defineEmits<{ + (e: 'update:modelValue', value: string): void +}>() +</script> +<style scoped></style> diff --git a/src/views/ai/write/index/index.vue b/src/views/ai/write/index/index.vue new file mode 100644 index 0000000..0dfda74 --- /dev/null +++ b/src/views/ai/write/index/index.vue @@ -0,0 +1,76 @@ +<template> + <div class="absolute top-0 left-0 right-0 bottom-0 flex"> + <Left + :is-writing="isWriting" + class="h-full" + @submit="submit" + @reset="reset" + @example="handleExampleClick" + /> + <Right + :is-writing="isWriting" + @stop-stream="stopStream" + ref="rightRef" + class="flex-grow" + v-model:content="writeResult" + /> + </div> +</template> + +<script setup lang="ts"> +import Left from './components/Left.vue' +import Right from './components/Right.vue' +import { WriteApi, WriteVO } from '@/api/ai/write' +import { WriteExample } from '@/views/ai/utils/constants' + +const message = useMessage() + +const writeResult = ref('') // 写作结果 +const isWriting = ref(false) // 是否正在写作中 +const abortController = ref<AbortController>() // // 写作进行中 abort 控制器(控制 stream 写作) + +/** 停止 stream 生成 */ +const stopStream = () => { + abortController.value?.abort() + isWriting.value = false +} + +/** 执行写作 */ +const rightRef = ref<InstanceType<typeof Right>>() +const submit = (data: WriteVO) => { + abortController.value = new AbortController() + writeResult.value = '' + isWriting.value = true + WriteApi.writeStream({ + data, + onMessage: async (res) => { + const { code, data, msg } = JSON.parse(res.data) + if (code !== 0) { + message.alert(`写作异常! ${msg}`) + stopStream() + return + } + writeResult.value = writeResult.value + data + // 滚动到底部 + await nextTick() + rightRef.value?.scrollToBottom() + }, + ctrl: abortController.value, + onClose: stopStream, + onError: (...err) => { + console.error('写作异常', ...err) + stopStream() + } + }) +} + +/** 点击示例触发 */ +const handleExampleClick = (type: keyof typeof WriteExample) => { + writeResult.value = WriteExample[type].data +} + +/** 点击重置的时候清空写作的结果**/ +const reset = () => { + writeResult.value = '' +} +</script> diff --git a/src/views/ai/write/manager/index.vue b/src/views/ai/write/manager/index.vue new file mode 100644 index 0000000..cbe8902 --- /dev/null +++ b/src/views/ai/write/manager/index.vue @@ -0,0 +1,226 @@ +<template> + + <ContentWrap> + <!-- 搜索工作栏 --> + <el-form + class="-mb-15px" + :model="queryParams" + ref="queryFormRef" + :inline="true" + label-width="68px" + > + <el-form-item label="用户编号" prop="userId"> + <el-select + v-model="queryParams.userId" + clearable + placeholder="请输入用户编号" + class="!w-240px" + > + <el-option + v-for="item in userList" + :key="item.id" + :label="item.nickname" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="写作类型" prop="type"> + <el-select + v-model="queryParams.type" + placeholder="请选择写作类型" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getIntDictOptions(DICT_TYPE.AI_WRITE_TYPE)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="平台" prop="platform"> + <el-select + v-model="queryParams.platform" + placeholder="请选择平台" + clearable + class="!w-240px" + > + <el-option + v-for="dict in getStrDictOptions(DICT_TYPE.AI_PLATFORM)" + :key="dict.value" + :label="dict.label" + :value="dict.value" + /> + </el-select> + </el-form-item> + <el-form-item label="创建时间" prop="createTime"> + <el-date-picker + v-model="queryParams.createTime" + value-format="YYYY-MM-DD HH:mm:ss" + type="daterange" + start-placeholder="开始日期" + end-placeholder="结束日期" + :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" + class="!w-240px" + /> + </el-form-item> + <el-form-item> + <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button> + <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button> + </el-form-item> + </el-form> + </ContentWrap> + + <!-- 列表 --> + <ContentWrap> + <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> + <el-table-column label="编号" align="center" prop="id" width="120" fixed="left" /> + <el-table-column label="用户" align="center" prop="userId" width="180"> + <template #default="scope"> + <span>{{ userList.find((item) => item.id === scope.row.userId)?.nickname }}</span> + </template> + </el-table-column> + <el-table-column label="写作类型" align="center" prop="type"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_WRITE_TYPE" :value="scope.row.type" /> + </template> + </el-table-column> + <el-table-column label="平台" align="center" prop="platform" width="120"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_PLATFORM" :value="scope.row.platform" /> + </template> + </el-table-column> + <el-table-column label="模型" align="center" prop="model" width="180" /> + <el-table-column + label="生成内容提示" + align="center" + prop="prompt" + width="180" + show-overflow-tooltip + /> + <el-table-column label="生成的内容" align="center" prop="generatedContent" width="180" /> + <el-table-column label="原文" align="center" prop="originalContent" width="180" /> + <el-table-column label="长度" align="center" prop="length"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_WRITE_LENGTH" :value="scope.row.length" /> + </template> + </el-table-column> + <el-table-column label="格式" align="center" prop="format"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_WRITE_FORMAT" :value="scope.row.format" /> + </template> + </el-table-column> + <el-table-column label="语气" align="center" prop="tone"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_WRITE_TONE" :value="scope.row.tone" /> + </template> + </el-table-column> + <el-table-column label="语言" align="center" prop="language"> + <template #default="scope"> + <dict-tag :type="DICT_TYPE.AI_WRITE_LANGUAGE" :value="scope.row.language" /> + </template> + </el-table-column> + <el-table-column + label="创建时间" + align="center" + prop="createTime" + :formatter="dateFormatter" + width="180px" + /> + <el-table-column label="错误信息" align="center" prop="errorMessage" /> + <el-table-column label="操作" align="center"> + <template #default="scope"> + <el-button + link + type="danger" + @click="handleDelete(scope.row.id)" + v-hasPermi="['ai:write:delete']" + > + 删除 + </el-button> + </template> + </el-table-column> + </el-table> + <!-- 分页 --> + <Pagination + :total="total" + v-model:page="queryParams.pageNo" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </ContentWrap> +</template> + +<script setup lang="ts"> +import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict' +import { dateFormatter } from '@/utils/formatTime' +import { useRouter } from 'vue-router' +import { WriteApi, AiWritePageReqVO, AiWriteRespVo } from '@/api/ai/write' +import * as UserApi from '@/api/system/user' + +/** AI 写作列表 */ +defineOptions({ name: 'AiWriteManager' }) + +const message = useMessage() // 消息弹窗 +const { t } = useI18n() // 国际化 +const router = useRouter() // 路由 + +const loading = ref(true) // 列表的加载中 +const list = ref<AiWriteRespVo[]>([]) // 列表的数据 +const total = ref(0) // 列表的总页数 +const queryParams = reactive<AiWritePageReqVO>({ + pageNo: 1, + pageSize: 10, + userId: undefined, + type: undefined, + platform: undefined, + createTime: undefined +}) +const queryFormRef = ref() // 搜索的表单 +const userList = ref<UserApi.UserVO[]>([]) // 用户列表 + +/** 查询列表 */ +const getList = async () => { + loading.value = true + try { + const data = await WriteApi.getWritePage(queryParams) + list.value = data.list + total.value = data.total + } finally { + loading.value = false + } +} + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.pageNo = 1 + getList() +} + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value.resetFields() + handleQuery() +} + +/** 删除按钮操作 */ +const handleDelete = async (id: number) => { + try { + // 删除的二次确认 + await message.delConfirm() + // 发起删除 + await WriteApi.deleteWrite(id) + message.success(t('common.delSuccess')) + // 刷新列表 + await getList() + } catch {} +} + +/** 初始化 **/ +onMounted(async () => { + getList() + // 获得用户列表 + userList.value = await UserApi.getSimpleUserList() +}) +</script> diff --git a/src/views/infra/monitor/components/MonitorDisk.vue b/src/views/infra/monitor/components/MonitorDisk.vue index ffec629..3a0c954 100644 --- a/src/views/infra/monitor/components/MonitorDisk.vue +++ b/src/views/infra/monitor/components/MonitorDisk.vue @@ -8,41 +8,25 @@ :inline="true" label-width="68px" > - <!-- <el-form-item label="主机名称" prop="hostName">--> - <!-- <el-input--> - <!-- v-model="queryParams.hostName"--> - <!-- placeholder="请输入主机名称"--> - <!-- clearable--> - <!-- @keyup.enter="handleQuery"--> - <!-- class="!w-240px"--> - <!-- />--> - <!-- </el-form-item>--> - <el-form-item label="服务器IP" prop="hostIp"> - <el-input - v-model="queryParams.hostIp" - placeholder="请输入服务器IP" - clearable - @keyup.enter="handleQuery" - class="!w-120px" - /> + <el-form-item label="主机名称" prop="hostName"> + <el-select v-model="queryParams.hostName" clearable placeholder="请选择" class="!w-180px" @change="getDataList"> + <el-option + v-for="(host, index) in hosts" + :key="index" + :label="host" + :value="host" + /> + </el-select> </el-form-item> - <!-- <el-form-item label="盘符" prop="disk">--> - <!-- <el-input--> - <!-- v-model="queryParams.disk"--> - <!-- placeholder="请输入盘符"--> - <!-- clearable--> - <!-- @keyup.enter="handleQuery"--> - <!-- class="!w-240px"--> - <!-- />--> - <!-- </el-form-item>--> - <el-form-item label="磁盘名" prop="diskName"> - <el-input - v-model="queryParams.diskName" - placeholder="请输入磁盘名" - clearable - @keyup.enter="handleQuery" - class="!w-120px" - /> + <el-form-item label="服务器IP" prop="hostIp"> + <el-select v-model="queryParams.hostIp" clearable placeholder="请选择" class="!w-160px" @change="getDataList"> + <el-option + v-for="(ip, index) in ips" + :key="index" + :label="ip" + :value="ip" + /> + </el-select> </el-form-item> <el-form-item label="创建时间" prop="createTime"> <el-date-picker @@ -109,9 +93,8 @@ <ContentWrap v-if="showType == 'chart'"> <!-- 磁盘使用率折线图 --> - <el-skeleton :loading="echartsLoading" animated> - <Echart :height="320" :options="diskChartOptions"/> - </el-skeleton> + <div id="chartArea"></div> + <!-- 磁盘使用率饼图 --> <h3 style="margin-top: 20px; margin-bottom: 10px">主机磁盘使用率</h3> <div v-for="host in hostList" :key="host.name" class="host"> @@ -131,19 +114,6 @@ </el-skeleton> </div> </div> -<!-- <div v-for="(host, hostIndex) in hostList" :key="hostIndex">--> -<!-- <div style="margin-top: 10px">--> -<!-- <el-skeleton :loading="echartsLoading" animated>--> -<!-- {{ hostIndex }} 主机名: {{ host.name }} --> -<!-- 服务器IP:{{ host.ip }}--> -<!-- <div v-for="(disk, diskIndex) in host.disks" :key="diskIndex">--> -<!-- <h3>{{ disk.name }}</h3>--> -<!-- <div :ref="el => chartRefs[hostIndex][diskIndex] = el"--> -<!-- :style="{ width: '300px', height: '300px' }"></div>--> -<!-- </div>--> -<!-- </el-skeleton>--> -<!-- </div>--> -<!-- </div>--> </ContentWrap> <!-- 列表 --> @@ -217,6 +187,8 @@ const loading = ref(true) // 列表的加载中 const list = ref<MonitorDiskVO[]>([]) // 列表的数据 +const hosts = ref<String[]>([]) // 主机列表 +const ips = ref<String[]>([]) // ip列表 const total = ref(0) // 列表的总页数 const queryParams = reactive({ pageNo: 1, @@ -236,26 +208,197 @@ const echartsLoading = ref(true) // 图表加载中 const showType = ref() //展示类型(chart-图例,data-数据) -const hostList = ref([ - { - name: 'Thinkpad-E14', - ip: '172.16.216.133', - disks: [ - {disk: '磁盘C', used: 70, total: 200}, - {disk: '磁盘D', used: 40, total: 60} - ] - }, - { - name: 'Thinkpad-E16', - ip: '172.16.216.133', - disks: [ - {disk: '磁盘C', used: 80, total: 500}, - {disk: '磁盘D', used: 20, total: 500} - ] - } -]); +const hostList = ref([]); -const chartRefs = ref([]); + +// 颜色集合(10个区分度较好的颜色) +const colors = [ + '#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE', + '#3BA272', '#FC8452', '#9A60B4', '#EA7CCC', '#19DCDC' +] +const chartRefs = ref<HTMLElement[]>([]) +// 图表实例和容器管理 +const chartInstances = ref<{ instance: echarts.ECharts; container: HTMLDivElement }[]>([]) + +// 清理所有图表资源 +const cleanCharts = () => { + // 1. 销毁所有图表实例 + chartInstances.value.forEach(({ instance }) => { + instance.dispose() + }) + + // 2. 移除所有容器元素 + chartInstances.value.forEach(({ container }) => { + container.remove() + }) + + // 3. 清空实例记录 + chartInstances.value = [] +} + + +// 处理接口数据 +const processServerData = (apiData: any[]) => { + return apiData.flatMap(serverObj => { + return Object.entries(serverObj).map(([serverName, dataPoints]) => ({ + serverName, + data: (dataPoints as any[]).map(item => ({ + createTime: item.createTime, + ...Object.fromEntries( + Object.entries(item) + .filter(([key]) => key !== 'createTime') + .map(([key, value]) => [key.replace(/[()/]/g, '_'), value]) // 清理特殊字符 + ) + })).sort((a, b) => a.createTime - b.createTime) // 按时间排序 + })) + }) +} + +// 初始化图表 +const initCharts = async (apiData: any[]) => { + cleanCharts() + await nextTick() + + const servers = processServerData(apiData) + const chartArea = document.querySelector('#chartArea') + + servers.forEach((server, index) => { + // 创建容器 + const container = document.createElement('div') + container.style.width = '100%' + container.style.height = '300px' + container.classList.add('chart-container') + chartArea?.appendChild(container) + + // 初始化图表实例 + const chart = echarts.init(container) + const dimensions = Object.keys(server.data[0]).filter(k => k !== 'createTime') + + // 图表配置 + const option: echarts.EChartsOption = { + title: { + text: `${server.serverName} 磁盘使用率趋势`, + left: 'center', + textStyle: { + fontSize: 16, + fontWeight: 'bold' + } + }, + tooltip: { + trigger: 'axis', + valueFormatter: (value) => `${value}%`, + axisPointer: { + type: 'cross', + label: { + backgroundColor: '#6a7985' + } + } + }, + legend: { + type: 'scroll', + top: 30, + pageIconColor: '#2c3e50', + pageTextStyle: { + color: '#666' + } + }, + grid: { + top: 80, + left: 50, + right: 30, + bottom: 30, + containLabel: true + }, + xAxis: { + type: 'time', + axisLabel: { + formatter: (value: number) => { + const date = new Date(value) + return `${date.getMonth() + 1}/${date.getDate()}` + }, + interval: 0, + length: 6, + rotate: 45, + fontSize: 12 + }, + min: (value) => value.min - 86400000, + max: (value) => value.max + 86400000 + }, + yAxis: { + type: 'value', + min: 0, + max: 100, + axisLabel: { + formatter: '{value}%', + fontSize: 12 + }, + splitLine: { + show: true, + lineStyle: { + type: 'dashed' + } + } + }, + toolbox: { + feature: { + dataZoom: { + type: 'inside', + filterMode: 'none', // 禁用数据过滤 + yAxisIndex: false, + title: { zoom: '缩放', back: '还原' } + }, + brush: { + type: ['lineX', 'clear'], + title: { lineX: '选择', clear: '清除' } + }, + saveAsImage: { + name: '磁盘使用率图表', + title: '保存', + pixelRatio: 2 + } + } + }, + color: colors, + series: dimensions.map((dim, dimIndex) => ({ + name: dim.replace(/_/g, ' '), // 还原清理的特殊字符 + type: 'line', + // 关键配置项 + progressive: 0, // 禁用分片渲染 + large: false, // 禁用大数据模式 + showAllSymbol: true, // 显示所有数据点 + sampling: 'none', // 禁用采样 + // 数据维度声明 + dimensions: ['createTime', 'value'], + smooth: true, + symbol: 'none', + areaStyle: { + opacity: 0.3, + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { offset: 0, color: colors[dimIndex % colors.length] }, + { offset: 1, color: 'rgba(255,255,255,0)' } + ]) + }, + lineStyle: { + width: 2, + color: colors[dimIndex % colors.length] + }, + data: server.data.map(d => [d.createTime, d[dim]]) + })), + dataZoom: [{ + type: 'inside', + filterMode: 'none' // 禁用数据过滤 + // type: 'inside', + // start: 0, + // end: 100, + // minValueSpan: 86400000 * 1 // 最小缩放范围为1天 + }] + } + + chart.setOption(option) + chartInstances.value.push({ instance: chart, container }) + }) +} + /** 查询列表 */ const getList = async () => { @@ -268,6 +411,19 @@ loading.value = false } } + +/** 查询所有主机名 */ +const getAllHost = async () => { + const data = await MonitorDiskApi.getAllHost() + hosts.value = data +} + +/** 查询所有ip */ +const getAllIp = async () => { + const data = await MonitorDiskApi.getAllIp() + ips.value = data +} + /** 搜索按钮操作 */ const handleQuery = () => { @@ -321,98 +477,26 @@ } } -/** 堆叠面积图配置 */ -const diskChartOptions = reactive<EChartsOption>({ - title: { - text: '磁盘空间折线图' - }, - dataset: { - dimensions: [], - source: [] - }, - grid: { - left: 30, - right: 20, - bottom: 10, - top: 70, - containLabel: true - }, - legend: { - top: 0 - }, - series: [ - { - name: 'disk', type: 'line', - emphasis: { - focus: 'series' - }, smooth: false - } - ], - toolbox: { - feature: { - // 数据区域缩放 - dataZoom: { - yAxisIndex: false // Y轴不缩放 - }, - brush: { - type: ['lineX', 'clear'] // 区域缩放按钮、还原按钮 - }, - saveAsImage: {show: true, name: '物理内存日志图片'} // 保存为图片 - } - }, - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross', - label: { - backgroundColor: '#6a7985' - } - }, - padding: [5, 10] - }, - xAxis: { - type: 'category', - boundaryGap: false, - axisTick: { - show: false - } - }, - yAxis: { - name: "单位(百分比)", - nameTextStyle: { - color: "#aaa", - nameLocation: "start", - }, - }, -}) as EChartsOption - /** 查询统计数据列表 */ const getMonitorDiskDataList = async () => { const list = await MonitorDiskApi.getMonitorDiskList(queryParams) - if (list != null && list != undefined && list.length > 0) { - diskChartOptions.dataset['dimensions'] = Object.keys(list[0]) - diskChartOptions.series = diskChartOptions.dataset['dimensions'].map(item => ({ - name: item.name, - type: 'line', - emphasis: { - focus: 'series' - }, - smooth: false - })); - diskChartOptions.series.splice(0, 1) - for (let item of list) { - item.createTime = formatTime(item.createTime, 'yyyy-MM-dd HH:mm:ss') - } - } - // 更新 Echarts 数据 - diskChartOptions.dataset['source'] = list echartsLoading.value = false + await initCharts(list) } const usedDiskInstance = async () => { const list = await MonitorDiskApi.getMonitorDiskInfo(queryParams) hostList.value = list - // 仪表盘详情,用于显示数据。 +} + +/** 封装接口调用 */ +const getDataList = async () => { + if(showType.value == 'data') { + await getList() + } else { + await getMonitorDiskDataList() + await usedDiskInstance() + } } /** 切换展示方式 */ @@ -430,19 +514,16 @@ /** 初始化 **/ onMounted(() => { showType.value = 'data'; - const currentDate = new Date(); - const previousDate = new Date(currentDate); - previousDate.setDate(currentDate.getDate() - 1); - queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss'); - queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss'); + getAllHost(); + getAllIp(); + getDataList() intervalId = setInterval(() => { - if (showType.value == 'data') { - getList() - } else { - getMonitorDiskDataList() - usedDiskInstance() - } - }, 30000); + getDataList() + }, 300000); +}) + +onBeforeUnmount(() => { + cleanCharts() }) onUnmounted(() => { @@ -484,4 +565,5 @@ font-size: 0.9em; color: #666; } + </style> diff --git a/src/views/infra/monitor/components/MonitorMem.vue b/src/views/infra/monitor/components/MonitorMem.vue index 768af0e..efd95cd 100644 --- a/src/views/infra/monitor/components/MonitorMem.vue +++ b/src/views/infra/monitor/components/MonitorMem.vue @@ -8,32 +8,35 @@ :inline="true" label-width="68px" > -<!-- <el-form-item label="主机名称" prop="hostName">--> -<!-- <el-input--> -<!-- v-model="queryParams.hostName"--> -<!-- placeholder="请输入主机名称"--> -<!-- clearable--> -<!-- @keyup.enter="handleQuery"--> -<!-- class="!w-120px"--> -<!-- />--> -<!-- </el-form-item>--> + <el-form-item label="主机名称" prop="hostName"> + <el-select v-model="queryParams.hostName" placeholder="请选择" class="!w-180px" @change="getDataList"> + <el-option + v-for="(host, index) in hosts" + :key="index" + :label="host" + :value="host" + /> + </el-select> + </el-form-item> <el-form-item label="服务器IP" prop="hostIp"> - <el-input - v-model="queryParams.hostIp" - placeholder="请输入服务器IP" - clearable - @keyup.enter="handleQuery" - class="!w-120px" - /> + <el-select v-model="queryParams.hostIp" clearable placeholder="请选择" class="!w-160px" @change="getDataList"> + <el-option + v-for="(ip, index) in ips" + :key="index" + :label="ip" + :value="ip" + /> + </el-select> </el-form-item> <el-form-item label="服务名" prop="serverName"> - <el-input - v-model="queryParams.serverName" - placeholder="请输入服务名" - clearable - @keyup.enter="handleQuery" - class="!w-120px" - /> + <el-select v-model="queryParams.serverName" placeholder="请选择" class="!w-160px" @change="getDataList"> + <el-option + v-for="(server, index) in servers" + :key="index" + :label="server" + :value="server" + /> + </el-select> </el-form-item> <el-form-item label="创建时间" prop="createTime"> <el-date-picker @@ -112,13 +115,13 @@ <!-- 列表 --> <ContentWrap v-if="showType == 'data'"> <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"> - <el-table-column label="主机名称" align="center" prop="hostName"/> - <el-table-column label="服务器ip" align="center" prop="hostIp"/> + <el-table-column label="主机名称" align="center" prop="hostName" width="160"/> + <el-table-column label="服务器ip" align="center" prop="hostIp" width="150"/> <el-table-column label="服务名" align="center" prop="serverName" width="120"/> <el-table-column label="总内存" align="center" prop="physicalTotal"/> <el-table-column label="已用内存" align="center" prop="physicalUsed"/> <el-table-column label="空闲内存" align="center" prop="physicalFree"/> - <el-table-column label="内存使用率" align="center" prop="physicalUsage" width="100"/> + <el-table-column label="内存使用率" align="center" prop="physicalUsage"/> <el-table-column label="JVM占用内存" align="center" prop="runtimeTotal"/> <el-table-column label="JVM最大内存" align="center" prop="runtimeMax"/> <el-table-column label="JVM可用内存" align="center" prop="runtimeUsed"/> @@ -131,7 +134,7 @@ :formatter="dateFormatter" width="180px" /> - <el-table-column label="操作" align="center"> + <el-table-column label="操作" align="center" width="120"> <template #default="scope"> <el-button link @@ -182,6 +185,9 @@ const loading = ref(true) // 列表的加载中 const list = ref<MonitorMemVO[]>([]) // 列表的数据 +const hosts = ref<String[]>([]) // 主机列表 +const ips = ref<String[]>([]) // ip列表 +const servers = ref<String[]>([]) // 服务列表 const total = ref(0) // 列表的总页数 const queryParams = reactive({ pageNo: 1, @@ -217,19 +223,65 @@ } } +/** 查询所有主机名 */ +const getAllHost = async () => { + const data = await MonitorMemApi.getAllHost() + // 确保 data 存在且非空后,设置默认选中第一个 + if (data.length > 0) { + queryParams.hostName = data[0]; + } + hosts.value = data +} + +/** 查询所有ip */ +const getAllIp = async () => { + const data = await MonitorMemApi.getAllIp() + ips.value = data +} + +/** 查询所有服务名 */ +const getAllServer = async () => { + const data = await MonitorMemApi.getAllServer() + servers.value = data +} + +/** 封装接口调用 */ +const getDataList = async () => { + if(showType.value == 'data') { + await getList() + } else { + dealDate() + await getMonitorMemDataList() + } +} + +/** 处理查询时间段 */ +const dealDate = async () => { + const currentDate = new Date(); + const previousDate = new Date(currentDate); + previousDate.setDate(currentDate.getDate() - 1); + queryParams.createTime = [] + queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss'); + queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss'); +} + /** 搜索按钮操作 */ const handleQuery = () => { queryParams.pageNo = 1 - if(showType.value == 'data') { - getList() - } else { - getMonitorMemDataList() - } + getDataList() } /** 重置按钮操作 */ const resetQuery = () => { queryFormRef.value.resetFields() + if (hosts.value.length > 0) { + queryParams.hostName = hosts.value[0]; + } + if(showType.value != 'data') { + if (servers.value.length > 0) { + queryParams.serverName = servers.value[0]; + } + } handleQuery() } @@ -432,11 +484,10 @@ /** 切换展示方式 */ const switchShow = (type: String) => { showType.value = type - if(showType.value == 'data') { - getList() - } else { - getMonitorMemDataList() + if (queryParams.serverName == null && servers.value.length > 0) { + queryParams.serverName = servers.value[0]; } + getDataList() } /** 导出按钮操作 */ @@ -458,17 +509,12 @@ /** 初始化 **/ onMounted(() => { showType.value = 'data'; - const currentDate = new Date(); - const previousDate = new Date(currentDate); - previousDate.setDate(currentDate.getDate() - 1); - queryParams.createTime[0] = formatDate(previousDate, 'YYYY-MM-DD HH:mm:ss'); - queryParams.createTime[1] = formatDate(currentDate, 'YYYY-MM-DD HH:mm:ss'); + getAllHost(); + getAllIp(); + getAllServer(); + getDataList() intervalId = setInterval(() => { - if(showType.value == 'data') { - getList() - } else { - getMonitorMemDataList() - } + getDataList() }, 60000); }) diff --git a/src/views/infra/monitor/components/test.html b/src/views/infra/monitor/components/test.html new file mode 100644 index 0000000..131133e --- /dev/null +++ b/src/views/infra/monitor/components/test.html @@ -0,0 +1,90 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>磁盘使用率监控</title> + <script src="https://cdn.jsdelivr.net/npm/echarts@5.4.2/dist/echarts.min.js"></script> +</head> +<body> +<script> + // 原始数据 + const rawData = { + "Thinkpad-E14": [ + { createTime: 1739149080000, "Windows (C:)": 84, "Data (D:)": 35 } + ], + "DESKTOP-50930CB": [ + { createTime: 1739844971000, "(D:)": 21, "(C:)": 54, "(F:)": 0, "(E:)": 7 }, + { createTime: 1739845405000, "(D:)": 21, "(C:)": 54, "(F:)": 0, "(E:)": 7 } + ] + }; + + // 生成图表函数 + function createChart(serverName, data) { + // 处理数据 + const seriesData = {}; + + data.forEach(entry => { + const createTime = entry.createTime; + Object.entries(entry).forEach(([key, value]) => { + if (key !== 'createTime') { + if (!seriesData[key]) { + seriesData[key] = []; + } + // 存储为 [时间戳, 数值] 格式 + seriesData[key].push([createTime, value]); + } + }); + }); + + // 生成series配置 + const series = Object.keys(seriesData).map(disk => ({ + name: disk, + type: 'line', + data: seriesData[disk], // 直接使用包含时间戳的数据 + smooth: true + })); + + // 配置选项 + const option = { + title: { text: `${serverName} 磁盘使用率监控` }, + tooltip: { + trigger: 'axis', + valueFormatter: value => `${value}%` + }, + legend: { data: Object.keys(seriesData) }, + grid: { left: '3%', right: '4%', bottom: '15%', containLabel: true }, + xAxis: { + type: 'time', + axisLabel: { + formatter: '{yyyy}-{MM}-{dd} {HH}:{mm}', + rotate: 45 + } + }, + yAxis: { + type: 'value', + axisLabel: { + formatter: '{value}%', + }, + max: 100 + }, + series: series + }; + + // 创建图表容器 + const div = document.createElement('div'); + div.style.width = '800px'; + div.style.height = '500px'; // 增加高度适应时间标签 + div.style.margin = '40px auto'; + document.body.appendChild(div); + + const chart = echarts.init(div); + chart.setOption(option); + } + + // 为每个服务器生成图表 + Object.entries(rawData).forEach(([serverName, data]) => { + createChart(serverName, data); + }); +</script> +</body> +</html> -- Gitblit v1.9.3