Skip to content

Releases: jonata/grav-plugin-smart-interlinker

v0.2.7 — Replacement targets the previewed occurrence

Choose a tag to compare

@jonata jonata released this 28 Apr 15:21

Bugfix

  • Accepting a suggestion now replaces the exact occurrence shown in the context preview, not the first lexical match. Previously, when a phrase appeared inside an existing markdown link AND outside it, accepting wrapped the first hit — which was usually the one inside the link — producing nested broken patterns like `[Fedora Linux 44](/y)`. The replacement now uses an index from the same skip-zone-aware lookup the preview uses, so the displayed and replaced positions are guaranteed to match.
  • No more empty suggestion rows. Suggestions whose phrase has no plain-text occurrence the user can see (every match is inside a markdown link, URL, heading, or code block) are no longer rendered in the modal at all.

See the full CHANGELOG.

v0.2.6 — Nested link fix, slider authoritative for taxonomies

Choose a tag to compare

@jonata jonata released this 28 Apr 13:15

Bugfix

  • Nested / broken markdown links. A source like `[Kdenlive](/x)` (which can appear after a re-accepted suggestion) used to leave dangling `](/url)` fragments that re-introduced the inner phrase as a phantom token. The stripper now peels the innermost match first and repeats until clean. Same fix in the frontend context preview's skip-zones.
  • Phrase-length slider is now authoritative for every target type. Previously taxonomy targets bypassed it (so single-word values surfaced even at slider=2+), which contradicted the editor's intent. To see single-word taxonomy matches, drag the slider to 1.

Improved

  • Taxonomy-routes admin help text uses `tag` as the example instead of the more specific `distro`.

See the full CHANGELOG.

v0.2.5 — Taxonomy targets, configurable headings, multi-field keywords

Choose a tag to compare

@jonata jonata released this 27 Apr 13:15

New

  • Taxonomy targets. Each unique `(key, value)` pair across the index becomes a virtual link target whose URL is built from a per-key template you configure. Set up via the new `taxonomy_routes` admin field (e.g. `distro: '/artigos/distro:{value}'`). Single-word taxonomy values bypass the modal's exact-phrase-length filter and get a `tag` badge so they're easy to tell apart from page suggestions.
  • `skip_headings` toggle. Choose whether phrases inside Markdown headings (`#`, `##`, `###`...) and HTML `

    `–`

    ` blocks are excluded from suggestions. Defaults to enabled (preserves v0.2.3 behavior); flip off to receive suggestions for words that appear inside headings.
  • Show-more for long target lists. Suggestions with many candidate pages now render only the first 10, with a Show N more button to expand. Cleans up noisy single-word matches that have hundreds of candidates.
  • Refresh after Accept. Inserting a link refetches suggestions automatically so other matches that overlap the just-linked region disappear. A spinner overlays the matches list during the refresh.

Improved

  • `keyword_field` (single string) → `keyword_fields` (array). Admin field is a selectize tag input with predefined options (`focus_keyword`, `keywords`, `seo_title`, `meta_title`, `aliases`, `tags`, `meta_description`) plus free-form add. Legacy single-string config is auto-migrated.
  • Indexer uses Symfony YAML (when available) so nested taxonomy front-matter is parsed correctly.
  • The IIFE survives being inlined late by the asset pipeline — editor-button observer starts immediately when the DOM is already ready.

Bugfix

  • `insertLink` is now case-insensitive and word-bounded; preserves the source's original casing in the wrapped link. Fixes the "Phrase no longer found in content" error when the title-derived phrase had different casing than the source occurrence.

See the full CHANGELOG.

v0.2.3 — Tokenized source matching, heading skip-zones

Choose a tag to compare

@jonata jonata released this 27 Apr 11:40

Improved

  • Source content is now tokenized with the same rules as target titles before phrase lookup. Compound tokens like `22.3`, `RISC-V`, `5.18`, `front-end` stay atomic on both sides — a phrase only matches when it appears as a contiguous sequence of whole tokens. Fixes false positives like "Linux Mint 22" matching inside "Linux Mint 22.3".
  • Markdown headings stripped from the source before matching. ATX (`#`, `##`, `###`...), Setext (text + `===`/`---`), and HTML `

    `–`

    ` blocks are excluded. Phrases that only appear inside a heading are no longer suggested. The in-modal context preview also honors the heading skip-zone.
  • Toolbar button shows a spinning icon (`fa-circle-o-notch fa-spin`) during analysis instead of the text "Analyzing…", preserving the compact icon-only toolbar layout.

See the full CHANGELOG.

v0.2.2 — Phrase-length slider + matching quality fixes

Choose a tag to compare

@jonata jonata released this 27 Apr 01:07

New

  • Phrase length slider in the suggestions modal — drag to filter by exact phrase length (1 to 6 words). Defaults to the configured `min_phrase_words`.
  • Modal height is fixed at 80vh so the sliders no longer shift while you drag.

Improved

  • Per-target dedup now subsumes only by overlap. Unrelated short matches in the same target's title (e.g. Software and QEMU) are both kept; only fragments contained inside a longer matching phrase are dropped.
  • Backend always generates phrases from 1 word up; `min_phrase_words` becomes the slider's default value.
  • Internal result cap raised 30 → 500 so single-word matches aren't silently truncated.
  • Stopword list expanded with Portuguese articles/prepositions/conjunctions/pronouns and common short English words.
  • Tokenizer preserves dots and underscores between alphanumerics (`6.1.4` is one token).
  • Suggestions inside markdown links, autolinks, HTML anchors, bare URLs, code blocks, and inline code are ignored.
  • Context preview lands on a real plain-text occurrence — never on a substring inside a longer word inside a markdown link.

Bugfix

  • Self-suggestion exclusion fixed for pages nested under the home alias.
  • Radio buttons inside a target group share the same `name`, so picking a different target deselects the previous one.
  • Pure-number 1-word phrases (`5.18`, `40`) no longer surface as standalone suggestions.

See the full CHANGELOG.

v0.2.1 — Remove unwired Rebuild Index button

Choose a tag to compare

@jonata jonata released this 26 Apr 23:47

Removes the Rebuild Index button from the plugin settings panel. Grav admin forms don't support type: button, so it was rendering as an unlabeled empty text input.

The index is already rebuilt automatically:

  • When the cache is missing (on first analyze)
  • On every page save (via onPageSave hook)
  • On every page delete (via onPageDelete hook)

So the manual button was redundant anyway.

v0.2.0 — Title-driven matching

Choose a tag to compare

@jonata jonata released this 26 Apr 23:23

Major algorithm overhaul. Suggestions are now driven by target page titles instead of source-content phrases, eliminating phantom matches and surfacing only SEO-meaningful anchor text.

Highlights

  • Title-driven matching: every suggestion's anchor text is guaranteed to come from the target page's own title. No more coincidental body-text matches that look unrelated to the target.
  • Per-target dedup: each candidate page surfaces at most once, with the longest matching title-fragment as the suggested anchor text.
  • Coverage-based confidence score: matched phrase length as a percent of the full target title. The in-modal slider is now actually meaningful.
  • Self-suggestion fix: route comparison normalizes the home alias on both sides — a page never suggests linking to itself.

New settings

  • keyword_field — front-matter field treated as an extra source of link-target phrases (e.g. focus_keyword)
  • min_phrase_words — minimum n-gram length for title fragments (default 2, range 1-5)
  • ignored_terms — site-specific filler words (e.g. linux) to skip during phrase generation
  • Default match_threshold lowered to 0; the in-modal slider is the primary live filter

Removed

  • match_fields setting (replaced by keyword_field)
  • Body-content matching and the title/field/content score weighting

See the full CHANGELOG and README for details.

v0.1.0 — Initial release

Choose a tag to compare

@jonata jonata released this 24 Apr 22:08

First public release of Smart Interlinker — an SEO-driven internal linking assistant for the Grav page editor.

Highlights

  • Toolbar icon that opens a review modal for internal-link suggestions
  • Phrase matching across page titles, custom front-matter fields, and content
  • SEO scoring: title > custom fields > content; +10/+20 boost for bigrams/trigrams
  • Grouped suggestions per phrase with a selectable target list
  • Context snippet showing the matched phrase with surrounding text
  • Live confidence threshold slider
  • Cached JSON index, kept in sync via onPageSave / onPageDelete
  • Respects system.home.hide_in_urls when generating target routes

Install

Manual:

```bash
cd user/plugins
git clone https://github.com/jonata/grav-plugin-smart-interlinker.git smart-interlinker
```

Or wait for it to land in the Grav plugin store and use `bin/gpm install smart-interlinker`.

See the README for configuration and details.