@glossarist/concept-browser 0.7.25 → 0.7.27
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/package.json +1 -1
- package/src/adapters/factory.ts +34 -0
- package/src/components/AppSidebar.vue +1 -1
- package/src/components/ConceptDetail.vue +15 -4
- package/src/components/GraphPanel.vue +11 -1
- package/src/i18n/locales/eng.yml +1 -0
- package/src/i18n/locales/fra.yml +1 -0
- package/src/style.css +8 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.27",
|
|
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": {
|
package/src/adapters/factory.ts
CHANGED
|
@@ -46,7 +46,18 @@ export class AdapterFactory {
|
|
|
46
46
|
await Promise.all(needManifest.map(a => a.loadManifest().catch(() => {})));
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// Register all datasets' URI patterns eagerly so cross-dataset refs resolve
|
|
50
|
+
for (const adapter of adapters) {
|
|
51
|
+
if (adapter.manifest) {
|
|
52
|
+
this.registerUriPatterns(adapter.registerId, adapter.manifest);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const adapter of this.adapters.values()) {
|
|
56
|
+
adapter.setUrnMap(this.urnMap);
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
return adapters;
|
|
60
|
+
|
|
50
61
|
}
|
|
51
62
|
|
|
52
63
|
getAdapter(registerId: string): DatasetAdapter | undefined {
|
|
@@ -57,6 +68,29 @@ export class AdapterFactory {
|
|
|
57
68
|
return [...this.adapters.values()];
|
|
58
69
|
}
|
|
59
70
|
|
|
71
|
+
|
|
72
|
+
private registerUriPatterns(registerId: string, manifest: Manifest): void {
|
|
73
|
+
const uriPatterns = [
|
|
74
|
+
manifest.datasetUri,
|
|
75
|
+
...(manifest.uriAliases ?? []),
|
|
76
|
+
manifest.uriBase ? `${manifest.uriBase}/${registerId}/*` : undefined,
|
|
77
|
+
].filter(Boolean) as string[];
|
|
78
|
+
this.resolver.registerDataset(registerId, uriPatterns);
|
|
79
|
+
|
|
80
|
+
if (manifest.ref) {
|
|
81
|
+
this.resolver.registerSourceRef(manifest.ref, registerId, manifest.datasetUri);
|
|
82
|
+
}
|
|
83
|
+
for (const alias of manifest.refAliases ?? []) {
|
|
84
|
+
this.resolver.registerSourceRef(alias, registerId, manifest.datasetUri);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (manifest.datasetUri) this.urnMap.set(manifest.datasetUri, registerId);
|
|
88
|
+
for (const alias of manifest.uriAliases ?? []) {
|
|
89
|
+
const base = alias.endsWith('*') ? alias.slice(0, -1) : alias;
|
|
90
|
+
if (base.startsWith('urn:')) this.urnMap.set(base, registerId);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
60
94
|
async loadDataset(registerId: string): Promise<DatasetAdapter> {
|
|
61
95
|
const adapter = this.adapters.get(registerId);
|
|
62
96
|
if (!adapter) throw new Error(`Unknown dataset: ${registerId}`);
|
|
@@ -252,7 +252,7 @@ const activeSectionId = computed(() => {
|
|
|
252
252
|
<button
|
|
253
253
|
v-if="group.label"
|
|
254
254
|
@click="toggleGroup(group.id)"
|
|
255
|
-
class="w-full flex items-start gap-1.5 px-2 py-1.5 rounded-lg text-xs font-semibold transition-colors hover:bg-ink-50"
|
|
255
|
+
class="sidebar-group-label w-full flex items-start gap-1.5 px-2 py-1.5 rounded-lg text-xs font-semibold transition-colors hover:bg-ink-50"
|
|
256
256
|
:style="group.color ? { color: group.color } : {}"
|
|
257
257
|
>
|
|
258
258
|
<span class="w-3 text-[10px] mt-0.5 flex-shrink-0">{{ isGroupExpanded(group.id) ? '▾' : '▸' }}</span>
|
|
@@ -143,6 +143,17 @@ async function navigateRelated(ref: { source: string | null; id: string | null }
|
|
|
143
143
|
router.push({ name: 'concept', params: { registerId: target.registerId, conceptId: target.conceptId } });
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
function relatedLabel(dr: { content?: string | null; ref?: { source: string | null; id: string | null } | null }): string {
|
|
147
|
+
if (dr.content) return dr.content;
|
|
148
|
+
const resolved = dr.ref ? getResolvedRef(dr.ref).target : null;
|
|
149
|
+
if (resolved) {
|
|
150
|
+
const m = store.manifests.get(resolved.registerId);
|
|
151
|
+
const dsLabel = m?.shortname || m?.title || resolved.registerId;
|
|
152
|
+
return `${resolved.conceptId} (${dsLabel})`;
|
|
153
|
+
}
|
|
154
|
+
return dr.ref ? `${dr.ref.id || ''} (${dr.ref.source || ''})`.trim() : '';
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
// Cross-reference resolver: generates clickable links for inline refs
|
|
147
158
|
|
|
148
159
|
const { ensureBibLoaded, bibResolver, figResolver } = useRenderOptions(() => props.registerId);
|
|
@@ -353,7 +364,7 @@ const edgeDisplayCache = computed(() => {
|
|
|
353
364
|
});
|
|
354
365
|
|
|
355
366
|
function getEdgeDisplay(uri: string): EdgeDisplay {
|
|
356
|
-
return edgeDisplayCache.value.get(uri) ?? { uri, conceptId: uri, tooltip: uri, isLocal: false, badge: null };
|
|
367
|
+
return edgeDisplayCache.value.get(uri) ?? { uri, conceptId: uri, designation: "", tooltip: uri, isLocal: false, badge: null };
|
|
357
368
|
}
|
|
358
369
|
|
|
359
370
|
interface ResolvedRef {
|
|
@@ -391,7 +402,7 @@ async function navigateEdge(edge: GraphEdge) {
|
|
|
391
402
|
|
|
392
403
|
function navigateDomain(domain: { slug: string; conceptId?: string }) {
|
|
393
404
|
const sectionId = domain.conceptId || domain.slug;
|
|
394
|
-
router.push({ name: 'dataset', params: { registerId: manifest.id }, query: { section: sectionId } });
|
|
405
|
+
router.push({ name: 'dataset', params: { registerId: props.manifest.id }, query: { section: sectionId } });
|
|
395
406
|
}
|
|
396
407
|
|
|
397
408
|
function getTermForLang(lang: string): string {
|
|
@@ -670,8 +681,8 @@ const nonVerbalReps = computed(() => {
|
|
|
670
681
|
<div v-if="d.related?.length" class="mt-0.5 space-y-0.5">
|
|
671
682
|
<div v-for="(dr, dri) in d.related" :key="'dr'+dri" class="text-xs text-ink-400 flex items-center gap-1.5">
|
|
672
683
|
<span class="badge text-[9px] bg-gray-50 text-gray-600">{{ relationshipLabel(dr.type) }}</span>
|
|
673
|
-
<button v-if="getResolvedRef(dr.ref).target" @click="navigateRelated(dr.ref!)" class="concept-link">{{
|
|
674
|
-
<span v-else>{{
|
|
684
|
+
<button v-if="getResolvedRef(dr.ref).target" @click="navigateRelated(dr.ref!)" class="concept-link">{{ relatedLabel(dr) }}</button>
|
|
685
|
+
<span v-else>{{ relatedLabel(dr) }}</span>
|
|
675
686
|
</div>
|
|
676
687
|
</div>
|
|
677
688
|
</div>
|
|
@@ -30,6 +30,7 @@ const containerRef = ref<HTMLDivElement | null>(null);
|
|
|
30
30
|
const selectedNode = ref<GraphNode | null>(null);
|
|
31
31
|
const detailCloseRef = ref<HTMLButtonElement | null>(null);
|
|
32
32
|
const labelMode = ref<'designation' | 'identifier'>('designation');
|
|
33
|
+
const showDomains = ref(true);
|
|
33
34
|
|
|
34
35
|
// Dataset enable/disable state
|
|
35
36
|
const registerEnabled = reactive<Record<string, boolean>>({});
|
|
@@ -74,7 +75,8 @@ const enabledRegisters = computed(() => {
|
|
|
74
75
|
|
|
75
76
|
const visibleNodes = computed(() => {
|
|
76
77
|
const enabled = enabledRegisters.value;
|
|
77
|
-
|
|
78
|
+
const domains = showDomains.value;
|
|
79
|
+
return props.nodes.filter(n => enabled.has(n.register) && (domains || n.nodeType !== 'domain'));
|
|
78
80
|
});
|
|
79
81
|
|
|
80
82
|
const visibleNodeUris = computed(() => {
|
|
@@ -549,6 +551,14 @@ function selectedNodeColor(): string {
|
|
|
549
551
|
</div>
|
|
550
552
|
</div>
|
|
551
553
|
|
|
554
|
+
<!-- Domain/section toggle -->
|
|
555
|
+
<div class="flex items-center gap-2 mt-3">
|
|
556
|
+
<label class="flex items-center gap-1.5 cursor-pointer text-xs text-ink-500">
|
|
557
|
+
<input type="checkbox" v-model="showDomains" @change="rebuildGraph()" class="rounded border-ink-200 text-ink-800 focus:ring-ink-400 w-3.5 h-3.5" />
|
|
558
|
+
{{ t('graph.showDomains') }}
|
|
559
|
+
</label>
|
|
560
|
+
</div>
|
|
561
|
+
|
|
552
562
|
<div v-if="nodeCount === 0" class="text-xs text-ink-300 mt-3 leading-relaxed">
|
|
553
563
|
{{ props.edges.length > 0 ? t('graph.enableDatasets') : t('graph.browseToPopulate') }}
|
|
554
564
|
</div>
|
package/src/i18n/locales/eng.yml
CHANGED
|
@@ -157,6 +157,7 @@ graph.stubStatus: stub
|
|
|
157
157
|
graph.collapseControls: Collapse controls
|
|
158
158
|
graph.expandControls: Expand controls
|
|
159
159
|
graph.loading: Loading graph data...
|
|
160
|
+
graph.showDomains: Show domains/sections
|
|
160
161
|
|
|
161
162
|
# Sidebar provenance
|
|
162
163
|
sidebar.provenance: Provenance
|
package/src/i18n/locales/fra.yml
CHANGED
|
@@ -157,6 +157,7 @@ graph.stubStatus: non chargé
|
|
|
157
157
|
graph.collapseControls: Réduire les contrôles
|
|
158
158
|
graph.expandControls: Développer les contrôles
|
|
159
159
|
graph.loading: Chargement des données du graphe...
|
|
160
|
+
graph.showDomains: Afficher les domaines/sections
|
|
160
161
|
|
|
161
162
|
# Sidebar provenance
|
|
162
163
|
sidebar.provenance: Provenance
|
package/src/style.css
CHANGED
|
@@ -434,6 +434,14 @@
|
|
|
434
434
|
.dark .bg-red-50 { background-color: rgba(239, 68, 68, 0.15) !important; }
|
|
435
435
|
.dark .text-red-600 { color: #fca5a5 !important; }
|
|
436
436
|
|
|
437
|
+
/* ── Sidebar group labels (brand colors are too dark in dark mode) ── */
|
|
438
|
+
.dark .sidebar-group-label {
|
|
439
|
+
filter: brightness(2);
|
|
440
|
+
}
|
|
441
|
+
.dark .sidebar-group-label:hover {
|
|
442
|
+
filter: brightness(1.8);
|
|
443
|
+
}
|
|
444
|
+
|
|
437
445
|
/* Scrollbar hide utility */
|
|
438
446
|
@layer utilities {
|
|
439
447
|
.scrollbar-none { -ms-overflow-style: none; scrollbar-width: none; }
|