@wix/web5-core 1.63.31 → 1.63.32
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.
- package/dist/cjs/component/componentDefinitions/ComparisonSectionDefinition.js +20 -3
- package/dist/cjs/component/componentDefinitions/ComparisonSectionDefinition.js.map +1 -1
- package/dist/cjs/component/componentDefinitions/NextStepsSectionDefinition.js +17 -2
- package/dist/cjs/component/componentDefinitions/NextStepsSectionDefinition.js.map +1 -1
- package/dist/esm/component/componentDefinitions/ComparisonSectionDefinition.js +20 -3
- package/dist/esm/component/componentDefinitions/ComparisonSectionDefinition.js.map +1 -1
- package/dist/esm/component/componentDefinitions/NextStepsSectionDefinition.js +17 -2
- package/dist/esm/component/componentDefinitions/NextStepsSectionDefinition.js.map +1 -1
- package/dist/types/component/componentDefinitions/ComparisonSectionDefinition.d.ts.map +1 -1
- package/dist/types/component/componentDefinitions/NextStepsSectionDefinition.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -25,7 +25,21 @@ function isBlankCell(raw) {
|
|
|
25
25
|
class ComparisonSectionDefinition {
|
|
26
26
|
constructor() {
|
|
27
27
|
(0, _defineProperty2.default)(this, "sectionType", 'comparison');
|
|
28
|
-
|
|
28
|
+
// The pre-table slot is the description paragraph, so it has to admit
|
|
29
|
+
// `link`: `convertToBlockElements` unwraps a paragraph into its inline
|
|
30
|
+
// nodes, so `Questions? Email support@shop.example for help.` arrives as
|
|
31
|
+
// `t link t` — GFM autolinks a bare email, no author required — and an
|
|
32
|
+
// authored `[men's shoes](web5://ask/…)` does the same. With `t*` the run
|
|
33
|
+
// ended at that `link`, the whole pattern failed, and the section never
|
|
34
|
+
// reached `parse()`: the heading and prose were claimed by `textBlock` and
|
|
35
|
+
// the TABLE fell to `FallbackSectionDefinition`, whose `partsToMarkdown`
|
|
36
|
+
// has no `table` branch — so it rendered as an empty section and the
|
|
37
|
+
// comparison silently vanished.
|
|
38
|
+
//
|
|
39
|
+
// The trailing slot stays `(t | link)?`: it is the CTA, and `parse()` reads
|
|
40
|
+
// it positionally (see `ctaText` below). Widening it would let the section
|
|
41
|
+
// swallow a paragraph belonging to whatever follows.
|
|
42
|
+
(0, _defineProperty2.default)(this, "patterns", ['html_comment[section^="comparison"] > (h2 | h3 | h4 | t) > html_comment? > (t | link)* > table > (t | link)?']);
|
|
29
43
|
(0, _defineProperty2.default)(this, "defaults", {
|
|
30
44
|
title: 'Comparison',
|
|
31
45
|
columnLabels: ['Option A', 'Option B'],
|
|
@@ -133,8 +147,11 @@ See how our platform stacks up against the competition.
|
|
|
133
147
|
}));
|
|
134
148
|
}
|
|
135
149
|
|
|
136
|
-
//
|
|
137
|
-
|
|
150
|
+
// The CTA is the link AFTER the table, not merely the first link in the
|
|
151
|
+
// section — the description may now carry its own inline links, and
|
|
152
|
+
// `firstLink(parts)` would promote one of those to the CTA button.
|
|
153
|
+
const lastTableIndex = parts.map(p => p.type).lastIndexOf('table');
|
|
154
|
+
const link = (0, _parseUtils.firstLink)(lastTableIndex >= 0 ? parts.slice(lastTableIndex + 1) : parts);
|
|
138
155
|
const ctaText = (link == null ? void 0 : link.text) || undefined;
|
|
139
156
|
const ctaHref = (link == null ? void 0 : link.href) || undefined;
|
|
140
157
|
|
|
@@ -1 +1 @@
|
|
|
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","proseBodyMarkdown","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 proseBodyMarkdown,\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 = proseBodyMarkdown(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":[]}
|
|
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","proseBodyMarkdown","undefined","tableParts","getPartsByType","tableMeta","meta","columns","rawValueRows","map","row","iconHint","parseTableValue","allHeaders","headers","rawRows","slice","label","lastTableIndex","p","type","lastIndexOf","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 proseBodyMarkdown,\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 // The pre-table slot is the description paragraph, so it has to admit\n // `link`: `convertToBlockElements` unwraps a paragraph into its inline\n // nodes, so `Questions? Email support@shop.example for help.` arrives as\n // `t link t` — GFM autolinks a bare email, no author required — and an\n // authored `[men's shoes](web5://ask/…)` does the same. With `t*` the run\n // ended at that `link`, the whole pattern failed, and the section never\n // reached `parse()`: the heading and prose were claimed by `textBlock` and\n // the TABLE fell to `FallbackSectionDefinition`, whose `partsToMarkdown`\n // has no `table` branch — so it rendered as an empty section and the\n // comparison silently vanished.\n //\n // The trailing slot stays `(t | link)?`: it is the CTA, and `parse()` reads\n // it positionally (see `ctaText` below). Widening it would let the section\n // swallow a paragraph belonging to whatever follows.\n patterns = [\n 'html_comment[section^=\"comparison\"] > (h2 | h3 | h4 | t) > html_comment? > (t | link)* > 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 = proseBodyMarkdown(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 // The CTA is the link AFTER the table, not merely the first link in the\n // section — the description may now carry its own inline links, and\n // `firstLink(parts)` would promote one of those to the CTA button.\n const lastTableIndex = parts.map((p) => p.type).lastIndexOf('table');\n const link = firstLink(\n lastTableIndex >= 0 ? parts.slice(lastTableIndex + 1) : parts,\n );\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":[]}
|
|
@@ -41,8 +41,23 @@ class NextStepsSectionDefinition {
|
|
|
41
41
|
constructor() {
|
|
42
42
|
(0, _defineProperty2.default)(this, "sectionType", 'nextSteps');
|
|
43
43
|
(0, _defineProperty2.default)(this, "patterns", [
|
|
44
|
-
// Framing sentence
|
|
45
|
-
|
|
44
|
+
// Framing sentence then one chip per bullet — the authored shape. The
|
|
45
|
+
// framing slot is `(t | link)*`, not `t?`, because a paragraph is unwrapped
|
|
46
|
+
// into its inline nodes: `Questions? Email support@shop.example for help.`
|
|
47
|
+
// arrives as `t link t` (GFM autolinks a bare email) and so does an
|
|
48
|
+
// authored `[men's shoes](web5://ask/…)`. With `t?` the run ended at that
|
|
49
|
+
// `link`, this pattern failed, and the legacy pattern below claimed the
|
|
50
|
+
// section instead — turning the prose link into a chip and stranding the
|
|
51
|
+
// real chip list in the fallback as raw bullets.
|
|
52
|
+
//
|
|
53
|
+
// This does NOT steal the legacy pattern's inputs: the `list` here is
|
|
54
|
+
// mandatory, so a section with chips in the prose and no list still falls
|
|
55
|
+
// through. It does change one rare shape — prose that is nothing but links
|
|
56
|
+
// ALONGSIDE a chip list now matches here, and `proseBodyMarkdown` drops
|
|
57
|
+
// link-only paragraphs, so those links are dropped rather than rendered
|
|
58
|
+
// inline. That shape previously stranded the list entirely, so neither
|
|
59
|
+
// outcome was whole; this one at least renders the authored chips.
|
|
60
|
+
'(html_comment[section^="next"] | html_comment[semantic^="next"]) > h2 > image? > (t | link)* > list{item[1-]:(icon | t)* > (t? > link)+}',
|
|
46
61
|
// Legacy inline shape: chips embedded in the prose. Still arrives from
|
|
47
62
|
// sites whose next-steps prompt has not moved to the list shape.
|
|
48
63
|
'(html_comment[section^="next"] | html_comment[semantic^="next"]) > h2 > image? > (t | link)+']);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_parseUtils","require","_parts","linkToMarkdown","link","_link$content","_link$meta","content","trim","meta","href","appendInlineToken","current","token","trimmed","test","inlineBodyMarkdown","parts","body","reduce","part","type","_part$meta","markdown","undefined","NextStepsSectionDefinition","constructor","_defineProperty2","default","title","backgroundImage","steps","expected","parse","headingText","firstImageSrc","chipsInlineInBody","lists","getPartsByType","length","proseBodyMarkdown","list","item","_list$meta","items","_item$meta","_iconPart$meta","itemParts","iconPart","find","p","iconHint","contentFragments","push","_p$content","filter","Boolean","join","extractListItemText","links","getLinks","_link$content2","_link$meta2","text","exports"],"sources":["../../../../src/component/componentDefinitions/NextStepsSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n proseBodyMarkdown,\n extractListItemText,\n firstImageSrc,\n} from './parse-utils';\nimport {\n getPartsByType,\n getLinks,\n type Part,\n type ListPart,\n type LinkPart,\n type IconPart,\n} from '../../parts/parts';\nimport type {\n NextStepsCompProps,\n NextStepsSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\nfunction linkToMarkdown(link: LinkPart): string {\n return `[${link.content?.trim() || ''}](${link.meta?.href || ''})`;\n}\n\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\nfunction inlineBodyMarkdown(parts: Part[]): string | undefined {\n const body = parts.reduce((current, part) => {\n if (part.type === 'link') {\n return appendInlineToken(current, linkToMarkdown(part as LinkPart));\n }\n if (part.type === 'text' || part.type === 'richText') {\n const markdown = (part.meta?.markdown as string | undefined)?.trim();\n return appendInlineToken(current, markdown || part.content || '');\n }\n return current;\n }, '');\n\n return body || undefined;\n}\n\nexport class NextStepsSectionDefinition\n implements SectionDefinition<NextStepsSectionComponent>\n{\n sectionType = 'nextSteps' as const;\n patterns = [\n // Framing sentence (`t?`) then one chip per bullet — the authored shape.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > t? > list{item[1-]:(icon | t)* > (t? > link)+}',\n // Legacy inline shape: chips embedded in the prose. Still arrives from\n // sites whose next-steps prompt has not moved to the list shape.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > (t | link)+',\n ];\n\n defaults: NextStepsCompProps = {\n title: 'Next Steps',\n body: 'Where would you like to go next?',\n backgroundImage: undefined,\n steps: [{ content: '[Get started](/start)' }],\n };\n\n fixtures: SectionFixture<NextStepsCompProps>[] = [\n {\n markdown: `<!-- section:next-steps, semantic:next_steps -->\n\n## Find the right care for mature, sensitive skin\n\nWant to focus on rich hydrators and barrier builders?\n\n- [hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)\n- [gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)`,\n expected: {\n title: 'Find the right care for mature, sensitive skin',\n body: 'Want to focus on rich hydrators and barrier builders?',\n steps: [\n {\n content:\n '[hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)',\n },\n {\n content:\n '[gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)',\n },\n ],\n },\n },\n {\n markdown: `<!-- section: next_steps -->\n\n## Next Steps\n\n- Check out [Documentation](/docs)\n- Try [Examples](/examples)`,\n expected: {\n title: 'Next Steps',\n steps: [\n { content: 'Check out [Documentation](/docs)' },\n { content: 'Try [Examples](/examples)' },\n ],\n },\n },\n ];\n\n parse(parts: Part[]): NextStepsCompProps {\n const title = headingText(parts, 2) || 'Next Steps';\n const backgroundImage = firstImageSrc(parts);\n const steps: { content: string; iconHint?: string }[] = [];\n let body: string | undefined;\n let chipsInlineInBody = false;\n\n // Try list-based pattern first\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length > 0) {\n // The framing sentence is the only top-level prose — list items live\n // under each list's meta.items, so this never picks up a chip.\n body = proseBodyMarkdown(parts) || undefined;\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\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 const contentFragments: string[] = [];\n for (const p of itemParts) {\n if (p.type === 'link') {\n const link = p as LinkPart;\n contentFragments.push(linkToMarkdown(link));\n } else if (p.type === 'text' || p.type === 'richText') {\n contentFragments.push(p.content?.trim() || '');\n }\n }\n const content =\n contentFragments.filter(Boolean).join(' ') ||\n extractListItemText(item);\n if (content.trim()) {\n steps.push({\n content: content.trim(),\n ...(iconHint ? { iconHint } : {}),\n });\n }\n }\n }\n } else {\n body = inlineBodyMarkdown(parts);\n // body carries the chips as inline links — flag it so renderers show the\n // prose alone instead of repeating every chip as a row beneath it.\n chipsInlineInBody = Boolean(body);\n }\n\n // Fallback: inline links pattern\n if (steps.length === 0) {\n const links = getLinks(parts);\n for (const link of links) {\n const text = link.content?.trim();\n const href = link.meta?.href;\n if (text && href) {\n steps.push({ content: linkToMarkdown(link) });\n }\n }\n }\n\n return {\n title,\n ...(body ? { body } : {}),\n ...(chipsInlineInBody ? { chipsInlineInBody } : {}),\n ...(backgroundImage ? { backgroundImage } : {}),\n steps,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["_parseUtils","require","_parts","linkToMarkdown","link","_link$content","_link$meta","content","trim","meta","href","appendInlineToken","current","token","trimmed","test","inlineBodyMarkdown","parts","body","reduce","part","type","_part$meta","markdown","undefined","NextStepsSectionDefinition","constructor","_defineProperty2","default","title","backgroundImage","steps","expected","parse","headingText","firstImageSrc","chipsInlineInBody","lists","getPartsByType","length","proseBodyMarkdown","list","item","_list$meta","items","_item$meta","_iconPart$meta","itemParts","iconPart","find","p","iconHint","contentFragments","push","_p$content","filter","Boolean","join","extractListItemText","links","getLinks","_link$content2","_link$meta2","text","exports"],"sources":["../../../../src/component/componentDefinitions/NextStepsSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n proseBodyMarkdown,\n extractListItemText,\n firstImageSrc,\n} from './parse-utils';\nimport {\n getPartsByType,\n getLinks,\n type Part,\n type ListPart,\n type LinkPart,\n type IconPart,\n} from '../../parts/parts';\nimport type {\n NextStepsCompProps,\n NextStepsSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\nfunction linkToMarkdown(link: LinkPart): string {\n return `[${link.content?.trim() || ''}](${link.meta?.href || ''})`;\n}\n\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\nfunction inlineBodyMarkdown(parts: Part[]): string | undefined {\n const body = parts.reduce((current, part) => {\n if (part.type === 'link') {\n return appendInlineToken(current, linkToMarkdown(part as LinkPart));\n }\n if (part.type === 'text' || part.type === 'richText') {\n const markdown = (part.meta?.markdown as string | undefined)?.trim();\n return appendInlineToken(current, markdown || part.content || '');\n }\n return current;\n }, '');\n\n return body || undefined;\n}\n\nexport class NextStepsSectionDefinition\n implements SectionDefinition<NextStepsSectionComponent>\n{\n sectionType = 'nextSteps' as const;\n patterns = [\n // Framing sentence then one chip per bullet — the authored shape. The\n // framing slot is `(t | link)*`, not `t?`, because a paragraph is unwrapped\n // into its inline nodes: `Questions? Email support@shop.example for help.`\n // arrives as `t link t` (GFM autolinks a bare email) and so does an\n // authored `[men's shoes](web5://ask/…)`. With `t?` the run ended at that\n // `link`, this pattern failed, and the legacy pattern below claimed the\n // section instead — turning the prose link into a chip and stranding the\n // real chip list in the fallback as raw bullets.\n //\n // This does NOT steal the legacy pattern's inputs: the `list` here is\n // mandatory, so a section with chips in the prose and no list still falls\n // through. It does change one rare shape — prose that is nothing but links\n // ALONGSIDE a chip list now matches here, and `proseBodyMarkdown` drops\n // link-only paragraphs, so those links are dropped rather than rendered\n // inline. That shape previously stranded the list entirely, so neither\n // outcome was whole; this one at least renders the authored chips.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > (t | link)* > list{item[1-]:(icon | t)* > (t? > link)+}',\n // Legacy inline shape: chips embedded in the prose. Still arrives from\n // sites whose next-steps prompt has not moved to the list shape.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > (t | link)+',\n ];\n\n defaults: NextStepsCompProps = {\n title: 'Next Steps',\n body: 'Where would you like to go next?',\n backgroundImage: undefined,\n steps: [{ content: '[Get started](/start)' }],\n };\n\n fixtures: SectionFixture<NextStepsCompProps>[] = [\n {\n markdown: `<!-- section:next-steps, semantic:next_steps -->\n\n## Find the right care for mature, sensitive skin\n\nWant to focus on rich hydrators and barrier builders?\n\n- [hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)\n- [gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)`,\n expected: {\n title: 'Find the right care for mature, sensitive skin',\n body: 'Want to focus on rich hydrators and barrier builders?',\n steps: [\n {\n content:\n '[hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)',\n },\n {\n content:\n '[gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)',\n },\n ],\n },\n },\n {\n markdown: `<!-- section: next_steps -->\n\n## Next Steps\n\n- Check out [Documentation](/docs)\n- Try [Examples](/examples)`,\n expected: {\n title: 'Next Steps',\n steps: [\n { content: 'Check out [Documentation](/docs)' },\n { content: 'Try [Examples](/examples)' },\n ],\n },\n },\n ];\n\n parse(parts: Part[]): NextStepsCompProps {\n const title = headingText(parts, 2) || 'Next Steps';\n const backgroundImage = firstImageSrc(parts);\n const steps: { content: string; iconHint?: string }[] = [];\n let body: string | undefined;\n let chipsInlineInBody = false;\n\n // Try list-based pattern first\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length > 0) {\n // The framing sentence is the only top-level prose — list items live\n // under each list's meta.items, so this never picks up a chip.\n body = proseBodyMarkdown(parts) || undefined;\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\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 const contentFragments: string[] = [];\n for (const p of itemParts) {\n if (p.type === 'link') {\n const link = p as LinkPart;\n contentFragments.push(linkToMarkdown(link));\n } else if (p.type === 'text' || p.type === 'richText') {\n contentFragments.push(p.content?.trim() || '');\n }\n }\n const content =\n contentFragments.filter(Boolean).join(' ') ||\n extractListItemText(item);\n if (content.trim()) {\n steps.push({\n content: content.trim(),\n ...(iconHint ? { iconHint } : {}),\n });\n }\n }\n }\n } else {\n body = inlineBodyMarkdown(parts);\n // body carries the chips as inline links — flag it so renderers show the\n // prose alone instead of repeating every chip as a row beneath it.\n chipsInlineInBody = Boolean(body);\n }\n\n // Fallback: inline links pattern\n if (steps.length === 0) {\n const links = getLinks(parts);\n for (const link of links) {\n const text = link.content?.trim();\n const href = link.meta?.href;\n if (text && href) {\n steps.push({ content: linkToMarkdown(link) });\n }\n }\n }\n\n return {\n title,\n ...(body ? { body } : {}),\n ...(chipsInlineInBody ? { chipsInlineInBody } : {}),\n ...(backgroundImage ? { backgroundImage } : {}),\n steps,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -21,7 +21,21 @@ function isBlankCell(raw) {
|
|
|
21
21
|
export class ComparisonSectionDefinition {
|
|
22
22
|
constructor() {
|
|
23
23
|
_defineProperty(this, "sectionType", 'comparison');
|
|
24
|
-
|
|
24
|
+
// The pre-table slot is the description paragraph, so it has to admit
|
|
25
|
+
// `link`: `convertToBlockElements` unwraps a paragraph into its inline
|
|
26
|
+
// nodes, so `Questions? Email support@shop.example for help.` arrives as
|
|
27
|
+
// `t link t` — GFM autolinks a bare email, no author required — and an
|
|
28
|
+
// authored `[men's shoes](web5://ask/…)` does the same. With `t*` the run
|
|
29
|
+
// ended at that `link`, the whole pattern failed, and the section never
|
|
30
|
+
// reached `parse()`: the heading and prose were claimed by `textBlock` and
|
|
31
|
+
// the TABLE fell to `FallbackSectionDefinition`, whose `partsToMarkdown`
|
|
32
|
+
// has no `table` branch — so it rendered as an empty section and the
|
|
33
|
+
// comparison silently vanished.
|
|
34
|
+
//
|
|
35
|
+
// The trailing slot stays `(t | link)?`: it is the CTA, and `parse()` reads
|
|
36
|
+
// it positionally (see `ctaText` below). Widening it would let the section
|
|
37
|
+
// swallow a paragraph belonging to whatever follows.
|
|
38
|
+
_defineProperty(this, "patterns", ['html_comment[section^="comparison"] > (h2 | h3 | h4 | t) > html_comment? > (t | link)* > table > (t | link)?']);
|
|
25
39
|
_defineProperty(this, "defaults", {
|
|
26
40
|
title: 'Comparison',
|
|
27
41
|
columnLabels: ['Option A', 'Option B'],
|
|
@@ -129,8 +143,11 @@ See how our platform stacks up against the competition.
|
|
|
129
143
|
}));
|
|
130
144
|
}
|
|
131
145
|
|
|
132
|
-
//
|
|
133
|
-
|
|
146
|
+
// The CTA is the link AFTER the table, not merely the first link in the
|
|
147
|
+
// section — the description may now carry its own inline links, and
|
|
148
|
+
// `firstLink(parts)` would promote one of those to the CTA button.
|
|
149
|
+
const lastTableIndex = parts.map(p => p.type).lastIndexOf('table');
|
|
150
|
+
const link = firstLink(lastTableIndex >= 0 ? parts.slice(lastTableIndex + 1) : parts);
|
|
134
151
|
const ctaText = (link == null ? void 0 : link.text) || undefined;
|
|
135
152
|
const ctaHref = (link == null ? void 0 : link.href) || undefined;
|
|
136
153
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["headingText","proseBodyMarkdown","firstLink","parseTableValue","getPartsByType","DROP_SECTION","DIAGNOSTIC_TYPES","isBlankCell","raw","v","trim","toLowerCase","test","ComparisonSectionDefinition","constructor","_defineProperty","title","columnLabels","rows","feature","values","markdown","expected","featureColumnLabel","description","ctaText","ctaHref","parse","parts","context","_tableParts$","undefined","tableParts","tableMeta","meta","columns","rawValueRows","map","row","iconHint","allHeaders","headers","rawRows","slice","label","link","text","href","keptRow","some","filteredRows","filter","_","i","filteredRawRows","droppedCount","length","reportDiagnostic","ITEMS_DROPPED","blankColumns","col","every","SECTION_REJECTED","c","join"],"sources":["../../../../src/component/componentDefinitions/ComparisonSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n proseBodyMarkdown,\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 = proseBodyMarkdown(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":[]}
|
|
1
|
+
{"version":3,"names":["headingText","proseBodyMarkdown","firstLink","parseTableValue","getPartsByType","DROP_SECTION","DIAGNOSTIC_TYPES","isBlankCell","raw","v","trim","toLowerCase","test","ComparisonSectionDefinition","constructor","_defineProperty","title","columnLabels","rows","feature","values","markdown","expected","featureColumnLabel","description","ctaText","ctaHref","parse","parts","context","_tableParts$","undefined","tableParts","tableMeta","meta","columns","rawValueRows","map","row","iconHint","allHeaders","headers","rawRows","slice","label","lastTableIndex","p","type","lastIndexOf","link","text","href","keptRow","some","filteredRows","filter","_","i","filteredRawRows","droppedCount","length","reportDiagnostic","ITEMS_DROPPED","blankColumns","col","every","SECTION_REJECTED","c","join"],"sources":["../../../../src/component/componentDefinitions/ComparisonSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n proseBodyMarkdown,\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 // The pre-table slot is the description paragraph, so it has to admit\n // `link`: `convertToBlockElements` unwraps a paragraph into its inline\n // nodes, so `Questions? Email support@shop.example for help.` arrives as\n // `t link t` — GFM autolinks a bare email, no author required — and an\n // authored `[men's shoes](web5://ask/…)` does the same. With `t*` the run\n // ended at that `link`, the whole pattern failed, and the section never\n // reached `parse()`: the heading and prose were claimed by `textBlock` and\n // the TABLE fell to `FallbackSectionDefinition`, whose `partsToMarkdown`\n // has no `table` branch — so it rendered as an empty section and the\n // comparison silently vanished.\n //\n // The trailing slot stays `(t | link)?`: it is the CTA, and `parse()` reads\n // it positionally (see `ctaText` below). Widening it would let the section\n // swallow a paragraph belonging to whatever follows.\n patterns = [\n 'html_comment[section^=\"comparison\"] > (h2 | h3 | h4 | t) > html_comment? > (t | link)* > 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 = proseBodyMarkdown(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 // The CTA is the link AFTER the table, not merely the first link in the\n // section — the description may now carry its own inline links, and\n // `firstLink(parts)` would promote one of those to the CTA button.\n const lastTableIndex = parts.map((p) => p.type).lastIndexOf('table');\n const link = firstLink(\n lastTableIndex >= 0 ? parts.slice(lastTableIndex + 1) : parts,\n );\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":[]}
|
|
@@ -36,8 +36,23 @@ export class NextStepsSectionDefinition {
|
|
|
36
36
|
constructor() {
|
|
37
37
|
_defineProperty(this, "sectionType", 'nextSteps');
|
|
38
38
|
_defineProperty(this, "patterns", [
|
|
39
|
-
// Framing sentence
|
|
40
|
-
|
|
39
|
+
// Framing sentence then one chip per bullet — the authored shape. The
|
|
40
|
+
// framing slot is `(t | link)*`, not `t?`, because a paragraph is unwrapped
|
|
41
|
+
// into its inline nodes: `Questions? Email support@shop.example for help.`
|
|
42
|
+
// arrives as `t link t` (GFM autolinks a bare email) and so does an
|
|
43
|
+
// authored `[men's shoes](web5://ask/…)`. With `t?` the run ended at that
|
|
44
|
+
// `link`, this pattern failed, and the legacy pattern below claimed the
|
|
45
|
+
// section instead — turning the prose link into a chip and stranding the
|
|
46
|
+
// real chip list in the fallback as raw bullets.
|
|
47
|
+
//
|
|
48
|
+
// This does NOT steal the legacy pattern's inputs: the `list` here is
|
|
49
|
+
// mandatory, so a section with chips in the prose and no list still falls
|
|
50
|
+
// through. It does change one rare shape — prose that is nothing but links
|
|
51
|
+
// ALONGSIDE a chip list now matches here, and `proseBodyMarkdown` drops
|
|
52
|
+
// link-only paragraphs, so those links are dropped rather than rendered
|
|
53
|
+
// inline. That shape previously stranded the list entirely, so neither
|
|
54
|
+
// outcome was whole; this one at least renders the authored chips.
|
|
55
|
+
'(html_comment[section^="next"] | html_comment[semantic^="next"]) > h2 > image? > (t | link)* > list{item[1-]:(icon | t)* > (t? > link)+}',
|
|
41
56
|
// Legacy inline shape: chips embedded in the prose. Still arrives from
|
|
42
57
|
// sites whose next-steps prompt has not moved to the list shape.
|
|
43
58
|
'(html_comment[section^="next"] | html_comment[semantic^="next"]) > h2 > image? > (t | link)+']);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["headingText","proseBodyMarkdown","extractListItemText","firstImageSrc","getPartsByType","getLinks","linkToMarkdown","link","_link$content","_link$meta","content","trim","meta","href","appendInlineToken","current","token","trimmed","test","inlineBodyMarkdown","parts","body","reduce","part","type","_part$meta","markdown","undefined","NextStepsSectionDefinition","constructor","_defineProperty","title","backgroundImage","steps","expected","parse","chipsInlineInBody","lists","length","list","item","_list$meta","items","_item$meta","_iconPart$meta","itemParts","iconPart","find","p","iconHint","contentFragments","push","_p$content","filter","Boolean","join","links","_link$content2","_link$meta2","text"],"sources":["../../../../src/component/componentDefinitions/NextStepsSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n proseBodyMarkdown,\n extractListItemText,\n firstImageSrc,\n} from './parse-utils';\nimport {\n getPartsByType,\n getLinks,\n type Part,\n type ListPart,\n type LinkPart,\n type IconPart,\n} from '../../parts/parts';\nimport type {\n NextStepsCompProps,\n NextStepsSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\nfunction linkToMarkdown(link: LinkPart): string {\n return `[${link.content?.trim() || ''}](${link.meta?.href || ''})`;\n}\n\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\nfunction inlineBodyMarkdown(parts: Part[]): string | undefined {\n const body = parts.reduce((current, part) => {\n if (part.type === 'link') {\n return appendInlineToken(current, linkToMarkdown(part as LinkPart));\n }\n if (part.type === 'text' || part.type === 'richText') {\n const markdown = (part.meta?.markdown as string | undefined)?.trim();\n return appendInlineToken(current, markdown || part.content || '');\n }\n return current;\n }, '');\n\n return body || undefined;\n}\n\nexport class NextStepsSectionDefinition\n implements SectionDefinition<NextStepsSectionComponent>\n{\n sectionType = 'nextSteps' as const;\n patterns = [\n // Framing sentence (`t?`) then one chip per bullet — the authored shape.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > t? > list{item[1-]:(icon | t)* > (t? > link)+}',\n // Legacy inline shape: chips embedded in the prose. Still arrives from\n // sites whose next-steps prompt has not moved to the list shape.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > (t | link)+',\n ];\n\n defaults: NextStepsCompProps = {\n title: 'Next Steps',\n body: 'Where would you like to go next?',\n backgroundImage: undefined,\n steps: [{ content: '[Get started](/start)' }],\n };\n\n fixtures: SectionFixture<NextStepsCompProps>[] = [\n {\n markdown: `<!-- section:next-steps, semantic:next_steps -->\n\n## Find the right care for mature, sensitive skin\n\nWant to focus on rich hydrators and barrier builders?\n\n- [hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)\n- [gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)`,\n expected: {\n title: 'Find the right care for mature, sensitive skin',\n body: 'Want to focus on rich hydrators and barrier builders?',\n steps: [\n {\n content:\n '[hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)',\n },\n {\n content:\n '[gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)',\n },\n ],\n },\n },\n {\n markdown: `<!-- section: next_steps -->\n\n## Next Steps\n\n- Check out [Documentation](/docs)\n- Try [Examples](/examples)`,\n expected: {\n title: 'Next Steps',\n steps: [\n { content: 'Check out [Documentation](/docs)' },\n { content: 'Try [Examples](/examples)' },\n ],\n },\n },\n ];\n\n parse(parts: Part[]): NextStepsCompProps {\n const title = headingText(parts, 2) || 'Next Steps';\n const backgroundImage = firstImageSrc(parts);\n const steps: { content: string; iconHint?: string }[] = [];\n let body: string | undefined;\n let chipsInlineInBody = false;\n\n // Try list-based pattern first\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length > 0) {\n // The framing sentence is the only top-level prose — list items live\n // under each list's meta.items, so this never picks up a chip.\n body = proseBodyMarkdown(parts) || undefined;\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\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 const contentFragments: string[] = [];\n for (const p of itemParts) {\n if (p.type === 'link') {\n const link = p as LinkPart;\n contentFragments.push(linkToMarkdown(link));\n } else if (p.type === 'text' || p.type === 'richText') {\n contentFragments.push(p.content?.trim() || '');\n }\n }\n const content =\n contentFragments.filter(Boolean).join(' ') ||\n extractListItemText(item);\n if (content.trim()) {\n steps.push({\n content: content.trim(),\n ...(iconHint ? { iconHint } : {}),\n });\n }\n }\n }\n } else {\n body = inlineBodyMarkdown(parts);\n // body carries the chips as inline links — flag it so renderers show the\n // prose alone instead of repeating every chip as a row beneath it.\n chipsInlineInBody = Boolean(body);\n }\n\n // Fallback: inline links pattern\n if (steps.length === 0) {\n const links = getLinks(parts);\n for (const link of links) {\n const text = link.content?.trim();\n const href = link.meta?.href;\n if (text && href) {\n steps.push({ content: linkToMarkdown(link) });\n }\n }\n }\n\n return {\n title,\n ...(body ? { body } : {}),\n ...(chipsInlineInBody ? { chipsInlineInBody } : {}),\n ...(backgroundImage ? { backgroundImage } : {}),\n steps,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":["headingText","proseBodyMarkdown","extractListItemText","firstImageSrc","getPartsByType","getLinks","linkToMarkdown","link","_link$content","_link$meta","content","trim","meta","href","appendInlineToken","current","token","trimmed","test","inlineBodyMarkdown","parts","body","reduce","part","type","_part$meta","markdown","undefined","NextStepsSectionDefinition","constructor","_defineProperty","title","backgroundImage","steps","expected","parse","chipsInlineInBody","lists","length","list","item","_list$meta","items","_item$meta","_iconPart$meta","itemParts","iconPart","find","p","iconHint","contentFragments","push","_p$content","filter","Boolean","join","links","_link$content2","_link$meta2","text"],"sources":["../../../../src/component/componentDefinitions/NextStepsSectionDefinition.ts"],"sourcesContent":["import {\n headingText,\n proseBodyMarkdown,\n extractListItemText,\n firstImageSrc,\n} from './parse-utils';\nimport {\n getPartsByType,\n getLinks,\n type Part,\n type ListPart,\n type LinkPart,\n type IconPart,\n} from '../../parts/parts';\nimport type {\n NextStepsCompProps,\n NextStepsSectionComponent,\n SectionFixture,\n} from '../types';\nimport type { SectionDefinition } from '../section-definition';\n\nfunction linkToMarkdown(link: LinkPart): string {\n return `[${link.content?.trim() || ''}](${link.meta?.href || ''})`;\n}\n\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\nfunction inlineBodyMarkdown(parts: Part[]): string | undefined {\n const body = parts.reduce((current, part) => {\n if (part.type === 'link') {\n return appendInlineToken(current, linkToMarkdown(part as LinkPart));\n }\n if (part.type === 'text' || part.type === 'richText') {\n const markdown = (part.meta?.markdown as string | undefined)?.trim();\n return appendInlineToken(current, markdown || part.content || '');\n }\n return current;\n }, '');\n\n return body || undefined;\n}\n\nexport class NextStepsSectionDefinition\n implements SectionDefinition<NextStepsSectionComponent>\n{\n sectionType = 'nextSteps' as const;\n patterns = [\n // Framing sentence then one chip per bullet — the authored shape. The\n // framing slot is `(t | link)*`, not `t?`, because a paragraph is unwrapped\n // into its inline nodes: `Questions? Email support@shop.example for help.`\n // arrives as `t link t` (GFM autolinks a bare email) and so does an\n // authored `[men's shoes](web5://ask/…)`. With `t?` the run ended at that\n // `link`, this pattern failed, and the legacy pattern below claimed the\n // section instead — turning the prose link into a chip and stranding the\n // real chip list in the fallback as raw bullets.\n //\n // This does NOT steal the legacy pattern's inputs: the `list` here is\n // mandatory, so a section with chips in the prose and no list still falls\n // through. It does change one rare shape — prose that is nothing but links\n // ALONGSIDE a chip list now matches here, and `proseBodyMarkdown` drops\n // link-only paragraphs, so those links are dropped rather than rendered\n // inline. That shape previously stranded the list entirely, so neither\n // outcome was whole; this one at least renders the authored chips.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > (t | link)* > list{item[1-]:(icon | t)* > (t? > link)+}',\n // Legacy inline shape: chips embedded in the prose. Still arrives from\n // sites whose next-steps prompt has not moved to the list shape.\n '(html_comment[section^=\"next\"] | html_comment[semantic^=\"next\"]) > h2 > image? > (t | link)+',\n ];\n\n defaults: NextStepsCompProps = {\n title: 'Next Steps',\n body: 'Where would you like to go next?',\n backgroundImage: undefined,\n steps: [{ content: '[Get started](/start)' }],\n };\n\n fixtures: SectionFixture<NextStepsCompProps>[] = [\n {\n markdown: `<!-- section:next-steps, semantic:next_steps -->\n\n## Find the right care for mature, sensitive skin\n\nWant to focus on rich hydrators and barrier builders?\n\n- [hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)\n- [gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)`,\n expected: {\n title: 'Find the right care for mature, sensitive skin',\n body: 'Want to focus on rich hydrators and barrier builders?',\n steps: [\n {\n content:\n '[hydrating creams →](web5://ask/hydrating+creams+for+mature+skin)',\n },\n {\n content:\n '[gentle resurfacing serums →](web5://ask/gentle+resurfacing+serums)',\n },\n ],\n },\n },\n {\n markdown: `<!-- section: next_steps -->\n\n## Next Steps\n\n- Check out [Documentation](/docs)\n- Try [Examples](/examples)`,\n expected: {\n title: 'Next Steps',\n steps: [\n { content: 'Check out [Documentation](/docs)' },\n { content: 'Try [Examples](/examples)' },\n ],\n },\n },\n ];\n\n parse(parts: Part[]): NextStepsCompProps {\n const title = headingText(parts, 2) || 'Next Steps';\n const backgroundImage = firstImageSrc(parts);\n const steps: { content: string; iconHint?: string }[] = [];\n let body: string | undefined;\n let chipsInlineInBody = false;\n\n // Try list-based pattern first\n const lists = getPartsByType<ListPart>(parts, 'list');\n if (lists.length > 0) {\n // The framing sentence is the only top-level prose — list items live\n // under each list's meta.items, so this never picks up a chip.\n body = proseBodyMarkdown(parts) || undefined;\n\n for (const list of lists) {\n for (const item of list.meta?.items ?? []) {\n const itemParts = (item.meta?.parts ?? []) as Part[];\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 const contentFragments: string[] = [];\n for (const p of itemParts) {\n if (p.type === 'link') {\n const link = p as LinkPart;\n contentFragments.push(linkToMarkdown(link));\n } else if (p.type === 'text' || p.type === 'richText') {\n contentFragments.push(p.content?.trim() || '');\n }\n }\n const content =\n contentFragments.filter(Boolean).join(' ') ||\n extractListItemText(item);\n if (content.trim()) {\n steps.push({\n content: content.trim(),\n ...(iconHint ? { iconHint } : {}),\n });\n }\n }\n }\n } else {\n body = inlineBodyMarkdown(parts);\n // body carries the chips as inline links — flag it so renderers show the\n // prose alone instead of repeating every chip as a row beneath it.\n chipsInlineInBody = Boolean(body);\n }\n\n // Fallback: inline links pattern\n if (steps.length === 0) {\n const links = getLinks(parts);\n for (const link of links) {\n const text = link.content?.trim();\n const href = link.meta?.href;\n if (text && href) {\n steps.push({ content: linkToMarkdown(link) });\n }\n }\n }\n\n return {\n title,\n ...(body ? { body } : {}),\n ...(chipsInlineInBody ? { chipsInlineInBody } : {}),\n ...(backgroundImage ? { backgroundImage } : {}),\n steps,\n };\n }\n}\n"],"mappings":";AAAA,SACEA,WAAW,EACXC,iBAAiB,EACjBC,mBAAmB,EACnBC,aAAa,QACR,eAAe;AACtB,SACEC,cAAc,EACdC,QAAQ,QAKH,mBAAmB;AAQ1B,SAASC,cAAcA,CAACC,IAAc,EAAU;EAAA,IAAAC,aAAA,EAAAC,UAAA;EAC9C,OAAO,IAAI,EAAAD,aAAA,GAAAD,IAAI,CAACG,OAAO,qBAAZF,aAAA,CAAcG,IAAI,CAAC,CAAC,KAAI,EAAE,KAAK,EAAAF,UAAA,GAAAF,IAAI,CAACK,IAAI,qBAATH,UAAA,CAAWI,IAAI,KAAI,EAAE,GAAG;AACpE;AAEA,SAASC,iBAAiBA,CAACC,OAAe,EAAEC,KAAa,EAAU;EACjE,MAAMC,OAAO,GAAGD,KAAK,CAACL,IAAI,CAAC,CAAC;EAC5B,IAAI,CAACM,OAAO,EAAE;IACZ,OAAOF,OAAO;EAChB;EACA,IAAI,CAACA,OAAO,EAAE;IACZ,OAAOE,OAAO;EAChB;EACA,IAAI,YAAY,CAACC,IAAI,CAACD,OAAO,CAAC,EAAE;IAC9B,OAAO,GAAGF,OAAO,GAAGE,OAAO,EAAE;EAC/B;EACA,OAAO,GAAGF,OAAO,IAAIE,OAAO,EAAE;AAChC;AAEA,SAASE,kBAAkBA,CAACC,KAAa,EAAsB;EAC7D,MAAMC,IAAI,GAAGD,KAAK,CAACE,MAAM,CAAC,CAACP,OAAO,EAAEQ,IAAI,KAAK;IAC3C,IAAIA,IAAI,CAACC,IAAI,KAAK,MAAM,EAAE;MACxB,OAAOV,iBAAiB,CAACC,OAAO,EAAET,cAAc,CAACiB,IAAgB,CAAC,CAAC;IACrE;IACA,IAAIA,IAAI,CAACC,IAAI,KAAK,MAAM,IAAID,IAAI,CAACC,IAAI,KAAK,UAAU,EAAE;MAAA,IAAAC,UAAA;MACpD,MAAMC,QAAQ,IAAAD,UAAA,GAAIF,IAAI,CAACX,IAAI,cAAAa,UAAA,GAATA,UAAA,CAAWC,QAAQ,qBAApBD,UAAA,CAA6Cd,IAAI,CAAC,CAAC;MACpE,OAAOG,iBAAiB,CAACC,OAAO,EAAEW,QAAQ,IAAIH,IAAI,CAACb,OAAO,IAAI,EAAE,CAAC;IACnE;IACA,OAAOK,OAAO;EAChB,CAAC,EAAE,EAAE,CAAC;EAEN,OAAOM,IAAI,IAAIM,SAAS;AAC1B;AAEA,OAAO,MAAMC,0BAA0B,CAEvC;EAAAC,YAAA;IAAAC,eAAA,sBACgB,WAAW;IAAAA,eAAA,mBACd;IACT;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA,0IAA0I;IAC1I;IACA;IACA,8FAA8F,CAC/F;IAAAA,eAAA,mBAE8B;MAC7BC,KAAK,EAAE,YAAY;MACnBV,IAAI,EAAE,kCAAkC;MACxCW,eAAe,EAAEL,SAAS;MAC1BM,KAAK,EAAE,CAAC;QAAEvB,OAAO,EAAE;MAAwB,CAAC;IAC9C,CAAC;IAAAoB,eAAA,mBAEgD,CAC/C;MACEJ,QAAQ,EAAE;AAChB;AACA;AACA;AACA;AACA;AACA;AACA,sEAAsE;MAChEQ,QAAQ,EAAE;QACRH,KAAK,EAAE,gDAAgD;QACvDV,IAAI,EAAE,uDAAuD;QAC7DY,KAAK,EAAE,CACL;UACEvB,OAAO,EACL;QACJ,CAAC,EACD;UACEA,OAAO,EACL;QACJ,CAAC;MAEL;IACF,CAAC,EACD;MACEgB,QAAQ,EAAE;AAChB;AACA;AACA;AACA;AACA,4BAA4B;MACtBQ,QAAQ,EAAE;QACRH,KAAK,EAAE,YAAY;QACnBE,KAAK,EAAE,CACL;UAAEvB,OAAO,EAAE;QAAmC,CAAC,EAC/C;UAAEA,OAAO,EAAE;QAA4B,CAAC;MAE5C;IACF,CAAC,CACF;EAAA;EAEDyB,KAAKA,CAACf,KAAa,EAAsB;IACvC,MAAMW,KAAK,GAAG/B,WAAW,CAACoB,KAAK,EAAE,CAAC,CAAC,IAAI,YAAY;IACnD,MAAMY,eAAe,GAAG7B,aAAa,CAACiB,KAAK,CAAC;IAC5C,MAAMa,KAA+C,GAAG,EAAE;IAC1D,IAAIZ,IAAwB;IAC5B,IAAIe,iBAAiB,GAAG,KAAK;;IAE7B;IACA,MAAMC,KAAK,GAAGjC,cAAc,CAAWgB,KAAK,EAAE,MAAM,CAAC;IACrD,IAAIiB,KAAK,CAACC,MAAM,GAAG,CAAC,EAAE;MACpB;MACA;MACAjB,IAAI,GAAGpB,iBAAiB,CAACmB,KAAK,CAAC,IAAIO,SAAS;MAE5C,KAAK,MAAMY,IAAI,IAAIF,KAAK,EAAE;QACxB,KAAK,MAAMG,IAAI,IAAI,EAAAC,UAAA,GAAAF,IAAI,CAAC3B,IAAI,qBAAT6B,UAAA,CAAWC,KAAK,KAAI,EAAE,EAAE;UAAA,IAAAD,UAAA,EAAAE,UAAA,EAAAC,cAAA;UACzC,MAAMC,SAAS,GAAI,EAAAF,UAAA,GAAAH,IAAI,CAAC5B,IAAI,qBAAT+B,UAAA,CAAWvB,KAAK,KAAI,EAAa;UAEpD,MAAM0B,QAAQ,GAAGD,SAAS,CAACE,IAAI,CAAEC,CAAC,IAAKA,CAAC,CAACxB,IAAI,KAAK,MAAM,CAE3C;UACb,MAAMyB,QAAQ,GAAIH,QAAQ,aAAAF,cAAA,GAARE,QAAQ,CAAElC,IAAI,qBAAfgC,cAAA,CAA2CK,QAAQ;UAEpE,MAAMC,gBAA0B,GAAG,EAAE;UACrC,KAAK,MAAMF,CAAC,IAAIH,SAAS,EAAE;YACzB,IAAIG,CAAC,CAACxB,IAAI,KAAK,MAAM,EAAE;cACrB,MAAMjB,IAAI,GAAGyC,CAAa;cAC1BE,gBAAgB,CAACC,IAAI,CAAC7C,cAAc,CAACC,IAAI,CAAC,CAAC;YAC7C,CAAC,MAAM,IAAIyC,CAAC,CAACxB,IAAI,KAAK,MAAM,IAAIwB,CAAC,CAACxB,IAAI,KAAK,UAAU,EAAE;cAAA,IAAA4B,UAAA;cACrDF,gBAAgB,CAACC,IAAI,CAAC,EAAAC,UAAA,GAAAJ,CAAC,CAACtC,OAAO,qBAAT0C,UAAA,CAAWzC,IAAI,CAAC,CAAC,KAAI,EAAE,CAAC;YAChD;UACF;UACA,MAAMD,OAAO,GACXwC,gBAAgB,CAACG,MAAM,CAACC,OAAO,CAAC,CAACC,IAAI,CAAC,GAAG,CAAC,IAC1CrD,mBAAmB,CAACsC,IAAI,CAAC;UAC3B,IAAI9B,OAAO,CAACC,IAAI,CAAC,CAAC,EAAE;YAClBsB,KAAK,CAACkB,IAAI,CAAC;cACTzC,OAAO,EAAEA,OAAO,CAACC,IAAI,CAAC,CAAC;cACvB,IAAIsC,QAAQ,GAAG;gBAAEA;cAAS,CAAC,GAAG,CAAC,CAAC;YAClC,CAAC,CAAC;UACJ;QACF;MACF;IACF,CAAC,MAAM;MACL5B,IAAI,GAAGF,kBAAkB,CAACC,KAAK,CAAC;MAChC;MACA;MACAgB,iBAAiB,GAAGkB,OAAO,CAACjC,IAAI,CAAC;IACnC;;IAEA;IACA,IAAIY,KAAK,CAACK,MAAM,KAAK,CAAC,EAAE;MACtB,MAAMkB,KAAK,GAAGnD,QAAQ,CAACe,KAAK,CAAC;MAC7B,KAAK,MAAMb,IAAI,IAAIiD,KAAK,EAAE;QAAA,IAAAC,cAAA,EAAAC,WAAA;QACxB,MAAMC,IAAI,IAAAF,cAAA,GAAGlD,IAAI,CAACG,OAAO,qBAAZ+C,cAAA,CAAc9C,IAAI,CAAC,CAAC;QACjC,MAAME,IAAI,IAAA6C,WAAA,GAAGnD,IAAI,CAACK,IAAI,qBAAT8C,WAAA,CAAW7C,IAAI;QAC5B,IAAI8C,IAAI,IAAI9C,IAAI,EAAE;UAChBoB,KAAK,CAACkB,IAAI,CAAC;YAAEzC,OAAO,EAAEJ,cAAc,CAACC,IAAI;UAAE,CAAC,CAAC;QAC/C;MACF;IACF;IAEA,OAAO;MACLwB,KAAK;MACL,IAAIV,IAAI,GAAG;QAAEA;MAAK,CAAC,GAAG,CAAC,CAAC,CAAC;MACzB,IAAIe,iBAAiB,GAAG;QAAEA;MAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;MACnD,IAAIJ,eAAe,GAAG;QAAEA;MAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;MAC/CC;IACF,CAAC;EACH;AACF","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComparisonSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/ComparisonSectionDefinition.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAEV,mBAAmB,EAEnB,0BAA0B,EAC1B,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAmB/B,qBAAa,2BACX,YAAW,iBAAiB,CAAC,0BAA0B,CAAC;IAExD,WAAW,eAAyB;
|
|
1
|
+
{"version":3,"file":"ComparisonSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/ComparisonSectionDefinition.ts"],"names":[],"mappings":"AAMA,OAAO,EAAkB,KAAK,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,KAAK,EAEV,mBAAmB,EAEnB,0BAA0B,EAC1B,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EACV,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACZ,MAAM,uBAAuB,CAAC;AAmB/B,qBAAa,2BACX,YAAW,iBAAiB,CAAC,0BAA0B,CAAC;IAExD,WAAW,eAAyB;IAepC,QAAQ,WAEN;IAEF,QAAQ,EAAE,mBAAmB,CAO3B;IAEF,QAAQ,EAAE,cAAc,CAAC,mBAAmB,CAAC,EAAE,CA8C7C;IAEF,KAAK,CACH,KAAK,EAAE,IAAI,EAAE,EACb,OAAO,CAAC,EAAE,YAAY,GACrB,mBAAmB,GAAG,WAAW;CA0GrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NextStepsSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/NextStepsSectionDefinition.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,IAAI,EAIV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EACzB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAmC/D,qBAAa,0BACX,YAAW,iBAAiB,CAAC,yBAAyB,CAAC;IAEvD,WAAW,cAAwB;IACnC,QAAQ,
|
|
1
|
+
{"version":3,"file":"NextStepsSectionDefinition.d.ts","sourceRoot":"","sources":["../../../../src/component/componentDefinitions/NextStepsSectionDefinition.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,IAAI,EAIV,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EACzB,cAAc,EACf,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAmC/D,qBAAa,0BACX,YAAW,iBAAiB,CAAC,yBAAyB,CAAC;IAEvD,WAAW,cAAwB;IACnC,QAAQ,WAqBN;IAEF,QAAQ,EAAE,kBAAkB,CAK1B;IAEF,QAAQ,EAAE,cAAc,CAAC,kBAAkB,CAAC,EAAE,CAwC5C;IAEF,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,kBAAkB;CAsEzC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wix/web5-core",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "1.63.
|
|
4
|
+
"version": "1.63.32",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "tsachis",
|
|
7
7
|
"email": "tsachis@wix.com"
|
|
@@ -100,5 +100,5 @@
|
|
|
100
100
|
"wallaby": {
|
|
101
101
|
"autoDetect": true
|
|
102
102
|
},
|
|
103
|
-
"falconPackageHash": "
|
|
103
|
+
"falconPackageHash": "6772047e941dfd79375c77ef18fe92b3d7cc9881d44877f06e0c6fc9"
|
|
104
104
|
}
|