@wix/web5-core 1.59.0 → 1.60.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.
Files changed (58) hide show
  1. package/dist/cjs/component/componentDefinitions/ComparisonSectionDefinition.js +33 -1
  2. package/dist/cjs/component/componentDefinitions/ComparisonSectionDefinition.js.map +1 -1
  3. package/dist/cjs/component/componentDefinitions/FeatureSection9PlusDefinition.js +9 -5
  4. package/dist/cjs/component/componentDefinitions/FeatureSection9PlusDefinition.js.map +1 -1
  5. package/dist/cjs/component/componentDefinitions/listItemsSectionDefinition.js +10 -5
  6. package/dist/cjs/component/componentDefinitions/listItemsSectionDefinition.js.map +1 -1
  7. package/dist/cjs/component/componentDefinitions/parse-utils.js +45 -8
  8. package/dist/cjs/component/componentDefinitions/parse-utils.js.map +1 -1
  9. package/dist/cjs/component/componentParser.js +15 -0
  10. package/dist/cjs/component/componentParser.js.map +1 -1
  11. package/dist/cjs/component/section-definition.js +14 -0
  12. package/dist/cjs/component/section-definition.js.map +1 -1
  13. package/dist/cjs/components/ui/MarkdownText.js +41 -42
  14. package/dist/cjs/components/ui/MarkdownText.js.map +1 -1
  15. package/dist/cjs/components/ui/UnifiedLink.js +22 -10
  16. package/dist/cjs/components/ui/UnifiedLink.js.map +1 -1
  17. package/dist/cjs/index.js +5 -3
  18. package/dist/cjs/index.js.map +1 -1
  19. package/dist/cjs/utils/markdownPreprocessor.js +162 -8
  20. package/dist/cjs/utils/markdownPreprocessor.js.map +1 -1
  21. package/dist/esm/component/componentDefinitions/ComparisonSectionDefinition.js +34 -1
  22. package/dist/esm/component/componentDefinitions/ComparisonSectionDefinition.js.map +1 -1
  23. package/dist/esm/component/componentDefinitions/FeatureSection9PlusDefinition.js +10 -6
  24. package/dist/esm/component/componentDefinitions/FeatureSection9PlusDefinition.js.map +1 -1
  25. package/dist/esm/component/componentDefinitions/listItemsSectionDefinition.js +11 -6
  26. package/dist/esm/component/componentDefinitions/listItemsSectionDefinition.js.map +1 -1
  27. package/dist/esm/component/componentDefinitions/parse-utils.js +44 -8
  28. package/dist/esm/component/componentDefinitions/parse-utils.js.map +1 -1
  29. package/dist/esm/component/componentParser.js +15 -0
  30. package/dist/esm/component/componentParser.js.map +1 -1
  31. package/dist/esm/component/section-definition.js +13 -1
  32. package/dist/esm/component/section-definition.js.map +1 -1
  33. package/dist/esm/components/ui/MarkdownText.js +8 -9
  34. package/dist/esm/components/ui/MarkdownText.js.map +1 -1
  35. package/dist/esm/components/ui/UnifiedLink.js +7 -1
  36. package/dist/esm/components/ui/UnifiedLink.js.map +1 -1
  37. package/dist/esm/index.js +2 -0
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/utils/markdownPreprocessor.js +160 -8
  40. package/dist/esm/utils/markdownPreprocessor.js.map +1 -1
  41. package/dist/types/component/componentDefinitions/ComparisonSectionDefinition.d.ts +2 -2
  42. package/dist/types/component/componentDefinitions/ComparisonSectionDefinition.d.ts.map +1 -1
  43. package/dist/types/component/componentDefinitions/FeatureSection9PlusDefinition.d.ts.map +1 -1
  44. package/dist/types/component/componentDefinitions/listItemsSectionDefinition.d.ts.map +1 -1
  45. package/dist/types/component/componentDefinitions/parse-utils.d.ts +10 -0
  46. package/dist/types/component/componentDefinitions/parse-utils.d.ts.map +1 -1
  47. package/dist/types/component/componentParser.d.ts.map +1 -1
  48. package/dist/types/component/section-definition.d.ts +13 -1
  49. package/dist/types/component/section-definition.d.ts.map +1 -1
  50. package/dist/types/components/ui/MarkdownText.d.ts +13 -0
  51. package/dist/types/components/ui/MarkdownText.d.ts.map +1 -1
  52. package/dist/types/components/ui/UnifiedLink.d.ts +1 -1
  53. package/dist/types/components/ui/UnifiedLink.d.ts.map +1 -1
  54. package/dist/types/index.d.ts +2 -1
  55. package/dist/types/index.d.ts.map +1 -1
  56. package/dist/types/utils/markdownPreprocessor.d.ts +30 -5
  57. package/dist/types/utils/markdownPreprocessor.d.ts.map +1 -1
  58. package/package.json +2 -2
@@ -6,7 +6,22 @@ exports.ComparisonSectionDefinition = void 0;
6
6
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
7
7
  var _parseUtils = require("./parse-utils");
8
8
  var _parts = require("../../parts/parts");
9
+ var _sectionDefinition = require("../section-definition");
9
10
  var _diagnosticTypes = require("../diagnosticTypes");
11
+ /**
12
+ * A cell the writer left blank: empty, or a dash / "n/a" placeholder standing in
13
+ * for "we have no fact here".
14
+ *
15
+ * This has to be judged on the RAW cell text, before `parseTableValue`, because
16
+ * that maps an empty cell to `false` — the very same value a deliberate "No"
17
+ * produces. After parsing, "no data" and "no, it does not have that" are
18
+ * indistinguishable.
19
+ */
20
+ function isBlankCell(raw) {
21
+ const v = (raw ?? '').trim().toLowerCase();
22
+ // U+2010–U+2015 are the hyphen/dash family; U+2212 is minus.
23
+ return v === '' || v === 'n/a' || /^[-‐-―−]+$/.test(v);
24
+ }
10
25
  class ComparisonSectionDefinition {
11
26
  constructor() {
12
27
  (0, _defineProperty2.default)(this, "sectionType", 'comparison');
@@ -87,11 +102,14 @@ See how our platform stacks up against the competition.
87
102
  let columns;
88
103
  let featureColumnLabel;
89
104
  let rows;
105
+ /** The cells exactly as written, index-aligned with `rows`. */
106
+ let rawValueRows;
90
107
  if ((tableMeta == null ? void 0 : tableMeta.columnLabels) !== undefined) {
91
108
  // SDK nodesToParts format
92
109
  columnLabels = tableMeta.columnLabels;
93
110
  columns = tableMeta.columns;
94
111
  featureColumnLabel = tableMeta.featureColumnLabel || undefined;
112
+ rawValueRows = (tableMeta.rows ?? []).map(row => row.values);
95
113
  rows = (tableMeta.rows ?? []).map(row => ({
96
114
  feature: row.feature,
97
115
  ...(row.iconHint ? {
@@ -108,6 +126,7 @@ See how our platform stacks up against the competition.
108
126
  columns = columnLabels.map(label => ({
109
127
  label
110
128
  }));
129
+ rawValueRows = rawRows.map(row => row.slice(1));
111
130
  rows = rawRows.map(row => ({
112
131
  feature: row[0] ?? '',
113
132
  values: row.slice(1).map(_parseUtils.parseTableValue)
@@ -120,11 +139,24 @@ See how our platform stacks up against the competition.
120
139
  const ctaHref = (link == null ? void 0 : link.href) || undefined;
121
140
 
122
141
  // Filter out rows where all values are empty (e.g. trailing non-table text parsed as a row)
123
- const filteredRows = rows.filter(row => row.values.some(v => v !== ''));
142
+ const keptRow = rawValueRows.map(raw => raw.some(v => !isBlankCell(v)));
143
+ const filteredRows = rows.filter((_, i) => keptRow[i]);
144
+ const filteredRawRows = rawValueRows.filter((_, i) => keptRow[i]);
124
145
  const droppedCount = rows.length - filteredRows.length;
125
146
  if (droppedCount > 0) {
126
147
  context == null || context.reportDiagnostic == null || context.reportDiagnostic(_diagnosticTypes.DIAGNOSTIC_TYPES.ITEMS_DROPPED, `comparison: ${droppedCount} row(s) dropped (all values empty)`);
127
148
  }
149
+
150
+ // A column blank in every row is a side of the comparison the writer had no
151
+ // facts for — it renders as a full column of dashes next to a populated one,
152
+ // which reads as a real answer and is not one. Half a comparison is worse
153
+ // than none, so drop the whole section. (No rows left at all lands here too:
154
+ // every column is vacuously blank.)
155
+ const blankColumns = columnLabels.map((_, col) => col).filter(col => filteredRawRows.every(raw => isBlankCell(raw[col])));
156
+ if (columnLabels.length > 0 && blankColumns.length > 0) {
157
+ context == null || context.reportDiagnostic == null || context.reportDiagnostic(_diagnosticTypes.DIAGNOSTIC_TYPES.SECTION_REJECTED, `comparison: column(s) ${blankColumns.map(c => columnLabels[c] || `#${c + 1}`).join(', ')} blank across all ${filteredRawRows.length} row(s) — section dropped`);
158
+ return _sectionDefinition.DROP_SECTION;
159
+ }
128
160
  return {
129
161
  title,
130
162
  description,
@@ -1 +1 @@
1
- {"version":3,"names":["_parseUtils","require","_parts","_diagnosticTypes","ComparisonSectionDefinition","constructor","_defineProperty2","default","title","columnLabels","rows","feature","values","markdown","expected","featureColumnLabel","description","ctaText","ctaHref","parse","parts","context","_tableParts$","headingText","bodyText","undefined","tableParts","getPartsByType","tableMeta","meta","columns","map","row","iconHint","parseTableValue","allHeaders","headers","rawRows","slice","label","link","firstLink","text","href","filteredRows","filter","some","v","droppedCount","length","reportDiagnostic","DIAGNOSTIC_TYPES","ITEMS_DROPPED","exports"],"sources":["../../../../src/component/componentDefinitions/ComparisonSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n bodyText,\n firstLink,\n parseTableValue,\n} from './parse-utils';\nimport { getPartsByType, type Part } from '../../parts/parts';\nimport type {\n ComparisonColumn,\n ComparisonCompProps,\n ComparisonRow,\n ComparisonSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition, ParseContext } from '../section-definition';\nimport { DIAGNOSTIC_TYPES } from '../diagnosticTypes';\n\nexport class ComparisonSectionDefinition\n implements SectionDefinition<ComparisonSectionComponent>\n{\n sectionType = 'comparison' as const;\n patterns = [\n 'html_comment[section^=\"comparison\"] > (h2 | h3 | h4 | t) > html_comment? > t* > table > (t | link)?',\n ];\n\n defaults: ComparisonCompProps = {\n title: 'Comparison',\n columnLabels: ['Option A', 'Option B'],\n rows: [\n { feature: 'Price', values: ['$10', '$20'] },\n { feature: 'Storage', values: ['100GB', '1TB'] },\n ],\n };\n\n fixtures: SectionFixture<ComparisonCompProps>[] = [\n {\n markdown: `<!-- section: comparison -->\n## Compare Plans\n\n| Feature | Basic | Pro |\n|---------|-------|-----|\n| Users | 5 | Unlimited |\n| Storage | 100GB | 1TB |`,\n expected: {\n title: 'Compare Plans',\n featureColumnLabel: 'Feature',\n columnLabels: ['Basic', 'Pro'],\n rows: [\n { feature: 'Users', values: ['5', 'Unlimited'] },\n { feature: 'Storage', values: ['100GB', '1TB'] },\n ],\n },\n },\n {\n markdown: `<!-- section: comparison -->\n## Platform Comparison\n\nSee how our platform stacks up against the competition.\n\n| Feature | Our Platform | Competitor |\n|---------|--------------|------------|\n| Storage | 1TB | 100GB |\n| Users | Unlimited | 5 |\n| Support | 24/7 Priority | Email Only |\n\n[Get started](#signup)`,\n expected: {\n title: 'Platform Comparison',\n description: 'See how our platform stacks up against the competition.',\n featureColumnLabel: 'Feature',\n columnLabels: ['Our Platform', 'Competitor'],\n rows: [\n { feature: 'Storage', values: ['1TB', '100GB'] },\n { feature: 'Users', values: ['Unlimited', '5'] },\n { feature: 'Support', values: ['24/7 Priority', 'Email Only'] },\n ],\n ctaText: 'Get started',\n ctaHref: '#signup',\n },\n },\n ];\n\n parse(parts: Part[], context?: ParseContext): ComparisonCompProps {\n const title =\n headingText(parts, 2) ||\n headingText(parts, 3) ||\n headingText(parts, 4) ||\n 'Comparison';\n const description = bodyText(parts) || undefined;\n\n // Table meta from nodesToParts: { columnLabels, featureColumnLabel, rows: { feature, values }[] }\n const tableParts = getPartsByType<Part>(parts, 'table');\n const tableMeta = tableParts[0]?.meta as\n | {\n columnLabels?: string[];\n columns?: ComparisonColumn[];\n featureColumnLabel?: string;\n rows?: { feature: string; iconHint?: string; values: string[] }[];\n // Legacy format from partsConverter\n headers?: string[];\n }\n | undefined;\n\n let columnLabels: string[];\n let columns: ComparisonColumn[] | undefined;\n let featureColumnLabel: string | undefined;\n let rows: ComparisonRow[];\n\n if (tableMeta?.columnLabels !== undefined) {\n // SDK nodesToParts format\n columnLabels = tableMeta.columnLabels;\n columns = tableMeta.columns;\n featureColumnLabel = tableMeta.featureColumnLabel || undefined;\n rows = (tableMeta.rows ?? []).map((row) => ({\n feature: row.feature,\n ...(row.iconHint ? { iconHint: row.iconHint } : {}),\n values: row.values.map(parseTableValue),\n }));\n } else {\n // Legacy partsConverter format: { headers: string[], rows: string[][] }\n const allHeaders = tableMeta?.headers ?? [];\n const rawRows = (tableMeta as any)?.rows ?? [];\n featureColumnLabel = allHeaders[0] || undefined;\n columnLabels = allHeaders.slice(1);\n columns = columnLabels.map((label) => ({ label }));\n rows = rawRows.map((row: string[]) => ({\n feature: row[0] ?? '',\n values: row.slice(1).map(parseTableValue),\n }));\n }\n\n // Extract CTA from link parts\n const link = firstLink(parts);\n const ctaText = link?.text || undefined;\n const ctaHref = link?.href || undefined;\n\n // Filter out rows where all values are empty (e.g. trailing non-table text parsed as a row)\n const filteredRows = rows.filter((row) => row.values.some((v) => v !== ''));\n const droppedCount = rows.length - filteredRows.length;\n if (droppedCount > 0) {\n context?.reportDiagnostic?.(\n DIAGNOSTIC_TYPES.ITEMS_DROPPED,\n `comparison: ${droppedCount} row(s) dropped (all values empty)`,\n );\n }\n\n return {\n title,\n description,\n columnLabels,\n columns,\n rows: filteredRows,\n featureColumnLabel,\n ctaText,\n ctaHref,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_parseUtils","require","_parts","_sectionDefinition","_diagnosticTypes","isBlankCell","raw","v","trim","toLowerCase","test","ComparisonSectionDefinition","constructor","_defineProperty2","default","title","columnLabels","rows","feature","values","markdown","expected","featureColumnLabel","description","ctaText","ctaHref","parse","parts","context","_tableParts$","headingText","bodyText","undefined","tableParts","getPartsByType","tableMeta","meta","columns","rawValueRows","map","row","iconHint","parseTableValue","allHeaders","headers","rawRows","slice","label","link","firstLink","text","href","keptRow","some","filteredRows","filter","_","i","filteredRawRows","droppedCount","length","reportDiagnostic","DIAGNOSTIC_TYPES","ITEMS_DROPPED","blankColumns","col","every","SECTION_REJECTED","c","join","DROP_SECTION","exports"],"sources":["../../../../src/component/componentDefinitions/ComparisonSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n bodyText,\n firstLink,\n parseTableValue,\n} from './parse-utils';\nimport { getPartsByType, type Part } from '../../parts/parts';\nimport type {\n ComparisonColumn,\n ComparisonCompProps,\n ComparisonRow,\n ComparisonSectionComponent,\n SectionFixture,\n} from '../types';\nimport type {\n SectionDefinition,\n ParseContext,\n DropSection,\n} from '../section-definition';\nimport { DROP_SECTION } from '../section-definition';\nimport { DIAGNOSTIC_TYPES } from '../diagnosticTypes';\n\n/**\n * A cell the writer left blank: empty, or a dash / \"n/a\" placeholder standing in\n * for \"we have no fact here\".\n *\n * This has to be judged on the RAW cell text, before `parseTableValue`, because\n * that maps an empty cell to `false` — the very same value a deliberate \"No\"\n * produces. After parsing, \"no data\" and \"no, it does not have that\" are\n * indistinguishable.\n */\nfunction isBlankCell(raw: string | undefined): boolean {\n const v = (raw ?? '').trim().toLowerCase();\n // U+2010–U+2015 are the hyphen/dash family; U+2212 is minus.\n return v === '' || v === 'n/a' || /^[-‐-―−]+$/.test(v);\n}\n\nexport class ComparisonSectionDefinition\n implements SectionDefinition<ComparisonSectionComponent>\n{\n sectionType = 'comparison' as const;\n patterns = [\n 'html_comment[section^=\"comparison\"] > (h2 | h3 | h4 | t) > html_comment? > t* > table > (t | link)?',\n ];\n\n defaults: ComparisonCompProps = {\n title: 'Comparison',\n columnLabels: ['Option A', 'Option B'],\n rows: [\n { feature: 'Price', values: ['$10', '$20'] },\n { feature: 'Storage', values: ['100GB', '1TB'] },\n ],\n };\n\n fixtures: SectionFixture<ComparisonCompProps>[] = [\n {\n markdown: `<!-- section: comparison -->\n## Compare Plans\n\n| Feature | Basic | Pro |\n|---------|-------|-----|\n| Users | 5 | Unlimited |\n| Storage | 100GB | 1TB |`,\n expected: {\n title: 'Compare Plans',\n featureColumnLabel: 'Feature',\n columnLabels: ['Basic', 'Pro'],\n rows: [\n { feature: 'Users', values: ['5', 'Unlimited'] },\n { feature: 'Storage', values: ['100GB', '1TB'] },\n ],\n },\n },\n {\n markdown: `<!-- section: comparison -->\n## Platform Comparison\n\nSee how our platform stacks up against the competition.\n\n| Feature | Our Platform | Competitor |\n|---------|--------------|------------|\n| Storage | 1TB | 100GB |\n| Users | Unlimited | 5 |\n| Support | 24/7 Priority | Email Only |\n\n[Get started](#signup)`,\n expected: {\n title: 'Platform Comparison',\n description: 'See how our platform stacks up against the competition.',\n featureColumnLabel: 'Feature',\n columnLabels: ['Our Platform', 'Competitor'],\n rows: [\n { feature: 'Storage', values: ['1TB', '100GB'] },\n { feature: 'Users', values: ['Unlimited', '5'] },\n { feature: 'Support', values: ['24/7 Priority', 'Email Only'] },\n ],\n ctaText: 'Get started',\n ctaHref: '#signup',\n },\n },\n ];\n\n parse(\n parts: Part[],\n context?: ParseContext,\n ): ComparisonCompProps | DropSection {\n const title =\n headingText(parts, 2) ||\n headingText(parts, 3) ||\n headingText(parts, 4) ||\n 'Comparison';\n const description = bodyText(parts) || undefined;\n\n // Table meta from nodesToParts: { columnLabels, featureColumnLabel, rows: { feature, values }[] }\n const tableParts = getPartsByType<Part>(parts, 'table');\n const tableMeta = tableParts[0]?.meta as\n | {\n columnLabels?: string[];\n columns?: ComparisonColumn[];\n featureColumnLabel?: string;\n rows?: { feature: string; iconHint?: string; values: string[] }[];\n // Legacy format from partsConverter\n headers?: string[];\n }\n | undefined;\n\n let columnLabels: string[];\n let columns: ComparisonColumn[] | undefined;\n let featureColumnLabel: string | undefined;\n let rows: ComparisonRow[];\n /** The cells exactly as written, index-aligned with `rows`. */\n let rawValueRows: string[][];\n\n if (tableMeta?.columnLabels !== undefined) {\n // SDK nodesToParts format\n columnLabels = tableMeta.columnLabels;\n columns = tableMeta.columns;\n featureColumnLabel = tableMeta.featureColumnLabel || undefined;\n rawValueRows = (tableMeta.rows ?? []).map((row) => row.values);\n rows = (tableMeta.rows ?? []).map((row) => ({\n feature: row.feature,\n ...(row.iconHint ? { iconHint: row.iconHint } : {}),\n values: row.values.map(parseTableValue),\n }));\n } else {\n // Legacy partsConverter format: { headers: string[], rows: string[][] }\n const allHeaders = tableMeta?.headers ?? [];\n const rawRows = (tableMeta as any)?.rows ?? [];\n featureColumnLabel = allHeaders[0] || undefined;\n columnLabels = allHeaders.slice(1);\n columns = columnLabels.map((label) => ({ label }));\n rawValueRows = rawRows.map((row: string[]) => row.slice(1));\n rows = rawRows.map((row: string[]) => ({\n feature: row[0] ?? '',\n values: row.slice(1).map(parseTableValue),\n }));\n }\n\n // Extract CTA from link parts\n const link = firstLink(parts);\n const ctaText = link?.text || undefined;\n const ctaHref = link?.href || undefined;\n\n // Filter out rows where all values are empty (e.g. trailing non-table text parsed as a row)\n const keptRow = rawValueRows.map((raw) => raw.some((v) => !isBlankCell(v)));\n const filteredRows = rows.filter((_, i) => keptRow[i]);\n const filteredRawRows = rawValueRows.filter((_, i) => keptRow[i]);\n const droppedCount = rows.length - filteredRows.length;\n if (droppedCount > 0) {\n context?.reportDiagnostic?.(\n DIAGNOSTIC_TYPES.ITEMS_DROPPED,\n `comparison: ${droppedCount} row(s) dropped (all values empty)`,\n );\n }\n\n // A column blank in every row is a side of the comparison the writer had no\n // facts for — it renders as a full column of dashes next to a populated one,\n // which reads as a real answer and is not one. Half a comparison is worse\n // than none, so drop the whole section. (No rows left at all lands here too:\n // every column is vacuously blank.)\n const blankColumns = columnLabels\n .map((_, col) => col)\n .filter((col) =>\n filteredRawRows.every((raw) => isBlankCell(raw[col])),\n );\n if (columnLabels.length > 0 && blankColumns.length > 0) {\n context?.reportDiagnostic?.(\n DIAGNOSTIC_TYPES.SECTION_REJECTED,\n `comparison: column(s) ${blankColumns\n .map((c) => columnLabels[c] || `#${c + 1}`)\n .join(', ')} blank across all ${filteredRawRows.length} row(s) — section dropped`,\n );\n return DROP_SECTION;\n }\n\n return {\n title,\n description,\n columnLabels,\n columns,\n rows: filteredRows,\n featureColumnLabel,\n ctaText,\n ctaHref,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -26,14 +26,18 @@ class FeatureSection9PlusDefinition {
26
26
  var _list$meta, _item$meta, _iconPart$meta, _item$meta2;
27
27
  const itemParts = ((_item$meta = item.meta) == null ? void 0 : _item$meta.parts) ?? [];
28
28
  const heading = itemParts.find(p => p.type === 'heading');
29
- const textParts = itemParts.filter(p => p.type === 'text' || p.type === 'richText');
30
- const featureDescription = textParts.map(p => {
31
- var _p$content;
32
- return (_p$content = p.content) == null ? void 0 : _p$content.trim();
33
- }).filter(Boolean).join('\n');
34
29
  const iconPart = itemParts.find(p => p.type === 'icon');
35
30
  const iconHint = iconPart == null || (_iconPart$meta = iconPart.meta) == null ? void 0 : _iconPart$meta.iconHint;
36
31
 
32
+ // Everything except the heading and the leading icon is the body.
33
+ // Serialize it rather than concatenating `content`: filtering to text
34
+ // parts used to drop `link` parts wholesale, deleting an inline link's
35
+ // url and label together. Same defect as listItemsSectionDefinition —
36
+ // this definition sits below that one in every registry, but the seed's
37
+ // FeatureIconsSection is registered against the other definition while
38
+ // typed on this one's props, so both have to behave the same.
39
+ const featureDescription = (0, _parseUtils.inlinePartsToMarkdown)(itemParts.filter(p => p !== heading && p !== iconPart));
40
+
37
41
  // Try to parse bold title from markdown (preserves **bold**) or plain content
38
42
  // Strip leading icon markdown (e.g., "![](web5://icon/hint) ") before bold matching
39
43
  const rawMarkdown = (_item$meta2 = item.meta) == null || (_item$meta2 = _item$meta2.markdown) == null ? void 0 : _item$meta2.trim();
@@ -1 +1 @@
1
- {"version":3,"names":["_parseUtils","require","_parts","FeatureSection9PlusDefinition","constructor","_defineProperty2","default","title","features","parse","parts","headingText","description","bodyText","undefined","lists","getPartsByType","list","item","_list$meta","meta","items","_item$meta","_iconPart$meta","_item$meta2","itemParts","heading","find","p","type","textParts","filter","featureDescription","map","_p$content","content","trim","Boolean","join","iconPart","iconHint","rawMarkdown","markdown","replace","extractListItemText","boldMatch","match","iconField","_heading$content","push","exports"],"sources":["../../../../src/component/componentDefinitions/FeatureSection9PlusDefinition.ts"],"sourcesContent":["import {\n headingText,\n bodyText,\n extractListItemText,\n} from './parse-utils';\nimport {\n getPartsByType,\n type Part,\n type ListPart,\n type IconPart,\n} from '../../parts/parts';\nimport type {\n FeatureSection9PlusCompProps,\n FeatureSection9PlusSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\nexport class FeatureSection9PlusDefinition\n implements SectionDefinition<FeatureSection9PlusSectionComponent>\n{\n sectionType = 'featureSection9Plus' as const;\n patterns = [\n 'html_comment? > (h2 | h3) > t? > list{item[2-]:(icon | h3 | t | link)* > t+}',\n ];\n\n defaults: FeatureSection9PlusCompProps = {\n title: 'Features',\n features: [],\n };\n\n fixtures: SectionFixture<FeatureSection9PlusCompProps>[] = [];\n\n parse(parts: Part[]): FeatureSection9PlusCompProps {\n const title = headingText(parts, 2) || headingText(parts, 3) || 'Features';\n const description = bodyText(parts) || undefined;\n\n const features: {\n title?: string;\n description: string;\n iconHint?: string;\n }[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const heading = itemParts.find((p) => p.type === 'heading');\n const textParts = itemParts.filter(\n (p) => p.type === 'text' || p.type === 'richText',\n );\n const featureDescription = textParts\n .map((p) => p.content?.trim())\n .filter(Boolean)\n .join('\\n');\n\n const iconPart = itemParts.find((p) => p.type === 'icon') as\n | IconPart\n | undefined;\n const iconHint = (iconPart?.meta as { iconHint?: string })?.iconHint;\n\n // Try to parse bold title from markdown (preserves **bold**) or plain content\n // Strip leading icon markdown (e.g., \"![](web5://icon/hint) \") before bold matching\n const rawMarkdown = (item.meta?.markdown as string)?.trim();\n const markdown = rawMarkdown\n ?.replace(/^!\\[[^\\]]*\\]\\([^)]*\\)\\s*/, '')\n .trim();\n const content = markdown || extractListItemText(item);\n const boldMatch = content.match(/^\\*\\*([^*]+)\\*\\*:?\\s*(.*)/s);\n\n const iconField = iconHint ? { iconHint } : {};\n\n if (heading) {\n features.push({\n title: heading.content?.trim(),\n description: featureDescription || content,\n ...iconField,\n });\n } else if (boldMatch) {\n features.push({\n title: boldMatch[1].trim(),\n description: boldMatch[2].trim() || featureDescription,\n ...iconField,\n });\n } else {\n features.push({\n description: featureDescription || content,\n ...iconField,\n });\n }\n }\n }\n\n return { title, description, features };\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_parseUtils","require","_parts","FeatureSection9PlusDefinition","constructor","_defineProperty2","default","title","features","parse","parts","headingText","description","bodyText","undefined","lists","getPartsByType","list","item","_list$meta","meta","items","_item$meta","_iconPart$meta","_item$meta2","itemParts","heading","find","p","type","iconPart","iconHint","featureDescription","inlinePartsToMarkdown","filter","rawMarkdown","markdown","trim","replace","content","extractListItemText","boldMatch","match","iconField","_heading$content","push","exports"],"sources":["../../../../src/component/componentDefinitions/FeatureSection9PlusDefinition.ts"],"sourcesContent":["import {\n headingText,\n bodyText,\n extractListItemText,\n inlinePartsToMarkdown,\n} from './parse-utils';\nimport {\n getPartsByType,\n type Part,\n type ListPart,\n type IconPart,\n} from '../../parts/parts';\nimport type {\n FeatureSection9PlusCompProps,\n FeatureSection9PlusSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\nexport class FeatureSection9PlusDefinition\n implements SectionDefinition<FeatureSection9PlusSectionComponent>\n{\n sectionType = 'featureSection9Plus' as const;\n patterns = [\n 'html_comment? > (h2 | h3) > t? > list{item[2-]:(icon | h3 | t | link)* > t+}',\n ];\n\n defaults: FeatureSection9PlusCompProps = {\n title: 'Features',\n features: [],\n };\n\n fixtures: SectionFixture<FeatureSection9PlusCompProps>[] = [];\n\n parse(parts: Part[]): FeatureSection9PlusCompProps {\n const title = headingText(parts, 2) || headingText(parts, 3) || 'Features';\n const description = bodyText(parts) || undefined;\n\n const features: {\n title?: string;\n description: string;\n iconHint?: string;\n }[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const heading = itemParts.find((p) => p.type === 'heading');\n\n const iconPart = itemParts.find((p) => p.type === 'icon') as\n | IconPart\n | undefined;\n const iconHint = (iconPart?.meta as { iconHint?: string })?.iconHint;\n\n // Everything except the heading and the leading icon is the body.\n // Serialize it rather than concatenating `content`: filtering to text\n // parts used to drop `link` parts wholesale, deleting an inline link's\n // url and label together. Same defect as listItemsSectionDefinition —\n // this definition sits below that one in every registry, but the seed's\n // FeatureIconsSection is registered against the other definition while\n // typed on this one's props, so both have to behave the same.\n const featureDescription = inlinePartsToMarkdown(\n itemParts.filter((p) => p !== heading && p !== iconPart),\n );\n\n // Try to parse bold title from markdown (preserves **bold**) or plain content\n // Strip leading icon markdown (e.g., \"![](web5://icon/hint) \") before bold matching\n const rawMarkdown = (item.meta?.markdown as string)?.trim();\n const markdown = rawMarkdown\n ?.replace(/^!\\[[^\\]]*\\]\\([^)]*\\)\\s*/, '')\n .trim();\n const content = markdown || extractListItemText(item);\n const boldMatch = content.match(/^\\*\\*([^*]+)\\*\\*:?\\s*(.*)/s);\n\n const iconField = iconHint ? { iconHint } : {};\n\n if (heading) {\n features.push({\n title: heading.content?.trim(),\n description: featureDescription || content,\n ...iconField,\n });\n } else if (boldMatch) {\n features.push({\n title: boldMatch[1].trim(),\n description: boldMatch[2].trim() || featureDescription,\n ...iconField,\n });\n } else {\n features.push({\n description: featureDescription || content,\n ...iconField,\n });\n }\n }\n }\n\n return { title, description, features };\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -39,11 +39,16 @@ class ListItemsSectionDefinition {
39
39
  var _list$meta, _item$meta, _item$meta2;
40
40
  const itemParts = ((_item$meta = item.meta) == null ? void 0 : _item$meta.parts) ?? [];
41
41
  const heading = itemParts.find(p => p.type === 'heading');
42
- const textParts = itemParts.filter(p => p.type === 'text' || p.type === 'richText');
43
- const featureDescription = textParts.map(p => {
44
- var _p$content;
45
- return (_p$content = p.content) == null ? void 0 : _p$content.trim();
46
- }).filter(Boolean).join('\n');
42
+ // Everything except the heading is the body. Serialize it rather than
43
+ // concatenating `content`, so an inline link survives: filtering to
44
+ // text parts used to drop `link` parts wholesale, deleting both the
45
+ // url and its label from the rendered item.
46
+ //
47
+ // The item's own `meta.markdown` is not usable here — for a
48
+ // `- ### Title` item it runs the heading straight into the body with no
49
+ // separator ("Gentle daily washRead the ..."), so it would duplicate
50
+ // and mangle the title.
51
+ const featureDescription = (0, _parseUtils.inlinePartsToMarkdown)(itemParts.filter(p => p !== heading));
47
52
  const markdown = (_item$meta2 = item.meta) == null || (_item$meta2 = _item$meta2.markdown) == null ? void 0 : _item$meta2.trim();
48
53
  const content = markdown || (0, _parseUtils.extractListItemText)(item);
49
54
  const boldMatch = content.match(/^\*\*([^*]+)\*\*:?\s*(.*)/s);
@@ -1 +1 @@
1
- {"version":3,"names":["_parseUtils","require","_parts","MIN_FEATURES","ListItemsSectionDefinition","constructor","_defineProperty2","default","title","features","parse","parts","headingText","lists","getPartsByType","list","item","_list$meta","meta","items","_item$meta","_item$meta2","itemParts","heading","find","p","type","textParts","filter","featureDescription","map","_p$content","content","trim","Boolean","join","markdown","extractListItemText","boldMatch","match","_heading$content","push","description","length","exports"],"sources":["../../../../src/component/componentDefinitions/listItemsSectionDefinition.ts"],"sourcesContent":["import { headingText, extractListItemText } from './parse-utils';\nimport { getPartsByType, type Part, type ListPart } from '../../parts/parts';\nimport type {\n ListItemsSectionCompProps,\n ListItemsSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\n/** A valid list section needs at least this many feature items. */\nconst MIN_FEATURES = 2;\n\nexport class ListItemsSectionDefinition\n implements SectionDefinition<ListItemsSectionComponent>\n{\n sectionType = 'listItemsSection' as const;\n\n // Comment-delimited: the section spans from a `<!-- section:list -->` marker\n // and greedily captures the tokens that follow. The repetition deliberately\n // excludes `html_comment`, so the match naturally ends just before the next\n // section marker (or end of input) without consuming it — no explicit `~`\n // stop clause needed, and it still matches when this is the final section.\n // The structural contract is enforced in `parse()` instead of the pattern:\n // `parse()` returns null unless the content is an h2 followed by a 2+ item\n // feature list, so a non-conforming block falls through to the next\n // matching definition.\n patterns = [\n 'html_comment[section=\"list\"] > (h2 | h3 | t | callout | list | image | link | table | blockquote)*',\n ];\n\n defaults: ListItemsSectionCompProps = {\n title: 'Features',\n features: [],\n };\n\n fixtures: SectionFixture<ListItemsSectionCompProps>[] = [];\n\n parse(parts: Part[]): ListItemsSectionCompProps | null {\n const title = headingText(parts, 2);\n if (!title) {\n return null;\n }\n\n const features: { title?: string; description: string }[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const heading = itemParts.find((p) => p.type === 'heading');\n const textParts = itemParts.filter(\n (p) => p.type === 'text' || p.type === 'richText',\n );\n const featureDescription = textParts\n .map((p) => p.content?.trim())\n .filter(Boolean)\n .join('\\n');\n\n const markdown = (item.meta?.markdown as string)?.trim();\n const content = markdown || extractListItemText(item);\n const boldMatch = content.match(/^\\*\\*([^*]+)\\*\\*:?\\s*(.*)/s);\n\n if (heading) {\n features.push({\n title: heading.content?.trim(),\n description: featureDescription || content,\n });\n } else if (boldMatch) {\n features.push({\n title: boldMatch[1].trim(),\n description: boldMatch[2].trim() || featureDescription,\n });\n } else {\n features.push({\n description: featureDescription || content,\n });\n }\n }\n }\n\n // Valid structure is an h2 title followed by a 2+ item feature list.\n // Anything else is not this section — let the next definition try.\n if (features.length < MIN_FEATURES) {\n return null;\n }\n\n return { title, features };\n }\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_parseUtils","require","_parts","MIN_FEATURES","ListItemsSectionDefinition","constructor","_defineProperty2","default","title","features","parse","parts","headingText","lists","getPartsByType","list","item","_list$meta","meta","items","_item$meta","_item$meta2","itemParts","heading","find","p","type","featureDescription","inlinePartsToMarkdown","filter","markdown","trim","content","extractListItemText","boldMatch","match","_heading$content","push","description","length","exports"],"sources":["../../../../src/component/componentDefinitions/listItemsSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n extractListItemText,\n inlinePartsToMarkdown,\n} from './parse-utils';\nimport { getPartsByType, type Part, type ListPart } from '../../parts/parts';\nimport type {\n ListItemsSectionCompProps,\n ListItemsSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\n/** A valid list section needs at least this many feature items. */\nconst MIN_FEATURES = 2;\n\nexport class ListItemsSectionDefinition\n implements SectionDefinition<ListItemsSectionComponent>\n{\n sectionType = 'listItemsSection' as const;\n\n // Comment-delimited: the section spans from a `<!-- section:list -->` marker\n // and greedily captures the tokens that follow. The repetition deliberately\n // excludes `html_comment`, so the match naturally ends just before the next\n // section marker (or end of input) without consuming it — no explicit `~`\n // stop clause needed, and it still matches when this is the final section.\n // The structural contract is enforced in `parse()` instead of the pattern:\n // `parse()` returns null unless the content is an h2 followed by a 2+ item\n // feature list, so a non-conforming block falls through to the next\n // matching definition.\n patterns = [\n 'html_comment[section=\"list\"] > (h2 | h3 | t | callout | list | image | link | table | blockquote)*',\n ];\n\n defaults: ListItemsSectionCompProps = {\n title: 'Features',\n features: [],\n };\n\n fixtures: SectionFixture<ListItemsSectionCompProps>[] = [];\n\n parse(parts: Part[]): ListItemsSectionCompProps | null {\n const title = headingText(parts, 2);\n if (!title) {\n return null;\n }\n\n const features: { title?: string; description: string }[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const heading = itemParts.find((p) => p.type === 'heading');\n // Everything except the heading is the body. Serialize it rather than\n // concatenating `content`, so an inline link survives: filtering to\n // text parts used to drop `link` parts wholesale, deleting both the\n // url and its label from the rendered item.\n //\n // The item's own `meta.markdown` is not usable here — for a\n // `- ### Title` item it runs the heading straight into the body with no\n // separator (\"Gentle daily washRead the ...\"), so it would duplicate\n // and mangle the title.\n const featureDescription = inlinePartsToMarkdown(\n itemParts.filter((p) => p !== heading),\n );\n\n const markdown = (item.meta?.markdown as string)?.trim();\n const content = markdown || extractListItemText(item);\n const boldMatch = content.match(/^\\*\\*([^*]+)\\*\\*:?\\s*(.*)/s);\n\n if (heading) {\n features.push({\n title: heading.content?.trim(),\n description: featureDescription || content,\n });\n } else if (boldMatch) {\n features.push({\n title: boldMatch[1].trim(),\n description: boldMatch[2].trim() || featureDescription,\n });\n } else {\n features.push({\n description: featureDescription || content,\n });\n }\n }\n }\n\n // Valid structure is an h2 title followed by a 2+ item feature list.\n // Anything else is not this section — let the next definition try.\n if (features.length < MIN_FEATURES) {\n return null;\n }\n\n return { title, features };\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -12,6 +12,7 @@ exports.firstLink = firstLink;
12
12
  exports.generatePartId = generatePartId;
13
13
  exports.headingMarkdown = headingMarkdown;
14
14
  exports.headingText = headingText;
15
+ exports.inlinePartsToMarkdown = inlinePartsToMarkdown;
15
16
  exports.linksToHeroButtons = linksToHeroButtons;
16
17
  exports.listToMarkdown = listToMarkdown;
17
18
  exports.parseCalloutTag = parseCalloutTag;
@@ -225,6 +226,42 @@ function parseKpiItem(markdown) {
225
226
  description
226
227
  };
227
228
  }
229
+
230
+ /** Join inline tokens the way prose reads: single spaces, none before punctuation. */
231
+ function appendInlineToken(current, token) {
232
+ const trimmed = token.trim();
233
+ if (!trimmed) {
234
+ return current;
235
+ }
236
+ if (!current) {
237
+ return trimmed;
238
+ }
239
+ if (/^[,.;:!?)]/.test(trimmed)) {
240
+ return `${current}${trimmed}`;
241
+ }
242
+ return `${current} ${trimmed}`;
243
+ }
244
+
245
+ /**
246
+ * Serialize a run of inline parts back to markdown, preserving links.
247
+ *
248
+ * Concatenating `part.content` alone silently deletes links: a `link` part
249
+ * keeps its label in `content` and its url in `meta.href`, so a filter that
250
+ * reads only text parts turns `Read the [pricing page](/pricing) first` into
251
+ * `Read the first` — url and label both vanish, with nothing in the output to
252
+ * show anything was lost.
253
+ */
254
+ function inlinePartsToMarkdown(parts) {
255
+ return parts.reduce((text, part) => {
256
+ if (part.type === 'link') {
257
+ var _meta2, _part$content;
258
+ const href = ((_meta2 = part.meta) == null ? void 0 : _meta2.href) ?? '';
259
+ const label = ((_part$content = part.content) == null ? void 0 : _part$content.trim()) ?? '';
260
+ return appendInlineToken(text, href ? `[${label}](${href})` : label);
261
+ }
262
+ return appendInlineToken(text, part.content ?? '');
263
+ }, '');
264
+ }
228
265
  function extractListItemText(item) {
229
266
  var _item$meta2;
230
267
  if (item.content) {
@@ -324,8 +361,8 @@ function extractEntityRefs(parts) {
324
361
  var _list$meta4, _item$meta4;
325
362
  const itemParts = ((_item$meta4 = item.meta) == null ? void 0 : _item$meta4.parts) ?? [];
326
363
  const entityLinks = itemParts.filter(p => {
327
- var _meta2;
328
- return p.type === 'link' && isEntityHref((_meta2 = p.meta) == null ? void 0 : _meta2.href);
364
+ var _meta3;
365
+ return p.type === 'link' && isEntityHref((_meta3 = p.meta) == null ? void 0 : _meta3.href);
329
366
  });
330
367
  for (const link of entityLinks) {
331
368
  const parsed = parseEntityUrl(link.meta.href);
@@ -384,18 +421,18 @@ function extractEntityRefs(parts) {
384
421
  if (linkIndex >= 0) {
385
422
  // Scan following parts for description, image, callout, and list items
386
423
  for (let j = linkIndex + 1; j < parts.length; j++) {
387
- var _meta3, _p$content3;
424
+ var _meta4, _p$content3;
388
425
  const p = parts[j];
389
426
  // Stop at next entity link
390
- if (p.type === 'link' && isEntityHref((_meta3 = p.meta) == null ? void 0 : _meta3.href)) {
427
+ if (p.type === 'link' && isEntityHref((_meta4 = p.meta) == null ? void 0 : _meta4.href)) {
391
428
  break;
392
429
  }
393
430
  if (p.type === 'text' && (_p$content3 = p.content) != null && _p$content3.trim()) {
394
431
  const text = markdownOrText(p);
395
432
  description = description ? `${description} ${text}` : text;
396
433
  } else if (p.type === 'image' && !markdownImageUrl) {
397
- var _meta4;
398
- markdownImageUrl = (_meta4 = p.meta) == null ? void 0 : _meta4.src;
434
+ var _meta5;
435
+ markdownImageUrl = (_meta5 = p.meta) == null ? void 0 : _meta5.src;
399
436
  } else if ((p.type === 'callout' || p.type === 'blockquote') && !callout) {
400
437
  var _p$meta4;
401
438
  const raw = ((_p$meta4 = p.meta) == null || (_p$meta4 = _p$meta4.markdown) == null ? void 0 : _p$meta4.trim()) || str(p);
@@ -407,8 +444,8 @@ function extractEntityRefs(parts) {
407
444
  };
408
445
  }
409
446
  } else if (p.type === 'list') {
410
- for (const item of ((_meta5 = p.meta) == null ? void 0 : _meta5.items) ?? []) {
411
- var _meta5;
447
+ for (const item of ((_meta6 = p.meta) == null ? void 0 : _meta6.items) ?? []) {
448
+ var _meta6;
412
449
  const text = extractListItemText(item);
413
450
  if (text) {
414
451
  features.push(text);
@@ -1 +1 @@
1
- {"version":3,"names":["_parts","require","_entityLinkParser","_linkTypes","str","p","_p$content","content","trim","markdownOrText","_p$meta","meta","markdown","normalizeEntityDescription","description","replace","stripMarkdown","md","headingText","parts","level","heading","getHeading","headingMarkdown","_meta$markdown","bodyText","textParts","getPartsByType","richTextParts","map","filter","Boolean","join","entitySectionBodyText","firstEntityLinkIndex","findIndex","_meta","type","isEntityHref","href","sectionParts","slice","linksToHeroButtons","links","getLinks","length","lists","flatMap","l","_l$meta","items","item","_item$meta","i","_l$content","id","text","variant","onClick","firstImageSrc","_img$meta","images","getImages","img","src","undefined","firstLink","_link$content","link","blockquoteText","bqs","_p$meta2","calloutFromParts","_p$meta3","calloutParts","raw","parsed","parseCalloutTag","kind","listToMarkdown","lines","list","_list$meta","extractListItemText","push","parseEntityUrl","url","trimmedUrl","isWeb5EntityUrl","parseWeb5Url","Web5UrlType","ENTITY","entityType","entityId","parseTableValue","value","normalized","toLowerCase","parseKpiItem","trimmed","boldMatch","match","title","afterBold","indexOf","_item$meta2","partsToMarkdown","inline","part","_part$meta","_part$meta2","repeat","_list$meta2","_item$meta3","toUpperCase","generatePartId","Math","random","toString","extractNestedListItems","itemParts","nestedLists","features","nested","_list$meta3","extractEntityRefs","entities","_list$meta4","_item$meta4","entityLinks","_meta2","_link$content2","_images$","_p$content2","callout","_calloutParts$0$meta","parsed2","linkText","entityUrl","markdownImageUrl","markdownFeatures","topLinks","_l$meta2","some","e","_link$content3","linkIndex","j","_meta3","_p$content3","_meta4","_p$meta4","parsedCallout","_meta5"],"sources":["../../../../src/component/componentDefinitions/parse-utils.ts"],"sourcesContent":["import type {\n Part,\n ListPart,\n LinkPart,\n ListItemPart,\n ImagePart,\n} from '../../parts/parts';\nimport {\n getPartsByType,\n getHeading,\n getImages,\n getLinks,\n} from '../../parts/parts';\nimport { parseWeb5Url } from '../../utils/entityLinkParser';\nimport { Web5UrlType, isWeb5EntityUrl } from '../../types/link-types';\nimport type { Callout } from '../../types/callout';\nimport type { HeroButton } from '../types';\n\nfunction str(p: Part | undefined): string {\n return p?.content?.trim() ?? '';\n}\n\nfunction markdownOrText(p: Part): string {\n return ((p.meta?.markdown as string)?.trim() || str(p)).trim();\n}\n\nfunction normalizeEntityDescription(description: string): string {\n return description\n .replace(/^[\\s\\u2013\\u2014-]+/, '')\n .replace(/^[:\\s]+/, '')\n .trim();\n}\n\n/** Strip basic markdown formatting to get plain text */\nexport function stripMarkdown(md: string): string {\n return md\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/\\*(.+?)\\*/g, '$1')\n .replace(/_(.+?)_/g, '$1')\n .replace(/~~(.+?)~~/g, '$1')\n .replace(/`(.+?)`/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .trim();\n}\n\nexport function headingText(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n return str(heading);\n}\n\n/** Like headingText but preserves inline markdown formatting (bold, italic, etc.) */\nexport function headingMarkdown(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n if (!heading) {\n return '';\n }\n const meta = heading.meta as Record<string, unknown>;\n return (meta?.markdown as string)?.trim() || str(heading);\n}\n\nexport function bodyText(parts: Part[]): string {\n const textParts = getPartsByType<Part>(parts, 'text');\n const richTextParts = getPartsByType<Part>(parts, 'richText');\n return [...textParts, ...richTextParts]\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Entity sections may be authored as one paragraph per entity:\n * `[Product](web5://entity/...) - why it matches`.\n * In that shape, text after the first entity link is item copy rather than\n * section copy, so only keep prose that appears before the first entity link.\n */\nexport function entitySectionBodyText(parts: Part[]): string {\n const firstEntityLinkIndex = parts.findIndex(\n (p) => p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n const sectionParts =\n firstEntityLinkIndex >= 0 ? parts.slice(0, firstEntityLinkIndex) : parts;\n\n return sectionParts\n .filter((p) => p.type === 'text' || p.type === 'richText')\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** Build HeroButton[] from link parts — aligned to HeroSection1Button shape */\nexport function linksToHeroButtons(parts: Part[]): HeroButton[] {\n let links = getLinks(parts);\n if (!links.length) {\n const lists = getPartsByType<ListPart>(parts, 'list');\n links = lists.flatMap((l) =>\n (l.meta?.items ?? []).flatMap((item) =>\n ((item.meta?.parts ?? []) as Part[]).filter(\n (p): p is LinkPart => p.type === 'link',\n ),\n ),\n );\n }\n return links.map(\n (l, i): HeroButton => ({\n id: `btn-${i}`,\n text: l.content?.trim() || 'Learn more',\n variant: i === 0 ? 'primary' : 'secondary',\n onClick: l.meta.href,\n }),\n );\n}\n\n/** Return just the src URL string of the first image part */\nexport function firstImageSrc(parts: Part[]): string | undefined {\n const images = getImages(parts);\n const img = images[0];\n return img?.meta?.src || undefined;\n}\n\n/** Return the first link as { text, href } */\nexport function firstLink(\n parts: Part[],\n): { text: string; href: string } | undefined {\n const links = getLinks(parts);\n const link = links[0];\n if (!link) {\n return undefined;\n }\n return { text: link.content?.trim() || '', href: link.meta.href };\n}\n\n/** Extract text content from blockquote parts, preserving markdown formatting when available */\nexport function blockquoteText(parts: Part[]): string | undefined {\n const bqs = getPartsByType<Part>(parts, 'blockquote');\n if (bqs.length === 0) {\n return undefined;\n }\n const text = bqs\n .map((p) => (p.meta?.markdown as string)?.trim() || str(p))\n .filter(Boolean)\n .join('\\n');\n return text || undefined;\n}\n\n/** Extract first callout from parts as a Callout object */\nexport function calloutFromParts(parts: Part[]): Callout | undefined {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n if (calloutParts.length === 0) {\n return undefined;\n }\n const p = calloutParts[0];\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsed = parseCalloutTag(raw);\n return parsed.text ? { text: parsed.text, kind: parsed.kind } : undefined;\n}\n\n/** Convert list parts to a markdown-like string */\nexport function listToMarkdown(parts: Part[]): string | undefined {\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length === 0) {\n return undefined;\n }\n const lines: string[] = [];\n for (const list of lists) {\n const items = list.meta?.items ?? [];\n for (const item of items) {\n const text = extractListItemText(item);\n if (text) {\n lines.push(`- ${text}`);\n }\n }\n }\n return lines.length > 0 ? lines.join('\\n') : undefined;\n}\n\n/**\n * Parse a web5://entity/ URL into entityType and entityId.\n * Delegates to the central `parseWeb5Url` parser.\n */\nexport function parseEntityUrl(\n url: string,\n): { entityType: string; entityId: string } | null {\n const trimmedUrl = url?.trim();\n if (!trimmedUrl || !isWeb5EntityUrl(trimmedUrl)) {\n return null;\n }\n const parsed = parseWeb5Url(trimmedUrl);\n if (\n parsed?.type !== Web5UrlType.ENTITY ||\n !parsed.entityType ||\n !parsed.entityId\n ) {\n return null;\n }\n return { entityType: parsed.entityType, entityId: parsed.entityId };\n}\n\n/**\n * Parse a table cell value into boolean or string.\n * Ported from web50-server-ui comparisonSectionParser.tsx.\n */\nexport function parseTableValue(value: string): boolean | string {\n const normalized = value.trim().toLowerCase();\n\n if (\n normalized === 'true' ||\n normalized === 'yes' ||\n normalized === '✓' ||\n normalized === 'check' ||\n normalized === 'y'\n ) {\n return true;\n }\n if (\n normalized === 'false' ||\n normalized === 'no' ||\n normalized === '✗' ||\n normalized === 'x' ||\n normalized === 'n' ||\n normalized === ''\n ) {\n return false;\n }\n\n return value.trim();\n}\n\n/**\n * Parse a KPI item from markdown text.\n * The first **bold** segment becomes the title; everything after it (leading \":\" stripped) is the description.\n */\nexport function parseKpiItem(\n markdown: string,\n): { title: string; description: string } | null {\n const trimmed = markdown.trim();\n if (!trimmed) {\n return null;\n }\n const boldMatch = trimmed.match(/\\*\\*(.+?)\\*\\*/);\n if (!boldMatch) {\n return null;\n }\n const title = boldMatch[1].trim();\n const afterBold = trimmed.slice(\n trimmed.indexOf(boldMatch[0]) + boldMatch[0].length,\n );\n const description = afterBold.replace(/^[:\\s]+/, '').trim();\n return { title, description };\n}\n\nexport function extractListItemText(item: ListItemPart): string {\n if (item.content) {\n return item.content;\n }\n if (item.meta?.parts) {\n return (item.meta.parts as Part[])\n .map((p) => p.content)\n .filter(Boolean)\n .join(' ');\n }\n return '';\n}\n\n/**\n * Reconstruct markdown-like content from parts.\n *\n * `part.content` is the *plain* text — `nodesToParts` strips inline\n * formatting and preserves the original markdown (bold, italic, links) in\n * `part.meta.markdown` when they differ. Prefer it so markdown-rendering\n * consumers (e.g. the fallback section's ReactMarkdown) keep `**bold**`.\n */\nexport function partsToMarkdown(sectionParts: Part[]): string {\n const inline = (part: Part): string =>\n (part.meta?.markdown as string | undefined) ?? part.content;\n const lines: string[] = [];\n for (const part of sectionParts) {\n if (part.type === 'heading') {\n const level = (part.meta as { level: number })?.level ?? 2;\n lines.push(`${'#'.repeat(level)} ${inline(part)}`);\n } else if (part.type === 'text' || part.type === 'richText') {\n lines.push(inline(part));\n } else if (part.type === 'list') {\n const list = part as ListPart;\n for (const item of list.meta?.items ?? []) {\n lines.push(\n `- ${(item.meta?.markdown as string | undefined) ?? extractListItemText(item)}`,\n );\n }\n } else if (part.type === 'blockquote' || part.type === 'callout') {\n lines.push(`> ${inline(part)}`);\n }\n }\n return lines.join('\\n\\n');\n}\n\n/**\n * Extract callout kind tag from blockquote text.\n * Handles [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION] GFM syntax.\n */\nexport function parseCalloutTag(text: string): { text: string; kind?: string } {\n const match = text.match(/^\\[!(\\w+)\\]\\s*(.*)/s);\n if (match) {\n return { kind: match[1].toUpperCase(), text: match[2].trim() };\n }\n return { text: text.trim() };\n}\n\n/** Generate a simple unique ID */\nexport function generatePartId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction isEntityHref(href: string | undefined): boolean {\n return !!href && isWeb5EntityUrl(href);\n}\n\nexport interface EntityRef {\n entityId: string;\n entityType: string;\n linkText: string;\n entityUrl: string;\n description: string;\n markdownImageUrl?: string;\n markdownFeatures?: string[];\n callout?: Callout;\n}\n\n/**\n * Extract text from nested list items within a list item's parts.\n */\nfunction extractNestedListItems(itemParts: Part[]): string[] {\n const nestedLists = itemParts.filter((p): p is ListPart => p.type === 'list');\n const features: string[] = [];\n for (const list of nestedLists) {\n for (const nested of list.meta?.items ?? []) {\n const text = extractListItemText(nested);\n if (text) {\n features.push(text);\n }\n }\n }\n return features;\n}\n\n/**\n * Extract entity references from list items containing web5://entity/ links.\n * Also extracts markdownFeatures from nested list items within each entity item.\n */\nexport function extractEntityRefs(parts: Part[]): EntityRef[] {\n const entities: EntityRef[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const entityLinks = itemParts.filter(\n (p): p is LinkPart =>\n p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n\n for (const link of entityLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed) {\n const images = itemParts.filter(\n (p): p is ImagePart => p.type === 'image',\n );\n const textParts = itemParts.filter(\n (p) => p.type === 'text' && p.content?.trim(),\n );\n const description = normalizeEntityDescription(\n textParts.map(markdownOrText).join(' '),\n );\n const features = extractNestedListItems(itemParts);\n const calloutParts = itemParts.filter(\n (p) => p.type === 'callout' || p.type === 'blockquote',\n );\n let callout: Callout | undefined;\n if (calloutParts.length > 0) {\n const raw =\n (calloutParts[0].meta?.markdown as string)?.trim() ||\n str(calloutParts[0]);\n const parsed2 = parseCalloutTag(raw);\n if (parsed2.text) {\n callout = { text: parsed2.text, kind: parsed2.kind };\n }\n }\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description,\n markdownImageUrl: images[0]?.meta?.src,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n }\n }\n\n // Also check top-level links (with surrounding context for description/image/list)\n const topLinks = getLinks(parts).filter((l) => isEntityHref(l.meta?.href));\n for (const link of topLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed && !entities.some((e) => e.entityId === parsed.entityId)) {\n // Look for description text and image after this link in the parts array\n const linkIndex = parts.indexOf(link);\n let description = '';\n let markdownImageUrl: string | undefined;\n let callout: Callout | undefined;\n const features: string[] = [];\n\n if (linkIndex >= 0) {\n // Scan following parts for description, image, callout, and list items\n for (let j = linkIndex + 1; j < parts.length; j++) {\n const p = parts[j];\n // Stop at next entity link\n if (p.type === 'link' && isEntityHref((p as LinkPart).meta?.href)) {\n break;\n }\n if (p.type === 'text' && p.content?.trim()) {\n const text = markdownOrText(p);\n description = description\n ? `${description} ${text}`\n : text;\n } else if (p.type === 'image' && !markdownImageUrl) {\n markdownImageUrl = (p as ImagePart).meta?.src;\n } else if (\n (p.type === 'callout' || p.type === 'blockquote') &&\n !callout\n ) {\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsedCallout = parseCalloutTag(raw);\n if (parsedCallout.text) {\n callout = { text: parsedCallout.text, kind: parsedCallout.kind };\n }\n } else if (p.type === 'list') {\n for (const item of (p as ListPart).meta?.items ?? []) {\n const text = extractListItemText(item);\n if (text) {\n features.push(text);\n }\n }\n }\n }\n }\n\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description: normalizeEntityDescription(description),\n markdownImageUrl,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n\n return entities;\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_parts","require","_entityLinkParser","_linkTypes","str","p","_p$content","content","trim","markdownOrText","_p$meta","meta","markdown","normalizeEntityDescription","description","replace","stripMarkdown","md","headingText","parts","level","heading","getHeading","headingMarkdown","_meta$markdown","bodyText","textParts","getPartsByType","richTextParts","map","filter","Boolean","join","entitySectionBodyText","firstEntityLinkIndex","findIndex","_meta","type","isEntityHref","href","sectionParts","slice","linksToHeroButtons","links","getLinks","length","lists","flatMap","l","_l$meta","items","item","_item$meta","i","_l$content","id","text","variant","onClick","firstImageSrc","_img$meta","images","getImages","img","src","undefined","firstLink","_link$content","link","blockquoteText","bqs","_p$meta2","calloutFromParts","_p$meta3","calloutParts","raw","parsed","parseCalloutTag","kind","listToMarkdown","lines","list","_list$meta","extractListItemText","push","parseEntityUrl","url","trimmedUrl","isWeb5EntityUrl","parseWeb5Url","Web5UrlType","ENTITY","entityType","entityId","parseTableValue","value","normalized","toLowerCase","parseKpiItem","trimmed","boldMatch","match","title","afterBold","indexOf","appendInlineToken","current","token","test","inlinePartsToMarkdown","reduce","part","_meta2","_part$content","label","_item$meta2","partsToMarkdown","inline","_part$meta","_part$meta2","repeat","_list$meta2","_item$meta3","toUpperCase","generatePartId","Math","random","toString","extractNestedListItems","itemParts","nestedLists","features","nested","_list$meta3","extractEntityRefs","entities","_list$meta4","_item$meta4","entityLinks","_meta3","_link$content2","_images$","_p$content2","callout","_calloutParts$0$meta","parsed2","linkText","entityUrl","markdownImageUrl","markdownFeatures","topLinks","_l$meta2","some","e","_link$content3","linkIndex","j","_meta4","_p$content3","_meta5","_p$meta4","parsedCallout","_meta6"],"sources":["../../../../src/component/componentDefinitions/parse-utils.ts"],"sourcesContent":["import type {\n Part,\n ListPart,\n LinkPart,\n ListItemPart,\n ImagePart,\n} from '../../parts/parts';\nimport {\n getPartsByType,\n getHeading,\n getImages,\n getLinks,\n} from '../../parts/parts';\nimport { parseWeb5Url } from '../../utils/entityLinkParser';\nimport { Web5UrlType, isWeb5EntityUrl } from '../../types/link-types';\nimport type { Callout } from '../../types/callout';\nimport type { HeroButton } from '../types';\n\nfunction str(p: Part | undefined): string {\n return p?.content?.trim() ?? '';\n}\n\nfunction markdownOrText(p: Part): string {\n return ((p.meta?.markdown as string)?.trim() || str(p)).trim();\n}\n\nfunction normalizeEntityDescription(description: string): string {\n return description\n .replace(/^[\\s\\u2013\\u2014-]+/, '')\n .replace(/^[:\\s]+/, '')\n .trim();\n}\n\n/** Strip basic markdown formatting to get plain text */\nexport function stripMarkdown(md: string): string {\n return md\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1')\n .replace(/__(.+?)__/g, '$1')\n .replace(/\\*(.+?)\\*/g, '$1')\n .replace(/_(.+?)_/g, '$1')\n .replace(/~~(.+?)~~/g, '$1')\n .replace(/`(.+?)`/g, '$1')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .trim();\n}\n\nexport function headingText(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n return str(heading);\n}\n\n/** Like headingText but preserves inline markdown formatting (bold, italic, etc.) */\nexport function headingMarkdown(parts: Part[], level?: number): string {\n const heading = getHeading(parts, level);\n if (!heading) {\n return '';\n }\n const meta = heading.meta as Record<string, unknown>;\n return (meta?.markdown as string)?.trim() || str(heading);\n}\n\nexport function bodyText(parts: Part[]): string {\n const textParts = getPartsByType<Part>(parts, 'text');\n const richTextParts = getPartsByType<Part>(parts, 'richText');\n return [...textParts, ...richTextParts]\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/**\n * Entity sections may be authored as one paragraph per entity:\n * `[Product](web5://entity/...) - why it matches`.\n * In that shape, text after the first entity link is item copy rather than\n * section copy, so only keep prose that appears before the first entity link.\n */\nexport function entitySectionBodyText(parts: Part[]): string {\n const firstEntityLinkIndex = parts.findIndex(\n (p) => p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n const sectionParts =\n firstEntityLinkIndex >= 0 ? parts.slice(0, firstEntityLinkIndex) : parts;\n\n return sectionParts\n .filter((p) => p.type === 'text' || p.type === 'richText')\n .map(markdownOrText)\n .filter(Boolean)\n .join('\\n\\n');\n}\n\n/** Build HeroButton[] from link parts — aligned to HeroSection1Button shape */\nexport function linksToHeroButtons(parts: Part[]): HeroButton[] {\n let links = getLinks(parts);\n if (!links.length) {\n const lists = getPartsByType<ListPart>(parts, 'list');\n links = lists.flatMap((l) =>\n (l.meta?.items ?? []).flatMap((item) =>\n ((item.meta?.parts ?? []) as Part[]).filter(\n (p): p is LinkPart => p.type === 'link',\n ),\n ),\n );\n }\n return links.map(\n (l, i): HeroButton => ({\n id: `btn-${i}`,\n text: l.content?.trim() || 'Learn more',\n variant: i === 0 ? 'primary' : 'secondary',\n onClick: l.meta.href,\n }),\n );\n}\n\n/** Return just the src URL string of the first image part */\nexport function firstImageSrc(parts: Part[]): string | undefined {\n const images = getImages(parts);\n const img = images[0];\n return img?.meta?.src || undefined;\n}\n\n/** Return the first link as { text, href } */\nexport function firstLink(\n parts: Part[],\n): { text: string; href: string } | undefined {\n const links = getLinks(parts);\n const link = links[0];\n if (!link) {\n return undefined;\n }\n return { text: link.content?.trim() || '', href: link.meta.href };\n}\n\n/** Extract text content from blockquote parts, preserving markdown formatting when available */\nexport function blockquoteText(parts: Part[]): string | undefined {\n const bqs = getPartsByType<Part>(parts, 'blockquote');\n if (bqs.length === 0) {\n return undefined;\n }\n const text = bqs\n .map((p) => (p.meta?.markdown as string)?.trim() || str(p))\n .filter(Boolean)\n .join('\\n');\n return text || undefined;\n}\n\n/** Extract first callout from parts as a Callout object */\nexport function calloutFromParts(parts: Part[]): Callout | undefined {\n const calloutParts = getPartsByType<Part>(parts, 'callout');\n if (calloutParts.length === 0) {\n return undefined;\n }\n const p = calloutParts[0];\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsed = parseCalloutTag(raw);\n return parsed.text ? { text: parsed.text, kind: parsed.kind } : undefined;\n}\n\n/** Convert list parts to a markdown-like string */\nexport function listToMarkdown(parts: Part[]): string | undefined {\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length === 0) {\n return undefined;\n }\n const lines: string[] = [];\n for (const list of lists) {\n const items = list.meta?.items ?? [];\n for (const item of items) {\n const text = extractListItemText(item);\n if (text) {\n lines.push(`- ${text}`);\n }\n }\n }\n return lines.length > 0 ? lines.join('\\n') : undefined;\n}\n\n/**\n * Parse a web5://entity/ URL into entityType and entityId.\n * Delegates to the central `parseWeb5Url` parser.\n */\nexport function parseEntityUrl(\n url: string,\n): { entityType: string; entityId: string } | null {\n const trimmedUrl = url?.trim();\n if (!trimmedUrl || !isWeb5EntityUrl(trimmedUrl)) {\n return null;\n }\n const parsed = parseWeb5Url(trimmedUrl);\n if (\n parsed?.type !== Web5UrlType.ENTITY ||\n !parsed.entityType ||\n !parsed.entityId\n ) {\n return null;\n }\n return { entityType: parsed.entityType, entityId: parsed.entityId };\n}\n\n/**\n * Parse a table cell value into boolean or string.\n * Ported from web50-server-ui comparisonSectionParser.tsx.\n */\nexport function parseTableValue(value: string): boolean | string {\n const normalized = value.trim().toLowerCase();\n\n if (\n normalized === 'true' ||\n normalized === 'yes' ||\n normalized === '✓' ||\n normalized === 'check' ||\n normalized === 'y'\n ) {\n return true;\n }\n if (\n normalized === 'false' ||\n normalized === 'no' ||\n normalized === '✗' ||\n normalized === 'x' ||\n normalized === 'n' ||\n normalized === ''\n ) {\n return false;\n }\n\n return value.trim();\n}\n\n/**\n * Parse a KPI item from markdown text.\n * The first **bold** segment becomes the title; everything after it (leading \":\" stripped) is the description.\n */\nexport function parseKpiItem(\n markdown: string,\n): { title: string; description: string } | null {\n const trimmed = markdown.trim();\n if (!trimmed) {\n return null;\n }\n const boldMatch = trimmed.match(/\\*\\*(.+?)\\*\\*/);\n if (!boldMatch) {\n return null;\n }\n const title = boldMatch[1].trim();\n const afterBold = trimmed.slice(\n trimmed.indexOf(boldMatch[0]) + boldMatch[0].length,\n );\n const description = afterBold.replace(/^[:\\s]+/, '').trim();\n return { title, description };\n}\n\n/** Join inline tokens the way prose reads: single spaces, none before punctuation. */\nfunction appendInlineToken(current: string, token: string): string {\n const trimmed = token.trim();\n if (!trimmed) {\n return current;\n }\n if (!current) {\n return trimmed;\n }\n if (/^[,.;:!?)]/.test(trimmed)) {\n return `${current}${trimmed}`;\n }\n return `${current} ${trimmed}`;\n}\n\n/**\n * Serialize a run of inline parts back to markdown, preserving links.\n *\n * Concatenating `part.content` alone silently deletes links: a `link` part\n * keeps its label in `content` and its url in `meta.href`, so a filter that\n * reads only text parts turns `Read the [pricing page](/pricing) first` into\n * `Read the first` — url and label both vanish, with nothing in the output to\n * show anything was lost.\n */\nexport function inlinePartsToMarkdown(parts: Part[]): string {\n return parts.reduce((text, part) => {\n if (part.type === 'link') {\n const href = (part as LinkPart).meta?.href ?? '';\n const label = part.content?.trim() ?? '';\n return appendInlineToken(text, href ? `[${label}](${href})` : label);\n }\n return appendInlineToken(text, part.content ?? '');\n }, '');\n}\n\nexport function extractListItemText(item: ListItemPart): string {\n if (item.content) {\n return item.content;\n }\n if (item.meta?.parts) {\n return (item.meta.parts as Part[])\n .map((p) => p.content)\n .filter(Boolean)\n .join(' ');\n }\n return '';\n}\n\n/**\n * Reconstruct markdown-like content from parts.\n *\n * `part.content` is the *plain* text — `nodesToParts` strips inline\n * formatting and preserves the original markdown (bold, italic, links) in\n * `part.meta.markdown` when they differ. Prefer it so markdown-rendering\n * consumers (e.g. the fallback section's ReactMarkdown) keep `**bold**`.\n */\nexport function partsToMarkdown(sectionParts: Part[]): string {\n const inline = (part: Part): string =>\n (part.meta?.markdown as string | undefined) ?? part.content;\n const lines: string[] = [];\n for (const part of sectionParts) {\n if (part.type === 'heading') {\n const level = (part.meta as { level: number })?.level ?? 2;\n lines.push(`${'#'.repeat(level)} ${inline(part)}`);\n } else if (part.type === 'text' || part.type === 'richText') {\n lines.push(inline(part));\n } else if (part.type === 'list') {\n const list = part as ListPart;\n for (const item of list.meta?.items ?? []) {\n lines.push(\n `- ${(item.meta?.markdown as string | undefined) ?? extractListItemText(item)}`,\n );\n }\n } else if (part.type === 'blockquote' || part.type === 'callout') {\n lines.push(`> ${inline(part)}`);\n }\n }\n return lines.join('\\n\\n');\n}\n\n/**\n * Extract callout kind tag from blockquote text.\n * Handles [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION] GFM syntax.\n */\nexport function parseCalloutTag(text: string): { text: string; kind?: string } {\n const match = text.match(/^\\[!(\\w+)\\]\\s*(.*)/s);\n if (match) {\n return { kind: match[1].toUpperCase(), text: match[2].trim() };\n }\n return { text: text.trim() };\n}\n\n/** Generate a simple unique ID */\nexport function generatePartId(): string {\n return Math.random().toString(36).slice(2, 10);\n}\n\nfunction isEntityHref(href: string | undefined): boolean {\n return !!href && isWeb5EntityUrl(href);\n}\n\nexport interface EntityRef {\n entityId: string;\n entityType: string;\n linkText: string;\n entityUrl: string;\n description: string;\n markdownImageUrl?: string;\n markdownFeatures?: string[];\n callout?: Callout;\n}\n\n/**\n * Extract text from nested list items within a list item's parts.\n */\nfunction extractNestedListItems(itemParts: Part[]): string[] {\n const nestedLists = itemParts.filter((p): p is ListPart => p.type === 'list');\n const features: string[] = [];\n for (const list of nestedLists) {\n for (const nested of list.meta?.items ?? []) {\n const text = extractListItemText(nested);\n if (text) {\n features.push(text);\n }\n }\n }\n return features;\n}\n\n/**\n * Extract entity references from list items containing web5://entity/ links.\n * Also extracts markdownFeatures from nested list items within each entity item.\n */\nexport function extractEntityRefs(parts: Part[]): EntityRef[] {\n const entities: EntityRef[] = [];\n const lists = getPartsByType<ListPart>(parts, 'list');\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\n const entityLinks = itemParts.filter(\n (p): p is LinkPart =>\n p.type === 'link' && isEntityHref((p as LinkPart).meta?.href),\n );\n\n for (const link of entityLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed) {\n const images = itemParts.filter(\n (p): p is ImagePart => p.type === 'image',\n );\n const textParts = itemParts.filter(\n (p) => p.type === 'text' && p.content?.trim(),\n );\n const description = normalizeEntityDescription(\n textParts.map(markdownOrText).join(' '),\n );\n const features = extractNestedListItems(itemParts);\n const calloutParts = itemParts.filter(\n (p) => p.type === 'callout' || p.type === 'blockquote',\n );\n let callout: Callout | undefined;\n if (calloutParts.length > 0) {\n const raw =\n (calloutParts[0].meta?.markdown as string)?.trim() ||\n str(calloutParts[0]);\n const parsed2 = parseCalloutTag(raw);\n if (parsed2.text) {\n callout = { text: parsed2.text, kind: parsed2.kind };\n }\n }\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description,\n markdownImageUrl: images[0]?.meta?.src,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n }\n }\n\n // Also check top-level links (with surrounding context for description/image/list)\n const topLinks = getLinks(parts).filter((l) => isEntityHref(l.meta?.href));\n for (const link of topLinks) {\n const parsed = parseEntityUrl(link.meta.href);\n if (parsed && !entities.some((e) => e.entityId === parsed.entityId)) {\n // Look for description text and image after this link in the parts array\n const linkIndex = parts.indexOf(link);\n let description = '';\n let markdownImageUrl: string | undefined;\n let callout: Callout | undefined;\n const features: string[] = [];\n\n if (linkIndex >= 0) {\n // Scan following parts for description, image, callout, and list items\n for (let j = linkIndex + 1; j < parts.length; j++) {\n const p = parts[j];\n // Stop at next entity link\n if (p.type === 'link' && isEntityHref((p as LinkPart).meta?.href)) {\n break;\n }\n if (p.type === 'text' && p.content?.trim()) {\n const text = markdownOrText(p);\n description = description\n ? `${description} ${text}`\n : text;\n } else if (p.type === 'image' && !markdownImageUrl) {\n markdownImageUrl = (p as ImagePart).meta?.src;\n } else if (\n (p.type === 'callout' || p.type === 'blockquote') &&\n !callout\n ) {\n const raw = (p.meta?.markdown as string)?.trim() || str(p);\n const parsedCallout = parseCalloutTag(raw);\n if (parsedCallout.text) {\n callout = { text: parsedCallout.text, kind: parsedCallout.kind };\n }\n } else if (p.type === 'list') {\n for (const item of (p as ListPart).meta?.items ?? []) {\n const text = extractListItemText(item);\n if (text) {\n features.push(text);\n }\n }\n }\n }\n }\n\n entities.push({\n entityId: parsed.entityId,\n entityType: parsed.entityType,\n linkText: link.content?.trim() || '',\n entityUrl: link.meta.href,\n description: normalizeEntityDescription(description),\n markdownImageUrl,\n markdownFeatures: features.length > 0 ? features : undefined,\n callout,\n });\n }\n }\n\n return entities;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -19,6 +19,7 @@ var _markdownPreprocessor = require("../utils/markdownPreprocessor");
19
19
  var _intentExtractor = require("../utils/intentExtractor");
20
20
  var _contentKeywordMatcher = require("../utils/contentKeywordMatcher");
21
21
  var _propsExtractor = require("../utils/propsExtractor");
22
+ var _sectionDefinition = require("./section-definition");
22
23
  /**
23
24
  * Markdown → component orchestrator. Lifted from
24
25
  * `web50-server-ui/src/utils/componentParser.ts` per DL #088 B9.5.
@@ -329,6 +330,20 @@ function tryParseComponent(nodes, sectionIndex, context) {
329
330
  } : undefined
330
331
  };
331
332
  let props = definition.parse(parts, parseContext);
333
+
334
+ // The definition claimed the block and then decided its content is not
335
+ // worth rendering. Consume the nodes and emit no component — returning
336
+ // `null` here instead would let the same heading and table fall through
337
+ // to the raw-markdown fallback, which is the outcome the drop exists to
338
+ // avoid.
339
+ if (props === _sectionDefinition.DROP_SECTION) {
340
+ if (trace) {
341
+ (0, _matchDebug.logMatchDebug)(` ${sectionType}: matched ${parsedNodes} node(s) but parse() dropped the section — rendering nothing`);
342
+ }
343
+ return {
344
+ parsedCount: parsedNodes
345
+ };
346
+ }
332
347
  if (props === null) {
333
348
  if (trace) {
334
349
  (0, _matchDebug.logMatchDebug)(` ${sectionType}: pattern matched ${parsedNodes} node(s) but parse() returned null — falling through`);