refactor(web): 前端目录重构 — consoles/pages → layouts/features + shared
- consoles/admin/ → layouts/admin-layout/ - consoles/workbench/ → layouts/workbench-layout/ + features/chat/ - pages/ → features/ (dashboard, models, projects, not-found) - components/ → shared/components/ - hooks/ → shared/hooks/ - utils/ → shared/utils/ - 更新所有 import 路径 (src/web/ + tests/web/) - 更新开发文档 (README.md, frontend.md, architecture.md)
This commit is contained in:
82
src/web/features/chat/parts/CodeBlockWithCopy.tsx
Normal file
82
src/web/features/chat/parts/CodeBlockWithCopy.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { CopyOutlined } from "@ant-design/icons";
|
||||
import { CodeHighlighter } from "@ant-design/x";
|
||||
import { App, Button, Flex, Typography } from "antd";
|
||||
import React from "react";
|
||||
import { oneDark, oneLight } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
|
||||
import { useIsDark } from "../../../shared/hooks/use-is-dark";
|
||||
|
||||
type SyntaxTheme = Record<string, Record<string, null | number | string>>;
|
||||
|
||||
const customOneDark: SyntaxTheme = {
|
||||
...(oneDark as SyntaxTheme),
|
||||
'pre[class*="language-"]': {
|
||||
...(oneDark as SyntaxTheme)['pre[class*="language-"]'],
|
||||
margin: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const customOneLight: SyntaxTheme = {
|
||||
...(oneLight as SyntaxTheme),
|
||||
'pre[class*="language-"]': {
|
||||
...(oneLight as SyntaxTheme)['pre[class*="language-"]'],
|
||||
margin: 0,
|
||||
},
|
||||
};
|
||||
|
||||
interface CodeBlockWithCopyProps {
|
||||
block?: boolean;
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
lang?: string;
|
||||
streamStatus?: "done" | "loading";
|
||||
}
|
||||
|
||||
export function CodeBlockWithCopy({ block, children, className, lang }: CodeBlockWithCopyProps) {
|
||||
const { message } = App.useApp();
|
||||
const isDark = useIsDark();
|
||||
|
||||
if (!block) {
|
||||
return <code className={className}>{children}</code>;
|
||||
}
|
||||
|
||||
const codeText = extractText(children);
|
||||
const displayLang = lang ?? "plaintext";
|
||||
|
||||
const handleCopy = () => {
|
||||
void navigator.clipboard.writeText(codeText).then(() => {
|
||||
void message.success("已复制");
|
||||
});
|
||||
};
|
||||
|
||||
const header = (
|
||||
<Flex align="center" justify="space-between" style={{ padding: "0 4px" }}>
|
||||
<Typography.Text style={{ color: "var(--ant-color-text-quaternary)", fontSize: 12 }}>
|
||||
{displayLang}
|
||||
</Typography.Text>
|
||||
<Button
|
||||
icon={<CopyOutlined />}
|
||||
onClick={handleCopy}
|
||||
size="small"
|
||||
style={{ color: "var(--ant-color-text-quaternary)" }}
|
||||
type="text"
|
||||
/>
|
||||
</Flex>
|
||||
);
|
||||
|
||||
return (
|
||||
<CodeHighlighter
|
||||
header={header}
|
||||
highlightProps={{ style: (isDark ? customOneDark : customOneLight) as React.CSSProperties }}
|
||||
lang={displayLang}
|
||||
>
|
||||
{codeText}
|
||||
</CodeHighlighter>
|
||||
);
|
||||
}
|
||||
|
||||
function extractText(children: React.ReactNode): string {
|
||||
return React.Children.toArray(children)
|
||||
.map((child) => (typeof child === "string" ? child : ""))
|
||||
.join("");
|
||||
}
|
||||
Reference in New Issue
Block a user