@pega/cosmos-react-rte 9.0.0-build.22.2 → 9.0.0-build.23.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AIRewriteButton.d.ts","sourceRoot":"","sources":["../../../../src/components/Editor/Toolbar/AIRewriteButton.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAOxD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,cAAc,EAAE,CACd,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,QAAQ,EACvB,sBAAsB,EAAE,MAAM,EAC9B,SAAS,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,KACvC,IAAI,CAAC;CACX;AAED,QAAA,MAAM,eAAe,GAAI,4BAA4B,oBAAoB,4CAsExE,CAAC;AAEF,eAAe,eAAe,CAAC"}
1
+ {"version":3,"file":"AIRewriteButton.d.ts","sourceRoot":"","sources":["../../../../src/components/Editor/Toolbar/AIRewriteButton.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,YAAY,EAAE,MAAM,cAAc,CAAC;AAG3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AASxD,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,YAAY,CAAC;IACrB,cAAc,EAAE,CACd,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,QAAQ,EACvB,sBAAsB,EAAE,MAAM,EAC9B,SAAS,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,KACvC,IAAI,CAAC;CACX;AAED,QAAA,MAAM,eAAe,GAAI,4BAA4B,oBAAoB,4CAqFxE,CAAC;AAEF,eAAe,eAAe,CAAC"}
@@ -1,8 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useState, useRef } from 'react';
3
3
  import { AIRewrite, Icon, registerIcon, useElement, useI18n } from '@pega/cosmos-react-core';
4
4
  import * as polarisIcon from '@pega/cosmos-react-core/lib/components/Icon/icons/polaris.icon';
5
5
  import ToolbarButton from '../../RichTextEditor/Toolbar/ToolbarButton';
6
+ import { extractLinkAndImg, restorePlaceholders } from '../utils/htmlPlaceholder';
6
7
  registerIcon(polarisIcon);
7
8
  const AIRewriteButton = ({ editor, onRewriteClick }) => {
8
9
  const t = useI18n();
@@ -10,35 +11,45 @@ const AIRewriteButton = ({ editor, onRewriteClick }) => {
10
11
  const [showPopover, setShowPopover] = useState(false);
11
12
  const [isRewriting, setIsRewriting] = useState(false);
12
13
  const [pendingRewriteText, setPendingRewriteText] = useState(undefined);
14
+ const placeholderMapRef = useRef({});
15
+ const userPlaceholdersRef = useRef([]);
13
16
  const getHtmlContent = () => {
14
17
  return editor ? editor.getHTML().trim() : '';
15
18
  };
16
19
  const hasContent = () => {
17
20
  return editor ? editor.getText().trim() !== '' : false;
18
21
  };
19
- // When the AI returns a result, store it for review instead of inserting immediately
22
+ // When the AI returns a result, keep tokens intact for the review TextArea
20
23
  const handleRewriteSuccess = (rewrittenText) => {
21
24
  setIsRewriting(false);
22
25
  setPendingRewriteText(rewrittenText);
23
26
  };
24
27
  // Called when the author accepts (and optionally edits) the AI suggestion
25
28
  const handleApplyRewrite = (editedText) => {
26
- editor.chain().setContent(editedText).focus('end').run();
29
+ const restoredText = restorePlaceholders(editedText, placeholderMapRef.current, userPlaceholdersRef.current);
30
+ editor.chain().setContent(restoredText).focus('end').run();
27
31
  setShowPopover(false);
28
32
  setPendingRewriteText(undefined);
33
+ placeholderMapRef.current = {};
34
+ userPlaceholdersRef.current = [];
29
35
  };
30
36
  return (_jsxs(_Fragment, { children: [_jsx(ToolbarButton, { ref: setButtonEl, onClick: () => {
31
37
  setShowPopover(true);
32
38
  }, disabled: !hasContent(), tooltip: t('ai_rewrite_title'), label: t('ai_rewrite_title'), children: _jsx(Icon, { name: 'polaris' }) }), showPopover && buttonEl && (_jsx(AIRewrite, { target: buttonEl, isLoading: isRewriting, rewrittenText: pendingRewriteText, onSubmit: (rewriteAction, additionalInstructions) => {
33
39
  const originalContent = getHtmlContent();
40
+ const { placeholderHtml, placeholderMap, userPlaceholders } = extractLinkAndImg(originalContent);
41
+ placeholderMapRef.current = placeholderMap;
42
+ userPlaceholdersRef.current = userPlaceholders;
34
43
  setIsRewriting(true);
35
44
  setPendingRewriteText(undefined);
36
- onRewriteClick(originalContent, rewriteAction, additionalInstructions || '', handleRewriteSuccess);
45
+ onRewriteClick(placeholderHtml, rewriteAction, additionalInstructions || '', handleRewriteSuccess);
37
46
  // Keep popover open — it will transition to loading then review mode
38
47
  }, onApply: handleApplyRewrite, onBack: () => setPendingRewriteText(undefined), onCancel: () => {
39
48
  setShowPopover(false);
40
49
  setPendingRewriteText(undefined);
41
50
  setIsRewriting(false);
51
+ placeholderMapRef.current = {};
52
+ userPlaceholdersRef.current = [];
42
53
  buttonEl?.focus();
43
54
  } }))] }));
44
55
  };
@@ -1 +1 @@
1
- {"version":3,"file":"AIRewriteButton.js","sourceRoot":"","sources":["../../../../src/components/Editor/Toolbar/AIRewriteButton.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react';\nimport type { Editor as TiptapEditor } from '@tiptap/core';\n\nimport { AIRewrite, Icon, registerIcon, useElement, useI18n } from '@pega/cosmos-react-core';\nimport type { AIAction } from '@pega/cosmos-react-core';\nimport * as polarisIcon from '@pega/cosmos-react-core/lib/components/Icon/icons/polaris.icon';\n\nimport ToolbarButton from '../../RichTextEditor/Toolbar/ToolbarButton';\n\nregisterIcon(polarisIcon);\n\nexport interface AIRewriteButtonProps {\n editor: TiptapEditor;\n onRewriteClick: (\n originalText: string,\n rewriteAction: AIAction,\n additionalInstructions: string,\n onSuccess: (rewrittenText: string) => void\n ) => void;\n}\n\nconst AIRewriteButton = ({ editor, onRewriteClick }: AIRewriteButtonProps) => {\n const t = useI18n();\n const [buttonEl, setButtonEl] = useElement<HTMLElement>();\n const [showPopover, setShowPopover] = useState(false);\n const [isRewriting, setIsRewriting] = useState(false);\n const [pendingRewriteText, setPendingRewriteText] = useState<string | undefined>(undefined);\n\n const getHtmlContent = (): string => {\n return editor ? editor.getHTML().trim() : '';\n };\n\n const hasContent = (): boolean => {\n return editor ? editor.getText().trim() !== '' : false;\n };\n\n // When the AI returns a result, store it for review instead of inserting immediately\n const handleRewriteSuccess = (rewrittenText: string) => {\n setIsRewriting(false);\n setPendingRewriteText(rewrittenText);\n };\n\n // Called when the author accepts (and optionally edits) the AI suggestion\n const handleApplyRewrite = (editedText: string) => {\n editor.chain().setContent(editedText).focus('end').run();\n setShowPopover(false);\n setPendingRewriteText(undefined);\n };\n\n return (\n <>\n <ToolbarButton\n ref={setButtonEl}\n onClick={() => {\n setShowPopover(true);\n }}\n disabled={!hasContent()}\n tooltip={t('ai_rewrite_title')}\n label={t('ai_rewrite_title')}\n >\n <Icon name='polaris' />\n </ToolbarButton>\n {showPopover && buttonEl && (\n <AIRewrite\n target={buttonEl}\n isLoading={isRewriting}\n rewrittenText={pendingRewriteText}\n onSubmit={(rewriteAction, additionalInstructions) => {\n const originalContent = getHtmlContent();\n setIsRewriting(true);\n setPendingRewriteText(undefined);\n onRewriteClick(\n originalContent,\n rewriteAction,\n additionalInstructions || '',\n handleRewriteSuccess\n );\n // Keep popover open — it will transition to loading then review mode\n }}\n onApply={handleApplyRewrite}\n onBack={() => setPendingRewriteText(undefined)}\n onCancel={() => {\n setShowPopover(false);\n setPendingRewriteText(undefined);\n setIsRewriting(false);\n buttonEl?.focus();\n }}\n />\n )}\n </>\n );\n};\n\nexport default AIRewriteButton;\n"]}
1
+ {"version":3,"file":"AIRewriteButton.js","sourceRoot":"","sources":["../../../../src/components/Editor/Toolbar/AIRewriteButton.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useRef } from 'react';\nimport type { Editor as TiptapEditor } from '@tiptap/core';\n\nimport { AIRewrite, Icon, registerIcon, useElement, useI18n } from '@pega/cosmos-react-core';\nimport type { AIAction } from '@pega/cosmos-react-core';\nimport * as polarisIcon from '@pega/cosmos-react-core/lib/components/Icon/icons/polaris.icon';\n\nimport ToolbarButton from '../../RichTextEditor/Toolbar/ToolbarButton';\nimport { extractLinkAndImg, restorePlaceholders } from '../utils/htmlPlaceholder';\nimport type { PlaceholderMap } from '../utils/htmlPlaceholder';\n\nregisterIcon(polarisIcon);\n\nexport interface AIRewriteButtonProps {\n editor: TiptapEditor;\n onRewriteClick: (\n originalText: string,\n rewriteAction: AIAction,\n additionalInstructions: string,\n onSuccess: (rewrittenText: string) => void\n ) => void;\n}\n\nconst AIRewriteButton = ({ editor, onRewriteClick }: AIRewriteButtonProps) => {\n const t = useI18n();\n const [buttonEl, setButtonEl] = useElement<HTMLElement>();\n const [showPopover, setShowPopover] = useState(false);\n const [isRewriting, setIsRewriting] = useState(false);\n const [pendingRewriteText, setPendingRewriteText] = useState<string | undefined>(undefined);\n const placeholderMapRef = useRef<PlaceholderMap>({});\n const userPlaceholdersRef = useRef<string[]>([]);\n\n const getHtmlContent = (): string => {\n return editor ? editor.getHTML().trim() : '';\n };\n\n const hasContent = (): boolean => {\n return editor ? editor.getText().trim() !== '' : false;\n };\n\n // When the AI returns a result, keep tokens intact for the review TextArea\n const handleRewriteSuccess = (rewrittenText: string) => {\n setIsRewriting(false);\n setPendingRewriteText(rewrittenText);\n };\n\n // Called when the author accepts (and optionally edits) the AI suggestion\n const handleApplyRewrite = (editedText: string) => {\n const restoredText = restorePlaceholders(\n editedText,\n placeholderMapRef.current,\n userPlaceholdersRef.current\n );\n editor.chain().setContent(restoredText).focus('end').run();\n setShowPopover(false);\n setPendingRewriteText(undefined);\n placeholderMapRef.current = {};\n userPlaceholdersRef.current = [];\n };\n\n return (\n <>\n <ToolbarButton\n ref={setButtonEl}\n onClick={() => {\n setShowPopover(true);\n }}\n disabled={!hasContent()}\n tooltip={t('ai_rewrite_title')}\n label={t('ai_rewrite_title')}\n >\n <Icon name='polaris' />\n </ToolbarButton>\n {showPopover && buttonEl && (\n <AIRewrite\n target={buttonEl}\n isLoading={isRewriting}\n rewrittenText={pendingRewriteText}\n onSubmit={(rewriteAction, additionalInstructions) => {\n const originalContent = getHtmlContent();\n const { placeholderHtml, placeholderMap, userPlaceholders } =\n extractLinkAndImg(originalContent);\n placeholderMapRef.current = placeholderMap;\n userPlaceholdersRef.current = userPlaceholders;\n setIsRewriting(true);\n setPendingRewriteText(undefined);\n onRewriteClick(\n placeholderHtml,\n rewriteAction,\n additionalInstructions || '',\n handleRewriteSuccess\n );\n // Keep popover open — it will transition to loading then review mode\n }}\n onApply={handleApplyRewrite}\n onBack={() => setPendingRewriteText(undefined)}\n onCancel={() => {\n setShowPopover(false);\n setPendingRewriteText(undefined);\n setIsRewriting(false);\n placeholderMapRef.current = {};\n userPlaceholdersRef.current = [];\n buttonEl?.focus();\n }}\n />\n )}\n </>\n );\n};\n\nexport default AIRewriteButton;\n"]}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Extract `<img>` and `<a>` elements from HTML content by replacing them with
3
+ * `[PEGA-IMG-XXXX]` / `[PEGA-LINK-XXXX]` placeholders and storing the originals.
4
+ *
5
+ * This reduces the payload size for API calls (e.g. AI rewrite) where large
6
+ * base64/blob image `src` values would exceed request limits, and prevents
7
+ * the AI from stripping anchor tags.
8
+ *
9
+ * Images wrapped in an anchor (image action URL) are captured as a single LINK
10
+ * placeholder — anchors are processed first so the full `<a><img/></a>`
11
+ * structure is stored intact, avoiding duplicate images on restore.
12
+ *
13
+ * After the API returns rewritten text, `restorePlaceholders` puts the original
14
+ * HTML back. Placeholders the AI dropped are appended at the end wrapped in
15
+ * `<p>` tags (TipTap's default block element). Any hallucinated placeholder
16
+ * strings that don't map to real entries are stripped.
17
+ */
18
+ export interface PlaceholderMap {
19
+ [placeholder: string]: string;
20
+ }
21
+ interface ExtractResult {
22
+ placeholderHtml: string;
23
+ placeholderMap: PlaceholderMap;
24
+ userPlaceholders: string[];
25
+ }
26
+ /**
27
+ * Replaces all `<a>` and standalone `<img>` elements in the HTML with unique
28
+ * placeholders. Uses DOMParser to reliably locate elements rather than regex.
29
+ * Anchors are processed first so that linked images (`<a><img/></a>`) are
30
+ * captured as a single LINK placeholder.
31
+ */
32
+ export declare function extractLinkAndImg(html: string): ExtractResult;
33
+ /**
34
+ * Restores placeholders in the rewritten HTML with the original HTML elements.
35
+ * Placeholders that were dropped by the AI are appended at the end, each
36
+ * wrapped in a `<p>` tag (TipTap's default block element).
37
+ * Any hallucinated placeholder strings (not in the map) are stripped.
38
+ */
39
+ export declare function restorePlaceholders(html: string, placeholderMap: PlaceholderMap, userPlaceholders?: string[]): string;
40
+ export {};
41
+ //# sourceMappingURL=htmlPlaceholder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlPlaceholder.d.ts","sourceRoot":"","sources":["../../../../src/components/Editor/utils/htmlPlaceholder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAIH,MAAM,WAAW,cAAc;IAC7B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;CAC/B;AAED,UAAU,aAAa;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAgBD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,CA0B7D;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,cAAc,EAAE,cAAc,EAC9B,gBAAgB,GAAE,MAAM,EAAO,GAC9B,MAAM,CAsBR"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Extract `<img>` and `<a>` elements from HTML content by replacing them with
3
+ * `[PEGA-IMG-XXXX]` / `[PEGA-LINK-XXXX]` placeholders and storing the originals.
4
+ *
5
+ * This reduces the payload size for API calls (e.g. AI rewrite) where large
6
+ * base64/blob image `src` values would exceed request limits, and prevents
7
+ * the AI from stripping anchor tags.
8
+ *
9
+ * Images wrapped in an anchor (image action URL) are captured as a single LINK
10
+ * placeholder — anchors are processed first so the full `<a><img/></a>`
11
+ * structure is stored intact, avoiding duplicate images on restore.
12
+ *
13
+ * After the API returns rewritten text, `restorePlaceholders` puts the original
14
+ * HTML back. Placeholders the AI dropped are appended at the end wrapped in
15
+ * `<p>` tags (TipTap's default block element). Any hallucinated placeholder
16
+ * strings that don't map to real entries are stripped.
17
+ */
18
+ import { createUID } from '@pega/cosmos-react-core';
19
+ const PLACEHOLDER_PATTERN = /\[PEGA-(?:IMG|LINK)-[^\]]+\]/g;
20
+ function createUniquePlaceholder(prefix, existingMap, html) {
21
+ let placeholder;
22
+ do {
23
+ placeholder = `[PEGA-${prefix}-${createUID()}]`;
24
+ } while (placeholder in existingMap || html.includes(placeholder));
25
+ return placeholder;
26
+ }
27
+ /**
28
+ * Replaces all `<a>` and standalone `<img>` elements in the HTML with unique
29
+ * placeholders. Uses DOMParser to reliably locate elements rather than regex.
30
+ * Anchors are processed first so that linked images (`<a><img/></a>`) are
31
+ * captured as a single LINK placeholder.
32
+ */
33
+ export function extractLinkAndImg(html) {
34
+ const placeholderMap = {};
35
+ const parser = new DOMParser();
36
+ const doc = parser.parseFromString(html, 'text/html');
37
+ // Record placeholder-like strings the user literally typed so they are
38
+ // not mistakenly stripped during restoration (they are NOT hallucinations).
39
+ const userPlaceholders = (html.match(PLACEHOLDER_PATTERN) ?? []).filter((match, i, arr) => arr.indexOf(match) === i);
40
+ // Process anchor tags first — captures linked images as one unit
41
+ for (const anchor of Array.from(doc.body.querySelectorAll('a'))) {
42
+ const placeholder = createUniquePlaceholder('LINK', placeholderMap, html);
43
+ placeholderMap[placeholder] = anchor.outerHTML;
44
+ anchor.replaceWith(doc.createTextNode(placeholder));
45
+ }
46
+ // Process remaining standalone img tags (not inside an <a> already replaced)
47
+ for (const img of Array.from(doc.body.querySelectorAll('img'))) {
48
+ const placeholder = createUniquePlaceholder('IMG', placeholderMap, html);
49
+ placeholderMap[placeholder] = img.outerHTML;
50
+ img.replaceWith(doc.createTextNode(placeholder));
51
+ }
52
+ return { placeholderHtml: doc.body.innerHTML, placeholderMap, userPlaceholders };
53
+ }
54
+ /**
55
+ * Restores placeholders in the rewritten HTML with the original HTML elements.
56
+ * Placeholders that were dropped by the AI are appended at the end, each
57
+ * wrapped in a `<p>` tag (TipTap's default block element).
58
+ * Any hallucinated placeholder strings (not in the map) are stripped.
59
+ */
60
+ export function restorePlaceholders(html, placeholderMap, userPlaceholders = []) {
61
+ let result = html;
62
+ const missingElements = [];
63
+ for (const [placeholder, originalHtml] of Object.entries(placeholderMap)) {
64
+ if (result.includes(placeholder)) {
65
+ result = result.replaceAll(placeholder, originalHtml);
66
+ }
67
+ else {
68
+ missingElements.push(originalHtml);
69
+ }
70
+ }
71
+ // Strip any hallucinated placeholder strings the AI may have invented,
72
+ // but preserve placeholder-like strings the user literally typed.
73
+ const userSet = new Set(userPlaceholders);
74
+ result = result.replace(PLACEHOLDER_PATTERN, match => (userSet.has(match) ? match : ''));
75
+ if (missingElements.length > 0) {
76
+ result = `${result}${missingElements.map(el => `<p>${el}</p>`).join('')}`;
77
+ }
78
+ return result;
79
+ }
80
+ //# sourceMappingURL=htmlPlaceholder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"htmlPlaceholder.js","sourceRoot":"","sources":["../../../../src/components/Editor/utils/htmlPlaceholder.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Extract `<img>` and `<a>` elements from HTML content by replacing them with\n * `[PEGA-IMG-XXXX]` / `[PEGA-LINK-XXXX]` placeholders and storing the originals.\n *\n * This reduces the payload size for API calls (e.g. AI rewrite) where large\n * base64/blob image `src` values would exceed request limits, and prevents\n * the AI from stripping anchor tags.\n *\n * Images wrapped in an anchor (image action URL) are captured as a single LINK\n * placeholder — anchors are processed first so the full `<a><img/></a>`\n * structure is stored intact, avoiding duplicate images on restore.\n *\n * After the API returns rewritten text, `restorePlaceholders` puts the original\n * HTML back. Placeholders the AI dropped are appended at the end wrapped in\n * `<p>` tags (TipTap's default block element). Any hallucinated placeholder\n * strings that don't map to real entries are stripped.\n */\n\nimport { createUID } from '@pega/cosmos-react-core';\n\nexport interface PlaceholderMap {\n [placeholder: string]: string;\n}\n\ninterface ExtractResult {\n placeholderHtml: string;\n placeholderMap: PlaceholderMap;\n userPlaceholders: string[];\n}\n\nconst PLACEHOLDER_PATTERN = /\\[PEGA-(?:IMG|LINK)-[^\\]]+\\]/g;\n\nfunction createUniquePlaceholder(\n prefix: string,\n existingMap: PlaceholderMap,\n html: string\n): string {\n let placeholder: string;\n do {\n placeholder = `[PEGA-${prefix}-${createUID()}]`;\n } while (placeholder in existingMap || html.includes(placeholder));\n return placeholder;\n}\n\n/**\n * Replaces all `<a>` and standalone `<img>` elements in the HTML with unique\n * placeholders. Uses DOMParser to reliably locate elements rather than regex.\n * Anchors are processed first so that linked images (`<a><img/></a>`) are\n * captured as a single LINK placeholder.\n */\nexport function extractLinkAndImg(html: string): ExtractResult {\n const placeholderMap: PlaceholderMap = {};\n const parser = new DOMParser();\n const doc = parser.parseFromString(html, 'text/html');\n\n // Record placeholder-like strings the user literally typed so they are\n // not mistakenly stripped during restoration (they are NOT hallucinations).\n const userPlaceholders = (html.match(PLACEHOLDER_PATTERN) ?? []).filter(\n (match, i, arr) => arr.indexOf(match) === i\n );\n\n // Process anchor tags first — captures linked images as one unit\n for (const anchor of Array.from(doc.body.querySelectorAll('a'))) {\n const placeholder = createUniquePlaceholder('LINK', placeholderMap, html);\n placeholderMap[placeholder] = anchor.outerHTML;\n anchor.replaceWith(doc.createTextNode(placeholder));\n }\n\n // Process remaining standalone img tags (not inside an <a> already replaced)\n for (const img of Array.from(doc.body.querySelectorAll('img'))) {\n const placeholder = createUniquePlaceholder('IMG', placeholderMap, html);\n placeholderMap[placeholder] = img.outerHTML;\n img.replaceWith(doc.createTextNode(placeholder));\n }\n\n return { placeholderHtml: doc.body.innerHTML, placeholderMap, userPlaceholders };\n}\n\n/**\n * Restores placeholders in the rewritten HTML with the original HTML elements.\n * Placeholders that were dropped by the AI are appended at the end, each\n * wrapped in a `<p>` tag (TipTap's default block element).\n * Any hallucinated placeholder strings (not in the map) are stripped.\n */\nexport function restorePlaceholders(\n html: string,\n placeholderMap: PlaceholderMap,\n userPlaceholders: string[] = []\n): string {\n let result = html;\n const missingElements: string[] = [];\n\n for (const [placeholder, originalHtml] of Object.entries(placeholderMap)) {\n if (result.includes(placeholder)) {\n result = result.replaceAll(placeholder, originalHtml);\n } else {\n missingElements.push(originalHtml);\n }\n }\n\n // Strip any hallucinated placeholder strings the AI may have invented,\n // but preserve placeholder-like strings the user literally typed.\n const userSet = new Set(userPlaceholders);\n result = result.replace(PLACEHOLDER_PATTERN, match => (userSet.has(match) ? match : ''));\n\n if (missingElements.length > 0) {\n result = `${result}${missingElements.map(el => `<p>${el}</p>`).join('')}`;\n }\n\n return result;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pega/cosmos-react-rte",
3
- "version": "9.0.0-build.22.2",
3
+ "version": "9.0.0-build.23.0",
4
4
  "license": "SEE LICENSE IN LICENSE",
5
5
  "author": "Pegasystems",
6
6
  "sideEffects": false,
@@ -14,7 +14,7 @@
14
14
  "build": "tsc -b tsconfig.build.json"
15
15
  },
16
16
  "dependencies": {
17
- "@pega/cosmos-react-core": "9.0.0-build.22.2",
17
+ "@pega/cosmos-react-core": "9.0.0-build.23.0",
18
18
  "@popperjs/core": "^2.11.6",
19
19
  "@tiptap/core": "^3.12.1",
20
20
  "@tiptap/extension-blockquote": "^3.12.1",