Skip to content

Commit

Permalink
refactor(ui): beautify ai window style (#699)
Browse files Browse the repository at this point in the history
## 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:

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related
issues/tests in a comment and do not use `Fixes`_*
-->

Fixes #
  • Loading branch information
hai-tian authored Jan 7, 2025
1 parent 75d576f commit fffaf1d
Show file tree
Hide file tree
Showing 16 changed files with 1,944 additions and 1,394 deletions.
3 changes: 2 additions & 1 deletion ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand All @@ -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',
Expand Down
1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
33 changes: 33 additions & 0 deletions ui/src/assets/ai-rect.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions ui/src/assets/ai-summary.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
237 changes: 144 additions & 93 deletions ui/src/components/yaml/index.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -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
Expand All @@ -32,6 +41,7 @@ type IProps = {

const Yaml = (props: IProps) => {
const { t } = useTranslation()
const handle = useFullScreenHandle()
const yamlRef = useRef<LegacyRef<HTMLDivElement> | undefined>()
const diagnosisContentRef = useRef<HTMLDivElement>(null)
const interpretEndRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -264,100 +279,136 @@ const Yaml = (props: IProps) => {
}}
>
<div className={styles.yaml_content} style={{ height: props?.height }}>
<div className={styles.copy}>
<Space>
{data && (
<Button
type="primary"
size="small"
onClick={copy}
disabled={!data}
icon={<CopyOutlined />}
>
{t('Copy')}
</Button>
)}
{isAIEnabled && (
<Tooltip title={t('YAML.Interpret')}>
<Button
type="primary"
size="small"
icon={<span className={styles.magicWand}></span>}
onClick={handleInterpret}
disabled={!data || isStreaming}
>
{t('YAML.Interpret')}
</Button>
</Tooltip>
)}
</Space>
</div>
<div className={styles.yaml_container}>
<div
className={styles.yaml_box}
style={{ height: props?.height }}
ref={yamlRef as any}
/>
{interpretStatus !== 'idle' && (
<div className={styles.diagnosisPanel}>
<div className={styles.diagnosisHeader}>
<Space>
<RobotOutlined />
{t('YAML.InterpretResult')}
</Space>
<Space>
{interpretStatus === 'streaming' && (
<Tooltip
title={t('YAML.StopInterpret')}
placement="bottom"
>
<FullScreen handle={handle} className={styles.fullScreenConatiner}>
<div className={styles.yaml_container}>
<div className={styles.copy}>
<Space>
{data && (
<>
{!handle.active && (
<Tooltip title={t('LogAggregator.FullScreen')}>
<Button
type="text"
icon={<FullscreenOutlined />}
onClick={handle.enter}
/>
</Tooltip>
)}
{!handle.active && (
<Button
type="text"
className={styles.stopButton}
icon={<PoweroffOutlined />}
onClick={() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort()
setInterpretStatus('complete' as InterpretStatus)
}
}}
/>
</Tooltip>
)}
<Button
type="text"
icon={<CloseOutlined />}
onClick={() =>
setInterpretStatus('idle' as InterpretStatus)
}
type="primary"
size="small"
onClick={copy}
disabled={!data}
icon={<CopyOutlined />}
>
{t('Copy')}
</Button>
)}
{handle.active && (
<Tooltip title={t('LogAggregator.FullScreen')}>
<Button
type="text"
icon={<FullscreenExitOutlined />}
onClick={handle.exit}
/>
</Tooltip>
)}
</>
)}
{isAIEnabled && !handle.active && (
<Tooltip title={t('YAML.Interpret')}>
<Button
type="primary"
size="small"
icon={<span className={styles.magicWand}></span>}
onClick={handleInterpret}
disabled={!data || isStreaming}
>
<span style={{ fontSize: 14, marginTop: 2 }}>
{t('YAML.Interpret')}
</span>
</Button>
</Tooltip>
)}
</Space>
</div>
<div
className={styles.yaml_box}
style={{ height: props?.height }}
ref={yamlRef as any}
/>
</div>
</FullScreen>
{interpretStatus !== 'idle' && (
<div
className={styles.yaml_content_diagnosisPanel}
style={{ height: moduleHeight }}
>
<div className={styles.yaml_content_diagnosisHeader}>
<Space>
<div className={styles.yaml_content_diagnosisHeader_aiIcon}>
<img src={aiSummarySvg} alt="ai summary" />
</div>
{t('YAML.InterpretResult')}
</Space>
<Space>
{interpretStatus === 'streaming' && (
<Tooltip title={t('YAML.StopInterpret')} placement="bottom">
<Button
type="text"
className={styles.stopButton}
icon={<PoweroffOutlined />}
onClick={() => {
if (abortControllerRef.current) {
abortControllerRef.current.abort()
setInterpretStatus('complete' as InterpretStatus)
}
}}
/>
</Tooltip>
)}
<Button
type="text"
icon={<CloseOutlined />}
onClick={() =>
setInterpretStatus('idle' as InterpretStatus)
}
/>
</Space>
</div>
<div className={styles.yaml_content_diagnosisContent}>
{interpretStatus === 'loading' ||
(interpretStatus === 'streaming' && !interpret) ? (
<div className={styles.yaml_content_diagnosisLoading}>
<Spin />
<p>{t('EventAggregator.DiagnosisInProgress')}</p>
</div>
) : interpretStatus === 'streaming' ? (
<div>
<Markdown>{interpret}</Markdown>
<div className={styles.yaml_content_streamingIndicator}>
<span className={styles.dot}></span>
<span className={styles.dot}></span>
<span className={styles.dot}></span>
</div>
<div
ref={interpretEndRef}
style={{ float: 'left', clear: 'both' }}
/>
</Space>
</div>
<div className={styles.diagnosisBody}>
<div
className={styles.diagnosisContent}
ref={diagnosisContentRef}
>
<Markdown
className={styles.markdownContent}
rehypePlugins={[]}
remarkPlugins={[]}
>
{interpret}
</Markdown>
{interpretStatus === 'streaming' && (
<div className={styles.streamingIndicator}>
<span className={styles.dot}></span>
<span className={styles.dot}></span>
<span className={styles.dot}></span>
</div>
)}
<div ref={interpretEndRef} />
</div>
</div>
) : interpretStatus === 'error' ? (
<Alert
type="error"
message={t('EventAggregator.DiagnosisFailed')}
description={t('EventAggregator.TryAgainLater')}
/>
) : (
<Markdown>{interpret}</Markdown>
)}
</div>
)}
</div>
</div>
)}
</div>
</Resizable>
</div>
Expand Down
Loading

0 comments on commit fffaf1d

Please sign in to comment.