| 12345678910111213141516171819202122232425262728293031 |
- "use client";
- import { useEffect, useRef } from "react";
- import { X } from "lucide-react";
- import type { PromptProjection } from "@/lib/types";
- import { RichText } from "@/components/ui/RichText";
- export function PromptDrawer({ prompt, loading, error, onClose }: { prompt?: PromptProjection; loading: boolean; error?: string; onClose: () => void }) {
- const close = useRef<HTMLButtonElement>(null);
- const dialog = useRef<HTMLElement>(null);
- useEffect(() => { close.current?.focus(); }, []);
- useEffect(() => { const handler = (event: KeyboardEvent) => { if (event.key === "Escape") onClose(); }; window.addEventListener("keydown", handler); return () => window.removeEventListener("keydown", handler); }, [onClose]);
- const trapFocus = (event: React.KeyboardEvent<HTMLElement>) => {
- if (event.key !== "Tab") return;
- const focusable = [...(dialog.current?.querySelectorAll<HTMLElement>('button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])') || [])];
- if (!focusable.length) return;
- const first = focusable[0]; const last = focusable[focusable.length - 1];
- if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); }
- else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); }
- };
- return <aside ref={dialog} className="promptDrawer" role="dialog" aria-modal="true" aria-label="Agent 任务与提示词" onKeyDown={trapFocus} onWheelCapture={(event) => event.stopPropagation()}>
- <header><div><span>Agent 提示词</span><h2>{prompt?.actor.label || "Agent"}</h2></div><button ref={close} className="iconButton" type="button" aria-label="关闭提示词" onClick={onClose}><X size={18} /></button></header>
- <div className="promptDrawerBody">
- {loading ? <div className="skeletonLines"><i /><i /><i /></div> : null}
- {error ? <section className="inspectorPanel isMissing"><h3>暂时无法读取</h3><p>{error}</p></section> : null}
- {prompt?.notices.map((notice) => <section className="inspectorPanel promptAccuracy" key={notice.code}><h3>准确性说明</h3><p>{notice.message}</p></section>)}
- {prompt?.exactTask ? <section className="inspectorPanel"><h3>本次真实任务</h3><RichText value={prompt.exactTask.content} /></section> : null}
- {prompt?.systemPrompt ? <section className="inspectorPanel currentPrompt"><h3>当前提示词</h3><p className="promptMeta">{prompt.systemPrompt.source === "current-db" ? "当前数据库版本" : "当前代码文件"}{prompt.systemPrompt.version ? ` · v${prompt.systemPrompt.version}` : ""}</p><RichText value={prompt.systemPrompt.content} /></section> : null}
- </div>
- </aside>;
- }
|