@glossarist/concept-browser 0.7.68 → 0.7.71
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/cli/index.mjs +28 -9
- package/package.json +14 -8
- package/public/favicon.svg +1 -4
- package/scripts/bridge-to-astro.mjs +155 -0
- package/scripts/generate-data.mjs +13 -0
- package/scripts/load-site-config.mjs +73 -1
- package/src/__tests__/tc204-deployment.test.ts +21 -0
- package/src/adapters/DatasetAdapter.ts +1 -0
- package/src/adapters/model-bridge.ts +50 -12
- package/src/adapters/non-verbal/glossarist-augment.d.ts +5 -1
- package/src/adapters/types.ts +1 -0
- package/src/assets/glossarist-logo-full.svg +1 -0
- package/src/astro-islands/app.ts +3 -0
- package/src/components/AppFooter.vue +3 -2
- package/src/components/AppHeader.vue +4 -7
- package/src/components/AppSidebar.vue +8 -5
- package/src/components/ConceptDetail.vue +12 -0
- package/src/components/DesignationList.vue +15 -4
- package/src/components/LanguageDetail.vue +6 -0
- package/src/components/RelationSphere.vue +36 -32
- package/src/components/site/SeoMeta.astro +23 -0
- package/src/components/site/SiteFooter.astro +14 -0
- package/src/components/site/SiteHeader.astro +25 -0
- package/src/components/site/SiteSidebar.astro +56 -0
- package/src/content.config.ts +85 -0
- package/src/i18n/locales/eng.yml +1 -0
- package/src/i18n/locales/fra.yml +1 -0
- package/src/islands/AppIsland.vue +7 -0
- package/src/islands/BibliographyIsland.vue +26 -0
- package/src/islands/ConceptDetailIsland.vue +27 -0
- package/src/islands/ConceptDiffIsland.vue +214 -0
- package/src/islands/ConceptIsland.vue +102 -0
- package/src/islands/GraphIsland.vue +15 -0
- package/src/islands/GroupRendererIsland.vue +15 -0
- package/src/islands/HeaderIsland.vue +10 -0
- package/src/islands/NonVerbalIsland.vue +75 -0
- package/src/islands/RelationSphereIsland.vue +20 -0
- package/src/islands/SearchIsland.vue +23 -0
- package/src/islands/SeriesCardIsland.vue +12 -0
- package/src/islands/ShortcutsIsland.vue +13 -0
- package/src/islands/SidebarIsland.vue +15 -0
- package/src/islands/SupersessionDiffIsland.vue +267 -0
- package/src/islands/app-entry.ts +8 -0
- package/src/islands/app.ts +4 -0
- package/src/layouts/Default.astro +70 -0
- package/src/lib/site-config.ts +26 -0
- package/src/pages/404.astro +9 -0
- package/src/pages/[...path].astro +29 -0
- package/src/pages/dataset/[id]/concept/[cid].astro +23 -0
- package/src/pages/dataset/[id]/index.astro +16 -0
- package/src/pages/graph.astro +5 -0
- package/src/pages/group/[id]/index.astro +16 -0
- package/src/pages/index.astro +10 -0
- package/src/pages/search.astro +5 -0
- package/src/styles/tailwind.css +503 -0
- package/src/views/ConceptView.vue +18 -23
- package/src/views/StatsView.vue +1 -1
- package/tsconfig.json +2 -1
- package/vite.config.ts +3 -1
- package/postcss.config.js +0 -13
package/cli/index.mjs
CHANGED
|
@@ -173,15 +173,34 @@ Environment:
|
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
175
|
|
|
176
|
-
//
|
|
177
|
-
console.log(`\n===
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
176
|
+
// Bridge data to Astro content collections
|
|
177
|
+
console.log(`\n=== BRIDGE DATA ===\n`);
|
|
178
|
+
const bridge = resolve(pkgRoot, 'scripts', 'bridge-to-astro.mjs');
|
|
179
|
+
if (existsSync(bridge)) {
|
|
180
|
+
await import(`file://${bridge}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Run Astro build (TODO.astro/22)
|
|
184
|
+
console.log(`\n=== BUILD ASTRO ===\n`);
|
|
185
|
+
const astroConfig = resolve(pkgRoot, 'astro.config.mjs');
|
|
186
|
+
if (existsSync(astroConfig)) {
|
|
187
|
+
try {
|
|
188
|
+
const { build: astroBuild } = await import('astro');
|
|
189
|
+
await astroBuild({ root: pkgRoot, logLevel: 'info' });
|
|
190
|
+
} catch (e) {
|
|
191
|
+
console.warn(` Astro build failed (${e.message}), falling back to Vite SPA`);
|
|
192
|
+
console.log(`\n=== BUILD SPA ===\n`);
|
|
193
|
+
const viteConfig = resolve(pkgRoot, 'vite.config.ts');
|
|
194
|
+
const { build: viteBuild } = await import('vite');
|
|
195
|
+
await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
|
|
196
|
+
}
|
|
197
|
+
} else {
|
|
198
|
+
// Fall back to Vite SPA if Astro config doesn't exist
|
|
199
|
+
console.log(`\n=== BUILD SPA ===\n`);
|
|
200
|
+
const viteConfig = resolve(pkgRoot, 'vite.config.ts');
|
|
201
|
+
const { build: viteBuild } = await import('vite');
|
|
202
|
+
await viteBuild({ configFile: viteConfig, root: pkgRoot, mode: 'production' });
|
|
203
|
+
}
|
|
185
204
|
|
|
186
205
|
// Run postbuild (404 page) via dynamic import
|
|
187
206
|
const postbuild = resolve(pkgRoot, 'scripts', 'generate-404.js');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.71",
|
|
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": {
|
|
@@ -23,29 +23,34 @@
|
|
|
23
23
|
"test": "vitest run",
|
|
24
24
|
"test:watch": "vitest",
|
|
25
25
|
"mutation:test": "stryker run stryker.config.mjs",
|
|
26
|
-
"validate:shacl": "node scripts/validate-shacl.mjs"
|
|
26
|
+
"validate:shacl": "node scripts/validate-shacl.mjs",
|
|
27
|
+
"dev:astro": "astro dev",
|
|
28
|
+
"build:astro": "astro build",
|
|
29
|
+
"preview:astro": "astro preview"
|
|
27
30
|
},
|
|
28
31
|
"dependencies": {
|
|
32
|
+
"@astrojs/sitemap": "^3.7.3",
|
|
33
|
+
"@astrojs/vue": "^7.0.1",
|
|
29
34
|
"@plurimath/plurimath": "^0.2.2",
|
|
30
|
-
"@vitejs/plugin-vue": "^
|
|
31
|
-
"
|
|
35
|
+
"@vitejs/plugin-vue": "^6.0.7",
|
|
36
|
+
"astro": "^7.0.7",
|
|
32
37
|
"d3": "^7.9.0",
|
|
33
38
|
"favicons": "^7.2.0",
|
|
34
|
-
"glossarist": "^0.4.
|
|
39
|
+
"glossarist": "^0.4.17",
|
|
35
40
|
"js-yaml": "^4.1.0",
|
|
36
41
|
"jszip": "^3.10.1",
|
|
37
42
|
"pinia": "^2.3.1",
|
|
38
|
-
"postcss": "^8.5.3",
|
|
39
43
|
"sharp": "^0.34.5",
|
|
40
|
-
"
|
|
41
|
-
"vite": "^6.3.5",
|
|
44
|
+
"vite": "^8.1.4",
|
|
42
45
|
"vue": "^3.5.13",
|
|
43
46
|
"vue-router": "^4.5.1"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
49
|
+
"@astrojs/check": "^0.9.9",
|
|
46
50
|
"@rdfjs/dataset": "^2.0.2",
|
|
47
51
|
"@stryker-mutator/core": "^9.6.1",
|
|
48
52
|
"@stryker-mutator/vitest-runner": "^9.6.1",
|
|
53
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
49
54
|
"@types/d3": "^7.4.3",
|
|
50
55
|
"@types/n3": "^1.26.1",
|
|
51
56
|
"@vue/test-utils": "^2.4.8",
|
|
@@ -53,6 +58,7 @@
|
|
|
53
58
|
"happy-dom": "^20.9.0",
|
|
54
59
|
"n3": "^1.26.0",
|
|
55
60
|
"rdf-validate-shacl": "^0.4.5",
|
|
61
|
+
"tailwindcss": "^4.3.2",
|
|
56
62
|
"typescript": "~5.7.3",
|
|
57
63
|
"vitest": "^4.1.5",
|
|
58
64
|
"vue-tsc": "^2.2.8"
|
package/public/favicon.svg
CHANGED
|
@@ -1,4 +1 @@
|
|
|
1
|
-
|
|
2
|
-
<rect width="32" height="32" rx="6" fill="#2563eb"/>
|
|
3
|
-
<text x="16" y="22" text-anchor="middle" font-family="system-ui" font-weight="bold" font-size="18" fill="white">G</text>
|
|
4
|
-
</svg>
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg id="uuid-e2e4f56e-6766-4eaf-8ba1-80c056213369" xmlns="http://www.w3.org/2000/svg" width="183.1" height="172.48" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 183.1 172.48"><defs><linearGradient id="uuid-526bfbac-cffb-4a71-b92d-c02fefae5080" x1="51.31" y1="161.8" x2="102.32" y2="73.02" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#3fb6b0"/><stop offset=".07" stop-color="#5cc5be"/><stop offset=".15" stop-color="#78d3cc"/><stop offset=".24" stop-color="#8fe0d8"/><stop offset=".34" stop-color="#a1e9e1"/><stop offset=".47" stop-color="#aef0e7"/><stop offset=".63" stop-color="#b5f3ea"/><stop offset="1" stop-color="#b8f5ec"/></linearGradient><linearGradient id="uuid-1b1e62d8-3f5c-498e-8428-628e5706abf6" x1="0" y1="59.58" x2="183.1" y2="59.58" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2d4164"/><stop offset=".47" stop-color="#2d4164" stop-opacity=".94"/><stop offset="1" stop-color="#2d4164" stop-opacity=".8"/></linearGradient><linearGradient id="uuid-b44d9113-c74c-48d0-8db3-7071ba63ee91" x1="71.68" y1="31.06" x2="111.41" y2="31.06" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2d4164"/><stop offset=".28" stop-color="#2c4365"/><stop offset="1" stop-color="#588ac1"/></linearGradient><linearGradient id="uuid-ef83f354-a6ba-455c-a262-42ad1af8aab0" x1="76.66" y1="69.37" x2="183.1" y2="69.37" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#34798a" stop-opacity=".7"/><stop offset="1" stop-color="#62c4dd"/></linearGradient><linearGradient id="uuid-9c4f3d11-41f0-40bc-8383-a054e148eefb" x1="81.4" y1="129.46" x2="127.22" y2="50.28" gradientUnits="userSpaceOnUse"><stop offset=".2" stop-color="#456399"/><stop offset="1" stop-color="#3a5c80" stop-opacity=".7"/></linearGradient><linearGradient id="uuid-75c138e5-3eb7-4084-88c8-ea7b28bec669" x1="88.72" y1="146.48" x2="54.34" y2="146.48" xlink:href="#uuid-b44d9113-c74c-48d0-8db3-7071ba63ee91"/><linearGradient id="uuid-648153de-b6a2-4cdb-bf68-e5b0950e981e" x1="86.16" y1="99.24" x2="47.06" y2="25.85" gradientUnits="userSpaceOnUse"><stop offset=".2" stop-color="#456399"/><stop offset="1" stop-color="#3a5c80" stop-opacity=".7"/></linearGradient><linearGradient id="uuid-e1874ccf-ad77-48dc-8e9c-b19bc34aaaf5" x1="89.58" y1="107.94" x2="63.88" y2="57.26" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#456399" stop-opacity=".5"/><stop offset="1" stop-color="#97a4b7" stop-opacity=".8"/></linearGradient></defs><path d="m87.41,59.48l-61.18,106.11c-1.63,2.85.6,6.85,3.98,6.88h37.88l63.56-112.75-44.23-.25Z" style="fill:url(#uuid-526bfbac-cffb-4a71-b92d-c02fefae5080); stroke-width:0px;"/><path d="m40.39,141.03c.29.28,1.01,2.7,1.61,4.91.67,2.49,2.77,4.38,5.33,4.68,21.2,2.5,21.4,20.71,21.4,20.71l22.5-39.92c-23.71,14.02-41.88,6.43-40.64-8.09-2.71,4.71-7.5,12.99-10.21,17.7Z" style="fill:#3a5c80; stroke-width:0px;"/><path d="m75.5,80.4s-14.96,25.41-18.41,31.59c8.71-8.42,17.69,2.79,24.11-8.66,8.13-14.5-5.7-22.93-5.7-22.93Z" style="fill:#60abbc; stroke-width:0px;"/><path d="m113.82,172.48h38.28c4.52-.01,6.66-3.57,4.43-7.48-6.55-11.51-59.96-105.41-59.96-105.41l-43.04-.37,60.29,113.27Z" style="fill:#b8f5ec; stroke-width:0px;"/><path d="m106.47,91.98c-9.24,2.3-34.69,12.42-21.32,20.21,12.57,7.31,28.11.2,32.05-11.73,1.79-4.66-2.29-10.67-10.74-8.47Z" style="fill:#3fb6b0; stroke-width:0px;"/><path d="m137.32,131.2l-13.09-23.07c.37,24.63-35.5,17.21-35.5,17.21l24.95,46.87c-1-25.77,10.54-35.8,23.65-41Z" style="fill:#7ee0d4; stroke-width:0px;"/><path d="m57.35,132.11s0-.01.01-.02c0,0,0,0,0,0" style="fill:#3a5c80; stroke-width:0px;"/><path d="m183.1,52.52c-.1-3.25-2.07-5.56-6-5.59-13.09-.07-163.15,0-163.15,0L0,72.26h183.1v-19.75Z" style="fill:url(#uuid-1b1e62d8-3f5c-498e-8428-628e5706abf6); stroke-width:0px;"/><path d="m71.68,47s19.67-31.93,27.9-31.9c10.36,0,15.93,31.9,8.24,31.83l-36.15.07Z" style="fill:url(#uuid-b44d9113-c74c-48d0-8db3-7071ba63ee91); stroke-width:0px;"/><path d="m183.1,59.91l-.13,10.94c.07,6.28-2.04,8.39-8.33,8.31h-97.99l10.97-19.59,95.47.33Z" style="fill:url(#uuid-ef83f354-a6ba-455c-a262-42ad1af8aab0); opacity:.78; stroke-width:0px;"/><polygon points="7.1 59.58 0 72.26 80.52 72.26 87.63 59.58 7.1 59.58" style="fill:#3a5c80; opacity:.8; stroke-width:0px;"/><path d="m117.54,47s-65.24,112.78-69.85,120.82c-1.1,1.92.38,4.63,2.65,4.65.71,0,17.74,0,17.74,0l70.73-125.47h-21.27Z" style="fill:url(#uuid-9c4f3d11-41f0-40bc-8383-a054e148eefb); opacity:.8; stroke-width:0px;"/><path d="m84.52,142.79c.55-.64.79-1.48,1.27-2.18,10.98-16.02-12.03-16.74-17.61-8.28l-12.79,23.17c-.14.24-.26.48-.38.72-3.39,7.29,6.71,13.38,11.92,7.26l17.59-20.69Z" style="fill:url(#uuid-75c138e5-3eb7-4084-88c8-ea7b28bec669); opacity:.8; stroke-width:0px;"/><path d="m124.71,126.57s0-.01-.01-.02c0,0,0,0,0,0,0,0,0,.02,0,.02Z" style="fill:#2e4365; stroke-width:0px;"/><polygon points="136.69 172.48 113.82 172.48 53.53 59.58 73.24 59.58 136.69 172.48" style="fill:url(#uuid-648153de-b6a2-4cdb-bf68-e5b0950e981e); opacity:.75; stroke-width:0px;"/><path d="m73.39,59.58l33.44,59.63c2.82,7.65-7.17,26.67-12.28,17.06L53.53,59.58h19.86Z" style="fill:url(#uuid-e1874ccf-ad77-48dc-8e9c-b19bc34aaaf5); stroke-width:0px;"/><path d="m115.87,135.34l18.22,32.5s-36.61-14.66-18.22-32.5Z" style="fill:#4977a4; stroke-width:0px;"/></svg>
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data bridge: converts existing public/data/*.json into Astro
|
|
3
|
+
* content collections at src/content/. Run before `astro build`.
|
|
4
|
+
*
|
|
5
|
+
* Reads: public/data/datasets.json, public/data/{id}/manifest.json,
|
|
6
|
+
* public/data/{id}/index.json
|
|
7
|
+
* Writes: src/content/datasets/*.json, src/content/groups/*.json,
|
|
8
|
+
* src/content/concepts/{registerId}/{conceptId}.json
|
|
9
|
+
*/
|
|
10
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from 'fs';
|
|
11
|
+
import { join, dirname } from 'path';
|
|
12
|
+
import { fileURLToPath } from 'url';
|
|
13
|
+
|
|
14
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const CWD = process.cwd();
|
|
16
|
+
const PKG_ROOT = join(__dirname, '..');
|
|
17
|
+
// Read generated data from the CONSUMER's working directory (where
|
|
18
|
+
// generate-data.mjs writes public/data/). Write content collections
|
|
19
|
+
// to the PACKAGE's src/content/ (where Astro expects them).
|
|
20
|
+
const PUBLIC_DATA = join(CWD, 'public', 'data');
|
|
21
|
+
const PUBLIC_ROOT = join(CWD, 'public');
|
|
22
|
+
const CONTENT_DIR = join(PKG_ROOT, 'src', 'content');
|
|
23
|
+
|
|
24
|
+
function readJson(p) {
|
|
25
|
+
if (!existsSync(p)) return null;
|
|
26
|
+
return JSON.parse(readFileSync(p, 'utf-8'));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function writeJson(dir, name, data) {
|
|
30
|
+
mkdirSync(dir, { recursive: true });
|
|
31
|
+
writeFileSync(join(dir, name), JSON.stringify(data, null, 2));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// --- Datasets ---
|
|
35
|
+
const registryPath = join(PUBLIC_ROOT, 'datasets.json');
|
|
36
|
+
const siteConfigPath = join(PUBLIC_ROOT, 'site-config.json');
|
|
37
|
+
const registry = readJson(registryPath) || [];
|
|
38
|
+
const siteConfig = readJson(siteConfigPath) || {};
|
|
39
|
+
|
|
40
|
+
console.log(`Bridging ${registry.length} datasets to content collections...`);
|
|
41
|
+
|
|
42
|
+
for (const reg of registry) {
|
|
43
|
+
const id = reg.id;
|
|
44
|
+
const manifestPath = join(PUBLIC_DATA, id, 'manifest.json');
|
|
45
|
+
const manifest = readJson(manifestPath);
|
|
46
|
+
if (!manifest) {
|
|
47
|
+
console.warn(` Skipping ${id}: no manifest.json`);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const summary = reg.summary || {};
|
|
52
|
+
const entry = {
|
|
53
|
+
id: manifest.id || id,
|
|
54
|
+
title: summary.title || manifest.title || id,
|
|
55
|
+
description: summary.description || manifest.description || '',
|
|
56
|
+
uri: manifest.datasetUri || reg.datasetUri || '',
|
|
57
|
+
uriBase: manifest.uriBase || reg.uriBase || '',
|
|
58
|
+
year: manifest.year ?? summary.year ?? undefined,
|
|
59
|
+
status: manifest.status || summary.status || 'valid',
|
|
60
|
+
ref: manifest.ref || reg.ref || undefined,
|
|
61
|
+
refAliases: manifest.refAliases || reg.refAliases || undefined,
|
|
62
|
+
owner: summary.owner || manifest.owner || '',
|
|
63
|
+
languages: manifest.languages || summary.languages || ['eng'],
|
|
64
|
+
conceptCount: manifest.conceptCount || summary.conceptCount || 0,
|
|
65
|
+
color: summary.color || manifest.color || undefined,
|
|
66
|
+
tags: summary.tags || manifest.tags || [],
|
|
67
|
+
sections: manifest.sections || [],
|
|
68
|
+
sourceRepo: manifest.sourceRepo || '',
|
|
69
|
+
lastUpdated: manifest.lastUpdated || '',
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Remove undefined values
|
|
73
|
+
Object.keys(entry).forEach(k => entry[k] === undefined && delete entry[k]);
|
|
74
|
+
|
|
75
|
+
writeJson(join(CONTENT_DIR, 'datasets'), `${id}.json`, entry);
|
|
76
|
+
console.log(` Dataset: ${id} (${entry.conceptCount} concepts)`);
|
|
77
|
+
|
|
78
|
+
// --- Concepts for this dataset ---
|
|
79
|
+
const indexPath = join(PUBLIC_DATA, id, 'index.json');
|
|
80
|
+
const index = readJson(indexPath);
|
|
81
|
+
if (index && index.concepts) {
|
|
82
|
+
for (const concept of index.concepts) {
|
|
83
|
+
if (!concept) continue;
|
|
84
|
+
const conceptEntry = {
|
|
85
|
+
registerId: id,
|
|
86
|
+
conceptId: concept.id,
|
|
87
|
+
uri: `${entry.uriBase || ''}/${id}/concept/${concept.id}`,
|
|
88
|
+
status: concept.status || 'valid',
|
|
89
|
+
designations: concept.designations || {},
|
|
90
|
+
eng: concept.eng || concept.designations?.eng || '',
|
|
91
|
+
groups: concept.groups || [],
|
|
92
|
+
tags: [],
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const fullConceptPath = join(PUBLIC_DATA, id, 'concepts', `${concept.id}.json`);
|
|
96
|
+
const fullConcept = readJson(fullConceptPath);
|
|
97
|
+
if (fullConcept) {
|
|
98
|
+
const lc = fullConcept['gl:localizedConcept'] || fullConcept.localizedConcept || {};
|
|
99
|
+
const localizations = {};
|
|
100
|
+
for (const [lang, loc] of Object.entries(lc)) {
|
|
101
|
+
localizations[lang] = {
|
|
102
|
+
languageCode: loc['gl:languageCode'] || lang,
|
|
103
|
+
terms: (loc['gl:designation'] || []).map(d => ({ designation: d['gl:term'] || d.term || '', normativeStatus: d['gl:normativeStatus'] || d.normativeStatus })).filter(d => d.designation),
|
|
104
|
+
definitions: (loc['gl:definition'] || []).map(d => ({ content: d['gl:content'] || d.content || '' })).filter(d => d.content),
|
|
105
|
+
notes: (loc['gl:notes'] || []).map(n => ({ content: n['gl:content'] || n.content || '' })).filter(n => n.content),
|
|
106
|
+
examples: (loc['gl:examples'] || []).map(e => ({ content: e['gl:content'] || e.content || '' })).filter(e => e.content),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
conceptEntry.localizations = localizations;
|
|
110
|
+
conceptEntry.languages = Object.keys(localizations);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
writeJson(
|
|
114
|
+
join(CONTENT_DIR, 'concepts', id),
|
|
115
|
+
`${concept.id}.json`,
|
|
116
|
+
conceptEntry
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
console.log(` ${index.concepts.filter(Boolean).length} concepts bridged`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// --- Groups ---
|
|
124
|
+
const groups = siteConfig.datasetGroups || [];
|
|
125
|
+
for (const g of groups) {
|
|
126
|
+
const entry = {
|
|
127
|
+
id: g.id,
|
|
128
|
+
label: g.label || g.id,
|
|
129
|
+
kind: g.kind || (g.series ? 'lineage' : 'default'),
|
|
130
|
+
description: g.description || '',
|
|
131
|
+
current: g.current || undefined,
|
|
132
|
+
datasets: g.datasets || [],
|
|
133
|
+
color: g.color || undefined,
|
|
134
|
+
};
|
|
135
|
+
Object.keys(entry).forEach(k => entry[k] === undefined && delete entry[k]);
|
|
136
|
+
writeJson(join(CONTENT_DIR, 'groups'), `${g.id}.json`, entry);
|
|
137
|
+
console.log(` Group: ${g.id} (${entry.datasets.length} datasets)`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// --- Pages (from public/pages/*.json) ---
|
|
141
|
+
const pagesDir = join(CWD, 'public', 'pages');
|
|
142
|
+
if (existsSync(pagesDir)) {
|
|
143
|
+
for (const file of readdirSync(pagesDir)) {
|
|
144
|
+
if (!file.endsWith('.json')) continue;
|
|
145
|
+
const page = readJson(join(pagesDir, file));
|
|
146
|
+
if (!page) continue;
|
|
147
|
+
// Write as a minimal markdown file for the pages content collection
|
|
148
|
+
const slug = file.replace('.json', '');
|
|
149
|
+
const md = `---\ntitle: "${page.title || slug}"\ntype: "page"\n---\n\n${page.html || ''}\n`;
|
|
150
|
+
writeFileSync(join(CONTENT_DIR, 'pages', `${slug}.md`), md);
|
|
151
|
+
}
|
|
152
|
+
console.log(' Pages bridged from public/pages/');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log('Data bridge complete.');
|
|
@@ -273,6 +273,7 @@ function sourcesToJsonLd(sources) {
|
|
|
273
273
|
if (s.id) doc['gl:id'] = s.id;
|
|
274
274
|
if (s.type) doc['gl:sourceType'] = s.type;
|
|
275
275
|
if (s.status) doc['gl:sourceStatus'] = s.status;
|
|
276
|
+
if (s.modification) doc['gl:modification'] = s.modification;
|
|
276
277
|
if (s.origin) {
|
|
277
278
|
const origin = { '@type': 'gl:Citation' };
|
|
278
279
|
const ref = refToJsonLd(s.origin.ref);
|
|
@@ -282,6 +283,17 @@ function sourcesToJsonLd(sources) {
|
|
|
282
283
|
if (s.origin.link) origin['gl:link'] = s.origin.link;
|
|
283
284
|
doc['gl:origin'] = origin;
|
|
284
285
|
}
|
|
286
|
+
if (s.sourced_from && s.sourced_from.length) {
|
|
287
|
+
doc['gl:sourcedFrom'] = s.sourced_from.map(sf => {
|
|
288
|
+
const cite = { '@type': 'gl:Citation' };
|
|
289
|
+
const ref = refToJsonLd(sf.ref);
|
|
290
|
+
if (ref) cite['gl:ref'] = ref;
|
|
291
|
+
const loc = localityToJsonLd(sf.locality);
|
|
292
|
+
if (loc) cite['gl:locality'] = loc;
|
|
293
|
+
if (sf.link) cite['gl:link'] = sf.link;
|
|
294
|
+
return cite;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
285
297
|
return doc;
|
|
286
298
|
});
|
|
287
299
|
}
|
|
@@ -1228,6 +1240,7 @@ for (let i = 0; i < config.datasets.length; i++) {
|
|
|
1228
1240
|
owner: resolvedOwner,
|
|
1229
1241
|
tags: resolvedTags,
|
|
1230
1242
|
color: ds.color || DS_PALETTE[i % DS_PALETTE.length],
|
|
1243
|
+
year: reg?.year ?? undefined,
|
|
1231
1244
|
},
|
|
1232
1245
|
datasetUri: resolvedDatasetUri,
|
|
1233
1246
|
uriBase: config.uriBase || undefined,
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
* 2. SITE_ID env var → configs/{SITE_ID}.yml
|
|
9
9
|
* 3. --site CLI flag → same as SITE_ID
|
|
10
10
|
* 4. Fallback → site-config.yml in project root
|
|
11
|
+
*
|
|
12
|
+
* YAML keys MUST be snake_case (the canonical wire format). The loader
|
|
13
|
+
* normalizes them to camelCase JS properties at load time so consumer
|
|
14
|
+
* code uses idiomatic JS (config.uriBase, ds.gcrPackage, etc.) while
|
|
15
|
+
* the on-disk format stays snake_case.
|
|
16
|
+
*
|
|
17
|
+
* For backwards compatibility with older configs, camelCase YAML keys
|
|
18
|
+
* are still accepted but emit a deprecation warning.
|
|
11
19
|
*/
|
|
12
20
|
|
|
13
21
|
import { readFileSync, existsSync } from 'fs';
|
|
@@ -18,6 +26,69 @@ import yaml from 'js-yaml';
|
|
|
18
26
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
19
27
|
const projectRoot = resolve(__dirname, '..');
|
|
20
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Known snake_case → camelCase mappings for site-config + register.
|
|
31
|
+
* Every YAML key must appear here in snake_case form. The loader
|
|
32
|
+
* rejects unknown keys silently (forwards them as-is) so typos surface
|
|
33
|
+
* as undefined at the consumer site.
|
|
34
|
+
*/
|
|
35
|
+
const SNAKE_TO_CAMEL = {
|
|
36
|
+
// Site-level
|
|
37
|
+
uri_base: 'uriBase',
|
|
38
|
+
base_path: 'basePath',
|
|
39
|
+
ui_languages: 'uiLanguages',
|
|
40
|
+
dataset_groups: 'datasetGroups',
|
|
41
|
+
dataset_translations: 'datasetTranslations',
|
|
42
|
+
primary_color: 'primaryColor',
|
|
43
|
+
dark_color: 'darkColor',
|
|
44
|
+
owner_name: 'ownerName',
|
|
45
|
+
owner_url: 'ownerUrl',
|
|
46
|
+
powered_by: 'poweredBy',
|
|
47
|
+
footer_nav: 'footerNav',
|
|
48
|
+
footer_logo: 'footerLogo',
|
|
49
|
+
news_dir: 'newsDir',
|
|
50
|
+
// Dataset-level
|
|
51
|
+
gcr_package: 'gcrPackage',
|
|
52
|
+
source_repo: 'sourceRepo',
|
|
53
|
+
local_path: 'localPath',
|
|
54
|
+
local_light: 'localLight',
|
|
55
|
+
local_dark: 'localDark',
|
|
56
|
+
uri_aliases: 'uriAliases',
|
|
57
|
+
language_order: 'languageOrder',
|
|
58
|
+
ref_aliases: 'refAliases',
|
|
59
|
+
urn_aliases: 'urnAliases',
|
|
60
|
+
existing_site_url: 'existingSiteUrl',
|
|
61
|
+
edition_status: 'editionStatus',
|
|
62
|
+
dataset_uri: 'datasetUri',
|
|
63
|
+
schema_version: 'schemaVersion',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function normalizeKey(key) {
|
|
67
|
+
if (typeof key !== 'string') return key;
|
|
68
|
+
// Known snake_case key → camelCase
|
|
69
|
+
if (Object.prototype.hasOwnProperty.call(SNAKE_TO_CAMEL, key)) {
|
|
70
|
+
return SNAKE_TO_CAMEL[key];
|
|
71
|
+
}
|
|
72
|
+
// Generic snake_case → camelCase fallback for unknown keys
|
|
73
|
+
if (key.includes('_') && /^[a-z][a-z0-9_]*$/.test(key)) {
|
|
74
|
+
return key.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
75
|
+
}
|
|
76
|
+
// camelCase or other → pass through (backwards compat)
|
|
77
|
+
return key;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function normalizeObject(obj) {
|
|
81
|
+
if (Array.isArray(obj)) return obj.map(normalizeObject);
|
|
82
|
+
if (obj && typeof obj === 'object') {
|
|
83
|
+
const out = {};
|
|
84
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
85
|
+
out[normalizeKey(k)] = normalizeObject(v);
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
return obj;
|
|
90
|
+
}
|
|
91
|
+
|
|
21
92
|
function findConfigFile(args = []) {
|
|
22
93
|
if (process.env.SITE_CONFIG) {
|
|
23
94
|
return resolve(process.env.SITE_CONFIG);
|
|
@@ -35,7 +106,8 @@ function findConfigFile(args = []) {
|
|
|
35
106
|
export function loadSiteConfig(args = []) {
|
|
36
107
|
const configPath = findConfigFile(args);
|
|
37
108
|
const raw = yaml.load(readFileSync(configPath, 'utf-8'));
|
|
38
|
-
|
|
109
|
+
const config = normalizeObject(raw);
|
|
110
|
+
return { config, configPath };
|
|
39
111
|
}
|
|
40
112
|
|
|
41
113
|
export function getProjectRoot() {
|
|
@@ -120,6 +120,27 @@ describe('tc204 deployment: multi-edition lineage series', () => {
|
|
|
120
120
|
expect(series[0].current?.id).toBe('isotc204-ed3');
|
|
121
121
|
expect(series[0].current?.status).toBe('unknown');
|
|
122
122
|
});
|
|
123
|
+
|
|
124
|
+
it('year propagates through DatasetSummary → setSummaryManifest → member', () => {
|
|
125
|
+
// When the factory loads the registry, datasets with a summary skip
|
|
126
|
+
// the full manifest fetch and use a minimal manifest built from the
|
|
127
|
+
// summary. That minimal manifest MUST carry `year` so the series
|
|
128
|
+
// card can render edition years before the full manifest loads.
|
|
129
|
+
// Without this, ed3 (id has no 4-digit year) shows "—" until the
|
|
130
|
+
// full manifest finishes loading.
|
|
131
|
+
const manifests = [
|
|
132
|
+
makeManifest({ id: 'isotc204-ed3', status: 'draft', year: 2026 }),
|
|
133
|
+
makeManifest({ id: 'isotc204-2025', status: 'valid', year: 2025 }),
|
|
134
|
+
makeManifest({ id: 'isotc204-2022', status: 'valid', year: 2022 }),
|
|
135
|
+
];
|
|
136
|
+
const series = groupManifestsIntoSeries(manifests, undefined, [TC204_GROUP]);
|
|
137
|
+
// Every member has a year — no "—" in the series card
|
|
138
|
+
for (const m of series[0].members) {
|
|
139
|
+
expect(m.year).toBeDefined();
|
|
140
|
+
expect(typeof m.year).toBe('number');
|
|
141
|
+
}
|
|
142
|
+
expect(series[0].members.find(m => m.id === 'isotc204-ed3')?.year).toBe(2026);
|
|
143
|
+
});
|
|
123
144
|
});
|
|
124
145
|
|
|
125
146
|
describe('tc204 deployment: group about discovery', () => {
|
|
@@ -103,6 +103,8 @@ interface JsonLdSource {
|
|
|
103
103
|
'gl:sourceStatus'?: string;
|
|
104
104
|
'gl:modification'?: string;
|
|
105
105
|
'gl:origin'?: JsonLdOrigin;
|
|
106
|
+
'gl:sourcedFrom'?: JsonLdOrigin[];
|
|
107
|
+
'gl:sourced_from'?: JsonLdOrigin[];
|
|
106
108
|
}
|
|
107
109
|
|
|
108
110
|
interface JsonLdRelated {
|
|
@@ -216,6 +218,21 @@ export function getRelatedCitation(rc: object): Record<string, unknown> | null {
|
|
|
216
218
|
// Relationship types whose target is a designation string, not a concept ref.
|
|
217
219
|
const DESIGNATION_REL_TYPES = new Set(['abbreviated_form_for', 'short_form_for']);
|
|
218
220
|
|
|
221
|
+
function attachSourcedFromToSources(
|
|
222
|
+
sources: readonly { sourced_from?: unknown }[],
|
|
223
|
+
rawSources: unknown,
|
|
224
|
+
): void {
|
|
225
|
+
if (!Array.isArray(rawSources)) return;
|
|
226
|
+
for (let i = 0; i < sources.length && i < rawSources.length; i++) {
|
|
227
|
+
const raw = rawSources[i] as Record<string, unknown> | undefined;
|
|
228
|
+
if (raw?.sourced_from && Array.isArray(raw.sourced_from)) {
|
|
229
|
+
(sources[i] as { sourced_from: unknown }).sourced_from = raw.sourced_from.map(
|
|
230
|
+
(sf: unknown) => sf instanceof Citation ? sf : Citation.fromJSON(sf as Record<string, unknown>)
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
219
236
|
function attachBridges(concept: Concept, localizations: Record<string, unknown>): void {
|
|
220
237
|
for (const lang of concept.languages) {
|
|
221
238
|
const lc = concept.localization(lang);
|
|
@@ -223,6 +240,9 @@ function attachBridges(concept: Concept, localizations: Record<string, unknown>)
|
|
|
223
240
|
if (!lc || !raw || typeof raw !== 'object') continue;
|
|
224
241
|
const rawObj = raw as Record<string, unknown>;
|
|
225
242
|
|
|
243
|
+
// Attach sourced_from to localization-level sources
|
|
244
|
+
attachSourcedFromToSources(lc.sources as unknown as { sourced_from?: unknown }[], rawObj.sources);
|
|
245
|
+
|
|
226
246
|
// Annotations
|
|
227
247
|
const annList = rawObj.annotations;
|
|
228
248
|
if (Array.isArray(annList) && annList.length > 0) {
|
|
@@ -234,7 +254,8 @@ function attachBridges(concept: Concept, localizations: Record<string, unknown>)
|
|
|
234
254
|
// Designation-level relationship targets, ref text, sourceId, citation
|
|
235
255
|
const rawTerms = rawObj.terms;
|
|
236
256
|
if (Array.isArray(rawTerms)) {
|
|
237
|
-
for (
|
|
257
|
+
for (let ti = 0; ti < rawTerms.length; ti++) {
|
|
258
|
+
const rawTerm = rawTerms[ti];
|
|
238
259
|
if (!rawTerm || typeof rawTerm !== 'object') continue;
|
|
239
260
|
const rawT = rawTerm as Record<string, unknown>;
|
|
240
261
|
const rawDesignation = rawT.designation as string | undefined;
|
|
@@ -244,6 +265,11 @@ function attachBridges(concept: Concept, localizations: Record<string, unknown>)
|
|
|
244
265
|
const rawRelated = rawT.related;
|
|
245
266
|
if (!Array.isArray(rawRelated)) continue;
|
|
246
267
|
attachRelatedBridges(designation.related, rawRelated);
|
|
268
|
+
// Attach sourced_from to designation-level sources
|
|
269
|
+
attachSourcedFromToSources(
|
|
270
|
+
designation.sources as unknown as { sourced_from?: unknown }[],
|
|
271
|
+
rawT.sources,
|
|
272
|
+
);
|
|
247
273
|
}
|
|
248
274
|
}
|
|
249
275
|
|
|
@@ -408,6 +434,19 @@ function mapDetailedDefinitionFromJsonLd(d: any): Record<string, unknown> {
|
|
|
408
434
|
return result;
|
|
409
435
|
}
|
|
410
436
|
|
|
437
|
+
function mapOriginFromJsonLd(o: JsonLdOrigin): Record<string, unknown> {
|
|
438
|
+
const origin: Record<string, unknown> = {};
|
|
439
|
+
const ref = mapRefFromJsonLd(o['gl:ref']);
|
|
440
|
+
if (ref) origin.ref = ref;
|
|
441
|
+
const loc = mapLocalityFromJsonLd(o['gl:locality']);
|
|
442
|
+
if (loc) origin.locality = loc;
|
|
443
|
+
if (o['gl:link']) origin.link = o['gl:link'];
|
|
444
|
+
if (o['gl:id']) origin.id = o['gl:id'];
|
|
445
|
+
if (o['gl:version']) origin.version = o['gl:version'];
|
|
446
|
+
if (o['gl:source']) origin.source = o['gl:source'];
|
|
447
|
+
return origin;
|
|
448
|
+
}
|
|
449
|
+
|
|
411
450
|
function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
|
|
412
451
|
const result: Record<string, unknown> = {};
|
|
413
452
|
if (s['gl:id']) result.id = s['gl:id'];
|
|
@@ -416,17 +455,12 @@ function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
|
|
|
416
455
|
if (s['gl:modification']) result.modification = s['gl:modification'];
|
|
417
456
|
|
|
418
457
|
if (s['gl:origin']) {
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (o['gl:link']) origin.link = o['gl:link'];
|
|
426
|
-
if (o['gl:id']) origin.id = o['gl:id'];
|
|
427
|
-
if (o['gl:version']) origin.version = o['gl:version'];
|
|
428
|
-
if (o['gl:source']) origin.source = o['gl:source'];
|
|
429
|
-
result.origin = origin;
|
|
458
|
+
result.origin = mapOriginFromJsonLd(s['gl:origin']);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const sf = s['gl:sourcedFrom'] ?? s['gl:sourced_from'];
|
|
462
|
+
if (sf?.length) {
|
|
463
|
+
result.sourced_from = sf.map(item => mapOriginFromJsonLd(item));
|
|
430
464
|
}
|
|
431
465
|
|
|
432
466
|
return result;
|
|
@@ -614,6 +648,10 @@ export function conceptFromJson(doc: Record<string, unknown>): Concept {
|
|
|
614
648
|
const concept = Concept.fromJSON(doc);
|
|
615
649
|
const locs = (doc as Record<string, unknown>).localizations as Record<string, unknown> | undefined;
|
|
616
650
|
if (locs) attachBridges(concept, locs);
|
|
651
|
+
attachSourcedFromToSources(
|
|
652
|
+
concept.sources as unknown as { sourced_from?: unknown }[],
|
|
653
|
+
(doc as Record<string, unknown>).sources,
|
|
654
|
+
);
|
|
617
655
|
return concept;
|
|
618
656
|
}
|
|
619
657
|
|
|
@@ -11,9 +11,13 @@
|
|
|
11
11
|
// DELETE this file when upstream ships proper declarations — tracked by
|
|
12
12
|
// PR glossarist/glossarist-js#31 (targets v0.4.3+).
|
|
13
13
|
|
|
14
|
-
import type { ConceptSource, GlossaristModel } from 'glossarist';
|
|
14
|
+
import type { ConceptSource, Citation, GlossaristModel } from 'glossarist';
|
|
15
15
|
|
|
16
16
|
declare module 'glossarist' {
|
|
17
|
+
interface ConceptSource {
|
|
18
|
+
sourced_from?: Citation[];
|
|
19
|
+
}
|
|
20
|
+
|
|
17
21
|
class RegistrableModel extends GlossaristModel {
|
|
18
22
|
static register(type: string, cls: typeof RegistrableModel): void;
|
|
19
23
|
static fromData(data: Record<string, unknown>): RegistrableModel;
|
package/src/adapters/types.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><svg id="uuid-fffeb195-1711-411a-b374-52204ad82fd6" xmlns="http://www.w3.org/2000/svg" width="183.1" height="172.48" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 183.1 172.48"><defs><linearGradient id="uuid-6317b9cc-2e5f-41e2-85f1-7443049bc3fc" x1="51.31" y1="161.8" x2="102.32" y2="73.02" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#3fb6b0"/><stop offset=".07" stop-color="#5cc5be"/><stop offset=".15" stop-color="#78d3cc"/><stop offset=".24" stop-color="#8fe0d8"/><stop offset=".34" stop-color="#a1e9e1"/><stop offset=".47" stop-color="#aef0e7"/><stop offset=".63" stop-color="#b5f3ea"/><stop offset="1" stop-color="#b8f5ec"/></linearGradient><linearGradient id="uuid-ea13e7e7-b134-4ab8-aa9c-2ec35d57d2e0" x1="0" y1="59.58" x2="183.1" y2="59.58" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2d4164"/><stop offset=".47" stop-color="#2d4164" stop-opacity=".94"/><stop offset="1" stop-color="#2d4164" stop-opacity=".8"/></linearGradient><linearGradient id="uuid-b04c0104-3d2b-4e91-a646-00a62f882d94" x1="71.68" y1="31.06" x2="111.41" y2="31.06" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#2d4164"/><stop offset=".28" stop-color="#2c4365"/><stop offset="1" stop-color="#588ac1"/></linearGradient><linearGradient id="uuid-3270c642-e286-4980-b9d4-c5a586959dc8" x1="76.66" y1="69.37" x2="183.1" y2="69.37" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#34798a" stop-opacity=".7"/><stop offset="1" stop-color="#62c4dd"/></linearGradient><linearGradient id="uuid-b517cc88-1e38-4408-9af5-0022fab7d735" x1="81.4" y1="129.46" x2="127.22" y2="50.28" gradientUnits="userSpaceOnUse"><stop offset=".2" stop-color="#456399"/><stop offset="1" stop-color="#3a5c80" stop-opacity=".7"/></linearGradient><linearGradient id="uuid-30648514-5669-4d95-96c9-4dab36559efb" x1="88.72" y1="146.48" x2="54.34" y2="146.48" xlink:href="#uuid-b04c0104-3d2b-4e91-a646-00a62f882d94"/><linearGradient id="uuid-ab3522e9-c0cd-4474-8859-9f36cdf98f81" x1="86.16" y1="99.24" x2="47.06" y2="25.85" gradientUnits="userSpaceOnUse"><stop offset=".2" stop-color="#456399"/><stop offset="1" stop-color="#3a5c80" stop-opacity=".7"/></linearGradient><linearGradient id="uuid-4f227c7d-7a65-4e56-9321-c6145c47210b" x1="89.58" y1="107.94" x2="63.88" y2="57.26" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#456399" stop-opacity=".5"/><stop offset="1" stop-color="#97a4b7" stop-opacity=".8"/></linearGradient></defs><path d="m87.41,59.48l-61.18,106.11c-1.63,2.85.6,6.85,3.98,6.88h37.88l63.56-112.75-44.23-.25Z" style="fill:url(#uuid-6317b9cc-2e5f-41e2-85f1-7443049bc3fc); stroke-width:0px;"/><path d="m40.39,141.03c.29.28,1.01,2.7,1.61,4.91.67,2.49,2.77,4.38,5.33,4.68,21.2,2.5,21.4,20.71,21.4,20.71l22.5-39.92c-23.71,14.02-41.88,6.43-40.64-8.09-2.71,4.71-7.5,12.99-10.21,17.7Z" style="fill:#3a5c80; stroke-width:0px;"/><path d="m75.5,80.4s-14.96,25.41-18.41,31.59c8.71-8.42,17.69,2.79,24.11-8.66,8.13-14.5-5.7-22.93-5.7-22.93Z" style="fill:#60abbc; stroke-width:0px;"/><path d="m113.82,172.48h38.28c4.52-.01,6.66-3.57,4.43-7.48-6.55-11.51-59.96-105.41-59.96-105.41l-43.04-.37,60.29,113.27Z" style="fill:#b8f5ec; stroke-width:0px;"/><path d="m106.47,91.98c-9.24,2.3-34.69,12.42-21.32,20.21,12.57,7.31,28.11.2,32.05-11.73,1.79-4.66-2.29-10.67-10.74-8.47Z" style="fill:#3fb6b0; stroke-width:0px;"/><path d="m137.32,131.2l-13.09-23.07c.37,24.63-35.5,17.21-35.5,17.21l24.95,46.87c-1-25.77,10.54-35.8,23.65-41Z" style="fill:#7ee0d4; stroke-width:0px;"/><path d="m57.35,132.11s0-.01.01-.02c0,0,0,0,0,0" style="fill:#3a5c80; stroke-width:0px;"/><path d="m183.1,52.52c-.1-3.25-2.07-5.56-6-5.59-13.09-.07-163.15,0-163.15,0L0,72.26h183.1v-19.75Z" style="fill:url(#uuid-ea13e7e7-b134-4ab8-aa9c-2ec35d57d2e0); stroke-width:0px;"/><path d="m71.68,47s19.67-31.93,27.9-31.9c10.36,0,15.93,31.9,8.24,31.83l-36.15.07Z" style="fill:url(#uuid-b04c0104-3d2b-4e91-a646-00a62f882d94); stroke-width:0px;"/><path d="m183.1,59.91l-.13,10.94c.07,6.28-2.04,8.39-8.33,8.31h-97.99l10.97-19.59,95.47.33Z" style="fill:url(#uuid-3270c642-e286-4980-b9d4-c5a586959dc8); opacity:.78; stroke-width:0px;"/><polygon points="7.1 59.58 0 72.26 80.52 72.26 87.63 59.58 7.1 59.58" style="fill:#3a5c80; opacity:.8; stroke-width:0px;"/><path d="m117.54,47s-65.24,112.78-69.85,120.82c-1.1,1.92.38,4.63,2.65,4.65.71,0,17.74,0,17.74,0l70.73-125.47h-21.27Z" style="fill:url(#uuid-b517cc88-1e38-4408-9af5-0022fab7d735); opacity:.8; stroke-width:0px;"/><path d="m84.52,142.79c.55-.64.79-1.48,1.27-2.18,10.98-16.02-12.03-16.74-17.61-8.28l-12.79,23.17c-.14.24-.26.48-.38.72-3.39,7.29,6.71,13.38,11.92,7.26l17.59-20.69Z" style="fill:url(#uuid-30648514-5669-4d95-96c9-4dab36559efb); opacity:.8; stroke-width:0px;"/><path d="m124.71,126.57s0-.01-.01-.02c0,0,0,0,0,0,0,0,0,.02,0,.02Z" style="fill:#2e4365; stroke-width:0px;"/><polygon points="136.69 172.48 113.82 172.48 53.53 59.58 73.24 59.58 136.69 172.48" style="fill:url(#uuid-ab3522e9-c0cd-4474-8859-9f36cdf98f81); opacity:.75; stroke-width:0px;"/><path d="m73.39,59.58l33.44,59.63c2.82,7.65-7.17,26.67-12.28,17.06L53.53,59.58h19.86Z" style="fill:url(#uuid-4f227c7d-7a65-4e56-9321-c6145c47210b); stroke-width:0px;"/><path d="m115.87,135.34l18.22,32.5s-36.61-14.66-18.22-32.5Z" style="fill:#4977a4; stroke-width:0px;"/></svg>
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
import { computed } from 'vue';
|
|
3
3
|
import { useSiteConfig } from '../config/use-site-config';
|
|
4
4
|
import { useI18n } from '../i18n';
|
|
5
|
-
|
|
5
|
+
|
|
6
|
+
const glossaristLogo = new URL('../assets/glossarist-logo.svg', import.meta.url).href;
|
|
6
7
|
|
|
7
8
|
const { config } = useSiteConfig();
|
|
8
9
|
const { t } = useI18n();
|
|
@@ -58,7 +59,7 @@ const ownerUrl = computed(() => config.value?.branding?.ownerUrl || '/');
|
|
|
58
59
|
>{{ link.label }}</a>
|
|
59
60
|
<span class="text-ink-200">|</span>
|
|
60
61
|
<span class="text-xs inline-flex items-center gap-1.5">
|
|
61
|
-
<img :src="glossaristLogo" alt="" class="w-4 h-4 opacity-80" />
|
|
62
|
+
<img :src="glossaristLogo" alt="Glossarist" class="w-4 h-4 opacity-80" />
|
|
62
63
|
<a :href="poweredBy.url" target="_blank" rel="noopener" class="concept-link">{{ poweredBy.message }}</a>
|
|
63
64
|
</span>
|
|
64
65
|
</div>
|
|
@@ -6,6 +6,8 @@ import { useSiteConfig } from '../config/use-site-config';
|
|
|
6
6
|
import { useI18n } from '../i18n';
|
|
7
7
|
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
|
|
8
8
|
|
|
9
|
+
const glossaristLogo = new URL('../assets/glossarist-logo.svg', import.meta.url).href;
|
|
10
|
+
|
|
9
11
|
const router = useRouter();
|
|
10
12
|
const ui = useUiStore();
|
|
11
13
|
const store = useVocabularyStore();
|
|
@@ -76,13 +78,8 @@ onBeforeUnmount(() => document.removeEventListener('click', closeLangOnOutside))
|
|
|
76
78
|
class="h-8 max-w-[48px] object-contain rounded"
|
|
77
79
|
/>
|
|
78
80
|
</div>
|
|
79
|
-
<div v-else class="
|
|
80
|
-
<
|
|
81
|
-
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
|
82
|
-
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
|
83
|
-
<line x1="9" y1="7" x2="17" y2="7"/>
|
|
84
|
-
<line x1="9" y1="11" x2="15" y2="11"/>
|
|
85
|
-
</svg>
|
|
81
|
+
<div v-else class="h-8 flex items-center">
|
|
82
|
+
<img :src="glossaristLogo" alt="Glossarist" class="h-8 max-w-[48px] object-contain rounded" />
|
|
86
83
|
</div>
|
|
87
84
|
<span class="font-serif text-lg text-ink-800 leading-none hidden sm:inline">{{ localizedTitle }}</span>
|
|
88
85
|
</button>
|