@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.
Files changed (54) hide show
  1. package/README.md +3 -2
  2. package/cli/index.mjs +2 -1
  3. package/env.d.ts +5 -0
  4. package/package.json +4 -3
  5. package/scripts/build-edges.js +78 -10
  6. package/scripts/generate-data.mjs +152 -20
  7. package/scripts/generate-ontology-data.mjs +184 -0
  8. package/scripts/generate-ontology-schema.mjs +315 -0
  9. package/src/__tests__/concept-card.test.ts +1 -1
  10. package/src/__tests__/concept-detail-interaction.test.ts +40 -18
  11. package/src/__tests__/concept-formats.test.ts +32 -30
  12. package/src/__tests__/concept-timeline.test.ts +108 -83
  13. package/src/__tests__/concept-view.test.ts +15 -2
  14. package/src/__tests__/dataset-adapter.test.ts +172 -23
  15. package/src/__tests__/dataset-view.test.ts +6 -5
  16. package/src/__tests__/designation-registry.test.ts +161 -0
  17. package/src/__tests__/graph.test.ts +62 -0
  18. package/src/__tests__/language-detail.test.ts +117 -60
  19. package/src/__tests__/ontology-registry.test.ts +109 -0
  20. package/src/__tests__/relationship-categories.test.ts +62 -0
  21. package/src/__tests__/test-helpers.ts +11 -8
  22. package/src/adapters/DatasetAdapter.ts +171 -48
  23. package/src/adapters/model-bridge.ts +277 -0
  24. package/src/adapters/ontology-registry.ts +75 -0
  25. package/src/adapters/ontology-schema.ts +100 -0
  26. package/src/adapters/types.ts +52 -77
  27. package/src/components/AppSidebar.vue +1 -1
  28. package/src/components/CitationDisplay.vue +35 -0
  29. package/src/components/ConceptDetail.vue +334 -93
  30. package/src/components/ConceptRdfView.vue +397 -0
  31. package/src/components/ConceptTimeline.vue +56 -52
  32. package/src/components/GraphPanel.vue +96 -31
  33. package/src/components/LanguageDetail.vue +45 -37
  34. package/src/components/NavIcon.vue +1 -0
  35. package/src/components/NonVerbalRepDisplay.vue +38 -0
  36. package/src/components/RelationshipList.vue +99 -0
  37. package/src/config/use-site-config.ts +3 -0
  38. package/src/data/ontology-schema.json +1551 -0
  39. package/src/data/taxonomies.json +543 -0
  40. package/src/graph/GraphEngine.ts +7 -4
  41. package/src/router/index.ts +5 -0
  42. package/src/shims/empty.ts +1 -0
  43. package/src/shims/node-crypto.ts +6 -0
  44. package/src/shims/node-path.ts +10 -0
  45. package/src/stores/vocabulary.ts +75 -25
  46. package/src/style.css +74 -20
  47. package/src/utils/concept-formats.ts +22 -20
  48. package/src/utils/concept-helpers.ts +43 -23
  49. package/src/utils/designation-registry.ts +124 -0
  50. package/src/utils/relationship-categories.ts +84 -0
  51. package/src/views/OntologySchemaView.vue +302 -0
  52. package/src/views/PageView.vue +28 -17
  53. package/src/views/StatsView.vue +34 -12
  54. 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
+ }
@@ -1,4 +1,38 @@
1
- /** Core types for the vocabulary browser data model. */
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; // concept URI
126
- target: string; // concept URI
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
- export type RelationType =
151
- | 'related'
152
- | 'narrower'
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(() => (route.params as any).registerId ?? '');
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>