From 8ad4594fc26804266e3ec09b50f71ffeddec96c1 Mon Sep 17 00:00:00 2001 From: shiro <185361175+moeyue23@users.noreply.github.com> Date: Sun, 12 Oct 2025 22:10:00 +0800 Subject: [PATCH 01/14] feat: introduce generic trigger system --- src/sender.tsx | 41 +++++++++++++++++++++++- src/suggestion.tsx | 79 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 src/suggestion.tsx diff --git a/src/sender.tsx b/src/sender.tsx index 54f45af..241b3d6 100644 --- a/src/sender.tsx +++ b/src/sender.tsx @@ -5,6 +5,8 @@ import { useCallback, useEffect, useRef, useState } from "react"; import { twMerge } from "tailwind-merge"; import PublishNew from "./icons/publish-new.svg"; import QuickStop from "./icons/quick-stop.svg"; +import type { OnTextInject, TriggerConfig } from "./suggestion"; +import Suggestion from "./suggestion"; import type { Backend } from "./utils"; export interface InputCountProps extends React.ComponentProps<"span"> { @@ -105,6 +107,10 @@ export interface SenderProps extends React.ComponentProps<"div"> { */ onSend?: (controller: AbortController) => void; toolbar?: React.ReactNode; + /** + * Trigger configurations for the suggestion list + */ + triggerConfigs?: TriggerConfig[]; } export function Sender({ @@ -115,11 +121,13 @@ export function Sender({ input, onSend, toolbar, + triggerConfigs, ...props }: SenderProps) { const textareaRef = useRef(null); const [message, setMessage] = useState(initialMessage); const [isSending, setIsSending] = useState(false); + const [caretPosition, setCaretPosition] = useState(null); useEffect(() => { if (textareaRef.current) { @@ -129,6 +137,15 @@ export function Sender({ onMessageChange?.(message); }, [message, onMessageChange]); + useEffect(() => { + if (textareaRef.current && caretPosition !== null) { + textareaRef.current.focus(); + textareaRef.current.selectionStart = caretPosition; + textareaRef.current.selectionEnd = caretPosition; + setCaretPosition(null); + } + }, [caretPosition]); + const [controller, setController] = useState(null); const handleSend = useCallback(() => { if (isSending) { @@ -175,12 +192,26 @@ export function Sender({ [], ); + const handleTextInject: OnTextInject = useCallback( + (newText, suggestionStartPosition) => { + setMessage((prevMessage) => { + const currentCaretPosition = + textareaRef.current?.selectionStart ?? prevMessage.length; + const textBefore = prevMessage.slice(0, suggestionStartPosition); + const textAfter = prevMessage.slice(currentCaretPosition); + const newMessage = textBefore + newText + textAfter; + return newMessage; + }); + setCaretPosition(suggestionStartPosition + newText.length); + }, + [], + ); return (
+
+ +