Make textAtom and showTocAtom in Markdown element optional (#331)

Fix backwards-compatibility for the Markdown element by adding back the option to pass text directly to the element and make atom parameters optional.
This commit is contained in:
Evan Simkowitz 2024-09-05 15:35:57 -07:00 committed by GitHub
parent fc5e53e476
commit 015ebf5dd5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 28 additions and 28 deletions

View File

@ -8,7 +8,8 @@ import ReactMarkdown from "react-markdown";
import rehypeRaw from "rehype-raw"; import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm"; import remarkGfm from "remark-gfm";
import { Atom, useAtomValue } from "jotai"; import { useAtomValueSafe } from "@/util/util";
import { Atom } from "jotai";
import { OverlayScrollbarsComponent } from "overlayscrollbars-react"; import { OverlayScrollbarsComponent } from "overlayscrollbars-react";
import RemarkFlexibleToc, { TocItem } from "remark-flexible-toc"; import RemarkFlexibleToc, { TocItem } from "remark-flexible-toc";
import { useHeight } from "../hook/useHeight"; import { useHeight } from "../hook/useHeight";
@ -75,17 +76,18 @@ const CodeBlock = ({ children, onClickExecute }: CodeBlockProps) => {
}; };
type MarkdownProps = { type MarkdownProps = {
textAtom: Atom<string> | Atom<Promise<string>>; text?: string;
showTocAtom: Atom<boolean>; textAtom?: Atom<string> | Atom<Promise<string>>;
showTocAtom?: Atom<boolean>;
style?: React.CSSProperties; style?: React.CSSProperties;
className?: string; className?: string;
onClickExecute?: (cmd: string) => void; onClickExecute?: (cmd: string) => void;
}; };
const Markdown = ({ textAtom, showTocAtom, style, className, onClickExecute }: MarkdownProps) => { const Markdown = ({ text, textAtom, showTocAtom, style, className, onClickExecute }: MarkdownProps) => {
const text = useAtomValue(textAtom); const textAtomValue = useAtomValueSafe(textAtom);
const tocRef = useRef<TocItem[]>([]); const tocRef = useRef<TocItem[]>([]);
const showToc = useAtomValue(showTocAtom); const showToc = useAtomValueSafe(showTocAtom) ?? false;
const contentsRef = useRef<HTMLDivElement>(null); const contentsRef = useRef<HTMLDivElement>(null);
const contentsHeight = useHeight(contentsRef, 200); const contentsHeight = useHeight(contentsRef, 200);
@ -133,6 +135,8 @@ const Markdown = ({ textAtom, showTocAtom, style, className, onClickExecute }: M
} }
}, [showToc, tocRef]); }, [showToc, tocRef]);
text = textAtomValue ?? text;
return ( return (
<div className={clsx("markdown", className)} style={style} ref={contentsRef}> <div className={clsx("markdown", className)} style={style} ref={contentsRef}>
<OverlayScrollbarsComponent <OverlayScrollbarsComponent

View File

@ -5,17 +5,18 @@ import { Modal } from "@/app/modals/modal";
import { Markdown } from "@/element/markdown"; import { Markdown } from "@/element/markdown";
import { modalsModel } from "@/store/modalmodel"; import { modalsModel } from "@/store/modalmodel";
import * as keyutil from "@/util/keyutil"; import * as keyutil from "@/util/keyutil";
import * as React from "react";
import { UserInputService } from "../store/services"; import { UserInputService } from "../store/services";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import "./userinputmodal.less"; import "./userinputmodal.less";
const UserInputModal = (userInputRequest: UserInputRequest) => { const UserInputModal = (userInputRequest: UserInputRequest) => {
const [responseText, setResponseText] = React.useState(""); const [responseText, setResponseText] = useState("");
const [countdown, setCountdown] = React.useState(Math.floor(userInputRequest.timeoutms / 1000)); const [countdown, setCountdown] = useState(Math.floor(userInputRequest.timeoutms / 1000));
const checkboxStatus = React.useRef(false); const checkboxStatus = useRef(false);
const queryTextAtom = useState;
const handleSendCancel = React.useCallback(() => { const handleSendCancel = useCallback(() => {
UserInputService.SendUserInputResponse({ UserInputService.SendUserInputResponse({
type: "userinputresp", type: "userinputresp",
requestid: userInputRequest.requestid, requestid: userInputRequest.requestid,
@ -24,7 +25,7 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
modalsModel.popModal(); modalsModel.popModal();
}, [responseText, userInputRequest]); }, [responseText, userInputRequest]);
const handleSendText = React.useCallback(() => { const handleSendText = useCallback(() => {
UserInputService.SendUserInputResponse({ UserInputService.SendUserInputResponse({
type: "userinputresp", type: "userinputresp",
requestid: userInputRequest.requestid, requestid: userInputRequest.requestid,
@ -34,7 +35,7 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
modalsModel.popModal(); modalsModel.popModal();
}, [responseText, userInputRequest]); }, [responseText, userInputRequest]);
const handleSendConfirm = React.useCallback(() => { const handleSendConfirm = useCallback(() => {
UserInputService.SendUserInputResponse({ UserInputService.SendUserInputResponse({
type: "userinputresp", type: "userinputresp",
requestid: userInputRequest.requestid, requestid: userInputRequest.requestid,
@ -44,7 +45,7 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
modalsModel.popModal(); modalsModel.popModal();
}, [userInputRequest]); }, [userInputRequest]);
const handleSubmit = React.useCallback(() => { const handleSubmit = useCallback(() => {
switch (userInputRequest.responsetype) { switch (userInputRequest.responsetype) {
case "text": case "text":
handleSendText(); handleSendText();
@ -55,7 +56,7 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
} }
}, [handleSendConfirm, handleSendText, userInputRequest.responsetype]); }, [handleSendConfirm, handleSendText, userInputRequest.responsetype]);
const handleKeyDown = React.useCallback( const handleKeyDown = useCallback(
(waveEvent: WaveKeyboardEvent): boolean => { (waveEvent: WaveKeyboardEvent): boolean => {
if (keyutil.checkKeyPressed(waveEvent, "Escape")) { if (keyutil.checkKeyPressed(waveEvent, "Escape")) {
handleSendCancel(); handleSendCancel();
@ -69,14 +70,14 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
[handleSendCancel, handleSubmit] [handleSendCancel, handleSubmit]
); );
const queryText = React.useMemo(() => { const queryText = useMemo(() => {
if (userInputRequest.markdown) { if (userInputRequest.markdown) {
return <Markdown text={userInputRequest.querytext} className="userinput-markdown" />; return <Markdown text={userInputRequest.querytext} className="userinput-markdown" />;
} }
return <span className="userinput-text">{userInputRequest.querytext}</span>; return <span className="userinput-text">{userInputRequest.querytext}</span>;
}, [userInputRequest.markdown, userInputRequest.querytext]); }, [userInputRequest.markdown, userInputRequest.querytext]);
const inputBox = React.useMemo(() => { const inputBox = useMemo(() => {
if (userInputRequest.responsetype === "confirm") { if (userInputRequest.responsetype === "confirm") {
return <></>; return <></>;
} }
@ -93,7 +94,7 @@ const UserInputModal = (userInputRequest: UserInputRequest) => {
); );
}, [userInputRequest.responsetype, userInputRequest.publictext, responseText, handleKeyDown, setResponseText]); }, [userInputRequest.responsetype, userInputRequest.publictext, responseText, handleKeyDown, setResponseText]);
React.useEffect(() => { useEffect(() => {
let timeout: ReturnType<typeof setTimeout>; let timeout: ReturnType<typeof setTimeout>;
if (countdown == 0) { if (countdown == 0) {
timeout = setTimeout(() => { timeout = setTimeout(() => {

View File

@ -180,8 +180,6 @@ Other useful metadata values to override block titles, icons, colors, themes, et
`; `;
const helpTextAtom = atom(helpText);
class HelpViewModel implements ViewModel { class HelpViewModel implements ViewModel {
viewType: string; viewType: string;
showTocAtom: PrimitiveAtom<boolean>; showTocAtom: PrimitiveAtom<boolean>;
@ -210,7 +208,7 @@ function makeHelpViewModel() {
} }
function HelpView({ model }: { model: HelpViewModel }) { function HelpView({ model }: { model: HelpViewModel }) {
return <Markdown textAtom={helpTextAtom} showTocAtom={model.showTocAtom} className="help-view" />; return <Markdown text={helpText} showTocAtom={model.showTocAtom} className="help-view" />;
} }
export { HelpView, HelpViewModel, makeHelpViewModel }; export { HelpView, HelpViewModel, makeHelpViewModel };

View File

@ -11,7 +11,7 @@ import { isBlank } from "@/util/util";
import { atom, Atom, PrimitiveAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai"; import { atom, Atom, PrimitiveAtom, useAtomValue, useSetAtom, WritableAtom } from "jotai";
import type { OverlayScrollbars } from "overlayscrollbars"; import type { OverlayScrollbars } from "overlayscrollbars";
import { OverlayScrollbarsComponent, OverlayScrollbarsComponentRef } from "overlayscrollbars-react"; import { OverlayScrollbarsComponent, OverlayScrollbarsComponentRef } from "overlayscrollbars-react";
import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { forwardRef, memo, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
import tinycolor from "tinycolor2"; import tinycolor from "tinycolor2";
import "./waveai.less"; import "./waveai.less";
@ -217,11 +217,8 @@ function makeWaveAiViewModel(blockId): WaveAiModel {
return waveAiModel; return waveAiModel;
} }
const showTocAtomDummy = atom(false);
const ChatItem = ({ chatItem, itemCount }: ChatItemProps) => { const ChatItem = ({ chatItem, itemCount }: ChatItemProps) => {
const { isAssistant, text, isError } = chatItem; const { isAssistant, text, isError } = chatItem;
const textAtom = useMemo(() => atom(text), [text]);
const senderClassName = isAssistant ? "chat-msg-assistant" : "chat-msg-user"; const senderClassName = isAssistant ? "chat-msg-assistant" : "chat-msg-user";
const msgClassName = `chat-msg ${senderClassName}`; const msgClassName = `chat-msg ${senderClassName}`;
const cssVar = "--panel-bg-color"; const cssVar = "--panel-bg-color";
@ -242,7 +239,7 @@ const ChatItem = ({ chatItem, itemCount }: ChatItemProps) => {
<div className="chat-msg-header"> <div className="chat-msg-header">
<i className="fa-sharp fa-solid fa-sparkles"></i> <i className="fa-sharp fa-solid fa-sparkles"></i>
</div> </div>
<Markdown textAtom={textAtom} showTocAtom={showTocAtomDummy} /> <Markdown text={text} />
</> </>
) : ( ) : (
<> <>
@ -258,7 +255,7 @@ const ChatItem = ({ chatItem, itemCount }: ChatItemProps) => {
<div className="chat-msg-header"> <div className="chat-msg-header">
<i className="fa-sharp fa-solid fa-user"></i> <i className="fa-sharp fa-solid fa-user"></i>
</div> </div>
<Markdown className="msg-text" textAtom={textAtom} showTocAtom={showTocAtomDummy} /> <Markdown className="msg-text" text={text} />
</> </>
); );
}; };

View File

@ -163,7 +163,7 @@ function jotaiLoadableValue<T>(value: Loadable<T>, def: T): T {
const NullAtom = atom(null); const NullAtom = atom(null);
function useAtomValueSafe<T>(atom: Atom<T>): T { function useAtomValueSafe<T>(atom: Atom<T> | Atom<Promise<T>>): T {
if (atom == null) { if (atom == null) {
return useAtomValue(NullAtom) as T; return useAtomValue(NullAtom) as T;
} }