Fix tiptap selection sync and toolbar event loop

This commit is contained in:
lix-2026
2026-04-19 21:03:25 +08:00
parent 111a87d4fd
commit 394e2a155c
87 changed files with 17415 additions and 527 deletions
@@ -6,7 +6,8 @@ import { isConvexEnabled } from "@/lib/convex/enabled";
import { fetchDocumentMetaViaBridge } from "@/lib/documents/bridge-server";
import { getAuthedConvexClient } from "@/lib/convex/route";
import { buildDocumentBridgeContext, buildDocumentQueryEnvelope } from "@/lib/documents/bridge";
import { buildPageSubtreeProjection } from "@/lib/documents/page-subtree";
import type { PageSubtreeProjection } from "@/lib/documents/page-subtree";
import { normalizeDocumentContentResponse } from "@/lib/documents/page-subtree-response";
import { executeRustBridgeQueryTransport, resolveRustBridgeQueryPlan } from "@/lib/documents/rust-runtime";
interface DocumentPageProps {
@@ -48,15 +49,18 @@ type DocumentContentPayload = {
content?: unknown;
revision?: number | null;
conflict_detection_key?: string | null;
page_subtree?: PageSubtreeProjection | null;
};
async function fetchDocumentContentOnServer(input: {
documentId: string;
workspaceId: string;
title?: string | null;
}): Promise<{
content: unknown;
revision: number | null;
conflictDetectionKey: string | null;
pageSubtree: PageSubtreeProjection | null;
}> {
try {
const headerList = await headers();
@@ -101,23 +105,19 @@ async function fetchDocumentContentOnServer(input: {
client,
plan,
});
const normalized = normalizeDocumentContentResponse({
documentId: input.documentId,
title: input.title ?? null,
payload: result,
});
return {
content: result?.content ?? null,
revision:
typeof result?.revision === "number" && Number.isInteger(result.revision)
? result.revision
: 0,
conflictDetectionKey:
typeof result?.conflict_detection_key === "string" && result.conflict_detection_key.trim()
? result.conflict_detection_key
: `${input.documentId}:0`,
};
return normalized;
} catch {
return {
content: null,
revision: null,
conflictDetectionKey: null,
pageSubtree: null,
};
}
}
@@ -169,11 +169,7 @@ export default async function DocumentPage({ params, searchParams }: DocumentPag
const initialDocumentContent = await fetchDocumentContentOnServer({
documentId: doc.id,
workspaceId: doc.workspace_id,
});
const initialPageSubtree = buildPageSubtreeProjection({
documentId: doc.id,
title: doc.title ?? "无标题",
content: initialDocumentContent.content,
});
return (
@@ -187,7 +183,7 @@ export default async function DocumentPage({ params, searchParams }: DocumentPag
initialContent={initialDocumentContent.content}
initialContentRevision={initialDocumentContent.revision}
initialConflictDetectionKey={initialDocumentContent.conflictDetectionKey}
initialPageSubtree={initialPageSubtree}
initialPageSubtree={initialDocumentContent.pageSubtree}
initialOptions={initialOptions}
initialStats={initialStats}
openTableId={openTableId}
@@ -7,6 +7,8 @@ import {
buildDocumentQueryEnvelope,
documentBridgeErrorResponse,
} from "@/lib/documents/bridge";
import type { PageSubtreeProjection } from "@/lib/documents/page-subtree";
import { normalizeDocumentContentResponse } from "@/lib/documents/page-subtree-response";
import {
executeRustBridgeQueryTransport,
resolveRustBridgeQueryPlan,
@@ -35,6 +37,8 @@ export async function GET(request: Request) {
content?: unknown;
revision?: number | null;
conflict_detection_key?: string | null;
title?: string | null;
page_subtree?: PageSubtreeProjection | null;
} | null>({
client,
plan,
@@ -54,16 +58,17 @@ export async function GET(request: Request) {
);
}
const normalized = normalizeDocumentContentResponse({
documentId,
title: typeof result.title === "string" && result.title.trim() ? result.title : null,
payload: result,
});
return NextResponse.json({
content: result.content ?? null,
revision:
typeof result.revision === "number" && Number.isInteger(result.revision)
? result.revision
: 0,
conflictDetectionKey:
typeof result.conflict_detection_key === "string" && result.conflict_detection_key.trim()
? result.conflict_detection_key
: `${documentId}:0`,
content: normalized.content,
revision: normalized.revision,
conflictDetectionKey: normalized.conflictDetectionKey,
pageSubtree: normalized.pageSubtree,
meta: {
requestId: bridgeContext.requestId,
traceId: bridgeContext.traceId,