mkdocs-rangefind 0.1.0__py3-none-any.whl
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.
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
"""Rangefind MkDocs plugin.
|
|
2
|
+
|
|
3
|
+
MkDocs is a Python static-site generator; Rangefind is a Node.js tool. There is
|
|
4
|
+
no way to run Rangefind's indexing logic from pure Python, so this plugin does
|
|
5
|
+
the honest thing: after MkDocs finishes building the site it *shells out* to the
|
|
6
|
+
Rangefind Node CLI (``npx rangefind build <site_dir>``) to crawl the freshly
|
|
7
|
+
rendered HTML into a static, range-request search index, then copies the
|
|
8
|
+
Rangefind search Web Component assets into the site and injects the widget tags
|
|
9
|
+
into every rendered page.
|
|
10
|
+
|
|
11
|
+
Prerequisite: Node.js and the ``rangefind`` npm package must be reachable from
|
|
12
|
+
the MkDocs project directory (typically ``npm install rangefind`` so that
|
|
13
|
+
``npx rangefind`` and Node's module resolution both find it).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import shutil
|
|
20
|
+
import subprocess
|
|
21
|
+
from urllib.parse import urlsplit
|
|
22
|
+
|
|
23
|
+
from mkdocs.config import config_options
|
|
24
|
+
from mkdocs.exceptions import PluginError
|
|
25
|
+
from mkdocs.plugins import BasePlugin
|
|
26
|
+
from mkdocs.utils import get_relative_url
|
|
27
|
+
|
|
28
|
+
try: # MkDocs >= 1.2 ships a namespaced logger helper.
|
|
29
|
+
from mkdocs.plugins import get_plugin_logger
|
|
30
|
+
|
|
31
|
+
log = get_plugin_logger(__name__)
|
|
32
|
+
except ImportError: # pragma: no cover - very old MkDocs
|
|
33
|
+
import logging
|
|
34
|
+
|
|
35
|
+
log = logging.getLogger(f"mkdocs.plugins.{__name__}")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class RangefindPlugin(BasePlugin):
|
|
39
|
+
"""Build a Rangefind index after the site is built and inject the widget."""
|
|
40
|
+
|
|
41
|
+
# MkDocs uses the classic tuple-based config scheme (name, option) pairs.
|
|
42
|
+
config_scheme = (
|
|
43
|
+
# Master switch. When false the plugin is a complete no-op.
|
|
44
|
+
("enabled", config_options.Type(bool, default=True)),
|
|
45
|
+
# URL prefix baked into the *result links* stored in the index
|
|
46
|
+
# (passed to the CLI as --base-url). Empty string => derive from the
|
|
47
|
+
# MkDocs `site_url` path (falling back to "/"). Set explicitly when the
|
|
48
|
+
# index's stored URLs need a specific prefix.
|
|
49
|
+
("base_url", config_options.Type(str, default="")),
|
|
50
|
+
# Index output directory, relative to the built site_dir.
|
|
51
|
+
("output_dir", config_options.Type(str, default="rangefind")),
|
|
52
|
+
# Where the Web Component JS/CSS assets are copied, relative to site_dir.
|
|
53
|
+
("assets_dir", config_options.Type(str, default="_rangefind")),
|
|
54
|
+
# Include the optional framework-free theme stylesheet. Off by default:
|
|
55
|
+
# the component is headless and inherits the host page's CSS.
|
|
56
|
+
("theme", config_options.Type(bool, default=False)),
|
|
57
|
+
# Command used to run the Rangefind CLI. "npx" resolves the local
|
|
58
|
+
# `rangefind` install; can be pointed at a specific binary in CI.
|
|
59
|
+
("node_command", config_options.Type(str, default="npx")),
|
|
60
|
+
# Where to place the <rangefind-search> element:
|
|
61
|
+
# "body_end" (default) - injected right before </body> on every page.
|
|
62
|
+
# "manual" - not injected; you hand-place it in a theme
|
|
63
|
+
# override. Script/CSS are still injected.
|
|
64
|
+
("placement", config_options.Choice(("body_end", "manual"), default="body_end")),
|
|
65
|
+
# Optional literal HTML marker. When set (and placement != manual), the
|
|
66
|
+
# <rangefind-search> element is inserted immediately after the first
|
|
67
|
+
# occurrence of this exact substring instead of before </body> (e.g.
|
|
68
|
+
# '<div id="rangefind">'). Falls back to body_end if not found.
|
|
69
|
+
("selector", config_options.Type(str, default="")),
|
|
70
|
+
# Input placeholder text for the injected element.
|
|
71
|
+
("placeholder", config_options.Type(str, default="Search")),
|
|
72
|
+
# Arbitrary extra attributes for the injected <rangefind-search> element
|
|
73
|
+
# (e.g. {hotkey: true, router: true, input-class: "w-full"}).
|
|
74
|
+
("element_attributes", config_options.Type(dict, default={})),
|
|
75
|
+
# Explicit paths to the component assets. When unset the plugin asks
|
|
76
|
+
# Node to resolve them from the installed `rangefind` package.
|
|
77
|
+
("element_js_path", config_options.Type(str, default="")),
|
|
78
|
+
("element_css_path", config_options.Type(str, default="")),
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# ---- lifecycle hooks -------------------------------------------------
|
|
82
|
+
|
|
83
|
+
def on_post_build(self, config, **kwargs):
|
|
84
|
+
"""Real MkDocs post-build hook: crawl the built site and copy assets."""
|
|
85
|
+
if not self.config["enabled"]:
|
|
86
|
+
return
|
|
87
|
+
|
|
88
|
+
site_dir = config["site_dir"]
|
|
89
|
+
project_dir = _project_dir(config)
|
|
90
|
+
output_abs = os.path.join(site_dir, self.config["output_dir"])
|
|
91
|
+
base_url = self._resolve_base_url(config)
|
|
92
|
+
|
|
93
|
+
# 1. Build the index by shelling out to the Node CLI. Real flags,
|
|
94
|
+
# confirmed against bin/rangefind.js:
|
|
95
|
+
# rangefind build <dir> --output <dir> --base-url <url>
|
|
96
|
+
command = [
|
|
97
|
+
self.config["node_command"],
|
|
98
|
+
"rangefind",
|
|
99
|
+
"build",
|
|
100
|
+
site_dir,
|
|
101
|
+
"--output",
|
|
102
|
+
output_abs,
|
|
103
|
+
"--base-url",
|
|
104
|
+
base_url,
|
|
105
|
+
]
|
|
106
|
+
log.info("Building Rangefind index: %s", " ".join(command))
|
|
107
|
+
try:
|
|
108
|
+
result = subprocess.run(
|
|
109
|
+
command,
|
|
110
|
+
cwd=project_dir,
|
|
111
|
+
capture_output=True,
|
|
112
|
+
text=True,
|
|
113
|
+
check=False,
|
|
114
|
+
)
|
|
115
|
+
except FileNotFoundError as exc:
|
|
116
|
+
raise PluginError(
|
|
117
|
+
f"mkdocs-rangefind: could not run '{self.config['node_command']}'. "
|
|
118
|
+
"Node.js and the `rangefind` npm package must be installed and on "
|
|
119
|
+
f"PATH. Original error: {exc}"
|
|
120
|
+
)
|
|
121
|
+
if result.returncode != 0:
|
|
122
|
+
raise PluginError(
|
|
123
|
+
"mkdocs-rangefind: `rangefind build` failed "
|
|
124
|
+
f"(exit code {result.returncode}).\n"
|
|
125
|
+
f"command: {' '.join(command)}\n"
|
|
126
|
+
f"stdout:\n{result.stdout}\n"
|
|
127
|
+
f"stderr:\n{result.stderr}"
|
|
128
|
+
)
|
|
129
|
+
if result.stdout.strip():
|
|
130
|
+
log.info(result.stdout.strip())
|
|
131
|
+
|
|
132
|
+
# 2. Copy the Web Component assets into the site.
|
|
133
|
+
js_src, css_src = self._resolve_assets(project_dir)
|
|
134
|
+
assets_abs = os.path.join(site_dir, self.config["assets_dir"])
|
|
135
|
+
os.makedirs(assets_abs, exist_ok=True)
|
|
136
|
+
shutil.copyfile(js_src, os.path.join(assets_abs, "rangefind-search.js"))
|
|
137
|
+
if self.config["theme"]:
|
|
138
|
+
shutil.copyfile(css_src, os.path.join(assets_abs, "rangefind-search.css"))
|
|
139
|
+
log.info("Copied Rangefind widget assets to %s", assets_abs)
|
|
140
|
+
|
|
141
|
+
def on_post_page(self, output, page, config, **kwargs):
|
|
142
|
+
"""Real MkDocs post-page hook: inject the widget into the page HTML."""
|
|
143
|
+
if not self.config["enabled"]:
|
|
144
|
+
return output
|
|
145
|
+
|
|
146
|
+
assets_dir = self.config["assets_dir"].strip("/")
|
|
147
|
+
output_dir = self.config["output_dir"].strip("/")
|
|
148
|
+
page_url = page.url
|
|
149
|
+
|
|
150
|
+
js_href = get_relative_url(f"{assets_dir}/rangefind-search.js", page_url)
|
|
151
|
+
index_src = get_relative_url(f"{output_dir}/", page_url)
|
|
152
|
+
|
|
153
|
+
head_injection = f'<script type="module" src="{_attr(js_href)}"></script>'
|
|
154
|
+
if self.config["theme"]:
|
|
155
|
+
css_href = get_relative_url(f"{assets_dir}/rangefind-search.css", page_url)
|
|
156
|
+
head_injection = (
|
|
157
|
+
f'<link rel="stylesheet" href="{_attr(css_href)}">\n' + head_injection
|
|
158
|
+
)
|
|
159
|
+
output = _inject_head(output, head_injection)
|
|
160
|
+
|
|
161
|
+
if self.config["placement"] != "manual":
|
|
162
|
+
element = self._render_element(index_src)
|
|
163
|
+
output = self._inject_element(output, element)
|
|
164
|
+
|
|
165
|
+
return output
|
|
166
|
+
|
|
167
|
+
# ---- helpers ---------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
def _resolve_base_url(self, config):
|
|
170
|
+
configured = self.config["base_url"].strip()
|
|
171
|
+
if configured:
|
|
172
|
+
return configured
|
|
173
|
+
site_url = config.get("site_url")
|
|
174
|
+
if site_url:
|
|
175
|
+
path = urlsplit(site_url).path or "/"
|
|
176
|
+
return path if path.endswith("/") else path + "/"
|
|
177
|
+
return "/"
|
|
178
|
+
|
|
179
|
+
def _render_element(self, index_src):
|
|
180
|
+
attrs = {"src": index_src, "placeholder": self.config["placeholder"]}
|
|
181
|
+
for key, value in (self.config["element_attributes"] or {}).items():
|
|
182
|
+
if value is True:
|
|
183
|
+
attrs[str(key)] = "" # bare boolean attribute
|
|
184
|
+
elif value is False or value is None:
|
|
185
|
+
continue
|
|
186
|
+
else:
|
|
187
|
+
attrs[str(key)] = str(value)
|
|
188
|
+
rendered = []
|
|
189
|
+
for key, value in attrs.items():
|
|
190
|
+
rendered.append(key if value == "" else f'{key}="{_attr(value)}"')
|
|
191
|
+
return f"<rangefind-search {' '.join(rendered)}></rangefind-search>"
|
|
192
|
+
|
|
193
|
+
def _inject_element(self, output, element):
|
|
194
|
+
selector = self.config["selector"]
|
|
195
|
+
if selector:
|
|
196
|
+
idx = output.find(selector)
|
|
197
|
+
if idx != -1:
|
|
198
|
+
cut = idx + len(selector)
|
|
199
|
+
return output[:cut] + "\n" + element + output[cut:]
|
|
200
|
+
log.warning(
|
|
201
|
+
"mkdocs-rangefind: selector %r not found on a page; "
|
|
202
|
+
"falling back to placement before </body>.",
|
|
203
|
+
selector,
|
|
204
|
+
)
|
|
205
|
+
return _inject_body_end(output, element)
|
|
206
|
+
|
|
207
|
+
def _resolve_assets(self, project_dir):
|
|
208
|
+
"""Locate the component's JS/CSS.
|
|
209
|
+
|
|
210
|
+
Preference order:
|
|
211
|
+
1. Explicit `element_js_path` / `element_css_path` config values.
|
|
212
|
+
2. Ask Node to resolve them via the `rangefind` package export map
|
|
213
|
+
(`rangefind/element` and `rangefind/element.css`). The export map
|
|
214
|
+
is the robust seam here: `require.resolve('rangefind/package.json')`
|
|
215
|
+
is blocked by the package's `exports`, but the declared subpath
|
|
216
|
+
exports point straight at dist/rangefind-search.{js,css}.
|
|
217
|
+
"""
|
|
218
|
+
js_cfg = self.config["element_js_path"].strip()
|
|
219
|
+
css_cfg = self.config["element_css_path"].strip()
|
|
220
|
+
if js_cfg and css_cfg:
|
|
221
|
+
return _require_file(js_cfg, project_dir), _require_file(css_cfg, project_dir)
|
|
222
|
+
|
|
223
|
+
node_bin = _node_binary(self.config["node_command"])
|
|
224
|
+
js = js_cfg or self._node_resolve(node_bin, "rangefind/element", project_dir)
|
|
225
|
+
css = css_cfg or self._node_resolve(node_bin, "rangefind/element.css", project_dir)
|
|
226
|
+
return _require_file(js, project_dir), _require_file(css, project_dir)
|
|
227
|
+
|
|
228
|
+
def _node_resolve(self, node_bin, specifier, project_dir):
|
|
229
|
+
code = f"process.stdout.write(require.resolve({specifier!r}))"
|
|
230
|
+
try:
|
|
231
|
+
result = subprocess.run(
|
|
232
|
+
[node_bin, "-e", code],
|
|
233
|
+
cwd=project_dir,
|
|
234
|
+
capture_output=True,
|
|
235
|
+
text=True,
|
|
236
|
+
check=False,
|
|
237
|
+
)
|
|
238
|
+
except FileNotFoundError as exc:
|
|
239
|
+
raise PluginError(
|
|
240
|
+
f"mkdocs-rangefind: could not run Node ('{node_bin}') to resolve "
|
|
241
|
+
f"the '{specifier}' asset. Install Node.js, or set "
|
|
242
|
+
"`element_js_path`/`element_css_path` explicitly in mkdocs.yml. "
|
|
243
|
+
f"Original error: {exc}"
|
|
244
|
+
)
|
|
245
|
+
path = result.stdout.strip()
|
|
246
|
+
if result.returncode != 0 or not path:
|
|
247
|
+
raise PluginError(
|
|
248
|
+
f"mkdocs-rangefind: could not resolve '{specifier}' from the "
|
|
249
|
+
f"`rangefind` npm package (run from {project_dir}). Make sure "
|
|
250
|
+
"`rangefind` is installed there (e.g. `npm install rangefind`), "
|
|
251
|
+
"or set `element_js_path`/`element_css_path` in mkdocs.yml.\n"
|
|
252
|
+
f"stderr:\n{result.stderr}"
|
|
253
|
+
)
|
|
254
|
+
return path
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
# ---- module-level pure helpers ------------------------------------------
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def _project_dir(config):
|
|
261
|
+
config_file = config.get("config_file_path")
|
|
262
|
+
if config_file:
|
|
263
|
+
return os.path.dirname(os.path.abspath(config_file))
|
|
264
|
+
return os.getcwd()
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _node_binary(node_command):
|
|
268
|
+
"""Derive a plain Node binary from the configured command.
|
|
269
|
+
|
|
270
|
+
The index is built with `node_command` (default "npx"), but resolving the
|
|
271
|
+
asset paths needs Node itself. If the command is npx, use the sibling
|
|
272
|
+
`node`; otherwise assume the command can already run `-e`.
|
|
273
|
+
"""
|
|
274
|
+
base = os.path.basename(node_command)
|
|
275
|
+
if base in ("npx", "npx.cmd", "npx.exe"):
|
|
276
|
+
directory = os.path.dirname(node_command)
|
|
277
|
+
return os.path.join(directory, "node") if directory else "node"
|
|
278
|
+
return node_command
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _require_file(path, project_dir):
|
|
282
|
+
resolved = path if os.path.isabs(path) else os.path.join(project_dir, path)
|
|
283
|
+
if not os.path.isfile(resolved):
|
|
284
|
+
raise PluginError(f"mkdocs-rangefind: asset file not found: {resolved}")
|
|
285
|
+
return resolved
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def _attr(value):
|
|
289
|
+
return str(value).replace("&", "&").replace('"', """)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _inject_head(output, snippet):
|
|
293
|
+
lower = output.lower()
|
|
294
|
+
idx = lower.rfind("</head>")
|
|
295
|
+
if idx != -1:
|
|
296
|
+
return output[:idx] + snippet + "\n" + output[idx:]
|
|
297
|
+
return _inject_body_end(output, snippet)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _inject_body_end(output, snippet):
|
|
301
|
+
lower = output.lower()
|
|
302
|
+
idx = lower.rfind("</body>")
|
|
303
|
+
if idx != -1:
|
|
304
|
+
return output[:idx] + snippet + "\n" + output[idx:]
|
|
305
|
+
return output + "\n" + snippet
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mkdocs-rangefind
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MkDocs plugin that builds a Rangefind static search index and injects the search widget after every build.
|
|
5
|
+
Project-URL: Homepage, https://github.com/xjodoin/rangefind
|
|
6
|
+
Project-URL: Repository, https://github.com/xjodoin/rangefind
|
|
7
|
+
Project-URL: Issues, https://github.com/xjodoin/rangefind/issues
|
|
8
|
+
Author-email: Xavier Jodoin <xavier@jodoin.me>
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: mkdocs,mkdocs-plugin,rangefind,search,static-search
|
|
11
|
+
Classifier: Framework :: MkDocs
|
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Documentation
|
|
15
|
+
Classifier: Topic :: Text Processing :: Indexing
|
|
16
|
+
Requires-Python: >=3.8
|
|
17
|
+
Requires-Dist: mkdocs>=1.4
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# mkdocs-rangefind
|
|
21
|
+
|
|
22
|
+
An MkDocs plugin that wires [Rangefind](https://github.com/xjodoin/rangefind)
|
|
23
|
+
static search into your docs site. After MkDocs builds your site, the plugin
|
|
24
|
+
crawls the rendered HTML into a static, range-request search index and injects
|
|
25
|
+
the Rangefind search widget into every page — so you get working, serverless
|
|
26
|
+
search with **zero manual HTML editing**.
|
|
27
|
+
|
|
28
|
+
## How it works (and the one prerequisite)
|
|
29
|
+
|
|
30
|
+
Rangefind is a Node.js tool; MkDocs is a Python static-site generator. There is
|
|
31
|
+
no way to run Rangefind's indexing logic from pure Python, so this plugin is a
|
|
32
|
+
small, honest bridge: its MkDocs lifecycle hooks **shell out to the Rangefind
|
|
33
|
+
Node CLI** after the site is built.
|
|
34
|
+
|
|
35
|
+
**Prerequisite:** Node.js (>= 22) and the `rangefind` npm package must be
|
|
36
|
+
reachable from your MkDocs project directory. The simplest setup:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install rangefind # adds ./node_modules/rangefind + the CLI bin
|
|
40
|
+
pip install mkdocs-rangefind
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
With `rangefind` installed locally, both `npx rangefind` (the index build) and
|
|
44
|
+
Node's module resolution (locating the widget's JS/CSS assets) work offline,
|
|
45
|
+
including in CI.
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
Add the plugin to `mkdocs.yml`:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
plugins:
|
|
53
|
+
- search # optional; Rangefind replaces the need for it
|
|
54
|
+
- rangefind
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Then `mkdocs build` (or `mkdocs gh-deploy`). The plugin will:
|
|
58
|
+
|
|
59
|
+
1. Run `rangefind build <site_dir> --output <site_dir>/rangefind --base-url <…>`
|
|
60
|
+
to crawl the built HTML and produce the index under `site/rangefind/`.
|
|
61
|
+
2. Copy the search Web Component assets into `site/_rangefind/`.
|
|
62
|
+
3. Inject a `<script type="module">` for the component, an optional theme
|
|
63
|
+
`<link>`, and (by default) a `<rangefind-search>` box on every page.
|
|
64
|
+
|
|
65
|
+
That's it — search works on the deployed static site with no server.
|
|
66
|
+
|
|
67
|
+
## Configuration
|
|
68
|
+
|
|
69
|
+
All options are optional. Defaults shown.
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
plugins:
|
|
73
|
+
- rangefind:
|
|
74
|
+
enabled: true # master switch; false = complete no-op
|
|
75
|
+
base_url: "" # URL prefix baked into result links stored in
|
|
76
|
+
# the index (CLI --base-url). Empty = derive from
|
|
77
|
+
# mkdocs `site_url` path, falling back to "/".
|
|
78
|
+
output_dir: rangefind # index output dir, relative to the built site
|
|
79
|
+
assets_dir: _rangefind # where the widget JS/CSS are copied, rel. to site
|
|
80
|
+
theme: false # also inject the optional framework-free stylesheet
|
|
81
|
+
node_command: npx # command used to run the Rangefind CLI
|
|
82
|
+
placement: body_end # "body_end" | "manual" (see below)
|
|
83
|
+
selector: "" # optional literal HTML marker to place the widget after
|
|
84
|
+
placeholder: Search # input placeholder for the injected element
|
|
85
|
+
element_attributes: {} # extra attributes for <rangefind-search>
|
|
86
|
+
element_js_path: "" # explicit path to rangefind-search.js (advanced)
|
|
87
|
+
element_css_path: "" # explicit path to rangefind-search.css (advanced)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### `base_url`
|
|
91
|
+
|
|
92
|
+
This is passed to the crawler as `--base-url` and controls the URL prefix stored
|
|
93
|
+
in the **result links** inside the index. Left empty, the plugin derives it from
|
|
94
|
+
your MkDocs `site_url` path (e.g. `https://example.com/docs/` → `/docs/`),
|
|
95
|
+
falling back to `/`. Set it explicitly if you need a different prefix. Note this
|
|
96
|
+
is unrelated to where the widget loads the index from — that is always resolved
|
|
97
|
+
as a path *relative to each page*, so nested and subpath-deployed sites work
|
|
98
|
+
without configuration.
|
|
99
|
+
|
|
100
|
+
### Placement modes
|
|
101
|
+
|
|
102
|
+
- **`body_end`** (default): the plugin injects
|
|
103
|
+
`<rangefind-search src="…"></rangefind-search>` right before `</body>` on every
|
|
104
|
+
page. Search "just works" out of the box.
|
|
105
|
+
- **`manual`**: the plugin injects the component's `<script>` (and theme `<link>`
|
|
106
|
+
if `theme: true`) but does **not** inject the `<rangefind-search>` element.
|
|
107
|
+
Hand-place it yourself in a
|
|
108
|
+
[theme override](https://www.mkdocs.org/user-guide/customizing-your-theme/#using-the-theme-custom_dir),
|
|
109
|
+
e.g. `<rangefind-search src="/rangefind/"></rangefind-search>`. Use this when
|
|
110
|
+
you want the search box in a specific spot (a header, a sidebar) rather than at
|
|
111
|
+
the end of the body.
|
|
112
|
+
|
|
113
|
+
```yaml
|
|
114
|
+
plugins:
|
|
115
|
+
- rangefind:
|
|
116
|
+
placement: manual
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- **`selector`**: a middle ground. Provide a literal HTML substring that appears
|
|
120
|
+
in your rendered pages (for example a placeholder you put in a template or
|
|
121
|
+
Markdown page: `<div id="rangefind"></div>`). The `<rangefind-search>` element
|
|
122
|
+
is inserted immediately after the first occurrence of that exact string. If the
|
|
123
|
+
marker is not found on a page, the plugin falls back to `body_end`.
|
|
124
|
+
|
|
125
|
+
### Styling / Tailwind
|
|
126
|
+
|
|
127
|
+
The `<rangefind-search>` component renders into **light DOM** and ships no
|
|
128
|
+
styling of its own, so your site's CSS applies directly. For manual placement
|
|
129
|
+
you can pass Tailwind (or any) classes through the component's `*-class`
|
|
130
|
+
attributes via `element_attributes`:
|
|
131
|
+
|
|
132
|
+
```yaml
|
|
133
|
+
plugins:
|
|
134
|
+
- rangefind:
|
|
135
|
+
element_attributes:
|
|
136
|
+
input-class: "w-full border rounded px-3 py-2"
|
|
137
|
+
mark-class: "bg-yellow-200"
|
|
138
|
+
hotkey: true # boolean attributes: use `true`
|
|
139
|
+
router: true
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The full attribute and class-hook vocabulary (`input-class`, `option-class`,
|
|
143
|
+
`mark-class`, …, plus attributes like `page-size`, `debounce`, `suggest`,
|
|
144
|
+
`hotkey`, `router`) is documented in the Rangefind monorepo's
|
|
145
|
+
[reference guide](https://github.com/xjodoin/rangefind/blob/main/docs/reference.md#search-ui-component)
|
|
146
|
+
— this plugin does not duplicate it, it just forwards attributes verbatim.
|
|
147
|
+
|
|
148
|
+
### Asset resolution
|
|
149
|
+
|
|
150
|
+
The plugin needs the component's `rangefind-search.js` / `.css`. Rather than
|
|
151
|
+
guess where npm installed them, it asks Node to resolve them through the
|
|
152
|
+
`rangefind` package's own export map (`rangefind/element` and
|
|
153
|
+
`rangefind/element.css`). This is robust across npm/pnpm/yarn layouts because it
|
|
154
|
+
uses the package's declared entry points rather than assuming a path. If you have
|
|
155
|
+
an unusual setup, set `element_js_path` / `element_css_path` explicitly (paths
|
|
156
|
+
are resolved relative to the MkDocs project directory).
|
|
157
|
+
|
|
158
|
+
## MkDocs hooks used
|
|
159
|
+
|
|
160
|
+
- `on_post_build` — builds the index (shell-out) and copies the widget assets.
|
|
161
|
+
- `on_post_page` — injects the `<script>`/`<link>`/`<rangefind-search>` tags,
|
|
162
|
+
computing correct page-relative asset paths for nested pages.
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
mkdocs_rangefind/__init__.py,sha256=jX_c4CBr0w9_h654x1179dWThCpnLc3zPaARWt82_tQ,72
|
|
2
|
+
mkdocs_rangefind/plugin.py,sha256=CLxfrn5gxw7VnhOGszYV0aXpSiamDgP2B2xwVZxZRAw,13152
|
|
3
|
+
mkdocs_rangefind-0.1.0.dist-info/METADATA,sha256=zIoAQleFNUZQ6pEbXLfkooKRmf_HOVj9Q8Tx_CgCyfQ,7131
|
|
4
|
+
mkdocs_rangefind-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
5
|
+
mkdocs_rangefind-0.1.0.dist-info/entry_points.txt,sha256=eu9PBcfgDU6Y0yLN_cIsXTDX6Vq2EEC63dIVnD-BqTI,69
|
|
6
|
+
mkdocs_rangefind-0.1.0.dist-info/RECORD,,
|