diff --git a/web/js/issues.js b/web/js/issues.js
index f48dd6d..5e6e4ad 100644
--- a/web/js/issues.js
+++ b/web/js/issues.js
@@ -86,7 +86,8 @@ function loadIssuesFromFiles(fileRefs, issueType, elementId, applyIssueSearchFil
allIssues.push({
id: issue.id || '',
summary: issue.summary || '',
- caveat: issue.caveat || '',
+ resolved: issue.resolved || '',
+ caveat: issue.caveat || '',
sourceVersion
});
});
@@ -157,6 +158,7 @@ function parseMarkdownIssues(markdownText) {
const issues = [];
let currentIssueId = '';
let currentIssueBodyParts = [];
+ let currentIssueResolvedBlocks = [];
let currentIssueCaveatBlocks = [];
const finalizeIssue = () => {
@@ -170,11 +172,13 @@ function parseMarkdownIssues(markdownText) {
.trim();
const summary = summaryBody;
+ const resolved = currentIssueResolvedBlocks.join('\n\n').trim();
const caveat = currentIssueCaveatBlocks.join('\n\n').trim();
issues.push({
id: currentIssueId,
summary,
+ resolved,
caveat
});
};
@@ -184,6 +188,7 @@ function parseMarkdownIssues(markdownText) {
finalizeIssue();
currentIssueId = String(token.text || '').trim();
currentIssueBodyParts = [];
+ currentIssueResolvedBlocks = [];
currentIssueCaveatBlocks = [];
return;
}
@@ -192,6 +197,14 @@ function parseMarkdownIssues(markdownText) {
return;
}
+ if (token.type === 'code' && String(token.lang || '').trim().toLowerCase() === 'resolved') {
+ const resolvedText = String(token.text || '').trim();
+ if (resolvedText) {
+ currentIssueResolvedBlocks.push(resolvedText);
+ }
+ return;
+ }
+
if (token.type === 'code' && String(token.lang || '').trim().toLowerCase() === 'caveat') {
const caveatText = String(token.text || '').trim();
if (caveatText) {
@@ -234,6 +247,7 @@ function normalizeIssuesFromPayload(data) {
return {
id: String(item.id).trim(),
summary: String(item.summary || item.description).trim(),
+ resolved: String(item.resolved || '').trim(),
caveat: String(item.caveat || '').trim()
};
}
@@ -251,6 +265,7 @@ function normalizeIssuesFromPayload(data) {
return {
id: String(issue.id).trim(),
summary: String(issue.description).trim(),
+ resolved: String(issue.resolved || '').trim(),
caveat: String(issue.caveat || '').trim()
};
}
@@ -390,17 +405,21 @@ function dedupeIssues(issues, versionKey) {
issues.forEach(issue => {
const id = (issue.id || '').trim();
const summary = (issue.summary || '').trim();
+ const resolved = (issue.resolved || '').trim();
const caveat = (issue.caveat || '').trim();
if (!id) return;
if (!map.has(id)) {
- map.set(id, { id, summary, caveat, [versionKey]: [] });
+ map.set(id, { id, summary, resolved, caveat, [versionKey]: [] });
}
const entry = map.get(id);
if (!entry.summary && summary) {
entry.summary = summary;
}
+ if (!entry.resolved && resolved) {
+ entry.resolved = resolved;
+ }
if (!entry.caveat && caveat) {
entry.caveat = caveat;
}
@@ -441,7 +460,7 @@ function getSourceVersionFromFileName(fileName) {
}
function renderSummaryCell(cell, issue) {
- cell.innerHTML = markdownSummaryToHtml(issue.summary, issue.caveat);
+ cell.innerHTML = markdownSummaryToHtml(issue.summary, issue.resolved, issue.caveat);
const refs = getSocialRefsForIssue(issue.id);
if (!refs.length) {
@@ -504,15 +523,19 @@ function normalizeSocialRefs(data) {
return map;
}
-function markdownSummaryToHtml(summaryText, caveatText) {
+function markdownSummaryToHtml(summaryText, resolvedText, caveatText) {
const input = String(summaryText || '').trim();
+ const resolved = String(resolvedText || '').trim();
const caveat = String(caveatText || '').trim();
- if (!input && !caveat) {
+ if (!input && !resolved && !caveat) {
return '';
}
const htmlParts = [];
+ if (resolved) {
+ htmlParts.push(`
Resolved: ${escapeHtml(resolved)}
`);
+ }
if (caveat) {
htmlParts.push(`Caveat: ${escapeHtml(caveat)}
`);
}
diff --git a/web/js/markdown.js b/web/js/markdown.js
index e231983..7171fdc 100644
--- a/web/js/markdown.js
+++ b/web/js/markdown.js
@@ -17,6 +17,13 @@ export function buildIssueMarkdownDocument(payload) {
lines.push(`## ${issue.id}`);
lines.push('');
+ if (issue.resolved) {
+ lines.push('```resolved');
+ lines.push(issue.resolved);
+ lines.push('```');
+ lines.push('');
+ }
+
if (issue.caveat) {
lines.push('```caveat');
lines.push(issue.caveat);
diff --git a/web/js/process.js b/web/js/process.js
index 02e56d5..7dcc102 100644
--- a/web/js/process.js
+++ b/web/js/process.js
@@ -10,6 +10,7 @@ let activeDownloadUrl = null;
const PROCESS_FORM_STATE_KEY = 'bugmedley.process.formState.v1';
const ISSUE_ID_PATTERN = /\b((?:[A-Z]{2,6}|WF500)-\d{4,8})\b/i;
const BLOCK_CONTAINER_TAGS = new Set(['ARTICLE', 'ASIDE', 'BLOCKQUOTE', 'DIV', 'P', 'PRE', 'SECTION']);
+const RESOLVED_LINE_PATTERN = /^resolved\s+in\b/i;
function loadProducts() {
fetch('data/products.json')
@@ -60,7 +61,7 @@ function generateJSON() {
return;
}
- const parsedIssues = parseIssuesInput(inputText);
+ const parsedIssues = parseIssuesFromHtmlTable(inputText);
if (parsedIssues.length === 0) {
document.getElementById('markdownOutput').value = '';
@@ -72,6 +73,7 @@ function generateJSON() {
const issues = parsedIssues.map(issue => ({
id: issue.id,
description: issue.description,
+ resolved: issue.resolved || '',
caveat: issue.caveat || ''
}));
@@ -87,15 +89,6 @@ function generateJSON() {
setParseStatus(`Parsed ${issues.length} issues from input.`);
}
-function parseIssuesInput(inputText) {
- const fromHtml = parseIssuesFromHtmlTable(inputText);
- if (fromHtml.length > 0) {
- return fromHtml;
- }
-
- return parseIssuesFromLinePairs(inputText);
-}
-
function parseIssuesFromHtmlTable(htmlText) {
const parser = new DOMParser();
const doc = parser.parseFromString(htmlText, 'text/html');
@@ -105,16 +98,20 @@ function parseIssuesFromHtmlTable(htmlText) {
return [];
}
- const rows = table.querySelectorAll(':scope > tr, :scope > tbody > tr, :scope > thead > tr, :scope > tfoot > tr');
+ const rows = Array.from(table.querySelectorAll('tr'));
const issues = [];
rows.forEach(row => {
- const cells = row.querySelectorAll('td, th');
+ const cells = Array.from(row.children).filter(cell => {
+ const tag = (cell.tagName || '').toUpperCase();
+ return tag === 'TD' || tag === 'TH';
+ });
+
if (cells.length < 2) {
return;
}
- const leftCellText = normalizeWhitespace(cells[0].textContent || '');
+ const leftCellText = extractCellText(cells[0]);
const issueDetails = extractIssueDetails(cells[1]);
const rightCellText = issueDetails.description;
@@ -128,6 +125,10 @@ function parseIssuesFromHtmlTable(htmlText) {
}
const id = issueIdMatch[1].toUpperCase();
+ const leftCellTrailingText = normalizeWhitespace(
+ leftCellText.replace(issueIdMatch[0], '').replace(/^[-:;,.\s]+/, '')
+ );
+ const resolved = RESOLVED_LINE_PATTERN.test(leftCellTrailingText) ? leftCellTrailingText : '';
const description = rightCellText;
if (!description) {
@@ -137,7 +138,8 @@ function parseIssuesFromHtmlTable(htmlText) {
issues.push({
id,
description,
- caveat: issueDetails.caveat
+ resolved,
+ caveat: issueDetails.caveat || (resolved ? '' : leftCellTrailingText)
});
});
@@ -330,33 +332,6 @@ function sanitizeTableCellText(text) {
return normalizeWhitespace(text).replace(/\|/g, '\\|');
}
-function parseIssuesFromLinePairs(rawText) {
- const lines = rawText
- .split(/\r?\n/)
- .map(line => line.trim())
- .filter(Boolean);
-
- const issues = [];
-
- for (let i = 0; i < lines.length; i += 2) {
- if (i + 1 >= lines.length) {
- continue;
- }
-
- const issueIdMatch = lines[i].match(ISSUE_ID_PATTERN);
- if (!issueIdMatch) {
- continue;
- }
-
- issues.push({
- id: issueIdMatch[1].toUpperCase(),
- description: lines[i + 1]
- });
- }
-
- return issues;
-}
-
function normalizeWhitespace(value) {
return value.replace(/\s+/g, ' ').trim();
}