nimbi-cms 1.0.0

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ # MIT License
2
+
3
+ Copyright (c) Abel Vázquez Montoro
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,501 @@
1
+ ---
2
+ title: nimbiCMS
3
+ author: Abel Vázquez Montoro
4
+ date: 2026-03-16
5
+ ---
6
+
7
+ <img src="./assets/logo.png" alt="logo" style="height:256px;width:256px;" />
8
+
9
+ # nimbiCMS
10
+
11
+ [![npm version](https://img.shields.io/npm/v/nimbi-cms.svg)](https://www.npmjs.com/package/nimbi-cms) [![jsDelivr](https://img.shields.io/jsdelivr/npm/hm/nimbi-cms)](https://www.jsdelivr.com/package/npm/nimbi-cms) [![bundle size](https://img.shields.io/bundlephobia/minzip/nimbi-cms)](https://bundlephobia.com/package/nimbi-cms)
12
+
13
+ Lightweight, lightspeed, SEO-first client-side CMS
14
+
15
+ ## The project
16
+
17
+ NimbiCMS is a client-side CMS for static sites that requires **no database, no server build step, and no backend**, just a bunch of Markdown or HTML pages and a minimalistic setup
18
+
19
+ Just drop your Markdown files into a folder, serve the site (GitHub Pages, S3, etc.), and the client will crawl the content, render Markdown to HTML, hook links, manage slugs and anchors, maintain navigation and search functionalities, and update SEO tags on the fly. All that while being compliant, fast and accesible!
20
+
21
+ ![lighthouse results](./assets/lighthouse.png)
22
+
23
+ Editing content via the GitHub web editor works too—just save and refresh to see updates.
24
+
25
+ ## Features
26
+
27
+ - Client-side rendering of [GitHub‑flavored Markdown](https://github.github.com/gfm/) via [marked.js](https://marked.js.org/), no need for site compilation
28
+ - No Jekyll, no Hugo, no CI at all
29
+ - Perfect for static servers (GitHub Pages, S3, etc.)
30
+ - Optional client-side search box built from headings and excerpts (enabled by default).
31
+ - Reading time estimation
32
+ - Code is organized into small modules to ease maintenance and testing.
33
+ - Sticky per-page, dynamically generated TOC.
34
+ - [Bulma](https://bulma.io/)‑based UI components.
35
+ - Runtime updates for SEO, Open Graph and Twitter meta tags.
36
+ - Markdown headers management ([yaml_metadata_block by pandoc](https://pandoc.org/MANUAL.html#extension-yaml_metadata_block))
37
+ - Lazily loads images by default, while heuristically marking above‑the‑fold images as eager.
38
+ - Image preview modal with zoom controls, wheel zoom, drag pan, double-click to zoom, and touch pinch support.
39
+ - Syntax highlighting using [highlight.js](https://highlightjs.org/) — languages are auto-registered when detected.
40
+ - Simple theming (light/dark), Bulma and hightlight.js customization options.
41
+ - Simplified deliverables: regardless all the dynamic imports, the bundle is kept in one JS file and one CSS file
42
+ - Bundle is compact size
43
+ - JS file: 240.15 kB, gzipped 70.09 kB (for UMD bundle)
44
+ - CSS file: 409.43 kB, gzipped 41.30 kB (includes Bulma)
45
+ - All the heavy work is managed by web workers to avoid hogging the main thread
46
+ - Pluggable architecture through the available hooks
47
+ - Fully typed and [documented](docs/README.md)
48
+ - [MIT licensed](LICENSE.md)
49
+
50
+ ## Installation
51
+
52
+ ```bash
53
+ npm install
54
+ npm run build
55
+ ```
56
+
57
+ For development with live reload:
58
+
59
+ ```bash
60
+ npm run dev
61
+ ```
62
+
63
+ ## How to use
64
+
65
+ ### Basic HTML example
66
+
67
+ ```html
68
+ <!doctype html>
69
+ <html><head><meta charset="utf-8">
70
+ <script src="/dist/nimbi-cms.js"></script>
71
+ <link rel="preload" href="/dist/nimbi-cms.css" as="style" onload="this.rel='stylesheet'">
72
+ <noscript><link rel="stylesheet" href="/dist/nimbi-cms.css"></noscript>
73
+ </head><body>
74
+ <div id="app" style="height:100vh"></div>
75
+ <script>
76
+ nimbiCMS.initCMS({ el: '#app' })
77
+ </script>
78
+ </body></html>
79
+ ```
80
+
81
+ ### Bundle formats
82
+
83
+ Examples showing how to consume each shipped bundle format produced by the build.
84
+
85
+ - UMD (browser global)
86
+
87
+ ```html
88
+ <!-- include the UMD bundle and CSS -->
89
+ <link rel="stylesheet" href="/dist/nimbi-cms.css">
90
+ <script src="/dist/nimbi-cms.js"></script>
91
+ <div id="app"></div>
92
+ <script>
93
+ // UMD exposes a global `nimbiCMS` object
94
+ nimbiCMS.initCMS({ el: '#app' })
95
+ </script>
96
+ ```
97
+
98
+ - ESM (modern bundlers / `<script type="module">`)
99
+
100
+ ```html
101
+ <script type="module">
102
+ import initCMS from '/dist/nimbi-cms.es.js'
103
+ initCMS({ el: '#app' })
104
+ </script>
105
+ ```
106
+
107
+ - CJS (Node / CommonJS consumers)
108
+
109
+ ```js
110
+ const { initCMS } = require('./dist/nimbi-cms.cjs.js')
111
+ initCMS({ el: '#app' })
112
+ ```
113
+
114
+ **Notes:**
115
+
116
+ - The UMD build is a single, self-contained `dist/nimbi-cms.js` file that exposes the public API on the `nimbiCMS` global.
117
+ - The ES build is `dist/nimbi-cms.es.js` and is ideal for modern bundlers and `<script type="module">` usage.
118
+ - The CJS build is `dist/nimbi-cms.cjs.js` for CommonJS consumers.
119
+ - CSS is always shipped as `dist/nimbi-cms.css` and should be loaded alongside the script for styling. For performance optimization, it's advised to preload the CSS file like:
120
+
121
+ ```html
122
+ <link rel="preload" href="/dist/nimbi-cms.css" as="style" onload="this.rel='stylesheet'">
123
+ <noscript><link rel="stylesheet" href="/dist/nimbi-cms.css"></noscript>
124
+ ```
125
+
126
+ ### Content Workflow
127
+
128
+ Drop `.md` and/or `.html` files into your content directory. No build step is necessary; a static server that serves the files is sufficient.
129
+
130
+ > When loading `.html` files, only the `body` block is parsed. All the code in `head` is overriden, so, if you want some specific styling or script being run for that page, add them to `body`. Check `assets/playground.html` source for an example
131
+
132
+ **Required files**
133
+
134
+ - `_home.md` — required by default. You can override this with the `homePage` option to use a different `.md` or `.html` file as the home page.
135
+
136
+ ```javascript
137
+ initCMS({ el: '#app', homePage: 'index.html' })
138
+ ```
139
+
140
+ - `_navigation.md` — renders into the navbar; use Markdown links. Example nav markup:
141
+
142
+ ```
143
+ [Home](_home.md)
144
+ [Blog](blog.md)
145
+ [About](about.md)
146
+ ```
147
+
148
+ - `_404.md` — optional fallback for 404 responses. When the server responds to a requested `.md` path with an HTML document (e.g., an SPA fallback serving `index.html`), the CMS treats that as a missing markdown page and will attempt to load `/_404.md` from the configured content base so a proper 404 page can be rendered instead of the site's index HTML.
149
+
150
+ ```javascript
151
+ initCMS({ el: '#app', notFoundPage: '_404.md' })
152
+ ```
153
+
154
+ Links are converted to hash‑based navigation (`?page=…`), preserving anchors and URL passed parameters
155
+
156
+ ## Options
157
+
158
+ `initCMS(InitOptions)` mounts the CMS into a page. The table below summarizes the supported `InitOptions`. This options can be overrrided using URL parameters with the same name.
159
+
160
+ ### Core
161
+
162
+ | Option | Type | Default | Description |
163
+ |---|---:|:---:|---|
164
+ | `el` | `string` \ `Element` | required | CSS selector or DOM element used as the mount target. |
165
+ | `contentPath` | `string` | `/content` | URL path to the content folder serving `.md`/`.html` files; normalized to a relative path with trailing slash. |
166
+ | `allowUrlPathOverrides` | `boolean` | `false` | Opt-in: when `true`, `contentPath`, `homePage`, and `notFoundPage` may be overridden from the page URL (validated). |
167
+
168
+ ### Indexing and Search
169
+
170
+ | Option | Type | Default | Description |
171
+ |---|---:|:---:|---|
172
+ | `searchIndex` | `boolean` | `true` | Enable the runtime search index and render a search box. |
173
+ | `searchIndexMode` | `'eager'` \| `'lazy'` | `'eager'` | When to build the index (`'eager'` on init, `'lazy'` on first query). |
174
+ | `indexDepth` | `1 \| 2 \| 3` | `1` | How deep headings are indexed (H1, H2, H3). |
175
+ | `noIndexing` | `string[]` | — | Paths (relative) to exclude from discovery and indexing. |
176
+ | `skipRootReadme` | `boolean` | `false` | When `true`, skip link discovery inside a repository-root `README.md`. |
177
+
178
+ ### Routing and Pages
179
+
180
+ | Option | Type | Default | Description |
181
+ |---|---:|:---:|---|
182
+ | `homePage` | `string` | `'_home.md'` | Basename for the site home page (`.md` or `.html`). |
183
+ | `notFoundPage` | `string` | `'_404.md'` | Basename for the not-found page (`.md` or `.html`). |
184
+
185
+ ### Styling and Theming
186
+
187
+ | Option | Type | Default | Description |
188
+ |---|---:|:---:|---|
189
+ | `defaultStyle` | `'light'` \| `'dark'` \| `'system'` | `'light'` | Initial UI theme. |
190
+ | `bulmaCustomize` | `string` | `'none'` | `'none'` (bundled), `'local'` (load `<contentPath>/bulma.css`) or a Bulmaswatch theme name to load remotely. |
191
+ | `navbarLogo` | `string` | `'favicon'` | Small site logo placed at the leftmost position of the navbar. Supported values: `none`, `favicon` (uses PNG favicon when available), a path or URL to an image, `copy-first` (use first image from `homePage`), and `move-first` (use first image from `homePage` and remove it from the rendered page). |
192
+
193
+ ### Localization
194
+
195
+ | Option | Type | Default | Description |
196
+ |---|---:|:---:|---|
197
+ | `lang` | `string` | — | UI language code (e.g. `en`, `de`). |
198
+ | `l10nFile` | `string \| null` | `null` | Path to a JSON localization file (relative paths resolve against the page). |
199
+ | `availableLanguages` | `string[]` | — | When set, treats a leading path segment as a language code and maps slugs per-language. |
200
+
201
+ ### Caching and Performance
202
+
203
+ | Option | Type | Default | Description |
204
+ |---|---:|:---:|---|
205
+ | `cacheTtlMinutes` | `number` | `5` | TTL for slug-resolution cache entries (minutes). Set `0` to disable expiration. |
206
+ | `cacheMaxEntries` | `number` | — | Maximum entries in the router resolution cache. |
207
+ | `crawlMaxQueue` | `number` | `1000` | Upper bound on directories queued during breadth-first crawl (0 disables the guard). |
208
+
209
+ ### Advanced and Extensions
210
+
211
+ | Option | Type | Default | Description |
212
+ |---|---:|:---:|---|
213
+ | `markdownExtensions` | `Array<object>` | — | `marked`-style extension/plugin objects registered at init via `addMarkdownExtension()`. |
214
+ | `markdownPaths` | `string[]` | — | Optional host-provided list of markdown paths used by slug resolution/search. |
215
+
216
+ ## API
217
+
218
+ The `nimbi-cms` package exports a small set of helpers in addition to the default `initCMS` export. These are available as named imports in ESM and as properties on the `nimbiCMS` global in UMD builds.
219
+
220
+ For a complete listing of exported symbols and TypeScript types, see [the documentation](docs/README.md).
221
+
222
+ > **Note:** the default export (`initCMS`) and the `default` alias are intentionally excluded from the list below.
223
+
224
+ ### Version
225
+
226
+ - `getVersion()` — returns a `Promise<string>` that resolves to the shipped package version (e.g. `"0.1.0"`). Useful for displaying build metadata or detecting whether the loaded bundle matches a deployed backend.
227
+
228
+ ```js
229
+ import { getVersion } from 'nimbi-cms'
230
+ getVersion().then(v => console.log('nimbiCMS version', v))
231
+ ```
232
+
233
+ ### Hooks (extension points)
234
+
235
+ These helpers let you hook into internal rendering and navigation without forking the source.
236
+
237
+ - `addHook(name, fn)` — register a callback for one of the supported hook points (`'onPageLoad' | 'onNavBuild' | 'transformHtml'`).
238
+ - `onPageLoad(fn)` — fired after a page is rendered and inserted into the DOM. Useful for analytics, adding UI enhancements, or triggering client-side behavior once content is available.
239
+ - `onNavBuild(fn)` — fired after the navigation HTML is constructed but before it is attached to the document; ideal for mutating nav links, injecting controls, or adding a search input.
240
+ - `transformHtml(fn)` — fired just before an article node is appended; gives you access to the generated DOM element and HTML string so you can alter structure, add attributes, or instrument the output.
241
+ - `runHooks(name, ctx)` — programmatically invoke hook callbacks (useful in tests or when you want to replay a lifecycle event after manually inserting content).
242
+ - `_clearHooks()` — clear all registered hooks. This is mainly intended for unit tests so each test can start with a clean hook slate.
243
+
244
+ ```js
245
+ import { onPageLoad, onNavBuild, transformHtml } from 'nimbi-cms'
246
+
247
+ onPageLoad(({ pagePath, article }) => {
248
+ console.log('page rendered', pagePath)
249
+ // e.g. initialize custom widgets inside the loaded article
250
+ })
251
+
252
+ onNavBuild(({ navWrap }) => {
253
+ const btn = document.createElement('button')
254
+ btn.textContent = 'Toggle theme'
255
+ btn.onclick = () => setStyle('dark')
256
+ navWrap.querySelector('.navbar-end')?.appendChild(btn)
257
+ })
258
+
259
+ transformHtml((html, article) => {
260
+ // Add a data attribute to every rendered page
261
+ article.dataset.nimbiRendered = 'true'
262
+ })
263
+ ```
264
+
265
+ ### Theming & Styling helpers
266
+
267
+ - `ensureBulma(bulmaCustomize?, pageDir?)` — ensures Bulma is loaded. Pass `'local'` to load a local `bulma.css` (looks in the page directory and `/${pageDir}/bulma.css`), or pass a Bulmaswatch theme name (e.g. `'flatly'`) to load from unpkg.
268
+ - `setStyle(style)` — switch between light/dark/system modes by updating `data-theme` and the `is-dark` class on the document. This matches the behavior of the built-in theme toggle.
269
+ - `setThemeVars(vars)` — apply a set of CSS custom properties (e.g. `{ "--primary": "#06c" }`) on the document root for runtime theming without rebuilding.
270
+
271
+ ```js
272
+ import { ensureBulma, setStyle, setThemeVars } from 'nimbi-cms'
273
+
274
+ // Load a Bulmaswatch theme from unpkg
275
+ ensureBulma('flatly')
276
+
277
+ // Or load a local bulma.css (useful for self-hosted overrides)
278
+ ensureBulma('local', './content/')
279
+
280
+ setStyle('dark')
281
+ setThemeVars({ primary: '#06c', 'font-family': 'system-ui' })
282
+ ```
283
+
284
+ ### Localization helpers
285
+
286
+ - `t(key, ...args)` — translate a UI string key using the currently loaded locale dictionary. Supports parameter substitution like `t('helloUser', 'Alice')`.
287
+ - `loadL10nFile(url)` — fetch and merge a JSON localization file into the current dictionary. Does not automatically rerender UI (useful for on-demand language packs).
288
+ - `setLang(code)` — switch the UI language at runtime (e.g. `'en'`, `'de'`). This updates internal strings and affects slug resolution when `availableLanguages` is configured.
289
+
290
+ ```js
291
+ import { t, loadL10nFile, setLang } from 'nimbi-cms'
292
+
293
+ // Dynamic translation
294
+ console.log(t('navigation'))
295
+
296
+ // Load an external translations file
297
+ await loadL10nFile('/i18n/l10n.json')
298
+ setLang('de')
299
+ ```
300
+
301
+ ### Code highlighting helpers
302
+
303
+ - `registerLanguage(name, modulePath)` — dynamically register a `highlight.js` language. Useful to lazy-load rarely used languages from a CDN.
304
+ - `loadSupportedLanguages()` — preload the supported languages map used by the on-demand language loader.
305
+ - `observeCodeBlocks(root)` — scan a DOM subtree and apply syntax highlighting to code blocks using the currently registered languages.
306
+ - `setHighlightTheme(name, { useCdn })` — switch the theme used by `highlight.js` (optionally fetch the CSS from CDN when `useCdn=true`).
307
+ - `SUPPORTED_HLJS_MAP` — map of supported highlight.js language identifiers (useful for building language selection UIs).
308
+ - `BAD_LANGUAGES` — list of languages that should not be auto-registered (usually because they are unsupported or conflict with browser file types).
309
+
310
+ ```js
311
+ import { registerLanguage, observeCodeBlocks, setHighlightTheme } from 'nimbi-cms'
312
+
313
+ registerLanguage('r', 'https://unpkg.com/highlight.js/lib/languages/r.js')
314
+ observeCodeBlocks(document.body)
315
+ setHighlightTheme('monokai', { useCdn: true })
316
+ ```
317
+
318
+ ## Examples
319
+
320
+ This very site is running **nimbiCMS**!
321
+
322
+ The files used to do so, apart from the bundle, are:
323
+ * index.html
324
+ * README.md (this file)
325
+ * LICENSE.md
326
+ * _navigation.md
327
+ * _404.md
328
+ * assets/playground.html
329
+
330
+ ## Theming and Customization
331
+
332
+ Bulma is bundled by default. To alter styles at runtime, use
333
+ `bulmaCustomize` with `'local'` or a Bulmaswatch theme name:
334
+
335
+ ```js
336
+ initCMS({ el: '#app', contentPath: './content', bulmaCustomize: 'flatly' })
337
+ ```
338
+
339
+ For build‑time custom Bulma, replace the import in `src/nimbi-cms.js` with
340
+ your compiled CSS and rebuild.
341
+
342
+ Light/dark toggling is managed via `defaultStyle` or `setStyle()`; highlight
343
+ colors can be changed with `setHighlightTheme()` (CDN load if `useCdn=true`).
344
+
345
+ ## Localization
346
+
347
+ - `lang` option forces a UI language (short code, e.g. `en`, `de`).
348
+ - `l10nFile` may point to a JSON file of translations; relative paths resolve
349
+ against the page directory.
350
+ - Built‑in defaults live in `DEFAULT_L10N`; loaded files merge on top and fall
351
+ back to English.
352
+
353
+ To localize content (e.g. `content/en/` and `content/fr/`), point `contentPath`
354
+ at the desired language directory (for example, `contentPath: './content/en/'`).
355
+ The `lang` option only affects UI strings, not which content directory is used.
356
+
357
+ Example:
358
+
359
+ ```js
360
+ // initial render (English content + UI)
361
+ initCMS({ el: '#app', contentPath: './content/en/', lang: 'en', availableLanguages: ['en','fr'] })
362
+
363
+ // later (French content + UI). This typically requires re-initializing the
364
+ // CMS or reloading the page with the new configuration.
365
+ initCMS({ el: '#app', contentPath: './content/fr/', lang: 'fr', availableLanguages: ['en','fr'] })
366
+ ```
367
+
368
+ After initialization you can also change only the UI language at any time by
369
+ calling the exported `setLang(code)` helper. This updates internal state so
370
+ subsequent calls to `t()` return strings from the new dictionary.
371
+
372
+ > Note: `setLang()` does **not** reload or re-initialize the CMS; it only
373
+ > affects UI strings and the slug resolution logic when `availableLanguages` is
374
+ > configured. To switch content folders (e.g. `content/en/` → `content/fr/`),
375
+ > re-run `initCMS()` with a different `contentPath` (and `availableLanguages`).
376
+
377
+ Example translation file:
378
+
379
+ ```json
380
+ {
381
+ "de": { "navigation": "Navigation", "onThisPage": "Auf dieser Seite" }
382
+ }
383
+ ```
384
+
385
+ Usage:
386
+
387
+ ```js
388
+ // contentPath is optional
389
+ initCMS({ el: '#app', l10nFile: '/i18n/l10n.json', lang: 'de' })
390
+
391
+ // later, switch to French at runtime
392
+ import { setLang } from 'nimbi-cms'
393
+ setLang('fr')
394
+ ```
395
+
396
+
397
+ ### Runtime path sanitization
398
+
399
+ To reduce the risk of accidental exposure or path traversal on static hosts,
400
+ the client sanitizes and normalizes runtime path options. Important
401
+ behaviour changes:
402
+
403
+ - `contentPath`, `homePage`, and `notFoundPage` are not accepted from the
404
+ page URL query string by default. These values may be provided programmatically
405
+ via the `initCMS()` `options` object only.
406
+ - When the host page explicitly opts in by passing `allowUrlPathOverrides: true`
407
+ to `initCMS()`, the library will consider URL query string overrides. Even
408
+ in that mode the values are validated and unsafe values are rejected.
409
+
410
+ Sanitization rules applied client-side:
411
+
412
+ - `contentPath`: must be a non-empty string, must not contain `..` segments,
413
+ must not be an absolute URL (no `protocol://`), must not start with `//`,
414
+ and must not be an absolute filesystem path (leading `/` or Windows drive
415
+ prefix). The value is normalized to a relative path with a trailing slash.
416
+ - `homePage` / `notFoundPage`: must be a simple basename (no slashes), only
417
+ contain letters, numbers, dot, underscore or hyphen, and must end with
418
+ `.md` or `.html`. Example safe names: `index.html`, `_home.md`.
419
+
420
+ If an unsafe value is detected the library will throw a `TypeError` when
421
+ initializing. Unit tests were added to cover the common misuse cases
422
+ (`tests/init.sanitization.test.js`) and the existing URL-override tests
423
+ ensure the default behaviour (ignoring URL-provided paths) remains safe.
424
+
425
+ If you need an advanced opt-in for integration tests or unusual hosting
426
+ environments, use `allowUrlPathOverrides: true` with caution and only when
427
+ you control the embedding page and the static host configuration.
428
+
429
+ ### Opt-in usage example (cautious)
430
+
431
+ If you really need URL-driven overrides (for example in an integration test
432
+ or a special controlled embed scenario), you must enable them explicitly in
433
+ script code — they cannot be enabled from the URL itself. Only do this when
434
+ you control both the embedding page and the static host's content layout.
435
+
436
+ UMD example (bundle exposes `nimbiCMS`):
437
+
438
+ ```html
439
+ <script>
440
+ // Only enable in trusted environments
441
+ nimbiCMS.initCMS({ el: '#app', allowUrlPathOverrides: true })
442
+ </script>
443
+ ```
444
+
445
+ ES module example:
446
+
447
+ ```js
448
+ import initCMS from 'nimbi-cms'
449
+
450
+ // Only enable in trusted environments where the host page is controlled
451
+ initCMS({ el: '#app', allowUrlPathOverrides: true })
452
+ ```
453
+
454
+ > Note: enabling `allowUrlPathOverrides` still runs client-side validation; if an unsafe value is supplied the call to `initCMS()` will throw a `TypeError`. Prefer passing `contentPath`, `homePage`, and `notFoundPage` directly in the `options` object from secure script code rather than relying on URL query parameters.
455
+
456
+ ## Available Bulmaswatch themes
457
+
458
+ The list of available Bulma themes is
459
+
460
+ > default, cerulean, cosmo, cyborg, darkly, flatly, journal, litera, lumen, lux, materia, minty, nuclear, pulse, sandstone, simplex, slate, solar, spacelab, superhero, united, yeti.
461
+
462
+ See previews at
463
+ <https://jenil.github.io/bulmaswatch/> and load via `bulmaCustomize` option or `ensureBulma` method.
464
+
465
+ Keep in mind that some themes do not play well with certain color schemas.
466
+
467
+ ## Using with GitHub Pages and the GitHub file editor
468
+
469
+ Nimbi CMS works well with GitHub Pages and the built-in GitHub web file editor. Minimal steps:
470
+
471
+ - Enable GitHub Pages for the repository (Settings → Pages) and choose the branch/folder you want to publish (e.g., `gh-pages` or `main` / `/docs`).
472
+ - Ensure your published site serves the built `dist` assets (upload `dist/` to the chosen branch or use a build step / GitHub Action to publish).
473
+ - Place your content under a folder (default: `content/`) or set `contentPath` when calling `initCMS()` to point somewhere else.
474
+
475
+ Editing content via the GitHub web editor:
476
+
477
+ 1. Open the repository on [GitHub](https://github.com) and navigate to the `content/` folder (or your chosen `contentPath`).
478
+ 2. Click any `.md` file, then click the pencil icon to edit the file in the browser.
479
+ 3. Make changes and commit them directly to the branch. The published site will receive the updates on the next Pages build (or immediately if you host the `dist` on the same branch).
480
+ 4. Refresh the site to see the updated content. Nimbi CMS loads content at runtime, so browser refresh shows the latest files.
481
+
482
+ Tips:
483
+ - Add or update `content/_navigation.md` to control the navigation bar; the nav is re-built when pages are crawled.
484
+ - If you publish `dist/` separately (for example to `gh-pages`), consider a GitHub Action to build and push `dist/` automatically from `main`.
485
+
486
+ > Security note: Avoid exposing sensitive paths via URL query options; do not allow untrusted runtime overrides for `contentPath`, `homePage`, or `notFoundPage` unless you validate them server- or build-side.
487
+
488
+ ## Troubleshooting
489
+
490
+ ### Content not loading / 404 pages
491
+ - Verify `contentPath` is correct and matches the directory containing your `.md` files.
492
+ - Ensure your static server is serving those files (a 404 is often because the content folder isn’t published or the path is wrong).
493
+ - If you’re using `homePage`/`notFoundPage`, confirm those files exist and are reachable (the default is `_home.md` and `_404.md`).
494
+
495
+ ### Styles not appearing / Bulma missing
496
+ - Ensure `dist/nimbi-cms.css` is loaded alongside `dist/nimbi-cms.js`.
497
+ - If using `bulmaCustomize: 'local'`, confirm `bulma.css` exists in the content path or at `/bulma.css`.
498
+ - If using a Bulmaswatch theme, verify the theme name is correct (see `Available Bulmaswatch themes`).
499
+
500
+ ### Scripts failing / no mount element
501
+ - Make sure the mount element exists (`<div id="app"></div>`) and `initCMS({ el: '#app' })` uses the correct selector.