@carlonicora/nextjs-jsonapi 6.12.0 → 6.12.1

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.
Files changed (80) hide show
  1. package/dist/{AbstractService-Ckm2X5fW.d.mts → AbstractService-N0wFgaHD.d.mts} +1 -1
  2. package/dist/{AbstractService-V3VGQMhL.d.ts → AbstractService-kNAykPDk.d.ts} +1 -1
  3. package/dist/{BlockNoteEditor-R2SLOYS6.js → BlockNoteEditor-CBYKEXAQ.js} +19 -19
  4. package/dist/{BlockNoteEditor-R2SLOYS6.js.map → BlockNoteEditor-CBYKEXAQ.js.map} +1 -1
  5. package/dist/{BlockNoteEditor-ZJPGGUQQ.mjs → BlockNoteEditor-N2NBYCPC.mjs} +4 -4
  6. package/dist/{BlockNoteViewer-UCA3LV3N.mjs → BlockNoteViewer-MTGS6YJV.mjs} +2 -2
  7. package/dist/{BlockNoteViewer-FLUHEXE6.js → BlockNoteViewer-WGCDB7IJ.js} +3 -3
  8. package/dist/{BlockNoteViewer-FLUHEXE6.js.map → BlockNoteViewer-WGCDB7IJ.js.map} +1 -1
  9. package/dist/billing/index.js +491 -491
  10. package/dist/billing/index.mjs +3 -3
  11. package/dist/{chunk-6RLATJ2L.mjs → chunk-4FNGWALA.mjs} +2 -2
  12. package/dist/{chunk-BXR36J25.js → chunk-6NZKWIOR.js} +908 -908
  13. package/dist/{chunk-BXR36J25.js.map → chunk-6NZKWIOR.js.map} +1 -1
  14. package/dist/{chunk-SQUMPLNN.mjs → chunk-6XJOQFCP.mjs} +5 -5
  15. package/dist/{chunk-BGLOFZ3K.mjs → chunk-AA52ORW2.mjs} +11 -15
  16. package/dist/chunk-AA52ORW2.mjs.map +1 -0
  17. package/dist/{chunk-YP6W4AB5.mjs → chunk-EWCY6QJA.mjs} +2 -2
  18. package/dist/{chunk-YGEOX2XX.js → chunk-FF4EIQ6H.js} +11 -15
  19. package/dist/chunk-FF4EIQ6H.js.map +1 -0
  20. package/dist/{chunk-FNPXCYHO.js → chunk-L6PYFQTO.js} +8 -8
  21. package/dist/{chunk-FNPXCYHO.js.map → chunk-L6PYFQTO.js.map} +1 -1
  22. package/dist/{chunk-OHJSI6L2.js → chunk-O3PDL2CV.js} +4 -4
  23. package/dist/{chunk-OHJSI6L2.js.map → chunk-O3PDL2CV.js.map} +1 -1
  24. package/dist/client/index.js +4 -4
  25. package/dist/client/index.mjs +3 -3
  26. package/dist/components/index.d.mts +3 -3
  27. package/dist/components/index.d.ts +3 -3
  28. package/dist/components/index.js +4 -4
  29. package/dist/components/index.mjs +3 -3
  30. package/dist/contexts/index.js +4 -4
  31. package/dist/contexts/index.mjs +3 -3
  32. package/dist/core/index.d.mts +7 -7
  33. package/dist/core/index.d.ts +7 -7
  34. package/dist/core/index.js +2 -2
  35. package/dist/core/index.mjs +1 -1
  36. package/dist/email-automations/index.js +93 -93
  37. package/dist/email-automations/index.mjs +3 -3
  38. package/dist/features/analytics/index.d.mts +1 -1
  39. package/dist/features/analytics/index.d.ts +1 -1
  40. package/dist/features/analytics/index.js +120 -120
  41. package/dist/features/analytics/index.mjs +3 -3
  42. package/dist/features/analytics/server.js +4 -4
  43. package/dist/features/analytics/server.mjs +2 -2
  44. package/dist/features/blog/index.js +150 -150
  45. package/dist/features/blog/index.mjs +3 -3
  46. package/dist/features/blog/server.js +16 -16
  47. package/dist/features/blog/server.mjs +1 -1
  48. package/dist/features/help/index.js +37 -37
  49. package/dist/features/help/index.mjs +3 -3
  50. package/dist/features/tokenusage/index.d.mts +1 -1
  51. package/dist/features/tokenusage/index.d.ts +1 -1
  52. package/dist/features/tokenusage/index.js +126 -126
  53. package/dist/features/tokenusage/index.mjs +3 -3
  54. package/dist/index.d.mts +4 -4
  55. package/dist/index.d.ts +4 -4
  56. package/dist/index.js +3 -3
  57. package/dist/index.mjs +2 -2
  58. package/dist/{referral-admin.interface-CMwl5NiV.d.ts → referral-admin.interface-CnytdDaQ.d.ts} +1 -1
  59. package/dist/{referral-admin.interface-C87ApGMR.d.mts → referral-admin.interface-RVO-ZTJz.d.mts} +1 -1
  60. package/dist/{s3.service-BG136Btz.d.mts → s3.service-CeUsKm-d.d.mts} +1 -1
  61. package/dist/{s3.service-CsrsRPRn.d.ts → s3.service-SkZnngeR.d.ts} +1 -1
  62. package/dist/server/index.d.mts +2 -2
  63. package/dist/server/index.d.ts +2 -2
  64. package/dist/server/index.js +3 -3
  65. package/dist/server/index.mjs +2 -2
  66. package/package.json +1 -1
  67. package/src/core/abstracts/AbstractService.ts +5 -11
  68. package/src/core/abstracts/ClientAbstractService.ts +5 -11
  69. package/src/core/abstracts/__tests__/AbstractService.locale.spec.ts +43 -0
  70. package/src/core/abstracts/__tests__/ClientAbstractService.locale.spec.ts +69 -0
  71. package/src/i18n/__tests__/config.spec.ts +22 -1
  72. package/src/i18n/config.ts +5 -0
  73. package/src/i18n/index.ts +1 -0
  74. package/dist/chunk-BGLOFZ3K.mjs.map +0 -1
  75. package/dist/chunk-YGEOX2XX.js.map +0 -1
  76. /package/dist/{BlockNoteEditor-ZJPGGUQQ.mjs.map → BlockNoteEditor-N2NBYCPC.mjs.map} +0 -0
  77. /package/dist/{BlockNoteViewer-UCA3LV3N.mjs.map → BlockNoteViewer-MTGS6YJV.mjs.map} +0 -0
  78. /package/dist/{chunk-6RLATJ2L.mjs.map → chunk-4FNGWALA.mjs.map} +0 -0
  79. /package/dist/{chunk-SQUMPLNN.mjs.map → chunk-6XJOQFCP.mjs.map} +0 -0
  80. /package/dist/{chunk-YP6W4AB5.mjs.map → chunk-EWCY6QJA.mjs.map} +0 -0
@@ -37,7 +37,7 @@ declare abstract class AbstractService {
37
37
  /**
38
38
  * Extract locale from client-side URL pathname
39
39
  * URL structure: /{locale}/route-path (e.g., /it/accounts)
40
- * Fallback chain: URL locale → navigator.language → "en"
40
+ * Fallback chain: URL locale → the app's default locale (first supported locale)
41
41
  */
42
42
  private static getClientLocale;
43
43
  static next<T>(params: {
@@ -37,7 +37,7 @@ declare abstract class AbstractService {
37
37
  /**
38
38
  * Extract locale from client-side URL pathname
39
39
  * URL structure: /{locale}/route-path (e.g., /it/accounts)
40
- * Fallback chain: URL locale → navigator.language → "en"
40
+ * Fallback chain: URL locale → the app's default locale (first supported locale)
41
41
  */
42
42
  private static getClientLocale;
43
43
  static next<T>(params: {
@@ -6,10 +6,10 @@
6
6
 
7
7
 
8
8
 
9
- var _chunkBXR36J25js = require('./chunk-BXR36J25.js');
9
+ var _chunk6NZKWIORjs = require('./chunk-6NZKWIOR.js');
10
10
 
11
11
 
12
- var _chunkFNPXCYHOjs = require('./chunk-FNPXCYHO.js');
12
+ var _chunkL6PYFQTOjs = require('./chunk-L6PYFQTO.js');
13
13
  require('./chunk-G4JR3SFV.js');
14
14
 
15
15
 
@@ -17,7 +17,7 @@ require('./chunk-G4JR3SFV.js');
17
17
 
18
18
 
19
19
 
20
- var _chunkYGEOX2XXjs = require('./chunk-YGEOX2XX.js');
20
+ var _chunkFF4EIQ6Hjs = require('./chunk-FF4EIQ6H.js');
21
21
  require('./chunk-GHE4VT2R.js');
22
22
 
23
23
 
@@ -189,7 +189,7 @@ var createDiffActionsInlineContentSpec = /* @__PURE__ */ _chunk7QVYU63Ejs.__name
189
189
  const diffIds = props.inlineContent.props.diffIds;
190
190
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "diff-actions-container mx-2 inline-flex items-center gap-1 align-middle", children: [
191
191
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
192
- _chunkBXR36J25js.Button,
192
+ _chunk6NZKWIORjs.Button,
193
193
  {
194
194
  title: labels.accept,
195
195
  onClick: (e) => {
@@ -201,7 +201,7 @@ var createDiffActionsInlineContentSpec = /* @__PURE__ */ _chunk7QVYU63Ejs.__name
201
201
  }
202
202
  ),
203
203
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
204
- _chunkBXR36J25js.Button,
204
+ _chunk6NZKWIORjs.Button,
205
205
  {
206
206
  title: labels.reject,
207
207
  className: "mx-2 p-0",
@@ -394,9 +394,9 @@ function BlockNoteEditor({
394
394
  }) {
395
395
  const isEditable = resolveEditable({ editable, onChange });
396
396
  const t = _nextintl.useTranslations.call(void 0, );
397
- const locale = _chunkYGEOX2XXjs.useI18nLocale.call(void 0, );
397
+ const locale = _chunkFF4EIQ6Hjs.useI18nLocale.call(void 0, );
398
398
  const { resolvedTheme } = _nextthemes.useTheme.call(void 0, );
399
- const company = _nullishCoalesce(_optionalChain([_chunkBXR36J25js.useCurrentUserContextOptional.call(void 0, ), 'optionalAccess', _6 => _6.company]), () => ( null));
399
+ const company = _nullishCoalesce(_optionalChain([_chunk6NZKWIORjs.useCurrentUserContextOptional.call(void 0, ), 'optionalAccess', _6 => _6.company]), () => ( null));
400
400
  const [acceptedChanges, setAcceptedChanges] = _react3.useState.call(void 0, /* @__PURE__ */ new Set());
401
401
  const [rejectedChanges, setRejectedChanges] = _react3.useState.call(void 0, /* @__PURE__ */ new Set());
402
402
  const editorRef = _react3.useRef.call(void 0, null);
@@ -437,7 +437,7 @@ function BlockNoteEditor({
437
437
  [handleAcceptChange, handleRejectChange, t]
438
438
  );
439
439
  const mentionSpec = _react3.useMemo.call(void 0,
440
- () => _chunkBXR36J25js.createMentionInlineContentSpec.call(void 0, mentionResolveFn, disableMentions, mentionNameResolver),
440
+ () => _chunk6NZKWIORjs.createMentionInlineContentSpec.call(void 0, mentionResolveFn, disableMentions, mentionNameResolver),
441
441
  [disableMentions, mentionResolveFn, mentionNameResolver]
442
442
  );
443
443
  const schema = _react3.useMemo.call(void 0,
@@ -461,7 +461,7 @@ function BlockNoteEditor({
461
461
  const aiEndpoint = _optionalChain([aiConfig, 'optionalAccess', _14 => _14.endpoint]);
462
462
  const aiExtension = _react3.useMemo.call(void 0, () => {
463
463
  if (!hasAiConfig || !aiEndpoint) return void 0;
464
- const base = _chunkFNPXCYHOjs.getPublicApiUrl.call(void 0, );
464
+ const base = _chunkL6PYFQTOjs.getPublicApiUrl.call(void 0, );
465
465
  const url = new URL(aiEndpoint, base.endsWith("/") ? base : base + "/").toString();
466
466
  return _xlai.AIExtension.call(void 0, {
467
467
  transport: new (0, _ai.DefaultChatTransport)({
@@ -506,7 +506,7 @@ function BlockNoteEditor({
506
506
  const uploadImage = _react3.useCallback.call(void 0,
507
507
  async (file) => {
508
508
  if (!company && !uploadKeyPrefix) {
509
- _chunkBXR36J25js.errorToast.call(void 0, {
509
+ _chunk6NZKWIORjs.errorToast.call(void 0, {
510
510
  title: t(`common.errors.upload`),
511
511
  error: t(`common.errors.upload_description`)
512
512
  });
@@ -514,7 +514,7 @@ function BlockNoteEditor({
514
514
  }
515
515
  const fileType = file.type;
516
516
  const key = uploadKeyPrefix ? `${uploadKeyPrefix}/${id}/${file.name}` : `companies/${company.id}/${type}/${id}/${file.name}`;
517
- const s3 = await _chunkYGEOX2XXjs.S3Service.getPreSignedUrl({
517
+ const s3 = await _chunkFF4EIQ6Hjs.S3Service.getPreSignedUrl({
518
518
  key,
519
519
  contentType: fileType,
520
520
  isPublic: true
@@ -524,7 +524,7 @@ function BlockNoteEditor({
524
524
  headers: s3.headers,
525
525
  body: file
526
526
  });
527
- const signedImage = await _chunkYGEOX2XXjs.S3Service.getSignedUrl({
527
+ const signedImage = await _chunkFF4EIQ6Hjs.S3Service.getSignedUrl({
528
528
  key,
529
529
  isPublic: true
530
530
  });
@@ -553,8 +553,8 @@ function BlockNoteEditor({
553
553
  const processedContent = _react3.useMemo.call(void 0, () => {
554
554
  if (diffContent && initialContent) {
555
555
  try {
556
- const diffResult = _chunkYGEOX2XXjs.BlockNoteDiffUtil.diff(initialContent, diffContent);
557
- const renderedDiff = _chunkYGEOX2XXjs.BlockNoteWordDiffRendererUtil.renderWordDiffs(
556
+ const diffResult = _chunkFF4EIQ6Hjs.BlockNoteDiffUtil.diff(initialContent, diffContent);
557
+ const renderedDiff = _chunkFF4EIQ6Hjs.BlockNoteWordDiffRendererUtil.renderWordDiffs(
558
558
  diffResult.blocks,
559
559
  handleAcceptChange,
560
560
  handleRejectChange,
@@ -804,7 +804,7 @@ function BlockNoteEditor({
804
804
  "div",
805
805
  {
806
806
  ref: editorRef,
807
- className: _chunkYGEOX2XXjs.cn.call(void 0,
807
+ className: _chunkFF4EIQ6Hjs.cn.call(void 0,
808
808
  bordered ? "rounded-md border border-input bg-input/20 dark:bg-input/30" : "",
809
809
  "flex flex-col w-full",
810
810
  // Pin BlockNote's font-size so it doesn't jump from 14→16px when the
@@ -827,7 +827,7 @@ function BlockNoteEditor({
827
827
  formattingToolbar: false,
828
828
  slashMenu: !aiActive,
829
829
  theme: resolvedTheme === "dark" ? "dark" : "light",
830
- className: _chunkYGEOX2XXjs.cn.call(void 0,
830
+ className: _chunkFF4EIQ6Hjs.cn.call(void 0,
831
831
  "BlockNoteView flex-1",
832
832
  isEditable && "[&.bn-container]:p-4",
833
833
  // In stretch mode the parent chain caps our height via flex; without
@@ -848,7 +848,7 @@ function BlockNoteEditor({
848
848
  children: [
849
849
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, BlockNoteEditorFormattingToolbar, { showAI: aiActive }),
850
850
  enableMentions && mentionSearchFn && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
851
- _chunkBXR36J25js.BlockNoteEditorMentionSuggestionMenu,
851
+ _chunk6NZKWIORjs.BlockNoteEditorMentionSuggestionMenu,
852
852
  {
853
853
  editor,
854
854
  mentionSearchFn,
@@ -857,7 +857,7 @@ function BlockNoteEditor({
857
857
  }
858
858
  ),
859
859
  _optionalChain([renderOverlays, 'optionalCall', _29 => _29(editor)]),
860
- enableMentions && mentionResolveFn && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkBXR36J25js.BlockNoteEditorMentionHoverCard, { containerRef: editorRef, mentionResolveFn }),
860
+ enableMentions && mentionResolveFn && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunk6NZKWIORjs.BlockNoteEditorMentionHoverCard, { containerRef: editorRef, mentionResolveFn }),
861
861
  aiActive && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
862
862
  _react.SuggestionMenuController,
863
863
  {
@@ -876,4 +876,4 @@ _chunk7QVYU63Ejs.__name.call(void 0, BlockNoteEditor, "BlockNoteEditor");
876
876
 
877
877
 
878
878
  exports.default = BlockNoteEditor;
879
- //# sourceMappingURL=BlockNoteEditor-R2SLOYS6.js.map
879
+ //# sourceMappingURL=BlockNoteEditor-CBYKEXAQ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteEditor-R2SLOYS6.js","../src/components/editors/BlockNoteEditor.tsx","../src/components/editors/BlockNoteEditorAiActions.tsx","../src/components/editors/BlockNoteEditorFormattingToolbar.tsx","../src/components/editors/blockNoteEditorOptions.ts"],"names":["jsx","jsxs","SparklesIcon","type"],"mappings":"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","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteEditor-R2SLOYS6.js","sourcesContent":[null,"\"use client\";\n\nimport { BlockNoteSchema, defaultInlineContentSpecs, filterSuggestionItems, PartialBlock } from \"@blocknote/core\";\nimport * as coreLocales from \"@blocknote/core/locales\";\nimport {\n createReactInlineContentSpec,\n DefaultReactSuggestionItem,\n getDefaultReactSlashMenuItems,\n SuggestionMenuController,\n SuggestionMenuProps,\n useBlockNoteEditor,\n useCreateBlockNote,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { BlockNoteView } from \"@blocknote/shadcn\";\nimport \"@blocknote/shadcn/style.css\";\nimport {\n AIExtension,\n AIMenuController,\n getAISlashMenuItems,\n getDefaultAIMenuItems,\n PromptSuggestionMenu,\n useAIDictionary,\n} from \"@blocknote/xl-ai\";\nimport * as aiLocales from \"@blocknote/xl-ai/locales\";\nimport \"@blocknote/xl-ai/style.css\";\nimport { DefaultChatTransport } from \"ai\";\nimport { CheckIcon, LanguagesIcon, SparklesIcon, TypeIcon, WandSparklesIcon, XIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport { useTheme } from \"next-themes\";\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { getPublicApiUrl } from \"../../client/config\";\nimport { getClientToken } from \"../../client/token\";\nimport { useCurrentUserContextOptional } from \"../../contexts\";\nimport { S3Interface } from \"../../features/s3/data\";\nimport { S3Service } from \"../../features/s3/data/s3.service\";\nimport { UserInterface } from \"../../features/user/data\";\nimport { useI18nLocale } from \"../../i18n/config\";\nimport { Button } from \"../../shadcnui\";\nimport { BlockNoteDiffUtil, BlockNoteWordDiffRendererUtil, cn } from \"../../utils\";\nimport { errorToast } from \"../errors\";\nimport { BlockNoteAiGenerateAction, generateActionItems } from \"./BlockNoteEditorAiActions\";\nimport { BlockNoteEditorFormattingToolbar } from \"./BlockNoteEditorFormattingToolbar\";\nimport { type BlockNoteCollaboration, buildBlockNoteEditorOptions, resolveEditable } from \"./blockNoteEditorOptions\";\nimport { BlockNoteEditorMentionHoverCard } from \"./BlockNoteEditorMentionHoverCard\";\nimport {\n createMentionInlineContentSpec,\n type MentionNameResolver,\n type MentionResolveFn,\n} from \"./BlockNoteEditorMentionInlineContent\";\nimport { BlockNoteEditorMentionSuggestionMenu } from \"./BlockNoteEditorSuggestionMenuController\";\n\n/**\n * Lets app code trigger a whole-document AI action from OUTSIDE the editor\n * (a visible button next to the field label, for instance) without owning the\n * BlockNote instance. Delivered through a plain prop, not a React `ref`:\n * `BlockNoteEditorContainer` is a `next/dynamic` import wrapped in\n * `React.memo`, and refs do not pass through that reliably.\n */\nexport type BlockNoteAiHandle = { invokeAi: (type: BlockNoteAiGenerateAction) => void };\n\nexport type BlockNoteAiConfig = {\n endpoint: string;\n entityType: string;\n entityId?: string;\n /** Whole-document actions shown when nothing is selected. Default: [\"fill-template\"]. */\n actions?: BlockNoteAiGenerateAction[];\n};\n\nexport type BlockNoteEditorProps = {\n id: string;\n type: string;\n initialContent?: PartialBlock[];\n onChange?: (content: any, isEmpty: boolean, hasUnresolvedDiff: boolean) => void;\n size?: \"sm\" | \"md\";\n className?: string;\n markdownContent?: string;\n diffContent?: PartialBlock[];\n placeholder?: string;\n bordered?: boolean;\n /**\n * Storage folder for pasted images, used instead of the current company's\n * `companies/<companyId>/<type>` folder: the image key becomes\n * `<uploadKeyPrefix>/<id>/<file name>`. For platform content edited by users\n * who belong to no company (the blog). Omitted: the company folder, as before.\n */\n uploadKeyPrefix?: string;\n inlineContentSpecs?: Record<string, any>;\n renderOverlays?: (editor: any) => React.ReactNode;\n enableMentions?: boolean;\n disableMentions?: boolean;\n mentionSearchFn?: (\n query: string,\n params?: Record<string, string>,\n ) => Promise<import(\"./BlockNoteEditorSuggestionMenuController\").MentionItem[]>;\n mentionSearchParams?: Record<string, string>;\n mentionResolveFn?: MentionResolveFn;\n suggestionMenuComponent?: React.FC<SuggestionMenuProps<DefaultReactSuggestionItem>>;\n mentionNameResolver?: MentionNameResolver;\n onWarmMentions?: (blocks: PartialBlock[]) => void;\n aiConfig?: BlockNoteAiConfig;\n // When the editor is inside a bounded flex column (parent gives it a real\n // height via flex-1+min-h-0), set this so `.bn-container` shrinks to that\n // height and scrolls internally. Without it the editor grows to fit its\n // content and pushes the surrounding form to scroll instead.\n stretch?: boolean;\n // Gates AI on top of `aiConfig` (credit availability, etc.). Defaults to\n // true so existing callers that only pass `aiConfig` are unaffected. When\n // false, the AI toolbar button and AI slash items are hidden, the editor\n // falls back to the plain slash menu, an open AI menu or run is closed, and\n // no AI request is sent. The AI extension itself stays: toggling this never\n // rebuilds the editor, so unsaved text survives credits running out.\n aiEnabled?: boolean;\n // Filled with a {@link BlockNoteAiHandle} while the editor is mounted and AI\n // is on, so a control outside the editor can run a whole-document AI action.\n // Cleared on unmount and whenever AI is unavailable.\n aiHandleRef?: React.MutableRefObject<BlockNoteAiHandle | null>;\n /**\n * Live co-editing (Yjs). When set, the Yjs document is the content: used\n * instead of `initialContent`/`onChange`. Changing `collaboration.fragment`\n * rebuilds the editor.\n */\n collaboration?: BlockNoteCollaboration;\n /** true: image uploads are off (no paste/drop upload, no upload tab). */\n disableUploads?: boolean;\n /**\n * Overrides the default `onChange !== undefined` editability, e.g. for a\n * collaborative editor, which has no `onChange`.\n */\n editable?: boolean;\n};\n\nfunction isBlockEmpty(block: any): boolean {\n if (!block || typeof block !== \"object\") return true;\n if (block.type !== \"paragraph\") return false;\n if (Array.isArray(block.children) && block.children.length > 0 && !isDocumentEmpty(block.children)) {\n return false;\n }\n if (Array.isArray(block.content)) {\n for (const inline of block.content) {\n if (typeof inline === \"string\") {\n if (inline.trim()) return false;\n } else if (inline && typeof inline === \"object\") {\n if (inline.type !== \"text\") return false;\n if (typeof inline.text === \"string\" && inline.text.trim()) return false;\n }\n }\n } else if (typeof block.content === \"string\" && block.content.trim()) {\n return false;\n }\n return true;\n}\n\nfunction isDocumentEmpty(blocks: any[]): boolean {\n if (!Array.isArray(blocks) || blocks.length === 0) return true;\n return blocks.every(isBlockEmpty);\n}\n\nconst createDiffActionsInlineContentSpec = (\n handleAcceptChange: (diffId: string) => void,\n handleRejectChange: (diffId: string) => void,\n labels: { accept: string; reject: string },\n) => {\n return createReactInlineContentSpec(\n {\n type: \"diffActions\",\n propSchema: {\n diffIds: {\n default: \"\",\n },\n },\n content: \"none\",\n },\n {\n render: (props) => {\n const diffIds = props.inlineContent.props.diffIds;\n\n return (\n <span className=\"diff-actions-container mx-2 inline-flex items-center gap-1 align-middle\">\n <Button\n title={labels.accept}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n diffIds.split(\",\").forEach((id: string) => handleAcceptChange(id.trim()));\n }}\n >\n <CheckIcon className=\"text-success h-3 w-3\" />\n </Button>\n <Button\n title={labels.reject}\n className=\"mx-2 p-0\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n diffIds.split(\",\").forEach((id: string) => handleRejectChange(id.trim()));\n }}\n >\n <XIcon className=\"text-destructive h-3 w-3\" />\n </Button>\n </span>\n );\n },\n },\n );\n};\n\n/**\n * Custom AI menu wrapper. Surfaces our backend-driven action items\n * (Improve Writing, Fix Spelling) above the free-form prompt input.\n *\n * Why a custom wrapper instead of `<AIMenu items={…}>` with the BlockNote\n * defaults: BlockNote's `PromptSuggestionMenu` hijacks Enter to pick the\n * highlighted item whenever `items.length > 0`. We gate items on\n * - status === \"user-input\"\n * - editor has a selection\n * - prompt input is empty\n * so the moment the user types, items disappear and Enter falls back to\n * free-form submission. Outside `user-input` (review / error states) we\n * keep BlockNote's default review buttons (accept/revert/retry/cancel).\n *\n * Each custom item passes a `type` discriminator on `chatRequestOptions.body`.\n * That field flows through `chat.sendMessage(msg, opts)` to the transport's\n * `prepareSendMessagesRequest({messages, body})` (see types.ts in\n * `@blocknote/xl-ai` — `ChatRequestOptions = Parameters<Chat[\"sendMessage\"]>[1]`).\n * The backend dispatcher reads `body.type` and routes to a per-type handler\n * with its own canonical prompt. NO prompt text lives in this file.\n */\nfunction NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {\n const t = useTranslations();\n const editor = useBlockNoteEditor();\n const ai = useExtension(AIExtension);\n const dict = useAIDictionary();\n const status = useExtensionState(AIExtension, {\n selector: (s) => (s.aiMenuState !== \"closed\" ? s.aiMenuState.status : \"closed\"),\n });\n const [prompt, setPrompt] = useState(\"\");\n // Set by items that pre-fill the input (Translate) so handleSubmit knows\n // which `type` to attach when the user submits the captured text. Cleared\n // after submit or on status change.\n const pendingTypeRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (status === \"ai-writing\" || status === \"user-reviewing\" || status === \"error\" || status === \"closed\") {\n setPrompt(\"\");\n pendingTypeRef.current = null;\n }\n }, [status]);\n\n // Selection-edit ops operate per-block. If the user's selection only\n // covers part of a block (cursor mid-paragraph dragged to mid-next), the\n // rewrite would silently replace the WHOLE containing blocks — beyond\n // what the user highlighted. Expand the selection to whole-block bounds\n // BEFORE invokeAI so the user sees exactly what will be rewritten.\n const expandSelectionToBlocks = useCallback(() => {\n const sel = editor.getSelection?.();\n const blocks = (sel as any)?.blocks;\n if (!Array.isArray(blocks) || blocks.length === 0) return;\n const first = blocks[0];\n const last = blocks[blocks.length - 1];\n try {\n (editor as any).setSelection?.(first, last);\n } catch {\n // If BlockNote's setSelection signature changes, fail silently — the\n // backend still operates per-block, so we just lose the visual hint.\n }\n }, [editor]);\n\n const items = useMemo(() => {\n // Outside user-input (reviewing / error), use BlockNote's default\n // review buttons (accept/revert/retry/cancel). Need to wrap onItemClick\n // because the default items expect a setPrompt argument.\n if (status !== \"user-input\") {\n return getDefaultAIMenuItems(editor, status).map((item) => ({\n ...item,\n onItemClick: () => item.onItemClick(setPrompt),\n }));\n }\n const hasSelection = editor.getSelection() !== undefined;\n const hasTyped = prompt.trim().length > 0;\n // Once the user starts typing, hide all items so Enter submits the\n // free-form (or pending-type) prompt instead of being hijacked.\n if (hasTyped) return [];\n\n const invokeGenerate = (type: BlockNoteAiGenerateAction) => {\n void ai.invokeAI({\n userPrompt: type,\n useSelection: false,\n chatRequestOptions: { body: { type } },\n });\n };\n // Whole-document items are shown in BOTH contexts (with and without\n // selection) because they operate on the whole document — they ignore any\n // active selection. Listed first so the first one is the\n // default-highlighted item.\n const generateItems = generateActionItems(actions, invokeGenerate, t);\n\n if (hasSelection) {\n // Selection-edit items + the always-available whole-document items.\n // Each selection item invokes ai.invokeAI with chatRequestOptions\n // carrying the `type` body field. The userPrompt is a short tag — the\n // backend ignores it and uses the canonical prompt for the type instead.\n return [\n ...generateItems,\n {\n key: \"improve_writing\",\n title: t(\"ui.editor.ai.improve_writing\"),\n aliases: [\"improve\", \"rewrite\", \"polish\"],\n icon: <SparklesIcon size={18} />,\n size: \"small\" as const,\n onItemClick: () => {\n expandSelectionToBlocks();\n void ai.invokeAI({\n userPrompt: \"improve-writing\",\n useSelection: true,\n chatRequestOptions: { body: { type: \"improve-writing\" } },\n });\n },\n },\n {\n key: \"fix_spelling\",\n title: t(\"ui.editor.ai.fix_spelling\"),\n aliases: [\"spelling\", \"grammar\", \"typo\"],\n icon: <TypeIcon size={18} />,\n size: \"small\" as const,\n onItemClick: () => {\n expandSelectionToBlocks();\n void ai.invokeAI({\n userPrompt: \"fix-spelling\",\n useSelection: true,\n chatRequestOptions: { body: { type: \"fix-spelling\" } },\n });\n },\n },\n {\n key: \"translate\",\n title: t(\"ui.editor.ai.translate\"),\n aliases: [\"translate\", \"language\"],\n icon: <LanguagesIcon size={18} />,\n size: \"small\" as const,\n // Pre-fills the input with a placeholder. User appends/replaces\n // with the target language and submits via Enter. handleSubmit\n // reads pendingTypeRef and forwards `type: \"translate\"`. We\n // expand the selection now so the user sees the scope before\n // typing the language — handleSubmit doesn't re-expand.\n onItemClick: () => {\n expandSelectionToBlocks();\n pendingTypeRef.current = \"translate\";\n setPrompt(t(\"ui.editor.ai.translate_prefill\"));\n },\n },\n {\n key: \"simplify\",\n title: t(\"ui.editor.ai.simplify\"),\n aliases: [\"simplify\", \"easier\", \"plain\"],\n icon: <WandSparklesIcon size={18} />,\n size: \"small\" as const,\n onItemClick: () => {\n expandSelectionToBlocks();\n void ai.invokeAI({\n userPrompt: \"simplify\",\n useSelection: true,\n chatRequestOptions: { body: { type: \"simplify\" } },\n });\n },\n },\n ];\n }\n\n // No selection (the /ai slash menu path): just the whole-document items.\n // Free-form typing still works — once the user types, items hide and\n // Enter submits with no type (backend defaults to fill-template).\n return generateItems;\n }, [editor, status, prompt, ai, expandSelectionToBlocks, actions, t]);\n\n const handleSubmit = useCallback(\n async (userPrompt: string) => {\n if (!userPrompt.trim()) return;\n const pendingType = pendingTypeRef.current;\n pendingTypeRef.current = null;\n const body = pendingType ? { type: pendingType } : undefined;\n await ai.invokeAI({\n userPrompt,\n useSelection: editor.getSelection() !== undefined,\n ...(body ? { chatRequestOptions: { body } } : {}),\n });\n },\n [ai, editor],\n );\n\n const placeholder =\n status === \"thinking\"\n ? dict.ai_menu.status.thinking\n : status === \"ai-writing\"\n ? dict.ai_menu.status.editing\n : status === \"error\"\n ? dict.ai_menu.status.error\n : dict.ai_menu.input_placeholder;\n\n const disabled = status === \"thinking\" || status === \"ai-writing\";\n\n return (\n <PromptSuggestionMenu\n items={items}\n onManualPromptSubmit={handleSubmit}\n promptText={prompt}\n onPromptTextChange={setPrompt}\n placeholder={placeholder}\n disabled={disabled}\n icon={\n <div className=\"bn-combobox-icon\">\n <SparklesIcon size={16} />\n </div>\n }\n />\n );\n}\n\nexport default function BlockNoteEditor({\n id,\n type,\n initialContent,\n onChange,\n size,\n className,\n markdownContent,\n diffContent,\n placeholder,\n bordered,\n uploadKeyPrefix,\n inlineContentSpecs,\n renderOverlays,\n enableMentions,\n disableMentions,\n mentionSearchFn,\n mentionSearchParams,\n mentionResolveFn,\n suggestionMenuComponent,\n mentionNameResolver,\n onWarmMentions,\n aiConfig,\n stretch,\n aiEnabled = true,\n aiHandleRef,\n collaboration,\n disableUploads,\n editable,\n}: BlockNoteEditorProps): React.JSX.Element {\n const isEditable = resolveEditable({ editable, onChange });\n const t = useTranslations();\n const locale = useI18nLocale();\n const { resolvedTheme } = useTheme();\n // Optional on purpose: a host app may mount its own user context instead of\n // this package's, and the editor already degrades without a company — image\n // upload reports it (`uploadImage`) and the AI transport simply omits the\n // company header. Throwing here would take the whole page down instead.\n const company = useCurrentUserContextOptional<UserInterface>()?.company ?? null;\n\n const [acceptedChanges, setAcceptedChanges] = useState<Set<string>>(new Set());\n const [rejectedChanges, setRejectedChanges] = useState<Set<string>>(new Set());\n\n const editorRef = useRef<HTMLDivElement>(null);\n\n // Ensure side menu buttons don't trigger form submission\n useEffect(() => {\n if (!editorRef.current) return;\n const setButtonTypes = () => {\n editorRef.current?.querySelectorAll(\".bn-side-menu button\").forEach((btn) => {\n if (!btn.getAttribute(\"type\")) {\n btn.setAttribute(\"type\", \"button\");\n }\n });\n };\n const observer = new MutationObserver(setButtonTypes);\n observer.observe(editorRef.current, { childList: true, subtree: true });\n return () => observer.disconnect();\n }, []);\n\n const handleAcceptChange = useCallback((diffId: string) => {\n setAcceptedChanges((prev) => new Set([...prev, diffId]));\n setRejectedChanges((prev) => {\n const newSet = new Set(prev);\n newSet.delete(diffId);\n return newSet;\n });\n }, []);\n\n const handleRejectChange = useCallback((diffId: string) => {\n setRejectedChanges((prev) => new Set([...prev, diffId]));\n setAcceptedChanges((prev) => {\n const newSet = new Set(prev);\n newSet.delete(diffId);\n return newSet;\n });\n }, []);\n\n const DiffActionsInlineContent = useMemo(\n () =>\n createDiffActionsInlineContentSpec(handleAcceptChange, handleRejectChange, {\n accept: t(\"ui.editor.accept_change\"),\n reject: t(\"ui.editor.reject_change\"),\n }),\n [handleAcceptChange, handleRejectChange, t],\n );\n\n const mentionSpec = useMemo(\n () => createMentionInlineContentSpec(mentionResolveFn, disableMentions, mentionNameResolver),\n [disableMentions, mentionResolveFn, mentionNameResolver],\n );\n\n const schema = useMemo(\n () =>\n BlockNoteSchema.create({\n inlineContentSpecs: {\n ...defaultInlineContentSpecs,\n diffActions: DiffActionsInlineContent,\n mention: mentionSpec,\n ...inlineContentSpecs,\n },\n } as any),\n [DiffActionsInlineContent, mentionSpec, inlineContentSpecs],\n );\n\n const docRef = useRef<{ getDoc: () => any[] }>({ getDoc: () => [] });\n // Selection getter used by the AI transport to attach `selectionBlocks` to\n // the outgoing request metadata. Populated in a useEffect once the editor\n // instance exists; reads the current BlockNote selection on every send.\n const selectionRef = useRef<{ getSelectedBlocks: () => any[] }>({ getSelectedBlocks: () => [] });\n\n // Callers pass `aiConfig` as an inline object, the company arrives after the\n // first render and `aiEnabled` flips with credits, so none of them may key\n // the AI extension: every new extension means a new editor, which restarts\n // from `initialContent` and drops unsaved text. The transport and the outside\n // handle read them through this ref at send time.\n const aiRequestRef = useRef({ aiConfig, companyId: company?.id, aiEnabled });\n useEffect(() => {\n aiRequestRef.current = { aiConfig, companyId: company?.id, aiEnabled };\n });\n\n const hasAiConfig = !!aiConfig;\n const aiEndpoint = aiConfig?.endpoint;\n const aiExtension = useMemo(() => {\n if (!hasAiConfig || !aiEndpoint) return undefined;\n const base = getPublicApiUrl();\n const url = new URL(aiEndpoint, base.endsWith(\"/\") ? base : base + \"/\").toString();\n return AIExtension({\n transport: new DefaultChatTransport({\n api: url,\n credentials: \"include\",\n headers: async () => {\n const headers: Record<string, string> = { \"x-language\": locale };\n const token = await getClientToken();\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n const companyId = aiRequestRef.current.companyId;\n if (companyId) headers[\"x-companyid\"] = companyId;\n return headers;\n },\n prepareSendMessagesRequest: ({ messages, body }: any) => {\n // Last guard before the network: AI switched off while a request was\n // being started never reaches the endpoint. Throwing here puts the\n // AI menu in its error state instead of sending.\n if (!aiRequestRef.current.aiEnabled) throw new Error(\"AI is disabled\");\n const latestAiConfig = aiRequestRef.current.aiConfig;\n let lastUserIdx = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === \"user\") {\n lastUserIdx = i;\n break;\n }\n }\n const selectedBlocks = selectionRef.current.getSelectedBlocks();\n const augmented = messages.map((m: any, i: number) =>\n i === lastUserIdx\n ? {\n ...m,\n metadata: {\n ...(m.metadata ?? {}),\n entityType: latestAiConfig?.entityType,\n entityId: latestAiConfig?.entityId,\n blocks: docRef.current.getDoc(),\n selectionBlocks: selectedBlocks,\n },\n }\n : m,\n );\n return { body: { ...(body ?? {}), messages: augmented } };\n },\n }),\n });\n }, [hasAiConfig, aiEndpoint, locale]);\n\n const uploadImage = useCallback(\n async (file: File): Promise<string> => {\n if (!company && !uploadKeyPrefix) {\n errorToast({\n title: t(`common.errors.upload`),\n error: t(`common.errors.upload_description`),\n });\n throw new Error(t(`common.errors.upload`));\n }\n\n const fileType = file.type;\n const key = uploadKeyPrefix\n ? `${uploadKeyPrefix}/${id}/${file.name}`\n : `companies/${company!.id}/${type}/${id}/${file.name}`;\n\n const s3: S3Interface = await S3Service.getPreSignedUrl({\n key: key,\n contentType: fileType,\n isPublic: true,\n });\n\n await fetch(s3.url, {\n method: \"PUT\",\n headers: s3.headers,\n body: file,\n });\n\n const signedImage: S3Interface = await S3Service.getSignedUrl({\n key: key,\n isPublic: true,\n });\n\n return signedImage.url;\n },\n [company, id, t, type, uploadKeyPrefix],\n );\n\n // Utility: Remove trailing empty blocks for read-only display\n const removeTrailingEmptyBlocks = useCallback(\n (blocks: PartialBlock[]): PartialBlock[] => {\n if (!blocks || blocks.length === 0) return blocks;\n\n // Only remove trailing empty blocks in read-only mode\n if (onChange !== undefined) return blocks;\n\n const result = [...blocks];\n\n // Remove trailing empty paragraph blocks, but keep at least one block\n while (result.length > 1) {\n const lastBlock = result[result.length - 1];\n\n // Check if it's an empty paragraph\n const isEmptyParagraph =\n lastBlock.type === \"paragraph\" &&\n (!lastBlock.content ||\n lastBlock.content.length === 0 ||\n (Array.isArray(lastBlock.content) && lastBlock.content.every((c: any) => !c.text || c.text.trim() === \"\")));\n\n if (isEmptyParagraph) {\n result.pop();\n } else {\n break;\n }\n }\n\n return result;\n },\n [onChange],\n );\n\n const processedContent = useMemo(() => {\n if (diffContent && initialContent) {\n try {\n const diffResult = BlockNoteDiffUtil.diff(initialContent, diffContent);\n const renderedDiff = BlockNoteWordDiffRendererUtil.renderWordDiffs(\n diffResult.blocks,\n handleAcceptChange,\n handleRejectChange,\n acceptedChanges,\n rejectedChanges,\n );\n return removeTrailingEmptyBlocks(renderedDiff);\n } catch (_error) {\n return initialContent && Array.isArray(initialContent) && initialContent.length > 0\n ? removeTrailingEmptyBlocks(initialContent)\n : [];\n }\n }\n\n if (!initialContent) {\n return [];\n }\n\n if (!Array.isArray(initialContent)) {\n return [];\n }\n\n return initialContent.length > 0 ? removeTrailingEmptyBlocks(initialContent) : [];\n }, [\n initialContent,\n diffContent,\n handleAcceptChange,\n handleRejectChange,\n acceptedChanges,\n rejectedChanges,\n removeTrailingEmptyBlocks,\n ]);\n\n const validatedInitialContent = useMemo(() => {\n if (processedContent && Array.isArray(processedContent) && processedContent.length > 0) {\n const validatedContent = processedContent.filter((block) => {\n if (!block || typeof block !== \"object\") return false;\n if (!(block as any).type) return false;\n return true;\n });\n return validatedContent.length > 0 ? (validatedContent as PartialBlock[]) : undefined;\n }\n return undefined;\n }, [processedContent]);\n\n // BlockNote's own UI text (slash menu, toolbar, AI menu) in the active\n // language; English when BlockNote has no dictionary for it.\n const language = locale.split(\"-\")[0];\n const coreDictionary = (coreLocales as Record<string, typeof coreLocales.en>)[language] ?? coreLocales.en;\n const aiDictionary = (aiLocales as Record<string, typeof aiLocales.en>)[language] ?? aiLocales.en;\n\n const editor = useCreateBlockNote(\n useMemo(\n () =>\n buildBlockNoteEditorOptions({\n placeholder: placeholder || t(`common.blocknote.placeholder`),\n schema,\n initialContent: validatedInitialContent,\n uploadFile: uploadImage,\n disableUploads,\n aiExtension,\n coreDictionary,\n aiDictionary,\n collaboration,\n }),\n [\n placeholder,\n t,\n schema,\n validatedInitialContent,\n uploadImage,\n disableUploads,\n aiExtension,\n coreDictionary,\n aiDictionary,\n collaboration,\n ],\n ),\n // useCreateBlockNote memoises on THIS list, not on the options' identity\n // (@blocknote/react: useMemo(() => BlockNoteEditor.create(options), deps),\n // deps defaulting to []). Without it the editor keeps the extensions of its\n // first render: AI config arriving later (user loaded after mount)\n // rendered the AI UI over an editor with no AIExtension, and BlockNote's\n // useExtension threw \"Extension not found\". The other options keep their\n // first-render values, as before. `aiEnabled` is deliberately not here.\n // A new Yjs fragment (another document) needs a new editor bound to it.\n [aiExtension, collaboration?.fragment],\n );\n\n // Every AI surface keys off the editor actually rendered, never off the\n // props: those can disagree with the options that editor was created with.\n // `aiEnabled` then hides that UI without touching the editor.\n const hasAiExtension = !!editor.getExtension(AIExtension);\n const aiActive = hasAiExtension && aiEnabled;\n\n // AI switched off while its menu is open or a run is streaming: hiding the\n // AIMenuController alone would leave the editor read-only (openAIMenuAtBlock\n // sets isEditable = false) and the AI suggestions in the document. Stop the\n // request, then reject the suggestions, or just close an idle prompt menu.\n useEffect(() => {\n if (aiEnabled || !hasAiExtension) return;\n const ai = editor.getExtension(AIExtension);\n if (!ai) return;\n const dismiss = async () => {\n const menu = ai.store.state.aiMenuState;\n if (menu === \"closed\") return;\n if (menu.status === \"thinking\" || menu.status === \"ai-writing\") await ai.abort(\"AI disabled\");\n const after = ai.store.state.aiMenuState;\n if (after === \"closed\") return;\n if (after.status === \"user-input\") ai.closeAIMenu();\n else ai.rejectChanges();\n };\n dismiss().catch((error) => console.error(\"Closing the AI menu failed:\", error));\n }, [aiEnabled, hasAiExtension, editor]);\n\n // Publish the outside-the-editor AI trigger. `editor.getExtension(AIExtension)`\n // is BlockNote 0.54's documented lookup by extension factory (core's\n // `getExtension` resolves a function argument through `extensionFactories`),\n // so the instance is reachable without the `useExtension` hook that only\n // works inside the BlockNote context (NarrAIMenu). The menu MUST be opened\n // first: `invokeAI` routes every status update through `setAIResponseStatus`,\n // which is a no-op while `aiMenuState === \"closed\"` — without the open menu\n // the request runs but the accept / reject UI never appears.\n useEffect(() => {\n if (!aiHandleRef) return;\n if (!aiActive) {\n aiHandleRef.current = null;\n return;\n }\n aiHandleRef.current = {\n invokeAi: (type: BlockNoteAiGenerateAction) => {\n // A caller may hold the handle across the render that turns AI off.\n if (!aiRequestRef.current.aiEnabled) return;\n const ai = editor.getExtension(AIExtension);\n if (!ai) return;\n editor.focus();\n const blocks = editor.document;\n const anchor = blocks?.[blocks.length - 1];\n if (anchor?.id) ai.openAIMenuAtBlock(anchor.id);\n void ai.invokeAI({\n userPrompt: type,\n useSelection: false,\n chatRequestOptions: { body: { type } },\n });\n },\n };\n return () => {\n aiHandleRef.current = null;\n };\n }, [aiHandleRef, aiActive, editor]);\n\n // Tracks the hash of the document the editor itself just emitted via onChange.\n // The sync effect below uses it to skip replaceBlocks when the parent's\n // initialContent is just an echo of our own emission (form-controlled flow).\n const lastEmittedHashRef = useRef<string | null>(null);\n\n const handleChange = useCallback(async () => {\n if (!onChange) return;\n const newBlocks = editor.document;\n lastEmittedHashRef.current = JSON.stringify(newBlocks);\n const isEmpty = isDocumentEmpty(newBlocks);\n\n function hasUnresolvedDiffsRecursive(block: any): boolean {\n if (!block || typeof block !== \"object\") return false;\n let diffId = undefined;\n if (block.props && block.props.diffId) diffId = block.props.diffId;\n if (!diffId && block.attrs && block.attrs.diffId) diffId = block.attrs.diffId;\n if (diffId && !acceptedChanges.has(diffId) && !rejectedChanges.has(diffId)) {\n return true;\n }\n\n if (block.content) {\n const contentArr = Array.isArray(block.content) ? block.content : [block.content];\n for (const inline of contentArr) {\n if (inline && typeof inline === \"object\") {\n if (inline.type === \"diffActions\" && inline.props && inline.props.diffIds) {\n const ids =\n typeof inline.props.diffIds === \"string\" ? inline.props.diffIds.split(\",\") : inline.props.diffIds;\n for (const id of ids) {\n const trimmed = (id || \"\").toString().trim();\n if (trimmed && !acceptedChanges.has(trimmed) && !rejectedChanges.has(trimmed)) {\n return true;\n }\n }\n }\n if (inline.props && inline.props.diffId) {\n const diffIdInline = inline.props.diffId;\n if (diffIdInline && !acceptedChanges.has(diffIdInline) && !rejectedChanges.has(diffIdInline)) {\n return true;\n }\n }\n if (inline.children && Array.isArray(inline.children)) {\n for (const child of inline.children) {\n if (hasUnresolvedDiffsRecursive(child)) return true;\n }\n }\n }\n }\n }\n if (Array.isArray(block.children)) {\n for (const child of block.children) {\n if (hasUnresolvedDiffsRecursive(child)) return true;\n }\n }\n return false;\n }\n\n let hasUnresolvedDiff = false;\n if (Array.isArray(newBlocks)) {\n hasUnresolvedDiff = newBlocks.some((block: any) => hasUnresolvedDiffsRecursive(block));\n }\n\n onChange(newBlocks, isEmpty, hasUnresolvedDiff);\n }, [editor, onChange, id, acceptedChanges, rejectedChanges]);\n\n // Utility: deep equality for arrays of blocks\n const areBlocksEqual = (a: any[], b: any[]): boolean => {\n return JSON.stringify(a) === JSON.stringify(b);\n };\n\n // Only initialize from markdownContent once per value, and only if different\n const hasInitializedFromMarkdown = useRef<string | null>(null);\n useEffect(() => {\n const updateContent = async (markdown: string) => {\n const blocks = await editor.tryParseMarkdownToBlocks(markdown);\n if (!areBlocksEqual(blocks, editor.document)) {\n editor.replaceBlocks(editor.document, blocks);\n }\n };\n\n if (markdownContent && hasInitializedFromMarkdown.current !== markdownContent) {\n hasInitializedFromMarkdown.current = markdownContent;\n updateContent(markdownContent).then(() => handleChange());\n }\n }, [markdownContent, editor]);\n\n // Update editor content when diff content changes, but only if different\n // Prevent unnecessary replaceBlocks calls that reset scroll/cursor.\n const previousContentHashRef = useRef<string | null>(null);\n useEffect(() => {\n if (!processedContent || !editor) return;\n // A collaborative editor's content IS the shared Yjs document: it has no initialContent,\n // so processedContent is [] and replaceBlocks would wipe the document it just loaded,\n // and that wipe would be synced to every other writer and stored.\n if (collaboration) return;\n const hash = JSON.stringify(processedContent);\n if (previousContentHashRef.current === hash) return; // no changes\n // Skip replaceBlocks when the new initialContent is just an echo of what\n // the editor itself emitted. Without this, a form-controlled parent\n // (FormBlockNote) round-trips field.value back as initialContent on every\n // keystroke and triggers replaceBlocks, which resets the cursor and can\n // strip just-inserted inline content like mentions.\n if (lastEmittedHashRef.current === hash) {\n previousContentHashRef.current = hash;\n return;\n }\n const currentHash = JSON.stringify(editor.document);\n if (currentHash === hash) {\n previousContentHashRef.current = hash;\n return; // already in sync\n }\n editor.replaceBlocks(editor.document, processedContent as PartialBlock[]);\n previousContentHashRef.current = hash;\n }, [processedContent, editor, collaboration]);\n\n useEffect(() => {\n if (!onWarmMentions || !initialContent || !Array.isArray(initialContent) || initialContent.length === 0) return;\n onWarmMentions(initialContent);\n }, [onWarmMentions, initialContent]);\n\n useEffect(() => {\n docRef.current.getDoc = () => editor?.document ?? [];\n selectionRef.current.getSelectedBlocks = () => {\n const sel = editor?.getSelection?.();\n const blocks = (sel as any)?.blocks;\n return Array.isArray(blocks) ? blocks : [];\n };\n }, [editor]);\n\n // Handle audio received from whisper transcription\n const _handleAudioReceived = useCallback(\n (message: string) => {\n try {\n // Ensure the editor has focus\n const editorElement = editorRef.current?.querySelector('[contenteditable=\"true\"]') as HTMLElement;\n if (editorElement && document.activeElement !== editorElement) {\n editorElement.focus();\n }\n\n // Insert the transcribed text at the current cursor position\n editor.insertInlineContent(message);\n } catch (error) {\n console.error(\"Error inserting transcribed text:\", error);\n // Fallback: try to insert at the end of the document\n try {\n const blocks = editor.document;\n if (blocks.length > 0) {\n const lastBlock = blocks[blocks.length - 1];\n editor.setTextCursorPosition(lastBlock.id, \"end\");\n editor.insertInlineContent(message);\n }\n } catch (fallbackError) {\n console.error(\"Fallback insertion also failed:\", fallbackError);\n }\n }\n },\n [editor],\n );\n\n return (\n <div\n ref={editorRef}\n className={cn(\n bordered ? \"rounded-md border border-input bg-input/20 dark:bg-input/30\" : \"\",\n \"flex flex-col w-full\",\n // Pin BlockNote's font-size so it doesn't jump from 14→16px when the\n // xl-ai AIMenu mounts. The shadcn theme sets `.bn-default-styles {\n // font-size: 16px }` explicitly; outside AI mode the form's text-sm\n // wins via cascade, but ForkYDocExtension re-evaluates the style\n // context on AI activation and the explicit 16px takes over.\n // Phone: an editable field types at 16px, or iPhone Safari zooms the\n // page in on focus. The two ranges never overlap, so neither depends on\n // CSS source order to win.\n isEditable\n ? \"md:[&_.bn-default-styles]:!text-sm max-md:[&_.bn-default-styles]:!text-base\"\n : \"[&_.bn-default-styles]:!text-sm\",\n className,\n )}\n >\n <BlockNoteView\n editor={editor}\n onChange={handleChange}\n editable={isEditable}\n formattingToolbar={false}\n slashMenu={!aiActive}\n // Follow the app theme: hardcoding \"light\" leaves dark (light-theme)\n // text on the dark card, which is nearly unreadable in dark mode.\n theme={resolvedTheme === \"dark\" ? \"dark\" : \"light\"}\n // `className` is applied by BlockNote to both the main `.bn-container`\n // AND `editor.portalElement` (the floating-UI portal root). Gate `p-4`\n // on `.bn-container` so it doesn't add padding to the empty portal\n // element and produce a phantom scrollbar on the wrapper.\n className={cn(\n \"BlockNoteView flex-1\",\n isEditable && \"[&.bn-container]:p-4\",\n // In stretch mode the parent chain caps our height via flex; without\n // these two classes the `.bn-container` keeps `min-height: auto`\n // (its content's intrinsic height) and pushes the bordered wrapper\n // — and the surrounding EditorSheet form — to scroll instead of\n // scrolling internally.\n isEditable && stretch && \"[&.bn-container]:min-h-0 [&.bn-container]:overflow-y-auto\",\n // Phone — these reach the floating menus because BlockNote puts this\n // className on its portal root too. The formatting toolbar (~450px)\n // scrolls sideways instead of losing alignment and link off-screen;\n // the slash menu is capped to the screen and drops its keyboard\n // shortcut badges, which mean nothing on a touch screen.\n \"max-md:[&_.bn-formatting-toolbar]:max-w-[calc(100vw-1rem)] max-md:[&_.bn-formatting-toolbar]:overflow-x-auto max-md:[&_.bn-formatting-toolbar>*]:shrink-0\",\n \"max-md:[&_.bn-suggestion-menu]:max-w-[calc(100vw-1rem)] max-md:[&_.bn-suggestion-menu_[role=option]>[data-position=right]]:hidden\",\n size === \"sm\" && \"small\",\n )}\n >\n <BlockNoteEditorFormattingToolbar showAI={aiActive} />\n {enableMentions && mentionSearchFn && (\n <BlockNoteEditorMentionSuggestionMenu\n editor={editor}\n mentionSearchFn={mentionSearchFn}\n mentionSearchParams={mentionSearchParams}\n suggestionMenuComponent={suggestionMenuComponent}\n />\n )}\n {renderOverlays?.(editor)}\n {enableMentions && mentionResolveFn && (\n <BlockNoteEditorMentionHoverCard containerRef={editorRef} mentionResolveFn={mentionResolveFn} />\n )}\n {aiActive && (\n <SuggestionMenuController\n triggerCharacter=\"/\"\n getItems={async (query: string) =>\n filterSuggestionItems([...getDefaultReactSlashMenuItems(editor), ...getAISlashMenuItems(editor)], query)\n }\n />\n )}\n {aiActive && <AIMenuController aiMenu={() => <NarrAIMenu actions={aiConfig?.actions} />} />}\n </BlockNoteView>\n </div>\n );\n}\n","import { LayoutTemplateIcon, SparklesIcon } from \"lucide-react\";\nimport React from \"react\";\n\n/**\n * Whole-document AI actions an editor can offer in its AI menu when nothing\n * is selected. Each maps to a backend `type` discriminator; NO prompt text\n * lives here. Selection-edit actions (Improve, Fix spelling, …) are separate\n * and always available.\n */\nexport type BlockNoteAiGenerateAction = \"fill-template\" | \"draft-impression\";\n\nexport const DEFAULT_AI_GENERATE_ACTIONS: BlockNoteAiGenerateAction[] = [\"fill-template\"];\n\nexport type AiGenerateMenuItem = {\n key: string;\n title: string;\n aliases: string[];\n /** BlockNote's DefaultReactSuggestionItem types icon as an element, not ReactNode. */\n icon: React.JSX.Element;\n size: \"small\";\n onItemClick: () => void;\n};\n\n/** `titleKey` is a message key; the caller's translator resolves it to the shown title. */\ntype AiGenerateItemDefinition = Omit<AiGenerateMenuItem, \"onItemClick\" | \"title\"> & { titleKey: string };\n\nconst ITEMS: Record<BlockNoteAiGenerateAction, AiGenerateItemDefinition> = {\n \"fill-template\": {\n key: \"generate_from_template\",\n titleKey: \"ui.editor.ai.generate_from_template\",\n aliases: [\"generate\", \"template\", \"fill\"],\n icon: <LayoutTemplateIcon size={18} />,\n size: \"small\",\n },\n \"draft-impression\": {\n key: \"suggest_impression\",\n titleKey: \"ui.editor.ai.suggest_impression\",\n aliases: [\"suggest\", \"impression\"],\n icon: <SparklesIcon size={18} />,\n size: \"small\",\n },\n};\n\nexport function generateActionItems(\n actions: BlockNoteAiGenerateAction[] | undefined,\n invoke: (type: BlockNoteAiGenerateAction) => void,\n translate: (key: string) => string,\n): AiGenerateMenuItem[] {\n return (actions ?? DEFAULT_AI_GENERATE_ACTIONS).map((type) => {\n const { titleKey, ...item } = ITEMS[type];\n return { ...item, title: translate(titleKey), onItemClick: () => invoke(type) };\n });\n}\n","\"use client\";\n\nimport {\n BasicTextStyleButton,\n BlockTypeSelect,\n CreateLinkButton,\n FileCaptionButton,\n FileReplaceButton,\n FormattingToolbar,\n FormattingToolbarController,\n TextAlignButton,\n} from \"@blocknote/react\";\nimport { AIToolbarButton } from \"@blocknote/xl-ai\";\n\nexport function BlockNoteEditorFormattingToolbar({ showAI = false }: { showAI?: boolean }) {\n return (\n <FormattingToolbarController\n formattingToolbar={() => (\n <FormattingToolbar>\n <BlockTypeSelect key={\"blockTypeSelect\"} />\n\n <FileCaptionButton key={\"fileCaptionButton\"} />\n <FileReplaceButton key={\"replaceFileButton\"} />\n\n <BasicTextStyleButton basicTextStyle={\"bold\"} key={\"boldStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"italic\"} key={\"italicStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"underline\"} key={\"underlineStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"strike\"} key={\"strikeStyleButton\"} />\n\n <TextAlignButton textAlignment={\"left\"} key={\"textAlignLeftButton\"} />\n <TextAlignButton textAlignment={\"center\"} key={\"textAlignCenterButton\"} />\n <TextAlignButton textAlignment={\"right\"} key={\"textAlignRightButton\"} />\n\n <CreateLinkButton key={\"createLinkButton\"} />\n\n {showAI ? <AIToolbarButton key={\"aiToolbarButton\"} /> : null}\n </FormattingToolbar>\n )}\n />\n );\n}\n","import type { BlockNoteEditorOptions, PartialBlock } from \"@blocknote/core\";\nimport * as coreLocales from \"@blocknote/core/locales\";\nimport { type CollaborationOptions, withCollaboration } from \"@blocknote/core/yjs\";\n\n/**\n * Live co-editing options for the shared editor: `{ fragment, user: { name, color }, provider?, showCursorLabels? }`.\n * BlockNote 0.55 no longer reads `collaboration` from the editor options; it is applied through\n * `withCollaboration()` from `@blocknote/core/yjs`, which this module does.\n */\nexport type BlockNoteCollaboration = CollaborationOptions;\n\nexport type BuildBlockNoteEditorOptionsParams = {\n placeholder: string;\n schema: any;\n /** The validated initial content. Ignored when `collaboration` is set: the Yjs document is the content. */\n initialContent: PartialBlock[] | undefined;\n uploadFile: (file: File) => Promise<string>;\n /** true: no image uploads (no `uploadFile`, so BlockNote offers no upload). */\n disableUploads?: boolean;\n aiExtension: any;\n coreDictionary: typeof coreLocales.en;\n aiDictionary: any;\n collaboration?: BlockNoteCollaboration;\n};\n\n/**\n * The options handed to `useCreateBlockNote`. Without `collaboration` they are exactly the options the\n * editor has always built; with it, BlockNote's `withCollaboration` adds the collaboration extension,\n * disables the local history (Yjs owns undo) and replaces `initialContent` with its fixed placeholder.\n */\nexport function buildBlockNoteEditorOptions(\n params: BuildBlockNoteEditorOptionsParams,\n): Partial<BlockNoteEditorOptions<any, any, any>> {\n const { aiExtension, coreDictionary, aiDictionary, collaboration } = params;\n\n const options: Partial<BlockNoteEditorOptions<any, any, any>> = {\n placeholders: {\n emptyDocument: params.placeholder,\n },\n schema: params.schema,\n uploadFile: params.disableUploads ? undefined : params.uploadFile,\n extensions: aiExtension ? [aiExtension] : undefined,\n dictionary: aiExtension\n ? { ...coreDictionary, ai: aiDictionary }\n : coreDictionary === coreLocales.en\n ? undefined\n : coreDictionary,\n };\n\n if (collaboration) return withCollaboration({ ...options, collaboration });\n\n return { ...options, initialContent: params.initialContent };\n}\n\n/** An explicit `editable` wins; otherwise the editor is editable when it reports changes. */\nexport function resolveEditable(params: { editable?: boolean; onChange?: unknown }): boolean {\n return params.editable !== undefined ? params.editable : params.onChange !== undefined;\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteEditor-CBYKEXAQ.js","../src/components/editors/BlockNoteEditor.tsx","../src/components/editors/BlockNoteEditorAiActions.tsx","../src/components/editors/BlockNoteEditorFormattingToolbar.tsx","../src/components/editors/blockNoteEditorOptions.ts"],"names":["jsx","jsxs","SparklesIcon","type"],"mappings":"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","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteEditor-CBYKEXAQ.js","sourcesContent":[null,"\"use client\";\n\nimport { BlockNoteSchema, defaultInlineContentSpecs, filterSuggestionItems, PartialBlock } from \"@blocknote/core\";\nimport * as coreLocales from \"@blocknote/core/locales\";\nimport {\n createReactInlineContentSpec,\n DefaultReactSuggestionItem,\n getDefaultReactSlashMenuItems,\n SuggestionMenuController,\n SuggestionMenuProps,\n useBlockNoteEditor,\n useCreateBlockNote,\n useExtension,\n useExtensionState,\n} from \"@blocknote/react\";\nimport { BlockNoteView } from \"@blocknote/shadcn\";\nimport \"@blocknote/shadcn/style.css\";\nimport {\n AIExtension,\n AIMenuController,\n getAISlashMenuItems,\n getDefaultAIMenuItems,\n PromptSuggestionMenu,\n useAIDictionary,\n} from \"@blocknote/xl-ai\";\nimport * as aiLocales from \"@blocknote/xl-ai/locales\";\nimport \"@blocknote/xl-ai/style.css\";\nimport { DefaultChatTransport } from \"ai\";\nimport { CheckIcon, LanguagesIcon, SparklesIcon, TypeIcon, WandSparklesIcon, XIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport { useTheme } from \"next-themes\";\nimport React, { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { getPublicApiUrl } from \"../../client/config\";\nimport { getClientToken } from \"../../client/token\";\nimport { useCurrentUserContextOptional } from \"../../contexts\";\nimport { S3Interface } from \"../../features/s3/data\";\nimport { S3Service } from \"../../features/s3/data/s3.service\";\nimport { UserInterface } from \"../../features/user/data\";\nimport { useI18nLocale } from \"../../i18n/config\";\nimport { Button } from \"../../shadcnui\";\nimport { BlockNoteDiffUtil, BlockNoteWordDiffRendererUtil, cn } from \"../../utils\";\nimport { errorToast } from \"../errors\";\nimport { BlockNoteAiGenerateAction, generateActionItems } from \"./BlockNoteEditorAiActions\";\nimport { BlockNoteEditorFormattingToolbar } from \"./BlockNoteEditorFormattingToolbar\";\nimport { type BlockNoteCollaboration, buildBlockNoteEditorOptions, resolveEditable } from \"./blockNoteEditorOptions\";\nimport { BlockNoteEditorMentionHoverCard } from \"./BlockNoteEditorMentionHoverCard\";\nimport {\n createMentionInlineContentSpec,\n type MentionNameResolver,\n type MentionResolveFn,\n} from \"./BlockNoteEditorMentionInlineContent\";\nimport { BlockNoteEditorMentionSuggestionMenu } from \"./BlockNoteEditorSuggestionMenuController\";\n\n/**\n * Lets app code trigger a whole-document AI action from OUTSIDE the editor\n * (a visible button next to the field label, for instance) without owning the\n * BlockNote instance. Delivered through a plain prop, not a React `ref`:\n * `BlockNoteEditorContainer` is a `next/dynamic` import wrapped in\n * `React.memo`, and refs do not pass through that reliably.\n */\nexport type BlockNoteAiHandle = { invokeAi: (type: BlockNoteAiGenerateAction) => void };\n\nexport type BlockNoteAiConfig = {\n endpoint: string;\n entityType: string;\n entityId?: string;\n /** Whole-document actions shown when nothing is selected. Default: [\"fill-template\"]. */\n actions?: BlockNoteAiGenerateAction[];\n};\n\nexport type BlockNoteEditorProps = {\n id: string;\n type: string;\n initialContent?: PartialBlock[];\n onChange?: (content: any, isEmpty: boolean, hasUnresolvedDiff: boolean) => void;\n size?: \"sm\" | \"md\";\n className?: string;\n markdownContent?: string;\n diffContent?: PartialBlock[];\n placeholder?: string;\n bordered?: boolean;\n /**\n * Storage folder for pasted images, used instead of the current company's\n * `companies/<companyId>/<type>` folder: the image key becomes\n * `<uploadKeyPrefix>/<id>/<file name>`. For platform content edited by users\n * who belong to no company (the blog). Omitted: the company folder, as before.\n */\n uploadKeyPrefix?: string;\n inlineContentSpecs?: Record<string, any>;\n renderOverlays?: (editor: any) => React.ReactNode;\n enableMentions?: boolean;\n disableMentions?: boolean;\n mentionSearchFn?: (\n query: string,\n params?: Record<string, string>,\n ) => Promise<import(\"./BlockNoteEditorSuggestionMenuController\").MentionItem[]>;\n mentionSearchParams?: Record<string, string>;\n mentionResolveFn?: MentionResolveFn;\n suggestionMenuComponent?: React.FC<SuggestionMenuProps<DefaultReactSuggestionItem>>;\n mentionNameResolver?: MentionNameResolver;\n onWarmMentions?: (blocks: PartialBlock[]) => void;\n aiConfig?: BlockNoteAiConfig;\n // When the editor is inside a bounded flex column (parent gives it a real\n // height via flex-1+min-h-0), set this so `.bn-container` shrinks to that\n // height and scrolls internally. Without it the editor grows to fit its\n // content and pushes the surrounding form to scroll instead.\n stretch?: boolean;\n // Gates AI on top of `aiConfig` (credit availability, etc.). Defaults to\n // true so existing callers that only pass `aiConfig` are unaffected. When\n // false, the AI toolbar button and AI slash items are hidden, the editor\n // falls back to the plain slash menu, an open AI menu or run is closed, and\n // no AI request is sent. The AI extension itself stays: toggling this never\n // rebuilds the editor, so unsaved text survives credits running out.\n aiEnabled?: boolean;\n // Filled with a {@link BlockNoteAiHandle} while the editor is mounted and AI\n // is on, so a control outside the editor can run a whole-document AI action.\n // Cleared on unmount and whenever AI is unavailable.\n aiHandleRef?: React.MutableRefObject<BlockNoteAiHandle | null>;\n /**\n * Live co-editing (Yjs). When set, the Yjs document is the content: used\n * instead of `initialContent`/`onChange`. Changing `collaboration.fragment`\n * rebuilds the editor.\n */\n collaboration?: BlockNoteCollaboration;\n /** true: image uploads are off (no paste/drop upload, no upload tab). */\n disableUploads?: boolean;\n /**\n * Overrides the default `onChange !== undefined` editability, e.g. for a\n * collaborative editor, which has no `onChange`.\n */\n editable?: boolean;\n};\n\nfunction isBlockEmpty(block: any): boolean {\n if (!block || typeof block !== \"object\") return true;\n if (block.type !== \"paragraph\") return false;\n if (Array.isArray(block.children) && block.children.length > 0 && !isDocumentEmpty(block.children)) {\n return false;\n }\n if (Array.isArray(block.content)) {\n for (const inline of block.content) {\n if (typeof inline === \"string\") {\n if (inline.trim()) return false;\n } else if (inline && typeof inline === \"object\") {\n if (inline.type !== \"text\") return false;\n if (typeof inline.text === \"string\" && inline.text.trim()) return false;\n }\n }\n } else if (typeof block.content === \"string\" && block.content.trim()) {\n return false;\n }\n return true;\n}\n\nfunction isDocumentEmpty(blocks: any[]): boolean {\n if (!Array.isArray(blocks) || blocks.length === 0) return true;\n return blocks.every(isBlockEmpty);\n}\n\nconst createDiffActionsInlineContentSpec = (\n handleAcceptChange: (diffId: string) => void,\n handleRejectChange: (diffId: string) => void,\n labels: { accept: string; reject: string },\n) => {\n return createReactInlineContentSpec(\n {\n type: \"diffActions\",\n propSchema: {\n diffIds: {\n default: \"\",\n },\n },\n content: \"none\",\n },\n {\n render: (props) => {\n const diffIds = props.inlineContent.props.diffIds;\n\n return (\n <span className=\"diff-actions-container mx-2 inline-flex items-center gap-1 align-middle\">\n <Button\n title={labels.accept}\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n diffIds.split(\",\").forEach((id: string) => handleAcceptChange(id.trim()));\n }}\n >\n <CheckIcon className=\"text-success h-3 w-3\" />\n </Button>\n <Button\n title={labels.reject}\n className=\"mx-2 p-0\"\n onClick={(e) => {\n e.preventDefault();\n e.stopPropagation();\n diffIds.split(\",\").forEach((id: string) => handleRejectChange(id.trim()));\n }}\n >\n <XIcon className=\"text-destructive h-3 w-3\" />\n </Button>\n </span>\n );\n },\n },\n );\n};\n\n/**\n * Custom AI menu wrapper. Surfaces our backend-driven action items\n * (Improve Writing, Fix Spelling) above the free-form prompt input.\n *\n * Why a custom wrapper instead of `<AIMenu items={…}>` with the BlockNote\n * defaults: BlockNote's `PromptSuggestionMenu` hijacks Enter to pick the\n * highlighted item whenever `items.length > 0`. We gate items on\n * - status === \"user-input\"\n * - editor has a selection\n * - prompt input is empty\n * so the moment the user types, items disappear and Enter falls back to\n * free-form submission. Outside `user-input` (review / error states) we\n * keep BlockNote's default review buttons (accept/revert/retry/cancel).\n *\n * Each custom item passes a `type` discriminator on `chatRequestOptions.body`.\n * That field flows through `chat.sendMessage(msg, opts)` to the transport's\n * `prepareSendMessagesRequest({messages, body})` (see types.ts in\n * `@blocknote/xl-ai` — `ChatRequestOptions = Parameters<Chat[\"sendMessage\"]>[1]`).\n * The backend dispatcher reads `body.type` and routes to a per-type handler\n * with its own canonical prompt. NO prompt text lives in this file.\n */\nfunction NarrAIMenu({ actions }: { actions?: BlockNoteAiGenerateAction[] }) {\n const t = useTranslations();\n const editor = useBlockNoteEditor();\n const ai = useExtension(AIExtension);\n const dict = useAIDictionary();\n const status = useExtensionState(AIExtension, {\n selector: (s) => (s.aiMenuState !== \"closed\" ? s.aiMenuState.status : \"closed\"),\n });\n const [prompt, setPrompt] = useState(\"\");\n // Set by items that pre-fill the input (Translate) so handleSubmit knows\n // which `type` to attach when the user submits the captured text. Cleared\n // after submit or on status change.\n const pendingTypeRef = useRef<string | null>(null);\n\n useEffect(() => {\n if (status === \"ai-writing\" || status === \"user-reviewing\" || status === \"error\" || status === \"closed\") {\n setPrompt(\"\");\n pendingTypeRef.current = null;\n }\n }, [status]);\n\n // Selection-edit ops operate per-block. If the user's selection only\n // covers part of a block (cursor mid-paragraph dragged to mid-next), the\n // rewrite would silently replace the WHOLE containing blocks — beyond\n // what the user highlighted. Expand the selection to whole-block bounds\n // BEFORE invokeAI so the user sees exactly what will be rewritten.\n const expandSelectionToBlocks = useCallback(() => {\n const sel = editor.getSelection?.();\n const blocks = (sel as any)?.blocks;\n if (!Array.isArray(blocks) || blocks.length === 0) return;\n const first = blocks[0];\n const last = blocks[blocks.length - 1];\n try {\n (editor as any).setSelection?.(first, last);\n } catch {\n // If BlockNote's setSelection signature changes, fail silently — the\n // backend still operates per-block, so we just lose the visual hint.\n }\n }, [editor]);\n\n const items = useMemo(() => {\n // Outside user-input (reviewing / error), use BlockNote's default\n // review buttons (accept/revert/retry/cancel). Need to wrap onItemClick\n // because the default items expect a setPrompt argument.\n if (status !== \"user-input\") {\n return getDefaultAIMenuItems(editor, status).map((item) => ({\n ...item,\n onItemClick: () => item.onItemClick(setPrompt),\n }));\n }\n const hasSelection = editor.getSelection() !== undefined;\n const hasTyped = prompt.trim().length > 0;\n // Once the user starts typing, hide all items so Enter submits the\n // free-form (or pending-type) prompt instead of being hijacked.\n if (hasTyped) return [];\n\n const invokeGenerate = (type: BlockNoteAiGenerateAction) => {\n void ai.invokeAI({\n userPrompt: type,\n useSelection: false,\n chatRequestOptions: { body: { type } },\n });\n };\n // Whole-document items are shown in BOTH contexts (with and without\n // selection) because they operate on the whole document — they ignore any\n // active selection. Listed first so the first one is the\n // default-highlighted item.\n const generateItems = generateActionItems(actions, invokeGenerate, t);\n\n if (hasSelection) {\n // Selection-edit items + the always-available whole-document items.\n // Each selection item invokes ai.invokeAI with chatRequestOptions\n // carrying the `type` body field. The userPrompt is a short tag — the\n // backend ignores it and uses the canonical prompt for the type instead.\n return [\n ...generateItems,\n {\n key: \"improve_writing\",\n title: t(\"ui.editor.ai.improve_writing\"),\n aliases: [\"improve\", \"rewrite\", \"polish\"],\n icon: <SparklesIcon size={18} />,\n size: \"small\" as const,\n onItemClick: () => {\n expandSelectionToBlocks();\n void ai.invokeAI({\n userPrompt: \"improve-writing\",\n useSelection: true,\n chatRequestOptions: { body: { type: \"improve-writing\" } },\n });\n },\n },\n {\n key: \"fix_spelling\",\n title: t(\"ui.editor.ai.fix_spelling\"),\n aliases: [\"spelling\", \"grammar\", \"typo\"],\n icon: <TypeIcon size={18} />,\n size: \"small\" as const,\n onItemClick: () => {\n expandSelectionToBlocks();\n void ai.invokeAI({\n userPrompt: \"fix-spelling\",\n useSelection: true,\n chatRequestOptions: { body: { type: \"fix-spelling\" } },\n });\n },\n },\n {\n key: \"translate\",\n title: t(\"ui.editor.ai.translate\"),\n aliases: [\"translate\", \"language\"],\n icon: <LanguagesIcon size={18} />,\n size: \"small\" as const,\n // Pre-fills the input with a placeholder. User appends/replaces\n // with the target language and submits via Enter. handleSubmit\n // reads pendingTypeRef and forwards `type: \"translate\"`. We\n // expand the selection now so the user sees the scope before\n // typing the language — handleSubmit doesn't re-expand.\n onItemClick: () => {\n expandSelectionToBlocks();\n pendingTypeRef.current = \"translate\";\n setPrompt(t(\"ui.editor.ai.translate_prefill\"));\n },\n },\n {\n key: \"simplify\",\n title: t(\"ui.editor.ai.simplify\"),\n aliases: [\"simplify\", \"easier\", \"plain\"],\n icon: <WandSparklesIcon size={18} />,\n size: \"small\" as const,\n onItemClick: () => {\n expandSelectionToBlocks();\n void ai.invokeAI({\n userPrompt: \"simplify\",\n useSelection: true,\n chatRequestOptions: { body: { type: \"simplify\" } },\n });\n },\n },\n ];\n }\n\n // No selection (the /ai slash menu path): just the whole-document items.\n // Free-form typing still works — once the user types, items hide and\n // Enter submits with no type (backend defaults to fill-template).\n return generateItems;\n }, [editor, status, prompt, ai, expandSelectionToBlocks, actions, t]);\n\n const handleSubmit = useCallback(\n async (userPrompt: string) => {\n if (!userPrompt.trim()) return;\n const pendingType = pendingTypeRef.current;\n pendingTypeRef.current = null;\n const body = pendingType ? { type: pendingType } : undefined;\n await ai.invokeAI({\n userPrompt,\n useSelection: editor.getSelection() !== undefined,\n ...(body ? { chatRequestOptions: { body } } : {}),\n });\n },\n [ai, editor],\n );\n\n const placeholder =\n status === \"thinking\"\n ? dict.ai_menu.status.thinking\n : status === \"ai-writing\"\n ? dict.ai_menu.status.editing\n : status === \"error\"\n ? dict.ai_menu.status.error\n : dict.ai_menu.input_placeholder;\n\n const disabled = status === \"thinking\" || status === \"ai-writing\";\n\n return (\n <PromptSuggestionMenu\n items={items}\n onManualPromptSubmit={handleSubmit}\n promptText={prompt}\n onPromptTextChange={setPrompt}\n placeholder={placeholder}\n disabled={disabled}\n icon={\n <div className=\"bn-combobox-icon\">\n <SparklesIcon size={16} />\n </div>\n }\n />\n );\n}\n\nexport default function BlockNoteEditor({\n id,\n type,\n initialContent,\n onChange,\n size,\n className,\n markdownContent,\n diffContent,\n placeholder,\n bordered,\n uploadKeyPrefix,\n inlineContentSpecs,\n renderOverlays,\n enableMentions,\n disableMentions,\n mentionSearchFn,\n mentionSearchParams,\n mentionResolveFn,\n suggestionMenuComponent,\n mentionNameResolver,\n onWarmMentions,\n aiConfig,\n stretch,\n aiEnabled = true,\n aiHandleRef,\n collaboration,\n disableUploads,\n editable,\n}: BlockNoteEditorProps): React.JSX.Element {\n const isEditable = resolveEditable({ editable, onChange });\n const t = useTranslations();\n const locale = useI18nLocale();\n const { resolvedTheme } = useTheme();\n // Optional on purpose: a host app may mount its own user context instead of\n // this package's, and the editor already degrades without a company — image\n // upload reports it (`uploadImage`) and the AI transport simply omits the\n // company header. Throwing here would take the whole page down instead.\n const company = useCurrentUserContextOptional<UserInterface>()?.company ?? null;\n\n const [acceptedChanges, setAcceptedChanges] = useState<Set<string>>(new Set());\n const [rejectedChanges, setRejectedChanges] = useState<Set<string>>(new Set());\n\n const editorRef = useRef<HTMLDivElement>(null);\n\n // Ensure side menu buttons don't trigger form submission\n useEffect(() => {\n if (!editorRef.current) return;\n const setButtonTypes = () => {\n editorRef.current?.querySelectorAll(\".bn-side-menu button\").forEach((btn) => {\n if (!btn.getAttribute(\"type\")) {\n btn.setAttribute(\"type\", \"button\");\n }\n });\n };\n const observer = new MutationObserver(setButtonTypes);\n observer.observe(editorRef.current, { childList: true, subtree: true });\n return () => observer.disconnect();\n }, []);\n\n const handleAcceptChange = useCallback((diffId: string) => {\n setAcceptedChanges((prev) => new Set([...prev, diffId]));\n setRejectedChanges((prev) => {\n const newSet = new Set(prev);\n newSet.delete(diffId);\n return newSet;\n });\n }, []);\n\n const handleRejectChange = useCallback((diffId: string) => {\n setRejectedChanges((prev) => new Set([...prev, diffId]));\n setAcceptedChanges((prev) => {\n const newSet = new Set(prev);\n newSet.delete(diffId);\n return newSet;\n });\n }, []);\n\n const DiffActionsInlineContent = useMemo(\n () =>\n createDiffActionsInlineContentSpec(handleAcceptChange, handleRejectChange, {\n accept: t(\"ui.editor.accept_change\"),\n reject: t(\"ui.editor.reject_change\"),\n }),\n [handleAcceptChange, handleRejectChange, t],\n );\n\n const mentionSpec = useMemo(\n () => createMentionInlineContentSpec(mentionResolveFn, disableMentions, mentionNameResolver),\n [disableMentions, mentionResolveFn, mentionNameResolver],\n );\n\n const schema = useMemo(\n () =>\n BlockNoteSchema.create({\n inlineContentSpecs: {\n ...defaultInlineContentSpecs,\n diffActions: DiffActionsInlineContent,\n mention: mentionSpec,\n ...inlineContentSpecs,\n },\n } as any),\n [DiffActionsInlineContent, mentionSpec, inlineContentSpecs],\n );\n\n const docRef = useRef<{ getDoc: () => any[] }>({ getDoc: () => [] });\n // Selection getter used by the AI transport to attach `selectionBlocks` to\n // the outgoing request metadata. Populated in a useEffect once the editor\n // instance exists; reads the current BlockNote selection on every send.\n const selectionRef = useRef<{ getSelectedBlocks: () => any[] }>({ getSelectedBlocks: () => [] });\n\n // Callers pass `aiConfig` as an inline object, the company arrives after the\n // first render and `aiEnabled` flips with credits, so none of them may key\n // the AI extension: every new extension means a new editor, which restarts\n // from `initialContent` and drops unsaved text. The transport and the outside\n // handle read them through this ref at send time.\n const aiRequestRef = useRef({ aiConfig, companyId: company?.id, aiEnabled });\n useEffect(() => {\n aiRequestRef.current = { aiConfig, companyId: company?.id, aiEnabled };\n });\n\n const hasAiConfig = !!aiConfig;\n const aiEndpoint = aiConfig?.endpoint;\n const aiExtension = useMemo(() => {\n if (!hasAiConfig || !aiEndpoint) return undefined;\n const base = getPublicApiUrl();\n const url = new URL(aiEndpoint, base.endsWith(\"/\") ? base : base + \"/\").toString();\n return AIExtension({\n transport: new DefaultChatTransport({\n api: url,\n credentials: \"include\",\n headers: async () => {\n const headers: Record<string, string> = { \"x-language\": locale };\n const token = await getClientToken();\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n const companyId = aiRequestRef.current.companyId;\n if (companyId) headers[\"x-companyid\"] = companyId;\n return headers;\n },\n prepareSendMessagesRequest: ({ messages, body }: any) => {\n // Last guard before the network: AI switched off while a request was\n // being started never reaches the endpoint. Throwing here puts the\n // AI menu in its error state instead of sending.\n if (!aiRequestRef.current.aiEnabled) throw new Error(\"AI is disabled\");\n const latestAiConfig = aiRequestRef.current.aiConfig;\n let lastUserIdx = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i]?.role === \"user\") {\n lastUserIdx = i;\n break;\n }\n }\n const selectedBlocks = selectionRef.current.getSelectedBlocks();\n const augmented = messages.map((m: any, i: number) =>\n i === lastUserIdx\n ? {\n ...m,\n metadata: {\n ...(m.metadata ?? {}),\n entityType: latestAiConfig?.entityType,\n entityId: latestAiConfig?.entityId,\n blocks: docRef.current.getDoc(),\n selectionBlocks: selectedBlocks,\n },\n }\n : m,\n );\n return { body: { ...(body ?? {}), messages: augmented } };\n },\n }),\n });\n }, [hasAiConfig, aiEndpoint, locale]);\n\n const uploadImage = useCallback(\n async (file: File): Promise<string> => {\n if (!company && !uploadKeyPrefix) {\n errorToast({\n title: t(`common.errors.upload`),\n error: t(`common.errors.upload_description`),\n });\n throw new Error(t(`common.errors.upload`));\n }\n\n const fileType = file.type;\n const key = uploadKeyPrefix\n ? `${uploadKeyPrefix}/${id}/${file.name}`\n : `companies/${company!.id}/${type}/${id}/${file.name}`;\n\n const s3: S3Interface = await S3Service.getPreSignedUrl({\n key: key,\n contentType: fileType,\n isPublic: true,\n });\n\n await fetch(s3.url, {\n method: \"PUT\",\n headers: s3.headers,\n body: file,\n });\n\n const signedImage: S3Interface = await S3Service.getSignedUrl({\n key: key,\n isPublic: true,\n });\n\n return signedImage.url;\n },\n [company, id, t, type, uploadKeyPrefix],\n );\n\n // Utility: Remove trailing empty blocks for read-only display\n const removeTrailingEmptyBlocks = useCallback(\n (blocks: PartialBlock[]): PartialBlock[] => {\n if (!blocks || blocks.length === 0) return blocks;\n\n // Only remove trailing empty blocks in read-only mode\n if (onChange !== undefined) return blocks;\n\n const result = [...blocks];\n\n // Remove trailing empty paragraph blocks, but keep at least one block\n while (result.length > 1) {\n const lastBlock = result[result.length - 1];\n\n // Check if it's an empty paragraph\n const isEmptyParagraph =\n lastBlock.type === \"paragraph\" &&\n (!lastBlock.content ||\n lastBlock.content.length === 0 ||\n (Array.isArray(lastBlock.content) && lastBlock.content.every((c: any) => !c.text || c.text.trim() === \"\")));\n\n if (isEmptyParagraph) {\n result.pop();\n } else {\n break;\n }\n }\n\n return result;\n },\n [onChange],\n );\n\n const processedContent = useMemo(() => {\n if (diffContent && initialContent) {\n try {\n const diffResult = BlockNoteDiffUtil.diff(initialContent, diffContent);\n const renderedDiff = BlockNoteWordDiffRendererUtil.renderWordDiffs(\n diffResult.blocks,\n handleAcceptChange,\n handleRejectChange,\n acceptedChanges,\n rejectedChanges,\n );\n return removeTrailingEmptyBlocks(renderedDiff);\n } catch (_error) {\n return initialContent && Array.isArray(initialContent) && initialContent.length > 0\n ? removeTrailingEmptyBlocks(initialContent)\n : [];\n }\n }\n\n if (!initialContent) {\n return [];\n }\n\n if (!Array.isArray(initialContent)) {\n return [];\n }\n\n return initialContent.length > 0 ? removeTrailingEmptyBlocks(initialContent) : [];\n }, [\n initialContent,\n diffContent,\n handleAcceptChange,\n handleRejectChange,\n acceptedChanges,\n rejectedChanges,\n removeTrailingEmptyBlocks,\n ]);\n\n const validatedInitialContent = useMemo(() => {\n if (processedContent && Array.isArray(processedContent) && processedContent.length > 0) {\n const validatedContent = processedContent.filter((block) => {\n if (!block || typeof block !== \"object\") return false;\n if (!(block as any).type) return false;\n return true;\n });\n return validatedContent.length > 0 ? (validatedContent as PartialBlock[]) : undefined;\n }\n return undefined;\n }, [processedContent]);\n\n // BlockNote's own UI text (slash menu, toolbar, AI menu) in the active\n // language; English when BlockNote has no dictionary for it.\n const language = locale.split(\"-\")[0];\n const coreDictionary = (coreLocales as Record<string, typeof coreLocales.en>)[language] ?? coreLocales.en;\n const aiDictionary = (aiLocales as Record<string, typeof aiLocales.en>)[language] ?? aiLocales.en;\n\n const editor = useCreateBlockNote(\n useMemo(\n () =>\n buildBlockNoteEditorOptions({\n placeholder: placeholder || t(`common.blocknote.placeholder`),\n schema,\n initialContent: validatedInitialContent,\n uploadFile: uploadImage,\n disableUploads,\n aiExtension,\n coreDictionary,\n aiDictionary,\n collaboration,\n }),\n [\n placeholder,\n t,\n schema,\n validatedInitialContent,\n uploadImage,\n disableUploads,\n aiExtension,\n coreDictionary,\n aiDictionary,\n collaboration,\n ],\n ),\n // useCreateBlockNote memoises on THIS list, not on the options' identity\n // (@blocknote/react: useMemo(() => BlockNoteEditor.create(options), deps),\n // deps defaulting to []). Without it the editor keeps the extensions of its\n // first render: AI config arriving later (user loaded after mount)\n // rendered the AI UI over an editor with no AIExtension, and BlockNote's\n // useExtension threw \"Extension not found\". The other options keep their\n // first-render values, as before. `aiEnabled` is deliberately not here.\n // A new Yjs fragment (another document) needs a new editor bound to it.\n [aiExtension, collaboration?.fragment],\n );\n\n // Every AI surface keys off the editor actually rendered, never off the\n // props: those can disagree with the options that editor was created with.\n // `aiEnabled` then hides that UI without touching the editor.\n const hasAiExtension = !!editor.getExtension(AIExtension);\n const aiActive = hasAiExtension && aiEnabled;\n\n // AI switched off while its menu is open or a run is streaming: hiding the\n // AIMenuController alone would leave the editor read-only (openAIMenuAtBlock\n // sets isEditable = false) and the AI suggestions in the document. Stop the\n // request, then reject the suggestions, or just close an idle prompt menu.\n useEffect(() => {\n if (aiEnabled || !hasAiExtension) return;\n const ai = editor.getExtension(AIExtension);\n if (!ai) return;\n const dismiss = async () => {\n const menu = ai.store.state.aiMenuState;\n if (menu === \"closed\") return;\n if (menu.status === \"thinking\" || menu.status === \"ai-writing\") await ai.abort(\"AI disabled\");\n const after = ai.store.state.aiMenuState;\n if (after === \"closed\") return;\n if (after.status === \"user-input\") ai.closeAIMenu();\n else ai.rejectChanges();\n };\n dismiss().catch((error) => console.error(\"Closing the AI menu failed:\", error));\n }, [aiEnabled, hasAiExtension, editor]);\n\n // Publish the outside-the-editor AI trigger. `editor.getExtension(AIExtension)`\n // is BlockNote 0.54's documented lookup by extension factory (core's\n // `getExtension` resolves a function argument through `extensionFactories`),\n // so the instance is reachable without the `useExtension` hook that only\n // works inside the BlockNote context (NarrAIMenu). The menu MUST be opened\n // first: `invokeAI` routes every status update through `setAIResponseStatus`,\n // which is a no-op while `aiMenuState === \"closed\"` — without the open menu\n // the request runs but the accept / reject UI never appears.\n useEffect(() => {\n if (!aiHandleRef) return;\n if (!aiActive) {\n aiHandleRef.current = null;\n return;\n }\n aiHandleRef.current = {\n invokeAi: (type: BlockNoteAiGenerateAction) => {\n // A caller may hold the handle across the render that turns AI off.\n if (!aiRequestRef.current.aiEnabled) return;\n const ai = editor.getExtension(AIExtension);\n if (!ai) return;\n editor.focus();\n const blocks = editor.document;\n const anchor = blocks?.[blocks.length - 1];\n if (anchor?.id) ai.openAIMenuAtBlock(anchor.id);\n void ai.invokeAI({\n userPrompt: type,\n useSelection: false,\n chatRequestOptions: { body: { type } },\n });\n },\n };\n return () => {\n aiHandleRef.current = null;\n };\n }, [aiHandleRef, aiActive, editor]);\n\n // Tracks the hash of the document the editor itself just emitted via onChange.\n // The sync effect below uses it to skip replaceBlocks when the parent's\n // initialContent is just an echo of our own emission (form-controlled flow).\n const lastEmittedHashRef = useRef<string | null>(null);\n\n const handleChange = useCallback(async () => {\n if (!onChange) return;\n const newBlocks = editor.document;\n lastEmittedHashRef.current = JSON.stringify(newBlocks);\n const isEmpty = isDocumentEmpty(newBlocks);\n\n function hasUnresolvedDiffsRecursive(block: any): boolean {\n if (!block || typeof block !== \"object\") return false;\n let diffId = undefined;\n if (block.props && block.props.diffId) diffId = block.props.diffId;\n if (!diffId && block.attrs && block.attrs.diffId) diffId = block.attrs.diffId;\n if (diffId && !acceptedChanges.has(diffId) && !rejectedChanges.has(diffId)) {\n return true;\n }\n\n if (block.content) {\n const contentArr = Array.isArray(block.content) ? block.content : [block.content];\n for (const inline of contentArr) {\n if (inline && typeof inline === \"object\") {\n if (inline.type === \"diffActions\" && inline.props && inline.props.diffIds) {\n const ids =\n typeof inline.props.diffIds === \"string\" ? inline.props.diffIds.split(\",\") : inline.props.diffIds;\n for (const id of ids) {\n const trimmed = (id || \"\").toString().trim();\n if (trimmed && !acceptedChanges.has(trimmed) && !rejectedChanges.has(trimmed)) {\n return true;\n }\n }\n }\n if (inline.props && inline.props.diffId) {\n const diffIdInline = inline.props.diffId;\n if (diffIdInline && !acceptedChanges.has(diffIdInline) && !rejectedChanges.has(diffIdInline)) {\n return true;\n }\n }\n if (inline.children && Array.isArray(inline.children)) {\n for (const child of inline.children) {\n if (hasUnresolvedDiffsRecursive(child)) return true;\n }\n }\n }\n }\n }\n if (Array.isArray(block.children)) {\n for (const child of block.children) {\n if (hasUnresolvedDiffsRecursive(child)) return true;\n }\n }\n return false;\n }\n\n let hasUnresolvedDiff = false;\n if (Array.isArray(newBlocks)) {\n hasUnresolvedDiff = newBlocks.some((block: any) => hasUnresolvedDiffsRecursive(block));\n }\n\n onChange(newBlocks, isEmpty, hasUnresolvedDiff);\n }, [editor, onChange, id, acceptedChanges, rejectedChanges]);\n\n // Utility: deep equality for arrays of blocks\n const areBlocksEqual = (a: any[], b: any[]): boolean => {\n return JSON.stringify(a) === JSON.stringify(b);\n };\n\n // Only initialize from markdownContent once per value, and only if different\n const hasInitializedFromMarkdown = useRef<string | null>(null);\n useEffect(() => {\n const updateContent = async (markdown: string) => {\n const blocks = await editor.tryParseMarkdownToBlocks(markdown);\n if (!areBlocksEqual(blocks, editor.document)) {\n editor.replaceBlocks(editor.document, blocks);\n }\n };\n\n if (markdownContent && hasInitializedFromMarkdown.current !== markdownContent) {\n hasInitializedFromMarkdown.current = markdownContent;\n updateContent(markdownContent).then(() => handleChange());\n }\n }, [markdownContent, editor]);\n\n // Update editor content when diff content changes, but only if different\n // Prevent unnecessary replaceBlocks calls that reset scroll/cursor.\n const previousContentHashRef = useRef<string | null>(null);\n useEffect(() => {\n if (!processedContent || !editor) return;\n // A collaborative editor's content IS the shared Yjs document: it has no initialContent,\n // so processedContent is [] and replaceBlocks would wipe the document it just loaded,\n // and that wipe would be synced to every other writer and stored.\n if (collaboration) return;\n const hash = JSON.stringify(processedContent);\n if (previousContentHashRef.current === hash) return; // no changes\n // Skip replaceBlocks when the new initialContent is just an echo of what\n // the editor itself emitted. Without this, a form-controlled parent\n // (FormBlockNote) round-trips field.value back as initialContent on every\n // keystroke and triggers replaceBlocks, which resets the cursor and can\n // strip just-inserted inline content like mentions.\n if (lastEmittedHashRef.current === hash) {\n previousContentHashRef.current = hash;\n return;\n }\n const currentHash = JSON.stringify(editor.document);\n if (currentHash === hash) {\n previousContentHashRef.current = hash;\n return; // already in sync\n }\n editor.replaceBlocks(editor.document, processedContent as PartialBlock[]);\n previousContentHashRef.current = hash;\n }, [processedContent, editor, collaboration]);\n\n useEffect(() => {\n if (!onWarmMentions || !initialContent || !Array.isArray(initialContent) || initialContent.length === 0) return;\n onWarmMentions(initialContent);\n }, [onWarmMentions, initialContent]);\n\n useEffect(() => {\n docRef.current.getDoc = () => editor?.document ?? [];\n selectionRef.current.getSelectedBlocks = () => {\n const sel = editor?.getSelection?.();\n const blocks = (sel as any)?.blocks;\n return Array.isArray(blocks) ? blocks : [];\n };\n }, [editor]);\n\n // Handle audio received from whisper transcription\n const _handleAudioReceived = useCallback(\n (message: string) => {\n try {\n // Ensure the editor has focus\n const editorElement = editorRef.current?.querySelector('[contenteditable=\"true\"]') as HTMLElement;\n if (editorElement && document.activeElement !== editorElement) {\n editorElement.focus();\n }\n\n // Insert the transcribed text at the current cursor position\n editor.insertInlineContent(message);\n } catch (error) {\n console.error(\"Error inserting transcribed text:\", error);\n // Fallback: try to insert at the end of the document\n try {\n const blocks = editor.document;\n if (blocks.length > 0) {\n const lastBlock = blocks[blocks.length - 1];\n editor.setTextCursorPosition(lastBlock.id, \"end\");\n editor.insertInlineContent(message);\n }\n } catch (fallbackError) {\n console.error(\"Fallback insertion also failed:\", fallbackError);\n }\n }\n },\n [editor],\n );\n\n return (\n <div\n ref={editorRef}\n className={cn(\n bordered ? \"rounded-md border border-input bg-input/20 dark:bg-input/30\" : \"\",\n \"flex flex-col w-full\",\n // Pin BlockNote's font-size so it doesn't jump from 14→16px when the\n // xl-ai AIMenu mounts. The shadcn theme sets `.bn-default-styles {\n // font-size: 16px }` explicitly; outside AI mode the form's text-sm\n // wins via cascade, but ForkYDocExtension re-evaluates the style\n // context on AI activation and the explicit 16px takes over.\n // Phone: an editable field types at 16px, or iPhone Safari zooms the\n // page in on focus. The two ranges never overlap, so neither depends on\n // CSS source order to win.\n isEditable\n ? \"md:[&_.bn-default-styles]:!text-sm max-md:[&_.bn-default-styles]:!text-base\"\n : \"[&_.bn-default-styles]:!text-sm\",\n className,\n )}\n >\n <BlockNoteView\n editor={editor}\n onChange={handleChange}\n editable={isEditable}\n formattingToolbar={false}\n slashMenu={!aiActive}\n // Follow the app theme: hardcoding \"light\" leaves dark (light-theme)\n // text on the dark card, which is nearly unreadable in dark mode.\n theme={resolvedTheme === \"dark\" ? \"dark\" : \"light\"}\n // `className` is applied by BlockNote to both the main `.bn-container`\n // AND `editor.portalElement` (the floating-UI portal root). Gate `p-4`\n // on `.bn-container` so it doesn't add padding to the empty portal\n // element and produce a phantom scrollbar on the wrapper.\n className={cn(\n \"BlockNoteView flex-1\",\n isEditable && \"[&.bn-container]:p-4\",\n // In stretch mode the parent chain caps our height via flex; without\n // these two classes the `.bn-container` keeps `min-height: auto`\n // (its content's intrinsic height) and pushes the bordered wrapper\n // — and the surrounding EditorSheet form — to scroll instead of\n // scrolling internally.\n isEditable && stretch && \"[&.bn-container]:min-h-0 [&.bn-container]:overflow-y-auto\",\n // Phone — these reach the floating menus because BlockNote puts this\n // className on its portal root too. The formatting toolbar (~450px)\n // scrolls sideways instead of losing alignment and link off-screen;\n // the slash menu is capped to the screen and drops its keyboard\n // shortcut badges, which mean nothing on a touch screen.\n \"max-md:[&_.bn-formatting-toolbar]:max-w-[calc(100vw-1rem)] max-md:[&_.bn-formatting-toolbar]:overflow-x-auto max-md:[&_.bn-formatting-toolbar>*]:shrink-0\",\n \"max-md:[&_.bn-suggestion-menu]:max-w-[calc(100vw-1rem)] max-md:[&_.bn-suggestion-menu_[role=option]>[data-position=right]]:hidden\",\n size === \"sm\" && \"small\",\n )}\n >\n <BlockNoteEditorFormattingToolbar showAI={aiActive} />\n {enableMentions && mentionSearchFn && (\n <BlockNoteEditorMentionSuggestionMenu\n editor={editor}\n mentionSearchFn={mentionSearchFn}\n mentionSearchParams={mentionSearchParams}\n suggestionMenuComponent={suggestionMenuComponent}\n />\n )}\n {renderOverlays?.(editor)}\n {enableMentions && mentionResolveFn && (\n <BlockNoteEditorMentionHoverCard containerRef={editorRef} mentionResolveFn={mentionResolveFn} />\n )}\n {aiActive && (\n <SuggestionMenuController\n triggerCharacter=\"/\"\n getItems={async (query: string) =>\n filterSuggestionItems([...getDefaultReactSlashMenuItems(editor), ...getAISlashMenuItems(editor)], query)\n }\n />\n )}\n {aiActive && <AIMenuController aiMenu={() => <NarrAIMenu actions={aiConfig?.actions} />} />}\n </BlockNoteView>\n </div>\n );\n}\n","import { LayoutTemplateIcon, SparklesIcon } from \"lucide-react\";\nimport React from \"react\";\n\n/**\n * Whole-document AI actions an editor can offer in its AI menu when nothing\n * is selected. Each maps to a backend `type` discriminator; NO prompt text\n * lives here. Selection-edit actions (Improve, Fix spelling, …) are separate\n * and always available.\n */\nexport type BlockNoteAiGenerateAction = \"fill-template\" | \"draft-impression\";\n\nexport const DEFAULT_AI_GENERATE_ACTIONS: BlockNoteAiGenerateAction[] = [\"fill-template\"];\n\nexport type AiGenerateMenuItem = {\n key: string;\n title: string;\n aliases: string[];\n /** BlockNote's DefaultReactSuggestionItem types icon as an element, not ReactNode. */\n icon: React.JSX.Element;\n size: \"small\";\n onItemClick: () => void;\n};\n\n/** `titleKey` is a message key; the caller's translator resolves it to the shown title. */\ntype AiGenerateItemDefinition = Omit<AiGenerateMenuItem, \"onItemClick\" | \"title\"> & { titleKey: string };\n\nconst ITEMS: Record<BlockNoteAiGenerateAction, AiGenerateItemDefinition> = {\n \"fill-template\": {\n key: \"generate_from_template\",\n titleKey: \"ui.editor.ai.generate_from_template\",\n aliases: [\"generate\", \"template\", \"fill\"],\n icon: <LayoutTemplateIcon size={18} />,\n size: \"small\",\n },\n \"draft-impression\": {\n key: \"suggest_impression\",\n titleKey: \"ui.editor.ai.suggest_impression\",\n aliases: [\"suggest\", \"impression\"],\n icon: <SparklesIcon size={18} />,\n size: \"small\",\n },\n};\n\nexport function generateActionItems(\n actions: BlockNoteAiGenerateAction[] | undefined,\n invoke: (type: BlockNoteAiGenerateAction) => void,\n translate: (key: string) => string,\n): AiGenerateMenuItem[] {\n return (actions ?? DEFAULT_AI_GENERATE_ACTIONS).map((type) => {\n const { titleKey, ...item } = ITEMS[type];\n return { ...item, title: translate(titleKey), onItemClick: () => invoke(type) };\n });\n}\n","\"use client\";\n\nimport {\n BasicTextStyleButton,\n BlockTypeSelect,\n CreateLinkButton,\n FileCaptionButton,\n FileReplaceButton,\n FormattingToolbar,\n FormattingToolbarController,\n TextAlignButton,\n} from \"@blocknote/react\";\nimport { AIToolbarButton } from \"@blocknote/xl-ai\";\n\nexport function BlockNoteEditorFormattingToolbar({ showAI = false }: { showAI?: boolean }) {\n return (\n <FormattingToolbarController\n formattingToolbar={() => (\n <FormattingToolbar>\n <BlockTypeSelect key={\"blockTypeSelect\"} />\n\n <FileCaptionButton key={\"fileCaptionButton\"} />\n <FileReplaceButton key={\"replaceFileButton\"} />\n\n <BasicTextStyleButton basicTextStyle={\"bold\"} key={\"boldStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"italic\"} key={\"italicStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"underline\"} key={\"underlineStyleButton\"} />\n <BasicTextStyleButton basicTextStyle={\"strike\"} key={\"strikeStyleButton\"} />\n\n <TextAlignButton textAlignment={\"left\"} key={\"textAlignLeftButton\"} />\n <TextAlignButton textAlignment={\"center\"} key={\"textAlignCenterButton\"} />\n <TextAlignButton textAlignment={\"right\"} key={\"textAlignRightButton\"} />\n\n <CreateLinkButton key={\"createLinkButton\"} />\n\n {showAI ? <AIToolbarButton key={\"aiToolbarButton\"} /> : null}\n </FormattingToolbar>\n )}\n />\n );\n}\n","import type { BlockNoteEditorOptions, PartialBlock } from \"@blocknote/core\";\nimport * as coreLocales from \"@blocknote/core/locales\";\nimport { type CollaborationOptions, withCollaboration } from \"@blocknote/core/yjs\";\n\n/**\n * Live co-editing options for the shared editor: `{ fragment, user: { name, color }, provider?, showCursorLabels? }`.\n * BlockNote 0.55 no longer reads `collaboration` from the editor options; it is applied through\n * `withCollaboration()` from `@blocknote/core/yjs`, which this module does.\n */\nexport type BlockNoteCollaboration = CollaborationOptions;\n\nexport type BuildBlockNoteEditorOptionsParams = {\n placeholder: string;\n schema: any;\n /** The validated initial content. Ignored when `collaboration` is set: the Yjs document is the content. */\n initialContent: PartialBlock[] | undefined;\n uploadFile: (file: File) => Promise<string>;\n /** true: no image uploads (no `uploadFile`, so BlockNote offers no upload). */\n disableUploads?: boolean;\n aiExtension: any;\n coreDictionary: typeof coreLocales.en;\n aiDictionary: any;\n collaboration?: BlockNoteCollaboration;\n};\n\n/**\n * The options handed to `useCreateBlockNote`. Without `collaboration` they are exactly the options the\n * editor has always built; with it, BlockNote's `withCollaboration` adds the collaboration extension,\n * disables the local history (Yjs owns undo) and replaces `initialContent` with its fixed placeholder.\n */\nexport function buildBlockNoteEditorOptions(\n params: BuildBlockNoteEditorOptionsParams,\n): Partial<BlockNoteEditorOptions<any, any, any>> {\n const { aiExtension, coreDictionary, aiDictionary, collaboration } = params;\n\n const options: Partial<BlockNoteEditorOptions<any, any, any>> = {\n placeholders: {\n emptyDocument: params.placeholder,\n },\n schema: params.schema,\n uploadFile: params.disableUploads ? undefined : params.uploadFile,\n extensions: aiExtension ? [aiExtension] : undefined,\n dictionary: aiExtension\n ? { ...coreDictionary, ai: aiDictionary }\n : coreDictionary === coreLocales.en\n ? undefined\n : coreDictionary,\n };\n\n if (collaboration) return withCollaboration({ ...options, collaboration });\n\n return { ...options, initialContent: params.initialContent };\n}\n\n/** An explicit `editable` wins; otherwise the editor is editable when it reports changes. */\nexport function resolveEditable(params: { editable?: boolean; onChange?: unknown }): boolean {\n return params.editable !== undefined ? params.editable : params.onChange !== undefined;\n}\n"]}
@@ -6,10 +6,10 @@ import {
6
6
  createMentionInlineContentSpec,
7
7
  errorToast,
8
8
  useCurrentUserContextOptional
9
- } from "./chunk-SQUMPLNN.mjs";
9
+ } from "./chunk-6XJOQFCP.mjs";
10
10
  import {
11
11
  getPublicApiUrl
12
- } from "./chunk-6RLATJ2L.mjs";
12
+ } from "./chunk-4FNGWALA.mjs";
13
13
  import "./chunk-PW42LRSL.mjs";
14
14
  import {
15
15
  BlockNoteDiffUtil,
@@ -17,7 +17,7 @@ import {
17
17
  S3Service,
18
18
  cn,
19
19
  useI18nLocale
20
- } from "./chunk-BGLOFZ3K.mjs";
20
+ } from "./chunk-AA52ORW2.mjs";
21
21
  import "./chunk-V5MI7RNB.mjs";
22
22
  import {
23
23
  getClientToken
@@ -876,4 +876,4 @@ __name(BlockNoteEditor, "BlockNoteEditor");
876
876
  export {
877
877
  BlockNoteEditor as default
878
878
  };
879
- //# sourceMappingURL=BlockNoteEditor-ZJPGGUQQ.mjs.map
879
+ //# sourceMappingURL=BlockNoteEditor-N2NBYCPC.mjs.map
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import {
3
3
  cn
4
- } from "./chunk-BGLOFZ3K.mjs";
4
+ } from "./chunk-AA52ORW2.mjs";
5
5
  import "./chunk-V5MI7RNB.mjs";
6
6
  import "./chunk-AUXK7QSA.mjs";
7
7
  import "./chunk-PI56A4DG.mjs";
@@ -44,4 +44,4 @@ __name(BlockNoteViewer, "BlockNoteViewer");
44
44
  export {
45
45
  BlockNoteViewer
46
46
  };
47
- //# sourceMappingURL=BlockNoteViewer-UCA3LV3N.mjs.map
47
+ //# sourceMappingURL=BlockNoteViewer-MTGS6YJV.mjs.map
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});"use client";
2
2
 
3
3
 
4
- var _chunkYGEOX2XXjs = require('./chunk-YGEOX2XX.js');
4
+ var _chunkFF4EIQ6Hjs = require('./chunk-FF4EIQ6H.js');
5
5
  require('./chunk-GHE4VT2R.js');
6
6
  require('./chunk-LXKSUWAV.js');
7
7
  require('./chunk-JTPRJNT2.js');
@@ -36,7 +36,7 @@ function BlockNoteViewer({ content, size }) {
36
36
  editor,
37
37
  editable: false,
38
38
  theme: resolvedTheme === "dark" ? "dark" : "light",
39
- className: _chunkYGEOX2XXjs.cn.call(void 0, "BlockNoteView", size === "sm" && "small")
39
+ className: _chunkFF4EIQ6Hjs.cn.call(void 0, "BlockNoteView", size === "sm" && "small")
40
40
  }
41
41
  );
42
42
  }
@@ -44,4 +44,4 @@ _chunk7QVYU63Ejs.__name.call(void 0, BlockNoteViewer, "BlockNoteViewer");
44
44
 
45
45
 
46
46
  exports.BlockNoteViewer = BlockNoteViewer;
47
- //# sourceMappingURL=BlockNoteViewer-FLUHEXE6.js.map
47
+ //# sourceMappingURL=BlockNoteViewer-WGCDB7IJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteViewer-FLUHEXE6.js","../src/components/viewers/BlockNoteViewer.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACTA,yCAAmC;AAKnC,2CAA8B;AAC9B,uCAAO;AACP,yCAAyB;AAsBrB,+CAAA;AAnBJ,SAAS,SAAA,CAAU,OAAA,EAA8C;AAC/D,EAAA,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,EAAA,GAAK,OAAA,CAAQ,OAAA,EAAS,CAAA,EAAG,OAAO,OAAA;AACzD,EAAA,GAAA,CAAI,OAAO,QAAA,IAAY,SAAA,GAAY,OAAA,CAAQ,IAAA,CAAK,CAAA,EAAG;AACjD,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AACjC,MAAA,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,EAAA,GAAK,MAAA,CAAO,OAAA,EAAS,CAAA,EAAG,OAAO,MAAA;AAAA,IACzD,EAAA,UAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAA;AACT;AAXS,qCAAA,SAAA,EAAA,WAAA,CAAA;AAaF,SAAS,eAAA,CAAgB,EAAE,OAAA,EAAS,KAAK,CAAA,EAAkD;AAChG,EAAA,MAAM,OAAA,EAAS,uCAAA,EAAqB,cAAA,EAAgB,SAAA,CAAU,OAAO,EAAE,CAAC,CAAA;AAGxE,EAAA,MAAM,EAAE,cAAc,EAAA,EAAI,kCAAA,CAAS;AACnC,EAAA,uBACE,6BAAA;AAAA,IAAC,qBAAA;AAAA,IAAA;AAAA,MACC,MAAA;AAAA,MACA,QAAA,EAAU,KAAA;AAAA,MACV,KAAA,EAAO,cAAA,IAAkB,OAAA,EAAS,OAAA,EAAS,OAAA;AAAA,MAI3C,SAAA,EAAW,iCAAA,eAAG,EAAiB,KAAA,IAAS,KAAA,GAAQ,OAAO;AAAA,IAAA;AAAA,EACzD,CAAA;AAEJ;AAhBgB,qCAAA,eAAA,EAAA,iBAAA,CAAA;ADkBhB;AACE;AACF,0CAAC","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteViewer-FLUHEXE6.js","sourcesContent":[null,"\"use client\";\n\nimport { useCreateBlockNote } from \"@blocknote/react\";\nimport type { PartialBlock } from \"@blocknote/core\";\n// Mirror BlockNoteEditor.tsx's BlockNoteView import + style.css. Default schema\n// only — no useCurrentUserContext, uploadImage, AI, or custom inline specs, so\n// this can render on auth-free public pages.\nimport { BlockNoteView } from \"@blocknote/shadcn\";\nimport \"@blocknote/shadcn/style.css\";\nimport { useTheme } from \"next-themes\";\nimport { cn } from \"../../utils\";\n\nfunction normalize(content: unknown): PartialBlock[] | undefined {\n if (Array.isArray(content) && content.length > 0) return content as PartialBlock[];\n if (typeof content === \"string\" && content.trim()) {\n try {\n const parsed = JSON.parse(content);\n if (Array.isArray(parsed) && parsed.length > 0) return parsed as PartialBlock[];\n } catch {\n /* plain string — fall through to undefined (rendered as empty) */\n }\n }\n return undefined;\n}\n\nexport function BlockNoteViewer({ content, size }: { content: unknown; size?: \"default\" | \"sm\" }) {\n const editor = useCreateBlockNote({ initialContent: normalize(content) });\n // Outside a next-themes ThemeProvider (public pages) resolvedTheme is\n // undefined, so this falls back to \"light\".\n const { resolvedTheme } = useTheme();\n return (\n <BlockNoteView\n editor={editor}\n editable={false}\n theme={resolvedTheme === \"dark\" ? \"dark\" : \"light\"}\n // Same two classes BlockNoteEditor applies for its `size` prop: the app\n // stylesheet keys the compact recipe off `.BlockNoteView.small`, so a\n // viewer that omits them can only ever render at reading-page size.\n className={cn(\"BlockNoteView\", size === \"sm\" && \"small\")}\n />\n );\n}\n"]}
1
+ {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteViewer-WGCDB7IJ.js","../src/components/viewers/BlockNoteViewer.tsx"],"names":[],"mappings":"AAAA,qFAAY;AACZ;AACE;AACF,sDAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B,+BAA4B;AAC5B;AACE;AACF,sDAA4B;AAC5B;AACA;ACTA,yCAAmC;AAKnC,2CAA8B;AAC9B,uCAAO;AACP,yCAAyB;AAsBrB,+CAAA;AAnBJ,SAAS,SAAA,CAAU,OAAA,EAA8C;AAC/D,EAAA,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,OAAO,EAAA,GAAK,OAAA,CAAQ,OAAA,EAAS,CAAA,EAAG,OAAO,OAAA;AACzD,EAAA,GAAA,CAAI,OAAO,QAAA,IAAY,SAAA,GAAY,OAAA,CAAQ,IAAA,CAAK,CAAA,EAAG;AACjD,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AACjC,MAAA,GAAA,CAAI,KAAA,CAAM,OAAA,CAAQ,MAAM,EAAA,GAAK,MAAA,CAAO,OAAA,EAAS,CAAA,EAAG,OAAO,MAAA;AAAA,IACzD,EAAA,UAAQ;AAAA,IAER;AAAA,EACF;AACA,EAAA,OAAO,KAAA,CAAA;AACT;AAXS,qCAAA,SAAA,EAAA,WAAA,CAAA;AAaF,SAAS,eAAA,CAAgB,EAAE,OAAA,EAAS,KAAK,CAAA,EAAkD;AAChG,EAAA,MAAM,OAAA,EAAS,uCAAA,EAAqB,cAAA,EAAgB,SAAA,CAAU,OAAO,EAAE,CAAC,CAAA;AAGxE,EAAA,MAAM,EAAE,cAAc,EAAA,EAAI,kCAAA,CAAS;AACnC,EAAA,uBACE,6BAAA;AAAA,IAAC,qBAAA;AAAA,IAAA;AAAA,MACC,MAAA;AAAA,MACA,QAAA,EAAU,KAAA;AAAA,MACV,KAAA,EAAO,cAAA,IAAkB,OAAA,EAAS,OAAA,EAAS,OAAA;AAAA,MAI3C,SAAA,EAAW,iCAAA,eAAG,EAAiB,KAAA,IAAS,KAAA,GAAQ,OAAO;AAAA,IAAA;AAAA,EACzD,CAAA;AAEJ;AAhBgB,qCAAA,eAAA,EAAA,iBAAA,CAAA;ADkBhB;AACE;AACF,0CAAC","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/BlockNoteViewer-WGCDB7IJ.js","sourcesContent":[null,"\"use client\";\n\nimport { useCreateBlockNote } from \"@blocknote/react\";\nimport type { PartialBlock } from \"@blocknote/core\";\n// Mirror BlockNoteEditor.tsx's BlockNoteView import + style.css. Default schema\n// only — no useCurrentUserContext, uploadImage, AI, or custom inline specs, so\n// this can render on auth-free public pages.\nimport { BlockNoteView } from \"@blocknote/shadcn\";\nimport \"@blocknote/shadcn/style.css\";\nimport { useTheme } from \"next-themes\";\nimport { cn } from \"../../utils\";\n\nfunction normalize(content: unknown): PartialBlock[] | undefined {\n if (Array.isArray(content) && content.length > 0) return content as PartialBlock[];\n if (typeof content === \"string\" && content.trim()) {\n try {\n const parsed = JSON.parse(content);\n if (Array.isArray(parsed) && parsed.length > 0) return parsed as PartialBlock[];\n } catch {\n /* plain string — fall through to undefined (rendered as empty) */\n }\n }\n return undefined;\n}\n\nexport function BlockNoteViewer({ content, size }: { content: unknown; size?: \"default\" | \"sm\" }) {\n const editor = useCreateBlockNote({ initialContent: normalize(content) });\n // Outside a next-themes ThemeProvider (public pages) resolvedTheme is\n // undefined, so this falls back to \"light\".\n const { resolvedTheme } = useTheme();\n return (\n <BlockNoteView\n editor={editor}\n editable={false}\n theme={resolvedTheme === \"dark\" ? \"dark\" : \"light\"}\n // Same two classes BlockNoteEditor applies for its `size` prop: the app\n // stylesheet keys the compact recipe off `.BlockNoteView.small`, so a\n // viewer that omits them can only ever render at reading-page size.\n className={cn(\"BlockNoteView\", size === \"sm\" && \"small\")}\n />\n );\n}\n"]}