@glossarist/concept-browser 0.7.95 → 0.7.96
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
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@glossarist/concept-browser",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.96",
|
|
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": {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
2
|
import { hashSeed, expandParams, portSide, portPoint, idToUriGet, visibleNodeIds } from '../utils/sphere-math';
|
|
3
|
+
import type { SphereHyperedgeLike } from '../utils/sphere-math';
|
|
3
4
|
|
|
4
5
|
describe('sphere-math', () => {
|
|
5
6
|
describe('hashSeed', () => {
|
|
@@ -148,6 +149,30 @@ describe('sphere-math', () => {
|
|
|
148
149
|
expect(visible.has('a')).toBe(false);
|
|
149
150
|
});
|
|
150
151
|
|
|
152
|
+
it('hyperedge member with no bilateral edges stays visible while its bundle is not muted', () => {
|
|
153
|
+
const nodes = [focus, a, b];
|
|
154
|
+
const links: Array<{ source: string; target: string; type: string }> = [];
|
|
155
|
+
const hyperedges: SphereHyperedgeLike[] = [
|
|
156
|
+
{ comprehensive: 'focus', members: ['a', 'b'], muteKey: '__partitive__' },
|
|
157
|
+
];
|
|
158
|
+
const visible = visibleNodeIds(nodes, links, new Set(), new Set(), hyperedges);
|
|
159
|
+
expect(visible.has('focus')).toBe(true);
|
|
160
|
+
expect(visible.has('a')).toBe(true);
|
|
161
|
+
expect(visible.has('b')).toBe(true);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('hyperedge members disappear when the bundle is muted', () => {
|
|
165
|
+
const nodes = [focus, a, b];
|
|
166
|
+
const links: Array<{ source: string; target: string; type: string }> = [];
|
|
167
|
+
const hyperedges: SphereHyperedgeLike[] = [
|
|
168
|
+
{ comprehensive: 'focus', members: ['a', 'b'], muteKey: '__partitive__' },
|
|
169
|
+
];
|
|
170
|
+
const visible = visibleNodeIds(nodes, links, new Set(['__partitive__']), new Set(), hyperedges);
|
|
171
|
+
expect(visible.has('focus')).toBe(true);
|
|
172
|
+
expect(visible.has('a')).toBe(false);
|
|
173
|
+
expect(visible.has('b')).toBe(false);
|
|
174
|
+
});
|
|
175
|
+
|
|
151
176
|
it('all-visible when no mutes are set', () => {
|
|
152
177
|
const nodes = [focus, a, b, c, d];
|
|
153
178
|
const links = buildLinks([
|
|
@@ -97,6 +97,13 @@ const mutedRegisters = ref<Set<string>>(new Set());
|
|
|
97
97
|
const panelOpen = ref(true);
|
|
98
98
|
const MAX_NODES = 36;
|
|
99
99
|
|
|
100
|
+
/** Convert a concept URI to the internal `${registerId}/${conceptId}` node ID. */
|
|
101
|
+
function uriToNodeId(uri: string): string {
|
|
102
|
+
const parsed = UriRouter.parseUri(uri);
|
|
103
|
+
if (parsed) return `${parsed.registerId}/${parsed.conceptId}`;
|
|
104
|
+
return uri;
|
|
105
|
+
}
|
|
106
|
+
|
|
100
107
|
/* Sphere-display language. Defaults to the i18n UI locale (which reads
|
|
101
108
|
localStorage) so e.g. a user returning in French mode sees French
|
|
102
109
|
terms by default. The user can override via the Language selector. */
|
|
@@ -735,8 +742,29 @@ function renderDOM() {
|
|
|
735
742
|
|
|
736
743
|
/* Cards with no visible edges (after applying type + register mutes)
|
|
737
744
|
float with no connections — hide them so toggling a relation type
|
|
738
|
-
cleans up orphaned cards. Focus node (depth 0) always renders.
|
|
739
|
-
|
|
745
|
+
cleans up orphaned cards. Focus node (depth 0) always renders.
|
|
746
|
+
Hyperedge members are always visible while their bundle is not
|
|
747
|
+
muted, even when they have no bilateral edges, so the rake has a
|
|
748
|
+
card to point at. */
|
|
749
|
+
const hyperedgesForVisibility = [
|
|
750
|
+
...(props.partitiveRelations ?? []).map(rel => ({
|
|
751
|
+
comprehensive: uriToNodeId(rel.comprehensive),
|
|
752
|
+
members: rel.partitives.map(m => uriToNodeId(m.uri)),
|
|
753
|
+
muteKey: '__partitive__',
|
|
754
|
+
})),
|
|
755
|
+
...(props.genericRelations ?? []).map(rel => ({
|
|
756
|
+
comprehensive: uriToNodeId(rel.comprehensive),
|
|
757
|
+
members: rel.members.map(m => uriToNodeId(m.uri)),
|
|
758
|
+
muteKey: '__generic__',
|
|
759
|
+
})),
|
|
760
|
+
];
|
|
761
|
+
const visibleIds = visibleNodeIds(
|
|
762
|
+
nodes,
|
|
763
|
+
links,
|
|
764
|
+
mutedTypes.value,
|
|
765
|
+
mutedRegisters.value,
|
|
766
|
+
hyperedgesForVisibility,
|
|
767
|
+
);
|
|
740
768
|
|
|
741
769
|
for (const n of nodes) {
|
|
742
770
|
if (!visibleIds.has(n.id)) continue;
|
package/src/utils/sphere-math.ts
CHANGED
|
@@ -65,13 +65,27 @@ export interface SphereLinkLike {
|
|
|
65
65
|
type: string;
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
export interface SphereHyperedgeLike {
|
|
69
|
+
/** Node ID of the comprehensive concept. */
|
|
70
|
+
comprehensive: string;
|
|
71
|
+
/** Node IDs of the member concepts. */
|
|
72
|
+
members: readonly string[];
|
|
73
|
+
/** Mute key — when present in mutedTypes, the whole hyperedge is hidden. */
|
|
74
|
+
muteKey?: string;
|
|
75
|
+
}
|
|
76
|
+
|
|
68
77
|
/**
|
|
69
78
|
* IDs of nodes that should render given the current mute state.
|
|
70
79
|
*
|
|
71
80
|
* The focus node (depth 0) is always visible. Every other node must have
|
|
72
81
|
* at least one edge whose type is not muted AND whose other endpoint is
|
|
73
|
-
* not in a muted register —
|
|
74
|
-
*
|
|
82
|
+
* not in a muted register — OR be a member of a non-muted hyperedge —
|
|
83
|
+
* otherwise the card floats with no connections and should disappear
|
|
84
|
+
* when its relations are hidden.
|
|
85
|
+
*
|
|
86
|
+
* Hyperedge membership counts as a "visible edge" because rake bundles
|
|
87
|
+
* are the connection: a member with no bilateral edges still needs its
|
|
88
|
+
* card rendered so the rake has something to point at.
|
|
75
89
|
*
|
|
76
90
|
* Extracted as a pure function so the visibility contract can be tested
|
|
77
91
|
* without mounting the sphere component.
|
|
@@ -81,10 +95,19 @@ export function visibleNodeIds(
|
|
|
81
95
|
links: readonly SphereLinkLike[],
|
|
82
96
|
mutedTypes: ReadonlySet<string>,
|
|
83
97
|
mutedRegisters: ReadonlySet<string>,
|
|
98
|
+
hyperedges: readonly SphereHyperedgeLike[] = [],
|
|
84
99
|
): Set<string> {
|
|
85
100
|
const byId = new Map<string, SphereNodeLike>();
|
|
86
101
|
for (const n of nodes) byId.set(n.id, n);
|
|
87
102
|
|
|
103
|
+
/* Pre-compute node IDs that participate in a non-muted hyperedge. */
|
|
104
|
+
const hyperedgeNodeIds = new Set<string>();
|
|
105
|
+
for (const he of hyperedges) {
|
|
106
|
+
if (he.muteKey && mutedTypes.has(he.muteKey)) continue;
|
|
107
|
+
hyperedgeNodeIds.add(he.comprehensive);
|
|
108
|
+
for (const m of he.members) hyperedgeNodeIds.add(m);
|
|
109
|
+
}
|
|
110
|
+
|
|
88
111
|
const visible = new Set<string>();
|
|
89
112
|
for (const n of nodes) {
|
|
90
113
|
if (n.depth === 0) {
|
|
@@ -92,6 +115,10 @@ export function visibleNodeIds(
|
|
|
92
115
|
continue;
|
|
93
116
|
}
|
|
94
117
|
if (mutedRegisters.has(n.register)) continue;
|
|
118
|
+
if (hyperedgeNodeIds.has(n.id)) {
|
|
119
|
+
visible.add(n.id);
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
95
122
|
for (const l of links) {
|
|
96
123
|
if (mutedTypes.has(l.type)) continue;
|
|
97
124
|
const otherId = l.source === n.id ? l.target : l.target === n.id ? l.source : null;
|
|
@@ -106,3 +133,4 @@ export function visibleNodeIds(
|
|
|
106
133
|
return visible;
|
|
107
134
|
}
|
|
108
135
|
|
|
136
|
+
|