@link-assistant/web-capture 1.7.20 → 1.7.21

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @link-assistant/web-capture
2
2
 
3
+ ## 1.7.21
4
+
5
+ ### Patch Changes
6
+
7
+ - b8ad97d: Number consecutive top-level `<ol>`s continuously across the document (1, 2, 3, ... N) so JS and Rust HTML→Markdown converters agree. `<ol start="N">` resets the counter and is honoured by both implementations. Nested ordered lists keep their own per-list numbering.
8
+
3
9
  ## 1.7.20
4
10
 
5
11
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/web-capture",
3
- "version": "1.7.20",
3
+ "version": "1.7.21",
4
4
  "description": "CLI and microservice to render web pages as HTML, Markdown, or PNG",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -245,6 +245,12 @@ function renderNestedListGroup($, group, classStyles) {
245
245
  const openTags = [];
246
246
  const itemOpen = [];
247
247
 
248
+ const topLevelStart =
249
+ group[0]?.tagName?.toLowerCase() === 'ol'
250
+ ? $(group[0]).attr('start')
251
+ : undefined;
252
+ let topLevelOpened = false;
253
+
248
254
  const closeItem = (level) => {
249
255
  if (itemOpen[level]) {
250
256
  html += '</li>';
@@ -261,7 +267,21 @@ function renderNestedListGroup($, group, classStyles) {
261
267
  const openList = (level, tagName) => {
262
268
  openTags[level] = tagName;
263
269
  itemOpen[level] = false;
264
- html += `<${tagName}>`;
270
+ if (
271
+ level === 0 &&
272
+ !topLevelOpened &&
273
+ tagName === 'ol' &&
274
+ topLevelStart !== undefined &&
275
+ topLevelStart !== ''
276
+ ) {
277
+ html += `<ol start="${topLevelStart}">`;
278
+ topLevelOpened = true;
279
+ } else {
280
+ if (level === 0) {
281
+ topLevelOpened = true;
282
+ }
283
+ html += `<${tagName}>`;
284
+ }
265
285
  };
266
286
 
267
287
  for (const list of group) {
package/src/lib.js CHANGED
@@ -59,6 +59,10 @@ export function convertHtmlToMarkdown(html, baseUrl) {
59
59
  // Demoting avoids ATX heading prefix, leaving the sub-number on a clean line.
60
60
  preserveLeadingHeadingNumbering($);
61
61
 
62
+ // Number consecutive top-level <ol>s continuously across the document
63
+ // (1, 2, 3 ... N) so JS and Rust agree. <ol start="N"> resets the counter.
64
+ applyContinuousOrderedListNumbering($);
65
+
62
66
  // Remove <a> tags with no direct text content (including only whitespace or only child elements)
63
67
  $('a').each(function () {
64
68
  // Get all text nodes directly under this <a>
@@ -228,6 +232,28 @@ function preserveLeadingHeadingNumbering($) {
228
232
  });
229
233
  }
230
234
 
235
+ // Walk every top-level <ol> in document order and assign a `start` attribute
236
+ // so consecutive lists number continuously (1, 2, 3, ... N). An explicit
237
+ // `start="N"` resets the running counter to N and is preserved.
238
+ //
239
+ // Top-level here means "not inside another <ol> or <ul>" — nested ordered
240
+ // lists keep their own numbering and Turndown's per-list start handling.
241
+ function applyContinuousOrderedListNumbering($) {
242
+ let counter = 1;
243
+ $('ol').each(function () {
244
+ if ($(this).parents('ol, ul').length > 0) {
245
+ return;
246
+ }
247
+ const explicitStart = parseInt($(this).attr('start') ?? '', 10);
248
+ if (Number.isFinite(explicitStart)) {
249
+ counter = explicitStart;
250
+ } else {
251
+ $(this).attr('start', String(counter));
252
+ }
253
+ counter += $(this).children('li').length;
254
+ });
255
+ }
256
+
231
257
  function stripLeadingTextFromHeading($, $heading, prefixLen) {
232
258
  // Walk text nodes from the start of the heading and remove prefixLen characters.
233
259
  let remaining = prefixLen;
@@ -506,6 +532,9 @@ export function convertHtmlToMarkdownEnhanced(html, baseUrl, options = {}) {
506
532
  // Preserve hierarchical heading numbering (see convertHtmlToMarkdown).
507
533
  preserveLeadingHeadingNumbering($);
508
534
 
535
+ // Continuous numbering across consecutive top-level <ol>s (see convertHtmlToMarkdown).
536
+ applyContinuousOrderedListNumbering($);
537
+
509
538
  // Remove empty links (same logic as convertHtmlToMarkdown)
510
539
  $('a').each(function () {
511
540
  const directText = $(this)
@@ -0,0 +1,12 @@
1
+ <ol>
2
+ <li><strong>First</strong></li>
3
+ <li><strong>Second</strong></li>
4
+ </ol>
5
+ <p>Some prose between lists.</p>
6
+ <ol>
7
+ <li><strong>Third</strong></li>
8
+ <li><strong>Fourth</strong></li>
9
+ </ol>
10
+ <ol start="13">
11
+ <li><strong>Top-level numbered heading.</strong></li>
12
+ </ol>
@@ -0,0 +1,20 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { convertHtmlToMarkdown } from '../../src/lib.js';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const HTML = fs.readFileSync(
9
+ path.join(__dirname, '..', 'fixtures', 'ol-continuous-numbering.html'),
10
+ 'utf8'
11
+ );
12
+
13
+ it('numbers consecutive top-level ordered lists continuously', () => {
14
+ const md = convertHtmlToMarkdown(HTML);
15
+ expect(md).toMatch(/^\s*1\.\s+\*\*First\*\*/m);
16
+ expect(md).toMatch(/^\s*2\.\s+\*\*Second\*\*/m);
17
+ expect(md).toMatch(/^\s*3\.\s+\*\*Third\*\*/m);
18
+ expect(md).toMatch(/^\s*4\.\s+\*\*Fourth\*\*/m);
19
+ expect(md).toMatch(/^\s*13\.\s+\*\*Top-level/m);
20
+ });