fix(prompt-input): 提供可选上下文入口并避免历史路由切换期异常
This commit is contained in:
1 parent
519e4c0c01
commit
9455160101
2 files changed
+33
-23
No files matched your search
@@ -56,12 +56,11 @@ import {
|
||||
type ChangeEventHandler,
|
||||
Children,
|
||||
type ClipboardEventHandler,
|
||||
type ComponentProps,
|
||||
createContext,
|
||||
type FormEvent,
|
||||
type FormEventHandler,
|
||||
Fragment,
|
||||
type HTMLAttributes,
|
||||
type ComponentProps,
|
||||
createContext,
|
||||
type FormEvent,
|
||||
type FormEventHandler,
|
||||
type HTMLAttributes,
|
||||
type KeyboardEventHandler,
|
||||
type PropsWithChildren,
|
||||
type ReactNode,
|
||||
@@ -124,9 +123,11 @@ export const usePromptInputController = () => {
|
||||
return ctx;
|
||||
};
|
||||
|
||||
// Optional variants (do NOT throw). Useful for dual-mode components.
|
||||
const useOptionalPromptInputController = () =>
|
||||
useContext(PromptInputController);
|
||||
// Optional variants (do NOT throw). Useful for dual-mode components and for
|
||||
// Fast Refresh transitions where a page can briefly retain the previous
|
||||
// provider context instance.
|
||||
export const useOptionalPromptInputController = () =>
|
||||
useContext(PromptInputController);
|
||||
|
||||
export const useProviderAttachments = () => {
|
||||
const ctx = useContext(ProviderAttachmentsContext);
|
||||
@@ -403,14 +404,14 @@ export function PromptInputAttachments({
|
||||
return (
|
||||
<div
|
||||
className={cn("flex w-full flex-wrap items-center gap-2 p-3", className)}
|
||||
{...props}
|
||||
>
|
||||
{attachments.files.map((file) => (
|
||||
<Fragment key={file.id}>
|
||||
<div className="max-w-60">{children(file)}</div>
|
||||
</Fragment>
|
||||
))}
|
||||
</div>
|
||||
{...props}
|
||||
>
|
||||
{attachments.files.map((file) => (
|
||||
<div key={file.id} className="max-w-60">
|
||||
{children(file)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useParams, useSearchParams } from "next/navigation";
|
||||
import { useEffect, useMemo, useRef } from "react";
|
||||
|
||||
import { usePromptInputController } from "@/components/ai-elements/prompt-input";
|
||||
import { useOptionalPromptInputController } from "@/components/ai-elements/prompt-input";
|
||||
import { useI18n } from "@/core/i18n/hooks";
|
||||
|
||||
/**
|
||||
@@ -11,7 +11,11 @@ export function useSpecificChatMode() {
|
||||
const { t } = useI18n();
|
||||
const { thread_id: threadIdFromPath } = useParams<{ thread_id: string }>();
|
||||
const searchParams = useSearchParams();
|
||||
const promptInputController = usePromptInputController();
|
||||
// A regular history item can navigate here while Vite Fast Refresh is
|
||||
// reconciling the previous page tree. In that short window the page may see
|
||||
// a stale provider context. This hook only pre-fills the skill-mode draft,
|
||||
// so it must not make an ordinary chat page fail to render.
|
||||
const promptInputController = useOptionalPromptInputController();
|
||||
const inputInitialValue = useMemo(() => {
|
||||
if (threadIdFromPath !== "new" || searchParams.get("mode") !== "skill") {
|
||||
return undefined;
|
||||
@@ -19,16 +23,21 @@ export function useSpecificChatMode() {
|
||||
return t.inputBox.createSkillPrompt;
|
||||
}, [threadIdFromPath, searchParams, t.inputBox.createSkillPrompt]);
|
||||
const lastInitialValueRef = useRef<string | undefined>(undefined);
|
||||
const setInputRef = useRef(promptInputController.textInput.setInput);
|
||||
setInputRef.current = promptInputController.textInput.setInput;
|
||||
const setInputRef = useRef<((value: string) => void) | undefined>(
|
||||
promptInputController?.textInput.setInput,
|
||||
);
|
||||
setInputRef.current = promptInputController?.textInput.setInput;
|
||||
useEffect(() => {
|
||||
if (
|
||||
inputInitialValue &&
|
||||
inputInitialValue !== lastInitialValueRef.current
|
||||
) {
|
||||
lastInitialValueRef.current = inputInitialValue;
|
||||
setTimeout(() => {
|
||||
setInputRef.current(inputInitialValue);
|
||||
setTimeout(() => {
|
||||
const setInput = setInputRef.current;
|
||||
if (!setInput) return;
|
||||
|
||||
setInput(inputInitialValue);
|
||||
const textarea = document.querySelector("textarea");
|
||||
if (textarea) {
|
||||
textarea.focus();
|
||||
|
||||
Reference in new issue
Block a user