mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
fix pre tag bg color in markdown (#678)
This commit is contained in:
@@ -7,7 +7,6 @@ export { InlineSettingsTextEdit } from "./inlinesettingstextedit";
|
||||
export { InputDecoration } from "./inputdecoration";
|
||||
export { LinkButton } from "./linkbutton";
|
||||
export { Markdown } from "./markdown";
|
||||
export { Markdown2 } from "./markdown2";
|
||||
export { Modal } from "./modal";
|
||||
export { PasswordField } from "./passwordfield";
|
||||
export { ResizableSidebar } from "./resizablesidebar";
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
padding: 2px 4px 2px 6px;
|
||||
}
|
||||
|
||||
pre.codeblock {
|
||||
pre {
|
||||
background-color: var(--markdown-bg-color);
|
||||
margin: 4px 10px;
|
||||
padding: 0.4em 0.7em;
|
||||
|
||||
@@ -5,14 +5,14 @@ import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { CopyButton } from "@/elements";
|
||||
import { clsx } from "clsx";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import * as mobx from "mobx";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
|
||||
import "./markdown.less";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
|
||||
function LinkRenderer(props: any): any {
|
||||
function Link(props: any): JSX.Element {
|
||||
let newUrl = "https://extern?" + encodeURIComponent(props.href);
|
||||
return (
|
||||
<a href={newUrl} target="_blank" rel={"noopener"}>
|
||||
@@ -21,99 +21,86 @@ function LinkRenderer(props: any): any {
|
||||
);
|
||||
}
|
||||
|
||||
function HeaderRenderer(props: any, hnum: number): any {
|
||||
function Header(props: any, hnum: number): JSX.Element {
|
||||
return <div className={clsx("title", "is-" + hnum)}>{props.children}</div>;
|
||||
}
|
||||
|
||||
function CodeRenderer(props: any): any {
|
||||
function Code(props: any): JSX.Element {
|
||||
return <code>{props.children}</code>;
|
||||
}
|
||||
|
||||
@mobxReact.observer
|
||||
class CodeBlockMarkdown extends React.Component<
|
||||
{ children: React.ReactNode; codeSelectSelectedIndex?: number; uuid: string },
|
||||
{}
|
||||
> {
|
||||
blockIndex: number;
|
||||
blockRef: React.RefObject<HTMLPreElement>;
|
||||
const CodeBlock = mobxReact.observer(
|
||||
(props: { children: React.ReactNode; onClickExecute?: (cmd: string) => void }): JSX.Element => {
|
||||
const copied: OV<boolean> = mobx.observable.box(false, { name: "copied" });
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.blockRef = React.createRef();
|
||||
this.blockIndex = GlobalModel.inputModel.addCodeBlockToCodeSelect(this.blockRef, this.props.uuid);
|
||||
}
|
||||
const getTextContent = (children: any) => {
|
||||
if (typeof children === "string") {
|
||||
return children;
|
||||
} else if (Array.isArray(children)) {
|
||||
return children.map(getTextContent).join("");
|
||||
} else if (children.props && children.props.children) {
|
||||
return getTextContent(children.props.children);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
render() {
|
||||
let clickHandler: (e: React.MouseEvent<HTMLElement>, blockIndex: number) => void;
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
clickHandler = (e: React.MouseEvent<HTMLElement>, blockIndex: number) => {
|
||||
const sel = window.getSelection();
|
||||
if (sel?.toString().length == 0) {
|
||||
inputModel.setCodeSelectSelectedCodeBlock(blockIndex);
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
copied.set(true);
|
||||
setTimeout(() => copied.set(false), 2000); // Reset copied state after 2 seconds
|
||||
};
|
||||
|
||||
const handleExecute = (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
if (props.onClickExecute) {
|
||||
props.onClickExecute(textToCopy);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let selected = this.blockIndex == this.props.codeSelectSelectedIndex;
|
||||
|
||||
return (
|
||||
<pre
|
||||
ref={this.blockRef}
|
||||
className={clsx({ selected: selected })}
|
||||
onClick={(event) => clickHandler(event, this.blockIndex)}
|
||||
>
|
||||
{this.props.children}
|
||||
<pre className="codeblock">
|
||||
{props.children}
|
||||
<div className="codeblock-actions">
|
||||
<CopyButton className="copy-button" onClick={handleCopy} title="Copy" />
|
||||
<If condition={props.onClickExecute}>
|
||||
<i className="fa-regular fa-square-terminal" onClick={handleExecute}></i>
|
||||
</If>
|
||||
</div>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@mobxReact.observer
|
||||
class Markdown extends React.Component<
|
||||
{ text: string; style?: any; extraClassName?: string; codeSelect?: boolean },
|
||||
{
|
||||
text: string;
|
||||
style?: any;
|
||||
className?: string;
|
||||
onClickExecute?: (cmd: string) => void;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
curUuid: string;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.curUuid = uuidv4();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
CodeBlockRenderer(props: any, codeSelect: boolean, codeSelectIndex: number, curUuid: string): any {
|
||||
if (codeSelect) {
|
||||
return (
|
||||
<CodeBlockMarkdown codeSelectSelectedIndex={codeSelectIndex} uuid={curUuid}>
|
||||
{props.children}
|
||||
</CodeBlockMarkdown>
|
||||
);
|
||||
} else {
|
||||
const clickHandler = (e: React.MouseEvent<HTMLElement>) => {
|
||||
let blockText = (e.target as HTMLElement).innerText;
|
||||
if (blockText) {
|
||||
blockText = blockText.replace(/\n$/, ""); // remove trailing newline
|
||||
navigator.clipboard.writeText(blockText);
|
||||
}
|
||||
};
|
||||
return <pre onClick={(event) => clickHandler(event)}>{props.children}</pre>;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let text = this.props.text;
|
||||
let codeSelect = this.props.codeSelect;
|
||||
let curCodeSelectIndex = GlobalModel.inputModel.getCodeSelectSelectedIndex();
|
||||
let { text, className, onClickExecute } = this.props;
|
||||
let markdownComponents = {
|
||||
a: LinkRenderer,
|
||||
h1: (props) => HeaderRenderer(props, 1),
|
||||
h2: (props) => HeaderRenderer(props, 2),
|
||||
h3: (props) => HeaderRenderer(props, 3),
|
||||
h4: (props) => HeaderRenderer(props, 4),
|
||||
h5: (props) => HeaderRenderer(props, 5),
|
||||
h6: (props) => HeaderRenderer(props, 6),
|
||||
code: (props) => CodeRenderer(props),
|
||||
pre: (props) => this.CodeBlockRenderer(props, codeSelect, curCodeSelectIndex, this.curUuid),
|
||||
a: Link,
|
||||
h1: (props) => <Header {...props} hnum={1} />,
|
||||
h2: (props) => <Header {...props} hnum={2} />,
|
||||
h3: (props) => <Header {...props} hnum={3} />,
|
||||
h4: (props) => <Header {...props} hnum={4} />,
|
||||
h5: (props) => <Header {...props} hnum={5} />,
|
||||
h6: (props) => <Header {...props} hnum={6} />,
|
||||
code: Code,
|
||||
pre: (props) => <CodeBlock {...props} onClickExecute={onClickExecute} />,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx("markdown content", this.props.extraClassName)} style={this.props.style}>
|
||||
<div className={clsx("markdown content", className)} style={this.props.style}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright 2023, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import * as React from "react";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import { CopyButton } from "@/elements";
|
||||
import { clsx } from "clsx";
|
||||
import * as mobx from "mobx";
|
||||
import { If } from "tsx-control-statements/components";
|
||||
|
||||
import "./markdown.less";
|
||||
|
||||
function Link(props: any): JSX.Element {
|
||||
let newUrl = "https://extern?" + encodeURIComponent(props.href);
|
||||
return (
|
||||
<a href={newUrl} target="_blank" rel={"noopener"}>
|
||||
{props.children}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
function Header(props: any, hnum: number): JSX.Element {
|
||||
return <div className={clsx("title", "is-" + hnum)}>{props.children}</div>;
|
||||
}
|
||||
|
||||
function Code(props: any): JSX.Element {
|
||||
return <code>{props.children}</code>;
|
||||
}
|
||||
|
||||
const CodeBlock = mobxReact.observer(
|
||||
(props: { children: React.ReactNode; onClickExecute?: (cmd: string) => void }): JSX.Element => {
|
||||
const copied: OV<boolean> = mobx.observable.box(false, { name: "copied" });
|
||||
|
||||
const getTextContent = (children: any) => {
|
||||
if (typeof children === "string") {
|
||||
return children;
|
||||
} else if (Array.isArray(children)) {
|
||||
return children.map(getTextContent).join("");
|
||||
} else if (children.props && children.props.children) {
|
||||
return getTextContent(children.props.children);
|
||||
}
|
||||
return "";
|
||||
};
|
||||
|
||||
const handleCopy = async (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
await navigator.clipboard.writeText(textToCopy);
|
||||
copied.set(true);
|
||||
setTimeout(() => copied.set(false), 2000); // Reset copied state after 2 seconds
|
||||
};
|
||||
|
||||
const handleExecute = (e: React.MouseEvent) => {
|
||||
let textToCopy = getTextContent(props.children);
|
||||
textToCopy = textToCopy.replace(/\n$/, ""); // remove trailing newline
|
||||
if (props.onClickExecute) {
|
||||
props.onClickExecute(textToCopy);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<pre className="codeblock">
|
||||
{props.children}
|
||||
<div className="codeblock-actions">
|
||||
<CopyButton className="copy-button" onClick={handleCopy} title="Copy" />
|
||||
<If condition={props.onClickExecute}>
|
||||
<i className="fa-regular fa-square-terminal" onClick={handleExecute}></i>
|
||||
</If>
|
||||
</div>
|
||||
</pre>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@mobxReact.observer
|
||||
class Markdown2 extends React.Component<
|
||||
{
|
||||
text: string;
|
||||
style?: any;
|
||||
className?: string;
|
||||
onClickExecute?: (cmd: string) => void;
|
||||
},
|
||||
{}
|
||||
> {
|
||||
render() {
|
||||
let { text, className, onClickExecute } = this.props;
|
||||
let markdownComponents = {
|
||||
a: Link,
|
||||
h1: (props) => <Header {...props} hnum={1} />,
|
||||
h2: (props) => <Header {...props} hnum={2} />,
|
||||
h3: (props) => <Header {...props} hnum={3} />,
|
||||
h4: (props) => <Header {...props} hnum={4} />,
|
||||
h5: (props) => <Header {...props} hnum={5} />,
|
||||
h6: (props) => <Header {...props} hnum={6} />,
|
||||
code: Code,
|
||||
pre: (props) => <CodeBlock {...props} onClickExecute={onClickExecute} />,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={clsx("markdown content", className)} style={this.props.style}>
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export { Markdown2 };
|
||||
@@ -7,7 +7,7 @@ import * as mobx from "mobx";
|
||||
import { GlobalModel } from "@/models";
|
||||
import { boundMethod } from "autobind-decorator";
|
||||
import { For, If } from "tsx-control-statements/components";
|
||||
import { Markdown2, TypingIndicator } from "@/elements";
|
||||
import { Markdown, TypingIndicator } from "@/elements";
|
||||
import type { OverlayScrollbars } from "overlayscrollbars";
|
||||
import { OverlayScrollbarsComponent, OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
|
||||
import tinycolor from "tinycolor2";
|
||||
@@ -72,7 +72,7 @@ class ChatItem extends React.Component<
|
||||
<div className="chat-msg-header">
|
||||
<i className="fa-sharp fa-solid fa-user"></i>
|
||||
</div>
|
||||
<Markdown2 className="msg-text" text={chatItem.userquery} />
|
||||
<Markdown className="msg-text" text={chatItem.userquery} />
|
||||
</>
|
||||
);
|
||||
if (isassistantresponse) {
|
||||
@@ -94,7 +94,7 @@ class ChatItem extends React.Component<
|
||||
<div className="chat-msg-header">
|
||||
<i className="fa-sharp fa-solid fa-sparkles"></i>
|
||||
</div>
|
||||
<Markdown2 text={assistantresponse.message} onClickExecute={onSetCmdInputValue} />
|
||||
<Markdown text={assistantresponse.message} onClickExecute={onSetCmdInputValue} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import * as mobx from "mobx";
|
||||
import * as mobxReact from "mobx-react";
|
||||
import { sprintf } from "sprintf-js";
|
||||
import { Markdown } from "@/elements";
|
||||
import { GlobalModel } from "@/models/global";
|
||||
|
||||
import "./markdown.less";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user