@glossarist/concept-browser 0.7.101 → 0.7.102

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 (45) hide show
  1. package/cli/index.mjs +14 -251
  2. package/cli/index.ts +289 -0
  3. package/package.json +3 -2
  4. package/src/__tests__/__fixtures__/concepts.ts +3 -3
  5. package/src/__tests__/composables/use-concept-edges-v2.test.ts +1 -0
  6. package/src/__tests__/designation-relationship.test.ts +2 -2
  7. package/src/__tests__/perf/serialization-perf.test.ts +1 -0
  8. package/src/__tests__/use-rdf-document.test.ts +1 -0
  9. package/src/adapters/bibliography-adapter.ts +1 -1
  10. package/src/adapters/model-bridge.ts +15 -15
  11. package/src/adapters/non-verbal/figure-bridge.ts +2 -2
  12. package/src/adapters/non-verbal/formula-bridge.ts +2 -2
  13. package/src/adapters/non-verbal/table-bridge.ts +2 -2
  14. package/src/components/CitationDisplay.vue +1 -1
  15. package/src/components/ConceptDetail.vue +2 -2
  16. package/src/components/ConceptDiffModal.vue +2 -2
  17. package/src/components/ConceptDiffView.vue +1 -1
  18. package/src/components/DesignationList.vue +4 -4
  19. package/src/components/NavIcon.vue +1 -0
  20. package/src/components/figure/FigureDisplay.vue +5 -5
  21. package/src/components/formula/FormulaDisplay.vue +4 -4
  22. package/src/components/table/TableDisplay.vue +3 -3
  23. package/src/components/use-rdf-document.ts +16 -7
  24. package/src/composables/use-concept-content.ts +3 -3
  25. package/src/composables/use-concept-diff.ts +2 -2
  26. package/src/composables/use-concept-edges.ts +3 -3
  27. package/src/config/page-types.ts +1 -1
  28. package/src/i18n/locales/eng.yml +12 -0
  29. package/src/i18n/locales/fra.yml +12 -0
  30. package/src/layouts/Default.astro +32 -2
  31. package/src/utils/content-renderer.ts +1 -1
  32. package/src/utils/partitive-multiplicity.ts +3 -2
  33. package/src/views/ConceptView.vue +3 -3
  34. package/src/views/learn/LearnRelationshipsView.vue +63 -0
  35. package/tailwind.config.js +3 -3
  36. package/tsconfig.json +1 -1
  37. package/src/adapters/model-bridge/bridges.ts +0 -159
  38. package/src/adapters/model-bridge/concept.ts +0 -97
  39. package/src/adapters/model-bridge/entity-refs.ts +0 -49
  40. package/src/adapters/model-bridge/hyperedge.ts +0 -103
  41. package/src/adapters/model-bridge/index.ts +0 -46
  42. package/src/adapters/model-bridge/mappers.ts +0 -277
  43. package/src/adapters/model-bridge/partitive.ts +0 -79
  44. package/src/adapters/non-verbal/glossarist-augment.d.ts +0 -339
  45. /package/src/adapters/{model-bridge/jsonld-types.ts → jsonld-types.ts} +0 -0
@@ -61,7 +61,7 @@ import type {
61
61
  JsonLdPartitiveMember,
62
62
  JsonLdPartitiveRelation,
63
63
  JsonLdConcept,
64
- } from './model-bridge/jsonld-types';
64
+ } from './jsonld-types';
65
65
 
66
66
  // ── Bridges for fields not yet in glossarist-js ────────────────────────────
67
67
  // Remove each bridge when glossarist-js publishes native support.
@@ -173,7 +173,7 @@ function attachBridges(concept: Concept, localizations: Record<string, unknown>)
173
173
  * instances, since RelatedConcept.fromJSON only reads type/content/ref.
174
174
  */
175
175
  function attachRelatedBridges(
176
- modelRelated: Array<{ type?: string | null; content?: Record<string, string> | string | null; ref?: any; related?: any[] }>,
176
+ modelRelated: ReadonlyArray<{ type?: string | null; content?: Record<string, string> | string | null; ref?: any; related?: ReadonlyArray<any> }>,
177
177
  rawRelated: unknown[],
178
178
  ): void {
179
179
  for (const rawRel of rawRelated) {
@@ -241,7 +241,7 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
241
241
 
242
242
  const pronunciation = d[GL.PRONUNCIATION];
243
243
  if (pronunciation?.length) {
244
- result.pronunciation = pronunciation.map(p => ({
244
+ result.pronunciation = pronunciation.map((p: any) => ({
245
245
  content: p[GL.CONTENT] ?? null,
246
246
  language: p[GL.LANGUAGE] ?? null,
247
247
  script: p[GL.SCRIPT] ?? null,
@@ -257,7 +257,7 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
257
257
 
258
258
  const related = d[GL.RELATED];
259
259
  if (related?.length) {
260
- result.related = related.map(r => {
260
+ result.related = related.map((r: any) => {
261
261
  const relType = r[GL.RELATIONSHIP_TYPE] ?? 'references';
262
262
  if (DESIGNATION_REL_TYPES.has(relType) && r[GL.TARGET]) {
263
263
  return { type: relType, target: r[GL.TARGET] };
@@ -272,7 +272,7 @@ function mapDesignationFromJsonLd(d: JsonLdDesignation): Record<string, unknown>
272
272
  }
273
273
  const grammarInfo = d[GL.GRAMMAR_INFO];
274
274
  if (grammarInfo?.length) {
275
- result.grammar_info = grammarInfo.map(gi => ({
275
+ result.grammar_info = grammarInfo.map((gi: any) => ({
276
276
  gender: gi[GL.GENDER] ?? null,
277
277
  number: gi[GL.NUMBER] ?? null,
278
278
  part_of_speech: gi[GL.PART_OF_SPEECH] ?? null,
@@ -354,7 +354,7 @@ function mapSourceFromJsonLd(s: JsonLdSource): Record<string, unknown> {
354
354
 
355
355
  const sf = s[GL.SOURCED_FROM] ?? s[GL.SOURCED_FROM_ALT];
356
356
  if (sf?.length) {
357
- result.sourced_from = sf.map(item => mapOriginFromJsonLd(item));
357
+ result.sourced_from = sf.map((item: any) => mapOriginFromJsonLd(item));
358
358
  }
359
359
 
360
360
  return result;
@@ -445,7 +445,7 @@ function mapLocalizedFromJsonLd(lc: JsonLdLocalizedConcept): Record<string, unkn
445
445
 
446
446
  const dates = lc[GL.DATES];
447
447
  if (dates?.length) {
448
- data.dates = dates.map(d => ({
448
+ data.dates = dates.map((d: any) => ({
449
449
  date: d[GL.DATE] ?? null,
450
450
  type: d[GL.DATE_TYPE] ?? null,
451
451
  }));
@@ -471,7 +471,7 @@ function mapPartitiveRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<stri
471
471
  if (!comprehensive) return null;
472
472
 
473
473
  const partitives = (r[GL.HAS_PARTITIVE] ?? [])
474
- .map((m): Record<string, unknown> | null => {
474
+ .map((m: any): Record<string, unknown> | null => {
475
475
  const ref = m[GL.REF] ? mapRefFromJsonLd(m[GL.REF]) : null;
476
476
  if (!ref) return null;
477
477
  const out: Record<string, unknown> = { ref };
@@ -494,7 +494,7 @@ function mapPartitiveRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<stri
494
494
  if (m[GL.IS_DELIMITING] === true) out.is_delimiting = true;
495
495
  return out;
496
496
  })
497
- .filter((m): m is Record<string, unknown> => m !== null);
497
+ .filter((m: any): m is Record<string, unknown> => m !== null);
498
498
 
499
499
  // ISO 704 requires ≥2 partitives. Skip malformed relations rather
500
500
  // than letting the constructor throw and break the whole concept.
@@ -529,7 +529,7 @@ function mapGenericRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<string
529
529
  if (!comprehensive) return null;
530
530
 
531
531
  const members = (r[GL.HAS_GENERIC] ?? [])
532
- .map((m): Record<string, unknown> | null => {
532
+ .map((m: any): Record<string, unknown> | null => {
533
533
  const ref = m[GL.REF] ? mapRefFromJsonLd(m[GL.REF]) : null;
534
534
  if (!ref) return null;
535
535
  const out: Record<string, unknown> = { ref };
@@ -552,7 +552,7 @@ function mapGenericRelationFromJsonLd(r: JsonLdPartitiveRelation): Record<string
552
552
  }
553
553
  return out;
554
554
  })
555
- .filter((m): m is Record<string, unknown> => m !== null);
555
+ .filter((m: any): m is Record<string, unknown> => m !== null);
556
556
 
557
557
  if (members.length < 2) return null;
558
558
 
@@ -589,7 +589,7 @@ function splitMultiplicity(m: string): { presence: 'required' | 'optional'; coun
589
589
 
590
590
  function conceptFromJsonLd(doc: JsonLdConcept): Concept {
591
591
  const id = String(doc[GL.IDENTIFIER] ?? doc['@id']?.split('/').pop() ?? '');
592
- const localizations: Record<string, unknown> = {};
592
+ const localizations: Record<string, Record<string, unknown>> = {};
593
593
 
594
594
  const rawLc = doc[GL.LOCALIZED_CONCEPT] ?? {};
595
595
  for (const [lang, lc] of Object.entries(rawLc)) {
@@ -601,10 +601,10 @@ function conceptFromJsonLd(doc: JsonLdConcept): Concept {
601
601
  const related = (doc[GL.RELATED] ?? []).map(mapRelatedFromJsonLd);
602
602
  const partitiveRelations = (doc[GL.PARTITIVE_RELATIONS] ?? [])
603
603
  .map(mapPartitiveRelationFromJsonLd)
604
- .filter((r): r is Record<string, unknown> => r !== null);
604
+ .filter((r: any): r is Record<string, unknown> => r !== null);
605
605
  const genericRelations = (doc[GL.GENERIC_RELATIONS] ?? [])
606
606
  .map(mapGenericRelationFromJsonLd)
607
- .filter((r): r is Record<string, unknown> => r !== null);
607
+ .filter((r: any): r is Record<string, unknown> => r !== null);
608
608
  const tagsArr = doc[GL.TAGS];
609
609
  const tags = Array.isArray(tagsArr) ? [...tagsArr] : [];
610
610
 
@@ -612,7 +612,7 @@ function conceptFromJsonLd(doc: JsonLdConcept): Concept {
612
612
  id,
613
613
  term: doc[GL.TERM] ?? null,
614
614
  uri: doc['@id'] ?? null,
615
- localizations,
615
+ localizations: localizations as Record<string, any>,
616
616
  related,
617
617
  partitive_relations: partitiveRelations,
618
618
  generic_relations: genericRelations,
@@ -48,7 +48,7 @@ function imageFromJsonLd(raw: Record<string, unknown>): FigureImage | null {
48
48
  ...(typeof width === 'number' && { width }),
49
49
  ...(typeof height === 'number' && { height }),
50
50
  ...(typeof scale === 'number' && { scale }),
51
- });
51
+ } as any);
52
52
  }
53
53
 
54
54
  function imagesFromJsonLd(raw: unknown): FigureImage[] {
@@ -96,5 +96,5 @@ export function figureFromJsonLd(doc: Record<string, unknown>): Figure | null {
96
96
  ...(description && { description }),
97
97
  ...(subfigures && { subfigures }),
98
98
  ...(sources.length && { sources }),
99
- });
99
+ } as any);
100
100
  }
@@ -40,11 +40,11 @@ export function formulaFromJsonLd(doc: Record<string, unknown>): Formula | null
40
40
 
41
41
  return new Formula({
42
42
  id,
43
- expression,
43
+ expression: expression as any,
44
44
  notation,
45
45
  ...(identifier && { identifier }),
46
46
  ...(caption && { caption }),
47
47
  ...(description && { description }),
48
48
  ...(sources.length && { sources }),
49
- });
49
+ } as any);
50
50
  }
@@ -90,11 +90,11 @@ export function tableFromJsonLd(doc: Record<string, unknown>): Table | null {
90
90
 
91
91
  return new Table({
92
92
  id,
93
- content: content as Record<string, unknown>,
93
+ content: content as any,
94
94
  ...(identifier && { identifier }),
95
95
  ...(caption && { caption }),
96
96
  ...(description && { description }),
97
97
  ...(format && { format }),
98
98
  ...(sources.length && { sources }),
99
- });
99
+ } as any);
100
100
  }
@@ -107,7 +107,7 @@ async function loadPreview() {
107
107
  const def = lc?.definitions?.[0]?.content || concept.definition('eng');
108
108
  const term = lc?.terms?.[0]?.designation || concept.primaryDesignation('eng');
109
109
  preview.value = {
110
- designation: term || entry?.designations?.eng || conceptId,
110
+ designation: String(term || entry?.designations?.eng || conceptId),
111
111
  definition: typeof def === 'string' ? def.slice(0, 200) : '',
112
112
  };
113
113
  } catch {
@@ -200,7 +200,7 @@ function getTermForLang(lang: string): string {
200
200
 
201
201
  function getDesignationsForLang(lang: string): Designation[] {
202
202
  const lc = props.concept.localization(lang);
203
- return lc?.terms ?? [];
203
+ return [...(lc?.terms ?? [])];
204
204
  }
205
205
 
206
206
  function hasDefinition(lang: string): boolean {
@@ -444,7 +444,7 @@ const nonVerbalReps = computed(() => {
444
444
  </div>
445
445
 
446
446
  <!-- Non-verbal representations -->
447
- <NonVerbalRepDisplay v-if="lc.lc.nonVerbalRep?.length" :reps="lc.lc.nonVerbalRep" :locale="lc.lang" :register-id="registerId" :dataset-locales="languages" />
447
+ <NonVerbalRepDisplay v-if="lc.lc.nonVerbalRep?.length" :reps="[...lc.lc.nonVerbalRep]" :locale="lc.lang" :register-id="registerId" :dataset-locales="languages" />
448
448
 
449
449
  <!-- Sources -->
450
450
  <div v-if="lc.sources.length" class="space-y-2">
@@ -33,7 +33,7 @@ const { t } = useI18n();
33
33
  const targetConcept = ref<Concept | null>(null);
34
34
  const loadError = ref<string | null>(null);
35
35
 
36
- function conceptToData(c: Concept | null, registerId: string): ConceptLikeData | null {
36
+ function conceptToData(c: any, registerId: string): ConceptLikeData | null {
37
37
  if (!c) return null;
38
38
  const langs = c.languages ?? [];
39
39
  const localizations: Record<string, unknown> = {};
@@ -51,7 +51,7 @@ function conceptToData(c: Concept | null, registerId: string): ConceptLikeData |
51
51
  }
52
52
 
53
53
  const oldData = computed(() => conceptToData(targetConcept.value, props.targetRegisterId));
54
- const newData = computed(() => conceptToData(props.currentConcept, props.currentRegisterId));
54
+ const newData = computed(() => conceptToData(props.currentConcept as any, props.currentRegisterId));
55
55
 
56
56
  async function loadTarget() {
57
57
  if (!props.open || !props.targetRegisterId || !props.targetConceptId) return;
@@ -51,7 +51,7 @@ watchEffect(() => {
51
51
  }
52
52
  });
53
53
 
54
- const effectiveDiff = computed(() => props.diff ?? internal.diffResult.value);
54
+ const effectiveDiff = computed(() => (props.diff ?? internal.diffResult.value) as any);
55
55
  const sections = computed(() => deriveDiffSections(effectiveDiff.value, props.lang));
56
56
  const similarity = computed(() => computeSimilarity(effectiveDiff.value));
57
57
  const hasChanges = computed(() => effectiveDiff.value?.hasChanges === true);
@@ -77,11 +77,11 @@ function resolvedLabel(dr: { content: string | null; ref?: { source: string | nu
77
77
  <div v-if="d.related?.length" class="mt-0.5 space-y-0.5">
78
78
  <div v-for="(dr, dri) in d.related" :key="'dr'+dri" class="text-xs text-ink-400 flex items-center gap-1.5">
79
79
  <span class="badge text-[9px] bg-gray-50 text-gray-600">{{ relationshipLabel(dr.type ?? '') }}</span>
80
- <template v-if="getDesignationTarget(dr)">
81
- <span class="italic">{{ getDesignationTarget(dr) }}</span>
80
+ <template v-if="getDesignationTarget(dr as any)">
81
+ <span class="italic">{{ getDesignationTarget(dr as any) }}</span>
82
82
  </template>
83
- <button v-else-if="'ref' in dr && dr.ref" @click="emit('navigate-related', dr.ref)" class="concept-link">{{ resolvedLabel(dr) }}</button>
84
- <span v-else>{{ resolvedLabel(dr) }}</span>
83
+ <button v-else-if="'ref' in dr && dr.ref" @click="emit('navigate-related', dr.ref)" class="concept-link">{{ resolvedLabel(dr as any) }}</button>
84
+ <span v-else>{{ resolvedLabel(dr as any) }}</span>
85
85
  </div>
86
86
  </div>
87
87
  </div>
@@ -10,6 +10,7 @@ const icons: Record<string, string> = {
10
10
  list: 'M4 6h16M4 10h16M4 14h16M4 18h16',
11
11
  schema: 'M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z',
12
12
  database: 'M4 7c0 1.1 3.6 2 8 2s8-.9 8-2M4 7c0-1.1 3.6-2 8-2s8 .9 8 2M4 7v10c0 1.1 3.6 2 8 2s8-.9 8-2V7M4 12c0 1.1 3.6 2 8 2s8-.9 8-2',
13
+ book: 'M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25',
13
14
  };
14
15
 
15
16
  defineProps<{ name: string }>();
@@ -50,8 +50,8 @@ const topLevelImages = computed(() => fig.value?.images ?? []);
50
50
  >
51
51
  <div v-if="topLevelImages.length" :class="`figure__media figure__media--${layout}`">
52
52
  <FigureImages
53
- :images="topLevelImages"
54
- :alt="fig.alt"
53
+ :images="[...topLevelImages]"
54
+ :alt="(fig.alt as any)"
55
55
  :dataset-id="datasetId"
56
56
  :locale="locale"
57
57
  :fallback-chain="fallbackChain"
@@ -76,8 +76,8 @@ const topLevelImages = computed(() => fig.value?.images ?? []);
76
76
 
77
77
  <NonVerbalCaption
78
78
  :identifier="fig.identifier"
79
- :caption="fig.caption"
80
- :description="fig.description"
79
+ :caption="(fig.caption as any)"
80
+ :description="(fig.description as any)"
81
81
  :locale="locale"
82
82
  :fallback-chain="fallbackChain"
83
83
  :description-id="descriptionId"
@@ -85,7 +85,7 @@ const topLevelImages = computed(() => fig.value?.images ?? []);
85
85
 
86
86
  <NonVerbalSources
87
87
  v-if="fig.sources?.length"
88
- :sources="fig.sources"
88
+ :sources="[...(fig.sources || [])]"
89
89
  />
90
90
  </figure>
91
91
 
@@ -41,7 +41,7 @@ const descriptionId = computed(() => `${anchor.value}-desc`);
41
41
  >
42
42
  <div class="formula__expr-line">
43
43
  <FormulaExpression
44
- :expression="form.expression"
44
+ :expression="(form.expression as any)"
45
45
  :notation="(form.notation as FormulaNotation | null)"
46
46
  :locale="locale"
47
47
  :fallback-chain="fallbackChain"
@@ -50,8 +50,8 @@ const descriptionId = computed(() => `${anchor.value}-desc`);
50
50
 
51
51
  <NonVerbalCaption
52
52
  :identifier="form.identifier"
53
- :caption="form.caption"
54
- :description="form.description"
53
+ :caption="(form.caption as any)"
54
+ :description="(form.description as any)"
55
55
  :locale="locale"
56
56
  :fallback-chain="fallbackChain"
57
57
  :description-id="descriptionId"
@@ -59,7 +59,7 @@ const descriptionId = computed(() => `${anchor.value}-desc`);
59
59
 
60
60
  <NonVerbalSources
61
61
  v-if="form.sources?.length"
62
- :sources="form.sources"
62
+ :sources="[...(form.sources || [])]"
63
63
  />
64
64
  </figure>
65
65
 
@@ -65,8 +65,8 @@ const markup = computed(() => {
65
65
 
66
66
  <NonVerbalCaption
67
67
  :identifier="table.identifier"
68
- :caption="table.caption"
69
- :description="table.description"
68
+ :caption="(table.caption as any)"
69
+ :description="(table.description as any)"
70
70
  :locale="locale"
71
71
  :fallback-chain="fallbackChain"
72
72
  :description-id="descriptionId"
@@ -74,7 +74,7 @@ const markup = computed(() => {
74
74
 
75
75
  <NonVerbalSources
76
76
  v-if="table.sources?.length"
77
- :sources="table.sources"
77
+ :sources="[...(table.sources || [])]"
78
78
  />
79
79
  </figure>
80
80
 
@@ -8,12 +8,21 @@ import {
8
8
  writeTurtleSync,
9
9
  collectQuads,
10
10
  PREFIXES,
11
- type ClassInstance,
12
- type PropValue,
13
- type Quad,
14
11
  } from 'glossarist/rdf';
12
+ import type { Quad } from '@rdfjs/types';
15
13
 
16
- export type { ClassInstance, PropValue };
14
+ export interface PropValue {
15
+ predicate: string;
16
+ values: string[];
17
+ nested?: boolean;
18
+ }
19
+
20
+ export interface ClassInstance {
21
+ classId: string;
22
+ classLabel: string;
23
+ label: string;
24
+ props: PropValue[];
25
+ }
17
26
 
18
27
  export interface RdfDocument {
19
28
  sections: ComputedRef<ClassInstance[]>;
@@ -111,10 +120,10 @@ export function useRdfDocument(
111
120
  const concept = getConcept();
112
121
  const uri = getConceptUri();
113
122
  const conceptQuads = collectQuads(
114
- conceptToQuads(concept, {
123
+ conceptToQuads(concept as any, {
115
124
  registerId: options.registerId ?? '',
116
125
  uriBase: options.uriBase ?? '',
117
- }),
126
+ }) as any,
118
127
  );
119
128
  const provenanceQuads = collectQuads(
120
129
  provenanceToQuads({
@@ -122,7 +131,7 @@ export function useRdfDocument(
122
131
  serializer: 'concept-browser',
123
132
  serializerVersion: SERIALIZER_VERSION,
124
133
  generatedAt: new Date().toISOString(),
125
- }),
134
+ }) as any,
126
135
  );
127
136
  return [...conceptQuads, ...provenanceQuads];
128
137
  });
@@ -101,14 +101,14 @@ export function useConceptContent(
101
101
  renderedAnnotations: annotations.map((a: string) => renderContent(a, opts)),
102
102
  notes,
103
103
  examples,
104
- sources: lc.sources,
105
- designations: lc.terms,
104
+ sources: [...lc.sources],
105
+ designations: [...lc.terms],
106
106
  renderedDesignations: new Map(lc.terms.map(d => [d.designation, renderContent(d.designation)])),
107
107
  entryStatus: lc.entryStatus ?? '',
108
108
  classification: lc.classification,
109
109
  reviewType: lc.reviewType,
110
110
  release: lc.release,
111
- lineageSourceSimilarity: lc.lineageSourceSimilarity,
111
+ lineageSourceSimilarity: (lc.lineageSourceSimilarity as any),
112
112
  lcScript: lc.script,
113
113
  lcSystem: lc.system,
114
114
  });
@@ -211,8 +211,8 @@ export function useConceptDiff() {
211
211
  error.value = null;
212
212
  try {
213
213
  diffResult.value = diffConcepts(
214
- oldData ? toDiffableConcept(oldData) : null,
215
- newData ? toDiffableConcept(newData) : null,
214
+ (oldData ? toDiffableConcept(oldData) : null) as any,
215
+ (newData ? toDiffableConcept(newData) : null) as any,
216
216
  );
217
217
  } catch (e: unknown) {
218
218
  error.value = e instanceof Error ? e.message : String(e);
@@ -7,7 +7,7 @@ import type { Concept, RelatedConcept, ConceptRef } from 'glossarist';
7
7
  import { PartitiveHyperedge } from 'glossarist';
8
8
  import { GenericHyperedge } from 'glossarist/models';
9
9
  import type {
10
- PartitiveRelation as GlsPartitiveRelation,
10
+ PartitiveHyperedge as GlsPartitiveRelation,
11
11
  PartitiveMember as GlsPartitiveMember,
12
12
  } from 'glossarist/models';
13
13
  import type {
@@ -39,7 +39,7 @@ export interface EdgeDisplay {
39
39
  }
40
40
 
41
41
  export function useConceptEdges(
42
- concept: ComputedRef<Concept | null | undefined>,
42
+ concept: ComputedRef<any>,
43
43
  registerId: ComputedRef<string>,
44
44
  manifest: ComputedRef<Manifest | null | undefined>,
45
45
  edges: ComputedRef<GraphEdge[]>,
@@ -138,7 +138,7 @@ export function useConceptEdges(
138
138
  // Concept-level related concepts (managed concept cross-references)
139
139
  const conceptRelated = computed(() => {
140
140
  if (!concept.value) return [];
141
- const direct = concept.value.relatedConcepts?.filter(rc => !INVERSE_RELATIONSHIPS[rc.type]) ?? [];
141
+ const direct = concept.value.relatedConcepts?.filter((rc: any) => !INVERSE_RELATIONSHIPS[rc.type]) ?? [];
142
142
  const derived = incomingEdges.value
143
143
  .filter(e => INVERSE_RELATIONSHIPS[e.type])
144
144
  .map(e => {
@@ -35,7 +35,7 @@ export const PAGE_TYPES: readonly PageTypeDefinition[] = [
35
35
  { type: 'stats', route: 'stats', title: 'Statistics', icon: 'chart', scope: 'dataset', featureFlag: '!stats', autoSynthesize: true },
36
36
  { type: 'sources', route: 'sources', title: 'Sources', icon: 'database', scope: 'dataset', autoSynthesize: true },
37
37
  { type: 'about', route: 'about', title: 'About', icon: 'info', scope: 'dataset', featureFlag: '!about', autoSynthesize: true },
38
- { type: 'learn', route: 'learn', title: 'Learn', icon: 'book-open', scope: 'global', autoSynthesize: true },
38
+ { type: 'learn', route: 'learn', title: 'Learn', icon: 'book', scope: 'global', autoSynthesize: true },
39
39
  ] as const;
40
40
 
41
41
  export type BuiltinPageType = typeof PAGE_TYPES[number]['type'];
@@ -56,6 +56,18 @@ learn.dimensions.delimiting.false.label: not delimiting
56
56
  learn.dimensions.delimiting.false.meaning: An ordinary part. Drawn at normal stroke width (1.5px).
57
57
  learn.dimensions.delimiting.true.label: delimiting
58
58
  learn.dimensions.delimiting.true.meaning: The part is essential to the whole's identity. Drawn at 3× normal stroke width (4.5px).
59
+ learn.dimensions.combinationsTitle: Valid multiplicity combinations
60
+ learn.dimensions.combinationsDescription: ISO 704 description
61
+ learn.dimensions.combinationsModel: Our model
62
+ learn.dimensions.combinationsVisual: Visual
63
+ learn.dimensions.combo.compulsory: compulsory
64
+ learn.dimensions.combo.atLeastOne: at least one
65
+ learn.dimensions.combo.optional: optional
66
+ learn.dimensions.combo.optionalMultiple: optional multiple
67
+ learn.dimensions.combo.compulsoryMultiple: compulsory multiple
68
+ learn.dimensions.invalidDescription: (invalid)
69
+ learn.dimensions.invalidModel: optional + at_least_one
70
+ learn.dimensions.invalidReason: Vacuous — collapses to optional + multiple (0..*). Rejected by glossarist-js.
59
71
  learn.designations.title: Designations
60
72
  learn.designations.short: Terms and symbols — preferred, admitted, deprecated, full form, abbreviation, acronym, initialism.
61
73
  learn.designations.intro: Concepts are designated by terms, proper names, and symbols. This page explains the vocabulary you'll see on each concept page.
@@ -55,6 +55,18 @@ learn.dimensions.delimiting.false.label: non délimitante
55
55
  learn.dimensions.delimiting.false.meaning: Une partie ordinaire. Dessinée à largeur de trait normale (1,5 px).
56
56
  learn.dimensions.delimiting.true.label: délimitante
57
57
  learn.dimensions.delimiting.true.meaning: La partie est essentielle à l'identité du tout. Dessinée à largeur de trait 3× (4,5 px).
58
+ learn.dimensions.combinationsTitle: Combinaisons de multiplicité valides
59
+ learn.dimensions.combinationsDescription: Description ISO 704
60
+ learn.dimensions.combinationsModel: Notre modèle
61
+ learn.dimensions.combinationsVisual: Visuel
62
+ learn.dimensions.combo.compulsory: obligatoire
63
+ learn.dimensions.combo.atLeastOne: au moins un
64
+ learn.dimensions.combo.optional: optionnel
65
+ learn.dimensions.combo.optionalMultiple: optionnel multiple
66
+ learn.dimensions.combo.compulsoryMultiple: obligatoire multiple
67
+ learn.dimensions.invalidDescription: (invalide)
68
+ learn.dimensions.invalidModel: optional + at_least_one
69
+ learn.dimensions.invalidReason: Vacuous — se réduit à optional + multiple (0..*). Rejeté par glossarist-js.
58
70
  learn.designations.title: Désignations
59
71
  learn.designations.short: Termes et symboles — préféré, admis, déconseillé, forme complète, abréviation, acronyme, sigle.
60
72
  learn.designations.intro: Les concepts sont désignés par des termes, noms propres et symboles. Cette page explique le vocabulaire que vous verrez sur chaque page de concept.
@@ -12,6 +12,30 @@ interface Props {
12
12
 
13
13
  const { title, description = '', conceptTerm, conceptDefinition } = Astro.props;
14
14
  const siteConfig = await getSiteConfig();
15
+
16
+ // Derive Google Fonts URL from siteConfig branding (build-time SSG).
17
+ // Falls back to the default DM Sans / DM Serif Display / JetBrains Mono
18
+ // when no branding.fonts is declared.
19
+ const brandingFonts = siteConfig?.branding?.fonts;
20
+ const fontParts: string[] = [];
21
+ if (brandingFonts?.header?.source === 'google' && brandingFonts.header.family) {
22
+ const w = (brandingFonts.header.weights || [400, 700]).join(';');
23
+ fontParts.push(`family=${brandingFonts.header.family.replace(/ /g, '+')}:wght@${w}`);
24
+ }
25
+ if (brandingFonts?.body?.source === 'google' && brandingFonts.body.family) {
26
+ const w = (brandingFonts.body.weights || [400, 700]).join(';');
27
+ fontParts.push(`family=${brandingFonts.body.family.replace(/ /g, '+')}:wght@${w}`);
28
+ }
29
+ const fontsUrl = fontParts.length > 0
30
+ ? `https://fonts.googleapis.com/css2?${fontParts.join('&')}&display=swap`
31
+ : 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=DM+Serif+Display:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap';
32
+
33
+ const fontHeader = brandingFonts?.header?.family
34
+ ? `'${brandingFonts.header.family}', Georgia, serif`
35
+ : `'DM Serif Display', Georgia, serif`;
36
+ const fontBody = brandingFonts?.body?.family
37
+ ? `'${brandingFonts.body.family}', system-ui, sans-serif`
38
+ : `'DM Sans', system-ui, sans-serif`;
15
39
  ---
16
40
 
17
41
  <!DOCTYPE html>
@@ -42,8 +66,14 @@ const siteConfig = await getSiteConfig();
42
66
  <title>{title}</title>
43
67
  <link rel="preconnect" href="https://fonts.googleapis.com">
44
68
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
45
- <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=DM+Serif+Display:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap" onload="this.rel='stylesheet'">
46
- <noscript><link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=DM+Serif+Display:ital@0;1&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"></noscript>
69
+ <link rel="preload" as="style" href={fontsUrl} onload="this.rel='stylesheet'">
70
+ <noscript><link href={fontsUrl} rel="stylesheet"></noscript>
71
+ <style is:global>
72
+ :root {
73
+ --font-header: ${fontHeader};
74
+ --font-body: ${fontBody};
75
+ }
76
+ </style>
47
77
  <script is:inline>
48
78
  (function() {
49
79
  const c = document.cookie.split(';').map(s => s.trim()).find(s => s.startsWith('theme='));
@@ -183,7 +183,7 @@ function resolveUrnRefs(text: string, opts: RenderOptions): string {
183
183
  function resolveMentions(text: string, opts: RenderOptions): string {
184
184
  return text.replace(/\{\{([^{}]+?)\}\}/g, (_orig, body) => {
185
185
  const parsed = parseMention(body);
186
- const p = parsed as Record<string, unknown>;
186
+ const p = parsed as unknown as Record<string, unknown>;
187
187
 
188
188
  switch (p.kind) {
189
189
  case 'fig-ref':
@@ -28,10 +28,11 @@ import {
28
28
  multiplicityFromPair,
29
29
  isValidPresence,
30
30
  isValidCount,
31
- type PartitivePresence,
32
- type PartitiveCount,
33
31
  } from 'glossarist/models';
34
32
 
33
+ type PartitivePresence = (typeof PARTITIVE_PRESENCE.VALUES)[number];
34
+ type PartitiveCount = (typeof PARTITIVE_COUNT.VALUES)[number];
35
+
35
36
  // ── Two independent axes (re-exported SSOT) ────────────────────────────
36
37
 
37
38
  export { PARTITIVE_PRESENCE, PARTITIVE_COUNT };
@@ -60,7 +60,7 @@ const activeRegisterId = computed(() => sphereFocusPayload.value?.registerId ??
60
60
  const adjacent = ref({ prev: null as string | null, next: null as string | null });
61
61
 
62
62
  const { conceptPartitiveRelations, conceptGenericRelations } = useConceptEdges(
63
- concept,
63
+ computed(() => concept.value as any),
64
64
  activeRegisterId,
65
65
  manifest,
66
66
  edges,
@@ -284,7 +284,7 @@ onUnmounted(() => window.removeEventListener('keydown', onKeydown));
284
284
  <!-- Sphere mode — receives concept + edges directly, no URI matching -->
285
285
  <RelationSphere
286
286
  v-if="viewMode === 'sphere'"
287
- :concept="concept"
287
+ :concept="concept as any"
288
288
  :manifest="manifest"
289
289
  :register-id="activeRegisterId"
290
290
  :edges="edges"
@@ -295,7 +295,7 @@ onUnmounted(() => window.removeEventListener('keydown', onKeydown));
295
295
  <!-- Detail mode -->
296
296
  <ConceptDetail
297
297
  v-else
298
- :concept="concept"
298
+ :concept="concept as any"
299
299
  :manifest="manifest"
300
300
  :edges="edges"
301
301
  :adjacent="adjacent"