@glossarist/concept-browser 0.7.84 → 0.7.86
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 +2 -2
- package/scripts/build-edges.js +42 -26
- package/scripts/generate-data.mjs +45 -5
- package/scripts/migrate-v1-to-v3.d.mts +6 -0
- package/scripts/migrate-v1-to-v3.mjs +176 -0
- package/src/__tests__/components/PartitiveRelationDiagram.test.ts +92 -136
- package/src/__tests__/composables/use-concept-edges-v2.test.ts +115 -87
- package/src/__tests__/partitive-relation-styling.test.ts +12 -54
- package/src/__tests__/partitive-relation.test.ts +42 -90
- package/src/__tests__/scripts/migrate-v1-to-v3.d.mts +8 -0
- package/src/__tests__/scripts/migrate-v1-to-v3.test.ts +115 -0
- package/src/__tests__/utils/partitive-multiplicity.test.ts +113 -0
- package/src/adapters/model-bridge.ts +15 -19
- package/src/adapters/non-verbal/glossarist-augment.d.ts +34 -18
- package/src/adapters/types.ts +2 -8
- package/src/components/PartitiveRelationDiagram.vue +88 -76
- package/src/components/PartitiveRelationList.vue +26 -34
- package/src/components/RelationSphere.vue +173 -106
- package/src/composables/use-concept-edges.ts +21 -21
- package/src/utils/partitive-multiplicity.ts +106 -0
- package/src/utils/partitive-relation-styling.ts +29 -38
- package/src/views/ConceptView.vue +7 -13
- package/src/__tests__/model-bridge-partitive.test.ts +0 -146
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Partitive relation styling — colors for one-to-many
|
|
2
|
+
* Partitive relation styling — colors and labels for one-to-many
|
|
3
|
+
* partitive decompositions.
|
|
3
4
|
*
|
|
4
5
|
* Independent of relationship-categories.ts (which handles binary edges).
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - plurality (type-shared, uncertain) adds accent badges
|
|
6
|
+
* The visual encoding for PartitiveRelation comes from two orthogonal
|
|
7
|
+
* axes per ISO 704:2022:
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* 1. completeness (complete / partial) — relation-level metadata,
|
|
10
|
+
* affects overall opacity. Partial relations have a continued
|
|
11
|
+
* backline ("more exist but aren't shown").
|
|
12
|
+
* 2. multiplicity + is_delimiting (per-member) — see
|
|
13
|
+
* partitive-multiplicity.ts for the registry + rakeStrokeStyle().
|
|
14
|
+
*
|
|
15
|
+
* This module owns:
|
|
16
|
+
* - relation-level color/badge (completeness-driven)
|
|
17
|
+
* - human-readable labels for badges
|
|
18
|
+
*
|
|
19
|
+
* It does NOT encode multiplicity visuals — those live in
|
|
20
|
+
* partitive-multiplicity.ts (separated so renderers can consume the
|
|
21
|
+
* pure rakeStrokeStyle() helper without pulling badge strings).
|
|
11
22
|
*/
|
|
12
23
|
|
|
13
|
-
import type { TypeSharedPluralityWire } from '../adapters/types';
|
|
14
|
-
|
|
15
24
|
const BASE_LIGHT = '#0d9488';
|
|
16
25
|
const BASE_DARK = '#2dd4bf';
|
|
17
|
-
const ACCENT_SHARED_LIGHT = '#3b82f6';
|
|
18
|
-
const ACCENT_SHARED_DARK = '#60a5fa';
|
|
19
|
-
const ACCENT_UNCERTAIN_LIGHT = '#f59e0b';
|
|
20
|
-
const ACCENT_UNCERTAIN_DARK = '#fbbf24';
|
|
21
26
|
|
|
22
27
|
export interface PartitiveRelationStyle {
|
|
23
28
|
color: string;
|
|
@@ -25,38 +30,24 @@ export interface PartitiveRelationStyle {
|
|
|
25
30
|
opacity: number;
|
|
26
31
|
}
|
|
27
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Relation-level style (color + opacity) driven by completeness.
|
|
35
|
+
*
|
|
36
|
+
* Per-member multiplicity + delimiting rendering is via
|
|
37
|
+
* `rakeStrokeStyle()` from partitive-multiplicity.ts, not here —
|
|
38
|
+
* this helper is for the relation's overall frame + header badges.
|
|
39
|
+
*/
|
|
28
40
|
export function partitiveRelationStyle(
|
|
29
41
|
completeness: 'complete' | 'partial',
|
|
30
|
-
plurality: TypeSharedPluralityWire | null,
|
|
31
42
|
isDark: boolean,
|
|
32
43
|
): PartitiveRelationStyle {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
badgeClass = 'badge-blue';
|
|
39
|
-
if (plurality.isUncertain) {
|
|
40
|
-
badgeClass = 'badge-yellow';
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { color: base, badgeClass, opacity };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function pluralityColor(
|
|
48
|
-
plurality: TypeSharedPluralityWire,
|
|
49
|
-
isDark: boolean,
|
|
50
|
-
): string {
|
|
51
|
-
if (plurality.isUncertain) return isDark ? ACCENT_UNCERTAIN_DARK : ACCENT_UNCERTAIN_LIGHT;
|
|
52
|
-
if (plurality.isShared) return isDark ? ACCENT_SHARED_DARK : ACCENT_SHARED_LIGHT;
|
|
53
|
-
return isDark ? BASE_DARK : BASE_LIGHT;
|
|
44
|
+
return {
|
|
45
|
+
color: isDark ? BASE_DARK : BASE_LIGHT,
|
|
46
|
+
badgeClass: 'badge-teal',
|
|
47
|
+
opacity: completeness === 'partial' ? 0.6 : 1.0,
|
|
48
|
+
};
|
|
54
49
|
}
|
|
55
50
|
|
|
56
51
|
export function completenessLabel(completeness: 'complete' | 'partial'): string {
|
|
57
52
|
return completeness === 'partial' ? 'Partial' : 'Complete';
|
|
58
53
|
}
|
|
59
|
-
|
|
60
|
-
export function certaintyLabel(certainty: 'confirmed' | 'possible'): string {
|
|
61
|
-
return certainty === 'possible' ? 'Possible' : 'Confirmed';
|
|
62
|
-
}
|
|
@@ -5,6 +5,8 @@ import { useVocabularyStore } from '../stores/vocabulary';
|
|
|
5
5
|
import { conceptUri } from '../adapters/model-bridge';
|
|
6
6
|
import { UriRouter } from '../adapters/UriRouter';
|
|
7
7
|
import type { PartitiveRelationWire, PartitiveMemberWire } from '../adapters/types';
|
|
8
|
+
import type { PartitiveMember } from 'glossarist/models';
|
|
9
|
+
import type { PartitiveMultiplicity } from '../utils/partitive-multiplicity';
|
|
8
10
|
import ConceptDetail from '../components/ConceptDetail.vue';
|
|
9
11
|
import RelationSphere from '../components/RelationSphere.vue';
|
|
10
12
|
import ShortcutsModal from '../components/ShortcutsModal.vue';
|
|
@@ -80,15 +82,14 @@ const partitiveRelationsForSphere = computed<PartitiveRelationWire[]>(() => {
|
|
|
80
82
|
: null;
|
|
81
83
|
if (!compRef.id || !compTarget) continue;
|
|
82
84
|
const partitives: PartitiveMemberWire[] = rel.partitives
|
|
83
|
-
.map(member => {
|
|
85
|
+
.map((member): PartitiveMemberWire | null => {
|
|
84
86
|
const ref = member.ref;
|
|
85
87
|
if (!ref?.id) return null;
|
|
86
|
-
const
|
|
87
|
-
? activeRegisterId.value
|
|
88
|
-
: activeRegisterId.value;
|
|
89
|
-
const uri = UriRouter.buildConceptUri(m.uriBase, targetRegister, ref.id);
|
|
88
|
+
const uri = UriRouter.buildConceptUri(m.uriBase, activeRegisterId.value, ref.id);
|
|
90
89
|
if (uri === sourceUri) return null;
|
|
91
|
-
|
|
90
|
+
const multiplicity: PartitiveMultiplicity = member.multiplicity;
|
|
91
|
+
const isDelimiting = member.isDelimiting;
|
|
92
|
+
return { uri, multiplicity, isDelimiting };
|
|
92
93
|
})
|
|
93
94
|
.filter((x): x is PartitiveMemberWire => x !== null);
|
|
94
95
|
if (partitives.length === 0) continue;
|
|
@@ -97,13 +98,6 @@ const partitiveRelationsForSphere = computed<PartitiveRelationWire[]>(() => {
|
|
|
97
98
|
comprehensive: compTarget,
|
|
98
99
|
partitives,
|
|
99
100
|
completeness: rel.completeness,
|
|
100
|
-
plurality: rel.plurality
|
|
101
|
-
? {
|
|
102
|
-
isShared: rel.plurality.isShared,
|
|
103
|
-
isUncertain: rel.plurality.isUncertain,
|
|
104
|
-
sharedType: null,
|
|
105
|
-
}
|
|
106
|
-
: null,
|
|
107
101
|
register: activeRegisterId.value,
|
|
108
102
|
});
|
|
109
103
|
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { conceptFromJson } from '../adapters/model-bridge';
|
|
3
|
-
|
|
4
|
-
describe('conceptFromJson — gl:partitiveRelations (v2)', () => {
|
|
5
|
-
function makeJson(overrides: Record<string, unknown> = {}) {
|
|
6
|
-
return {
|
|
7
|
-
'@type': 'gl:Concept',
|
|
8
|
-
'@id': 'https://example.org/test/concept/1.3',
|
|
9
|
-
'gl:identifier': '1.3',
|
|
10
|
-
'gl:localizedConcept': {},
|
|
11
|
-
...overrides,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
it('returns empty array when no partitiveRelations', () => {
|
|
16
|
-
const c = conceptFromJson(makeJson());
|
|
17
|
-
expect(c.partitiveRelations).toEqual([]);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('parses comprehensive + 2 partitives', () => {
|
|
21
|
-
const c = conceptFromJson(makeJson({
|
|
22
|
-
'gl:partitiveRelations': [{
|
|
23
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
24
|
-
'gl:hasPartitive': [
|
|
25
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
26
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.5' } },
|
|
27
|
-
],
|
|
28
|
-
}],
|
|
29
|
-
}));
|
|
30
|
-
expect(c.partitiveRelations).toHaveLength(1);
|
|
31
|
-
const r = c.partitiveRelations[0];
|
|
32
|
-
expect(r.comprehensive.source).toBe('urn:test');
|
|
33
|
-
expect(r.comprehensive.id).toBe('1.3');
|
|
34
|
-
expect(r.partitives).toHaveLength(2);
|
|
35
|
-
expect(r.partitives[0].ref.id).toBe('1.4');
|
|
36
|
-
expect(r.partitives[1].ref.id).toBe('1.5');
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('parses completeness', () => {
|
|
40
|
-
const c = conceptFromJson(makeJson({
|
|
41
|
-
'gl:partitiveRelations': [{
|
|
42
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
43
|
-
'gl:hasPartitive': [
|
|
44
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
45
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.5' } },
|
|
46
|
-
],
|
|
47
|
-
'gl:completeness': 'partial',
|
|
48
|
-
}],
|
|
49
|
-
}));
|
|
50
|
-
expect(c.partitiveRelations[0].completeness).toBe('partial');
|
|
51
|
-
expect(c.partitiveRelations[0].isPartial).toBe(true);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('parses per-member certainty', () => {
|
|
55
|
-
const c = conceptFromJson(makeJson({
|
|
56
|
-
'gl:partitiveRelations': [{
|
|
57
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
58
|
-
'gl:hasPartitive': [
|
|
59
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
60
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.5' }, 'gl:certainty': 'possible' },
|
|
61
|
-
],
|
|
62
|
-
}],
|
|
63
|
-
}));
|
|
64
|
-
const members = c.partitiveRelations[0].partitives;
|
|
65
|
-
expect(members[0].certainty).toBe('confirmed');
|
|
66
|
-
expect(members[1].certainty).toBe('possible');
|
|
67
|
-
expect(members[1].isPossible).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('parses plurality block', () => {
|
|
71
|
-
const c = conceptFromJson(makeJson({
|
|
72
|
-
'gl:partitiveRelations': [{
|
|
73
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
74
|
-
'gl:hasPartitive': [
|
|
75
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
76
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.5' } },
|
|
77
|
-
],
|
|
78
|
-
'gl:hasPlurality': {
|
|
79
|
-
'gl:isShared': true,
|
|
80
|
-
'gl:isUncertain': true,
|
|
81
|
-
'gl:sharedType': { 'gl:source': 'urn:test', 'gl:id': '1.99' },
|
|
82
|
-
},
|
|
83
|
-
}],
|
|
84
|
-
}));
|
|
85
|
-
const p = c.partitiveRelations[0].plurality;
|
|
86
|
-
expect(p).not.toBeNull();
|
|
87
|
-
expect(p?.isShared).toBe(true);
|
|
88
|
-
expect(p?.isUncertain).toBe(true);
|
|
89
|
-
expect(p?.sharedType?.id).toBe('1.99');
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
it('parses criterion as localized hash', () => {
|
|
93
|
-
const c = conceptFromJson(makeJson({
|
|
94
|
-
'gl:partitiveRelations': [{
|
|
95
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
96
|
-
'gl:hasPartitive': [
|
|
97
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
98
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.5' } },
|
|
99
|
-
],
|
|
100
|
-
'gl:criterion': { eng: 'physical structure', fra: 'structure physique' },
|
|
101
|
-
}],
|
|
102
|
-
}));
|
|
103
|
-
expect(c.partitiveRelations[0].criterion?.eng).toBe('physical structure');
|
|
104
|
-
expect(c.partitiveRelations[0].criterion?.fra).toBe('structure physique');
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('parses criterion as plain string (normalized to default)', () => {
|
|
108
|
-
const c = conceptFromJson(makeJson({
|
|
109
|
-
'gl:partitiveRelations': [{
|
|
110
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
111
|
-
'gl:hasPartitive': [
|
|
112
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
113
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.5' } },
|
|
114
|
-
],
|
|
115
|
-
'gl:criterion': 'a bare criterion string',
|
|
116
|
-
}],
|
|
117
|
-
}));
|
|
118
|
-
expect(c.partitiveRelations[0].criterion?.default).toBe('a bare criterion string');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('skips relation with no comprehensive', () => {
|
|
122
|
-
const c = conceptFromJson(makeJson({
|
|
123
|
-
'gl:partitiveRelations': [{
|
|
124
|
-
'gl:hasPartitive': [
|
|
125
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
126
|
-
],
|
|
127
|
-
}],
|
|
128
|
-
}));
|
|
129
|
-
expect(c.partitiveRelations).toEqual([]);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('skips relation with < 2 valid partitives', () => {
|
|
133
|
-
const c = conceptFromJson(makeJson({
|
|
134
|
-
'gl:partitiveRelations': [{
|
|
135
|
-
'gl:comprehensive': { 'gl:source': 'urn:test', 'gl:id': '1.3' },
|
|
136
|
-
'gl:hasPartitive': [
|
|
137
|
-
{ 'gl:ref': { 'gl:source': 'urn:test', 'gl:id': '1.4' } },
|
|
138
|
-
// Single member — PartitiveRelation constructor throws
|
|
139
|
-
],
|
|
140
|
-
}],
|
|
141
|
-
}));
|
|
142
|
-
// The constructor throws before we catch it, so the relation is dropped
|
|
143
|
-
// via the runtime error path. conceptFromJson should not propagate.
|
|
144
|
-
expect(c.partitiveRelations).toEqual([]);
|
|
145
|
-
});
|
|
146
|
-
});
|