@glossarist/concept-browser 0.7.51 → 0.7.53

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.
Files changed (159) hide show
  1. package/cli/index.mjs +32 -0
  2. package/env.d.ts +15 -0
  3. package/package.json +12 -2
  4. package/scripts/__tests__/doctor.test.mjs +147 -0
  5. package/scripts/doctor.mjs +327 -0
  6. package/scripts/generate-data.mjs +136 -0
  7. package/scripts/generate-ontology-data.mjs +3 -3
  8. package/scripts/generate-ontology-schema.mjs +3 -3
  9. package/scripts/lib/agents-turtle.mjs +64 -0
  10. package/scripts/lib/bibliography-turtle.mjs +54 -0
  11. package/scripts/lib/build-activity-turtle.mjs +92 -0
  12. package/scripts/lib/build-cache.mjs +70 -0
  13. package/scripts/lib/dataset-turtle.mjs +79 -0
  14. package/scripts/lib/turtle-escape.mjs +0 -0
  15. package/scripts/lib/version-turtle.mjs +56 -0
  16. package/scripts/lib/vocab-turtle.mjs +64 -0
  17. package/scripts/normalize-yaml.mjs +99 -0
  18. package/scripts/sync-concept-model.mjs +86 -0
  19. package/scripts/validate-shacl.mjs +194 -0
  20. package/src/App.vue +2 -0
  21. package/src/__fixtures__/concept-shape.ttl +20 -0
  22. package/src/__fixtures__/shacl/bad/concept.ttl +7 -0
  23. package/src/__fixtures__/shacl/empty/.gitkeep +0 -0
  24. package/src/__fixtures__/shacl/good/concept.ttl +8 -0
  25. package/src/__tests__/__fixtures__/concepts.ts +221 -0
  26. package/src/__tests__/adapters/concept-identity.test.ts +76 -0
  27. package/src/__tests__/components/error-boundary.test.ts +109 -0
  28. package/src/__tests__/composables/use-dataset-series.test.ts +262 -0
  29. package/src/__tests__/concept-rdf/agents-emitter.test.ts +110 -0
  30. package/src/__tests__/concept-rdf/bibliography-emitter.test.ts +159 -0
  31. package/src/__tests__/concept-rdf/build-activity-emitter.test.ts +119 -0
  32. package/src/__tests__/concept-rdf/coerce-date.test.ts +97 -0
  33. package/src/__tests__/concept-rdf/concept-emitter.test.ts +258 -0
  34. package/src/__tests__/concept-rdf/dataset-emitter.test.ts +224 -0
  35. package/src/__tests__/concept-rdf/differential.test.ts +96 -0
  36. package/src/__tests__/concept-rdf/group-emitter.test.ts +109 -0
  37. package/src/__tests__/concept-rdf/image-variant-emitter.test.ts +135 -0
  38. package/src/__tests__/concept-rdf/jsonld-writer.test.ts +109 -0
  39. package/src/__tests__/concept-rdf/nonverbal-rep.test.ts +177 -0
  40. package/src/__tests__/concept-rdf/property-based.test.ts +179 -0
  41. package/src/__tests__/concept-rdf/provenance.test.ts +110 -0
  42. package/src/__tests__/concept-rdf/quad-isomorphism.test.ts +43 -0
  43. package/src/__tests__/concept-rdf/quad-isomorphism.ts +47 -0
  44. package/src/__tests__/concept-rdf/rdf-components.test.ts +145 -0
  45. package/src/__tests__/concept-rdf/rdf-graph.test.ts +115 -0
  46. package/src/__tests__/concept-rdf/round-trip.test.ts +243 -0
  47. package/src/__tests__/concept-rdf/scoped-examples.test.ts +126 -0
  48. package/src/__tests__/concept-rdf/sections-builder.test.ts +94 -0
  49. package/src/__tests__/concept-rdf/shacl-conformance.test.ts +110 -0
  50. package/src/__tests__/concept-rdf/shape-consistency.test.ts +138 -0
  51. package/src/__tests__/concept-rdf/snapshot-generation.test.ts +75 -0
  52. package/src/__tests__/concept-rdf/table-formula-emitter.test.ts +142 -0
  53. package/src/__tests__/concept-rdf/turtle-writer.test.ts +114 -0
  54. package/src/__tests__/concept-rdf/use-rdf-document.test.ts +246 -0
  55. package/src/__tests__/concept-rdf/version-emitter.test.ts +120 -0
  56. package/src/__tests__/concept-rdf/vocabulary-ssot.test.ts +100 -0
  57. package/src/__tests__/concept-rdf-view.test.ts +136 -0
  58. package/src/__tests__/config/group-renderers.test.ts +35 -0
  59. package/src/__tests__/config/group-types.test.ts +76 -0
  60. package/src/__tests__/dataset-style.test.ts +12 -7
  61. package/src/__tests__/errors/errors.test.ts +142 -0
  62. package/src/__tests__/format-downloads.test.ts +47 -65
  63. package/src/__tests__/markdown-lite.test.ts +19 -0
  64. package/src/__tests__/perf/bundle-layout.test.ts +50 -0
  65. package/src/__tests__/perf/serialization-perf.test.ts +121 -0
  66. package/src/__tests__/scripts/agents-turtle.test.ts +61 -0
  67. package/src/__tests__/scripts/bibliography-turtle.test.ts +59 -0
  68. package/src/__tests__/scripts/build-activity-turtle.test.ts +75 -0
  69. package/src/__tests__/scripts/build-cache.test.ts +78 -0
  70. package/src/__tests__/scripts/build-integration.test.ts +134 -0
  71. package/src/__tests__/scripts/dataset-turtle.test.ts +94 -0
  72. package/src/__tests__/scripts/normalize-yaml.test.ts +98 -0
  73. package/src/__tests__/scripts/stryker-config.test.ts +33 -0
  74. package/src/__tests__/scripts/turtle-escape.test.ts +63 -0
  75. package/src/__tests__/scripts/version-turtle.test.ts +72 -0
  76. package/src/__tests__/scripts/vocab-turtle.test.ts +63 -0
  77. package/src/__tests__/use-format-registry.test.ts +125 -0
  78. package/src/__tests__/utils/bcp47.test.ts +166 -0
  79. package/src/__tests__/utils/color-theme.test.ts +143 -0
  80. package/src/__tests__/utils/url-safety.test.ts +65 -0
  81. package/src/__tests__/validate-shacl.test.ts +100 -0
  82. package/src/adapters/DatasetAdapter.ts +11 -5
  83. package/src/adapters/GraphDataSource.ts +2 -1
  84. package/src/adapters/UriRouter.ts +2 -1
  85. package/src/adapters/concept-identity.ts +69 -0
  86. package/src/adapters/factory.ts +3 -2
  87. package/src/adapters/model-bridge.ts +2 -1
  88. package/src/adapters/non-verbal/glossarist-augment.d.ts +7 -0
  89. package/src/adapters/non-verbal-resolver.ts +2 -1
  90. package/src/components/AppSidebar.vue +189 -93
  91. package/src/components/ConceptDetail.vue +8 -0
  92. package/src/components/ConceptEditionRail.vue +222 -0
  93. package/src/components/ConceptRdfView.vue +37 -377
  94. package/src/components/DatasetSeriesCard.vue +270 -0
  95. package/src/components/ErrorBoundary.vue +95 -0
  96. package/src/components/FormatDownloads.vue +17 -13
  97. package/src/components/HomeSeriesSection.vue +277 -0
  98. package/src/components/RelationSphere.vue +1672 -0
  99. package/src/components/SidebarSeriesSection.vue +239 -0
  100. package/src/components/concept-rdf/RdfInstanceHeader.vue +47 -0
  101. package/src/components/concept-rdf/RdfInstanceSection.vue +54 -0
  102. package/src/components/concept-rdf/RdfPrefixLegend.vue +27 -0
  103. package/src/components/concept-rdf/RdfSourcePanel.vue +72 -0
  104. package/src/components/concept-rdf/agents-emitter.ts +82 -0
  105. package/src/components/concept-rdf/bibliography-emitter.ts +83 -0
  106. package/src/components/concept-rdf/build-activity-emitter.ts +89 -0
  107. package/src/components/concept-rdf/concept-emitter.ts +443 -0
  108. package/src/components/concept-rdf/dataset-emitter.ts +95 -0
  109. package/src/components/concept-rdf/group-emitter.ts +69 -0
  110. package/src/components/concept-rdf/image-variant-emitter.ts +46 -0
  111. package/src/components/concept-rdf/jsonld-writer.ts +82 -0
  112. package/src/components/concept-rdf/predicates.ts +261 -0
  113. package/src/components/concept-rdf/provenance.ts +80 -0
  114. package/src/components/concept-rdf/rdf-graph.ts +211 -0
  115. package/src/components/concept-rdf/rdf-prefixes.ts +23 -0
  116. package/src/components/concept-rdf/sections-builder.ts +62 -0
  117. package/src/components/concept-rdf/table-formula-emitter.ts +101 -0
  118. package/src/components/concept-rdf/turtle-writer.ts +116 -0
  119. package/src/components/concept-rdf/use-rdf-document.ts +72 -0
  120. package/src/components/concept-rdf/version-emitter.ts +65 -0
  121. package/src/components/concept-rdf/vocabulary-emitter.ts +62 -0
  122. package/src/components/groups/DatasetGroupRenderer.vue +32 -0
  123. package/src/components/groups/DefaultGroupSidebar.vue +50 -0
  124. package/src/components/groups/LineageGroupSidebar.vue +75 -0
  125. package/src/composables/use-color-theme.ts +82 -0
  126. package/src/composables/use-format-registry.ts +42 -0
  127. package/src/composables/useDatasetSeries.ts +258 -0
  128. package/src/composables/useSphereProjection.ts +125 -0
  129. package/src/config/group-renderers.ts +27 -0
  130. package/src/config/group-types.ts +92 -0
  131. package/src/config/types.ts +81 -2
  132. package/src/config/use-site-config.ts +2 -1
  133. package/src/errors.ts +136 -0
  134. package/src/i18n/locales/eng.yml +24 -0
  135. package/src/i18n/locales/fra.yml +24 -0
  136. package/src/stores/vocabulary.ts +3 -1
  137. package/src/style.css +17 -2
  138. package/src/types/agents-version-turtle.d.ts +27 -0
  139. package/src/types/bibliography-turtle.d.ts +12 -0
  140. package/src/types/build-activity-turtle.d.ts +16 -0
  141. package/src/types/build-cache.d.ts +20 -0
  142. package/src/types/dataset-turtle.d.ts +32 -0
  143. package/src/types/normalize-yaml.d.ts +16 -0
  144. package/src/types/turtle-escape.d.ts +6 -0
  145. package/src/types/vocab-turtle.d.ts +13 -0
  146. package/src/utils/asciidoc-lite.ts +11 -6
  147. package/src/utils/bcp47.ts +141 -0
  148. package/src/utils/color-theme-integration.ts +11 -0
  149. package/src/utils/color-theme.ts +129 -0
  150. package/src/utils/dataset-style.ts +31 -6
  151. package/src/utils/locale.ts +6 -14
  152. package/src/utils/markdown-lite.ts +6 -1
  153. package/src/utils/relation-sphere-styling.ts +63 -0
  154. package/src/utils/relationship-categories.ts +30 -0
  155. package/src/utils/url-safety.ts +30 -0
  156. package/src/views/ConceptView.vue +187 -9
  157. package/src/views/DatasetView.vue +6 -0
  158. package/src/views/HomeView.vue +5 -0
  159. package/vite.config.ts +7 -0
@@ -2,7 +2,9 @@
2
2
  import { computed, watch, ref, onMounted, onUnmounted } from 'vue';
3
3
  import { useRouter } from 'vue-router';
4
4
  import { useVocabularyStore } from '../stores/vocabulary';
5
+ import { conceptUri } from '../adapters/model-bridge';
5
6
  import ConceptDetail from '../components/ConceptDetail.vue';
7
+ import RelationSphere from '../components/RelationSphere.vue';
6
8
  import ShortcutsModal from '../components/ShortcutsModal.vue';
7
9
  import { useI18n } from '../i18n';
8
10
 
@@ -50,6 +52,26 @@ const concept = computed(() => store.currentConcept);
50
52
  const manifest = computed(() => store.currentManifest);
51
53
  const edges = computed(() => store.conceptEdges);
52
54
  const adjacent = ref({ prev: null as string | null, next: null as string | null });
55
+ const viewMode = ref<'detail' | 'sphere'>(
56
+ router.currentRoute.value.query.view === 'sphere' ? 'sphere' : 'detail'
57
+ );
58
+
59
+ /* When the user clicks a card in the sphere, we store the navigation
60
+ payload here. The concept loads via store.viewConcept (without
61
+ router.push). When the user switches to Detail, we commit the URL. */
62
+ const sphereFocusPayload = ref<{ registerId: string; conceptId: string } | null>(null);
63
+ const permalinkCopied = ref(false);
64
+
65
+ async function copyPermalink() {
66
+ try {
67
+ await navigator.clipboard.writeText(window.location.href);
68
+ permalinkCopied.value = true;
69
+ setTimeout(() => { permalinkCopied.value = false; }, 1800);
70
+ } catch {
71
+ /* Clipboard API not available — fall back to URL prompt */
72
+ window.prompt('Copy this URL:', window.location.href);
73
+ }
74
+ }
53
75
 
54
76
  async function loadAdjacent() {
55
77
  const adapter = store.datasets.get(props.registerId);
@@ -66,6 +88,47 @@ function goAdjacent(id: string) {
66
88
  window.scrollTo({ top: 0, behavior: 'smooth' });
67
89
  }
68
90
 
91
+ function onSphereNavigate(payload: { registerId: string; conceptId: string }) {
92
+ if (!payload.registerId || !payload.conceptId) return;
93
+ /* DON'T router.push — that sets conceptLoading=true and unmounts the
94
+ sphere. Instead, load the concept directly via the store. This
95
+ updates store.currentConcept + store.conceptEdges, which flow as
96
+ props to RelationSphere without any loading flash. The sphere's
97
+ watch on props.concept fires → rebuilds the graph → animates. */
98
+ sphereFocusPayload.value = { registerId: payload.registerId, conceptId: payload.conceptId };
99
+ (async () => {
100
+ try {
101
+ const adapter = store.datasets.get(payload.registerId);
102
+ if (!adapter?.index) {
103
+ await store.loadDataset(payload.registerId);
104
+ }
105
+ await store.viewConcept(payload.registerId, payload.conceptId);
106
+ loadAdjacent();
107
+ } catch (e) {
108
+ console.warn('Sphere navigation failed:', e);
109
+ }
110
+ })();
111
+ }
112
+
113
+ function switchToSphere() {
114
+ viewMode.value = 'sphere';
115
+ sphereFocusPayload.value = null;
116
+ router.replace({ query: { ...router.currentRoute.value.query, view: 'sphere' } });
117
+ }
118
+
119
+ function switchToDetail() {
120
+ viewMode.value = 'detail';
121
+ router.replace({ query: { ...router.currentRoute.value.query, view: 'detail' } });
122
+ /* Commit the URL if the sphere navigated to a different concept.
123
+ This triggers loadConcept → the Detail view shows the right concept. */
124
+ if (sphereFocusPayload.value) {
125
+ const { registerId, conceptId } = sphereFocusPayload.value;
126
+ if (registerId !== props.registerId || conceptId !== props.conceptId) {
127
+ router.push({ name: 'concept', params: { registerId, conceptId } });
128
+ }
129
+ }
130
+ }
131
+
69
132
  function onKeydown(e: KeyboardEvent) {
70
133
  if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return;
71
134
 
@@ -78,6 +141,17 @@ function onKeydown(e: KeyboardEvent) {
78
141
  showShortcuts.value = false;
79
142
  return;
80
143
  }
144
+ /* View mode toggle: 's' for sphere, 'd' for detail */
145
+ if (e.key === 's' && concept.value) {
146
+ e.preventDefault();
147
+ switchToSphere();
148
+ return;
149
+ }
150
+ if (e.key === 'd') {
151
+ e.preventDefault();
152
+ switchToDetail();
153
+ return;
154
+ }
81
155
  if (e.key === 'j' && adjacent.value.prev) {
82
156
  e.preventDefault();
83
157
  goAdjacent(adjacent.value.prev);
@@ -92,7 +166,68 @@ onUnmounted(() => window.removeEventListener('keydown', onKeydown));
92
166
  </script>
93
167
 
94
168
  <template>
95
- <div class="px-4 sm:px-6 lg:px-8 py-8">
169
+ <div :class="['concept-view', { 'sphere-mode': viewMode === 'sphere' }]">
170
+ <!-- View mode toolbar — slim sub-header with segmented control + permalink.
171
+ Sits ABOVE the content (not floating, doesn't block anything). -->
172
+ <div
173
+ v-if="!conceptLoading && !localError && concept"
174
+ class="flex-shrink-0 w-full max-w-7xl mx-auto mb-4 flex items-center justify-between gap-4 pb-3 border-b border-ink-100 dark:border-ink-700"
175
+ >
176
+ <nav aria-label="View mode" class="inline-flex gap-1 p-1 rounded-lg bg-surface-alt dark:bg-ink-800" role="tablist">
177
+ <button
178
+ role="tab"
179
+ :aria-selected="viewMode === 'detail'"
180
+ class="inline-flex items-center gap-1.5 px-3 py-1.5 text-[13px] font-medium rounded-md cursor-pointer transition-all border-none font-inherit"
181
+ :class="viewMode === 'detail'
182
+ ? 'bg-surface-raised dark:bg-ink-600 text-ink-800 dark:text-ink-50 shadow-sm'
183
+ : 'bg-transparent text-ink-500 dark:text-ink-400 hover:text-ink-700 dark:hover:text-ink-200'"
184
+ @click="switchToDetail"
185
+ title="Detail view (d)"
186
+ >
187
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
188
+ <path d="M4 6h16M4 12h16M4 18h10" stroke-linecap="round"/>
189
+ </svg>
190
+ <span>{{ t('concept.detailView') }}</span>
191
+ <kbd class="ml-1 px-1 py-0.5 font-mono text-[9px] font-semibold rounded bg-ink-100 dark:bg-ink-700 text-ink-500 dark:text-ink-400 tracking-wide">d</kbd>
192
+ </button>
193
+ <button
194
+ role="tab"
195
+ :aria-selected="viewMode === 'sphere'"
196
+ class="inline-flex items-center gap-1.5 px-3 py-1.5 text-[13px] font-medium rounded-md cursor-pointer transition-all border-none font-inherit"
197
+ :class="viewMode === 'sphere'
198
+ ? 'bg-surface-raised dark:bg-ink-600 text-ink-800 dark:text-ink-50 shadow-sm'
199
+ : 'bg-transparent text-ink-500 dark:text-ink-400 hover:text-ink-700 dark:hover:text-ink-200'"
200
+ @click="switchToSphere"
201
+ title="Relation sphere view (s)"
202
+ >
203
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
204
+ <circle cx="12" cy="12" r="9"/>
205
+ <ellipse cx="12" cy="12" rx="9" ry="3.5"/>
206
+ <ellipse cx="12" cy="12" rx="3.5" ry="9"/>
207
+ </svg>
208
+ <span>{{ t('concept.relationSphere') }}</span>
209
+ <kbd class="ml-1 px-1 py-0.5 font-mono text-[9px] font-semibold rounded bg-ink-100 dark:bg-ink-700 text-ink-500 dark:text-ink-400 tracking-wide">s</kbd>
210
+ </button>
211
+ </nav>
212
+
213
+ <div class="flex items-center gap-2.5">
214
+ <button
215
+ class="inline-flex items-center gap-1.5 px-3 py-1.5 text-[13px] font-medium text-ink-500 dark:text-ink-400 bg-transparent border border-ink-100 dark:border-ink-700 rounded-md cursor-pointer transition-all font-inherit hover:text-ink-800 dark:hover:text-ink-100 hover:bg-surface-raised dark:hover:bg-ink-700 hover:border-ink-200 dark:hover:border-ink-600"
216
+ title="Copy permalink to this concept"
217
+ @click="copyPermalink"
218
+ >
219
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
220
+ <path stroke-linecap="round" stroke-linejoin="round" d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101"/>
221
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10.172 13.828a4 4 0 005.656 0l4-4a4 4 0 10-5.656-5.656l-1.1 1.1"/>
222
+ </svg>
223
+ <span>{{ t('concept.permalink') }}</span>
224
+ </button>
225
+ <Transition name="fade">
226
+ <span v-if="permalinkCopied" class="text-xs font-semibold text-green-600 dark:text-green-400">{{ t('concept.copied') }}</span>
227
+ </Transition>
228
+ </div>
229
+ </div>
230
+
96
231
  <div v-if="conceptLoading" class="max-w-5xl mx-auto py-8 space-y-5">
97
232
  <!-- Breadcrumb skeleton -->
98
233
  <div class="flex items-center gap-1.5">
@@ -141,15 +276,58 @@ onUnmounted(() => window.removeEventListener('keydown', onKeydown));
141
276
  </router-link>
142
277
  </div>
143
278
  </div>
144
- <ConceptDetail
145
- v-else-if="concept && manifest"
146
- :concept="concept"
147
- :manifest="manifest"
148
- :edges="edges"
149
- :adjacent="adjacent"
150
- :register-id="registerId"
151
- />
279
+ <template v-else-if="concept && manifest">
280
+ <div class="concept-content" :class="{ 'sphere-content': viewMode === 'sphere' }">
281
+ <!-- Sphere mode — receives concept + edges directly, no URI matching -->
282
+ <RelationSphere
283
+ v-if="viewMode === 'sphere'"
284
+ :concept="concept"
285
+ :manifest="manifest"
286
+ :register-id="registerId"
287
+ :edges="edges"
288
+ @navigate="onSphereNavigate"
289
+ />
290
+ <!-- Detail mode -->
291
+ <ConceptDetail
292
+ v-else
293
+ :concept="concept"
294
+ :manifest="manifest"
295
+ :edges="edges"
296
+ :adjacent="adjacent"
297
+ :register-id="registerId"
298
+ />
299
+ </div>
300
+ </template>
152
301
 
153
302
  <ShortcutsModal v-if="showShortcuts" @close="showShortcuts = false" />
154
303
  </div>
155
304
  </template>
305
+
306
+ <style scoped>
307
+ .concept-view {
308
+ display: flex;
309
+ flex-direction: column;
310
+ position: relative;
311
+ padding: 1rem;
312
+ min-height: calc(100vh - 56px);
313
+ }
314
+ .concept-view.sphere-mode {
315
+ height: calc(100vh - 56px);
316
+ overflow: hidden;
317
+ }
318
+ .concept-content {
319
+ flex: 1;
320
+ min-height: 0;
321
+ width: 100%;
322
+ max-width: 80rem;
323
+ margin: 0 auto;
324
+ }
325
+ .concept-content.sphere-content {
326
+ position: relative;
327
+ overflow: hidden;
328
+ display: flex;
329
+ flex-direction: column;
330
+ }
331
+ .fade-enter-active, .fade-leave-active { transition: opacity 0.2s; }
332
+ .fade-enter-from, .fade-leave-to { opacity: 0; }
333
+ </style>
@@ -7,6 +7,7 @@ import { useDatasetLoader } from '../composables/use-dataset-loader';
7
7
  import { FORMAT_LABELS } from '../config/types';
8
8
  import { langName, langLabel, sortLanguages } from '../utils/lang';
9
9
  import ConceptCard from '../components/ConceptCard.vue';
10
+ import DatasetSeriesCard from '../components/DatasetSeriesCard.vue';
10
11
  import { useI18n, locale } from '../i18n';
11
12
  import { useSiteConfig } from '../config/use-site-config';
12
13
  import type { SectionNode, ConceptSummary } from '../adapters/types';
@@ -321,6 +322,11 @@ function clearSection() {
321
322
  </div>
322
323
  </div>
323
324
 
325
+ <!-- Edition series sidebar (only renders if this dataset belongs to a multi-edition series) -->
326
+ <div v-if="manifest" class="mb-6">
327
+ <DatasetSeriesCard :register-id="registerId" />
328
+ </div>
329
+
324
330
  <!-- Downloads -->
325
331
  <div v-if="bulkDownloads.length" class="card p-4 mb-6">
326
332
  <h3 class="text-xs font-semibold text-ink-400 uppercase tracking-wide mb-3">{{ t('dataset.download') }}</h3>
@@ -5,6 +5,7 @@ import { useRouter } from 'vue-router';
5
5
  import { useDsStyle } from '../utils/dataset-style';
6
6
  import { useSiteConfig } from '../config/use-site-config';
7
7
  import { useI18n } from '../i18n';
8
+ import HomeSeriesSection from '../components/HomeSeriesSection.vue';
8
9
 
9
10
  const store = useVocabularyStore();
10
11
  const router = useRouter();
@@ -74,6 +75,10 @@ function goToGraph() { router.push({ name: 'graph' }); }
74
75
  <p class="text-base text-ink-400 max-w-lg leading-relaxed">
75
76
  {{ localizedDescription || 'Explore standardized terminology datasets from ISO and IEC technical committees. Browse concepts, definitions, and cross-references across multilingual vocabularies.' }}
76
77
  </p>
78
+
79
+ <!-- Edition-series section (renders only if multi-edition series exist) -->
80
+ <HomeSeriesSection />
81
+
77
82
  <div class="flex flex-wrap gap-3 mt-7">
78
83
  <button @click="goToSearch" class="btn-primary flex items-center gap-2">
79
84
  <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"/></svg>
package/vite.config.ts CHANGED
@@ -10,6 +10,10 @@ const cwd = process.cwd()
10
10
 
11
11
  const isTest = process.env.VITEST !== undefined
12
12
 
13
+ const pkgVersion = JSON.parse(
14
+ readFileSync(resolve(__dirname, 'package.json'), 'utf8'),
15
+ ).version
16
+
13
17
  function yamlPlugin() {
14
18
  return {
15
19
  name: 'yaml-transform',
@@ -170,6 +174,9 @@ export default defineConfig({
170
174
  } : {}),
171
175
  },
172
176
  },
177
+ define: {
178
+ __CONCEPT_BROWSER_VERSION__: JSON.stringify(pkgVersion),
179
+ },
173
180
  test: {
174
181
  environment: 'happy-dom',
175
182
  globals: true,