@glossarist/concept-browser 0.2.9 → 0.2.11

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.2.9",
3
+ "version": "0.2.11",
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": {
@@ -364,6 +364,10 @@ function selectOnly(registerId: string) {
364
364
  }
365
365
  }
366
366
 
367
+ function registerTitle(id: string): string {
368
+ return props.registers.find(r => r.id === id)?.title ?? id;
369
+ }
370
+
367
371
  function selectedNodeColor(): string {
368
372
  if (!selectedNode.value) return STUB_COLOR;
369
373
  if (!selectedNode.value.loaded) return STUB_COLOR;
@@ -458,15 +462,15 @@ function selectedNodeColor(): string {
458
462
  </div>
459
463
  </div>
460
464
 
461
- <!-- Legend -->
462
- <div v-if="nodeCount > 0" class="absolute top-4 right-4 z-10 bg-surface-raised/90 backdrop-blur rounded-lg px-3 py-2.5 border border-ink-100/60 text-xs" style="box-shadow: 0 2px 6px rgba(26, 27, 46, 0.04);">
465
+ <!-- Legend (only when multiple registers) -->
466
+ <div v-if="nodeCount > 0 && registers.length > 1" class="absolute top-4 right-4 z-10 bg-surface-raised/90 backdrop-blur rounded-lg px-3 py-2.5 border border-ink-100/60 text-xs" style="box-shadow: 0 2px 6px rgba(26, 27, 46, 0.04);">
463
467
  <div class="font-semibold text-ink-400 text-[10px] uppercase tracking-wide mb-2">Datasets</div>
464
468
  <div v-for="reg in registers" :key="reg.id" class="flex items-center gap-2 mb-1.5 last:mb-0">
465
469
  <span
466
470
  class="w-2.5 h-2.5 rounded-full inline-block flex-shrink-0"
467
471
  :style="{ backgroundColor: registerColor(reg.id) }"
468
472
  ></span>
469
- <span class="text-ink-500">{{ reg.id }}</span>
473
+ <span class="text-ink-500">{{ reg.title }}</span>
470
474
  </div>
471
475
  <div class="flex items-center gap-2 mt-2 pt-2 border-t border-ink-100/40">
472
476
  <span class="w-2 h-2 rounded-full inline-block" :style="{ backgroundColor: STUB_COLOR }"></span>
@@ -495,7 +499,7 @@ function selectedNodeColor(): string {
495
499
  :style="{ backgroundColor: selectedNodeColor() }"
496
500
  ></span>
497
501
  <span class="text-[10px] text-ink-400 uppercase tracking-wide">
498
- {{ selectedNode.register || 'unknown' }} &middot;
502
+ {{ registerTitle(selectedNode.register) }} &middot;
499
503
  {{ selectedNode.loaded ? 'loaded' : 'stub' }}
500
504
  </span>
501
505
  </div>
package/src/utils/math.ts CHANGED
@@ -45,6 +45,59 @@ function convertLists(text: string): string {
45
45
  return result;
46
46
  }
47
47
 
48
+ /**
49
+ * Replace `prefix:[content]` where content may contain nested brackets.
50
+ * Handles `*prefix:[content]*` (bold) too.
51
+ */
52
+ function replaceBracketed(
53
+ text: string,
54
+ prefix: string,
55
+ render: (math: string, bold: boolean) => string,
56
+ ): string {
57
+ let result = '';
58
+ let i = 0;
59
+ const boldPrefix = '*' + prefix;
60
+ while (i < text.length) {
61
+ // Check for bold variant: *prefix:[...]
62
+ if (text.startsWith(boldPrefix + '[', i)) {
63
+ const start = i;
64
+ i += boldPrefix.length + 1; // skip *prefix:[
65
+ const depth = 1;
66
+ let j = i;
67
+ let d = 1;
68
+ while (j < text.length && d > 0) {
69
+ if (text[j] === '[') d++;
70
+ else if (text[j] === ']') d--;
71
+ j++;
72
+ }
73
+ const content = text.slice(i, j - 1);
74
+ // Check for closing *
75
+ let end = j;
76
+ if (end < text.length && text[end] === '*') end++;
77
+ result += render(content, true);
78
+ i = end;
79
+ }
80
+ // Check for normal variant: prefix:[...]
81
+ else if (text.startsWith(prefix + '[', i)) {
82
+ i += prefix.length + 1;
83
+ let j = i;
84
+ let d = 1;
85
+ while (j < text.length && d > 0) {
86
+ if (text[j] === '[') d++;
87
+ else if (text[j] === ']') d--;
88
+ j++;
89
+ }
90
+ const content = text.slice(i, j - 1);
91
+ result += render(content, false);
92
+ i = j;
93
+ } else {
94
+ result += text[i];
95
+ i++;
96
+ }
97
+ }
98
+ return result;
99
+ }
100
+
48
101
  /**
49
102
  * Render stem:[...] math notation to KaTeX HTML.
50
103
  * Also handles cross-reference inline patterns (URN refs, bibliography, figures).
@@ -57,13 +110,8 @@ export function renderMath(text: string, xrefResolverOrOpts?: XrefResolver | Ren
57
110
  ? { xrefResolver: xrefResolverOrOpts }
58
111
  : (xrefResolverOrOpts ?? {});
59
112
 
60
- result = result.replace(/\*stem:\[([^\]]*)\]\*/g, (_, math) => {
61
- return renderKatexSpan(math, true);
62
- });
63
-
64
- result = result.replace(/stem:\[([^\]]*)\]/g, (_, math) => {
65
- return renderKatexSpan(math, false);
66
- });
113
+ result = replaceBracketed(result, 'stem:', renderKatexSpan);
114
+ result = replaceBracketed(result, 'latexmath:', renderKatexSpan);
67
115
 
68
116
  result = convertLists(result);
69
117
 
@@ -135,9 +183,7 @@ function escapeHtml(text: string): string {
135
183
  */
136
184
  export function cleanContent(text: string): string {
137
185
  if (!text) return '';
138
- return text
139
- .replace(/\*stem:\[([^\]]*)\]\*/g, '$1')
140
- .replace(/stem:\[([^\]]*)\]/g, '$1')
186
+ let result = text
141
187
  .replace(/\*([^*]+)\*/g, '$1')
142
188
  .replace(/~([^~]+)~/g, '_$1')
143
189
  .replace(/\n[ \t]*\* /g, '; ')
@@ -146,4 +192,7 @@ export function cleanContent(text: string): string {
146
192
  .replace(/\{\{urn:[^,}]+,([^,}]+)(?:,[^}]+)?\}\}/g, '$1')
147
193
  .replace(/\{urn:[^,}]+,([^,}]+)(?:,[^}]+)?\}/g, '$1')
148
194
  .replace(/\{\{([^,}]+)(?:,\s*[^}]+)?\}\}/g, '$1');
195
+ result = replaceBracketed(result, 'stem:', (math) => math);
196
+ result = replaceBracketed(result, 'latexmath:', (math) => math);
197
+ return result;
149
198
  }