From af20ce04f242758b35faecdf6ede493244f48491 Mon Sep 17 00:00:00 2001 From: vudsen Date: Wed, 21 Jan 2026 10:00:57 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=B0=86?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=9C=A8=E6=96=B0=E6=A0=87=E7=AD=BE=E9=A1=B5?= =?UTF-8?q?=E4=B8=AD=E6=9F=A5=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spectre-frontend/src/components/icon/icon.ts | 1 + .../src/components/icon/svg-symbols.tsx | 11 ++++++ .../channel/[channelId]/ChannelLayout.tsx | 1 + .../_message_view/ArthasResponseDetail.tsx | 0 .../_message_view/ArthasResponsePreview.tsx | 0 .../_component/ClassloaderMessageDetail.tsx | 2 +- .../_component/CommandMessageDetail.tsx | 2 +- .../_component/DashboardMessageDetail.tsx | 0 .../_component/EnhancerMessageDetail.tsx | 2 +- .../_component/JadMessageDetail.tsx | 2 +- .../_component/MemoryMessageDetail.tsx | 2 +- .../_component/MessageDetail.tsx | 2 +- .../_component/OgnlCommonMessageDetail.tsx | 2 +- .../_component/ScMessageDetail.tsx | 2 +- .../_component/StackMessageDetail.tsx | 0 .../_component/StatusMessageDetail.tsx | 2 +- .../_component/TraceMessageDetail.tsx | 2 +- .../_component/WelcomeMessageDetail.tsx | 2 +- .../_ognl_result/OgnlMessageView.tsx | 0 .../_component/_ognl_result/parser.ts | 0 .../_message_view/_register/classloader.ts | 2 +- .../_message_view/_register/command.ts | 0 .../_message_view/_register/dashboard.ts | 12 +++++++ .../_message_view/_register/enhancer.ts | 0 .../_message_view/_register/jad.ts | 12 +++++++ .../_message_view/_register/jvm.ts | 0 .../_message_view/_register/memory.ts | 12 +++++++ .../_message_view/_register/message.ts | 0 .../_message_view/_register/ognl-common.tsx | 0 .../[channelId]/_message_view/_register/sc.ts | 12 +++++++ .../_message_view/_register/stack.ts | 0 .../_message_view/_register/status.ts | 0 .../_message_view/_register/trace.ts | 0 .../_message_view/_register/uncomplete.ts | 0 .../_message_view/_register/welcome.ts | 0 .../_console => }/_message_view/factory.ts | 0 .../_console => }/_message_view/init.ts | 0 .../_console/ArthasResponseDetailTab.tsx | 34 ++++++++++++++++--- .../_component/ArthasResponseItem.tsx | 2 +- .../_message_view/_register/dashboard.ts | 12 ------- .../_console/_message_view/_register/jad.ts | 12 ------- .../_message_view/_register/memory.ts | 12 ------- .../_console/_message_view/_register/sc.ts | 12 ------- .../[channelId]/_tabs/_console/index.tsx | 2 -- .../channel/[channelId]/_tabs/_jad/index.tsx | 2 +- .../_tabs/_message_detail/index.tsx | 16 +++++++++ .../channel/[channelId]/_tabs/tab-constant.ts | 12 +++++++ .../src/pages/channel/[channelId]/context.ts | 2 +- .../src/pages/channel/[channelId]/db.ts | 0 49 files changed, 133 insertions(+), 70 deletions(-) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/ArthasResponseDetail.tsx (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/ArthasResponsePreview.tsx (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/ClassloaderMessageDetail.tsx (95%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/CommandMessageDetail.tsx (85%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/DashboardMessageDetail.tsx (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/EnhancerMessageDetail.tsx (87%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/JadMessageDetail.tsx (91%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/MemoryMessageDetail.tsx (94%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/MessageDetail.tsx (79%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/OgnlCommonMessageDetail.tsx (64%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/ScMessageDetail.tsx (97%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/StackMessageDetail.tsx (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/StatusMessageDetail.tsx (86%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/TraceMessageDetail.tsx (98%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/WelcomeMessageDetail.tsx (89%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/_ognl_result/OgnlMessageView.tsx (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_component/_ognl_result/parser.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/classloader.ts (72%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/command.ts (100%) create mode 100644 spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/dashboard.ts rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/enhancer.ts (100%) create mode 100644 spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/jad.ts rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/jvm.ts (100%) create mode 100644 spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/memory.ts rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/message.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/ognl-common.tsx (100%) create mode 100644 spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/sc.ts rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/stack.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/status.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/trace.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/uncomplete.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/_register/welcome.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/factory.ts (100%) rename spectre-frontend/src/pages/channel/[channelId]/{_tabs/_console => }/_message_view/init.ts (100%) delete mode 100644 spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/dashboard.ts delete mode 100644 spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/jad.ts delete mode 100644 spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/memory.ts delete mode 100644 spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/sc.ts create mode 100644 spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx create mode 100644 spectre-frontend/src/pages/channel/[channelId]/db.ts diff --git a/spectre-frontend/src/components/icon/icon.ts b/spectre-frontend/src/components/icon/icon.ts index 7f3b275..5d27fee 100644 --- a/spectre-frontend/src/components/icon/icon.ts +++ b/spectre-frontend/src/components/icon/icon.ts @@ -28,6 +28,7 @@ const Icon = { RIGHT_ARROW: 'fc-angle-right', BOOKMARK: 'fc-bookmark', LOCK: 'fc-lock', + EXTERNAL: 'fc-arrow-up-right-from-square', } as const export type Icons = (typeof Icon)[keyof typeof Icon] diff --git a/spectre-frontend/src/components/icon/svg-symbols.tsx b/spectre-frontend/src/components/icon/svg-symbols.tsx index 4997af1..714a86e 100644 --- a/spectre-frontend/src/components/icon/svg-symbols.tsx +++ b/spectre-frontend/src/components/icon/svg-symbols.tsx @@ -337,6 +337,17 @@ const SvgSymbols: React.FC = () => { d="M256 160L256 224L384 224L384 160C384 124.7 355.3 96 320 96C284.7 96 256 124.7 256 160zM192 224L192 160C192 89.3 249.3 32 320 32C390.7 32 448 89.3 448 160L448 224C483.3 224 512 252.7 512 288L512 512C512 547.3 483.3 576 448 576L192 576C156.7 576 128 547.3 128 512L128 288C128 252.7 156.7 224 192 224z" /> + + {/**/} + + , document.body, diff --git a/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx b/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx index a089e5d..2b45083 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx @@ -10,6 +10,7 @@ import ChannelContext, { } from '@/pages/channel/[channelId]/context.ts' import type { QuickCommandRef } from '@/pages/channel/[channelId]/_component/QuickCommand' import useArthasMessageBus from '@/pages/channel/[channelId]/useArthasMessageBus.tsx' +import './_message_view/init.ts' interface ChannelLayoutProps { channelId: string diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/ArthasResponseDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/ArthasResponseDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/ArthasResponsePreview.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/ArthasResponsePreview.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ClassloaderMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx similarity index 95% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ClassloaderMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx index 6b41f4a..511ef2e 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ClassloaderMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx @@ -1,5 +1,5 @@ import type React from 'react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' import { Table, TableBody, diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/CommandMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx similarity index 85% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/CommandMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx index 73cf245..f84fa32 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/CommandMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx @@ -1,6 +1,6 @@ import type React from 'react' import { Code } from '@heroui/react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' export type CommandMessage = { type: 'command' diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/DashboardMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/DashboardMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/EnhancerMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx similarity index 87% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/EnhancerMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx index abdb71d..5d31f07 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/EnhancerMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx @@ -1,5 +1,5 @@ import { Code } from '@heroui/react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' type EnhancerMessage = { effect: { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/JadMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx similarity index 91% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/JadMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx index 43ab472..c424e95 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/JadMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useContext } from 'react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' import Code from '@/components/Code.tsx' import { Link } from '@heroui/react' import ChannelContext from '@/pages/channel/[channelId]/context.ts' diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MemoryMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx similarity index 94% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MemoryMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx index d318d33..22d07bc 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MemoryMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx @@ -7,7 +7,7 @@ import { TableRow, } from '@heroui/react' import React, { useMemo } from 'react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' import PercentageData from '@/pages/channel/[channelId]/_tabs/_dashboard/PercentageData.tsx' type MemoryInfo = { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx similarity index 79% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx index 652c3fa..6c98705 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx @@ -1,6 +1,6 @@ import type React from 'react' import { Code } from '@heroui/react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' type MessageResponse = { type: 'message' diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/OgnlCommonMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/OgnlCommonMessageDetail.tsx similarity index 64% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/OgnlCommonMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/OgnlCommonMessageDetail.tsx index ea43b2a..3a8ff85 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/OgnlCommonMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/OgnlCommonMessageDetail.tsx @@ -1,5 +1,5 @@ import type React from 'react' -import OgnlMessageView from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/_ognl_result/OgnlMessageView.tsx' +import OgnlMessageView from '../_component/_ognl_result/OgnlMessageView.tsx' const OgnlCommonMessageDetail: React.FC<{ raw: string }> = ({ raw }) => { return ( diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ScMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx similarity index 97% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ScMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx index 9086c59..3a80883 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ScMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' import { Card, CardBody, diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/StackMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StackMessageDetail.tsx similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/StackMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StackMessageDetail.tsx diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/StatusMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx similarity index 86% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/StatusMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx index 88f636b..47e8797 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/StatusMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx @@ -1,6 +1,6 @@ import type React from 'react' import { Code } from '@heroui/react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' type StatusMessage = { type: 'status' diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/TraceMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx similarity index 98% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/TraceMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx index 9338e55..dbd02dc 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/TraceMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx @@ -2,7 +2,7 @@ import React, { useContext, useMemo, useRef } from 'react' import clsx from 'clsx' import { formatTime } from '@/common/util.ts' import { ListboxItem, Tooltip } from '@heroui/react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' import RightClickMenu from '@/components/RightClickMenu/RightClickMenu.tsx' import useRightClickMenu from '@/components/RightClickMenu/useRightClickMenu.ts' import ChannelContext from '@/pages/channel/[channelId]/context.ts' diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/WelcomeMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx similarity index 89% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/WelcomeMessageDetail.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx index 9a54372..d399485 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/WelcomeMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx @@ -1,6 +1,6 @@ import { Code, Link } from '@heroui/react' import type React from 'react' -import type { DetailComponentProps } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import type { DetailComponentProps } from '../factory.ts' type WelcomeMessage = { type: 'welcome' diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/_ognl_result/OgnlMessageView.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/_ognl_result/OgnlMessageView.tsx similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/_ognl_result/OgnlMessageView.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/_ognl_result/OgnlMessageView.tsx diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/_ognl_result/parser.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/_ognl_result/parser.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_component/_ognl_result/parser.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/_ognl_result/parser.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/classloader.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/classloader.ts similarity index 72% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/classloader.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/classloader.ts index 9fcea4e..3f9f870 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/classloader.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/classloader.ts @@ -1,4 +1,4 @@ -import { registerMessageView } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' +import { registerMessageView } from '../factory.ts' import ClassloaderMessageDetail from '../_component/ClassloaderMessageDetail' registerMessageView({ diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/command.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/command.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/command.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/command.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/dashboard.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/dashboard.ts new file mode 100644 index 0000000..71265dd --- /dev/null +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/dashboard.ts @@ -0,0 +1,12 @@ +import { registerMessageView } from '../factory.ts' +import DashboardMessageDetail from '../_component/DashboardMessageDetail.tsx' + +registerMessageView({ + detailComponent: DashboardMessageDetail, + type: 'dashboard', + display: (_) => ({ + name: '服务状态', + color: 'default', + tag: 'Dashboard', + }), +}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/enhancer.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/enhancer.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/enhancer.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/enhancer.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/jad.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/jad.ts new file mode 100644 index 0000000..12ae06c --- /dev/null +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/jad.ts @@ -0,0 +1,12 @@ +import { registerMessageView } from '../factory.ts' +import JadMessageDetail from '../_component/JadMessageDetail.tsx' + +registerMessageView({ + detailComponent: JadMessageDetail, + type: 'jad', + display: (message) => ({ + name: message.classInfo.name, + tag: 'jad', + color: 'default', + }), +}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/jvm.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/jvm.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/jvm.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/jvm.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/memory.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/memory.ts new file mode 100644 index 0000000..f433eba --- /dev/null +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/memory.ts @@ -0,0 +1,12 @@ +import { registerMessageView } from '../factory.ts' +import MemoryMessageDetail from '../_component/MemoryMessageDetail.tsx' + +registerMessageView({ + detailComponent: MemoryMessageDetail, + display: (_) => ({ + color: 'default', + name: '查看内存信息', + tag: 'memory', + }), + type: 'memory', +}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/message.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/message.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/message.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/message.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/ognl-common.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/ognl-common.tsx similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/ognl-common.tsx rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/ognl-common.tsx diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/sc.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/sc.ts new file mode 100644 index 0000000..222797d --- /dev/null +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/sc.ts @@ -0,0 +1,12 @@ +import { registerMessageView } from '../factory.ts' +import ScMessageDetail from '../_component/ScMessageDetail.tsx' + +registerMessageView({ + detailComponent: ScMessageDetail, + type: 'sc', + display: (_) => ({ + tag: 'sc', + name: '查找类', + color: 'default', + }), +}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/stack.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/stack.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/stack.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/stack.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/status.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/status.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/status.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/status.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/trace.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/trace.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/trace.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/trace.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/uncomplete.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/uncomplete.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/uncomplete.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/uncomplete.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/welcome.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/welcome.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/welcome.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/welcome.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/factory.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/factory.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/init.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/init.ts similarity index 100% rename from spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/init.ts rename to spectre-frontend/src/pages/channel/[channelId]/_message_view/init.ts diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx index f5203be..b6f467d 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx @@ -1,7 +1,10 @@ -import React from 'react' +import React, { useContext } from 'react' import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' -import { Tab, Tabs } from '@heroui/react' -import ArthasResponseDetail from './_message_view/ArthasResponseDetail.tsx' +import { Tab, Tabs, Tooltip } from '@heroui/react' +import ArthasResponseDetail from '@/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx' +import SvgIcon from '@/components/icon/SvgIcon.tsx' +import Icon from '@/components/icon/icon.ts' +import ChannelContext from '@/pages/channel/[channelId]/context.ts' interface ArthasResponseDetailProps { entity?: ArthasResponseWithId @@ -10,6 +13,7 @@ interface ArthasResponseDetailProps { const ArthasResponseDetailTab: React.FC = ({ entity, }) => { + const context = useContext(ChannelContext) if (!entity) { return (
@@ -20,13 +24,24 @@ const ArthasResponseDetailTab: React.FC = ({
) } + const openInNewTab = () => { + context + .getTabsController() + .openTab('MESSAGE_DETAIL', { name: 'TODO' }, { msg: entity }) + } return ( -
+
@@ -37,6 +52,15 @@ const ArthasResponseDetailTab: React.FC = ({
+
+ + + +
) } diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx index 456e283..2e3062e 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx @@ -2,7 +2,7 @@ import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import clsx from 'clsx' import React, { useMemo } from 'react' import './listStyle.css' -import ArthasResponsePreview from '@/pages/channel/[channelId]/_tabs/_console/_message_view/ArthasResponsePreview.tsx' +import ArthasResponsePreview from '@/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx' export type ResponseGroupItem = { entity: ArthasResponseWithId diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/dashboard.ts b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/dashboard.ts deleted file mode 100644 index 6a27fc1..0000000 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/dashboard.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { registerMessageView } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' -import DashboardMessageDetail from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/DashboardMessageDetail.tsx' - -registerMessageView({ - detailComponent: DashboardMessageDetail, - type: 'dashboard', - display: (_) => ({ - name: '服务状态', - color: 'default', - tag: 'Dashboard', - }), -}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/jad.ts b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/jad.ts deleted file mode 100644 index 1b9c6d4..0000000 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/jad.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { registerMessageView } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' -import JadMessageDetail from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/JadMessageDetail.tsx' - -registerMessageView({ - detailComponent: JadMessageDetail, - type: 'jad', - display: (message) => ({ - name: message.classInfo.name, - tag: 'jad', - color: 'default', - }), -}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/memory.ts b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/memory.ts deleted file mode 100644 index 256d2a9..0000000 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/memory.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { registerMessageView } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' -import MemoryMessageDetail from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/MemoryMessageDetail.tsx' - -registerMessageView({ - detailComponent: MemoryMessageDetail, - display: (_) => ({ - color: 'default', - name: '查看内存信息', - tag: 'memory', - }), - type: 'memory', -}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/sc.ts b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/sc.ts deleted file mode 100644 index d7e0fce..0000000 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_message_view/_register/sc.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { registerMessageView } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/factory.ts' -import ScMessageDetail from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/ScMessageDetail.tsx' - -registerMessageView({ - detailComponent: ScMessageDetail, - type: 'sc', - display: (_) => ({ - tag: 'sc', - name: '查找类', - color: 'default', - }), -}) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/index.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/index.tsx index 8a33ab2..fdb3ce8 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/index.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/index.tsx @@ -6,8 +6,6 @@ import CommandExecuteBlock from './CommandExecuteBlock.tsx' import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels' import ArthasResponseListTab from './ArthasResponseListTab.tsx' -import './_message_view/init.ts' - const ConsoleTab: React.FC = () => { const [selectedEntity, setSelectedEntity] = useState() diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_jad/index.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_jad/index.tsx index 484561a..17024f8 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_jad/index.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_jad/index.tsx @@ -3,7 +3,7 @@ import { executeArthasCommandSync } from '@/api/impl/arthas.ts' import { store } from '@/store' import { addToast } from '@heroui/react' import Code from '@/components/Code.tsx' -import type { JadMessage } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/JadMessageDetail.tsx' +import type { JadMessage } from '../../_message_view/_component/JadMessageDetail' export interface JadPageProps { /** diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx new file mode 100644 index 0000000..74ba8be --- /dev/null +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx @@ -0,0 +1,16 @@ +import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' +import ArthasResponseDetail from '@/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx' + +export interface MessageDetailPageProps { + msg: ArthasResponseWithId +} + +const MessageDetailPage: React.FC = (props) => { + return ( +
+ +
+ ) +} + +export default MessageDetailPage diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts b/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts index 9a85045..9429975 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts @@ -5,10 +5,14 @@ import type React from 'react' import ChannelIcon from '@/pages/channel/[channelId]/_channel_icons/ChannelIcon.ts' import type { TabOptions } from '@/pages/channel/[channelId]/context.ts' import DashBoardTab from '@/pages/channel/[channelId]/_tabs/_dashboard' +import MessageDetailPage, { + type MessageDetailPageProps, +} from '@/pages/channel/[channelId]/_tabs/_message_detail' export interface TabArgs { JAD: JadPageProps DASHBOARD: undefined + MESSAGE_DETAIL: MessageDetailPageProps } type ComponentHolder = { @@ -38,8 +42,16 @@ const DASHBOARD: ComponentHolder = { }), } +const MESSAGE_DETAIL: ComponentHolder = { + Component: MessageDetailPage, + defaultPropsFactory: (props) => ({ + uniqueId: props.msg.fid, + }), +} + // eslint-disable-next-line @typescript-eslint/no-explicit-any export const TabComponents: Record> = { JAD, DASHBOARD, + MESSAGE_DETAIL, } diff --git a/spectre-frontend/src/pages/channel/[channelId]/context.ts b/spectre-frontend/src/pages/channel/[channelId]/context.ts index b891a3f..90080a2 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/context.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/context.ts @@ -9,7 +9,7 @@ export type TabOptions = { /** * 如果该标签页是唯一的,则需要提供该值,当用户重复打开时,将会跳转而不是开启一个新的 */ - uniqueId?: string + uniqueId?: React.Key hoverMessage?: string } diff --git a/spectre-frontend/src/pages/channel/[channelId]/db.ts b/spectre-frontend/src/pages/channel/[channelId]/db.ts new file mode 100644 index 0000000..e69de29 From bcecce0c3d7561b7c7d3a1b683d4cd6bdba9a8b5 Mon Sep 17 00:00:00 2001 From: vudsen Date: Fri, 23 Jan 2026 14:14:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=E4=BD=BF=E7=94=A8=20indexedDB=20?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E6=B6=88=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- spectre-frontend/package.json | 1 + spectre-frontend/pnpm-lock.yaml | 8 + spectre-frontend/src/api/impl/arthas.ts | 39 +-- .../channel/[channelId]/ChannelLayout.tsx | 10 +- .../[channelId]/_channel_icons/ChannelIcon.ts | 1 + .../_channel_icons/svg-symbols.tsx | 8 + .../_message_view/ArthasResponseDetail.tsx | 33 +-- .../_message_view/ArthasResponsePreview.tsx | 10 +- .../_component/ClassloaderMessageDetail.tsx | 1 - .../_component/CommandMessageDetail.tsx | 1 - .../_component/DashboardMessageDetail.tsx | 1 - .../_component/EnhancerMessageDetail.tsx | 1 - .../_component/JadMessageDetail.tsx | 1 - .../_component/MemoryMessageDetail.tsx | 4 +- .../_component/MessageDetail.tsx | 1 - .../_component/ScMessageDetail.tsx | 1 - .../_component/StackMessageDetail.tsx | 1 - .../_component/StatusMessageDetail.tsx | 1 - .../_component/TraceMessageDetail.tsx | 1 - .../_component/WelcomeMessageDetail.tsx | 1 - .../_message_view/_register/ognl-common.tsx | 4 +- .../[channelId]/_message_view/factory.ts | 17 +- .../[channelId]/_tabs/TabsController.tsx | 11 +- .../_console/ArthasResponseDetailTab.tsx | 11 +- .../_tabs/_console/ArthasResponseListTab.tsx | 43 ++- .../_component/ArthasResponseItem.tsx | 6 +- .../[channelId]/_tabs/_console/index.tsx | 4 +- .../_tabs/_dashboard/MemoryChart.tsx | 9 +- .../_tabs/_dashboard/ThreadTable.tsx | 2 +- .../[channelId]/_tabs/_dashboard/index.tsx | 14 +- .../_tabs/_message_detail/index.tsx | 4 +- .../channel/[channelId]/_tabs/tab-constant.ts | 2 +- .../src/pages/channel/[channelId]/context.ts | 1 + .../src/pages/channel/[channelId]/db.ts | 255 ++++++++++++++++++ .../src/pages/channel/[channelId]/index.tsx | 6 - .../[channelId]/useArthasMessageBus.tsx | 145 +++++++--- spectre-frontend/src/store/channelSlice.ts | 77 +----- 37 files changed, 492 insertions(+), 244 deletions(-) diff --git a/spectre-frontend/package.json b/spectre-frontend/package.json index 93812ad..114f7b2 100644 --- a/spectre-frontend/package.json +++ b/spectre-frontend/package.json @@ -19,6 +19,7 @@ "echarts": "^6.0.0", "framer-motion": "^12.23.9", "i18next": "^25.7.3", + "idb": "^8.0.3", "react": "~19.1.2", "react-dom": "~19.1.2", "react-hook-form": "^7.61.1", diff --git a/spectre-frontend/pnpm-lock.yaml b/spectre-frontend/pnpm-lock.yaml index 1a46b5f..f3443d6 100644 --- a/spectre-frontend/pnpm-lock.yaml +++ b/spectre-frontend/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: i18next: specifier: ^25.7.3 version: 25.7.3(typescript@5.9.3) + idb: + specifier: ^8.0.3 + version: 8.0.3 react: specifier: ~19.1.2 version: 19.1.2 @@ -3220,6 +3223,9 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + idb@8.0.3: + resolution: {integrity: sha512-LtwtVyVYO5BqRvcsKuB2iUMnHwPVByPCXFXOpuU96IZPPoPN6xjOGxZQ74pgSVVLQWtUOYgyeL4GE98BY5D3wg==} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -8478,6 +8484,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + idb@8.0.3: {} + ieee754@1.2.1: {} ignore@5.3.2: {} diff --git a/spectre-frontend/src/api/impl/arthas.ts b/spectre-frontend/src/api/impl/arthas.ts index 4c2afde..89ec3aa 100644 --- a/spectre-frontend/src/api/impl/arthas.ts +++ b/spectre-frontend/src/api/impl/arthas.ts @@ -43,42 +43,19 @@ export const joinChannel = ( export type InputStatusResponse = { type: 'input_status' - fid: number inputStatus: 'ALLOW_INPUT' | 'DISABLED' | 'ALLOW_INTERRUPT' jobId: number } -type ArthasResponse = { +export type PureArthasResponse = { type: string jobId: number } -export type ArthasResponseWithId = ArthasResponse & { - /** - * arthas 实际没用这个值,这个是给前端识别用的 - */ - fid: number -} -let fid = Date.now() - -function applyIdForArthasResponse(r: unknown): Promise { - const resp = r as ArthasResponse[] - const result: ArthasResponseWithId[] = [] - for (const arthasRespons of resp) { - result.push({ - ...arthasRespons, - fid: fid++, - }) - } - return Promise.resolve(result) -} - export const pullResults = async ( channelId: string, -): Promise => - axios - .get(`arthas/channel/${channelId}/pull-result`) - .then(applyIdForArthasResponse) +): Promise => + axios.get(`arthas/channel/${channelId}/pull-result`) export const executeArthasCommand = (channelId: string, command: string) => { return axios.post(`arthas/channel/${channelId}/execute`, { @@ -89,12 +66,10 @@ export const executeArthasCommand = (channelId: string, command: string) => { export const executeArthasCommandSync = ( channelId: string, command: string, -): Promise => { - return axios - .post(`arthas/channel/${channelId}/execute-sync`, { - command, - }) - .then(applyIdForArthasResponse) +): Promise => { + return axios.post(`arthas/channel/${channelId}/execute-sync`, { + command, + }) } export const disconnectSession = (channelId: string) => { diff --git a/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx b/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx index 2b45083..a04cd48 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/ChannelLayout.tsx @@ -18,10 +18,13 @@ interface ChannelLayoutProps { } const ChannelLayout: React.FC = (props) => { - const bus = useArthasMessageBus() + const bus = useArthasMessageBus(props.channelId) const tabsController = useRef(null) const quickCommandRef = useRef(null) - const contextValue = useMemo(() => { + const contextValue = useMemo(() => { + if (!bus) { + return null + } return { messageBus: bus, getTabsController() { @@ -33,6 +36,9 @@ const ChannelLayout: React.FC = (props) => { } }, [bus]) + if (!contextValue) { + return + } return ( <> diff --git a/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/ChannelIcon.ts b/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/ChannelIcon.ts index a128273..fd8a0ee 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/ChannelIcon.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/ChannelIcon.ts @@ -6,6 +6,7 @@ const ChannelIcon = { DASHBOARD: 'fc-chart-line', FIELD: 'jb-field', ARRAY: 'jb-db-array', + ALIGN_LEFT: 'fc-align-left', } export default ChannelIcon diff --git a/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/svg-symbols.tsx b/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/svg-symbols.tsx index efb461f..b60097c 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/svg-symbols.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_channel_icons/svg-symbols.tsx @@ -147,6 +147,14 @@ const ChannelSvgSymbols: React.FC = () => { fill="#6C707E" /> + + {/**/} + + , document.body, diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx index 3cae535..1c846f0 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx @@ -1,19 +1,22 @@ -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' +import type { PureArthasResponse } from '@/api/impl/arthas.ts' import React, { useCallback, useMemo, useRef, useState } from 'react' import { type DetailComponentProps, getArthasMessageView } from './factory.ts' +import type { ArthasMessage } from '@/pages/channel/[channelId]/db.ts' interface ArthasResponseDetailProps { - message: ArthasResponseWithId + message: ArthasMessage } const ArthasResponseDetail: React.FC = (props) => { const Component = useMemo(() => { - return getArthasMessageView(props.message.type)?.detailComponent - }, [props.message.type]) + return getArthasMessageView(props.message.value.type)?.detailComponent + }, [props.message.value.type]) as React.FC< + DetailComponentProps + > const componentCache = useRef(new Map()) - const [dirtyIds, setDirtyIds] = useState(new Set()) + const [dirtyIds, setDirtyIds] = useState(new Set()) const handleOnDirty = useCallback( - (id: number) => { + (id: string) => { if (!dirtyIds.has(id)) { setDirtyIds((prev) => new Set(prev).add(id)) } @@ -23,8 +26,8 @@ const ArthasResponseDetail: React.FC = (props) => { const renderDetail = useCallback( ( - id: number, - Component: React.FC>, + id: string, + Component: React.FC>, ) => { // 1. 如果已经在缓存池里,直接返回缓存的实例 if (componentCache.current.has(id)) { @@ -34,8 +37,8 @@ const ArthasResponseDetail: React.FC = (props) => { // 2. 如果不在缓存中,创建一个新的实例 const newComponent = ( handleOnDirty(id)} /> ) @@ -54,15 +57,15 @@ const ArthasResponseDetail: React.FC = (props) => { ) } if ( - dirtyIds.has(props.message.fid) && - !componentCache.current.has(props.message.fid) + dirtyIds.has(props.message.id) && + !componentCache.current.has(props.message.id) ) { componentCache.current.set( - props.message.fid, - renderDetail(props.message.fid, Component), + props.message.id, + renderDetail(props.message.id, Component), ) } - const currentId = props.message.fid + const currentId = props.message.id return ( <> {/* 策略:对于脏组件,我们全部渲染但在 CSS 上隐藏;对于非脏组件,动态切换 */} diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx index 195c58e..526815c 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx @@ -1,19 +1,21 @@ -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import React, { useMemo } from 'react' import { getArthasMessageView, type PreviewInfo } from './factory.ts' import clsx from 'clsx' import { Tooltip } from '@heroui/react' +import type { ArthasMessage } from '@/pages/channel/[channelId]/db.ts' interface ArthasResponsePreviewProps { - message: ArthasResponseWithId + message: ArthasMessage } const ArthasResponsePreview: React.FC = (props) => { const state: PreviewInfo = useMemo(() => { return ( - getArthasMessageView(props.message.type)?.display(props.message) ?? { + getArthasMessageView(props.message.value.type)?.display( + props.message.value, + ) ?? { name: '', color: 'default', - tag: props.message.type, + tag: props.message.value.type, } ) }, [props.message]) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx index 511ef2e..fa01f4d 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ClassloaderMessageDetail.tsx @@ -19,7 +19,6 @@ type Classloader = { export type ClassLoaderMessage = { type: 'classloader' jobId: number - fid: number classLoaderStats?: Record< string, { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx index f84fa32..1e14612 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx @@ -8,7 +8,6 @@ export type CommandMessage = { state: string command: string message?: string - fid: number } const CommandMessageDetail: React.FC> = ( diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx index 9889f83..148b85e 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx @@ -50,7 +50,6 @@ export type DashboardMessage = { runtimeInfo: RuntimeInfo threads: Thread[] type: 'dashboard' - fid: number } export const DashboardMessageDetail: React.FC = () => { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx index 5d31f07..3eb61f5 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/EnhancerMessageDetail.tsx @@ -10,7 +10,6 @@ type EnhancerMessage = { } jobId: number success: boolean - fid: number type: 'enhancer' } diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx index c424e95..c237a57 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/JadMessageDetail.tsx @@ -15,7 +15,6 @@ export type JadMessage = { mappings: Record source: string type: 'jad' - fid: number } const JadMessageDetail: React.FC> = ( diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx index 22d07bc..2858a10 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MemoryMessageDetail.tsx @@ -21,19 +21,17 @@ type MemoryMessage = { jobId: number memoryInfo: Record type: 'memory' - fid: number } const MemoryMessageDetail: React.FC> = ({ msg, }) => { const infos = useMemo(() => { const infos: MemoryInfo[] = [] - console.log(msg.memoryInfo) for (const entry of Object.entries(msg.memoryInfo)) { infos.push(...entry[1]) } return infos - }, [msg.fid]) + }, [msg.memoryInfo]) return ( diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx index 6c98705..e8d6d68 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/MessageDetail.tsx @@ -6,7 +6,6 @@ type MessageResponse = { type: 'message' jobId: number message: string - fid: number } const MessageDetail: React.FC> = ( diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx index 3a80883..86df8ad 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/ScMessageDetail.tsx @@ -49,7 +49,6 @@ type ClassInfo = { synthetic: boolean } type ScMessage = { - fid: number withField: boolean type: 'sc' segment: number diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StackMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StackMessageDetail.tsx index cae7264..398d765 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StackMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StackMessageDetail.tsx @@ -27,7 +27,6 @@ type Trace = { } type StackMessage = { - fid: number type: 'stack' threadId: number threadName: string diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx index 47e8797..e98e402 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/StatusMessageDetail.tsx @@ -7,7 +7,6 @@ type StatusMessage = { statusCode: number jobId: number message?: string - fid: number } const StatusMessageDetail: React.FC> = ({ diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx index dbd02dc..fa5ebba 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/TraceMessageDetail.tsx @@ -36,7 +36,6 @@ type TraceMessage = { jobId: number nodeCount: number root: Root - fid: number } function toKey(invoke: Invoke): string { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx index d399485..ff6c5e5 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_component/WelcomeMessageDetail.tsx @@ -11,7 +11,6 @@ type WelcomeMessage = { tutorials: string version: string wiki: string - fid: number } const WelcomeMessageDetail: React.FC> = ( diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/ognl-common.tsx b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/ognl-common.tsx index de9fcab..cde06ad 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/ognl-common.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/_register/ognl-common.tsx @@ -1,9 +1,9 @@ import { type DetailComponentProps, registerMessageView } from '../factory.ts' import OgnlCommonMessageDetail from '../_component/OgnlCommonMessageDetail.tsx' -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import React from 'react' +import type { PureArthasResponse } from '@/api/impl/arthas.ts' -function createComponent( +function createComponent( ognlResultGetter: (r: T) => string, ): React.FC> { const Component: React.FC> = (props) => { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_message_view/factory.ts b/spectre-frontend/src/pages/channel/[channelId]/_message_view/factory.ts index a1160f1..6653ed1 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_message_view/factory.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/_message_view/factory.ts @@ -1,5 +1,5 @@ import type { ChipProps } from '@heroui/react' -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' +import type { PureArthasResponse } from '@/api/impl/arthas.ts' import type React from 'react' export type PreviewInfo = { @@ -8,7 +8,7 @@ export type PreviewInfo = { tag: string } -export interface DetailComponentProps { +export interface DetailComponentProps { msg: T /** * 表示当前视图已经“脏”了,需要进行持久化,以免用户下次进入时丢失相关数据 @@ -16,25 +16,22 @@ export interface DetailComponentProps { onDirty?: () => void } -export type RegisterConfiguration = { +export type RegisterConfiguration = { type: string detailComponent?: React.FC> display: (message: T) => PreviewInfo } -const configMap: Record< - string, - RegisterConfiguration -> = {} +const configMap: Record> = {} -export function registerMessageView( +export function registerMessageView( conf: RegisterConfiguration, ) { - configMap[conf.type] = conf as RegisterConfiguration + configMap[conf.type] = conf as RegisterConfiguration } export function getArthasMessageView( type: string, -): RegisterConfiguration | undefined { +): RegisterConfiguration | undefined { return configMap[type] } diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/TabsController.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/TabsController.tsx index 5f2d73e..aedf3ce 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/TabsController.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/TabsController.tsx @@ -20,8 +20,7 @@ import ChannelIcon from '@/pages/channel/[channelId]/_channel_icons/ChannelIcon. type TabInfo = { node: React.ReactNode - icon?: string - id: number | string + id: React.Key } & TabOptions type OpenTabFuncArgs = TabArgs[K] extends undefined @@ -43,7 +42,7 @@ interface TabsControllerProps { } const TabsController: React.FC = (props) => { - const [activeTabId, setActiveTabId] = useState(0) + const [activeTabId, setActiveTabId] = useState(0) const { menuProps, onContextMenu } = useRightClickMenu() const currentHoverTab = useRef(undefined) const [tabs, setTabs] = useState(() => { @@ -84,7 +83,7 @@ const TabsController: React.FC = (props) => { { ...options, node, - icon: holder.icon, + icon: holder.icon ?? options.icon, id, }, ] @@ -93,12 +92,12 @@ const TabsController: React.FC = (props) => { }, })) - const switchTab = (id: number | string) => { + const switchTab = (id: React.Key) => { setActiveTabId(id) } const closeCurrentTab = ( - id: number | string, + id: React.Key, e?: React.MouseEvent, ) => { e?.stopPropagation() diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx index b6f467d..e57dee9 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseDetailTab.tsx @@ -1,13 +1,14 @@ import React, { useContext } from 'react' -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import { Tab, Tabs, Tooltip } from '@heroui/react' import ArthasResponseDetail from '@/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx' import SvgIcon from '@/components/icon/SvgIcon.tsx' import Icon from '@/components/icon/icon.ts' import ChannelContext from '@/pages/channel/[channelId]/context.ts' +import type { ArthasMessage } from '@/pages/channel/[channelId]/db.ts' +import ChannelIcon from '@/pages/channel/[channelId]/_channel_icons/ChannelIcon.ts' interface ArthasResponseDetailProps { - entity?: ArthasResponseWithId + entity?: ArthasMessage } const ArthasResponseDetailTab: React.FC = ({ @@ -27,7 +28,11 @@ const ArthasResponseDetailTab: React.FC = ({ const openInNewTab = () => { context .getTabsController() - .openTab('MESSAGE_DETAIL', { name: 'TODO' }, { msg: entity }) + .openTab( + 'MESSAGE_DETAIL', + { name: entity.context.command, icon: ChannelIcon.ALIGN_LEFT }, + { msg: entity }, + ) } return (
diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseListTab.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseListTab.tsx index d6c810c..8549004 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseListTab.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/ArthasResponseListTab.tsx @@ -5,30 +5,27 @@ import React, { useRef, useState, } from 'react' -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import { useSelector } from 'react-redux' import { type RootState, store } from '@/store' import ArthasResponseItem, { type ResponseGroupItem, } from '@/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx' import ChannelContext from '@/pages/channel/[channelId]/context.ts' +import type { ArthasMessage } from '@/pages/channel/[channelId]/db.ts' +import type { ArthasMessageBus } from '@/pages/channel/[channelId]/useArthasMessageBus.tsx' interface ArthasResponseListProps { - onEntitySelect: (e: ArthasResponseWithId) => void + onEntitySelect: (e: ArthasMessage) => void } const IGNORED_TYPES = new Set(['input_status']) -function buildArray0() { +function buildArray0(bus: ArthasMessageBus) { const channelSlice = store.getState().channel const isDebugMode = channelSlice.context.isDebugMode - return buildArray( - channelSlice.messages[channelSlice.context.channelId] ?? [], - undefined, - isDebugMode, - ) + return buildArray(bus.messages, undefined, isDebugMode) } function buildArray( - messages: ArthasResponseWithId[], + messages: ArthasMessage[], previousMessage?: ResponseGroupItem, isDebugMode?: boolean, ): ResponseGroupItem[] { @@ -39,19 +36,22 @@ function buildArray( })) } else { result = [] - let lastJobId = previousMessage?.entity.jobId ?? -1 + let lastJobId = previousMessage?.entity.value.jobId ?? -1 let groupColorFlag = previousMessage?.groupInfo?.colorFlag ?? -1 - let lastMsgType = previousMessage ? previousMessage.entity.type : '' + let lastMsgType = previousMessage ? previousMessage.entity.value.type : '' for (const entity of messages) { - if (IGNORED_TYPES.has(entity.type)) { + if (IGNORED_TYPES.has(entity.value.type)) { continue } // dashboard 仅显示第一条 - if ('dashboard' === entity.type && lastMsgType === entity.type) { + if ( + 'dashboard' === entity.value.type && + lastMsgType === entity.value.type + ) { continue } - lastMsgType = entity.type - if (entity.jobId === lastJobId) { + lastMsgType = entity.value.type + if (entity.value.jobId === lastJobId) { result.push({ entity, groupInfo: { @@ -70,22 +70,22 @@ function buildArray( }, }) } - lastJobId = entity.jobId + lastJobId = entity.value.jobId } } return result } - const ArthasResponseListTab: React.FC = (props) => { const isDebugMode = useSelector( (state) => state.channel.context.isDebugMode, ) ?? false - const [filteredResponses, setFilteredResponse] = - useState(buildArray0) + const context = useContext(ChannelContext) + const [filteredResponses, setFilteredResponse] = useState< + ResponseGroupItem[] + >(() => buildArray0(context.messageBus)) const scrollRef = useRef(null) const [selectedEntityIndex, setSelectedEntityIndex] = useState(-1) - const context = useContext(ChannelContext) useEffect(() => { const id = context.messageBus.addListener({ @@ -110,7 +110,7 @@ const ArthasResponseListTab: React.FC = (props) => { }, [context.messageBus]) useEffect(() => { - setFilteredResponse(buildArray0()) + setFilteredResponse(buildArray0(context.messageBus)) }, [isDebugMode]) useLayoutEffect(() => { @@ -119,7 +119,6 @@ const ArthasResponseListTab: React.FC = (props) => { const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 100 - console.log(isAtBottom) if (isAtBottom) { container.scrollTo({ top: container.scrollHeight }) diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx index 2e3062e..ec8e75e 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_console/_component/ArthasResponseItem.tsx @@ -1,11 +1,11 @@ -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import clsx from 'clsx' import React, { useMemo } from 'react' import './listStyle.css' import ArthasResponsePreview from '@/pages/channel/[channelId]/_message_view/ArthasResponsePreview.tsx' +import type { ArthasMessage } from '@/pages/channel/[channelId]/db.ts' export type ResponseGroupItem = { - entity: ArthasResponseWithId + entity: ArthasMessage groupInfo?: { colorFlag: number } @@ -59,7 +59,7 @@ const ArthasResponseItem: React.FC = ({ return (
{ - const [selectedEntity, setSelectedEntity] = useState() + const [selectedEntity, setSelectedEntity] = useState() return (
diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/MemoryChart.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/MemoryChart.tsx index c01e09b..01fe6da 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/MemoryChart.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/MemoryChart.tsx @@ -16,15 +16,16 @@ import { import { LineChart, type LineSeriesOption } from 'echarts/charts' import { CanvasRenderer } from 'echarts/renderers' import { UniversalTransition } from 'echarts/features' -import type { - DashboardMessage, - MemoryInfo, -} from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/DashboardMessageDetail.tsx' + import { formatTime } from '@/common/util.ts' import { Card, CardBody } from '@heroui/react' import KVGird from '@/components/KVGird' import KVGridItem from '@/components/KVGird/KVGridItem.tsx' import PercentageData from '@/pages/channel/[channelId]/_tabs/_dashboard/PercentageData.tsx' +import type { + DashboardMessage, + MemoryInfo, +} from '@/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx' type EChartsOption = echarts.ComposeOption< | TitleComponentOption diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/ThreadTable.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/ThreadTable.tsx index 18ed1b0..d7251e2 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/ThreadTable.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/ThreadTable.tsx @@ -1,4 +1,3 @@ -import type { DashboardMessage } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/DashboardMessageDetail.tsx' import { Table, TableBody, @@ -9,6 +8,7 @@ import { } from '@heroui/react' import PercentageData from '@/pages/channel/[channelId]/_tabs/_dashboard/PercentageData.tsx' import React from 'react' +import type { DashboardMessage } from '@/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx' interface ThreadTableProps { lastMessage: DashboardMessage diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/index.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/index.tsx index a4bd3b4..e57952e 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/index.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_dashboard/index.tsx @@ -1,14 +1,14 @@ import React, { useContext, useEffect, useState } from 'react' import ChannelContext from '@/pages/channel/[channelId]/context.ts' -import type { DashboardMessage } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/DashboardMessageDetail.tsx' import { Button, Card, CardBody } from '@heroui/react' import KVGird from '@/components/KVGird' import KVGridItem from '@/components/KVGird/KVGridItem.tsx' -import type { CommandMessage } from '@/pages/channel/[channelId]/_tabs/_console/_message_view/_component/CommandMessageDetail.tsx' import MemoryChart from '@/pages/channel/[channelId]/_tabs/_dashboard/MemoryChart.tsx' import ThreadTable from '@/pages/channel/[channelId]/_tabs/_dashboard/ThreadTable.tsx' import PercentageData from '@/pages/channel/[channelId]/_tabs/_dashboard/PercentageData.tsx' import clsx from 'clsx' +import type { DashboardMessage } from '@/pages/channel/[channelId]/_message_view/_component/DashboardMessageDetail.tsx' +import type { CommandMessage } from '../../_message_view/_component/CommandMessageDetail' const DashBoardTab: React.FC = () => { const context = useContext(ChannelContext) @@ -19,18 +19,18 @@ const DashBoardTab: React.FC = () => { useEffect(() => { const id = context.messageBus.addListener({ - onMessage(msg) { + onMessage: function (msg) { const current = msg[msg.length - 1] if ( - current.type === 'command' && - (current as CommandMessage).command === 'dashboard' + current.value.type === 'command' && + (current.value as CommandMessage).command === 'dashboard' ) { return - } else if (current.type !== 'dashboard') { + } else if (current.value.type !== 'dashboard') { setStopped(true) return } else { - const dashboardMessage = current as DashboardMessage + const dashboardMessage = current.value as DashboardMessage setState(dashboardMessage) return } diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx index 74ba8be..5e23e62 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/_message_detail/index.tsx @@ -1,8 +1,8 @@ -import type { ArthasResponseWithId } from '@/api/impl/arthas.ts' import ArthasResponseDetail from '@/pages/channel/[channelId]/_message_view/ArthasResponseDetail.tsx' +import type { ArthasMessage } from '@/pages/channel/[channelId]/db.ts' export interface MessageDetailPageProps { - msg: ArthasResponseWithId + msg: ArthasMessage } const MessageDetailPage: React.FC = (props) => { diff --git a/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts b/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts index 9429975..baec4bc 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/_tabs/tab-constant.ts @@ -45,7 +45,7 @@ const DASHBOARD: ComponentHolder = { const MESSAGE_DETAIL: ComponentHolder = { Component: MessageDetailPage, defaultPropsFactory: (props) => ({ - uniqueId: props.msg.fid, + uniqueId: props.msg.id, }), } diff --git a/spectre-frontend/src/pages/channel/[channelId]/context.ts b/spectre-frontend/src/pages/channel/[channelId]/context.ts index 90080a2..a8481f1 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/context.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/context.ts @@ -6,6 +6,7 @@ import type { ArthasMessageBus } from '@/pages/channel/[channelId]/useArthasMess export type TabOptions = { name?: string isLocked?: boolean + icon?: string /** * 如果该标签页是唯一的,则需要提供该值,当用户重复打开时,将会跳转而不是开启一个新的 */ diff --git a/spectre-frontend/src/pages/channel/[channelId]/db.ts b/spectre-frontend/src/pages/channel/[channelId]/db.ts index e69de29..503f8ff 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/db.ts +++ b/spectre-frontend/src/pages/channel/[channelId]/db.ts @@ -0,0 +1,255 @@ +import { type DBSchema, type IDBPDatabase, openDB } from 'idb' +import type { PureArthasResponse } from '@/api/impl/arthas.ts' + +const VERSION = 1 + +type ContextValue = { + command?: string + channelId: string +} +type ContextValueWithPK = ContextValue & { id: string } + +type MessageValue = { + channelId: string + contextId: string + /** + * Arthas 消息,JSON格式 + */ + value: PureArthasResponse +} + +type MessageValueWithId = MessageValue & { id: string } + +type ChannelInfoValue = { + lastAccess: number +} + +interface ArthasMessageDB extends DBSchema { + messages: { + key: string + value: MessageValue + indexes: { 'by-channel-id': string } + } + context: { + key: string + value: ContextValue + indexes: { 'by-channel-id': string } + } + channelInfo: { + /** + * channel id + */ + key: string + value: ChannelInfoValue + } +} + +export type ArthasMessage = { + id: string + context: ContextValue + value: T +} +/** + * 1h + */ +const MAX_ALIVE = 1000 * 60 * 60 + +async function setupDB() { + let contextId = Date.now() + let messageId = Date.now() + const db = await openDB('ArthasMessage', VERSION, { + upgrade(database: IDBPDatabase) { + const messagesStore = database.createObjectStore('messages') + messagesStore.createIndex('by-channel-id', 'channelId') + const contextStore = database.createObjectStore('context') + contextStore.createIndex('by-channel-id', 'channelId') + database.createObjectStore('channelInfo') + }, + }) + + async function findAllContextByIdIn( + ids: string[], + ): Promise> { + const scopes = await Promise.all(ids.map((id) => db.get('context', id))) + const result: Record = {} + + for (let i = 0; i < scopes.length; i++) { + const context = scopes[i] + if (!context) { + continue + } + result[ids[i]] = { + ...context, + id: ids[i], + } + } + return result + } + + async function doClear(channelId: string) { + console.log(`Cleaning channel messages of "${channelId}"`) + + const messageTx = db.transaction('messages', 'readwrite') + const messageIndex = messageTx + .objectStore('messages') + .index('by-channel-id') + + const contextIds = new Set() + for ( + let cursor = await messageIndex.openCursor( + IDBKeyRange.only(channelId), + 'prev', + ); + cursor; + cursor = await cursor.continue() + ) { + contextIds.add(cursor.value.contextId) + cursor.delete() + } + const contextTx = db.transaction('context', 'readwrite') + const contextStore = contextTx.objectStore('context') + for (const contextId1 of contextIds) { + await contextStore.delete(contextId1) + } + + await db.delete('channelInfo', channelId) + await contextTx.done + await messageTx.done + } + + return { + async clearUnusedMessages() { + const tx = db.transaction('channelInfo') + const store = tx.objectStore('channelInfo') + + for ( + let cursor = await store.openCursor(); + cursor; + cursor = await cursor.continue() + ) { + if (Date.now() - cursor.value.lastAccess >= MAX_ALIVE) { + await doClear(cursor.primaryKey) + } + } + }, + /** + * 找到对应类型的最后一条消息 + */ + async findLastMessage( + channelId: string, + messageType: string, + ): Promise { + const tx = db.transaction('messages') + const store = tx.objectStore('messages') + const index = store.index('by-channel-id') + + for ( + let cursor = await index.openCursor( + IDBKeyRange.only(channelId), + 'prev', + ); + cursor; + cursor = await cursor.continue() + ) { + if (cursor.value.value.type === messageType) { + return { + id: cursor.primaryKey, + value: cursor.value.value, + context: (await findAllContextByIdIn([cursor.value.contextId]))[0], + } + } + } + }, + async listAllMessages( + channelId: string, + size: number, + ): Promise { + const tx = db.transaction('messages') + const store = tx.objectStore('messages') + const index = store.index('by-channel-id') + const messages: MessageValueWithId[] = [] + const scopeIds = new Set() + for ( + let cursor = await index.openCursor(IDBKeyRange.only(channelId)); + cursor; + cursor = await cursor.continue() + ) { + if (cursor.value.channelId !== channelId) { + continue + } + if (messages.length > size) { + break + } + messages.push({ + id: cursor.primaryKey, + ...cursor.value, + }) + scopeIds.add(cursor.value.contextId) + } + const contextMap = await findAllContextByIdIn([...scopeIds]) + const result: ArthasMessage[] = messages.map( + (msg) => ({ + id: msg.id, + context: contextMap[msg.contextId], + value: msg.value, + }), + ) + await db.put( + 'channelInfo', + { + lastAccess: Date.now(), + }, + channelId, + ) + await tx.done + return result + }, + async createNewContext(context: ContextValue): Promise { + const myId = (++contextId).toString() + await db.put('context', context, myId) + return myId + }, + async findLastContextId(channelId: string) { + const tx = db.transaction('context') + const store = tx.objectStore('context') + const index = store.index('by-channel-id') + + const cursor = await index.openCursor(IDBKeyRange.only(channelId), 'prev') + return cursor?.primaryKey + }, + async insertAllMessages( + messages: MessageValue[], + ): Promise { + const contextIds = new Set() + const ids: string[] = [] + const updateAccessIds = new Set() + for (const message of messages) { + const myId = (++messageId).toString() + db.put('messages', message, myId) + ids.push(myId) + contextIds.add(message.contextId) + updateAccessIds.add(message.channelId) + } + for (const updateAccessId of updateAccessIds) { + await db.put( + 'channelInfo', + { + lastAccess: Date.now(), + }, + updateAccessId, + ) + } + const contextMap = await findAllContextByIdIn([...contextIds]) + return messages.map((message, index) => ({ + id: ids[index], + context: contextMap[message.contextId], + value: message.value, + })) + }, + close() { + db.close() + }, + } +} + +export default setupDB diff --git a/spectre-frontend/src/pages/channel/[channelId]/index.tsx b/spectre-frontend/src/pages/channel/[channelId]/index.tsx index 084f05c..1fb0a53 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/index.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/index.tsx @@ -2,7 +2,6 @@ import React, { useCallback, useEffect, useState } from 'react' import { useParams } from 'react-router' import { type ChannelSessionDTO, joinChannel } from '@/api/impl/arthas.ts' import { useDispatch } from 'react-redux' -import { setupChannelContext } from '@/store/channelSlice.ts' import ChannelLayout from '@/pages/channel/[channelId]/ChannelLayout.tsx' const ChannelPage: React.FC = () => { @@ -19,11 +18,6 @@ const ChannelPage: React.FC = () => { .then((session) => { if (session) { setSession(session) - dispatch( - setupChannelContext({ - channelId, - }), - ) setLoading(false) } else { setTimeout(() => { diff --git a/spectre-frontend/src/pages/channel/[channelId]/useArthasMessageBus.tsx b/spectre-frontend/src/pages/channel/[channelId]/useArthasMessageBus.tsx index 9ec0f51..981ba6b 100644 --- a/spectre-frontend/src/pages/channel/[channelId]/useArthasMessageBus.tsx +++ b/spectre-frontend/src/pages/channel/[channelId]/useArthasMessageBus.tsx @@ -1,22 +1,19 @@ import { - type ArthasResponseWithId, executeArthasCommand, type InputStatusResponse, interruptCommand, pullResults, } from '@/api/impl/arthas.ts' -import { useEffect, useMemo } from 'react' -import { - appendMessages, - clearExpiredMessages, - updateInputStatus, -} from '@/store/channelSlice.ts' +import { useEffect, useState } from 'react' +import { setupChannelContext, updateInputStatus } from '@/store/channelSlice.ts' import { store } from '@/store' import { useDispatch } from 'react-redux' import type { Dispatch } from '@reduxjs/toolkit' +import setupDB, { type ArthasMessage } from '@/pages/channel/[channelId]/db.ts' +import type { CommandMessage } from '@/pages/channel/[channelId]/_message_view/_component/CommandMessageDetail.tsx' interface Listener { - onMessage?: (messages: ArthasResponseWithId[]) => void + onMessage?: (messages: ArthasMessage[]) => void afterExecute?: (command: string, fail: boolean) => void } @@ -37,6 +34,10 @@ export type ArthasMessageBus = { * @param interruptCurrent 是否中止当前正在执行的命令 */ execute(command: string, interruptCurrent?: boolean): Promise + /** + * 当前 channel 上的消息. 不一定是全部消息 + */ + messages: ArthasMessage[] } let globalId = Date.now() @@ -51,40 +52,92 @@ type PollState = { type ArthasMessageBusInternal = { launchPullResultTask: () => void pullNow: () => void - state: PollState + close: () => void } & ArthasMessageBus const classloaderHashRegx = /-c +[\da-zA-Z]{8}/ +const INPUT_STATUS = 'input_status' +const MAX_BUS_MESSAGE_SIZE = 100 +/** + * 达到最大值后,清理至这么多消息 + */ +const BUS_MESSAGE_THRESHOLD = 90 -const createArthasMessageBusInternal = ( +const createArthasMessageBusInternal = async ( + channelId: string, dispatch: Dispatch, -): ArthasMessageBusInternal => { +): Promise => { const listenerMap = new Map() + const db = await setupDB() + let currentContextId = + (await db.findLastContextId(channelId)) ?? + (await db.createNewContext({ + channelId, + })) + const messages = await setupMessages() const state: PollState = { taskDelay: 0, isFetching: false, isExcited: false, } + async function setupMessages() { + const messages = await db.listAllMessages(channelId, MAX_BUS_MESSAGE_SIZE) + if (messages.length === 0) { + dispatch( + setupChannelContext({ + channelId, + inputStatus: 'DISABLED', + }), + ) + return messages + } + const status = await db.findLastMessage(channelId, INPUT_STATUS) + dispatch( + setupChannelContext({ + channelId, + inputStatus: status + ? (status.value as InputStatusResponse).inputStatus + : 'ALLOW_INPUT', + }), + ) + return messages + } + const doPullResults = async (): Promise => { const channelId = store.getState().channel.context.channelId const r = await pullResults(channelId) for (const resp of r) { - if (resp.type === 'input_status') { - const status = (resp as InputStatusResponse).inputStatus - dispatch(updateInputStatus(status)) + switch (resp.type) { + case INPUT_STATUS: { + const status = (resp as InputStatusResponse).inputStatus + dispatch(updateInputStatus(status)) + break + } + case 'command': { + const command = resp as CommandMessage + currentContextId = await db.createNewContext({ + command: command.command, + channelId, + }) + } } } if (r.length > 0) { - for (const entry of listenerMap.entries()) { - entry[1].onMessage?.(r) - } - dispatch( - appendMessages({ - messages: r, + const dbMsg = await db.insertAllMessages( + r.map((msg) => ({ + value: msg, channelId, - }), + contextId: currentContextId, + })), ) + messages.push(...dbMsg) + if (messages.length > MAX_BUS_MESSAGE_SIZE) { + messages.splice(0, messages.length - BUS_MESSAGE_THRESHOLD) + } + for (const entry of listenerMap.entries()) { + entry[1].onMessage?.(dbMsg) + } } return r.length } @@ -161,37 +214,57 @@ const createArthasMessageBusInternal = ( } } + function close() { + state.isExcited = true + if (state.pullResultsTaskId) { + clearTimeout(state.pullResultsTaskId) + } + db.close() + } + return { launchPullResultTask, pullNow, addListener, removeListener, execute, - state, + messages, + close, } } -const useArthasMessageBus = (): ArthasMessageBus => { +const useArthasMessageBus = ( + channelId: string, +): ArthasMessageBus | undefined => { const dispatch = useDispatch() - const internalBus = useMemo( - () => createArthasMessageBusInternal(dispatch), - [dispatch], - ) + const [internalBus, setInternalBus] = useState< + ArthasMessageBusInternal | undefined + >() useEffect(() => { - const state = internalBus.state - state.isExcited = false - internalBus.launchPullResultTask() - dispatch(clearExpiredMessages()) + let isDestroyed = false + let myBus: ArthasMessageBusInternal | undefined + createArthasMessageBusInternal(channelId, dispatch) + .then((r) => { + if (isDestroyed) { + myBus = r + r.close() + } else { + setInternalBus(r) + r.launchPullResultTask() + } + }) + .catch((e) => { + console.error(e) + }) return () => { - state.isExcited = true - if (state.pullResultsTaskId) { - clearTimeout(state.pullResultsTaskId) + isDestroyed = true + if (myBus) { + myBus.close() } } - }, [dispatch, internalBus]) + }, [channelId, dispatch]) - // 偷个懒 return internalBus } diff --git a/spectre-frontend/src/store/channelSlice.ts b/spectre-frontend/src/store/channelSlice.ts index eead197..beffccc 100644 --- a/spectre-frontend/src/store/channelSlice.ts +++ b/spectre-frontend/src/store/channelSlice.ts @@ -1,7 +1,4 @@ -import type { - ArthasResponseWithId, - InputStatusResponse, -} from '@/api/impl/arthas.ts' +import type { InputStatusResponse } from '@/api/impl/arthas.ts' import { createSlice, type PayloadAction } from '@reduxjs/toolkit' type ChannelContext = { @@ -12,14 +9,6 @@ type ChannelContext = { } interface ChannelState { - /** - * channel id -> 缓存的消息 - */ - messages: Record - /** - * 记录上次更新时间,并删除长时间不更新的记录 - */ - updates: Record /** * 保存当前界面频道上下文. 不会被持久化 */ @@ -27,69 +16,18 @@ interface ChannelState { } const initialState: ChannelState = { - messages: {}, - updates: {}, context: { channelId: '-1', inputStatus: 'DISABLED', }, } -type AppendMessagePayload = { - channelId: string - messages: ArthasResponseWithId[] -} - export const channelSlice = createSlice({ name: 'channel', initialState, reducers: { - /** - * 追加消息 - */ - appendMessages(state, action: PayloadAction) { - const payload = action.payload - const messagesTarget = state.messages[payload.channelId] - if (messagesTarget) { - messagesTarget.push(...payload.messages) - } else { - state.messages[payload.channelId] = payload.messages - } - const len = state.messages[payload.channelId].length - if (len > 64) { - // TODO 优化代码,避免频繁清除 - state.messages[payload.channelId] = state.messages[ - payload.channelId - ].slice(len - 64) - } - state.updates[payload.channelId] = Date.now() - }, - clearExpiredMessages(state) { - const newMessage: Record = {} - const newUpdates: Record = {} - Object.entries(state.updates).forEach(([k, v]) => { - if (Date.now() - v < 1000 * 60 * 30) { - newMessage[k] = state.messages[k] - newUpdates[k] = v - } - }) - state.messages = newMessage - state.updates = newUpdates - }, - setupChannelContext(state, action: PayloadAction<{ channelId: string }>) { - const messages = state.messages[action.payload.channelId] ?? [] - let inputStatus: InputStatusResponse['inputStatus'] = 'DISABLED' - for (let i = messages.length - 1; i >= 0; i--) { - const msg = messages[i] - if (msg.type === 'input_status') { - inputStatus = (msg as InputStatusResponse).inputStatus - break - } - } - state.context = { - channelId: action.payload.channelId, - inputStatus, - } + setupChannelContext(state, action: PayloadAction) { + state.context = action.payload }, updateChannelContext( state, @@ -109,11 +47,6 @@ export const channelSlice = createSlice({ }, }) -export const { - clearExpiredMessages, - appendMessages, - setupChannelContext, - updateChannelContext, - updateInputStatus, -} = channelSlice.actions +export const { setupChannelContext, updateChannelContext, updateInputStatus } = + channelSlice.actions export default channelSlice.reducer