fix(ai-toolbar): gate paste capture on AI availability
This commit is contained in:
@@ -121,6 +121,7 @@ export const AIToolbar = ({
|
||||
"Project sync tomorrow from 9am to 10am on Google Meet with a weekly repeat.",
|
||||
"Dentist appointment on May 14 at 3pm at Smile Studio, add confirmation #A4821.",
|
||||
];
|
||||
const canUseAi = adminAiEnabled && aiEnabled;
|
||||
|
||||
// Ref to imperatively open the file picker from the keyboard shortcut
|
||||
const imageTriggerRef = useRef<{ open: () => void }>(null);
|
||||
@@ -153,7 +154,7 @@ export const AIToolbar = ({
|
||||
// focused element or OS clipboard model (X11/Wayland).
|
||||
// This is the approach used by Excalidraw's actionPaste.
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || isPending) return;
|
||||
if (!isAuthenticated || isPending || !canUseAi || aiLoading) return;
|
||||
|
||||
// ── Handler 1: paste event (works when textarea is NOT focused) ───────
|
||||
const handleDocumentPaste = (e: ClipboardEvent) => {
|
||||
@@ -247,9 +248,9 @@ export const AIToolbar = ({
|
||||
document.removeEventListener("paste", handlePasteHandled, {
|
||||
capture: true,
|
||||
});
|
||||
document.removeEventListener("keydown", handleDocumentKeydown);
|
||||
document.removeEventListener("keydown", handleDocumentKeydown);
|
||||
};
|
||||
}, [isAuthenticated, isPending]); // onImagesSelect intentionally omitted — ref stays current
|
||||
}, [isAuthenticated, isPending, canUseAi, aiLoading]); // onImagesSelect intentionally omitted — ref stays current
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
@@ -261,7 +262,6 @@ export const AIToolbar = ({
|
||||
}
|
||||
|
||||
const hasImages = imagePreviews.length > 0;
|
||||
const canUseAi = adminAiEnabled && aiEnabled;
|
||||
const showDisabledState = isAuthenticated && !canUseAi;
|
||||
|
||||
return (
|
||||
|
||||
@@ -323,6 +323,19 @@ describe("Keyboard shortcuts – toolbar integration contract", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Global paste capture – AI availability gate", () => {
|
||||
test("document-level paste handlers only arm when AI is actually usable", () => {
|
||||
const source = readToolbarSource();
|
||||
|
||||
expect(source).toContain(
|
||||
"if (!isAuthenticated || isPending || !canUseAi || aiLoading) return;",
|
||||
);
|
||||
expect(source).toContain(
|
||||
"}, [isAuthenticated, isPending, canUseAi, aiLoading]);",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Cycle 8: Multi-image thumbnail strip ────────────────────────────────────
|
||||
//
|
||||
// When multiple images are attached, they render as a horizontal scrollable
|
||||
|
||||
Reference in New Issue
Block a user