diff --git a/src/components/json-drawer.tsx b/src/components/json-drawer.tsx index 7ea94de..8ae66fc 100644 --- a/src/components/json-drawer.tsx +++ b/src/components/json-drawer.tsx @@ -2,6 +2,7 @@ import { validateBlockKit } from '@tightknitai/slack-block-kit-validator'; import { Check, Copy } from 'lucide-react'; import { useEffect, useMemo, useRef, useState } from 'react'; import { BLOCKS_INPUT_SHAPE_ERROR, unwrapBlocksInput } from '../lib/parse-blocks-input'; +import { Button } from '../lib/ui/button'; import { Sheet, SheetContent, SheetDescription, SheetTitle } from '../lib/ui/sheet'; import type { SupportedBlock } from '../types'; @@ -49,10 +50,12 @@ export function JsonDrawer({ const [parseError, setParseError] = useState(null); const [validationErrors, setValidationErrors] = useState([]); const [copied, setCopied] = useState(false); + const [accepted, setAccepted] = useState(false); const textareaRef = useRef(null); const gutterRef = useRef(null); const copyResetRef = useRef | null>(null); + const acceptResetRef = useRef | null>(null); // Hold the latest blocks in a ref so the open-seed effect can read them // without listing them as a dependency. While the drawer is open the @@ -64,19 +67,24 @@ export function JsonDrawer({ useEffect(() => { if (open) { - setValue(JSON.stringify(blocksRef.current, null, 2)); + const current = blocksRef.current; + setValue(current.length === 0 ? '' : JSON.stringify(current, null, 2)); setParseError(null); setValidationErrors([]); setCopied(false); + setAccepted(false); } }, [open]); - // Clear the "copied" reset timer on unmount. + // Clear the "copied" and "accepted" reset timers on unmount. useEffect( () => () => { if (copyResetRef.current) { clearTimeout(copyResetRef.current); } + if (acceptResetRef.current) { + clearTimeout(acceptResetRef.current); + } }, [] ); @@ -103,6 +111,20 @@ export function JsonDrawer({ setValidationErrors([]); return; } + // A blank textarea (or one the user cleared entirely) is treated as an + // empty block list rather than a parse error, so clearing the box to + // paste something new doesn't flash red first. + if (next.trim() === '') { + setParseError(null); + setValidationErrors([]); + onApply([]); + setAccepted(true); + if (acceptResetRef.current) { + clearTimeout(acceptResetRef.current); + } + acceptResetRef.current = setTimeout(() => setAccepted(false), 1800); + return; + } let parsed: unknown; try { parsed = JSON.parse(next); @@ -123,6 +145,11 @@ export function JsonDrawer({ } setParseError(null); onApply(blocks); + setAccepted(true); + if (acceptResetRef.current) { + clearTimeout(acceptResetRef.current); + } + acceptResetRef.current = setTimeout(() => setAccepted(false), 1800); const result = validateBlockKit(blocks, { target: 'blocks', surface: 'message' @@ -151,14 +178,26 @@ export function JsonDrawer({ Edits update the preview as you type. Parse errors show below.
- +
+
+ + Input accepted +
+ +