From fffaf1d9278a9b296eaa22225d9499482406abfe Mon Sep 17 00:00:00 2001 From: hai-tian Date: Tue, 7 Jan 2025 16:19:32 +0800 Subject: [PATCH] refactor(ui): beautify ai window style (#699) ## What type of PR is this? /kind refactor ## What this PR does / why we need it: ![image](https://github.com/user-attachments/assets/d064bc16-5841-482f-93aa-824cf8c07c3e) ![image](https://github.com/user-attachments/assets/716d7241-e2ae-4904-91bb-945969bfc10c) ![image](https://github.com/user-attachments/assets/de6b72a0-11a5-414d-853f-9090c8f42dc7) ![image](https://github.com/user-attachments/assets/0ab342d3-ac01-41e8-9990-a8a689aecb9b) ## Which issue(s) this PR fixes: Fixes # --- ui/.eslintrc.js | 3 +- ui/package.json | 1 + ui/src/assets/ai-rect.svg | 33 + ui/src/assets/ai-summary.svg | 16 + ui/src/components/yaml/index.tsx | 237 ++-- ui/src/components/yaml/styles.module.less | 698 ++++++---- ui/src/locales/de.json | 1 + ui/src/locales/en.json | 1 + ui/src/locales/pt.json | 1 + ui/src/locales/zh.json | 1 + .../components/eventAggregator/index.tsx | 91 +- .../eventAggregator/styles.module.less | 883 ++++++++---- .../components/podLogs/index.tsx | 97 +- .../components/podLogs/style.less | 75 -- .../components/podLogs/styles.module.less | 1199 ++++++++--------- .../components/topologyMap/index.tsx | 1 + 16 files changed, 1944 insertions(+), 1394 deletions(-) create mode 100644 ui/src/assets/ai-rect.svg create mode 100644 ui/src/assets/ai-summary.svg delete mode 100644 ui/src/pages/insightDetail/components/podLogs/style.less diff --git a/ui/.eslintrc.js b/ui/.eslintrc.js index e1f5fe02..6ef3b460 100644 --- a/ui/.eslintrc.js +++ b/ui/.eslintrc.js @@ -41,6 +41,7 @@ module.exports = { 'no-console': 'off', 'no-empty': 'off', 'no-unsafe-optional-chaining': 'off', + 'no-unused-vars': 'off', // ts rules:https://typescript-eslint.io/rules/ /** @typescript */ @@ -50,7 +51,7 @@ module.exports = { /** @react */ 'react-refresh/only-export-components': 'off', 'react/no-unescaped-entities': 'off', - 'react-hooks/exhaustive-deps': 'warn', + 'react-hooks/exhaustive-deps': 'off', 'react/display-name': 'off', 'react/prop-types': 'off', 'jsx-a11y/anchor-has-rel': 'off', diff --git a/ui/package.json b/ui/package.json index bac45935..ca839c48 100644 --- a/ui/package.json +++ b/ui/package.json @@ -45,6 +45,7 @@ "react": "18.2.0", "react-diff-viewer-continued": "^3.3.1", "react-dom": "18.2.0", + "react-full-screen": "^1.1.1", "react-grid-layout": "^1.3.4", "react-i18next": "^14.0.5", "react-markdown": "^9.0.1", diff --git a/ui/src/assets/ai-rect.svg b/ui/src/assets/ai-rect.svg new file mode 100644 index 00000000..73573284 --- /dev/null +++ b/ui/src/assets/ai-rect.svg @@ -0,0 +1,33 @@ + + + 矩形 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/assets/ai-summary.svg b/ui/src/assets/ai-summary.svg new file mode 100644 index 00000000..7e063a68 --- /dev/null +++ b/ui/src/assets/ai-summary.svg @@ -0,0 +1,16 @@ + + + 编组 3 + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/src/components/yaml/index.tsx b/ui/src/components/yaml/index.tsx index 37937276..4e3a3c57 100644 --- a/ui/src/components/yaml/index.tsx +++ b/ui/src/components/yaml/index.tsx @@ -1,13 +1,14 @@ import React, { useEffect, useRef, useState, useCallback } from 'react' import type { LegacyRef } from 'react' -import { Button, message, Space, Tooltip } from 'antd' +import { Alert, Button, message, Space, Spin, Tooltip } from 'antd' import { Resizable } from 're-resizable' import { useTranslation } from 'react-i18next' import { CopyOutlined, - RobotOutlined, CloseOutlined, PoweroffOutlined, + FullscreenExitOutlined, + FullscreenOutlined, } from '@ant-design/icons' import hljs from 'highlight.js' import yaml from 'js-yaml' @@ -16,14 +17,22 @@ import { yaml2json } from '@/utils/tools' import { useSelector } from 'react-redux' import Markdown from 'react-markdown' import axios from 'axios' +import { FullScreen, useFullScreenHandle } from 'react-full-screen' import i18n from '@/i18n' +import aiSummarySvg from '@/assets/ai-summary.svg' import styles from './styles.module.less' // eslint-disable-next-line @typescript-eslint/no-var-requires hljs.registerLanguage('yaml', require('highlight.js/lib/languages/yaml')) -type InterpretStatus = 'idle' | 'init' | 'streaming' | 'complete' | 'error' +type InterpretStatus = + | 'idle' + | 'init' + | 'streaming' + | 'complete' + | 'error' + | 'loading' type IProps = { data: any @@ -32,6 +41,7 @@ type IProps = { const Yaml = (props: IProps) => { const { t } = useTranslation() + const handle = useFullScreenHandle() const yamlRef = useRef | undefined>() const diagnosisContentRef = useRef(null) const interpretEndRef = useRef(null) @@ -193,6 +203,11 @@ const Yaml = (props: IProps) => { break case 'chunk': setInterpret(prev => prev + interpretEvent.content) + if (interpretEndRef.current) { + interpretEndRef.current.scrollIntoView({ + behavior: 'smooth', + }) + } break case 'error': streaming = false @@ -264,100 +279,136 @@ const Yaml = (props: IProps) => { }} >
-
- - {data && ( - - )} - {isAIEnabled && ( - - - - )} - -
-
-
- {interpretStatus !== 'idle' && ( -
-
- - - {t('YAML.InterpretResult')} - - - {interpretStatus === 'streaming' && ( - + +
+
+ + {data && ( + <> + {!handle.active && ( + + + )} + {handle.active && ( + + + + )} + +
+
+
+ + {interpretStatus !== 'idle' && ( +
+
+ +
+ ai summary +
+ {t('YAML.InterpretResult')} +
+ + {interpretStatus === 'streaming' && ( + +
+
+ {interpretStatus === 'loading' || + (interpretStatus === 'streaming' && !interpret) ? ( +
+ +

{t('EventAggregator.DiagnosisInProgress')}

+
+ ) : interpretStatus === 'streaming' ? ( +
+ {interpret} +
+ + + +
+
- -
-
-
- - {interpret} - - {interpretStatus === 'streaming' && ( -
- - - -
- )} -
-
+ ) : interpretStatus === 'error' ? ( + + ) : ( + {interpret} + )}
- )} -
+
+ )}
diff --git a/ui/src/components/yaml/styles.module.less b/ui/src/components/yaml/styles.module.less index aa338958..d863b99c 100644 --- a/ui/src/components/yaml/styles.module.less +++ b/ui/src/components/yaml/styles.module.less @@ -3,218 +3,227 @@ overflow-y: auto; border-radius: 8px; position: relative; - background: #1e1e1e; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + display: flex; + gap: 16px; + background: #fff; - &:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); - transform: translateY(-1px); + .fullScreenConatiner { + width: 100%; - .copy { - opacity: 1; - transform: scale(1.02); - } - } + .yaml_container { + height: 100%; + position: relative; + flex: 1; - .copy { - position: absolute; - top: 16px; - right: 24px; - z-index: 10; - opacity: 0.95; - transform: translateY(0); - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + transform: translateY(-1px); + border-radius: 8px; - &::before { - content: ''; - position: absolute; - top: -2px; - left: -2px; - right: -2px; - bottom: -2px; - background: linear-gradient(45deg, #7cdce7, #7ec699); - border-radius: 6px; - z-index: -1; - opacity: 0; - transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } + .copy { + opacity: 1; + transform: scale(1.02); + } + } - :global { - .ant-btn { - background: rgba(30, 30, 30, 0.95); - color: #7cdce7; - border: 1px solid rgba(124, 220, 231, 0.3); - height: 28px; - padding: 10px; - font-weight: 500; - font-family: 'JetBrains Mono', monospace; - letter-spacing: 0.5px; - backdrop-filter: blur(4px); + .copy { + position: absolute; + top: 16px; + right: 24px; + z-index: 10; + opacity: 0.95; + transform: translateY(0); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 0 2px rgba(20, 20, 20, 0.3); - border-radius: 4px; - display: flex; - align-items: center; - gap: 6px; - .anticon { - font-size: 14px; - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &::before { + content: ''; + position: absolute; + top: -2px; + left: -2px; + right: -2px; + bottom: -2px; + background: linear-gradient(45deg, #7cdce7, #7ec699); + border-radius: 6px; + z-index: -1; + opacity: 0; + transition: opacity 0.3s cubic-bezier(0.4, 0, 0.2, 1); } - &:hover { - color: #fff; - border-color: rgba(124, 220, 231, 0.4); - transform: translateY(-1px); - box-shadow: 0 0 3px rgba(20, 20, 20, 0.4); - background: rgba(35, 35, 35, 0.95); + :global { + .ant-btn { + background: rgba(30, 30, 30, 0.95); + color: #7cdce7; + border: 1px solid rgba(124, 220, 231, 0.3); + height: 28px; + font-weight: 500; + font-family: 'JetBrains Mono', monospace; + letter-spacing: 0.5px; + backdrop-filter: blur(4px); + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 0 2px rgba(20, 20, 20, 0.3); + border-radius: 4px; + display: flex; + align-items: center; + gap: 6px; + + .anticon { + font-size: 14px; + transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } - .anticon { - transform: scale(1.1); + &:hover { + color: #fff; + border-color: rgba(124, 220, 231, 0.4); + transform: translateY(-1px); + box-shadow: 0 0 3px rgba(20, 20, 20, 0.4); + background: rgba(35, 35, 35, 0.95); + + .anticon { + transform: scale(1.1); + } + } + + &:active { + transform: translateY(0); + box-shadow: 0 0 1px rgba(20, 20, 20, 0.3); + background: rgba(28, 28, 28, 0.95); + } } } - &:active { - transform: translateY(0); - box-shadow: 0 0 1px rgba(20, 20, 20, 0.3); - background: rgba(28, 28, 28, 0.95); + &:hover { + &::before { + opacity: 0.1; + } } } - } - - &:hover { - &::before { - opacity: 0.1; - } - } - } - - .yaml_container { - height: 100%; - position: relative; - - .yaml_box { - width: 100%; - height: 100%; - padding: 16px 20px; - overflow-y: auto; - font-family: 'JetBrains Mono', 'Fira Code', Menlo, Monaco, Consolas, - 'Courier New', monospace; - font-size: 13px; - line-height: 1.6; - color: #e0e0e0; - word-break: break-all; - white-space: pre-wrap; - background-color: #1e1e1e; - border-radius: 8px; - box-sizing: border-box; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - - &::-webkit-scrollbar { - width: 8px; - height: 8px; - } - &::-webkit-scrollbar-track { - background: rgba(255, 255, 255, 0.05); - border-radius: 4px; + .magicWand { + font-size: 16px; + line-height: 1; + display: inline-block; + transform: translateY(1px); + filter: drop-shadow(0 0 4px rgba(147, 112, 219, 0.4)); + animation: sparkle 1.5s ease-in-out infinite; } - &::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.1); - border-radius: 4px; + .yaml_box { + width: 100%; + height: 100%; + padding: 16px 20px; + overflow-y: auto; + font-family: 'JetBrains Mono', 'Fira Code', Menlo, Monaco, Consolas, + 'Courier New', monospace; + font-size: 13px; + line-height: 1.6; + color: #e0e0e0; + word-break: break-all; + white-space: pre-wrap; + background-color: #1e1e1e; + border-radius: 8px; + box-sizing: border-box; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - &:hover { - background: rgba(255, 255, 255, 0.2); + &::-webkit-scrollbar { + width: 8px; + height: 8px; } - } - :global { - .hljs { - background: transparent; - padding: 0; - transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &::-webkit-scrollbar-track { + background: rgba(147, 112, 219, 0.05); + border-radius: 4px; + } - &-attr { - color: #7cdce7; + &::-webkit-scrollbar-thumb { + background: rgba(147, 112, 219, 0.2); + border-radius: 4px; + border: 2px solid transparent; + background-clip: padding-box; + + &:hover { + background: rgba(147, 112, 219, 0.3); + border: 2px solid transparent; + background-clip: padding-box; + } + } + + :global { + .hljs { + background: transparent; + padding: 0; transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); - &:hover { - color: lighten(#7cdce7, 10%); + &-attr { + color: #7cdce7; + transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &:hover { + color: lighten(#7cdce7, 10%); + } } - } - &-string { - color: #7ec699; - transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &-string { + color: #7ec699; + transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); - &:hover { - color: lighten(#7ec699, 10%); + &:hover { + color: lighten(#7ec699, 10%); + } } - } - &-number { - color: #f08d49; - transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &-number { + color: #f08d49; + transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); - &:hover { - color: lighten(#f08d49, 10%); + &:hover { + color: lighten(#f08d49, 10%); + } } - } - &-boolean { - color: #cc99cd; - transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &-boolean { + color: #cc99cd; + transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); - &:hover { - color: lighten(#cc99cd, 10%); + &:hover { + color: lighten(#cc99cd, 10%); + } } - } - &-null { - color: #cc99cd; - transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); + &-null { + color: #cc99cd; + transition: color 0.3s cubic-bezier(0.4, 0, 0.2, 1); - &:hover { - color: lighten(#cc99cd, 10%); + &:hover { + color: lighten(#cc99cd, 10%); + } } } } } } + } - .diagnosisPanel { - position: absolute; - right: 12px; - top: 12px; - width: 400px; - max-height: calc(100% - 24px); - background: #2b1d3c; - border-radius: 12px; - display: flex; - flex-direction: column; - border: 1px solid rgba(147, 112, 219, 0.2); - box-shadow: - 0 8px 24px rgba(0, 0, 0, 0.3), - 0 2px 4px rgba(147, 112, 219, 0.1), - 0 0 1px rgba(147, 112, 219, 0.2); - backdrop-filter: blur(8px); - z-index: 10; - animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - .diagnosisHeader { + .yaml_content_diagnosisPanel { + box-sizing: border-box; + width: 400px; + min-height: 200px; + max-height: min(100%, 600px); + background: #1e1e1e; + border-radius: 12px; + display: flex; + flex-direction: column; + backdrop-filter: blur(8px); + z-index: 10; + animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + .yaml_content_diagnosisHeader { display: flex; justify-content: space-between; align-items: center; padding: 12px; - background: linear-gradient( - 135deg, - rgba(147, 112, 219, 0.15) 0%, - rgba(43, 29, 60, 0.95) 100% - ); + background: #1e1e1e; border-bottom: 1px solid rgba(147, 112, 219, 0.2); border-radius: 12px 12px 0 0; color: #e6e6fa; @@ -222,40 +231,15 @@ font-weight: 500; backdrop-filter: blur(4px); - .anticon { - margin-right: 8px; - font-size: 16px; - color: #9370db; - text-shadow: 0 0 8px rgba(147, 112, 219, 0.4); - } + .yaml_content_diagnosisHeader_aiIcon { + width: 18px; + height: 18px; - button { - color: rgba(230, 230, 250, 0.85); - transition: all 0.3s ease; - border-radius: 6px; - width: 28px; - height: 28px; - display: flex; - align-items: center; - justify-content: center; - - &:hover { - color: #fff; - background: rgba(147, 112, 219, 0.2); - transform: scale(1.05); - } - - &:active { - transform: scale(0.95); + img { + width: 100%; + height: 100%; } } - } - - .diagnosisBody { - flex: 1; - overflow-y: auto; - background: #1e1e1e; - color: #d4d4d4; .stopButton { color: rgba(255, 255, 255, 0.45); @@ -311,6 +295,47 @@ } } + .anticon { + margin-right: 8px; + font-size: 16px; + color: #9370db; + text-shadow: 0 0 8px rgba(147, 112, 219, 0.4); + } + + button { + color: rgba(230, 230, 250, 0.85); + transition: all 0.3s ease; + border-radius: 6px; + width: 28px; + height: 28px; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + color: #fff; + background: rgba(147, 112, 219, 0.2); + transform: scale(1.05); + } + + &:active { + transform: scale(0.95); + } + } + } + + .yaml_content_diagnosisContent { + width: 400px; + box-sizing: border-box; + height: 100%; + overflow-y: auto; + color: #d4d4d4; + background: #1e1e1e; + border-radius: 0 0 8px 8px; + padding: 16px; + font-size: 14px; + line-height: 1.6; + &::-webkit-scrollbar { width: 8px; height: 8px; @@ -324,21 +349,17 @@ &::-webkit-scrollbar-thumb { background: rgba(147, 112, 219, 0.2); border-radius: 4px; + border: 2px solid transparent; + background-clip: padding-box; &:hover { background: rgba(147, 112, 219, 0.3); + border: 2px solid transparent; + background-clip: padding-box; } } - } - .diagnosisContent { - padding: 16px; - overflow-y: auto; - height: 100%; - position: relative; - scroll-behavior: smooth; - - .streamingIndicator { + .yaml_content_streamingIndicator { margin: 18px 0; display: flex; align-items: center; @@ -347,7 +368,7 @@ .dot { width: 6px; height: 6px; - background-color: #1890ff; + background-color: #4447c3; border-radius: 50%; opacity: 0.3; animation: dotPulse 1.4s infinite; @@ -362,101 +383,192 @@ } } - .markdownContent { - :global { - h1 { - font-size: 16px !important; - margin-top: 12px; - margin-bottom: 6px; - font-weight: 500; - } + .yaml_content_diagnosisLoading { + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 16px; + color: rgba(230, 230, 250, 0.85); - h2 { - font-size: 15px !important; - margin-top: 10px; - margin-bottom: 6px; - font-weight: 500; + :global(.ant-spin) { + .ant-spin-dot-item { + background-color: #9370db; } + } + + p { + font-size: 14px; + color: rgba(230, 230, 250, 0.85); + } + } + + h1, + h2, + h3 { + color: #e6e6fa; + margin-bottom: 16px; + font-weight: 600; + letter-spacing: -0.01em; + } + + h1 { + font-size: 20px; + } + + h2 { + font-size: 18px; + margin-top: 24px; + padding-bottom: 8px; + border-bottom: 1px solid rgba(147, 112, 219, 0.2); + } + h3 { + font-size: 16px; + margin-top: 20px; + } + + p { + margin-bottom: 16px; + line-height: 1.7; + } + + ul, + ol { + padding-left: 24px; + margin-bottom: 16px; + } + + li { + margin-bottom: 8px; + } + + code { + background: rgba(147, 112, 219, 0.1); + padding: 2px 6px; + border-radius: 4px; + font-family: 'Menlo', 'Monaco', 'Courier New', monospace; + font-size: 13px; + } + + pre { + background: rgba(20, 15, 30, 0.8); + padding: 16px; + border-radius: 8px; + margin-bottom: 16px; + overflow-x: auto; + border: 1px solid rgba(147, 112, 219, 0.1); + + code { + background: none; + padding: 0; + } + } + + :global { + .markdown-body { + color: #d4d4d4; + background: transparent; + padding: 16px; + + h1, + h2, h3, h4, h5, h6 { - font-size: 14px !important; - margin-top: 8px; - margin-bottom: 6px; - font-weight: 500; + color: rgba(230, 230, 250, 0.95); + border-bottom: 1px solid rgba(147, 112, 219, 0.2); + margin-top: 24px; + margin-bottom: 16px; + font-weight: 600; + line-height: 1.25; } - p { - font-size: 14px !important; - margin-bottom: 6px; - line-height: 1.5; + h1 { + font-size: 24px; } - ul, - ol { - font-size: 14px !important; - margin-bottom: 6px; - padding-left: 16px; + h2 { + font-size: 20px; } - li { - font-size: 14px !important; - margin-bottom: 3px; - line-height: 1.5; + h3 { + font-size: 18px; } - code { - font-size: 14px !important; - padding: 1px 3px; - background-color: rgba(0, 0, 0, 0.04); - border-radius: 2px; + h4 { + font-size: 16px; } - pre { - margin-bottom: 6px; - code { - font-size: 14px !important; - padding: 8px; - background-color: rgba(0, 0, 0, 0.04); - border-radius: 4px; - } + h5 { + font-size: 14px; } - h1, - h2, - h3, - h4, - h5, h6 { - color: #e6e6fa; - border-bottom-color: rgba(147, 112, 219, 0.2); + font-size: 13px; } pre { - background: rgba(30, 30, 30, 0.6); - border-radius: 8px; - border: 1px solid rgba(147, 112, 219, 0.1); + background: rgba(0, 0, 0, 0.4); + border: 1px solid rgba(147, 112, 219, 0.5); + border-radius: 6px; + padding: 12px; + margin: 12px 0; + overflow-x: auto; code { background: transparent; - color: #d4d4d4; padding: 0; + color: #f0e6ff; + } + } + + code { + background: rgba(147, 112, 219, 0.2); + padding: 2px 6px; + border-radius: 4px; + color: #f0e6ff; + font-family: 'Menlo', 'Monaco', 'Courier New', monospace; + } + + ul, + ol { + padding-left: 24px; + margin: 8px 0; + + li { + margin: 4px 0; } } blockquote { + color: rgba(230, 230, 250, 0.85); + border-left: 4px solid rgba(147, 112, 219, 0.4); + background: rgba(147, 112, 219, 0.1); + margin: 16px 0; + padding: 12px 16px; + border-radius: 0 8px 8px 0; + } + + a { color: #9370db; - border-left-color: rgba(147, 112, 219, 0.4); - background: rgba(147, 112, 219, 0.05); - padding: 8px 16px; - border-radius: 4px; + text-decoration: none; + transition: all 0.2s ease; + border-bottom: 1px solid transparent; + + &:hover { + color: lighten(#9370db, 10%); + border-bottom-color: currentColor; + } } table { border-collapse: separate; border-spacing: 0; + width: 100%; + margin: 16px 0; border-radius: 8px; border: 1px solid rgba(147, 112, 219, 0.2); overflow: hidden; @@ -464,43 +576,45 @@ th, td { border: 1px solid rgba(147, 112, 219, 0.2); - padding: 8px 12px; + padding: 12px; } th { background: rgba(147, 112, 219, 0.1); - color: #e6e6fa; + font-weight: 600; + text-align: left; } - tr:nth-child(even) { - background: rgba(30, 30, 30, 0.3); - } + tr { + background-color: transparent; + transition: background-color 0.2s ease; - tr:hover { - background: rgba(147, 112, 219, 0.05); + &:nth-child(2n) { + background-color: rgba(147, 112, 219, 0.05); + } + + &:hover { + background-color: rgba(147, 112, 219, 0.1); + } } } - } - } - } - .diagnosisLoading { - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 16px; - color: rgba(230, 230, 250, 0.85); - padding: 24px; - - :global(.ant-spin) { - .ant-spin-dot-item { - background-color: #9370db; + hr { + border: none; + height: 1px; + background: linear-gradient(to right, + rgba(147, 112, 219, 0.1), + rgba(147, 112, 219, 0.4), + rgba(147, 112, 219, 0.1)); + margin: 24px 0; + } } } } } + + + } .resizeHandle { @@ -548,20 +662,12 @@ } } -.magicWand { - font-size: 16px; - line-height: 1; - display: inline-block; - transform: translateY(1px); - filter: drop-shadow(0 0 4px rgba(147, 112, 219, 0.4)); - animation: sparkle 1.5s ease-in-out infinite; -} - @keyframes slideIn { from { opacity: 0; transform: translateX(20px); } + to { opacity: 1; transform: translateX(0); @@ -569,11 +675,13 @@ } @keyframes sparkle { + 0%, 100% { filter: drop-shadow(0 0 4px rgba(147, 112, 219, 0.4)); transform: translateY(1px) scale(1); } + 50% { filter: drop-shadow(0 0 8px rgba(147, 112, 219, 0.6)); transform: translateY(1px) scale(1.1); @@ -584,9 +692,11 @@ 0% { opacity: 0.3; } + 50% { opacity: 1; } + 100% { opacity: 0.3; } diff --git a/ui/src/locales/de.json b/ui/src/locales/de.json index 10d852c8..311bc937 100644 --- a/ui/src/locales/de.json +++ b/ui/src/locales/de.json @@ -56,6 +56,7 @@ "LogAggregator.SearchPlaceholder": "Protokolle durchsuchen...", "LogAggregator.SearchModeHighlight": "Hervorhebungsmodus", "LogAggregator.SearchModeFilter": "Filtermodus", + "LogAggregator.FullScreen": "Vollbild", "EventAggregator": "Ereignis", "EventAggregator.Type": "Ereignistyp", "EventAggregator.Normal": "Normal", diff --git a/ui/src/locales/en.json b/ui/src/locales/en.json index f451879b..c5eb8fbe 100644 --- a/ui/src/locales/en.json +++ b/ui/src/locales/en.json @@ -56,6 +56,7 @@ "LogAggregator.SearchPlaceholder": "Search logs...", "LogAggregator.SearchModeHighlight": "Highlight Mode", "LogAggregator.SearchModeFilter": "Filter Mode", + "LogAggregator.FullScreen": "Full Screen", "EventAggregator": "Events", "EventAggregator.Type": "Event Type", "EventAggregator.Normal": "Normal", diff --git a/ui/src/locales/pt.json b/ui/src/locales/pt.json index 58c120cf..9b8be3c4 100644 --- a/ui/src/locales/pt.json +++ b/ui/src/locales/pt.json @@ -58,6 +58,7 @@ "LogAggregator.SearchPlaceholder": "Pesquisar logs...", "LogAggregator.SearchModeHighlight": "Modo de Destaque", "LogAggregator.SearchModeFilter": "Modo de Filtro", + "LogAggregator.FullScreen": "Tela Cheia", "EventAggregator": "Evento", "EventAggregator.Type": "Tipo de Evento", "EventAggregator.Normal": "Normal", diff --git a/ui/src/locales/zh.json b/ui/src/locales/zh.json index f387bb7f..56aaafef 100644 --- a/ui/src/locales/zh.json +++ b/ui/src/locales/zh.json @@ -56,6 +56,7 @@ "LogAggregator.SearchPlaceholder": "搜索日志...", "LogAggregator.SearchModeHighlight": "高亮模式", "LogAggregator.SearchModeFilter": "过滤模式", + "LogAggregator.FullScreen": "全屏", "EventAggregator": "事件", "EventAggregator.Type": "事件类型", "EventAggregator.Normal": "正常", diff --git a/ui/src/pages/insightDetail/components/eventAggregator/index.tsx b/ui/src/pages/insightDetail/components/eventAggregator/index.tsx index 33747dbe..94173fde 100644 --- a/ui/src/pages/insightDetail/components/eventAggregator/index.tsx +++ b/ui/src/pages/insightDetail/components/eventAggregator/index.tsx @@ -14,7 +14,6 @@ import { } from 'antd' import { SearchOutlined, - RobotOutlined, CloseOutlined, PoweroffOutlined, } from '@ant-design/icons' @@ -22,10 +21,12 @@ import { useTranslation } from 'react-i18next' import { formatTime } from '@/utils/tools' import axios from 'axios' import classNames from 'classnames' -import styles from './styles.module.less' import Markdown from 'react-markdown' import { useSelector } from 'react-redux' -import debounce from 'lodash.debounce' +import { debounce } from 'lodash' +import aiSummarySvg from '@/assets/ai-summary.svg' + +import styles from './styles.module.less' interface Event { type: string @@ -95,19 +96,19 @@ const EventAggregator: React.FC = ({ eventSource.current.onmessage = event => { try { - const events: Event[] = JSON.parse(event.data) + const events: Event[] = JSON.parse(event?.data) if ( - events.length === 1 && - events[0].type === 'Warning' && - events[0].reason === 'Error' + events?.length === 1 && + events?.[0]?.type === 'Warning' && + events?.[0].reason === 'Error' ) { setError(events[0].message) return } setEvents(events) - setHasEvents(events.length > 0 || hasEvents) + setHasEvents(events?.length > 0 || hasEvents) } catch (error) { setError(t('EventAggregator.ConnectionError')) } @@ -338,16 +339,32 @@ const EventAggregator: React.FC = ({ debouncedDiagnose() }, [debouncedDiagnose]) + const isVertical = filteredEvents?.length <= 6 + const events_content_styles: React.CSSProperties = isVertical + ? { flexDirection: 'column' } + : { flexDirection: 'row' } + const events_content_withDiagnosis_style: React.CSSProperties = isVertical + ? { width: '100%' } + : { width: 'calc(100% - 424px)', height: 600, overflowY: 'scroll' } + const events_content_diagnosisPanel_style: React.CSSProperties = isVertical + ? { width: '100%', height: 300 } + : { width: 400, height: 600 } + const renderDiagnosisWindow = () => { if (diagnosisStatus === 'idle') { return null } return ( -
-
+
+
- +
+ ai summary +
{t('EventAggregator.DiagnosisResult')}
@@ -358,7 +375,7 @@ const EventAggregator: React.FC = ({ >
-
+
{diagnosisStatus === 'loading' || (diagnosisStatus === 'streaming' && !diagnosis) ? ( -
+

{t('EventAggregator.DiagnosisInProgress')}

@@ -398,10 +415,17 @@ const EventAggregator: React.FC = ({ description={t('EventAggregator.TryAgainLater')} /> ) : ( -
+ <> {diagnosis} + {diagnosisStatus === 'streaming' && ( +
+ + + +
+ )}
-
+ )}
@@ -409,13 +433,16 @@ const EventAggregator: React.FC = ({ } return ( -
-
-
+
+
+
{hasEvents && ( <> } allowClear @@ -423,7 +450,7 @@ const EventAggregator: React.FC = ({ onChange={e => handleSearch(e.target.value)} />