Skip to content

useXChat onRequest return message id #704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 42 additions & 17 deletions .dumi/components/SemanticPreview.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { XProvider } from '@ant-design/x';
import { Col, Flex, Row, Tag, Typography, theme } from 'antd';
import { Col, Flex, Popover, Row, Tag, Typography, theme } from 'antd';
import { createStyles, css } from 'antd-style';
import classnames from 'classnames';
/* eslint-disable react-hooks-extra/no-direct-set-state-in-use-effect */
import React from 'react';

const MARK_BORDER_SIZE = 2;
Expand Down Expand Up @@ -66,13 +67,14 @@ const useStyle = createStyles(({ token }, markPos: [number, number, number, numb
}));

export interface SemanticPreviewProps {
componentName: string;
semantics: { name: string; desc: string; version?: string }[];
children: React.ReactElement | ((injectProps: any) => React.ReactElement);
height?: number;
}

const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
const { semantics = [], children, height } = props;
const { semantics = [], children, height, componentName = 'Component' } = props;
const { token } = theme.useToken();

// ======================= Semantic =======================
Expand Down Expand Up @@ -117,12 +119,14 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
const targetElement = containerRef.current?.querySelector<HTMLElement>(`.${targetClassName}`);
const containerRect = containerRef.current?.getBoundingClientRect();
const targetRect = targetElement?.getBoundingClientRect();

setMarkPos([
(targetRect?.left || 0) - (containerRect?.left || 0),
(targetRect?.top || 0) - (containerRect?.top || 0),
targetRect?.width || 0,
targetRect?.height || 0,
]);

timerRef.current = setTimeout(() => {
setPositionMotion(true);
}, 10);
Expand All @@ -148,24 +152,45 @@ const SemanticPreview: React.FC<SemanticPreviewProps> = (props) => {
<Col span={8}>
<ul className={classnames(styles.listWrap)}>
{semantics.map<React.ReactNode>((semantic) => (
<li
<Popover
key={semantic.name}
className={classnames(styles.listItem)}
onMouseEnter={() => setHoverSemantic(semantic.name)}
onMouseLeave={() => setHoverSemantic(null)}
content={
<Typography style={{ fontSize: 12, minWidth: 300 }}>
<pre dir="ltr">
<code dir="ltr">
{`<${componentName}
classNames={{
${semantic.name}: 'my-${componentName.toLowerCase()}',
}}
styles={{
${semantic.name}: { color: 'red' },
}}
>
...
</${componentName}>`}
</code>
</pre>
</Typography>
}
>
<Flex vertical gap="small">
<Flex gap="small" align="center">
<Typography.Title level={5} style={{ margin: 0 }}>
{semantic.name}
</Typography.Title>
{semantic.version && <Tag color="blue">{semantic.version}</Tag>}
<li
className={classnames(styles.listItem)}
onMouseEnter={() => setHoverSemantic(semantic.name)}
onMouseLeave={() => setHoverSemantic(null)}
>
<Flex vertical gap="small">
<Flex gap="small" align="center">
<Typography.Title level={5} style={{ margin: 0 }}>
{semantic.name}
</Typography.Title>
{semantic.version && <Tag color="blue">{semantic.version}</Tag>}
</Flex>
<Typography.Paragraph style={{ margin: 0, fontSize: token.fontSizeSM }}>
{semantic.desc}
</Typography.Paragraph>
</Flex>
<Typography.Paragraph style={{ margin: 0, fontSize: token.fontSizeSM }}>
{semantic.desc}
</Typography.Paragraph>
</Flex>
</li>
</li>
</Popover>
))}
</ul>
</Col>
Expand Down
2 changes: 1 addition & 1 deletion .dumi/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@

html {
scrollbar-width: thin;
scrollbar-color: unset;
scrollbar-color: #eaeaea transparent;
}
4 changes: 0 additions & 4 deletions .dumi/hooks/useDark.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
import React from 'react';

export const DarkContext = React.createContext(false);

export default function useDark() {
return React.useContext(DarkContext);
}
4 changes: 2 additions & 2 deletions .dumi/hooks/useFetch/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import fetch from 'cross-fetch';
import React from 'react';

import use from '../use';
import FetchCache from './cache';

const cache = new FetchCache();
Expand All @@ -15,7 +15,7 @@ const useFetch = <T>(options: string | { request: () => PromiseLike<T>; key: str
request = options.request;
key = options.key;
}
return use(cache.promise<T>(key, request));
return React.use<T>(cache.promise<T>(key, request));
};

export default useFetch;
73 changes: 55 additions & 18 deletions .dumi/hooks/useLottie.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,77 @@ interface UseLottieOptions extends Omit<AnimationConfig, 'container' | 'renderer
path?: string;
}

// 用于确保 lottie-web 只加载一次
let lottiePromise: Promise<LottiePlayer> | null = null;

const loadLottie = async (): Promise<LottiePlayer> => {
if (!lottiePromise) {
lottiePromise = new Promise((resolve, reject) => {
if ((window as any)?.lottie) {
resolve((window as any).lottie);
return;
}

const script = document.createElement('script');
script.src =
'https://gw.alipayobjects.com/os/lib/lottie-web/5.12.2/build/player/lottie_svg.min.js';
script.async = true;
script.onload = () => resolve((window as any).lottie);
script.onerror = reject;
document.body.appendChild(script);
});
}
return lottiePromise;
};

const useLottie = (options: UseLottieOptions) => {
const { lazyLoad = true, rootMargin = '200px', disabled = false, ...lottieOptions } = options;
const stableLottieOptions = React.useMemo(() => lottieOptions, []);

const containerRef = React.useRef<HTMLDivElement>(null);
const [isIntersected, setIsIntersected] = React.useState(!lazyLoad);
const [animationInstance, setAnimationInstance] = React.useState<AnimationItem | null>(null);
const animationInstanceRef = React.useRef<AnimationItem | null>(null);
const [error, setError] = React.useState<Error | null>(null);

React.useEffect(() => {
if (disabled) return;
let mounted = true;

const initAnimation = async () => {
if (!animationInstanceRef.current && (!lazyLoad || isIntersected)) {
if (!containerRef.current) return;

let animation: AnimationItem | undefined;
try {
const lottie = await loadLottie();

const lottie: LottiePlayer = (window as any)?.lottie;
if (!mounted) return;

if (!animationInstance) {
if (!lazyLoad || isIntersected) {
if (containerRef.current && lottie) {
animation = lottie.loadAnimation({
const animation = lottie.loadAnimation({
container: containerRef.current,
renderer: 'svg', // 默认使用 SVG 渲染,性能更好
...stableLottieOptions,
});

setAnimationInstance(animation);
animationInstanceRef.current = animation;
} catch (err) {
if (mounted) {
setError(err as Error);
console.error('Failed to load Lottie animation:', err);
}
}
}
} else {
return () => {
if (animation) {
animation.destroy();
setAnimationInstance(null);
}
};
}
}, [isIntersected, lazyLoad, stableLottieOptions, animationInstance, disabled]);
};

initAnimation();

return () => {
mounted = false;
if (animationInstanceRef.current) {
animationInstanceRef.current.destroy();
animationInstanceRef.current = null;
}
};
}, [isIntersected, lazyLoad, stableLottieOptions, disabled]);

React.useEffect(() => {
if (disabled) return;
Expand Down Expand Up @@ -72,9 +108,10 @@ const useLottie = (options: UseLottieOptions) => {

return [
containerRef,
animationInstance,
animationInstanceRef.current,
{
isIntersected,
error,
},
] as const;
};
Expand Down
38 changes: 25 additions & 13 deletions .dumi/hooks/useMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { version } from '@ant-design/x';
import type { MenuProps } from 'antd';
import { Space, Tag } from 'antd';
import { Flex, Tag, version } from 'antd';
import { createStyles } from 'antd-style';
import classnames from 'classnames';
import { useFullSidebarData, useSidebarData } from 'dumi';
Expand All @@ -13,6 +12,22 @@ function isVersionNumber(value?: string) {
return value && /^\d+\.\d+\.\d+$/.test(value);
}

const getTagColor = (val?: string) => {
if (isVersionNumber(val)) {
return 'success';
}
if (val?.toUpperCase() === 'NEW') {
return 'success';
}
if (val?.toUpperCase() === 'UPDATED') {
return 'processing';
}
if (val?.toUpperCase() === 'DEPRECATED') {
return 'red';
}
return 'success';
};

const useStyle = createStyles(({ css, token }) => ({
link: css`
display: flex;
Expand Down Expand Up @@ -43,20 +58,17 @@ interface MenuItemLabelProps {
const MenuItemLabelWithTag: React.FC<MenuItemLabelProps> = (props) => {
const { styles } = useStyle();
const { before, after, link, title, subtitle, search, tag, className } = props;

if (!before && !after) {
return (
<Link to={`${link}${search}`} className={classnames(className, { [styles.link]: tag })}>
<Space>
<Flex justify="flex-start" align="center" gap="small">
<span>{title}</span>
{subtitle && <span className={styles.subtitle}>{subtitle}</span>}
</Space>
</Flex>
{tag && (
<Tag
bordered={false}
className={classnames(styles.tag)}
color={isVersionNumber(tag) || tag === 'New' ? 'success' : 'processing'}
>
{tag.replace('VERSION', version)}
<Tag bordered={false} className={classnames(styles.tag)} color={getTagColor(tag)}>
{tag.replace(/VERSION/i, version)}
</Tag>
)}
</Link>
Expand Down Expand Up @@ -84,12 +96,12 @@ const useMenu = (options: UseMenuOptions = {}): readonly [MenuProps['items'], st
const { before, after } = options;

const menuItems = useMemo<MenuProps['items']>(() => {
let sidebarItems = [...(sidebarData ?? [])];
const sidebarItems = [...(sidebarData ?? [])];

// 将设计文档未分类的放在最前面
// 将设计文档未分类的放在最后
if (pathname.startsWith('/docs/spec')) {
const notGrouped = sidebarItems.splice(0, 1);
sidebarItems = [...notGrouped, ...sidebarItems];
sidebarItems.push(...notGrouped);
}

// 把 /changelog 拼到开发文档中
Expand Down
Loading