@glossarist/concept-browser 0.7.98 → 0.7.99

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.98",
3
+ "version": "0.7.99",
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": {
@@ -99,7 +99,7 @@ describe('drawGenericPipes — pipe-and-thread contract', () => {
99
99
  expect(svg.querySelectorAll('rect.pipe-junction').length).toBe(1);
100
100
  });
101
101
 
102
- it('renders a delimiting-characteristic label per thread', () => {
102
+ it('renders ONE characteristic label (the criterion) — no per-thread labels', () => {
103
103
  const svg = makeSvg();
104
104
  drawGenericPipes(svg, pos, {
105
105
  isDark: false,
@@ -109,11 +109,26 @@ describe('drawGenericPipes — pipe-and-thread contract', () => {
109
109
  locale: 'eng',
110
110
  });
111
111
  const labels = Array.from(svg.querySelectorAll('text.pipe-label')).map(t => t.textContent);
112
- /* 3 per-member characteristics + 1 group-level criterion = 4 labels */
113
- expect(labels).toContain('multiple of a unit');
114
- expect(labels).toContain('submultiple of a unit');
115
- expect(labels).toContain('coherent derived unit');
116
- expect(labels).toContain('by magnitude relationship');
112
+ /* Exactly one label — the hyperedge-level criterion. Per-member
113
+ delimitingCharacteristic values are NOT rendered. */
114
+ expect(labels).toEqual(['by magnitude relationship']);
115
+ });
116
+
117
+ it('renders NO label when the criterion is absent', () => {
118
+ const svg = makeSvg();
119
+ const rel = makeRelation();
120
+ delete rel.criterion;
121
+ drawGenericPipes(svg, pos, {
122
+ isDark: false,
123
+ color: '#b45309',
124
+ isMuted: false,
125
+ relations: [rel],
126
+ locale: 'eng',
127
+ });
128
+ /* Pipe + threads + junction + diamond still render, but zero labels. */
129
+ expect(svg.querySelectorAll('text.pipe-label').length).toBe(0);
130
+ expect(svg.querySelectorAll('line.pipe-seg').length).toBe(4);
131
+ expect(svg.querySelectorAll('rect.pipe-junction').length).toBe(1);
117
132
  });
118
133
 
119
134
  it('renders a diamond at the comprehensive end (origin marker)', () => {
@@ -20,10 +20,13 @@
20
20
  * the rake code with isDelimiting=true on all members, which made
21
21
  * the children THICKER than the parent — backwards.)
22
22
  *
23
- * 3. Per-member delimiting characteristic text is rendered along each
24
- * thread (ISO 704 §5.5.4.2.1 — the intension difference that
25
- * distinguishes each coordinate concept). The optional group-level
26
- * `criterion` is shown at the middle node.
23
+ * 3. Characteristic label — the criterion is a single hyperedge-level
24
+ * field shared by all members (the dimension along which they
25
+ * differ), rendered once on the pipe body. Optional: when the
26
+ * criterion is absent, no label is shown at all. Per-member
27
+ * delimitingCharacteristic is NOT labeled here — those values
28
+ * distinguish each member but are not "the characteristic of
29
+ * the hyperedge".
27
30
  *
28
31
  * Pure DOM/geometry — no Vue reactivity, no d3 state. The caller passes
29
32
  * the generic relations, position map, and rendering context.
@@ -58,8 +61,8 @@ interface NodePos { x: number; y: number }
58
61
  *
59
62
  * Each relation produces: 1 thick pipe (comp → middle), N thin threads
60
63
  * (middle → each member), 1 junction square at the middle node, 1
61
- * diamond at the comp end, optional criterion label at the middle,
62
- * optional delimiting-characteristic label per thread.
64
+ * diamond at the comp end, and — only when the hyperedge has a
65
+ * criterion — 1 characteristic label on the pipe body.
63
66
  */
64
67
  export function drawGenericPipes(
65
68
  svg: SVGSVGElement,
@@ -89,23 +92,15 @@ export function drawGenericPipes(
89
92
  const layout = computePipeLayout(compPos, memberPositions);
90
93
  if (!layout) continue;
91
94
 
92
- /* 1. Thick parent pipe: comp → middle node */
95
+ /* 1. Thick parent pipe: comp → middle node. The characteristic
96
+ label (criterion) sits on the pipe body — see step 4. */
93
97
  drawSegment(svg, opts.color, layout.pipeStart, layout.pipeEnd, PIPE_WIDTH);
94
98
 
95
- /* 2. Thin threads: middle → each member, with characteristic label */
99
+ /* 2. Thin threads: middle → each member. No per-thread labels —
100
+ the characteristic is a single hyperedge-level field shared
101
+ by all members, rendered once on the pipe body. */
96
102
  for (const thread of layout.threads) {
97
103
  drawSegment(svg, opts.color, thread.start, thread.end, THREAD_WIDTH);
98
- const dcText = pickLocalized(thread.member.delimitingCharacteristic, opts.locale);
99
- if (dcText) {
100
- const midX = (thread.start.x + thread.end.x) / 2;
101
- const midY = (thread.start.y + thread.end.y) / 2;
102
- drawLabel(svg, midX, midY, dcText, opts.color, {
103
- fontSize: 8.5,
104
- italic: true,
105
- bg: true,
106
- isDark: opts.isDark,
107
- });
108
- }
109
104
  }
110
105
 
111
106
  /* 3. Square junction at the middle node (distinct from the rake's
@@ -113,7 +108,9 @@ export function drawGenericPipes(
113
108
  circle = partitive). */
114
109
  drawSquare(svg, layout.middleNode.x, layout.middleNode.y, JUNCTION_RADIUS, opts.color, contrastStroke);
115
110
 
116
- /* 4. Group-level criterion label, if present */
111
+ /* 4. Characteristic label — single, hyperedge-level, optional.
112
+ Drawn on the pipe body (midpoint between comp and middle node).
113
+ When criterion is absent, no label is rendered at all. */
117
114
  const critText = pickLocalized(rel.criterion, opts.locale);
118
115
  if (critText) {
119
116
  const midX = (layout.pipeStart.x + layout.pipeEnd.x) / 2;