(null);
// Grow the field to fit wrapped lines as the user types (capped, then scrolls).
useEffect(() => {
const el = ref.current;
if (!el) return;
el.style.height = "0px";
el.style.height = `${Math.min(el.scrollHeight, 220)}px`;
}, [value]);
const submit = () => {
const trimmed = value.trim();
if (!trimmed) return;
onSubmit(trimmed);
setValue("");
if (ref.current) ref.current.style.height = "";
ref.current?.blur();
};
return (
);
}
function fencedBlockData(value: unknown): string {
try {
return ["Block data:", "```json", JSON.stringify(value, null, 2), "```"]
.filter(Boolean)
.join("\n");
} catch {
return ["Block data:", "```text", String(value), "```"].join("\n");
}
}
function renderNestedContentBlock(
block: NestedBlock,
ctx: BlockRenderContext,
editing: boolean,
onChange?: (next: NestedBlock) => void,
) {
if (block.type === "rich-text") {
const currentData =
block.data && typeof block.data === "object"
? (block.data as Record)
: {};
const markdown =
typeof (block.data as { markdown?: unknown } | null)?.markdown ===
"string"
? ((block.data as { markdown: string }).markdown ?? "")
: "";
return editing ? (
onChange?.({
...block,
data: { ...currentData, markdown: nextMarkdown },
})
}
/>
) : (
);
}
const spec = contentBlockRegistry.get(block.type);
if (!spec) return null;
return (
onChange({ ...block, data: nextData })
: undefined
}
ctx={ctx}
/>
);
}