Skip to content
This repository has been archived by the owner on Sep 23, 2024. It is now read-only.

Commit

Permalink
fix: hovering on a text should stop the text from updating itself
Browse files Browse the repository at this point in the history
  • Loading branch information
clement2026 committed Mar 8, 2024
1 parent 6216b10 commit 301e86e
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 32 deletions.
72 changes: 50 additions & 22 deletions src/home/chat-window/compnent/my-text.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {memo, useEffect, useState} from 'react'
import React, {memo, useCallback, useEffect, useState} from 'react'
import {cx, formatAgo} from "../../../util/util.tsx"
import {Message} from "../../../data-structure/message.tsx"
import {MySpin} from "./widget/icon.tsx"
Expand Down Expand Up @@ -26,22 +26,27 @@ import mifoot from "markdown-it-footnote"
import './widget/highlightjs-plugins/copy-button-plugin.css'
import {LanguageLabelPlugin} from "./widget/highlightjs-plugins/language-label-plugin.tsx";
import {CopyButtonPlugin} from "./widget/highlightjs-plugins/copy-button-plugin.tsx";
import {debounce, throttle} from "lodash";
import {useSnapshot} from "valtio/react";
import {appState} from "../../../state/app-state.ts";
import {controlState} from "../../../state/control-state.ts";
import {debounce, throttle} from "lodash";

hljs.configure({
ignoreUnescapedHTML: true,
});
hljs.addPlugin(new LanguageLabelPlugin());
hljs.addPlugin(new CopyButtonPlugin());

const ha = throttle(debounce(() => hljs.highlightAll(), 1000, {
'trailing': true
}), 1000);
// debounce and throttle function only works as being singleton
const haDebounce = debounce(hljs.highlightAll, 0.5, {
leading: true,
trailing: true
})

const haNow = () => hljs.highlightAll()
const haThrottle = throttle(hljs.highlightAll, 0.5, {
leading: true,
trailing: true
})

interface TextProps {
messageSnap: Message
Expand All @@ -54,16 +59,19 @@ export const MyText: React.FC<TextProps> = ({messageSnap, theme}) => {
const {showMarkdown} = useSnapshot(appState.pref)
const {isWindowsBlurred} = useSnapshot(controlState)
const [text, setText] = useState(messageSnap.text)
const [latestText, setLatestText] = useState<string | undefined>()
const [hovering, setHovering] = useState(false)

// stop updating text when mouse hovering
const haNow = useCallback(() => showMarkdown && hljs.highlightAll(), [showMarkdown])

const haTh = useCallback(() => showMarkdown && haThrottle(), [showMarkdown])

const haDe = useCallback(() => showMarkdown && haDebounce(), [showMarkdown])

// run after text first loading
useEffect(() => {
if (!hovering) {
controlState.isTextPending = false
} else {
controlState.isTextPending = messageSnap.status === "typing"
}
}, [messageSnap.status, hovering]);
haDe()
}, [haDe]);

useEffect(() => {
if (isWindowsBlurred) {
Expand All @@ -72,24 +80,44 @@ export const MyText: React.FC<TextProps> = ({messageSnap, theme}) => {
}, [isWindowsBlurred]);

useEffect(() => {
if (!controlState.isTextPending) {
setText(messageSnap.text)
ha()
setLatestText(messageSnap.text)
}, [messageSnap.text]);

useEffect(() => {
if (!hovering && latestText && latestText != text) {
// if user is moving the cursor away and there is text in the buffer
setText(latestText)
if (messageSnap.status == "typing") {
haTh()
} else {
// highlight after latestText is added to the UI
setTimeout(haNow)
}
}
}, [messageSnap]);
}, [hovering, latestText, messageSnap.status, haNow, text, haTh]);

useEffect(() => {
// apply highlight plugin immediately after message is fully received
if (messageSnap.status === 'received') {
haNow()
hovering && messageSnap.status == "typing" && haNow()
}, [hovering, messageSnap.status, haNow])

useEffect(() => {
if (hovering) {
if (messageSnap.status === "typing") {
controlState.textPendingState = "pending"
return
} else if (latestText && latestText !== text) {
controlState.textPendingState = "done"
return;
}
}
}, [messageSnap.status]);
controlState.textPendingState = "none"
}, [hovering, latestText, messageSnap.status, text]);

return <div
className={cx("flex flex-col rounded-2xl px-3 pt-1.5 pb-0.5",
theme.text, theme.bg
)}
onMouseEnter={() => setHovering(true)}
onMouseOver={() => setHovering(true)}
onMouseLeave={() => setHovering(false)}
>

Expand Down
17 changes: 9 additions & 8 deletions src/home/chat-window/text-area.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {bestModel, matchKeyCombo} from "../../state/shortcuts.ts";
import IosSpinner from "../../assets/svg/ios-spinner.svg?react"
import {modelInUse} from "../../data-structure/client-option.tsx";
import {PopOverDot} from "./compnent/dot.tsx";
import {FaCheckCircle} from "react-icons/fa";

type Props = {
chatProxy: Chat
Expand Down Expand Up @@ -171,7 +172,6 @@ const TextArea: React.FC<Props> = ({chatProxy}) => {
}
}, [inputAreaIsLarge])


return (<div className="flex flex-col items-center w-full mt-auto bottom-0 max-w-4xl">
<div className="relative flex items-center justify-center w-full z-10">
<div className="absolute left-2 flex items-center gap-1.5">
Expand All @@ -191,7 +191,8 @@ const TextArea: React.FC<Props> = ({chatProxy}) => {
<PopOverDot text="MD" popOverText="Display messages in markdown style" activated={showMarkdown}
action={() => appState.pref.showMarkdown = !appState.pref.showMarkdown}/>

{currentModel && <PopOverDot text={currentModel} fixedRound={false} popOverText="Lanuage model in use"/>}
{currentModel &&
<PopOverDot text={currentModel} fixedRound={false} popOverText="Lanuage model in use"/>}
<TextPendingIcon/>
</div>
<button
Expand Down Expand Up @@ -243,15 +244,15 @@ const TextArea: React.FC<Props> = ({chatProxy}) => {

const TextPendingIcon = () => {

const {isTextPending} = useSnapshot(controlState)
const {textPendingState} = useSnapshot(controlState)

return (
<div className={cx("flex rounded-full w-5 h-5 items-center",
"select-none transform duration-200 ",
isTextPending ? "opacity-100" : "opacity-0")}>
<IosSpinner className={"stroke-white"}/>
<div className={cx("flex rounded-full w-4 h-4 items-center",
"select-none transition duration-200",
textPendingState == "none" ? "opacity-0" : "opacity-100")}>
{textPendingState == "pending" && <IosSpinner className={"stroke-white"}/>}
{textPendingState == "done" && <FaCheckCircle className={"fill-white"}/>}
</div>

)
}

Expand Down
4 changes: 2 additions & 2 deletions src/state/control-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ type ControlState = {
isMouseLeftDown: boolean
isMouseDragging: boolean
isWindowsBlurred: boolean,
isTextPending: boolean,
textPendingState: "none" | "pending" | "done",
player: Player
recordingMimeType?: RecordingMimeType
recorder: EnhancedRecorder<RecordingCtx>
Expand All @@ -52,7 +52,7 @@ export const controlState = proxy<ControlState>({
isMouseLeftDown: false,
isMouseDragging: false,
isWindowsBlurred: false,
isTextPending: false,
textPendingState: "none",
player: {
autoPlay: true,
isPlaying: false,
Expand Down

0 comments on commit 301e86e

Please sign in to comment.