@glossarist/concept-browser 0.7.53 → 0.7.54

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.53",
3
+ "version": "0.7.54",
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": {
@@ -283,132 +283,128 @@ const activeSectionId = computed(() => {
283
283
 
284
284
  <!-- Group entries -->
285
285
  <div v-if="isGroupExpanded(group.id)" class="space-y-1" :class="group.label ? 'ml-1' : ''">
286
- <!-- LINEAGE series: timeline-style entries -->
287
- <template v-if="group.kind === 'lineage'">
288
- <div class="series-timeline">
289
- <button
290
- v-for="ds in group.entries"
291
- :key="ds.id"
292
- @click="goToDataset(ds.id)"
293
- class="series-entry w-full text-left flex items-center gap-2 pl-6 pr-3 py-1.5 rounded-md text-sm border-l-2 transition-all duration-150"
294
- :class="currentDataset === ds.id
295
- ? 'bg-amber-50/70 dark:bg-amber-400/10 border-l-[3px] text-ink-900 dark:text-ink-50 font-semibold'
296
- : 'border-transparent text-ink-600 dark:text-ink-300 hover:bg-ink-50 dark:hover:bg-ink-700/40 hover:text-ink-900 dark:hover:text-ink-50'"
297
- :style="currentDataset === ds.id ? { borderLeftColor: 'var(--gold-accent, #B8935A)' } : {}"
298
- >
299
- <span class="flex-1 truncate text-[13.5px] font-medium leading-snug">{{ ds.ref || ds.title || ds.id }}</span>
300
- <span
301
- v-if="ds.status && ds.status !== 'valid'"
302
- class="text-[9px] uppercase tracking-wide italic text-ink-400 dark:text-ink-400"
303
- >{{ ds.status }}</span>
304
- <span
305
- v-if="ds.isCurrent"
306
- class="current-star flex-shrink-0"
307
- title="Current edition"
308
- >✦</span>
309
- </button>
310
- </div>
311
- </template>
312
-
313
- <!-- REGULAR group: original entry style with expansion -->
314
- <template v-else>
315
- <div
316
- v-for="ds in group.entries"
317
- :key="ds.id"
318
- class="rounded-lg transition-all duration-150"
319
- :class="currentDataset === ds.id ? 'bg-surface' : ''"
286
+ <!-- Per-entry rendering: button is kind-specific, expansion is shared (DRY/MECE) -->
287
+ <div
288
+ v-for="ds in group.entries"
289
+ :key="ds.id"
290
+ class="rounded-lg transition-all duration-150"
291
+ :class="currentDataset === ds.id ? 'bg-surface' : ''"
292
+ >
293
+ <!-- LINEAGE: compact timeline button -->
294
+ <button
295
+ v-if="group.kind === 'lineage'"
296
+ @click="goToDataset(ds.id)"
297
+ class="series-entry w-full text-left flex items-center gap-2 pl-6 pr-3 py-1.5 rounded-md text-sm border-l-2 transition-all duration-150"
298
+ :class="currentDataset === ds.id
299
+ ? 'bg-amber-50/70 dark:bg-amber-400/10 border-l-[3px] text-ink-900 dark:text-ink-50 font-semibold'
300
+ : 'border-transparent text-ink-600 dark:text-ink-300 hover:bg-ink-50 dark:hover:bg-ink-700/40 hover:text-ink-900 dark:hover:text-ink-50'"
301
+ :style="currentDataset === ds.id ? { borderLeftColor: 'var(--gold-accent, #B8935A)' } : {}"
320
302
  >
321
- <button
322
- @click="goToDataset(ds.id)"
323
- class="w-full text-left px-3 py-2 rounded-lg text-sm border-l-2"
324
- :class="[
325
- currentDataset === ds.id
326
- ? 'text-ink-800 dark:text-ink-50'
327
- : 'border-transparent text-ink-600 dark:text-ink-300 hover:bg-ink-50 dark:hover:bg-ink-700 hover:text-ink-800 dark:hover:text-ink-50'
328
- ]"
329
- :style="currentDataset === ds.id ? { borderLeftColor: getColor(ds.id), borderLeftWidth: '2px' } : {}"
330
- >
331
- <div class="font-medium truncate leading-snug">{{ localizedDatasetField(ds.id, 'title', ds.title) }}</div>
332
- <div v-if="ds.loaded" class="text-xs mt-0.5" :class="currentDataset === ds.id ? 'text-ink-400 dark:text-ink-300' : 'text-ink-300 dark:text-ink-400'">
333
- {{ ds.conceptCount.toLocaleString() }} {{ t('home.concepts').toLowerCase() }}
334
- </div>
335
- </button>
303
+ <span class="flex-1 truncate text-[13.5px] font-medium leading-snug">{{ ds.ref || ds.title || ds.id }}</span>
304
+ <span
305
+ v-if="ds.status && ds.status !== 'valid'"
306
+ class="text-[9px] uppercase tracking-wide italic text-ink-400 dark:text-ink-400"
307
+ >{{ ds.status }}</span>
308
+ <span
309
+ v-if="ds.isCurrent"
310
+ class="current-star flex-shrink-0"
311
+ title="Current edition"
312
+ >✦</span>
313
+ </button>
336
314
 
337
- <!-- Expanded dataset: sub-pages + sections + provenance -->
338
- <div v-if="currentDataset === ds.id && (filteredDatasetPages.length || provenance.owner)" class="px-2 pb-2">
339
- <nav v-if="filteredDatasetPages.length" class="space-y-0.5 mt-1">
340
- <router-link
341
- v-for="page in filteredDatasetPages"
342
- :key="page.route || 'concepts'"
343
- :to="pageRoute(page)"
344
- class="btn-ghost w-full text-left flex items-center gap-2 text-sm"
345
- :class="isActive(page) ? 'active' : ''"
346
- @click="closeMobile"
347
- >
348
- <NavIcon :name="page.icon" />
349
- {{ navTitle(page) }}
350
- </router-link>
351
- </nav>
352
-
353
- <!-- Sections tree -->
354
- <div v-if="getDatasetSections(ds.id).length" class="mt-2 pt-2 border-t border-ink-100/60">
355
- <button @click="toggleSectionNode(ds.id + '-sections')"
356
- class="w-full flex items-center gap-1.5 px-2 py-1 rounded-lg text-[10px] uppercase tracking-wide text-ink-400 hover:text-ink-600 hover:bg-ink-50 transition-colors"
315
+ <!-- DEFAULT/OTHER: standard entry button -->
316
+ <button
317
+ v-else
318
+ @click="goToDataset(ds.id)"
319
+ class="w-full text-left px-3 py-2 rounded-lg text-sm border-l-2"
320
+ :class="[
321
+ currentDataset === ds.id
322
+ ? 'text-ink-800 dark:text-ink-50'
323
+ : 'border-transparent text-ink-600 dark:text-ink-300 hover:bg-ink-50 dark:hover:bg-ink-700 hover:text-ink-800 dark:hover:text-ink-50'
324
+ ]"
325
+ :style="currentDataset === ds.id ? { borderLeftColor: getColor(ds.id), borderLeftWidth: '2px' } : {}"
326
+ >
327
+ <div class="font-medium truncate leading-snug">{{ localizedDatasetField(ds.id, 'title', ds.title) }}</div>
328
+ <div v-if="ds.loaded" class="text-xs mt-0.5" :class="currentDataset === ds.id ? 'text-ink-400 dark:text-ink-300' : 'text-ink-300 dark:text-ink-400'">
329
+ {{ ds.conceptCount.toLocaleString() }} {{ t('home.concepts').toLowerCase() }}
330
+ </div>
331
+ </button>
332
+
333
+ <!-- SHARED expansion content: sub-pages + sections + provenance.
334
+ Appears for the active dataset in ALL group kinds (DRY). -->
335
+ <div v-if="currentDataset === ds.id && (filteredDatasetPages.length || provenance.owner)" class="px-2 pb-2">
336
+ <nav v-if="filteredDatasetPages.length" class="space-y-0.5 mt-1">
337
+ <router-link
338
+ v-for="page in filteredDatasetPages"
339
+ :key="page.route || 'concepts'"
340
+ :to="pageRoute(page)"
341
+ class="btn-ghost w-full text-left flex items-center gap-2 text-sm"
342
+ :class="isActive(page) ? 'active' : ''"
343
+ @click="closeMobile"
344
+ >
345
+ <NavIcon :name="page.icon" />
346
+ {{ navTitle(page) }}
347
+ </router-link>
348
+ </nav>
349
+
350
+ <!-- Sections tree -->
351
+ <div v-if="getDatasetSections(ds.id).length" class="mt-2 pt-2 border-t border-ink-100/60">
352
+ <button @click="toggleSectionNode(ds.id + '-sections')"
353
+ class="w-full flex items-center gap-1.5 px-2 py-1 rounded-lg text-[10px] uppercase tracking-wide text-ink-400 hover:text-ink-600 hover:bg-ink-50 transition-colors"
354
+ >
355
+ <span class="w-3 text-[10px]">{{ expandedSectionNodes.has(ds.id + '-sections') ? '▾' : '▸' }}</span>
356
+ <span class="flex-1 text-left">{{ t('nav.sections') }}</span>
357
+ <span class="badge text-[9px] bg-amber-50 text-amber-600 px-1 py-0.5">{{ getDatasetSections(ds.id).length }}</span>
358
+ </button>
359
+ <div v-if="expandedSectionNodes.has(ds.id + '-sections')" class="mt-0.5 max-h-64 overflow-y-auto">
360
+ <button
361
+ @click="clearSectionFilter()"
362
+ class="w-full flex items-center gap-1.5 px-2 py-1 rounded-lg text-[11px] transition-colors"
363
+ :class="!activeSectionId ? 'bg-ink-800/8 text-blue-700 font-medium' : 'text-ink-500 hover:bg-ink-50'"
357
364
  >
358
- <span class="w-3 text-[10px]">{{ expandedSectionNodes.has(ds.id + '-sections') ? '▾' : '▸' }}</span>
359
- <span class="flex-1 text-left">{{ t('nav.sections') }}</span>
360
- <span class="badge text-[9px] bg-amber-50 text-amber-600 px-1 py-0.5">{{ getDatasetSections(ds.id).length }}</span>
365
+ <span class="w-3 text-ink-200">&#183;</span>
366
+ <span class="flex-1 text-left">{{ t('dataset.all') }}</span>
361
367
  </button>
362
- <div v-if="expandedSectionNodes.has(ds.id + '-sections')" class="mt-0.5 max-h-64 overflow-y-auto">
363
- <button
364
- @click="clearSectionFilter()"
368
+ <template v-for="section in getDatasetSections(ds.id)" :key="section.id">
369
+ <button @click="goToSection(ds.id, 'section-' + section.id)"
365
370
  class="w-full flex items-center gap-1.5 px-2 py-1 rounded-lg text-[11px] transition-colors"
366
- :class="!activeSectionId ? 'bg-ink-800/8 text-blue-700 font-medium' : 'text-ink-500 hover:bg-ink-50'"
371
+ :class="activeSectionId === 'section-' + section.id ? 'bg-ink-800/8 text-blue-700 font-medium' : 'text-ink-500 hover:bg-ink-50'"
367
372
  >
368
- <span class="w-3 text-ink-200">&#183;</span>
369
- <span class="flex-1 text-left">{{ t('dataset.all') }}</span>
373
+ <span v-if="section.children?.length" class="text-[10px] text-ink-300 w-3 cursor-pointer" @click.stop="toggleSectionNode(ds.id + '-s-' + section.id)">{{ expandedSectionNodes.has(ds.id + '-s-' + section.id) ? '▾' : '▸' }}</span>
374
+ <span v-else class="w-3 text-ink-200">&#183;</span>
375
+ <span class="flex-1 text-left truncate">{{ sectionDisplay(section) }}</span>
370
376
  </button>
371
- <template v-for="section in getDatasetSections(ds.id)" :key="section.id">
372
- <button @click="goToSection(ds.id, 'section-' + section.id)"
377
+ <div v-if="section.children?.length && expandedSectionNodes.has(ds.id + '-s-' + section.id)" class="ml-3">
378
+ <button v-for="child in section.children" :key="child.id"
379
+ @click="goToSection(ds.id, 'section-' + child.id)"
373
380
  class="w-full flex items-center gap-1.5 px-2 py-1 rounded-lg text-[11px] transition-colors"
374
- :class="activeSectionId === 'section-' + section.id ? 'bg-ink-800/8 text-blue-700 font-medium' : 'text-ink-500 hover:bg-ink-50'"
381
+ :class="activeSectionId === 'section-' + child.id ? 'bg-ink-800/8 text-blue-700 font-medium' : 'text-ink-400 hover:bg-ink-50'"
375
382
  >
376
- <span v-if="section.children?.length" class="text-[10px] text-ink-300 w-3 cursor-pointer" @click.stop="toggleSectionNode(ds.id + '-s-' + section.id)">{{ expandedSectionNodes.has(ds.id + '-s-' + section.id) ? '▾' : '▸' }}</span>
377
- <span v-else class="w-3 text-ink-200">&#183;</span>
378
- <span class="flex-1 text-left truncate">{{ sectionDisplay(section) }}</span>
383
+ <span class="w-3 text-ink-200">&#183;</span>
384
+ <span class="flex-1 text-left truncate">{{ sectionDisplay(child) }}</span>
379
385
  </button>
380
- <div v-if="section.children?.length && expandedSectionNodes.has(ds.id + '-s-' + section.id)" class="ml-3">
381
- <button v-for="child in section.children" :key="child.id"
382
- @click="goToSection(ds.id, 'section-' + child.id)"
383
- class="w-full flex items-center gap-1.5 px-2 py-1 rounded-lg text-[11px] transition-colors"
384
- :class="activeSectionId === 'section-' + child.id ? 'bg-ink-800/8 text-blue-700 font-medium' : 'text-ink-400 hover:bg-ink-50'"
385
- >
386
- <span class="w-3 text-ink-200">&#183;</span>
387
- <span class="flex-1 text-left truncate">{{ sectionDisplay(child) }}</span>
388
- </button>
389
- </div>
390
- </template>
391
- </div>
386
+ </div>
387
+ </template>
392
388
  </div>
389
+ </div>
393
390
 
394
- <div v-if="provenance.owner" class="mt-3 pt-3 border-t border-ink-100/60">
395
- <div class="text-[11px] text-ink-300 space-y-1.5 px-1">
396
- <div v-if="provenance.ref" class="text-xs font-semibold text-ink-700">
397
- {{ provenance.ref }}
398
- </div>
399
- <div class="flex items-center gap-1">
400
- <span class="text-ink-400">{{ t('sidebar.publishedBy') }}</span>
401
- <a v-if="provenance.ownerUrl" :href="provenance.ownerUrl" target="_blank" rel="noopener" class="concept-link font-medium">{{ provenance.owner }}</a>
402
- <span v-else class="text-ink-600 font-medium">{{ provenance.owner }}</span>
403
- </div>
404
- <div v-if="provenance.sourceRepo">
405
- <a :href="provenance.sourceRepo" target="_blank" rel="noopener" class="concept-link">{{ t('sidebar.viewSource') }}</a>
406
- </div>
391
+ <div v-if="provenance.owner" class="mt-3 pt-3 border-t border-ink-100/60">
392
+ <div class="text-[11px] text-ink-300 space-y-1.5 px-1">
393
+ <div v-if="provenance.ref" class="text-xs font-semibold text-ink-700">
394
+ {{ provenance.ref }}
395
+ </div>
396
+ <div class="flex items-center gap-1">
397
+ <span class="text-ink-400">{{ t('sidebar.publishedBy') }}</span>
398
+ <a v-if="provenance.ownerUrl" :href="provenance.ownerUrl" target="_blank" rel="noopener" class="concept-link font-medium">{{ provenance.owner }}</a>
399
+ <span v-else class="text-ink-600 font-medium">{{ provenance.owner }}</span>
400
+ </div>
401
+ <div v-if="provenance.sourceRepo">
402
+ <a :href="provenance.sourceRepo" target="_blank" rel="noopener" class="concept-link">{{ t('sidebar.viewSource') }}</a>
407
403
  </div>
408
404
  </div>
409
405
  </div>
410
406
  </div>
411
- </template>
407
+ </div>
412
408
  </div>
413
409
  </div>
414
410
  </template>