@glossarist/concept-browser 0.7.69 → 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 +12 -0
- package/src/adapters/model-bridge.ts +50 -12
- package/src/adapters/non-verbal/glossarist-augment.d.ts +5 -1
- 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
|
}
|
|
@@ -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;
|
|
@@ -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>
|
|
@@ -319,17 +319,20 @@ const activeSectionId = computed(() => {
|
|
|
319
319
|
<button
|
|
320
320
|
v-else
|
|
321
321
|
@click="goToDataset(ds.id)"
|
|
322
|
-
class="w-full text-left px-3 py-2 rounded-lg text-sm border-l-2"
|
|
322
|
+
class="w-full text-left px-3 py-2 rounded-lg text-sm border-l-2 flex items-start gap-2"
|
|
323
323
|
:class="[
|
|
324
324
|
currentDataset === ds.id
|
|
325
325
|
? 'text-ink-800 dark:text-ink-50'
|
|
326
326
|
: 'border-transparent text-ink-600 dark:text-ink-300 hover:bg-ink-50 dark:hover:bg-ink-700 hover:text-ink-800 dark:hover:text-ink-50'
|
|
327
327
|
]"
|
|
328
|
-
:style="currentDataset === ds.id ?
|
|
328
|
+
:style="{ borderLeftColor: currentDataset === ds.id ? getColor(ds.id) : 'transparent' }"
|
|
329
329
|
>
|
|
330
|
-
<
|
|
331
|
-
<div
|
|
332
|
-
|
|
330
|
+
<span class="w-2 h-2 rounded-full flex-shrink-0 mt-1.5" :style="{ backgroundColor: getColor(ds.id) }"></span>
|
|
331
|
+
<div class="min-w-0 flex-1">
|
|
332
|
+
<div class="font-medium truncate leading-snug">{{ localizedDatasetField(ds.id, 'title', ds.title) }}</div>
|
|
333
|
+
<div v-if="ds.loaded" class="text-xs mt-0.5" :class="currentDataset === ds.id ? 'text-ink-400 dark:text-ink-300' : 'text-ink-300 dark:text-ink-400'">
|
|
334
|
+
{{ ds.conceptCount.toLocaleString() }} {{ t('home.concepts').toLowerCase() }}
|
|
335
|
+
</div>
|
|
333
336
|
</div>
|
|
334
337
|
</button>
|
|
335
338
|
|
|
@@ -448,6 +448,12 @@ const nonVerbalReps = computed(() => {
|
|
|
448
448
|
<span v-if="!src.origin && src.modification" class="text-ink-400">{{ src.modification }}</span>
|
|
449
449
|
</div>
|
|
450
450
|
<div v-if="src.modification" class="text-xs text-ink-300 mt-1">{{ src.modification }}</div>
|
|
451
|
+
<div v-if="src.sourced_from?.length" class="text-xs text-ink-400 mt-1">
|
|
452
|
+
<span class="text-ink-300">{{ t('concept.sourcedFrom') }}:</span>
|
|
453
|
+
<div v-for="(sf, sfi) in src.sourced_from" :key="'sf'+sfi" class="ml-2">
|
|
454
|
+
<CitationDisplay v-if="sf" :citation="sf" :register-id="registerId" />
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
451
457
|
</div>
|
|
452
458
|
</div>
|
|
453
459
|
|
|
@@ -614,6 +620,12 @@ const nonVerbalReps = computed(() => {
|
|
|
614
620
|
<CitationDisplay v-if="src.origin" :citation="src.origin" />
|
|
615
621
|
</div>
|
|
616
622
|
<div v-if="src.modification" class="text-ink-300 mt-0.5">{{ src.modification }}</div>
|
|
623
|
+
<div v-if="src.sourced_from?.length" class="text-ink-400 mt-0.5">
|
|
624
|
+
<span class="text-ink-300">{{ t('concept.sourcedFrom') }}:</span>
|
|
625
|
+
<div v-for="(sf, sfi) in src.sourced_from" :key="'sf'+sfi" class="ml-2">
|
|
626
|
+
<CitationDisplay v-if="sf" :citation="sf" />
|
|
627
|
+
</div>
|
|
628
|
+
</div>
|
|
617
629
|
</div>
|
|
618
630
|
</div>
|
|
619
631
|
</div>
|
|
@@ -4,8 +4,11 @@ import { designationTypeInfo, normativeStatusInfo, abbreviationDetails, termType
|
|
|
4
4
|
import { relationshipLabel } from '../utils/relationship-categories';
|
|
5
5
|
import { langName } from '../utils/lang';
|
|
6
6
|
import { getDesignationTarget } from '../adapters/model-bridge';
|
|
7
|
+
import { useI18n } from '../i18n';
|
|
7
8
|
import CitationDisplay from './CitationDisplay.vue';
|
|
8
9
|
|
|
10
|
+
const { t } = useI18n();
|
|
11
|
+
|
|
9
12
|
const props = defineProps<{
|
|
10
13
|
designations: Designation[];
|
|
11
14
|
renderedDesignations: Map<string, string>;
|
|
@@ -57,10 +60,18 @@ function resolvedLabel(dr: { content: string | null; ref?: { source: string | nu
|
|
|
57
60
|
<span v-if="d.system" class="badge text-[10px] bg-gray-50 text-gray-600">system: {{ d.system }}</span>
|
|
58
61
|
</div>
|
|
59
62
|
<div v-if="d.sources?.length" class="mt-1 space-y-0.5">
|
|
60
|
-
<div v-for="(ds, dsi) in d.sources" :key="'ds'+dsi" class="text-xs text-ink-400
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
<div v-for="(ds, dsi) in d.sources" :key="'ds'+dsi" class="text-xs text-ink-400">
|
|
64
|
+
<div class="flex items-center gap-1.5">
|
|
65
|
+
<span v-if="ds.type" class="badge text-[9px]" :class="sourceTypeInfo(ds.type).color">{{ sourceTypeInfo(ds.type).label }}</span>
|
|
66
|
+
<CitationDisplay v-if="ds.origin" :citation="ds.origin" :register-id="registerId" />
|
|
67
|
+
<span v-else-if="ds.modification" class="text-ink-300">{{ ds.modification }}</span>
|
|
68
|
+
</div>
|
|
69
|
+
<div v-if="ds.sourced_from?.length" class="ml-4 mt-0.5">
|
|
70
|
+
<span class="text-ink-300 text-[9px]">{{ t('concept.sourcedFrom') }}:</span>
|
|
71
|
+
<div v-for="(sf, sfi) in ds.sourced_from" :key="'sf'+sfi" class="ml-2">
|
|
72
|
+
<CitationDisplay v-if="sf" :citation="sf" :register-id="registerId" />
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
64
75
|
</div>
|
|
65
76
|
</div>
|
|
66
77
|
<div v-if="d.related?.length" class="mt-0.5 space-y-0.5">
|
|
@@ -176,6 +176,12 @@ function handleContentClick(e: MouseEvent) {
|
|
|
176
176
|
<div class="text-ink-700">
|
|
177
177
|
<CitationDisplay v-if="src.origin" :citation="src.origin" />
|
|
178
178
|
</div>
|
|
179
|
+
<div v-if="src.sourced_from?.length" class="text-xs text-ink-400 mt-1">
|
|
180
|
+
<span class="text-ink-300">{{ t('concept.sourcedFrom') }}:</span>
|
|
181
|
+
<div v-for="(sf, sfi) in src.sourced_from" :key="'sf'+sfi" class="ml-2">
|
|
182
|
+
<CitationDisplay v-if="sf" :citation="sf" />
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
179
185
|
</div>
|
|
180
186
|
</div>
|
|
181
187
|
</div>
|