mdshelf 1.1.1 → 1.1.2
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/README.md +66 -4
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,6 +27,7 @@ Ships as a single static Rust binary with zero runtime dependencies.
|
|
|
27
27
|
- Auto-generated index pages for folders that have no `index.md`
|
|
28
28
|
- Home page listing all mounted sites with page counts
|
|
29
29
|
- Copy or download any page's Markdown source straight from its header
|
|
30
|
+
- **Search** — keyword and semantic search across every mounted site, with [qmd](https://github.com/tobi/qmd) ranking and results limited to pages the reader may open
|
|
30
31
|
|
|
31
32
|
**Theme**
|
|
32
33
|
- Bundled `mdshelf-theme` — mobile-first, responsive, light/dark aware
|
|
@@ -439,12 +440,69 @@ Keep `mdshelf share list --json` if you want an inventory of what a deletion wou
|
|
|
439
440
|
before creating a folder's `index.md`. Serving, checking, exporting, and explaining are
|
|
440
441
|
strictly read-only.
|
|
441
442
|
|
|
443
|
+
## Search
|
|
444
|
+
|
|
445
|
+
With a `[search]` section, every page gets a search field in its header (`/` or
|
|
446
|
+
<kbd>Cmd</kbd>/<kbd>Ctrl</kbd>+<kbd>K</kbd> opens it). Results update as you type, with the
|
|
447
|
+
matching words highlighted; <kbd>Enter</kbd> opens the selected page, and **All results**
|
|
448
|
+
opens the full results page at `/__search?q=…`. It finds pages both by the words they
|
|
449
|
+
contain and by meaning — "how do we handle incidents" finds an incident response page
|
|
450
|
+
that never uses those words.
|
|
451
|
+
|
|
452
|
+
mdshelf does not rank pages itself. It works with a [qmd](https://github.com/tobi/qmd)
|
|
453
|
+
server (`npm install -g @tobilu/qmd`), and splits the work so that access rules stay
|
|
454
|
+
mdshelf's:
|
|
455
|
+
|
|
456
|
+
1. **mdshelf writes a corpus.** At startup and after every content change, mdshelf writes
|
|
457
|
+
each published page's body — frontmatter removed, drafts left out — to
|
|
458
|
+
`<corpus>/<collection>/<page path>`. The collection is the site's mount without its
|
|
459
|
+
leading `/` (`/docs/v2` → `docs-v2`). A page whose file does not end in `.md` gets `.md`
|
|
460
|
+
appended, because qmd indexes `*.md`.
|
|
461
|
+
2. **qmd indexes the corpus** and answers queries over HTTP. It never sees the vault, so
|
|
462
|
+
`allow` and `deny` lists are not searchable text.
|
|
463
|
+
3. **mdshelf filters what qmd ranks.** Each ranked page is looked up exactly as a page
|
|
464
|
+
request would be, for the reader asking. Pages they may not open and drafts are left
|
|
465
|
+
out, so a query that matches only restricted pages answers exactly like one that
|
|
466
|
+
matches nothing. Titles and snippets come from mdshelf's own copy of the page.
|
|
467
|
+
|
|
468
|
+
```toml
|
|
469
|
+
[search]
|
|
470
|
+
qmd_url = "http://127.0.0.1:4600" # the qmd server holding this site's index
|
|
471
|
+
corpus = "/var/lib/mdshelf/search" # a directory of its own: mdshelf deletes stale files here
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
Set up qmd once, with one collection per site, pointing at the corpus:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
qmd --index mysite collection add /var/lib/mdshelf/search/docs --name docs
|
|
478
|
+
qmd --index mysite embed # downloads the ~300 MB embedding model once
|
|
479
|
+
qmd --index mysite mcp --http --host 127.0.0.1 --port 4600
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
Keep the index current by running `qmd --index mysite update && qmd --index mysite embed`
|
|
483
|
+
on a timer; both only process what changed. Restart the qmd server after adding a
|
|
484
|
+
collection, since it reads the collection list at startup.
|
|
485
|
+
|
|
486
|
+
mdshelf asks qmd for keyword and vector matches fused by rank, without qmd's language-model
|
|
487
|
+
reranker, so only the embedding model is loaded: about 800 MB of memory per qmd server,
|
|
488
|
+
with queries in tens of milliseconds once warm. The first query after qmd starts loads
|
|
489
|
+
the model and takes several seconds. If qmd is unreachable, the results page says search
|
|
490
|
+
is unavailable rather than showing no matches.
|
|
491
|
+
|
|
492
|
+
Notes:
|
|
493
|
+
|
|
494
|
+
- Run qmd with `CI` unset: qmd refuses to load models when `CI` is set.
|
|
495
|
+
- qmd always skips folders named `node_modules`, `.git`, `.cache`, `vendor`, `dist` and
|
|
496
|
+
`build`, so pages under such folders are not searchable.
|
|
497
|
+
- Share-link readers get no search; anonymous visitors get the sign-in page, as for any
|
|
498
|
+
other path.
|
|
499
|
+
|
|
442
500
|
## Theme customisation
|
|
443
501
|
|
|
444
502
|
Override or extend templates by pointing `[theme].directory` at a local directory:
|
|
445
503
|
|
|
446
|
-
- `layouts/` — `base.html`, `doc.html`, `home.html`, `error.html`, `link.html`
|
|
447
|
-
- `partials/` — `header.html`, `sidebar.html`, `toc.html`, `breadcrumbs.html`, `footer.html`
|
|
504
|
+
- `layouts/` — `base.html`, `doc.html`, `home.html`, `error.html`, `link.html`, `search.html`
|
|
505
|
+
- `partials/` — `header.html`, `sidebar.html`, `toc.html`, `breadcrumbs.html`, `footer.html`, `search.html`
|
|
448
506
|
- `assets/` — static files served from `/__assets/...`
|
|
449
507
|
|
|
450
508
|
### Theme capabilities
|
|
@@ -499,8 +557,12 @@ start. It prints the redirect URI to register with Google.
|
|
|
499
557
|
(`~/.config/mdshelf/secret.key`) and the ACME cache resolve to the paths install checked.
|
|
500
558
|
- **Relative paths are pinned** to the directory you ran install in: `--tls-cert`,
|
|
501
559
|
`--tls-key` and `--acme-cache` are stored as absolute paths.
|
|
502
|
-
- **
|
|
503
|
-
|
|
560
|
+
- **Paths with spaces** work with launchd, systemd and Windows: install quotes each
|
|
561
|
+
argument for systemd, so `%` and `$` stay literal too.
|
|
562
|
+
- **OpenRC, rc.d and Windows** services are not given environment variables, so `install`
|
|
563
|
+
refuses `--auth`, and `--domain` without `--acme-cache`, there. OpenRC and rc.d also
|
|
564
|
+
split their command on spaces with no way to quote, so install refuses paths or values
|
|
565
|
+
containing spaces or shell characters on those systems.
|
|
504
566
|
|
|
505
567
|
## Serving on port 80
|
|
506
568
|
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"hasInstallScript": true,
|
|
24
24
|
"license": "MIT OR Apache-2.0",
|
|
25
25
|
"name": "mdshelf",
|
|
26
|
-
"version": "1.1.
|
|
26
|
+
"version": "1.1.2"
|
|
27
27
|
},
|
|
28
28
|
"node_modules/@isaacs/cliui": {
|
|
29
29
|
"engines": {
|
|
@@ -542,5 +542,5 @@
|
|
|
542
542
|
}
|
|
543
543
|
},
|
|
544
544
|
"requires": true,
|
|
545
|
-
"version": "1.1.
|
|
545
|
+
"version": "1.1.2"
|
|
546
546
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"artifactDownloadUrls": [
|
|
3
|
-
"https://github.com/prbski/mdshelf/releases/download/v1.1.
|
|
3
|
+
"https://github.com/prbski/mdshelf/releases/download/v1.1.2"
|
|
4
4
|
],
|
|
5
5
|
"author": "Patryk Porebski",
|
|
6
6
|
"bin": {
|
|
@@ -100,7 +100,7 @@
|
|
|
100
100
|
"zipExt": ".tar.xz"
|
|
101
101
|
}
|
|
102
102
|
},
|
|
103
|
-
"version": "1.1.
|
|
103
|
+
"version": "1.1.2",
|
|
104
104
|
"volta": {
|
|
105
105
|
"node": "18.14.1",
|
|
106
106
|
"npm": "9.5.0"
|