@glossarist/concept-browser 0.3.7 → 0.4.0
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 +3 -2
- package/cli/index.mjs +2 -1
- package/env.d.ts +5 -0
- package/package.json +4 -3
- package/scripts/build-edges.js +78 -10
- package/scripts/generate-data.mjs +152 -20
- package/scripts/generate-ontology-data.mjs +184 -0
- package/scripts/generate-ontology-schema.mjs +315 -0
- package/src/__tests__/concept-card.test.ts +1 -1
- package/src/__tests__/concept-detail-interaction.test.ts +40 -18
- package/src/__tests__/concept-formats.test.ts +32 -30
- package/src/__tests__/concept-timeline.test.ts +108 -83
- package/src/__tests__/concept-view.test.ts +15 -2
- package/src/__tests__/dataset-adapter.test.ts +172 -23
- package/src/__tests__/dataset-view.test.ts +6 -5
- package/src/__tests__/designation-registry.test.ts +161 -0
- package/src/__tests__/graph.test.ts +62 -0
- package/src/__tests__/language-detail.test.ts +117 -60
- package/src/__tests__/ontology-registry.test.ts +109 -0
- package/src/__tests__/relationship-categories.test.ts +62 -0
- package/src/__tests__/test-helpers.ts +11 -8
- package/src/adapters/DatasetAdapter.ts +171 -48
- package/src/adapters/model-bridge.ts +277 -0
- package/src/adapters/ontology-registry.ts +75 -0
- package/src/adapters/ontology-schema.ts +100 -0
- package/src/adapters/types.ts +52 -77
- package/src/components/AppSidebar.vue +1 -1
- package/src/components/CitationDisplay.vue +35 -0
- package/src/components/ConceptDetail.vue +334 -93
- package/src/components/ConceptRdfView.vue +397 -0
- package/src/components/ConceptTimeline.vue +56 -52
- package/src/components/GraphPanel.vue +96 -31
- package/src/components/LanguageDetail.vue +45 -37
- package/src/components/NavIcon.vue +1 -0
- package/src/components/NonVerbalRepDisplay.vue +38 -0
- package/src/components/RelationshipList.vue +99 -0
- package/src/config/use-site-config.ts +3 -0
- package/src/data/ontology-schema.json +1551 -0
- package/src/data/taxonomies.json +543 -0
- package/src/graph/GraphEngine.ts +7 -4
- package/src/router/index.ts +5 -0
- package/src/shims/empty.ts +1 -0
- package/src/shims/node-crypto.ts +6 -0
- package/src/shims/node-path.ts +10 -0
- package/src/stores/vocabulary.ts +75 -25
- package/src/style.css +74 -20
- package/src/utils/concept-formats.ts +22 -20
- package/src/utils/concept-helpers.ts +43 -23
- package/src/utils/designation-registry.ts +124 -0
- package/src/utils/relationship-categories.ts +84 -0
- package/src/views/OntologySchemaView.vue +302 -0
- package/src/views/PageView.vue +28 -17
- package/src/views/StatsView.vue +34 -12
- package/vite.config.ts +8 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ontology Registry — taxonomy-driven labels and definitions for the browser.
|
|
3
|
+
*
|
|
4
|
+
* All enumeration labels, definitions, and colors come from the SKOS taxonomy
|
|
5
|
+
* data extracted at build time from concept-model/ontologies/taxonomies/*.ttl.
|
|
6
|
+
* The browser never hardcodes taxonomy values — it looks them up here.
|
|
7
|
+
*/
|
|
8
|
+
import taxonomyData from '../data/taxonomies.json';
|
|
9
|
+
|
|
10
|
+
export interface TaxonomyConcept {
|
|
11
|
+
id: string;
|
|
12
|
+
iri: string;
|
|
13
|
+
prefLabel: string;
|
|
14
|
+
altLabel?: string;
|
|
15
|
+
definition?: string;
|
|
16
|
+
broader?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Taxonomy {
|
|
20
|
+
scheme: string;
|
|
21
|
+
schemeLabel: string | null;
|
|
22
|
+
schemeDefinition: string | null;
|
|
23
|
+
concepts: Record<string, TaxonomyConcept>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
type TaxonomyKey = keyof typeof taxonomyData;
|
|
27
|
+
|
|
28
|
+
class OntologyRegistry {
|
|
29
|
+
private data: Record<string, Taxonomy>;
|
|
30
|
+
|
|
31
|
+
constructor() {
|
|
32
|
+
this.data = taxonomyData as unknown as Record<string, Taxonomy>;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getConcept(taxonomy: TaxonomyKey, id: string): TaxonomyConcept | null {
|
|
36
|
+
return this.data[taxonomy]?.concepts[id] ?? null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getLabel(taxonomy: TaxonomyKey, id: string | null | undefined): string {
|
|
40
|
+
if (!id) return '';
|
|
41
|
+
return this.getConcept(taxonomy, id)?.prefLabel ?? id;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getAltLabel(taxonomy: TaxonomyKey, id: string): string | null {
|
|
45
|
+
return this.getConcept(taxonomy, id)?.altLabel ?? null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getDefinition(taxonomy: TaxonomyKey, id: string): string | null {
|
|
49
|
+
return this.getConcept(taxonomy, id)?.definition ?? null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
getAll(taxonomy: TaxonomyKey): TaxonomyConcept[] {
|
|
53
|
+
return Object.values(this.data[taxonomy]?.concepts ?? {});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
getScheme(taxonomy: TaxonomyKey): string {
|
|
57
|
+
return this.data[taxonomy]?.scheme ?? '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
has(taxonomy: TaxonomyKey, id: string): boolean {
|
|
61
|
+
return id in (this.data[taxonomy]?.concepts ?? {});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Get broader concept ID, if any (for hierarchical taxonomies like designation-type). */
|
|
65
|
+
getBroader(taxonomy: TaxonomyKey, id: string): string | null {
|
|
66
|
+
return this.getConcept(taxonomy, id)?.broader ?? null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Get all child concept IDs of a given concept. */
|
|
70
|
+
getNarrower(taxonomy: TaxonomyKey, id: string): TaxonomyConcept[] {
|
|
71
|
+
return this.getAll(taxonomy).filter(c => c.broader === id);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const ontology = new OntologyRegistry();
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ontology schema loader — provides class/property definitions parsed from
|
|
3
|
+
* the Glossarist OWL ontology for the Ontospy-style concept view.
|
|
4
|
+
*/
|
|
5
|
+
import schemaData from '../data/ontology-schema.json';
|
|
6
|
+
|
|
7
|
+
export interface OwlClass {
|
|
8
|
+
iri: string;
|
|
9
|
+
compact: string;
|
|
10
|
+
label: string;
|
|
11
|
+
comment: string | null;
|
|
12
|
+
subClassOf: string | null;
|
|
13
|
+
disjointWith: string | null;
|
|
14
|
+
children: string[];
|
|
15
|
+
ancestors: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface OwlProperty {
|
|
19
|
+
iri: string;
|
|
20
|
+
compact: string;
|
|
21
|
+
label: string;
|
|
22
|
+
comment: string | null;
|
|
23
|
+
type: 'object' | 'datatype';
|
|
24
|
+
domain: string | null;
|
|
25
|
+
domainUnion: string[] | null;
|
|
26
|
+
range: string | null;
|
|
27
|
+
rangeUnion: string[] | null;
|
|
28
|
+
inverseOf: string | null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface OntologySchema {
|
|
32
|
+
ontologyIri: string;
|
|
33
|
+
ontologyLabel: string;
|
|
34
|
+
classes: Record<string, OwlClass>;
|
|
35
|
+
classHierarchyRoots: string[];
|
|
36
|
+
properties: Record<string, OwlProperty>;
|
|
37
|
+
propertiesByDomain: Record<string, { object: string[]; datatype: string[] }>;
|
|
38
|
+
stats: { classCount: number; objectPropertyCount: number; datatypePropertyCount: number };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const data = schemaData as unknown as OntologySchema;
|
|
42
|
+
|
|
43
|
+
export function getClass(id: string): OwlClass | null {
|
|
44
|
+
return data.classes[id] ?? null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getProperty(id: string): OwlProperty | null {
|
|
48
|
+
return data.properties[id] ?? null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getPropertiesForDomain(domain: string): { object: OwlProperty[]; datatype: OwlProperty[] } {
|
|
52
|
+
const group = data.propertiesByDomain[domain];
|
|
53
|
+
if (!group) return { object: [], datatype: [] };
|
|
54
|
+
return {
|
|
55
|
+
object: group.object.map(id => data.properties[id]).filter(Boolean),
|
|
56
|
+
datatype: group.datatype.map(id => data.properties[id]).filter(Boolean),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Get all properties applicable to a class, including inherited ones. */
|
|
61
|
+
export function getAllPropertiesForClass(classId: string): { object: OwlProperty[]; datatype: OwlProperty[] } {
|
|
62
|
+
const cls = data.classes[classId];
|
|
63
|
+
if (!cls) return { object: [], datatype: [] };
|
|
64
|
+
|
|
65
|
+
const classChain = [classId, ...cls.ancestors];
|
|
66
|
+
const objectProps: OwlProperty[] = [];
|
|
67
|
+
const datatypeProps: OwlProperty[] = [];
|
|
68
|
+
const seen = new Set<string>();
|
|
69
|
+
|
|
70
|
+
for (const c of classChain) {
|
|
71
|
+
const props = getPropertiesForDomain(c);
|
|
72
|
+
for (const p of props.object) {
|
|
73
|
+
if (!seen.has(p.compact)) { seen.add(p.compact); objectProps.push(p); }
|
|
74
|
+
}
|
|
75
|
+
for (const p of props.datatype) {
|
|
76
|
+
if (!seen.has(p.compact)) { seen.add(p.compact); datatypeProps.push(p); }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { object: objectProps, datatype: datatypeProps };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Get the full class hierarchy tree starting from roots. */
|
|
84
|
+
export function getClassTree(): OwlClass[] {
|
|
85
|
+
return data.classHierarchyRoots
|
|
86
|
+
.map(id => data.classes[id])
|
|
87
|
+
.filter(Boolean);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function getAllClasses(): OwlClass[] {
|
|
91
|
+
return Object.values(data.classes);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function getAllProperties(): OwlProperty[] {
|
|
95
|
+
return Object.values(data.properties);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function getStats() {
|
|
99
|
+
return data.stats;
|
|
100
|
+
}
|
package/src/adapters/types.ts
CHANGED
|
@@ -1,4 +1,38 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/**
|
|
2
|
+
* Infrastructure types for the vocabulary browser.
|
|
3
|
+
* Concept model types come from glossarist-js via model-bridge.ts.
|
|
4
|
+
*/
|
|
5
|
+
import type { RELATIONSHIP_TYPES } from 'glossarist';
|
|
6
|
+
|
|
7
|
+
// Re-export glossarist model types for convenience
|
|
8
|
+
export type {
|
|
9
|
+
Concept,
|
|
10
|
+
LocalizedConcept,
|
|
11
|
+
Designation,
|
|
12
|
+
Expression,
|
|
13
|
+
Abbreviation,
|
|
14
|
+
Symbol as SymbolDesignation,
|
|
15
|
+
GraphicalSymbol,
|
|
16
|
+
Citation,
|
|
17
|
+
ConceptSource,
|
|
18
|
+
RelatedConcept,
|
|
19
|
+
ConceptDate,
|
|
20
|
+
DetailedDefinition,
|
|
21
|
+
NonVerbRep,
|
|
22
|
+
} from 'glossarist';
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
LetterSymbol,
|
|
26
|
+
GrammarInfo,
|
|
27
|
+
Pronunciation,
|
|
28
|
+
ConceptReference,
|
|
29
|
+
Locality,
|
|
30
|
+
} from 'glossarist/models';
|
|
31
|
+
|
|
32
|
+
export { RELATIONSHIP_TYPES, DATE_TYPES } from 'glossarist';
|
|
33
|
+
export { GRAMMAR_GENDERS, GRAMMAR_NUMBERS, GRAMMAR_PARTS_OF_SPEECH } from 'glossarist/models';
|
|
34
|
+
|
|
35
|
+
// ── Dataset metadata ──────────────────────────────────────────────────────
|
|
2
36
|
|
|
3
37
|
export interface Manifest {
|
|
4
38
|
id: string;
|
|
@@ -39,6 +73,7 @@ export interface ConceptIndex {
|
|
|
39
73
|
|
|
40
74
|
export interface ConceptSummary {
|
|
41
75
|
id: string;
|
|
76
|
+
designations: Record<string, string>;
|
|
42
77
|
eng: string;
|
|
43
78
|
status: string;
|
|
44
79
|
}
|
|
@@ -50,83 +85,25 @@ export interface ConceptEntry {
|
|
|
50
85
|
status: string;
|
|
51
86
|
}
|
|
52
87
|
|
|
53
|
-
export interface ConceptDocument {
|
|
54
|
-
'@context': string;
|
|
55
|
-
'@id': string;
|
|
56
|
-
'@type': string;
|
|
57
|
-
'gl:identifier': string;
|
|
58
|
-
'gl:localizedConcept': Record<string, LocalizedConcept>;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export interface LocalizedConcept {
|
|
62
|
-
'@id': string;
|
|
63
|
-
'@type': string;
|
|
64
|
-
'gl:languageCode': string;
|
|
65
|
-
'gl:entryStatus'?: string;
|
|
66
|
-
'gl:designation'?: Designation[];
|
|
67
|
-
'gl:definition'?: DetailedDefinition[];
|
|
68
|
-
'gl:notes'?: DetailedDefinition[];
|
|
69
|
-
'gl:examples'?: DetailedDefinition[];
|
|
70
|
-
'gl:source'?: ConceptSource[];
|
|
71
|
-
'gl:release'?: number;
|
|
72
|
-
'gl:reviewDate'?: string;
|
|
73
|
-
'gl:reviewDecisionDate'?: string;
|
|
74
|
-
'gl:reviewDecisionEvent'?: string;
|
|
75
|
-
'gl:reviewStatus'?: string;
|
|
76
|
-
'gl:reviewDecision'?: string;
|
|
77
|
-
'gl:reviewDecisionNotes'?: string;
|
|
78
|
-
'gl:dates'?: ConceptDate[];
|
|
79
|
-
'gl:references'?: CrossReference[];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export interface Designation {
|
|
83
|
-
'@type': string;
|
|
84
|
-
'gl:normativeStatus': string;
|
|
85
|
-
'gl:term': string;
|
|
86
|
-
'gl:gender'?: string;
|
|
87
|
-
'gl:plurality'?: string;
|
|
88
|
-
'gl:international'?: boolean;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export interface DetailedDefinition {
|
|
92
|
-
'@type': string;
|
|
93
|
-
'gl:content': string;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export interface ConceptSource {
|
|
97
|
-
'@type': string;
|
|
98
|
-
'gl:sourceType'?: string;
|
|
99
|
-
'gl:sourceStatus'?: string;
|
|
100
|
-
'gl:modification'?: string;
|
|
101
|
-
'gl:origin'?: {
|
|
102
|
-
'@type': string;
|
|
103
|
-
'gl:ref'?: string;
|
|
104
|
-
'gl:clause'?: string;
|
|
105
|
-
'gl:link'?: string;
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export interface ConceptDate {
|
|
110
|
-
'gl:dateType': string;
|
|
111
|
-
'gl:date': string;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface CrossReference {
|
|
115
|
-
'@id': string;
|
|
116
|
-
'gl:term': string;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
88
|
export interface DatasetRegistry {
|
|
120
89
|
id: string;
|
|
121
90
|
manifestUrl: string;
|
|
122
91
|
}
|
|
123
92
|
|
|
93
|
+
// ── Graph types ────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
export const EDGE_TYPE = {
|
|
96
|
+
REFERENCES: 'references',
|
|
97
|
+
DOMAIN: 'domain',
|
|
98
|
+
} as const;
|
|
99
|
+
|
|
124
100
|
export interface GraphEdge {
|
|
125
|
-
source: string;
|
|
126
|
-
target: string;
|
|
101
|
+
source: string;
|
|
102
|
+
target: string;
|
|
127
103
|
type: string;
|
|
128
104
|
label?: string;
|
|
129
105
|
register: string;
|
|
106
|
+
lang?: string;
|
|
130
107
|
}
|
|
131
108
|
|
|
132
109
|
export interface GraphNode {
|
|
@@ -136,8 +113,11 @@ export interface GraphNode {
|
|
|
136
113
|
designations: Record<string, string>;
|
|
137
114
|
status: string;
|
|
138
115
|
loaded: boolean;
|
|
116
|
+
nodeType?: 'concept' | 'domain';
|
|
139
117
|
}
|
|
140
118
|
|
|
119
|
+
// ── Search ─────────────────────────────────────────────────────────────────
|
|
120
|
+
|
|
141
121
|
export interface SearchHit {
|
|
142
122
|
conceptId: string;
|
|
143
123
|
registerId: string;
|
|
@@ -147,14 +127,9 @@ export interface SearchHit {
|
|
|
147
127
|
snippet?: string;
|
|
148
128
|
}
|
|
149
129
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
| 'broader'
|
|
154
|
-
| 'see'
|
|
155
|
-
| 'references'
|
|
156
|
-
| 'replaces'
|
|
157
|
-
| 'superseded';
|
|
130
|
+
// ── Resolution ─────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
export type RelationType = typeof RELATIONSHIP_TYPES[number];
|
|
158
133
|
|
|
159
134
|
export type Resolution =
|
|
160
135
|
| { type: 'internal'; registerId: string; conceptId: string; crossDataset: boolean }
|
|
@@ -14,7 +14,7 @@ const route = useRoute();
|
|
|
14
14
|
const { getColor } = useDsStyle();
|
|
15
15
|
const { globalPages, datasetPages, config: siteConfig } = useSiteConfig();
|
|
16
16
|
|
|
17
|
-
const currentDataset = computed(() =>
|
|
17
|
+
const currentDataset = computed(() => route.params.registerId as string ?? '');
|
|
18
18
|
|
|
19
19
|
const datasetEntries = computed(() => {
|
|
20
20
|
const entries: { id: string; title: string; loaded: boolean; conceptCount: number }[] = [];
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Citation } from 'glossarist';
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
citation: Citation;
|
|
6
|
+
}>();
|
|
7
|
+
|
|
8
|
+
function formatRef(c: Citation): string {
|
|
9
|
+
const ref = c.ref;
|
|
10
|
+
if (!ref) return '';
|
|
11
|
+
const parts: string[] = [];
|
|
12
|
+
if (ref.source) parts.push(ref.source);
|
|
13
|
+
if (ref.id) parts.push(ref.id);
|
|
14
|
+
if (ref.version) parts.push(`(${ref.version})`);
|
|
15
|
+
return parts.join(' ');
|
|
16
|
+
}
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<span class="inline">
|
|
21
|
+
<template v-if="citation.ref">
|
|
22
|
+
<span v-if="citation.ref.source" class="font-medium">{{ citation.ref.source }}</span>
|
|
23
|
+
<span v-if="citation.ref.id"> {{ citation.ref.id }}</span>
|
|
24
|
+
<span v-if="citation.ref.version" class="text-ink-400"> ({{ citation.ref.version }})</span>
|
|
25
|
+
</template>
|
|
26
|
+
<template v-if="citation.locality">
|
|
27
|
+
<span v-if="citation.locality.type" class="text-ink-400">, {{ citation.locality.type }}</span>
|
|
28
|
+
<span v-if="citation.locality.referenceFrom" class="text-ink-400">
|
|
29
|
+
{{ citation.locality.referenceTo ? ` ${citation.locality.referenceFrom}–${citation.locality.referenceTo}` : ` ${citation.locality.referenceFrom}` }}
|
|
30
|
+
</span>
|
|
31
|
+
</template>
|
|
32
|
+
<a v-if="citation.link" :href="citation.link" target="_blank" rel="noopener" class="concept-link ml-1">[link]</a>
|
|
33
|
+
<span v-if="citation.original" class="text-xs text-ink-300 ml-1">(orig: {{ citation.original }})</span>
|
|
34
|
+
</span>
|
|
35
|
+
</template>
|