From f7c4e16be9be8cc339528c882cd25f2823d77b17 Mon Sep 17 00:00:00 2001 From: lucaronin Date: Tue, 24 Feb 2026 23:40:08 +0100 Subject: [PATCH] fix: exclude title heading and wikilinks from word count countWords() was including the title line (# Heading) in the count, inflating it by ~2-3 words. Now strips the first H1 heading and [[wikilinks]] before counting, so only body prose is tallied. Co-Authored-By: Claude Opus 4.6 --- src/components/Inspector.test.tsx | 6 ++--- src/utils/wikilinks.test.ts | 45 ++++++++++++++++++++++++++----- src/utils/wikilinks.ts | 4 ++- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/components/Inspector.test.tsx b/src/components/Inspector.test.tsx index 515b6674..330c0b24 100644 --- a/src/components/Inspector.test.tsx +++ b/src/components/Inspector.test.tsx @@ -129,10 +129,10 @@ describe('Inspector', () => { expect(pill).toHaveStyle({ borderRadius: '16px' }) }) - it('computes word count from content minus frontmatter', () => { + it('computes word count from content minus frontmatter and title', () => { render() - // "Test Project" (# stripped) + "This is a test note with some words to count." = 12 words - expect(screen.getByText('12')).toBeInTheDocument() + // Title "# Test Project" excluded; body: "This is a test note with some words to count." = 10 words + expect(screen.getByText('10')).toBeInTheDocument() }) it('shows "Add property" button as disabled placeholder', () => { diff --git a/src/utils/wikilinks.test.ts b/src/utils/wikilinks.test.ts index 33161dcf..7eac7bf0 100644 --- a/src/utils/wikilinks.test.ts +++ b/src/utils/wikilinks.test.ts @@ -222,10 +222,9 @@ describe('countWords', () => { '', 'Only these five words count.', ].join('\n') - // Body: "# My Note\n\nOnly these five words count." - // After stripping '#': " My Note\n\nOnly these five words count." - // Words: My, Note, Only, these, five, words, count. = 7 - expect(countWords(content)).toBe(7) + // Title "# My Note" is excluded from word count + // Body: "Only these five words count." = 5 words + expect(countWords(content)).toBe(5) }) it('ignores frontmatter values containing dashes', () => { @@ -254,15 +253,47 @@ describe('countWords', () => { '', 'After the rule.', ].join('\n') - // Body includes: Title, Before, the, rule., After, the, rule. - // The --- horizontal rule is stripped by the markdown char filter - expect(countWords(content)).toBe(7) + // Title "# Title" is excluded; body: Before, the, rule., After, the, rule. = 6 + expect(countWords(content)).toBe(6) }) it('returns 0 when content is only frontmatter with no trailing body', () => { const content = '---\ntitle: Hello\nstatus: Active\ntags: [a, b, c]\n---' expect(countWords(content)).toBe(0) }) + + it('excludes title heading from word count', () => { + const content = '---\ntitle: Hello World\n---\n\n# Hello World\n\nThree body words.' + expect(countWords(content)).toBe(3) + }) + + it('counts correctly when no title heading is present', () => { + const content = '---\ntitle: Test\n---\n\nFour words in body.' + expect(countWords(content)).toBe(4) + }) + + it('excludes wikilinks from word count', () => { + const content = '---\ntitle: Test\n---\n\n# Test\n\nSee [[My Note]] for details.' + // Title excluded, wikilink excluded: See, for, details. = 3 + expect(countWords(content)).toBe(3) + }) + + it('excludes multiple wikilinks from word count', () => { + const content = 'Check [[note-a]] and [[note-b]] here.' + // wikilinks excluded: Check, and, here. = 3 + expect(countWords(content)).toBe(3) + }) + + it('returns 0 for note with only title and no body', () => { + const content = '---\ntitle: Empty\n---\n\n# Empty\n' + expect(countWords(content)).toBe(0) + }) + + it('does not strip ## or ### subheadings as title', () => { + const content = '---\ntitle: Test\n---\n\n## Subheading\n\nBody text here.' + // ## is not a title heading; "Subheading" counts: Subheading, Body, text, here. = 4 + expect(countWords(content)).toBe(4) + }) }) describe('restoreWikilinksInBlocks', () => { diff --git a/src/utils/wikilinks.ts b/src/utils/wikilinks.ts index dbbc25fc..0bbfe242 100644 --- a/src/utils/wikilinks.ts +++ b/src/utils/wikilinks.ts @@ -106,7 +106,9 @@ export function splitFrontmatter(content: string): [string, string] { export function countWords(content: string): number { const [, body] = splitFrontmatter(content) - const text = body.replace(/[#*_[\]`>~\-|]/g, '').trim() + const withoutTitle = body.replace(/^\s*# [^\n]+\n?/, '') + const withoutWikilinks = withoutTitle.replace(/\[\[[^\]]*\]\]/g, '') + const text = withoutWikilinks.replace(/[#*_[\]`>~\-|]/g, '').trim() if (!text) return 0 return text.split(/\s+/).filter(Boolean).length }