@glossarist/concept-browser 0.4.18 → 0.5.1
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 +32 -0
- package/cli/index.mjs +4 -0
- package/package.json +1 -1
- package/scripts/generate-data.mjs +2 -1
- package/src/adapters/factory.ts +3 -2
- package/src/composables/use-render-options.ts +2 -2
- package/src/config/use-site-config.ts +1 -1
- package/src/stores/vocabulary.ts +1 -1
- package/src/views/NewsView.vue +1 -1
- package/src/views/PageView.vue +3 -2
package/README.md
CHANGED
|
@@ -291,6 +291,38 @@ BASE_PATH=/vocab/ npm run build
|
|
|
291
291
|
|
|
292
292
|
This sets the Vite `base` config so all asset paths are prefixed correctly.
|
|
293
293
|
|
|
294
|
+
**How it works:**
|
|
295
|
+
|
|
296
|
+
The `BASE_PATH` environment variable controls subpath deployment through two mechanisms:
|
|
297
|
+
|
|
298
|
+
1. **Build time (Node.js scripts):** `generate-data.mjs` uses `BASE_PATH` to prefix logo paths in the generated `site-config.json`. The favicon generation in `cli/index.mjs` also prefixes favicon link hrefs. These are build-time rewrites because they produce static JSON/HTML that can't use Vite's runtime resolution.
|
|
299
|
+
|
|
300
|
+
2. **Runtime (browser):** The Vue app uses `import.meta.env.BASE_URL` (a Vite compile-time constant derived from `base`) to prefix all `fetch()` paths. This includes:
|
|
301
|
+
- `datasets.json` (dataset registry)
|
|
302
|
+
- `site-config.json` (branding, features)
|
|
303
|
+
- `data/{id}/...` (concept data via `DatasetAdapter`)
|
|
304
|
+
- `pages/*.json` (content pages)
|
|
305
|
+
- `news.json` (news feed)
|
|
306
|
+
- `data/{id}/bibliography.json` and `data/{id}/images/*` (render-time resources)
|
|
307
|
+
|
|
308
|
+
The Vite `base` config normalizes trailing slashes automatically, so `BASE_PATH=/vocab` and `BASE_PATH=/vocab/` both work. The value is passed through to `import.meta.env.BASE_URL` which always ends with `/`.
|
|
309
|
+
|
|
310
|
+
**CLI usage:**
|
|
311
|
+
|
|
312
|
+
When using the `concept-browser` CLI from a deployment repo (not the package source):
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
git clone --branch fix/subpath-base-url --depth 1 https://github.com/glossarist/concept-browser.git /tmp/cb
|
|
316
|
+
cd /tmp/cb && npm install --ignore-scripts
|
|
317
|
+
npm install --prefix /tmp/cb sharp 2>/dev/null || true
|
|
318
|
+
|
|
319
|
+
# Build from the deployment repo's working directory
|
|
320
|
+
cd /path/to/deployment-repo
|
|
321
|
+
node /tmp/cb/cli/index.mjs build
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
The CLI looks for `site-config.yml` in the CWD first, then falls back to the package's own `site-config.yml`.
|
|
325
|
+
|
|
294
326
|
### Other hosting platforms
|
|
295
327
|
|
|
296
328
|
The build produces static files in `dist/` with an SPA `404.html` fallback. Deploy `dist/` to any static host:
|
package/cli/index.mjs
CHANGED
|
@@ -141,6 +141,10 @@ Environment:
|
|
|
141
141
|
|
|
142
142
|
// Pass favicon tags to Vite via env
|
|
143
143
|
if (faviconHtml) {
|
|
144
|
+
const basePath = process.env.BASE_PATH?.replace(/\/+$/, '') || '';
|
|
145
|
+
if (basePath) {
|
|
146
|
+
faviconHtml = faviconHtml.replace(/(href|content)="\/([^"]+)"/g, `$1="${basePath}/$2"`);
|
|
147
|
+
}
|
|
144
148
|
process.env.FAVICON_HTML = faviconHtml;
|
|
145
149
|
}
|
|
146
150
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Vue SPA for browsing Glossarist terminology datasets with cross-reference resolution, graph visualization, and multi-language support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1063,10 +1063,11 @@ const processedPages = processPages(config);
|
|
|
1063
1063
|
// Generate site-config.json from site config
|
|
1064
1064
|
const siteBranding = { ...config.branding };
|
|
1065
1065
|
// Rewrite logo paths to destination filenames and strip build-time fields
|
|
1066
|
+
const basePathPrefix = process.env.BASE_PATH?.replace(/\/+$/, '') || '';
|
|
1066
1067
|
for (const key of ['logo', 'footerLogo']) {
|
|
1067
1068
|
const suffix = key === 'logo' ? 'logo.svg' : 'footer-logo.svg';
|
|
1068
1069
|
if (siteBranding[key]) {
|
|
1069
|
-
siteBranding[key] = { ...siteBranding[key], path:
|
|
1070
|
+
siteBranding[key] = { ...siteBranding[key], path: `${basePathPrefix}/logos/${config.id}-${suffix}` };
|
|
1070
1071
|
delete siteBranding[key].localPath;
|
|
1071
1072
|
delete siteBranding[key].remoteUrl;
|
|
1072
1073
|
}
|
package/src/adapters/factory.ts
CHANGED
|
@@ -18,9 +18,10 @@ export class AdapterFactory {
|
|
|
18
18
|
if (!resp.ok) throw new Error(`Failed to load dataset registry: ${resp.status}`);
|
|
19
19
|
const registry = (await resp.json()) as DatasetRegistry[];
|
|
20
20
|
|
|
21
|
+
const base = import.meta.env.BASE_URL;
|
|
21
22
|
const adapters: DatasetAdapter[] = [];
|
|
22
23
|
for (const reg of registry) {
|
|
23
|
-
const adapter = new DatasetAdapter(reg.id,
|
|
24
|
+
const adapter = new DatasetAdapter(reg.id, `${base}data/${reg.id}`);
|
|
24
25
|
this.adapters.set(reg.id, adapter);
|
|
25
26
|
adapters.push(adapter);
|
|
26
27
|
}
|
|
@@ -45,7 +46,7 @@ export class AdapterFactory {
|
|
|
45
46
|
const manifest = await adapter.loadManifest();
|
|
46
47
|
await adapter.loadIndex();
|
|
47
48
|
|
|
48
|
-
this.router.registerDataset(registerId,
|
|
49
|
+
this.router.registerDataset(registerId, `${import.meta.env.BASE_URL}data/${registerId}`, manifest);
|
|
49
50
|
|
|
50
51
|
const uriPatterns = [
|
|
51
52
|
manifest.datasetUri,
|
|
@@ -14,7 +14,7 @@ const bibCache = new Map<string, Record<string, BibEntry>>();
|
|
|
14
14
|
async function loadBibliography(registerId: string): Promise<Record<string, BibEntry> | null> {
|
|
15
15
|
if (bibCache.has(registerId)) return bibCache.get(registerId)!;
|
|
16
16
|
try {
|
|
17
|
-
const resp = await fetch(
|
|
17
|
+
const resp = await fetch(`${import.meta.env.BASE_URL}data/${registerId}/bibliography.json`);
|
|
18
18
|
if (!resp.ok) return null;
|
|
19
19
|
const data = await resp.json();
|
|
20
20
|
bibCache.set(registerId, data);
|
|
@@ -47,7 +47,7 @@ export function useRenderOptions(registerId: () => string) {
|
|
|
47
47
|
|
|
48
48
|
const figResolver: FigResolver = (figId) => {
|
|
49
49
|
const id = registerId();
|
|
50
|
-
const imgSrc =
|
|
50
|
+
const imgSrc = `${import.meta.env.BASE_URL}data/${id}/images/${figId}.png`;
|
|
51
51
|
return `<span class="fig-ref"><a href="${escapeAttr(imgSrc)}" target="_blank" rel="noopener">${escapeAttr(figId)}</a></span>`;
|
|
52
52
|
};
|
|
53
53
|
|
|
@@ -94,7 +94,7 @@ function applyBranding(config: RuntimeSiteConfig) {
|
|
|
94
94
|
async function loadConfig(): Promise<RuntimeSiteConfig | null> {
|
|
95
95
|
if (loaded.value) return siteConfig.value;
|
|
96
96
|
try {
|
|
97
|
-
const resp = await fetch(
|
|
97
|
+
const resp = await fetch(`${import.meta.env.BASE_URL}site-config.json`);
|
|
98
98
|
if (resp.ok) {
|
|
99
99
|
siteConfig.value = await resp.json();
|
|
100
100
|
if (siteConfig.value) applyBranding(siteConfig.value);
|
package/src/stores/vocabulary.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const useVocabularyStore = defineStore('vocabulary', () => {
|
|
|
52
52
|
loading.value = true;
|
|
53
53
|
error.value = null;
|
|
54
54
|
try {
|
|
55
|
-
const adapters = await factory.discoverDatasets(
|
|
55
|
+
const adapters = await factory.discoverDatasets(`${import.meta.env.BASE_URL}datasets.json`);
|
|
56
56
|
for (const adapter of adapters) {
|
|
57
57
|
datasets.value.set(adapter.registerId, adapter);
|
|
58
58
|
if (adapter.manifest) {
|
package/src/views/NewsView.vue
CHANGED
|
@@ -23,7 +23,7 @@ const activeLoading = ref(false);
|
|
|
23
23
|
|
|
24
24
|
onMounted(async () => {
|
|
25
25
|
try {
|
|
26
|
-
const resp = await fetch(
|
|
26
|
+
const resp = await fetch(`${import.meta.env.BASE_URL}news.json`);
|
|
27
27
|
if (resp.ok) posts.value = await resp.json();
|
|
28
28
|
} catch (e: any) {
|
|
29
29
|
error.value = e.message;
|
package/src/views/PageView.vue
CHANGED
|
@@ -23,9 +23,10 @@ onMounted(async () => {
|
|
|
23
23
|
const page = pageName.value;
|
|
24
24
|
const dsId = registerId.value;
|
|
25
25
|
|
|
26
|
+
const base = import.meta.env.BASE_URL;
|
|
26
27
|
const urls = dsId
|
|
27
|
-
? [
|
|
28
|
-
: [
|
|
28
|
+
? [`${base}pages/${dsId}-${page}.json`, `${base}pages/${page}.json`]
|
|
29
|
+
: [`${base}pages/${page}.json`];
|
|
29
30
|
|
|
30
31
|
for (const url of urls) {
|
|
31
32
|
try {
|