-
-
Notifications
You must be signed in to change notification settings - Fork 229
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
015dea7
commit 588fa67
Showing
8 changed files
with
367 additions
and
263 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,66 +1,147 @@ | ||
import classNames from "classnames"; | ||
import { globalZ } from "../lib/globalZ"; | ||
import { useDoc } from "../lib/useDoc"; | ||
import { useEffect, useMemo } from "react"; | ||
import { getDefaultText } from "../lib/getDefaultText"; | ||
import { useEditorStore } from "../lib/useEditorStore"; | ||
import { ConvertToFlowchart } from "./ConvertToFlowchart"; | ||
import { EditWithAI } from "./EditWithAI"; | ||
import { usePromptStore } from "../lib/usePromptStore"; | ||
import { Button2, IconButton2, Textarea } from "../ui/Shared"; | ||
import { CaretDown, CaretUp, MagicWand } from "phosphor-react"; | ||
import cx from "classnames"; | ||
import { t } from "@lingui/macro"; | ||
import { createExamples } from "../pages/createExamples"; | ||
import { | ||
Mode, | ||
acceptDiff, | ||
rejectDiff, | ||
setCurrentText, | ||
setIsOpen, | ||
setMode, | ||
usePromptStore, | ||
useRunAiWithStore, | ||
} from "../lib/usePromptStore"; | ||
|
||
function getModeDescription(mode: Mode): string { | ||
const prompts = createExamples(); | ||
switch (mode) { | ||
case "prompt": | ||
return `E.G., "${prompts[0]}"`; | ||
case "convert": | ||
return t`Paste your document or outline here to convert it into an organized flowchart.`; | ||
case "edit": | ||
return t`Use this mode to modify and enhance your current chart.`; | ||
} | ||
} | ||
|
||
function getModeTitle(mode: Mode): string { | ||
switch (mode) { | ||
case "prompt": | ||
return t`Prompt`; | ||
case "convert": | ||
return t`Convert`; | ||
case "edit": | ||
return t`Edit`; | ||
} | ||
} | ||
|
||
/** | ||
* Watch the current state of the graph and the users actions and determine | ||
* which, if any, AI tools to display to the user. | ||
*/ | ||
export function AiToolbar() { | ||
const text = useDoc((state) => state.text); | ||
const defaultText = useMemo(() => { | ||
return getDefaultText(); | ||
}, []); | ||
const isDefaultText = text === defaultText; | ||
const selection = useEditorStore((s) => s.selection); | ||
const fullTextSelected = selection.trim() === text.trim(); | ||
const userPasted = useEditorStore((s) => s.userPasted); | ||
const enoughCharacters = text.length > 150; | ||
const lastResult = usePromptStore((s) => s.lastResult); | ||
const isOpen = usePromptStore((state) => state.isOpen); | ||
const currentMode = usePromptStore((state) => state.mode); | ||
const isRunning = usePromptStore((state) => state.isRunning); | ||
const runAiWithStore = useRunAiWithStore(); | ||
const diff = usePromptStore((state) => state.diff); | ||
|
||
const toggleOpen = () => setIsOpen(!isOpen); | ||
|
||
// Set the user pasted back to false after 15 seconds, and on unmount | ||
useEffect(() => { | ||
if (userPasted) { | ||
const timeout = setTimeout(() => { | ||
useEditorStore.setState({ userPasted: "" }); | ||
}, 15000); | ||
return () => clearTimeout(timeout); | ||
} | ||
}, [userPasted]); | ||
const handleModeChange = (mode: Mode) => { | ||
setMode(mode); | ||
if (!isOpen) setIsOpen(true); | ||
}; | ||
|
||
const convertIsRunning = usePromptStore((s) => s.isRunning); | ||
const currentText = usePromptStore((state) => state.currentText); | ||
|
||
// Qualities for displaying Convert to Flowchart button: | ||
// OR | ||
// Convert is currently running | ||
// AND | ||
// Is not the default text | ||
// There is more than 150 characters | ||
// Text is not equal to the last result | ||
// OR | ||
// Full text is selected and is more than 150 characters | ||
// Less than 15 seconds have passed since user pasted more than 150 characters | ||
const showConvertToFlowchart = | ||
convertIsRunning || | ||
(!isDefaultText && | ||
enoughCharacters && | ||
lastResult !== text && | ||
(fullTextSelected || userPasted)); | ||
const showAcceptDiffButton = diff && !isRunning; | ||
|
||
return ( | ||
<div | ||
className={classNames( | ||
"drop-shadow-lg absolute bottom-2 right-2", | ||
globalZ.editWithAiButton | ||
<div className="bg-purple-300/60 dark:bg-purple-800/20"> | ||
<div className="flex items-center justify-between p-2"> | ||
<div className="flex items-center space-x-2"> | ||
<MagicWand | ||
size={24} | ||
className="text-purple-600 dark:text-white mx-1" | ||
/> | ||
{!showAcceptDiffButton ? ( | ||
(["prompt", "convert", "edit"] as Mode[]).map((mode) => ( | ||
<Button2 | ||
key={mode} | ||
color={mode === currentMode ? "purple" : "default"} | ||
size="xs" | ||
onClick={() => handleModeChange(mode)} | ||
className={cx({ | ||
"hover:bg-white dark:hover:bg-neutral-700": | ||
mode !== currentMode, | ||
"dark:bg-purple-700 dark:text-purple-100": | ||
mode === currentMode, | ||
})} | ||
> | ||
{getModeTitle(mode)} | ||
</Button2> | ||
)) | ||
) : ( | ||
<span className="text-sm text-purple-600 dark:text-white"> | ||
Keep changes? | ||
</span> | ||
)} | ||
</div> | ||
{!showAcceptDiffButton ? ( | ||
<IconButton2 | ||
onClick={toggleOpen} | ||
color="purple" | ||
size="xs" | ||
className="flex items-center justify-center dark:bg-purple-700/50 dark:text-purple-100" | ||
isLoading={isRunning} | ||
> | ||
{!isOpen ? <CaretDown size={16} /> : <CaretUp size={16} />} | ||
</IconButton2> | ||
) : ( | ||
<div className="flex space-x-2"> | ||
<Button2 color="green" size="xs" onClick={acceptDiff}> | ||
Accept | ||
</Button2> | ||
<Button2 color="red" size="xs" onClick={rejectDiff}> | ||
Reject | ||
</Button2> | ||
</div> | ||
)} | ||
</div> | ||
{isOpen && ( | ||
<div className="grid p-4 pt-0"> | ||
<p className="text-xs text-purple-600 dark:text-white mb-2 text-wrap-balance leading-normal"> | ||
{getModeDescription(currentMode)} | ||
</p> | ||
<Textarea | ||
value={currentText} | ||
box={{ | ||
className: | ||
"bg-white dark:bg-purple-800/50 !rounded-md w-full mb-2 border-2 border-purple-400 dark:border-purple-700 rounded-md focus:ring-purple-500 focus:border-purple-500", | ||
}} | ||
onChange={(e) => setCurrentText(e.target.value)} | ||
className="resize-none dark:text-white dark:bg-transparent" | ||
disabled={isRunning} | ||
onKeyDown={(e) => { | ||
if (e.key === "Enter" && !e.shiftKey) { | ||
e.preventDefault(); | ||
runAiWithStore(); | ||
} | ||
}} | ||
/> | ||
<div className="flex justify-end"> | ||
<Button2 | ||
color="purple" | ||
size="xs" | ||
className="dark:bg-purple-700/50 dark:text-purple-100" | ||
disabled={isRunning} | ||
onClick={runAiWithStore} | ||
data-session-activity={`Run AI: ${currentMode}`} | ||
> | ||
{!isRunning ? t`Submit` : "..."} | ||
</Button2> | ||
</div> | ||
</div> | ||
)} | ||
> | ||
{showConvertToFlowchart ? <ConvertToFlowchart /> : <EditWithAI />} | ||
</div> | ||
); | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.