Convert wiki markup to readable text and render each detail field in its own card block. Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
3.2 KiB
JavaScript
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);
|
|
}
|