Repository navigation
Releases: jonata/grav-plugin-smart-interlinker
Release list
v0.2.7 — Replacement targets the previewed occurrence
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
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
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
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
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
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
onPageSavehook) - On every page delete (via
onPageDeletehook)
So the manual button was redundant anyway.
v0.2.0 — Title-driven matching
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_thresholdlowered to 0; the in-modal slider is the primary live filter
Removed
match_fieldssetting (replaced bykeyword_field)- Body-content matching and the title/field/content score weighting
v0.1.0 — Initial release
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_urlswhen 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.