diff --git a/.gitignore b/.gitignore
index f8b97dd..a2b30d0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,6 +44,7 @@ apps/web/test-results/
apps/web/playwright-report/
apps/web/blob-report/
apps/web/.playwright-artifacts/
+.playwright-cli/
**/trace.zip
**/*.webm
.vercel/
diff --git a/apps/web/package.json b/apps/web/package.json
index a844f66..4fa7d88 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -17,6 +17,10 @@
"@plot/api-client": "workspace:*",
"@plot/auth": "workspace:*",
"@stylexjs/stylex": "^0.19.0",
+ "@tiptap/core": "^3.30.2",
+ "@tiptap/extension-placeholder": "^3.30.2",
+ "@tiptap/react": "^3.30.2",
+ "@tiptap/starter-kit": "^3.30.2",
"better-auth": "^1.6.23",
"blobatar": "0.2.0",
"clsx": "^2.1.1",
@@ -26,7 +30,8 @@
"react": "19.2.4",
"react-dom": "19.2.4",
"resend": "^6.17.1",
- "tailwind-merge": "^3.6.0"
+ "tailwind-merge": "^3.6.0",
+ "tiptap-markdown": "^0.9.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
diff --git a/apps/web/src/features/artifacts/artifact-document-surface.tsx b/apps/web/src/features/artifacts/artifact-document-surface.tsx
index 56137b8..b41a226 100644
--- a/apps/web/src/features/artifacts/artifact-document-surface.tsx
+++ b/apps/web/src/features/artifacts/artifact-document-surface.tsx
@@ -2,7 +2,7 @@
import type { Artifact, ArtifactHistoryDetail, PlotApiClient } from "@plot/api-client";
-import { CitedDraftEditor, type SaveArtifactInput } from "@/features/citations/cited-draft-editor";
+import { TiptapDraftEditor, type SaveArtifactInput } from "@/features/citations/tiptap-draft-editor";
import { ExportDialog } from "@/features/citations/export-dialog";
type ArtifactDocumentSurfaceProps = {
@@ -47,7 +47,7 @@ export function ArtifactDocumentSurface({
{!workspacePresentation ? (
{shownPack.title || "Generated artifact"}
) : null}
- : Editing and delivery are disabled for this snapshot.
}
- ({
),
}));
-vi.mock("@/features/citations/cited-draft-editor", () => ({ CitedDraftEditor: () => Reviewed artifact
}));
+vi.mock("@/features/citations/tiptap-draft-editor", () => ({ TiptapDraftEditor: () => Reviewed artifact
}));
vi.mock("@/features/citations/export-dialog", () => ({ ExportDialog: ({ presentation }: { presentation?: string }) => presentation === "copy" ? Copy : null }));
vi.mock("@/features/citations/artifact-history-panel", () => ({ ArtifactHistoryPanel: () => History
}));
diff --git a/apps/web/src/features/citations/tiptap-citation-extension.tsx b/apps/web/src/features/citations/tiptap-citation-extension.tsx
new file mode 100644
index 0000000..1274baa
--- /dev/null
+++ b/apps/web/src/features/citations/tiptap-citation-extension.tsx
@@ -0,0 +1,317 @@
+"use client";
+
+import { Node as TiptapNode, mergeAttributes } from "@tiptap/core";
+import { NodeViewWrapper, ReactNodeViewRenderer, type NodeViewProps } from "@tiptap/react";
+import { Citation } from "@astryxdesign/core/Citation";
+import { ExternalLink, ChevronLeft, ChevronRight, X } from "lucide-react";
+import { useState, useId, useRef, useEffect, type ReactNode } from "react";
+
+export type CitationSourceItem = {
+ title: string;
+ url: string;
+ provider?: string;
+ excerpt?: string;
+};
+
+export const TiptapCitationExtension = TiptapNode.create({
+ name: "citation",
+ group: "inline",
+ inline: true,
+ atom: true,
+ selectable: true,
+
+ addAttributes() {
+ return {
+ statementId: {
+ default: null,
+ parseHTML: (element) => element.getAttribute("data-statement-id"),
+ renderHTML: (attributes) => ({
+ "data-statement-id": attributes.statementId,
+ }),
+ },
+ sources: {
+ default: [],
+ parseHTML: (element) => {
+ try {
+ const raw = element.getAttribute("data-sources");
+ return raw ? JSON.parse(raw) : [];
+ } catch {
+ return [];
+ }
+ },
+ renderHTML: (attributes) => ({
+ "data-sources": JSON.stringify(attributes.sources || []),
+ }),
+ },
+ number: {
+ default: 1,
+ parseHTML: (element) => Number(element.getAttribute("data-number")) || 1,
+ renderHTML: (attributes) => ({
+ "data-number": attributes.number,
+ }),
+ },
+ };
+ },
+
+ parseHTML() {
+ return [{ tag: "span[data-citation-node]" }];
+ },
+
+ renderHTML({ HTMLAttributes }) {
+ return ["span", mergeAttributes(HTMLAttributes, { "data-citation-node": "" })];
+ },
+
+ addNodeView() {
+ return ReactNodeViewRenderer(TiptapCitationNodeView);
+ },
+});
+
+export function TiptapCitationNodeView({ node }: NodeViewProps) {
+ const { sources = [], number = 1 } = node.attrs as {
+ sources: CitationSourceItem[];
+ number: number;
+ statementId: string | null;
+ };
+ const [open, setOpen] = useState(false);
+ const [currentIndex, setCurrentIndex] = useState(0);
+ const popoverId = useId();
+ const popoverRef = useRef(null);
+ const triggerRef = useRef(null);
+
+ const sourceList: CitationSourceItem[] = sources.length
+ ? sources
+ : [{ title: "Source", url: "#" }];
+ const currentSource = sourceList[currentIndex] || sourceList[0];
+ const primarySource = sourceList[0];
+ const additionalCount = sourceList.length - 1;
+
+ useEffect(() => {
+ if (!open) return;
+
+ function handleOutsideClick(event: Event) {
+ if (
+ event.target instanceof Node &&
+ !popoverRef.current?.contains(event.target) &&
+ !triggerRef.current?.contains(event.target)
+ ) {
+ setOpen(false);
+ }
+ }
+
+ function handleKeyDown(event: KeyboardEvent) {
+ if (event.key === "Escape") {
+ event.preventDefault();
+ setOpen(false);
+ }
+ }
+
+ document.addEventListener("pointerdown", handleOutsideClick, true);
+ document.addEventListener("click", handleOutsideClick, true);
+ document.addEventListener("keydown", handleKeyDown);
+ return () => {
+ document.removeEventListener("pointerdown", handleOutsideClick, true);
+ document.removeEventListener("click", handleOutsideClick, true);
+ document.removeEventListener("keydown", handleKeyDown);
+ };
+ }, [open]);
+
+ return (
+
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ setOpen((prev) => !prev);
+ }}
+ onKeyDown={(e) => {
+ if (e.key === "Enter" || e.key === " ") {
+ e.preventDefault();
+ setOpen((prev) => !prev);
+ }
+ }}
+ className="inline-flex cursor-pointer items-center transition-transform hover:opacity-90 active:scale-95"
+ >
+ 0 ? `${primarySource.title} +${additionalCount}` : primarySource.title,
+ url: primarySource.url,
+ icon: getSourceProviderIcon(primarySource),
+ }}
+ number={number}
+ variant="label"
+ />
+
+
+ {open ? (
+
+ {/* Header with Provider Badge and Navigation */}
+
+
+ {getSourceProviderIcon(currentSource)}
+ {currentSource.provider || resolveProviderName(currentSource)}
+
+
+
+ {sourceList.length > 1 ? (
+
+ {
+ e.stopPropagation();
+ setCurrentIndex((prev) => Math.max(0, prev - 1));
+ }}
+ className="inline-flex size-6 items-center justify-center rounded-md text-black/50 hover:bg-black/5 disabled:opacity-25 dark:text-white/50 dark:hover:bg-white/10"
+ >
+
+
+
+ {currentIndex + 1} / {sourceList.length}
+
+ = sourceList.length - 1}
+ aria-label="Next source"
+ onClick={(e) => {
+ e.stopPropagation();
+ setCurrentIndex((prev) => Math.min(sourceList.length - 1, prev + 1));
+ }}
+ className="inline-flex size-6 items-center justify-center rounded-md text-black/50 hover:bg-black/5 disabled:opacity-25 dark:text-white/50 dark:hover:bg-white/10"
+ >
+
+
+
+ ) : null}
+
+
{
+ e.stopPropagation();
+ setOpen(false);
+ }}
+ className="inline-flex size-6 items-center justify-center rounded-md text-black/45 hover:bg-black/5 hover:text-black dark:text-white/45 dark:hover:bg-white/10 dark:hover:text-white"
+ >
+
+
+
+
+
+ {/* Body */}
+
+
+ {currentSource.title}
+
+
+ {currentSource.excerpt ? (
+
+ {currentSource.excerpt}
+
+ ) : null}
+
+ {currentSource.url && currentSource.url !== "#" ? (
+
+ ) : null}
+
+
+ ) : null}
+
+ );
+}
+
+function getSourceProviderIcon(source?: CitationSourceItem): ReactNode {
+ const url = (source?.url || "").toLowerCase();
+ const provider = (source?.provider || "").toLowerCase();
+
+ if (url.includes("github.com") || provider === "github") {
+ return (
+
+
+
+ );
+ }
+
+ if (url.includes("linear.app") || provider === "linear") {
+ return (
+
+
+
+ );
+ }
+
+ if (url.includes("notion.so") || provider === "notion") {
+ return (
+
+
+
+ );
+ }
+
+ if (url.includes("slack.com") || provider === "slack") {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ if (url.includes("figma.com") || provider === "figma") {
+ return (
+
+
+
+
+
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
+ );
+}
+
+function resolveProviderName(source?: CitationSourceItem): string {
+ const url = (source?.url || "").toLowerCase();
+ if (url.includes("github.com")) return "GitHub";
+ if (url.includes("linear.app")) return "Linear";
+ if (url.includes("notion.so")) return "Notion";
+ if (url.includes("slack.com")) return "Slack";
+ if (url.includes("figma.com")) return "Figma";
+ return "Source";
+}
diff --git a/apps/web/src/features/citations/tiptap-draft-editor.test.tsx b/apps/web/src/features/citations/tiptap-draft-editor.test.tsx
new file mode 100644
index 0000000..48ad56b
--- /dev/null
+++ b/apps/web/src/features/citations/tiptap-draft-editor.test.tsx
@@ -0,0 +1,114 @@
+// @vitest-environment jsdom
+
+import { render, screen, waitFor } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+import { TiptapDraftEditor } from "./tiptap-draft-editor";
+import type { Artifact } from "@plot/api-client";
+
+const pack: Artifact = {
+ id: "pack-1",
+ status: "NEEDS_REVIEW",
+ title: "July changelog",
+ variant: {
+ id: "variant-1",
+ status: "NEEDS_REVIEW",
+ revisionId: "artifact-revision-1",
+ revisionNumber: 1,
+ lexicalContent: lexicalContent(
+ "Sign-in recovery now explains the next step.",
+ "The release is delightful.",
+ ),
+ sentences: [
+ {
+ id: "sentence-1",
+ revisionId: "sentence-revision-1",
+ revisionNumber: 1,
+ orderIndex: 0,
+ body: "Sign-in recovery now explains the next step.",
+ origin: "GENERATED",
+ citations: [
+ {
+ evidenceId: "evidence-1",
+ provider: "GITHUB",
+ sourceLabel: "PR #184",
+ originalUrl: "https://github.com/acme/plot/pull/184",
+ },
+ ],
+ },
+ {
+ id: "sentence-2",
+ revisionId: "sentence-revision-2",
+ revisionNumber: 1,
+ orderIndex: 1,
+ body: "The release is delightful.",
+ origin: "GENERATED",
+ citations: [],
+ },
+ ],
+ sources: [
+ {
+ evidenceId: "evidence-1",
+ provider: "GITHUB",
+ sourceLabel: "PR #184",
+ originalUrl: "https://github.com/acme/plot/pull/184",
+ statementIds: ["sentence-1"],
+ },
+ ],
+ },
+};
+
+function lexicalContent(...bodies: string[]) {
+ return {
+ root: {
+ children: bodies.map((body) => ({
+ children: [{ detail: 0, format: 0, mode: "normal", style: "", text: body, type: "text", version: 1 }],
+ direction: null,
+ format: "",
+ indent: 0,
+ type: "paragraph",
+ version: 1,
+ })),
+ direction: null,
+ format: "",
+ indent: 0,
+ type: "root",
+ version: 1,
+ },
+ };
+}
+
+describe("TiptapDraftEditor", () => {
+ it("renders historical snapshots read-only without a delivery edit control", () => {
+ render( );
+
+ expect(screen.getByRole("textbox", { name: "Historical artifact content" })).toHaveAttribute("contenteditable", "false");
+ expect(screen.queryByRole("button", { name: "Save draft" })).not.toBeInTheDocument();
+ });
+
+ it("renders document text and Astryx inline citations correctly", async () => {
+ const onSaveArtifact = vi.fn().mockResolvedValue(pack);
+ render( );
+
+ expect(screen.getByRole("textbox", { name: "Draft content" })).toBeInTheDocument();
+ expect(screen.getByText("Sign-in recovery now explains the next step.")).toBeInTheDocument();
+ expect(screen.getByText("The release is delightful.")).toBeInTheDocument();
+ });
+
+ it("displays save confirmation status", async () => {
+ const onSaveArtifact = vi.fn().mockResolvedValue(pack);
+ const onSaveStateChange = vi.fn();
+
+ render(
+ ,
+ );
+
+ await waitFor(() => {
+ expect(onSaveStateChange).toHaveBeenCalledWith("saved");
+ });
+ });
+});
diff --git a/apps/web/src/features/citations/tiptap-draft-editor.tsx b/apps/web/src/features/citations/tiptap-draft-editor.tsx
new file mode 100644
index 0000000..6c7ef35
--- /dev/null
+++ b/apps/web/src/features/citations/tiptap-draft-editor.tsx
@@ -0,0 +1,410 @@
+"use client";
+
+import { useEditor, EditorContent, type JSONContent } from "@tiptap/react";
+import StarterKit from "@tiptap/starter-kit";
+import Placeholder from "@tiptap/extension-placeholder";
+import { Markdown } from "tiptap-markdown";
+import { Save } from "lucide-react";
+import { useCallback, useEffect, useMemo, useRef, useState } from "react";
+
+import type {
+ Artifact,
+ ContentSentence,
+ ContentStatementInput,
+} from "@plot/api-client";
+import { SourcesPopover } from "./sources-popover";
+import { TiptapCitationExtension, type CitationSourceItem } from "./tiptap-citation-extension";
+
+export type SaveArtifactInput = {
+ expectedRevisionNumber: number;
+ lexicalContent: Record;
+ statements: ContentStatementInput[];
+};
+
+type TiptapDraftEditorProps = {
+ pack: Artifact;
+ onSaveArtifact: (input: SaveArtifactInput) => Promise;
+ onPackChange?: (pack: Artifact) => void;
+ readOnly?: boolean;
+ embedded?: boolean;
+ onSaveStateChange?: (state: "saved" | "saving" | "dirty" | "error") => void;
+ initialDraft?: Omit;
+ onDraftChange?: (draft: Omit) => void;
+ presentation?: "panel" | "document";
+ saveRequestToken?: number;
+};
+
+export function TiptapDraftEditor(props: TiptapDraftEditorProps) {
+ const revisionKey = `${props.pack.variant.revisionId}:${props.pack.variant.revisionNumber}:${props.readOnly ? "read-only" : "editable"}`;
+ return ;
+}
+
+function TiptapArtifactEditor({
+ pack,
+ onSaveArtifact,
+ onPackChange,
+ readOnly = false,
+ embedded = false,
+ onSaveStateChange,
+ initialDraft,
+ onDraftChange,
+ presentation = "panel",
+ saveRequestToken,
+}: TiptapDraftEditorProps) {
+ const sentences = useMemo(
+ () => [...pack.variant.sentences].sort((a, b) => a.orderIndex - b.orderIndex),
+ [pack.variant.sentences],
+ );
+ const revisionNumber = pack.variant.revisionNumber;
+ const revisionKey = `${pack.variant.revisionId}:${revisionNumber}`;
+ const initialContent = useMemo(
+ () => initialDraft?.lexicalContent ?? pack.variant.lexicalContent,
+ [initialDraft?.lexicalContent, pack.variant.lexicalContent],
+ );
+
+ const initialTiptapDoc = useMemo(
+ () => convertToTiptapDoc(initialContent, sentences),
+ [initialContent, sentences],
+ );
+
+ const [saving, setSaving] = useState(false);
+ const [message, setMessage] = useState("");
+ const previousSaveRequestRef = useRef(saveRequestToken);
+ const draftStateRef = useRef>(initialContent);
+ const draftStatementsRef = useRef(
+ initialDraft?.statements ?? defaultStatementsFor(sentences),
+ );
+
+ const editor = useEditor({
+ immediatelyRender: false,
+ editable: !readOnly,
+ content: initialTiptapDoc,
+ extensions: [
+ StarterKit.configure({
+ heading: { levels: [1, 2, 3] },
+ }),
+ Placeholder.configure({
+ placeholder: "Write the source-backed artifact…",
+ }),
+ Markdown.configure({
+ html: true,
+ tightLists: true,
+ }),
+ TiptapCitationExtension,
+ ],
+ editorProps: {
+ attributes: {
+ role: "textbox",
+ "aria-label": readOnly ? "Historical artifact content" : "Draft content",
+ "aria-readonly": readOnly ? "true" : "false",
+ class: presentation === "document"
+ ? "min-h-[720px] focus:outline-none text-[15px] leading-6 text-black/88 dark:text-white/88 prose prose-none max-w-none [&_h1]:mb-[22px] [&_h1]:font-display [&_h1]:text-[30px] [&_h1]:leading-[38px] [&_h2]:mb-[22px] [&_h2]:text-[19px] [&_h2]:font-semibold [&_h2]:leading-[26px] [&_li]:mb-1.5 [&_ol]:list-decimal [&_ol]:pl-5 [&_p]:mb-[22px] [&_ul]:list-disc [&_ul]:pl-5"
+ : `min-h-[260px] rounded-lg border border-black/10 px-4 py-4 text-[15px] leading-7 text-black/84 focus:outline-none focus-within:border-black/35 dark:border-white/12 dark:text-white/86 dark:focus-within:border-white/35 prose prose-none max-w-none ${
+ readOnly ? "bg-black/[0.025] dark:bg-white/[0.025]" : "bg-white dark:bg-[#18181b]"
+ }`,
+ },
+ },
+ onUpdate: ({ editor: currentEditor }) => {
+ const json = currentEditor.getJSON();
+ const lexicalJson = tiptapToLexicalJson(json);
+ draftStateRef.current = lexicalJson;
+
+ // Extract statement inputs from block nodes
+ const statements = extractStatementsFromTiptap(json, sentences);
+ draftStatementsRef.current = statements;
+
+ if (!readOnly) {
+ onDraftChange?.({ lexicalContent: lexicalJson, statements });
+ onSaveStateChange?.("dirty");
+ }
+ },
+ });
+
+ useEffect(() => {
+ onSaveStateChange?.("saved");
+ }, [onSaveStateChange, revisionKey]);
+
+ const save = useCallback(async () => {
+ if (saving || readOnly) return;
+ setSaving(true);
+ setMessage("");
+ onSaveStateChange?.("saving");
+ try {
+ const updated = await onSaveArtifact({
+ expectedRevisionNumber: revisionNumber,
+ lexicalContent: draftStateRef.current,
+ statements: draftStatementsRef.current,
+ });
+ onPackChange?.(updated);
+ setMessage(
+ `Saved ${new Intl.DateTimeFormat(undefined, {
+ hour: "numeric",
+ minute: "2-digit",
+ }).format(new Date())}.`,
+ );
+ onSaveStateChange?.("saved");
+ } catch (error) {
+ setMessage(error instanceof Error ? error.message : "The draft could not be saved.");
+ onSaveStateChange?.("error");
+ } finally {
+ setSaving(false);
+ }
+ }, [onPackChange, onSaveArtifact, onSaveStateChange, readOnly, revisionNumber, saving]);
+
+ useEffect(() => {
+ if (saveRequestToken === undefined || previousSaveRequestRef.current === saveRequestToken) return;
+ previousSaveRequestRef.current = saveRequestToken;
+ void save();
+ }, [save, saveRequestToken]);
+
+ const documentPresentation = presentation === "document";
+
+ return (
+
+ {!documentPresentation ? (
+
+
+
+ {readOnly ? "Historical preview" : "Artifact document"}
+
+
+ {readOnly
+ ? "This snapshot is read-only. Editing and delivery are disabled."
+ : "Edit the whole artifact. Sources stay outside the document and stay attached to it."}
+
+
+
+
+ ) : null}
+
+
+
+
+
+ {!documentPresentation ? (
+
+
+ {readOnly ? "Saved snapshot" : saving ? "Saving…" : message || "Saved"}
+
+ {!readOnly ? (
+
void save()}
+ className="inline-flex min-h-10 items-center gap-2 rounded-lg bg-black px-3 text-sm font-semibold text-white transition hover:bg-black/80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-400 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-40 dark:bg-white dark:text-black dark:hover:bg-white/85"
+ >
+
+ {saving ? "Saving…" : "Save draft"}
+
+ ) : null}
+
+ ) : null}
+
+ {message ? (
+
+ {message}
+
+ ) : null}
+
+ );
+}
+
+// Converts Lexical or Tiptap JSON to a Tiptap Document with Inline Citation Nodes
+function convertToTiptapDoc(
+ content: Record | undefined,
+ sentences: ContentSentence[],
+): JSONContent {
+ if (!content) {
+ return {
+ type: "doc",
+ content: [{ type: "paragraph" }],
+ };
+ }
+
+ // If already in Tiptap format
+ if (content.type === "doc" && Array.isArray(content.content)) {
+ return content as JSONContent;
+ }
+
+ // Convert Lexical AST to Tiptap JSON
+ const root = (content.root as Record) || content;
+ const children = Array.isArray(root.children) ? (root.children as Record[]) : [];
+
+ if (!children.length) {
+ return {
+ type: "doc",
+ content: [{ type: "paragraph" }],
+ };
+ }
+
+ const tiptapContent: JSONContent[] = children.map((lexicalNode, blockIndex) => {
+ const nodeType = (lexicalNode.type as string) || "paragraph";
+ const lexicalChildren = Array.isArray(lexicalNode.children)
+ ? (lexicalNode.children as Record[])
+ : [];
+
+ const paragraphContent: JSONContent[] = [];
+
+ // Extract text nodes
+ for (const child of lexicalChildren) {
+ if (child.type === "text" && typeof child.text === "string" && child.text) {
+ paragraphContent.push({
+ type: "text",
+ text: child.text,
+ });
+ } else if (child.type === "linebreak") {
+ paragraphContent.push({
+ type: "hardBreak",
+ });
+ }
+ }
+
+ // Attach inline citation if this sentence block has citations
+ const matchedSentence = sentences[blockIndex] || sentences.find((s) => s.orderIndex === blockIndex);
+ if (matchedSentence && matchedSentence.citations && matchedSentence.citations.length > 0) {
+ const citationSources: CitationSourceItem[] = matchedSentence.citations.map((c) => ({
+ title: c.sourceLabel || "Source",
+ url: c.originalUrl || "#",
+ provider: c.provider || "GitHub",
+ }));
+
+ paragraphContent.push({
+ type: "citation",
+ attrs: {
+ statementId: matchedSentence.id,
+ number: blockIndex + 1,
+ sources: citationSources,
+ },
+ });
+ }
+
+ if (nodeType === "heading") {
+ const tag = (lexicalNode.tag as string) || "h2";
+ const level = tag === "h1" ? 1 : tag === "h3" ? 3 : 2;
+ return {
+ type: "heading",
+ attrs: { level },
+ content: paragraphContent.length ? paragraphContent : undefined,
+ };
+ }
+
+ return {
+ type: "paragraph",
+ content: paragraphContent.length ? paragraphContent : undefined,
+ };
+ });
+
+ return {
+ type: "doc",
+ content: tiptapContent.length ? tiptapContent : [{ type: "paragraph" }],
+ };
+}
+
+function extractStatementsFromTiptap(
+ doc: JSONContent,
+ originalSentences: ContentSentence[],
+): ContentStatementInput[] {
+ const content = doc.content || [];
+ const statements: ContentStatementInput[] = [];
+
+ content.forEach((block, index) => {
+ const text = extractTextFromBlock(block);
+ if (!text.trim()) return;
+
+ const matchedSentence = originalSentences[index];
+ statements.push({
+ id: matchedSentence?.id,
+ orderIndex: index,
+ body: text.trim(),
+ });
+ });
+
+ return statements.length
+ ? statements
+ : [{ id: null, orderIndex: 0, body: "Generated artifact" }];
+}
+
+function extractTextFromBlock(node: JSONContent): string {
+ if (!node.content) return "";
+ return node.content
+ .map((child) => {
+ if (child.type === "text") return child.text || "";
+ if (child.type === "hardBreak") return "\n";
+ return "";
+ })
+ .join("");
+}
+
+function defaultStatementsFor(sentences: ContentSentence[]): ContentStatementInput[] {
+ return sentences.map((sentence) => ({
+ id: sentence.id,
+ orderIndex: sentence.orderIndex,
+ body: sentence.body,
+ }));
+}
+
+function tiptapToLexicalJson(doc: JSONContent): Record {
+ const content = doc.content || [];
+ return {
+ root: {
+ children: content.map((block) => {
+ const paragraphChildren: Record[] = [];
+ if (block.content) {
+ for (const child of block.content) {
+ if (child.type === "text" && child.text) {
+ paragraphChildren.push({
+ detail: 0,
+ format: 0,
+ mode: "normal",
+ style: "",
+ text: child.text,
+ type: "text",
+ version: 1,
+ });
+ } else if (child.type === "hardBreak") {
+ paragraphChildren.push({
+ type: "linebreak",
+ version: 1,
+ });
+ }
+ }
+ }
+ if (!paragraphChildren.length) {
+ paragraphChildren.push({
+ detail: 0,
+ format: 0,
+ mode: "normal",
+ style: "",
+ text: "",
+ type: "text",
+ version: 1,
+ });
+ }
+ return {
+ children: paragraphChildren,
+ direction: null,
+ format: "",
+ indent: 0,
+ type: "paragraph",
+ version: 1,
+ };
+ }),
+ direction: null,
+ format: "",
+ indent: 0,
+ type: "root",
+ version: 1,
+ },
+ };
+}
diff --git a/bun.lock b/bun.lock
index 1b4ddbd..324d050 100644
--- a/bun.lock
+++ b/bun.lock
@@ -16,6 +16,10 @@
"@plot/api-client": "workspace:*",
"@plot/auth": "workspace:*",
"@stylexjs/stylex": "^0.19.0",
+ "@tiptap/core": "^3.30.2",
+ "@tiptap/extension-placeholder": "^3.30.2",
+ "@tiptap/react": "^3.30.2",
+ "@tiptap/starter-kit": "^3.30.2",
"better-auth": "^1.6.23",
"blobatar": "0.2.0",
"clsx": "^2.1.1",
@@ -26,6 +30,7 @@
"react-dom": "19.2.4",
"resend": "^6.17.1",
"tailwind-merge": "^3.6.0",
+ "tiptap-markdown": "^0.9.0",
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
@@ -442,6 +447,64 @@
"@testing-library/react": ["@testing-library/react@16.3.2", "", { "dependencies": { "@babel/runtime": "7.29.7" }, "optionalDependencies": { "@types/react": "19.2.17", "@types/react-dom": "19.2.3" }, "peerDependencies": { "@testing-library/dom": "10.4.1", "react": "19.2.4", "react-dom": "19.2.4" } }, "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g=="],
+ "@tiptap/core": ["@tiptap/core@3.30.2", "", { "peerDependencies": { "@tiptap/pm": "3.30.2" } }, "sha512-QbZC/s1OOqcoUdkhIY16TjR/gCtR0qAk9e4bJwUqOJqZuv5ozqCL5hzWm22jjTPp6c6Ei2tPd6t30VwfIKW4lQ=="],
+
+ "@tiptap/extension-blockquote": ["@tiptap/extension-blockquote@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-BOkwhZenek7vzXBOgKppSrlx4YryBdAYu1p1MXKn0R9A9eNmE2HVhmm0gG49+E8BhsE/TG8wKVclwET42JJiIg=="],
+
+ "@tiptap/extension-bold": ["@tiptap/extension-bold@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-MsvJhPgYejY2D9MhwYJv8AmscozvLBI8qtJ7YLdYZBWkMR4bgmxHq5+xqEfBsao9bOMMwBon9p3+P+/Tq5ReWA=="],
+
+ "@tiptap/extension-bubble-menu": ["@tiptap/extension-bubble-menu@3.30.2", "", { "dependencies": { "@floating-ui/dom": "^1.0.0" }, "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-oS0WiWNXHKpiPYMkcnHm1j7iEvufTGGtLFtcJJd3olb5OS6V2acoXVDL0nNJDmRQ32K3QXut3fbRcMseMxT+lw=="],
+
+ "@tiptap/extension-bullet-list": ["@tiptap/extension-bullet-list@3.30.2", "", { "peerDependencies": { "@tiptap/extension-list": "3.30.2" } }, "sha512-+awIL/TUz4aB3rL68igU1rWfaaoBIAkPcakkktkRq8gYf0bd9eSb48P6kHkpx/3q+JyK7g9vsnltLNHNh6twnA=="],
+
+ "@tiptap/extension-code": ["@tiptap/extension-code@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-r8EZk3R9yGpF6v5xxafAU1HwrD/e+RpbfnmVi2TeB/ZHAsVO62fW96E32G0t6IdaCtOFtAd85hkDAaOfCT4yGg=="],
+
+ "@tiptap/extension-code-block": ["@tiptap/extension-code-block@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-9otGKaQZmePHrLXFtCtz+BYDn5z4sSumTkUqQIQHz0gVxwPoTi7g51RedwxvViTb/zu2XV5ROXYLHIxKxypMPg=="],
+
+ "@tiptap/extension-document": ["@tiptap/extension-document@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-+xIv67V+/2L1uvz98FAT5W7kWEfHwfNV3MD7b4UsKPU0lhcCWuVOXy0JB8yYmdNExqpI7xT9g3MWzREoBvBQSg=="],
+
+ "@tiptap/extension-dropcursor": ["@tiptap/extension-dropcursor@3.30.2", "", { "peerDependencies": { "@tiptap/extensions": "3.30.2" } }, "sha512-nyRKUmItATnKI9AiRChmjhcBbCEsNxRu+AaCz+cx8EvnAcNHsVRdNYL5PmBs3WlNA/Et4Eb2DG0hVQDnNF61eg=="],
+
+ "@tiptap/extension-floating-menu": ["@tiptap/extension-floating-menu@3.30.2", "", { "peerDependencies": { "@floating-ui/dom": "^1.0.0", "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-A8PLvvh8W6PUMrqh+EpBerxm+Ucr0irGxJvwAnzYQmNGNIJ9U4OVgw4OcEU+9JH0gMmEzDcHzMPP2s/s1lIcyw=="],
+
+ "@tiptap/extension-gapcursor": ["@tiptap/extension-gapcursor@3.30.2", "", { "peerDependencies": { "@tiptap/extensions": "3.30.2" } }, "sha512-7Xk0ut6FM+RAsvKxDN3bAtk7zvYZ6Aa8pawJ6s7dLAmLR9JwrZevlcL4FSrj4bR7rqKOj92RYCFxzgTEbWimag=="],
+
+ "@tiptap/extension-hard-break": ["@tiptap/extension-hard-break@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-IxSNgmG3d4OZdUTeebrOI7SxdIWXXJqlcGiSNDabWqxipUitfy3mZ3gDDE6G01koKxZRbhz4KIplAZlpxnTFSg=="],
+
+ "@tiptap/extension-heading": ["@tiptap/extension-heading@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-PblDvgSJ05p1t6hzyPi02xeiBjB0M2abReoGEImqSWCy79UqnAGacgsZo4EeEawtJV1NEP8chhvmX+nRtzdT1A=="],
+
+ "@tiptap/extension-horizontal-rule": ["@tiptap/extension-horizontal-rule@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-j8aswLTsuEdJKC62DF+kw0EgvIRL7QMUyAVp2fdjR0qgM0ZVlEwCC4qIEq3kK9tFVU4kRtQ5BSj/jn6QwrlbCA=="],
+
+ "@tiptap/extension-italic": ["@tiptap/extension-italic@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-pp8uaiuXsUbLm5rYzR1jlWbwm1mAahRajdHwAKBtthFRB2rDvC7ZWhKaCSoKhZvfIDRmu9/B67+uAHoutL0dCA=="],
+
+ "@tiptap/extension-link": ["@tiptap/extension-link@3.30.2", "", { "dependencies": { "linkifyjs": "^4.3.3" }, "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-jwdcymKcrbFpj5hRAuGVLCq8FieVkGFnENyroYmvkad+XAt8ZLy/MTFYRN6SK3ukH6PZMY7H4iObGtciQaC5nw=="],
+
+ "@tiptap/extension-list": ["@tiptap/extension-list@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-MIUpo1Bd9Rf1Qg+TNYNwDZ4xsfFeQahjU9Xhy6UcaszKQzbAM7KCzn5BObNytK1NdcqNHsC8Wj5vFvMKEzrXdw=="],
+
+ "@tiptap/extension-list-item": ["@tiptap/extension-list-item@3.30.2", "", { "peerDependencies": { "@tiptap/extension-list": "3.30.2" } }, "sha512-HWgRCRlGxulE+hN1VUcnWD6P2NE08VBgGtcaxOfdXVqaI93BCK6AhRQZGpLsfKgajLk+5DXTBraaitwnBqzCxg=="],
+
+ "@tiptap/extension-list-keymap": ["@tiptap/extension-list-keymap@3.30.2", "", { "peerDependencies": { "@tiptap/extension-list": "3.30.2" } }, "sha512-TTve3WOlQaYu1ahMqsQ/T0wzaxfgZvcOl3/OuPyInOi8QtxXhqGhFjmYe5jOr56G9W2QDuFWVsZecVwfDte9zg=="],
+
+ "@tiptap/extension-ordered-list": ["@tiptap/extension-ordered-list@3.30.2", "", { "peerDependencies": { "@tiptap/extension-list": "3.30.2" } }, "sha512-Z7OO1HcF0idda1n6vodXeQ3h2ylN9JR4IfIGUYkar5Xl9JusK8PDETTBQZQn//96p49I2d+GoWsD2LXPtjHXXg=="],
+
+ "@tiptap/extension-paragraph": ["@tiptap/extension-paragraph@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-ulEu3LNt+kPVAWEnrhoz13Fs8Q/v/8NUxQbAeteuBchQ8joxJXuWExhpy1fUfZir5+b+W5z7/NesgPjZQfv47w=="],
+
+ "@tiptap/extension-placeholder": ["@tiptap/extension-placeholder@3.30.2", "", { "peerDependencies": { "@tiptap/extensions": "3.30.2" } }, "sha512-Bj1seUvPCoRrD/LpzMoKD+jQIjxuc+oq931GpPq4wobSUUbD4pF/0NMwpCLpiHO19QnTQz8+9p2dqPdlc44LHA=="],
+
+ "@tiptap/extension-strike": ["@tiptap/extension-strike@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-fBLxMXz6hYIURzLOD+/L6aVATztsKham00ANWmGi13vN0hx2lQMYZffN+gR+QqiCDfMQxBXzrf5a7tJuDiQHLQ=="],
+
+ "@tiptap/extension-text": ["@tiptap/extension-text@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-n/iZnirgRmXet6f97kolAnP3j8DsgLSiTbz/KLWc8eBYiFmkjRzkuisOm5xuGdfGIxwpB4x3tlSF4ef4DLnbRg=="],
+
+ "@tiptap/extension-underline": ["@tiptap/extension-underline@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2" } }, "sha512-SZiTMnvqXcnrtJX+X25ZbYsuDO83haGOVMBD/O+mAWYNYXhaSc5Rkph5czzItxrd+Yyp/vs4PiwD7XTNbfqmpA=="],
+
+ "@tiptap/extensions": ["@tiptap/extensions@3.30.2", "", { "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-2LqAHXk26QDsryW+beECxYeBzv5Ylk4GuB3cOmfghS7/G37R2W+Te3TkUK7BT0EWoDryvBT57/5q0DEFIhfZZg=="],
+
+ "@tiptap/pm": ["@tiptap/pm@3.30.2", "", { "dependencies": { "prosemirror-changeset": "^2.4.1", "prosemirror-commands": "^1.7.1", "prosemirror-dropcursor": "^1.8.2", "prosemirror-gapcursor": "^1.4.1", "prosemirror-history": "^1.5.0", "prosemirror-inputrules": "^1.5.1", "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.11", "prosemirror-schema-list": "^1.5.1", "prosemirror-state": "^1.4.4", "prosemirror-tables": "^1.8.5", "prosemirror-transform": "^1.12.0", "prosemirror-view": "^1.41.9" } }, "sha512-BJN8tUx4ppFN3R3cV/FJfrJbJkvo1lj4uciq+nwpjwzdRvFzqIuglWf+HLcJ6CwlYpLOHp7ArgkBg4Q5e60Gog=="],
+
+ "@tiptap/react": ["@tiptap/react@3.30.2", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "fast-equals": "^5.3.3", "use-sync-external-store": "^1.4.0" }, "optionalDependencies": { "@tiptap/extension-bubble-menu": "^3.30.2", "@tiptap/extension-floating-menu": "^3.30.2" }, "peerDependencies": { "@tiptap/core": "3.30.2", "@tiptap/pm": "3.30.2", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-7hGaTstpUeTmQ008mCPkjz+GSlChWhucgy+PeX0z93v4+nh7qM5F+0lh+kJ9zo6Os5abO7v36GtgHRZdQI6+FQ=="],
+
+ "@tiptap/starter-kit": ["@tiptap/starter-kit@3.30.2", "", { "dependencies": { "@tiptap/core": "3.30.2", "@tiptap/extension-blockquote": "3.30.2", "@tiptap/extension-bold": "3.30.2", "@tiptap/extension-bullet-list": "3.30.2", "@tiptap/extension-code": "3.30.2", "@tiptap/extension-code-block": "3.30.2", "@tiptap/extension-document": "3.30.2", "@tiptap/extension-dropcursor": "3.30.2", "@tiptap/extension-gapcursor": "3.30.2", "@tiptap/extension-hard-break": "3.30.2", "@tiptap/extension-heading": "3.30.2", "@tiptap/extension-horizontal-rule": "3.30.2", "@tiptap/extension-italic": "3.30.2", "@tiptap/extension-link": "3.30.2", "@tiptap/extension-list": "3.30.2", "@tiptap/extension-list-item": "3.30.2", "@tiptap/extension-list-keymap": "3.30.2", "@tiptap/extension-ordered-list": "3.30.2", "@tiptap/extension-paragraph": "3.30.2", "@tiptap/extension-strike": "3.30.2", "@tiptap/extension-text": "3.30.2", "@tiptap/extension-underline": "3.30.2", "@tiptap/extensions": "3.30.2", "@tiptap/pm": "3.30.2" } }, "sha512-fJSrhW1CyD4sjYA20evSP4Cp13B/HhbxCdM974K0xpHOVqvCtNU9w2s9hfq9mg2yGoU7MSNHKNYMkJjIi2/Xyw=="],
+
"@tybys/wasm-util": ["@tybys/wasm-util@0.10.3", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg=="],
"@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="],
@@ -456,6 +519,12 @@
"@types/json5": ["@types/json5@0.0.29", "", {}, "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ=="],
+ "@types/linkify-it": ["@types/linkify-it@3.0.5", "", {}, "sha512-yg6E+u0/+Zjva+buc3EIb+29XEg4wltq7cSmd4Uc2EE/1nUVmxyzpX6gUXD0V8jIrG0r7YeOGVIbYRkxeooCtw=="],
+
+ "@types/markdown-it": ["@types/markdown-it@13.0.9", "", { "dependencies": { "@types/linkify-it": "^3", "@types/mdurl": "^1" } }, "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw=="],
+
+ "@types/mdurl": ["@types/mdurl@1.0.5", "", {}, "sha512-6L6VymKTzYSrEf4Nev4Xa1LCHKrlTlYCBMTlQKFuddo1CvQcE52I0mwfOJayueUC7MJuXOeHTcIU683lzd0cUA=="],
+
"@types/node": ["@types/node@20.19.43", "", { "dependencies": { "undici-types": "6.21.0" } }, "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA=="],
"@types/pg": ["@types/pg@8.20.0", "", { "dependencies": { "@types/node": "20.19.43", "pg-protocol": "1.15.0", "pg-types": "2.2.0" } }, "sha512-bEPFOaMAHTEP1EzpvHTbmwR8UsFyHSKsRisLIHVMXnpNefSbGA1bD6CVy+qKjGSqmZqNqBDV2azOBo8TgkcVow=="],
@@ -466,6 +535,8 @@
"@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="],
+ "@types/use-sync-external-store": ["@types/use-sync-external-store@0.0.6", "", {}, "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg=="],
+
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.62.0", "", { "dependencies": { "@eslint-community/regexpp": "4.12.2", "@typescript-eslint/scope-manager": "8.62.0", "@typescript-eslint/type-utils": "8.62.0", "@typescript-eslint/utils": "8.62.0", "@typescript-eslint/visitor-keys": "8.62.0", "ignore": "7.0.5", "natural-compare": "1.4.0", "ts-api-utils": "2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "8.62.0", "eslint": "9.39.4", "typescript": "5.9.3" } }, "sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.62.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.62.0", "@typescript-eslint/types": "8.62.0", "@typescript-eslint/typescript-estree": "8.62.0", "@typescript-eslint/visitor-keys": "8.62.0", "debug": "4.4.3" }, "peerDependencies": { "eslint": "9.39.4", "typescript": "5.9.3" } }, "sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA=="],
@@ -742,6 +813,8 @@
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
+ "fast-equals": ["fast-equals@5.4.1", "", {}, "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ=="],
+
"fast-glob": ["fast-glob@3.3.1", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "@nodelib/fs.walk": "1.2.8", "glob-parent": "5.1.2", "merge2": "1.4.1", "micromatch": "4.0.8" } }, "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
@@ -954,6 +1027,10 @@
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
+ "linkify-it": ["linkify-it@5.0.2", "", { "dependencies": { "uc.micro": "^2.0.0" } }, "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q=="],
+
+ "linkifyjs": ["linkifyjs@4.3.3", "", {}, "sha512-P8aEP5U/D1/IlTY2OeYsErdwh9bGuLE30NcXtKEjgdHcahveQoQwM2yZNsioQHsWFz0P7KKudisbrzCgR0sDHg=="],
+
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
@@ -968,10 +1045,16 @@
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
+ "markdown-it": ["markdown-it@14.3.0", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.5.0", "linkify-it": "^5.0.2", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw=="],
+
+ "markdown-it-task-lists": ["markdown-it-task-lists@2.1.1", "", {}, "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="],
+
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
"mdn-data": ["mdn-data@2.27.1", "", {}, "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ=="],
+ "mdurl": ["mdurl@2.1.0", "", {}, "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg=="],
+
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "3.0.3", "picomatch": "2.3.2" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
@@ -1018,6 +1101,8 @@
"optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "0.1.4", "fast-levenshtein": "2.0.6", "levn": "0.4.1", "prelude-ls": "1.2.1", "type-check": "0.4.0", "word-wrap": "1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
+ "orderedmap": ["orderedmap@2.1.1", "", {}, "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g=="],
+
"own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "1.3.0", "object-keys": "1.1.1", "safe-push-apply": "1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
@@ -1080,8 +1165,38 @@
"prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "1.4.0", "object-assign": "4.1.1", "react-is": "16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="],
+ "prosemirror-changeset": ["prosemirror-changeset@2.4.1", "", { "dependencies": { "prosemirror-transform": "^1.0.0" } }, "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw=="],
+
+ "prosemirror-commands": ["prosemirror-commands@1.7.2", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.10.2" } }, "sha512-q6Q6szxqdu9Xd6EcdKsqXghu5nQdZTpB4Q9yd04WRc7/jt763e/rT60Owh0L1GYY+T46o5rD+9lEN36dZS43tw=="],
+
+ "prosemirror-dropcursor": ["prosemirror-dropcursor@1.8.3", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0", "prosemirror-view": "^1.1.0" } }, "sha512-FoYbsJR8gK+DGlqhNoE29Loa38eIZPzQRIb1VMaDNBoo4OLP6vVof/jR8qFY/6XvUd6Dhug8MDCHl2a/h8RTfQ=="],
+
+ "prosemirror-gapcursor": ["prosemirror-gapcursor@1.4.1", "", { "dependencies": { "prosemirror-keymap": "^1.0.0", "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-view": "^1.0.0" } }, "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw=="],
+
+ "prosemirror-history": ["prosemirror-history@1.5.0", "", { "dependencies": { "prosemirror-state": "^1.2.2", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.31.0", "rope-sequence": "^1.3.0" } }, "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg=="],
+
+ "prosemirror-inputrules": ["prosemirror-inputrules@1.5.1", "", { "dependencies": { "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.0.0" } }, "sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw=="],
+
+ "prosemirror-keymap": ["prosemirror-keymap@1.2.3", "", { "dependencies": { "prosemirror-state": "^1.0.0", "w3c-keyname": "^2.2.0" } }, "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw=="],
+
+ "prosemirror-markdown": ["prosemirror-markdown@1.13.6", "", { "dependencies": { "@types/markdown-it": "^14.0.0", "markdown-it": "^14.0.0", "prosemirror-model": "^1.25.0" } }, "sha512-dY6g2BXRjkHW2ldNRDKfTF0x0R4ifk5rgPaABd/UhvrymtsGVxTbHn01goEsHAvO9nN0O34cttlW1qg/XUcaIg=="],
+
+ "prosemirror-model": ["prosemirror-model@1.25.11", "", { "dependencies": { "orderedmap": "^2.0.0" } }, "sha512-QWg9RhnpLlogAmp3p96uEFrE5txQpFynd4vhBAELkwgOCWQs/X0yCzB3/hrHqiPwf91RG5KyWq6553zs9JqIOQ=="],
+
+ "prosemirror-schema-list": ["prosemirror-schema-list@1.5.1", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.7.3" } }, "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q=="],
+
+ "prosemirror-state": ["prosemirror-state@1.4.4", "", { "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", "prosemirror-view": "^1.27.0" } }, "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw=="],
+
+ "prosemirror-tables": ["prosemirror-tables@1.8.5", "", { "dependencies": { "prosemirror-keymap": "^1.2.3", "prosemirror-model": "^1.25.4", "prosemirror-state": "^1.4.4", "prosemirror-transform": "^1.10.5", "prosemirror-view": "^1.41.4" } }, "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw=="],
+
+ "prosemirror-transform": ["prosemirror-transform@1.12.0", "", { "dependencies": { "prosemirror-model": "^1.21.0" } }, "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w=="],
+
+ "prosemirror-view": ["prosemirror-view@1.42.2", "", { "dependencies": { "prosemirror-model": "^1.25.8", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } }, "sha512-Pdg0l5kXm8aLDquFAnQFTCITg0q44sLqBlHlpsVLD9segdOao8TOfQdAhCrCXyVgPSRr6UDDROOIWA3bIrN9YQ=="],
+
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
+ "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="],
+
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
"react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="],
@@ -1110,6 +1225,8 @@
"rolldown": ["rolldown@1.1.5", "", { "dependencies": { "@oxc-project/types": "0.139.0", "@rolldown/pluginutils": "1.0.1" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.1.5", "@rolldown/binding-darwin-arm64": "1.1.5", "@rolldown/binding-darwin-x64": "1.1.5", "@rolldown/binding-freebsd-x64": "1.1.5", "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", "@rolldown/binding-linux-arm64-gnu": "1.1.5", "@rolldown/binding-linux-arm64-musl": "1.1.5", "@rolldown/binding-linux-ppc64-gnu": "1.1.5", "@rolldown/binding-linux-s390x-gnu": "1.1.5", "@rolldown/binding-linux-x64-gnu": "1.1.5", "@rolldown/binding-linux-x64-musl": "1.1.5", "@rolldown/binding-openharmony-arm64": "1.1.5", "@rolldown/binding-wasm32-wasi": "1.1.5", "@rolldown/binding-win32-arm64-msvc": "1.1.5", "@rolldown/binding-win32-x64-msvc": "1.1.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA=="],
+ "rope-sequence": ["rope-sequence@1.3.4", "", {}, "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ=="],
+
"rou3": ["rou3@0.7.12", "", {}, "sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg=="],
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "1.2.3" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
@@ -1208,6 +1325,8 @@
"tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="],
+ "tiptap-markdown": ["tiptap-markdown@0.9.0", "", { "dependencies": { "@types/markdown-it": "^13.0.7", "markdown-it": "^14.1.0", "markdown-it-task-lists": "^2.1.1", "prosemirror-markdown": "^1.11.1" }, "peerDependencies": { "@tiptap/core": "^3.0.1" } }, "sha512-dKLQ9iiuGNgrlGVjrNauF/UBzWu4LYOx5pkD0jNkmQt/GOwfCJsBuzZTsf1jZ204ANHOm572mZ9PYvGh1S7tpQ=="],
+
"tldts": ["tldts@7.4.8", "", { "dependencies": { "tldts-core": "7.4.8" }, "bin": { "tldts": "bin/cli.js" } }, "sha512-htwgN/8KRB3z3vnC0BOETVh2m499g5GmyTK9Wq5JBLX3FNz6tSBveAd+fQhzy9hkjif8vy2jwDMR1sGhLtZl2A=="],
"tldts-core": ["tldts-core@7.4.8", "", {}, "sha512-c1P7u0EhACHj7lPy4MJm8iTFEU8+nB0LCtddH0fhP7noaVoXAqafMtOOeX+ulpuPBqnrRgRhw494RICT3mbhnw=="],
@@ -1238,6 +1357,8 @@
"typescript-eslint": ["typescript-eslint@8.62.0", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.62.0", "@typescript-eslint/parser": "8.62.0", "@typescript-eslint/typescript-estree": "8.62.0", "@typescript-eslint/utils": "8.62.0" }, "peerDependencies": { "eslint": "9.39.4", "typescript": "5.9.3" } }, "sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q=="],
+ "uc.micro": ["uc.micro@2.1.0", "", {}, "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A=="],
+
"unbox-primitive": ["unbox-primitive@1.1.0", "", { "dependencies": { "call-bound": "1.0.4", "has-bigints": "1.1.0", "has-symbols": "1.1.0", "which-boxed-primitive": "1.1.1" } }, "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw=="],
"undici": ["undici@7.28.0", "", {}, "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA=="],
@@ -1250,10 +1371,14 @@
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "2.3.1" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
+ "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="],
+
"vite": ["vite@8.1.4", "", { "dependencies": { "lightningcss": "1.32.0", "picomatch": "4.0.5", "postcss": "8.5.23", "rolldown": "1.1.5", "tinyglobby": "0.2.17" }, "optionalDependencies": { "@types/node": "20.19.43", "fsevents": "2.3.3", "jiti": "2.7.0" }, "bin": { "vite": "bin/vite.js" } }, "sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ=="],
"vitest": ["vitest@4.1.10", "", { "dependencies": { "@vitest/expect": "4.1.10", "@vitest/mocker": "4.1.10", "@vitest/pretty-format": "4.1.10", "@vitest/runner": "4.1.10", "@vitest/snapshot": "4.1.10", "@vitest/spy": "4.1.10", "@vitest/utils": "4.1.10", "es-module-lexer": "2.3.1", "expect-type": "1.4.0", "magic-string": "0.30.21", "obug": "2.1.3", "pathe": "2.0.3", "picomatch": "4.0.4", "std-env": "4.2.0", "tinybench": "2.9.0", "tinyexec": "1.2.4", "tinyglobby": "0.2.17", "tinyrainbow": "3.1.0", "why-is-node-running": "2.3.0" }, "optionalDependencies": { "@types/node": "20.19.43", "jsdom": "29.1.1" }, "peerDependencies": { "vite": "8.1.4" }, "bin": { "vitest": "./vitest.mjs" } }, "sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw=="],
+ "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="],
+
"w3c-xmlserializer": ["w3c-xmlserializer@5.0.0", "", { "dependencies": { "xml-name-validator": "5.0.0" } }, "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA=="],
"webidl-conversions": ["webidl-conversions@8.0.1", "", {}, "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ=="],
@@ -1330,6 +1455,8 @@
"is-bun-module/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
+ "markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
+
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
"playwright/fsevents": ["fsevents@2.3.2", "", { "os": "darwin" }, "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA=="],
@@ -1338,6 +1465,8 @@
"prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="],
+ "prosemirror-markdown/@types/markdown-it": ["@types/markdown-it@14.2.0", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-NoQ2yGlLWj4wpxMs+TYmRKk3thDrQ97agr7sFqfLsAlvoS8SNQuTrlObhFqG9iugdTtgOE9jpJ6FNM4ZGsa5xQ=="],
+
"sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
"vite/picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="],
@@ -1348,6 +1477,10 @@
"@unrs/resolver-binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
+ "prosemirror-markdown/@types/markdown-it/@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="],
+
+ "prosemirror-markdown/@types/markdown-it/@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="],
+
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
}
}