@blinkk/root-cms 3.0.1-alpha.1 → 3.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/plugin.js CHANGED
@@ -1,19 +1,27 @@
1
1
  import {
2
- ChatClient,
3
- summarizeDiff
4
- } from "./chunk-T5UK2H24.js";
5
- import {
6
- getServerVersion
7
- } from "./chunk-BBOESYH7.js";
2
+ ChatStore,
3
+ findModel,
4
+ generateAltText,
5
+ generateImage,
6
+ generatePublishMessage,
7
+ getAiConfig,
8
+ getServerVersion,
9
+ runChatStream,
10
+ runEditObjectStream,
11
+ serializeAiConfig,
12
+ summarizeDiff,
13
+ translateString
14
+ } from "./chunk-ATPWU3CU.js";
8
15
  import {
9
16
  SearchIndexService,
10
17
  runCronJobs
11
- } from "./chunk-UTSL2E2P.js";
18
+ } from "./chunk-TRM4MQHU.js";
12
19
  import {
13
20
  RootCMSClient,
14
21
  parseDocId,
15
22
  unmarshalData
16
- } from "./chunk-SNZ4S4IC.js";
23
+ } from "./chunk-F4SODS5S.js";
24
+ import "./chunk-CRK7N6RR.js";
17
25
  import "./chunk-MLKGABMK.js";
18
26
 
19
27
  // core/plugin.ts
@@ -110,6 +118,31 @@ function isRecord(value) {
110
118
  function isDocVersion(value) {
111
119
  return value === "draft" || value === "published";
112
120
  }
121
+ async function pipeWebResponse(webResponse, res) {
122
+ res.status(webResponse.status);
123
+ webResponse.headers.forEach((value, key) => {
124
+ res.setHeader(key, value);
125
+ });
126
+ res.setHeader("X-Accel-Buffering", "no");
127
+ if (!webResponse.body) {
128
+ res.end();
129
+ return;
130
+ }
131
+ const reader = webResponse.body.getReader();
132
+ try {
133
+ let done = false;
134
+ while (!done) {
135
+ const next = await reader.read();
136
+ done = next.done;
137
+ if (next.value) {
138
+ res.write(next.value);
139
+ }
140
+ }
141
+ } finally {
142
+ reader.releaseLock();
143
+ res.end();
144
+ }
145
+ }
113
146
  function api(server, options) {
114
147
  async function getCollectionSchema(req, collectionId) {
115
148
  if (req.viteServer) {
@@ -342,39 +375,6 @@ function api(server, options) {
342
375
  res.status(500).json({ success: false, error: "UNKNOWN" });
343
376
  }
344
377
  });
345
- server.use("/cms/api/ai.chat", async (req, res) => {
346
- if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
347
- res.status(400).json({ success: false, error: "BAD_REQUEST" });
348
- return;
349
- }
350
- if (!req.user?.email) {
351
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
352
- return;
353
- }
354
- const reqBody = req.body || {};
355
- if (!reqBody.prompt) {
356
- res.status(400).json({ success: false, error: "MISSING_PROMPT" });
357
- return;
358
- }
359
- const prompt = reqBody.prompt;
360
- try {
361
- const cmsClient = new RootCMSClient(req.rootConfig);
362
- const chatClient = new ChatClient(cmsClient, req.user.email);
363
- const chat = await chatClient.getOrCreateChat(reqBody.chatId);
364
- const apiResponse = {
365
- success: true,
366
- chatId: chat.id,
367
- response: await chat.sendPrompt(prompt, {
368
- mode: reqBody.options?.mode || "chat",
369
- editData: reqBody.options?.editData
370
- })
371
- };
372
- res.status(200).json(apiResponse);
373
- } catch (err) {
374
- console.error(err.stack || err);
375
- res.status(500).json({ success: false, error: "UNKNOWN" });
376
- }
377
- });
378
378
  server.use("/cms/api/ai.diff", async (req, res) => {
379
379
  if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
380
380
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
@@ -406,35 +406,14 @@ function api(server, options) {
406
406
  res.status(200).json({ success: true, summary: "" });
407
407
  return;
408
408
  }
409
- const summary = await summarizeDiff(cmsClient, {
409
+ const summary = await summarizeDiff(req.rootConfig, {
410
410
  before: diffPayload.before,
411
411
  after: diffPayload.after
412
412
  });
413
413
  res.status(200).json({ success: true, summary });
414
414
  } catch (err) {
415
415
  console.error(err.stack || err);
416
- res.status(500).json({ success: false, error: "UNKNOWN" });
417
- }
418
- });
419
- server.use("/cms/api/ai.list_chats", async (req, res) => {
420
- if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
421
- res.status(400).json({ success: false, error: "BAD_REQUEST" });
422
- return;
423
- }
424
- if (!req.user?.email) {
425
- res.status(401).json({ success: false, error: "UNAUTHORIZED" });
426
- return;
427
- }
428
- const reqBody = req.body || {};
429
- const limit = reqBody.limit;
430
- try {
431
- const cmsClient = new RootCMSClient(req.rootConfig);
432
- const chatClient = new ChatClient(cmsClient, req.user.email);
433
- const chats = await chatClient.listChats({ limit });
434
- res.status(200).json({ success: true, chats });
435
- } catch (err) {
436
- console.error(err.stack || err);
437
- res.status(500).json({ success: false, error: "UNKNOWN" });
416
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
438
417
  }
439
418
  });
440
419
  server.use(
@@ -456,13 +435,11 @@ function api(server, options) {
456
435
  return;
457
436
  }
458
437
  try {
459
- const cmsClient = new RootCMSClient(req.rootConfig);
460
- const { generateImage } = await import("./ai-OZY3JXDH.js");
461
- const imageUrl = await generateImage(cmsClient, {
438
+ const result = await generateImage(req.rootConfig, {
462
439
  prompt,
463
440
  aspectRatio
464
441
  });
465
- res.status(200).json({ success: true, image: imageUrl });
442
+ res.status(200).json({ success: true, image: result.imageUrl });
466
443
  } catch (err) {
467
444
  console.error(err.stack || err);
468
445
  res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
@@ -492,28 +469,259 @@ function api(server, options) {
492
469
  }
493
470
  try {
494
471
  const cmsClient = new RootCMSClient(req.rootConfig);
495
- const beforeVersion = "published";
496
- const afterVersion = "draft";
497
472
  const diffPayload = await buildDocDiffPayload(cmsClient, docId, {
498
- beforeVersion,
499
- afterVersion
473
+ beforeVersion: "published",
474
+ afterVersion: "draft"
500
475
  });
501
476
  if (!diffPayload.before && !diffPayload.after) {
502
477
  res.status(200).json({ success: true, message: "Initial version" });
503
478
  return;
504
479
  }
505
- const { generatePublishMessage } = await import("./ai-OZY3JXDH.js");
506
- const message = await generatePublishMessage(cmsClient, {
480
+ const message = await generatePublishMessage(req.rootConfig, {
507
481
  before: diffPayload.before,
508
482
  after: diffPayload.after
509
483
  });
510
484
  res.status(200).json({ success: true, message });
485
+ } catch (err) {
486
+ console.error(err.stack || err);
487
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
488
+ }
489
+ }
490
+ );
491
+ server.use(
492
+ "/cms/api/ai.generate_alt_text",
493
+ async (req, res) => {
494
+ if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
495
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
496
+ return;
497
+ }
498
+ if (!req.user?.email) {
499
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
500
+ return;
501
+ }
502
+ const reqBody = req.body || {};
503
+ const imageUrl = typeof reqBody.imageUrl === "string" ? reqBody.imageUrl.trim() : "";
504
+ if (!imageUrl) {
505
+ res.status(400).json({
506
+ success: false,
507
+ error: "MISSING_REQUIRED_FIELD",
508
+ field: "imageUrl"
509
+ });
510
+ return;
511
+ }
512
+ try {
513
+ const altText = await generateAltText(req.rootConfig, { imageUrl });
514
+ res.status(200).json({ success: true, altText });
515
+ } catch (err) {
516
+ console.error(err.stack || err);
517
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
518
+ }
519
+ }
520
+ );
521
+ server.use("/cms/api/ai.config", async (req, res) => {
522
+ if (!req.user?.email) {
523
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
524
+ return;
525
+ }
526
+ const aiConfig = getAiConfig(req.rootConfig);
527
+ if (!aiConfig) {
528
+ res.status(200).json({ success: true, enabled: false });
529
+ return;
530
+ }
531
+ res.status(200).json({ success: true, enabled: true, ...serializeAiConfig(aiConfig) });
532
+ });
533
+ server.use("/cms/api/ai.chat", async (req, res) => {
534
+ if (req.method !== "POST") {
535
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
536
+ return;
537
+ }
538
+ if (!req.user?.email) {
539
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
540
+ return;
541
+ }
542
+ const aiConfig = getAiConfig(req.rootConfig);
543
+ if (!aiConfig) {
544
+ res.status(404).json({ success: false, error: "AI_NOT_CONFIGURED" });
545
+ return;
546
+ }
547
+ const body = req.body || {};
548
+ const messages = body.messages || [];
549
+ if (!Array.isArray(messages) || messages.length === 0) {
550
+ res.status(400).json({ success: false, error: "MISSING_MESSAGES" });
551
+ return;
552
+ }
553
+ const model = findModel(aiConfig, body.modelId);
554
+ if (!model) {
555
+ res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
556
+ return;
557
+ }
558
+ const cmsClient = new RootCMSClient(req.rootConfig);
559
+ const store = new ChatStore(cmsClient, req.user.email);
560
+ const requestedChatId = typeof body.chatId === "string" ? body.chatId.trim() : "";
561
+ let chatId = "";
562
+ if (requestedChatId) {
563
+ const existing = await store.getChat(requestedChatId);
564
+ if (existing) {
565
+ chatId = existing.id;
566
+ } else {
567
+ const created = await store.createChat({
568
+ id: requestedChatId,
569
+ modelId: model.id
570
+ });
571
+ chatId = created.id;
572
+ }
573
+ } else {
574
+ const created = await store.createChat({ modelId: model.id });
575
+ chatId = created.id;
576
+ }
577
+ try {
578
+ const streamResponse = await runChatStream({
579
+ rootConfig: req.rootConfig,
580
+ cmsClient,
581
+ config: aiConfig,
582
+ model,
583
+ messages,
584
+ chatId,
585
+ user: req.user.email
586
+ });
587
+ streamResponse.headers.set("x-root-cms-chat-id", chatId);
588
+ await pipeWebResponse(streamResponse, res);
589
+ } catch (err) {
590
+ console.error(err.stack || err);
591
+ if (!res.headersSent) {
592
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
593
+ } else {
594
+ res.end();
595
+ }
596
+ }
597
+ });
598
+ server.use(
599
+ "/cms/api/ai.edit_object",
600
+ async (req, res) => {
601
+ if (req.method !== "POST") {
602
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
603
+ return;
604
+ }
605
+ if (!req.user?.email) {
606
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
607
+ return;
608
+ }
609
+ const aiConfig = getAiConfig(req.rootConfig);
610
+ if (!aiConfig) {
611
+ res.status(404).json({ success: false, error: "AI_NOT_CONFIGURED" });
612
+ return;
613
+ }
614
+ const body = req.body || {};
615
+ const messages = body.messages || [];
616
+ if (!Array.isArray(messages) || messages.length === 0) {
617
+ res.status(400).json({ success: false, error: "MISSING_MESSAGES" });
618
+ return;
619
+ }
620
+ const model = findModel(aiConfig, body.modelId);
621
+ if (!model) {
622
+ res.status(400).json({ success: false, error: "UNKNOWN_MODEL" });
623
+ return;
624
+ }
625
+ try {
626
+ const streamResponse = await runEditObjectStream({
627
+ rootConfig: req.rootConfig,
628
+ config: aiConfig,
629
+ model,
630
+ messages,
631
+ editData: body.editData
632
+ });
633
+ await pipeWebResponse(streamResponse, res);
634
+ } catch (err) {
635
+ console.error(err.stack || err);
636
+ if (!res.headersSent) {
637
+ res.status(500).json({ success: false, error: err.message || "UNKNOWN" });
638
+ } else {
639
+ res.end();
640
+ }
641
+ }
642
+ }
643
+ );
644
+ server.use(
645
+ "/cms/api/ai.chats.list",
646
+ async (req, res) => {
647
+ if (!req.user?.email) {
648
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
649
+ return;
650
+ }
651
+ const cmsClient = new RootCMSClient(req.rootConfig);
652
+ const store = new ChatStore(cmsClient, req.user.email);
653
+ try {
654
+ const chats = await store.listChats({ limit: req.body?.limit });
655
+ res.status(200).json({
656
+ success: true,
657
+ chats: chats.map((c) => ({
658
+ id: c.id,
659
+ title: c.title,
660
+ modelId: c.modelId,
661
+ createdAt: c.createdAt.toMillis(),
662
+ modifiedAt: c.modifiedAt.toMillis()
663
+ }))
664
+ });
511
665
  } catch (err) {
512
666
  console.error(err.stack || err);
513
667
  res.status(500).json({ success: false, error: "UNKNOWN" });
514
668
  }
515
669
  }
516
670
  );
671
+ server.use(
672
+ "/cms/api/ai.chats.get",
673
+ async (req, res) => {
674
+ if (!req.user?.email) {
675
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
676
+ return;
677
+ }
678
+ const id = String(req.body?.id || "").trim();
679
+ if (!id) {
680
+ res.status(400).json({ success: false, error: "MISSING_ID" });
681
+ return;
682
+ }
683
+ const cmsClient = new RootCMSClient(req.rootConfig);
684
+ const store = new ChatStore(cmsClient, req.user.email);
685
+ const chat = await store.getChat(id);
686
+ if (!chat) {
687
+ res.status(404).json({ success: false, error: "NOT_FOUND" });
688
+ return;
689
+ }
690
+ res.status(200).json({
691
+ success: true,
692
+ chat: {
693
+ id: chat.id,
694
+ title: chat.title,
695
+ modelId: chat.modelId,
696
+ messages: chat.messages,
697
+ createdAt: chat.createdAt.toMillis(),
698
+ modifiedAt: chat.modifiedAt.toMillis()
699
+ }
700
+ });
701
+ }
702
+ );
703
+ server.use(
704
+ "/cms/api/ai.chats.delete",
705
+ async (req, res) => {
706
+ if (req.method !== "POST") {
707
+ res.status(400).json({ success: false, error: "BAD_REQUEST" });
708
+ return;
709
+ }
710
+ if (!req.user?.email) {
711
+ res.status(401).json({ success: false, error: "UNAUTHORIZED" });
712
+ return;
713
+ }
714
+ const id = String(req.body?.id || "").trim();
715
+ if (!id) {
716
+ res.status(400).json({ success: false, error: "MISSING_ID" });
717
+ return;
718
+ }
719
+ const cmsClient = new RootCMSClient(req.rootConfig);
720
+ const store = new ChatStore(cmsClient, req.user.email);
721
+ await store.deleteChat(id);
722
+ res.status(200).json({ success: true });
723
+ }
724
+ );
517
725
  server.use("/cms/api/ai.translate", async (req, res) => {
518
726
  if (req.method !== "POST" || !String(req.get("content-type")).startsWith("application/json")) {
519
727
  res.status(400).json({ success: false, error: "BAD_REQUEST" });
@@ -533,9 +741,7 @@ function api(server, options) {
533
741
  return;
534
742
  }
535
743
  try {
536
- const cmsClient = new RootCMSClient(req.rootConfig);
537
- const { translateString } = await import("./ai-OZY3JXDH.js");
538
- const translations = await translateString(cmsClient, {
744
+ const translations = await translateString(req.rootConfig, {
539
745
  sourceText,
540
746
  targetLocales,
541
747
  description,
@@ -1378,7 +1584,7 @@ function cmsPlugin(options) {
1378
1584
  if (process.env.NODE_ENV === "development" && options.watch !== false) {
1379
1585
  plugin.onFileChange = (eventName, filepath) => {
1380
1586
  if (filepath.endsWith(".schema.ts")) {
1381
- import("./generate-types-MHWSSOWV.js").then((generateTypesModule) => {
1587
+ import("./generate-types-SPV7I3A5.js").then((generateTypesModule) => {
1382
1588
  const generateTypes = generateTypesModule.generateTypes;
1383
1589
  generateTypes();
1384
1590
  });
package/dist/project.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { S as Schema, C as Collection } from './schema-rjBOZk-j.js';
1
+ import { S as Schema, C as Collection } from './schema-Db_xODoi.js';
2
2
 
3
3
  /**
4
4
  * Loads various files or configurations from the project.
@@ -27,5 +27,18 @@ declare function resolveOneOfPatterns(schemaObj: Schema): Schema;
27
27
  * `/collections/<id>.schema.ts`.
28
28
  */
29
29
  declare function getCollectionSchema(collectionId: string): Collection | null;
30
+ /**
31
+ * Converts all `oneof` field type definitions into a map keyed by the type
32
+ * name. The field definitions are replaced with an array of type names.
33
+ *
34
+ * String references (used for self-referencing schemas) are resolved from the
35
+ * project's schema modules. SchemaPatterns are resolved by matching file paths.
36
+ *
37
+ * Schemas pulled in via SchemaPattern or string-name reference are always
38
+ * deep-cloned before being walked. The walk rewrites `oneof` fields in place,
39
+ * and skipping the clone would mutate the shared entries in `SCHEMA_MODULES`
40
+ * and corrupt subsequent calls (e.g. when building multiple collections).
41
+ */
42
+ declare function convertOneOfTypes(collection: Collection, schemaModules?: Record<string, SchemaModule>): Collection;
30
43
 
31
- export { SCHEMA_MODULES, type SchemaModule, getCollectionSchema, getProjectSchemas, resolveOneOfPatterns };
44
+ export { SCHEMA_MODULES, type SchemaModule, convertOneOfTypes, getCollectionSchema, getProjectSchemas, resolveOneOfPatterns };
package/dist/project.js CHANGED
@@ -1,12 +1,14 @@
1
1
  import {
2
2
  SCHEMA_MODULES,
3
+ convertOneOfTypes,
3
4
  getCollectionSchema,
4
5
  getProjectSchemas,
5
6
  resolveOneOfPatterns
6
- } from "./chunk-R4LKO3EZ.js";
7
+ } from "./chunk-MAZA5B27.js";
7
8
  import "./chunk-MLKGABMK.js";
8
9
  export {
9
10
  SCHEMA_MODULES,
11
+ convertOneOfTypes,
10
12
  getCollectionSchema,
11
13
  getProjectSchemas,
12
14
  resolveOneOfPatterns
@@ -7,25 +7,36 @@ interface RichTextBlock {
7
7
  }
8
8
  interface RichTextData {
9
9
  [key: string]: any;
10
- blocks: any[];
10
+ blocks: RichTextBlock[];
11
11
  }
12
- type RichTextBlockComponent = FunctionalComponent<any>;
12
+ type RichTextComponent = FunctionalComponent<any>;
13
+ type RichTextBlockComponent = RichTextComponent;
14
+ type RichTextInlineComponent = RichTextComponent;
15
+ type RichTextComponentMap = Record<string, RichTextComponent>;
13
16
  interface RichTextContextProps {
14
- components?: Record<string, RichTextBlockComponent>;
17
+ /**
18
+ * Rich text components override for both inline and block level components.
19
+ */
20
+ components?: RichTextComponentMap;
21
+ /**
22
+ * Translator function override.
23
+ */
24
+ t?: (msg: string, params?: Record<string, string | number>) => string;
15
25
  }
16
26
  declare const RichTextContext: preact.Context<RichTextContextProps>;
17
27
  declare function useRichTextContext(): RichTextContextProps;
18
28
  interface RichTextProps {
19
- data: RichTextData;
29
+ data: RichTextData | undefined;
20
30
  components?: Record<string, RichTextBlockComponent>;
31
+ /** @deprecated */
32
+ translate?: boolean;
21
33
  }
22
34
  /** Renders data from the "richtext" field. */
23
- declare function RichText(props: RichTextProps): preact.JSX.Element;
35
+ declare function RichText(props: RichTextProps): preact.JSX.Element | null;
24
36
  declare namespace RichText {
37
+ var Block: (props: RichTextBlock) => preact.JSX.Element | null;
25
38
  var ParagraphBlock: (props: RichTextParagraphBlockProps) => preact.JSX.Element | null;
26
- var DelimiterBlock: () => preact.JSX.Element;
27
39
  var HeadingBlock: (props: RichTextHeadingBlockProps) => preact.JSX.Element | null;
28
- var QuoteBlock: (props: RichTextQuoteBlockProps) => preact.JSX.Element | null;
29
40
  var ListBlock: (props: RichTextListBlockProps) => preact.JSX.Element | null;
30
41
  var ImageBlock: (props: RichTextImageBlockProps) => preact.JSX.Element | null;
31
42
  var HtmlBlock: (props: RichTextHtmlBlockProps) => preact.JSX.Element | null;
@@ -35,12 +46,9 @@ interface RichTextParagraphBlockProps {
35
46
  type: 'paragraph';
36
47
  data?: {
37
48
  text?: string;
49
+ components?: Record<string, any>;
38
50
  };
39
51
  }
40
- interface RichTextDelimiterBlockProps {
41
- type: 'delimiter';
42
- data?: {};
43
- }
44
52
  interface RichTextHeadingBlockProps {
45
53
  type: 'heading';
46
54
  data?: {
@@ -48,15 +56,10 @@ interface RichTextHeadingBlockProps {
48
56
  text?: string;
49
57
  };
50
58
  }
51
- interface RichTextQuoteBlockProps {
52
- type: 'quote';
53
- data?: {
54
- text?: string;
55
- };
56
- }
57
59
  interface ListItem {
58
60
  content?: string;
59
61
  items?: ListItem[];
62
+ components?: Record<string, any>;
60
63
  }
61
64
  interface RichTextListBlockProps {
62
65
  type: 'orderedList' | 'unorderedList';
@@ -68,6 +71,8 @@ interface RichTextListBlockProps {
68
71
  interface RichTextImageBlockProps {
69
72
  type: 'image';
70
73
  data?: {
74
+ /** The caption entered into the CMS Image field. */
75
+ caption?: string;
71
76
  file?: {
72
77
  url: string;
73
78
  width: string | number;
@@ -93,5 +98,7 @@ interface RichTextTableBlockProps {
93
98
  }>;
94
99
  };
95
100
  }
101
+ /** Returns whether the rich text value is truthy. */
102
+ declare function testContent(data: RichTextData): boolean;
96
103
 
97
- export { RichText, type RichTextBlock, type RichTextBlockComponent, RichTextContext, type RichTextContextProps, type RichTextData, type RichTextDelimiterBlockProps, type RichTextHeadingBlockProps, type RichTextHtmlBlockProps, type RichTextImageBlockProps, type RichTextListBlockProps, type RichTextParagraphBlockProps, type RichTextProps, type RichTextQuoteBlockProps, type RichTextTableBlockProps, useRichTextContext };
104
+ export { RichText, type RichTextBlock, type RichTextBlockComponent, type RichTextComponent, type RichTextComponentMap, RichTextContext, type RichTextContextProps, type RichTextData, type RichTextHeadingBlockProps, type RichTextHtmlBlockProps, type RichTextImageBlockProps, type RichTextInlineComponent, type RichTextListBlockProps, type RichTextParagraphBlockProps, type RichTextProps, type RichTextTableBlockProps, testContent, useRichTextContext };