jiraCloud/lib/jiraRichText.js
jmcqueen 98dd33a20e Clean up Jira rich text in Webex approval card details.
Convert wiki markup to readable text and render each detail field in its own card block.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 12:26:28 -04:00

101 lines
3.2 KiB
JavaScript

function adfNodeToText(node) {
if (!node) {
return '';
}
if (Array.isArray(node)) {
return node.map(adfNodeToText).join('');
}
if (typeof node === 'string') {
return node;
}
switch (node.type) {
case 'text':
return node.text || '';
case 'hardBreak':
return '\n';
case 'paragraph':
case 'heading':
return `${(node.content || []).map(adfNodeToText).join('')}\n`;
case 'bulletList':
case 'orderedList':
return (node.content || [])
.map(item => adfNodeToText(item))
.join('');
case 'listItem':
return `${(node.content || []).map(adfNodeToText).join('').trim()}\n`;
case 'rule':
return '\n';
case 'codeBlock':
return `${(node.content || []).map(adfNodeToText).join('')}\n`;
case 'inlineCard':
case 'blockCard':
return node.attrs?.url || '';
default:
if (node.content) {
return node.content.map(adfNodeToText).join('');
}
return '';
}
}
export function adfToPlainText(value) {
if (!value || typeof value !== 'object') {
return '';
}
if (value.type !== 'doc' || !Array.isArray(value.content)) {
return '';
}
return value.content.map(adfNodeToText).join('').trim();
}
export function formatJiraWikiMarkup(text) {
if (text == null || text === '') {
return '';
}
let output = String(text).replace(/\r\n/g, '\n');
output = output.replace(/\{noformat\}([\s\S]*?)\{noformat\}/gi, (_, body) => `\n${body.trim()}\n`);
output = output.replace(/\{code(?::[^}]*)?\}([\s\S]*?)\{code\}/gi, (_, body) => `\n${body.trim()}\n`);
output = output.replace(/\{color(?::[^}]*)?\}([\s\S]*?)\{color\}/gi, '$1');
output = output.replace(/\{quote\}([\s\S]*?)\{quote\}/gi, '$1');
output = output.replace(/![^!\n]+!/g, '');
output = output.replace(/\[~accountid:[^\]]+\]/gi, '');
output = output.replace(/\[([^\]|]+)\|([^\]]+?)(?:\|smart-link)?\]/gi, (_, label, url) => {
const cleanLabel = label.trim();
const cleanUrl = url.trim();
if (!cleanLabel || cleanLabel === cleanUrl) {
return cleanUrl;
}
return `${cleanLabel} (${cleanUrl})`;
});
output = output.replace(/\*\*([^*\n]+?)\*\*/g, '$1');
output = output.replace(/(?<!\*)\*([^*\n]+)\*(?!\*)/g, '$1');
output = output.replace(/^h([1-6])\.\s*(.+)$/gim, (_, __, title) => `${title.trim()}\n`);
output = output.replace(/^#{1,6}\s+(.+)$/gm, '$1\n');
output = output.replace(/^\s*[-*#]\s+/gm, '• ');
output = output.replace(/^-{4,}\s*$/gm, '\n');
output = output.replace(/[ \t]+\n/g, '\n');
output = output.replace(/\n{3,}/g, '\n\n');
return output.trim();
}
export function formatJiraRichText(value) {
if (value == null || value === '') {
return '';
}
if (typeof value === 'object') {
const adfText = adfToPlainText(value);
if (adfText) {
return formatJiraWikiMarkup(adfText);
}
return value.displayName || value.value || value.name || '';
}
return formatJiraWikiMarkup(value);
}