Flatten cmjs/ into the repo root (AI)
The repo is already named cmjs, so the nested cmjs/ subfolder was redundant. Moved css/, js/, strings/, templates/, and the gitignored articles/ up to the repo root and removed the empty cmjs/ directory, so the whole repo (or just css/js/strings/templates/articles) can be dropped into an existing site and embedded as-is — no wrapper folder required. This also fixes a real bug: js/config.js's MARKED_URL previously reached outside cmjs/ (../../js/marked.min.js) to find the vendored marked.min.js, which already lived at the repo root as a sibling of cmjs/ rather than inside it. Now that both files sit in the same js/ folder, MARKED_URL is a same-directory reference and no longer depends on anything outside this one folder tree. BASE_URL's own formula is unchanged (config.js is still exactly one level below whatever holds articles/, strings/, and templates/ — previously that was cmjs/, now it's the repo root). Updated index.html, .gitignore (/cmjs/articles/ -> /articles/), and every path reference throughout README.md (project structure diagram, embedding instructions, nginx config, discovery/caching docs) to match. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AXSVPS3UmmASiiprPfcnUd
This commit is contained in:
1 parent
d3c6789cfe
commit
1dc5e16d14
25 files changed
+100
-99
No files matched your search
+1
-2
@@ -1,2 +1 @@
|
||||
/cmjs/articles/
|
||||
/cmjs/pictures/
|
||||
/articles/
|
||||
@@ -4,7 +4,7 @@ A minimal CMS where articles are Markdown files rendered in the browser. No buil
|
||||
|
||||
## How it works
|
||||
|
||||
1. The browser traverses `cmjs/articles/` via a server directory index — discovering authors and
|
||||
1. The browser traverses `articles/` via a server directory index — discovering authors and
|
||||
their article files; nginx JSON autoindex is preferred, with HTML autoindexes supported as a
|
||||
fallback.
|
||||
2. Sorting, filtering, and publish status all come from the article file's own *name*
|
||||
@@ -36,50 +36,51 @@ A minimal CMS where articles are Markdown files rendered in the browser. No buil
|
||||
|
||||
## Project structure
|
||||
|
||||
This whole repo *is* cmjs — clone it as-is into an existing site (or copy `css/`, `js/`,
|
||||
`strings/`, `templates/`, and your own `articles/` into one) and embed it into a page there; see
|
||||
"Embedding in an existing page" below. `index.html` plus the root `css/` files are just a
|
||||
standalone demo/preview shell, not something an embedding page needs to bring along.
|
||||
|
||||
```
|
||||
cmjs/
|
||||
├── README.md
|
||||
├── index.html # Single-page shell — served from the repo root
|
||||
├── css/ # Site-level styles for index.html
|
||||
│ ├── reset.css # CSS variables, dark mode, box-sizing reset, focus styles
|
||||
│ ├── header.css # Site header, title link, search form
|
||||
│ └── layout.css # Main content wrapper, state messages, responsive
|
||||
├── index.html # Single-page demo shell — served from the repo root
|
||||
├── css/
|
||||
│ ├── reset.css # Demo shell: CSS variables, dark mode, box-sizing reset, focus styles
|
||||
│ ├── header.css # Demo shell: site header, title link, search form
|
||||
│ ├── layout.css # Demo shell: main content wrapper, state messages, responsive
|
||||
│ └── components/ # Component styles — needed by any embedding page
|
||||
│ ├── card.css # Article list cards and filter heading
|
||||
│ ├── article.css # Single article view, back link, hero image
|
||||
│ ├── content.css # Rendered markdown styles (.content *)
|
||||
│ ├── pagination.css # Prev/next page navigation
|
||||
│ ├── toc.css # Auto-generated table of contents
|
||||
│ └── author-profile.css # Author profile card (avatar, bio, social links)
|
||||
├── js/
|
||||
│ └── marked.min.js # marked.js (local copy, loaded as a global)
|
||||
└── cmjs/ # App content — articles, templates, component styles
|
||||
├── css/
|
||||
│ └── components/
|
||||
│ ├── card.css # Article list cards and filter heading
|
||||
│ ├── article.css # Single article view, back link, hero image
|
||||
│ ├── content.css # Rendered markdown styles (.content *)
|
||||
│ ├── pagination.css # Prev/next page navigation
|
||||
│ ├── toc.css # Auto-generated table of contents
|
||||
│ └── author-profile.css # Author profile card (avatar, bio, social links)
|
||||
├── js/
|
||||
│ ├── config.js # BASE_URL, MARKED_URL (all via import.meta.url)
|
||||
│ ├── frontmatter.js # parseFrontmatter() — pure YAML-front parser
|
||||
│ ├── templates.js # loadTemplates(), templates cache
|
||||
│ ├── discovery.js # discoverArticles(), fetchJson(), article cache
|
||||
│ ├── cache.js # IndexedDB cache for discovered articles, keyed by article file path
|
||||
│ ├── concurrency.js # mapWithConcurrency() — caps simultaneous per-article fetches
|
||||
│ ├── i18n.js # t(), loadStrings(), getLocale() — UI string lookup
|
||||
│ ├── views.js # loadArticleList, loadArticle, loadAuthorView, loadCategoryView, loadSearch
|
||||
│ └── router.js # Hash router and app entry point
|
||||
├── strings/
|
||||
│ ├── en.json # UI strings (default/fallback locale) — see "Translations" below
|
||||
│ ├── cs.json # Czech translation
|
||||
│ └── sk.json # Slovak translation
|
||||
├── templates/
|
||||
│ ├── card.html # Article list card layout
|
||||
│ ├── article.html # Full article view layout
|
||||
│ └── author-profile.html # Author profile card layout
|
||||
└── articles/
|
||||
└── author/ # One directory per author
|
||||
├── author.md # Author profile (optional)
|
||||
├── media/ # Shared images for this author — see "Adding an article"
|
||||
│ ├── avatar.jpg # Referenced by author.md's `avatar:` field
|
||||
│ └── cover.jpg # Referenced by one or more articles' `image:` field
|
||||
└── slug~DDMMYYYY~tag1+tag2.md # One file per article — see "Adding an article"
|
||||
│ ├── marked.min.js # marked.js (local copy, loaded as a global)
|
||||
│ ├── config.js # BASE_URL, MARKED_URL (all via import.meta.url)
|
||||
│ ├── frontmatter.js # parseFrontmatter() — pure YAML-front parser
|
||||
│ ├── templates.js # loadTemplates(), templates cache
|
||||
│ ├── discovery.js # discoverArticles(), fetchJson(), article cache
|
||||
│ ├── cache.js # IndexedDB cache for discovered articles, keyed by article file path
|
||||
│ ├── concurrency.js # mapWithConcurrency() — caps simultaneous per-article fetches
|
||||
│ ├── i18n.js # t(), loadStrings(), getLocale() — UI string lookup
|
||||
│ ├── views.js # loadArticleList, loadArticle, loadAuthorView, loadCategoryView, loadSearch
|
||||
│ └── router.js # Hash router and app entry point
|
||||
├── strings/
|
||||
│ ├── en.json # UI strings (default/fallback locale) — see "Translations" below
|
||||
│ ├── cs.json # Czech translation
|
||||
│ └── sk.json # Slovak translation
|
||||
├── templates/
|
||||
│ ├── card.html # Article list card layout
|
||||
│ ├── article.html # Full article view layout
|
||||
│ └── author-profile.html # Author profile card layout
|
||||
└── articles/
|
||||
└── author/ # One directory per author
|
||||
├── author.md # Author profile (optional)
|
||||
├── media/ # Shared images for this author — see "Adding an article"
|
||||
│ ├── avatar.jpg # Referenced by author.md's `avatar:` field
|
||||
│ └── cover.jpg # Referenced by one or more articles' `image:` field
|
||||
└── slug~DDMMYYYY~tag1+tag2.md # One file per article — see "Adding an article"
|
||||
```
|
||||
|
||||
## Adding an article
|
||||
@@ -116,10 +117,10 @@ markdown with a path relative to the author directory, e.g. `image: media/photo.
|
||||
|
||||
Steps:
|
||||
|
||||
1. Create the author directory if it doesn't exist: `cmjs/articles/author/`
|
||||
1. Create the author directory if it doesn't exist: `articles/author/`
|
||||
2. Create the Markdown file directly inside it, using the naming convention above, e.g.
|
||||
`cmjs/articles/author/my-article~16062026~guides+year-2026.md`
|
||||
3. Optionally add a cover image to that author's shared `cmjs/articles/author/media/` folder
|
||||
`articles/author/my-article~16062026~guides+year-2026.md`
|
||||
3. Optionally add a cover image to that author's shared `articles/author/media/` folder
|
||||
(creating it if needed), and reference it from frontmatter with `image: media/photo.jpg`.
|
||||
4. Add a frontmatter block at the top of the Markdown file
|
||||
5. Write your content — it will appear in the list automatically
|
||||
@@ -157,7 +158,7 @@ the directory name described above.
|
||||
To add a profile for an author, create a file named `{author}.md` directly inside the author's directory:
|
||||
|
||||
```
|
||||
cmjs/articles/bob/bob.md
|
||||
articles/bob/bob.md
|
||||
```
|
||||
|
||||
The profile appears as a card above the article list when a visitor filters by that author. It is optional — authors without a profile file work normally.
|
||||
@@ -200,33 +201,33 @@ The body text below the frontmatter is rendered as markdown and shown as the bio
|
||||
| `#category/year-2026/page/2` | Same, page 2 |
|
||||
| `#tag/guides` | All articles tagged "guides", page 1 |
|
||||
| `#tag/guides/page/2` | Same, page 2 |
|
||||
| `#post/alice/my-article` | Single article at `cmjs/articles/alice/my-article~DDMMYYYY~tags.md` |
|
||||
| `#post/alice/my-article` | Single article at `articles/alice/my-article~DDMMYYYY~tags.md` |
|
||||
|
||||
Author names and category tags on each card and article are clickable links to their respective filter views. A search box in the header navigates to `#search/query`.
|
||||
|
||||
## Customising the layout
|
||||
|
||||
The HTML structure for the article list card, the full article view, and the author profile
|
||||
card live in `cmjs/templates/`:
|
||||
card live in `templates/`:
|
||||
|
||||
- `cmjs/templates/card.html` — the card shown in the article list
|
||||
- `cmjs/templates/article.html` — the full article view (header, hero image, content)
|
||||
- `cmjs/templates/author-profile.html` — the profile card (avatar, bio, social links)
|
||||
- `templates/card.html` — the card shown in the article list
|
||||
- `templates/article.html` — the full article view (header, hero image, content)
|
||||
- `templates/author-profile.html` — the profile card (avatar, bio, social links)
|
||||
|
||||
Edit these files directly to change layout — no JS changes needed. Elements are identified by `data-field` attributes, which is how the JS modules find and populate them. Keep existing `data-field` values when rearranging; add new ones if you add new fields.
|
||||
|
||||
## Translations
|
||||
|
||||
Every UI string the JS renders (loading/error states, pagination, filter labels, the table of
|
||||
contents heading, etc.) is looked up through `t(key, params)` in `cmjs/js/i18n.js` rather than
|
||||
hardcoded — `cmjs/strings/en.json` is the default/fallback string table.
|
||||
contents heading, etc.) is looked up through `t(key, params)` in `js/i18n.js` rather than
|
||||
hardcoded — `strings/en.json` is the default/fallback string table.
|
||||
|
||||
Available locales: `en` (default), `cs` (Czech), `sk` (Slovak).
|
||||
|
||||
To add another translation:
|
||||
|
||||
1. Set `<html lang="xx">` in your `index.html` to the target language code (e.g. `cs`).
|
||||
2. Add `cmjs/strings/xx.json` with the same keys as `en.json` (copy it as a starting point) —
|
||||
2. Add `strings/xx.json` with the same keys as `en.json` (copy it as a starting point) —
|
||||
you only need to include the keys you're translating; anything missing quietly falls back to
|
||||
English rather than breaking.
|
||||
3. `{placeholder}` tokens in a string (e.g. `"{page} of {totalPages}"`) are substituted at
|
||||
@@ -254,12 +255,12 @@ To add cmjs to any HTML page, three things are needed: the CSS, the two mount-po
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
|
||||
<!-- Component styles -->
|
||||
<link rel="stylesheet" href="cmjs/css/components/card.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/article.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/content.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/pagination.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/toc.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/author-profile.css">
|
||||
<link rel="stylesheet" href="css/components/card.css">
|
||||
<link rel="stylesheet" href="css/components/article.css">
|
||||
<link rel="stylesheet" href="css/components/content.css">
|
||||
<link rel="stylesheet" href="css/components/pagination.css">
|
||||
<link rel="stylesheet" href="css/components/toc.css">
|
||||
<link rel="stylesheet" href="css/components/author-profile.css">
|
||||
```
|
||||
|
||||
The site-level CSS files (`reset.css`, `header.css`, `layout.css`) are optional — they style the shell around the CMS. If your page already has a layout and reset, you can skip them and only load the component stylesheets. Note that `reset.css` also defines the CSS custom properties (colours, radius) used throughout — if you skip it, define the variables yourself. `author-profile.css` is only needed if any of your authors have a profile file (see "Author profiles" above) — safe to skip otherwise.
|
||||
@@ -284,17 +285,17 @@ Optionally add a search form anywhere in the page:
|
||||
### 3. Script — before `</body>`
|
||||
|
||||
```html
|
||||
<script type="module" src="cmjs/js/router.js"></script>
|
||||
<script type="module" src="js/router.js"></script>
|
||||
```
|
||||
|
||||
`router.js` is the only script tag needed. It loads `marked.min.js` automatically at startup (path configured in `cmjs/js/config.js`).
|
||||
`router.js` is the only script tag needed. It loads `marked.min.js` automatically at startup (path configured in `js/config.js`).
|
||||
|
||||
> **Subdirectory pages:** If the embedding page lives in a subdirectory (e.g. `/blog/index.html`), relative paths like `cmjs/...` will resolve incorrectly. Use root-relative paths with a leading `/` for the CSS links and script tag:
|
||||
> **Subdirectory pages:** If the embedding page lives in a subdirectory (e.g. `/blog/index.html`) and cmjs isn't installed at the site root, a plain relative path like `css/...` will resolve against the *page's* directory, not wherever cmjs actually lives. Use a path (root-relative or otherwise) that correctly points at wherever you placed it, e.g. if cmjs lives at `/cmjs/` on your site:
|
||||
> ```html
|
||||
> <link rel="stylesheet" href="/cmjs/css/components/card.css">
|
||||
> <script type="module" src="/cmjs/js/router.js"></script>
|
||||
> ```
|
||||
> All internal fetches (articles, templates, marked.js) resolve relative to the module files via `import.meta.url` and work automatically — only the HTML tags need the `/` prefix.
|
||||
> All internal fetches (articles, templates, marked.js) resolve relative to the module files via `import.meta.url` and work automatically — only the HTML tags need a path pointing at the right place.
|
||||
|
||||
### Minimal example
|
||||
|
||||
@@ -305,24 +306,24 @@ Optionally add a search form anywhere in the page:
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>My Site</title>
|
||||
<link rel="stylesheet" href="cmjs/css/components/card.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/article.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/content.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/pagination.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/toc.css">
|
||||
<link rel="stylesheet" href="css/components/card.css">
|
||||
<link rel="stylesheet" href="css/components/article.css">
|
||||
<link rel="stylesheet" href="css/components/content.css">
|
||||
<link rel="stylesheet" href="css/components/pagination.css">
|
||||
<link rel="stylesheet" href="css/components/toc.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="list-view"></div>
|
||||
<div id="article-view" hidden></div>
|
||||
|
||||
<script type="module" src="cmjs/js/router.js"></script>
|
||||
<script type="module" src="js/router.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## nginx configuration
|
||||
|
||||
Point nginx `root` at the repo root. The `autoindex` directive applies recursively to all subdirectories under `/cmjs/articles/`.
|
||||
Point nginx `root` at the repo root. The `autoindex` directive applies recursively to all subdirectories under `/articles/`.
|
||||
|
||||
```nginx
|
||||
server {
|
||||
@@ -336,7 +337,7 @@ server {
|
||||
}
|
||||
|
||||
# Enable JSON directory listing for article discovery (applies to all subdirs)
|
||||
location /cmjs/articles/ {
|
||||
location /articles/ {
|
||||
autoindex on;
|
||||
autoindex_format json;
|
||||
autoindex_localtime off; # keep mtimes in GMT — the crawler's cache relies on consistent, unambiguous timestamps
|
||||
@@ -375,7 +376,7 @@ not built into stock nginx — only worth it if you're already building custom n
|
||||
|
||||
## Article discovery & caching
|
||||
|
||||
Discovery happens in two phases. **Indexing** crawls `cmjs/articles/` client-side to build a
|
||||
Discovery happens in two phases. **Indexing** crawls `articles/` client-side to build a
|
||||
lightweight "stub" for every article — the author directories, then each author's own directory
|
||||
listing (their article files, bio file, and shared `media/` folder together) along with each
|
||||
file's `mtime`. Non-article entries (the author's own `{author}.md`, the `media/` folder) are
|
||||
@@ -385,13 +386,13 @@ listing is enough to sort, filter, paginate, and populate the sidebar's tag/year
|
||||
any further request per article and without ever fetching an article's actual Markdown.
|
||||
**Hydration** is the separate, deferred step that fetches a specific article's content (title,
|
||||
description, cover image, reading time) — it only ever runs for articles about to be rendered:
|
||||
the current page's cards (`hydrateArticles()` in `cmjs/js/discovery.js`), a single visited
|
||||
the current page's cards (`hydrateArticles()` in `js/discovery.js`), a single visited
|
||||
article, or (since full-text search needs real content) as many search results as the page being
|
||||
viewed actually needs. A large site's list is therefore fast to show regardless of how many
|
||||
articles it has — indexing costs one request per author, full stop, and only ~20 articles' worth
|
||||
of content is ever fetched per page view.
|
||||
|
||||
- **Cache** — hydrated articles are cached per article file in IndexedDB (`cmjs/js/cache.js`),
|
||||
- **Cache** — hydrated articles are cached per article file in IndexedDB (`js/cache.js`),
|
||||
keyed by the file's own path and invalidated by comparing its live `mtime` against the cached
|
||||
one — once *any* visit renders an article, every later visit skips its content fetch too, until
|
||||
the file is renamed (a rename changes the cache key, so a retagged/redated article is treated as
|
||||
@@ -401,11 +402,11 @@ of content is ever fetched per page view.
|
||||
(private browsing, disabled storage), discovery transparently falls back to fetching everything
|
||||
live on every visit.
|
||||
- **Cache schema version** — the cached records store the full derived article shape, not raw
|
||||
content, so a code change to how articles are derived (in `cmjs/js/discovery.js`) can make old
|
||||
content, so a code change to how articles are derived (in `js/discovery.js`) can make old
|
||||
cached entries stale in *structure*, not just content — and a file's `mtime` won't catch that.
|
||||
Bump `CACHE_SCHEMA_VERSION` in `cmjs/js/cache.js` whenever that shape changes; it's baked into
|
||||
Bump `CACHE_SCHEMA_VERSION` in `js/cache.js` whenever that shape changes; it's baked into
|
||||
the IndexedDB database name, so a bump invalidates every cached entry on the next deploy.
|
||||
- **Concurrency** — `MAX_CONCURRENT_REQUESTS` in `cmjs/js/discovery.js` (default `10`) caps how
|
||||
- **Concurrency** — `MAX_CONCURRENT_REQUESTS` in `js/discovery.js` (default `10`) caps how
|
||||
many hydration (content) fetches run at once — the only per-article requests left, since
|
||||
indexing no longer needs one; raise it for a fast/local nginx, lower it for a slow or shared/
|
||||
production instance.
|
||||
@@ -415,7 +416,7 @@ of content is ever fetched per page view.
|
||||
- **Paused/resumable search** — search needs real text (title, description), unlike the list
|
||||
view, so it hydrates articles rather than just reading stubs — but only as many as the page
|
||||
being viewed needs, in the same newest-first order as everything else (`loadSearch()` in
|
||||
`cmjs/js/views.js`). It scans stubs in small batches, stops as soon as it's found enough matches
|
||||
`js/views.js`). It scans stubs in small batches, stops as soon as it's found enough matches
|
||||
to fill the current page (plus one, just to know whether a next page exists), and resumes
|
||||
exactly where it left off — no re-checking — if the user pages further into the *same* query.
|
||||
Paging back needs no scanning at all, since matches only ever accumulate. This keeps a search on
|
||||
@@ -426,7 +427,7 @@ of content is ever fetched per page view.
|
||||
known candidate files parsed successfully (a malformed file name, or a total breakage like the
|
||||
wrong origin or CORS misconfigured), a small warning banner is shown above the article list
|
||||
instead of silently rendering an empty page (see `getCrawlStats()` /
|
||||
`renderCrawlHealthWarning()` in `cmjs/js/discovery.js` / `cmjs/js/views.js`).
|
||||
`renderCrawlHealthWarning()` in `js/discovery.js` / `js/views.js`).
|
||||
- **Design rationale** — the original crawler design (wave-based crawling with a per-article
|
||||
listing fetch, recency prioritisation, HTML-autoindex fallback, long-term "deploy and forget"
|
||||
constraints) is documented in
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
// All paths are derived from this module's own location via import.meta.url,
|
||||
// so they resolve correctly regardless of where the embedding HTML page lives.
|
||||
|
||||
// Base URL of the cmjs/ directory (e.g. https://example.com/cmjs/)
|
||||
export const BASE_URL = new URL('../', import.meta.url).href;
|
||||
|
||||
// Path to the marked.js vendor script (lives one level above cmjs/)
|
||||
export const MARKED_URL = new URL('../../js/marked.min.js', import.meta.url).href;
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+7
-7
@@ -7,12 +7,12 @@
|
||||
<link rel="stylesheet" href="css/reset.css">
|
||||
<link rel="stylesheet" href="css/header.css">
|
||||
<link rel="stylesheet" href="css/layout.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/card.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/article.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/content.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/pagination.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/toc.css">
|
||||
<link rel="stylesheet" href="cmjs/css/components/author-profile.css">
|
||||
<link rel="stylesheet" href="css/components/card.css">
|
||||
<link rel="stylesheet" href="css/components/article.css">
|
||||
<link rel="stylesheet" href="css/components/content.css">
|
||||
<link rel="stylesheet" href="css/components/pagination.css">
|
||||
<link rel="stylesheet" href="css/components/toc.css">
|
||||
<link rel="stylesheet" href="css/components/author-profile.css">
|
||||
</head>
|
||||
<body class="blog-page">
|
||||
<header>
|
||||
@@ -43,6 +43,6 @@
|
||||
</aside>
|
||||
</main>
|
||||
|
||||
<script type="module" src="cmjs/js/router.js"></script>
|
||||
<script type="module" src="js/router.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,9 @@
|
||||
// All paths are derived from this module's own location via import.meta.url,
|
||||
// so they resolve correctly regardless of where the embedding HTML page lives.
|
||||
|
||||
// Base URL of the repo root — articles/, strings/, and templates/ all live
|
||||
// here, one level up from this file (e.g. https://example.com/).
|
||||
export const BASE_URL = new URL('../', import.meta.url).href;
|
||||
|
||||
// Path to the marked.js vendor script (bundled alongside this file)
|
||||
export const MARKED_URL = new URL('marked.min.js', import.meta.url).href;
|
||||
File renamed without changes.
File renamed without changes.
@@ -1,6 +1,6 @@
|
||||
// UI string lookup, so every user-facing message lives in a translatable
|
||||
// JSON file (cmjs/strings/{locale}.json) instead of being hardcoded in the
|
||||
// JS. English (cmjs/strings/en.json) is always fetched first as the
|
||||
// JSON file (strings/{locale}.json) instead of being hardcoded in the
|
||||
// JS. English (strings/en.json) is always fetched first as the
|
||||
// fallback set; if the page's declared language (<html lang>) names another
|
||||
// locale, that file is fetched too and merged on top — a translation file
|
||||
// only needs to cover the keys it actually translates, missing ones still
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Reference in new issue
Block a user