@glossarist/concept-browser 0.7.99 → 0.7.101

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.
@@ -0,0 +1,83 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import { createRouter, createMemoryHistory } from 'vue-router';
5
+ import LearnRelationshipsView from '../../../views/learn/LearnRelationshipsView.vue';
6
+
7
+ async function makeRouter() {
8
+ const router = createRouter({
9
+ history: createMemoryHistory(),
10
+ routes: [
11
+ { path: '/', name: 'home', component: { template: '<div/>' } },
12
+ { path: '/learn/relationships', name: 'learn-relationships', component: LearnRelationshipsView },
13
+ ],
14
+ });
15
+ await router.push('/learn/relationships');
16
+ await router.isReady();
17
+ return router;
18
+ }
19
+
20
+ describe('LearnRelationshipsView', () => {
21
+ beforeEach(() => setActivePinia(createPinia()));
22
+
23
+ it('renders the page header with translated title', async () => {
24
+ const router = await makeRouter();
25
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
26
+ expect(w.find('h1').text()).toContain('Relationships');
27
+ });
28
+
29
+ it('renders three top-level sections (bilateral, partitive, general)', async () => {
30
+ const router = await makeRouter();
31
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
32
+ const sections = w.findAll('section[aria-label]');
33
+ expect(sections.length).toBe(3);
34
+ });
35
+
36
+ it('renders the hyperedge cards with the expected anchors', async () => {
37
+ const router = await makeRouter();
38
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
39
+ const ids = w.findAll('.learn-card[id]').map(c => c.attributes('id'));
40
+ expect(ids).toContain('__partitive__');
41
+ expect(ids).toContain('__generic__');
42
+ });
43
+
44
+ it('renders three DimensionCards for the partitive dimensions', async () => {
45
+ const router = await makeRouter();
46
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
47
+ const dimIds = w.findAll('.dimension-card[id]').map(c => c.attributes('id'));
48
+ expect(dimIds).toEqual(expect.arrayContaining(['partitive-presence', 'partitive-count', 'partitive-delimiting']));
49
+ });
50
+
51
+ it('renders the bilateral section grouped by category', async () => {
52
+ const router = await makeRouter();
53
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
54
+ const bilateralArticles = w.find('section[aria-label="Bilateral relations (one-to-one)"]').findAll('article');
55
+ expect(bilateralArticles.length).toBeGreaterThanOrEqual(5);
56
+ });
57
+
58
+ it('every bilateral type row has an id anchor matching the type key', async () => {
59
+ const router = await makeRouter();
60
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
61
+ const rows = w.findAll('.bilateral-row');
62
+ expect(rows.length).toBeGreaterThan(10);
63
+ for (const row of rows) {
64
+ const id = row.attributes('id');
65
+ expect(id).toBeTruthy();
66
+ expect(id).toMatch(/^[a-z_]+$/);
67
+ }
68
+ });
69
+
70
+ it('renders a live diagram for each hyperedge card', async () => {
71
+ const router = await makeRouter();
72
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
73
+ expect(w.findAll('svg.hyperedge-diagram').length).toBe(2);
74
+ });
75
+
76
+ it('renders ISO references on each hyperedge card', async () => {
77
+ const router = await makeRouter();
78
+ const w = mount(LearnRelationshipsView, { global: { plugins: [router] } });
79
+ const isoRefs = w.findAll('.learn-card span.font-mono').map(s => s.text());
80
+ expect(isoRefs.some(t => t.includes('ISO 704:2022 §5.5.4.3'))).toBe(true);
81
+ expect(isoRefs.some(t => t.includes('ISO 704:2022 §5.5.4.2'))).toBe(true);
82
+ });
83
+ });
@@ -0,0 +1,60 @@
1
+ import { describe, it, expect, beforeEach } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import { createPinia, setActivePinia } from 'pinia';
4
+ import { createRouter, createMemoryHistory } from 'vue-router';
5
+ import LearnStatusesView from '../../../views/learn/LearnStatusesView.vue';
6
+
7
+ async function makeRouter() {
8
+ const router = createRouter({
9
+ history: createMemoryHistory(),
10
+ routes: [
11
+ { path: '/', name: 'home', component: { template: '<div/>' } },
12
+ { path: '/learn/statuses', name: 'learn-statuses', component: LearnStatusesView },
13
+ { path: '/learn/designations', name: 'learn-designations', component: { template: '<div/>' } },
14
+ ],
15
+ });
16
+ await router.push('/learn/statuses');
17
+ await router.isReady();
18
+ return router;
19
+ }
20
+
21
+ describe('LearnStatusesView', () => {
22
+ beforeEach(() => setActivePinia(createPinia()));
23
+
24
+ it('renders the page header', async () => {
25
+ const router = await makeRouter();
26
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
27
+ expect(w.find('h1').text()).toContain('Statuses');
28
+ });
29
+
30
+ it('renders three top-level sections (entry, normative, source)', async () => {
31
+ const router = await makeRouter();
32
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
33
+ const sections = w.findAll('section[aria-label]');
34
+ expect(sections.length).toBe(3);
35
+ });
36
+
37
+ it('entry section renders all four entry statuses with anchors', async () => {
38
+ const router = await makeRouter();
39
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
40
+ const entrySection = w.find('section[aria-label="Entry status"]');
41
+ const ids = entrySection.findAll('.learn-card[id]').map(c => c.attributes('id'));
42
+ expect(ids).toEqual(expect.arrayContaining(['entry-valid', 'entry-withdrawn', 'entry-draft', 'entry-superseded']));
43
+ });
44
+
45
+ it('normative section cross-links to /learn/designations', async () => {
46
+ const router = await makeRouter();
47
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
48
+ const normative = w.find('section[aria-label="Normative status"]');
49
+ const link = normative.find('a');
50
+ expect(link.attributes('href')).toBe('/learn/designations');
51
+ });
52
+
53
+ it('source section renders the four source statuses with anchors', async () => {
54
+ const router = await makeRouter();
55
+ const w = mount(LearnStatusesView, { global: { plugins: [router] } });
56
+ const sourceSection = w.find('section[aria-label="Source status"]');
57
+ const ids = sourceSection.findAll('.learn-card[id]').map(c => c.attributes('id'));
58
+ expect(ids).toEqual(expect.arrayContaining(['source-identical', 'source-modified', 'source-restyled', 'source-lineage']));
59
+ });
60
+ });
@@ -0,0 +1,115 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * HyperedgeDiagram — small inline SVG that calls the actual sphere
4
+ * renderers (drawRakeBundles / drawGenericPipes) on hand-picked
5
+ * sample data. Used on the Relation Types teaching page.
6
+ *
7
+ * The diagrams stay in sync with the real sphere rendering by
8
+ * construction — no static SVG to drift out of date.
9
+ */
10
+ import { onMounted, ref } from 'vue';
11
+ import { drawRakeBundles } from './relation-sphere/rake-bundles';
12
+ import { drawGenericPipes } from './relation-sphere/pipe-bundles';
13
+ import type {
14
+ PartitiveRelationWire,
15
+ PartitiveMemberWire,
16
+ GenericRelationWire,
17
+ } from '../adapters/types';
18
+ import { useUiStore } from '../stores/ui';
19
+
20
+ const props = defineProps<{
21
+ kind: 'partitive' | 'generic';
22
+ }>();
23
+
24
+ const svgRef = ref<SVGSVGElement | null>(null);
25
+ const uiStore = useUiStore();
26
+
27
+ /* Synthetic positions: comp at top-center, members fanned across the
28
+ bottom. Mirrors the layout the sphere produces for a 3-member
29
+ hyperedge with the focus at the center. */
30
+ const POS = new Map<string, { x: number; y: number }>([
31
+ ['vim-2012/1.3', { x: 120, y: 24 }],
32
+ ['vim-2012/1.4', { x: 36, y: 132 }],
33
+ ['vim-2012/1.5', { x: 120, y: 144 }],
34
+ ['vim-2012/1.22', { x: 204, y: 132 }],
35
+ ]);
36
+
37
+ const PARTITIVE: PartitiveRelationWire = {
38
+ source: 'https://example.org/vim-2012/concept/1.3',
39
+ comprehensive: 'https://example.org/vim-2012/concept/1.3',
40
+ completeness: 'partial',
41
+ register: 'vim-2012',
42
+ partitives: [
43
+ /* required + multiple → solid double-line */
44
+ { uri: 'https://example.org/vim-2012/concept/1.4', presence: 'required', count: 'multiple', isDelimiting: false },
45
+ /* required + exactly_one + delimiting → 3× width single solid */
46
+ { uri: 'https://example.org/vim-2012/concept/1.5', presence: 'required', count: 'exactly_one', isDelimiting: true },
47
+ /* optional + at_least_one → dashed double-line */
48
+ { uri: 'https://example.org/vim-2012/concept/1.22', presence: 'optional', count: 'at_least_one', isDelimiting: false },
49
+ ] satisfies PartitiveMemberWire[],
50
+ };
51
+
52
+ const GENERIC: GenericRelationWire = {
53
+ source: 'https://example.org/vim-2012/concept/1.9',
54
+ comprehensive: 'https://example.org/vim-2012/concept/1.9',
55
+ completeness: 'partial',
56
+ register: 'vim-2012',
57
+ criterion: { eng: 'by magnitude relationship', fra: 'selon la relation de grandeur' },
58
+ members: [
59
+ { uri: 'https://example.org/vim-2012/concept/1.17', presence: 'required', count: 'at_least_one',
60
+ delimitingCharacteristic: { eng: 'multiple of a unit', fra: "multiple d'une unité" } },
61
+ { uri: 'https://example.org/vim-2012/concept/1.18', presence: 'required', count: 'at_least_one',
62
+ delimitingCharacteristic: { eng: 'submultiple of a unit', fra: "sous-multiple d'une unité" } },
63
+ { uri: 'https://example.org/vim-2012/concept/1.12', presence: 'required', count: 'at_least_one',
64
+ delimitingCharacteristic: { eng: 'coherent derived unit', fra: 'unité dérivée cohérente' } },
65
+ ],
66
+ };
67
+
68
+ /* Override positions for the generic sample (comp is 1.9, members are 1.17/1.18/1.12). */
69
+ const GENERIC_POS = new Map<string, { x: number; y: number }>([
70
+ ['vim-2012/1.9', { x: 120, y: 24 }],
71
+ ['vim-2012/1.17', { x: 36, y: 132 }],
72
+ ['vim-2012/1.18', { x: 120, y: 144 }],
73
+ ['vim-2012/1.12', { x: 204, y: 132 }],
74
+ ]);
75
+
76
+ onMounted(() => {
77
+ const svg = svgRef.value;
78
+ if (!svg) return;
79
+ if (props.kind === 'partitive') {
80
+ drawRakeBundles(svg, POS, {
81
+ isDark: uiStore.isDark,
82
+ color: uiStore.isDark ? '#2dd4bf' : '#0d9488',
83
+ isMuted: false,
84
+ relations: [PARTITIVE],
85
+ });
86
+ } else {
87
+ drawGenericPipes(svg, GENERIC_POS, {
88
+ isDark: uiStore.isDark,
89
+ color: uiStore.isDark ? '#fbbf24' : '#b45309',
90
+ isMuted: false,
91
+ relations: [GENERIC],
92
+ locale: 'eng',
93
+ });
94
+ }
95
+ });
96
+ </script>
97
+
98
+ <template>
99
+ <svg
100
+ ref="svgRef"
101
+ class="hyperedge-diagram"
102
+ viewBox="0 0 240 168"
103
+ xmlns="http://www.w3.org/2000/svg"
104
+ aria-hidden="true"
105
+ />
106
+ </template>
107
+
108
+ <style scoped>
109
+ .hyperedge-diagram {
110
+ width: 100%;
111
+ max-width: 240px;
112
+ height: auto;
113
+ display: block;
114
+ }
115
+ </style>
@@ -1068,7 +1068,7 @@ const legendItems = computed(() => {
1068
1068
  if (partitiveCount > 0) {
1069
1069
  typeMap.set('__partitive__', {
1070
1070
  type: '__partitive__',
1071
- label: 'Partitive Relation',
1071
+ label: 'partitive relation',
1072
1072
  color: uiStore.isDark ? '#2dd4bf' : '#0d9488',
1073
1073
  count: partitiveCount,
1074
1074
  });
@@ -1078,7 +1078,7 @@ const legendItems = computed(() => {
1078
1078
  if (genericCount > 0) {
1079
1079
  typeMap.set('__generic__', {
1080
1080
  type: '__generic__',
1081
- label: 'General Relation',
1081
+ label: 'general relation',
1082
1082
  color: uiStore.isDark ? '#fbbf24' : '#b45309',
1083
1083
  count: genericCount,
1084
1084
  });
@@ -1405,16 +1405,23 @@ defineExpose({ navigate });
1405
1405
  <div v-if="legendItems.length" class="sp-section">
1406
1406
  <div class="sp-section-label">{{ t('sphere.relationTypes') }}</div>
1407
1407
  <div class="sp-legend-grid">
1408
- <button v-for="item in legendItems" :key="item.type" class="sp-legend-item"
1408
+ <div v-for="item in legendItems" :key="item.type"
1409
+ class="sp-legend-item"
1409
1410
  :class="{ muted: isTypeMuted(item.type), 'is-partitive': item.type === '__partitive__' }"
1410
- @click="toggleType(item.type)"
1411
- :title="item.type">
1412
- <span v-if="item.type === '__partitive__'" class="sp-swatch sp-swatch-rake"
1413
- :style="{ '--rake-color': isTypeMuted(item.type) ? '#b8b9cc' : item.color }"></span>
1414
- <span v-else class="sp-swatch" :style="{ background: isTypeMuted(item.type) ? '#b8b9cc' : item.color }"></span>
1415
- <span>{{ item.label }}</span>
1416
- <span class="sp-count">{{ item.count }}</span>
1417
- </button>
1411
+ >
1412
+ <button class="sp-legend-toggle"
1413
+ @click="toggleType(item.type)"
1414
+ :title="item.type"
1415
+ :aria-label="`${t('sphere.toggleMute')}: ${item.label}`">
1416
+ <span v-if="item.type === '__partitive__'" class="sp-swatch sp-swatch-rake"
1417
+ :style="{ '--rake-color': isTypeMuted(item.type) ? '#b8b9cc' : item.color }"></span>
1418
+ <span v-else class="sp-swatch" :style="{ background: isTypeMuted(item.type) ? '#b8b9cc' : item.color }"></span>
1419
+ <span class="sp-count">{{ item.count }}</span>
1420
+ </button>
1421
+ <router-link class="sp-legend-label"
1422
+ :to="{ name: 'learn-relationships', hash: '#' + item.type }"
1423
+ :title="t('sphere.learnAbout') + ': ' + item.label">{{ item.label }}</router-link>
1424
+ </div>
1418
1425
  </div>
1419
1426
  </div>
1420
1427
 
@@ -1653,12 +1660,22 @@ defineExpose({ navigate });
1653
1660
  /* Type legend */
1654
1661
  .sp-legend-grid { display: flex; flex-direction: column; gap: 2px; }
1655
1662
  .sp-legend-item {
1656
- display: flex; align-items: center; gap: 8px; padding: 3px 6px; border-radius: 3px;
1657
- cursor: pointer; border: 1px solid transparent; background: transparent; text-align: left;
1663
+ display: flex; align-items: center; gap: 6px; padding: 3px 6px; border-radius: 3px;
1664
+ border: 1px solid transparent; background: transparent; text-align: left;
1658
1665
  width: 100%; font-family: inherit; font-size: 11px; color: var(--ink-mute);
1659
1666
  }
1660
1667
  .sp-legend-item:hover { background: var(--rule); }
1661
1668
  .sp-legend-item.muted { opacity: 0.35; }
1669
+ .sp-legend-toggle {
1670
+ display: inline-flex; align-items: center; gap: 6px; flex-shrink: 0;
1671
+ cursor: pointer; border: none; background: transparent; padding: 0;
1672
+ font: inherit; color: inherit;
1673
+ }
1674
+ .sp-legend-label {
1675
+ color: inherit; text-decoration: none; flex: 1; min-width: 0;
1676
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
1677
+ }
1678
+ .sp-legend-label:hover { text-decoration: underline; }
1662
1679
  .sp-swatch { width: 18px; height: 2px; flex-shrink: 0; border-radius: 1px; }
1663
1680
  /* Rake swatch — small bracket icon signaling a PartitiveRelation hyperedge
1664
1681
  (not a single binary edge). Two parallel solid lines (close-set double)
@@ -0,0 +1,71 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * DimensionCard — structured breakdown of a single dimension along
4
+ * which hyperedge members vary. Used on /learn/relationships for the
5
+ * partitive dimensions (presence, count, delimiting).
6
+ *
7
+ * Renders one row per value showing:
8
+ * - the raw value (machine key)
9
+ * - the human label
10
+ * - a short meaning (what it encodes)
11
+ * - a small visual representation (slot — typically a mini SVG)
12
+ *
13
+ * The user's specific concern: "individually show what dimensions
14
+ * there are and how lines look like". This component is the answer.
15
+ */
16
+ export interface DimensionValue {
17
+ value: string;
18
+ label: string;
19
+ meaning: string;
20
+ }
21
+
22
+ defineProps<{
23
+ id?: string;
24
+ name: string;
25
+ isoReference?: string;
26
+ /** Plain-language summary of what this dimension captures. */
27
+ summary: string;
28
+ values: readonly DimensionValue[];
29
+ }>();
30
+ </script>
31
+
32
+ <template>
33
+ <article :id="id" class="dimension-card card p-5 scroll-mt-20">
34
+ <header class="flex items-baseline justify-between gap-3 mb-2 flex-wrap">
35
+ <h4 class="text-base font-semibold text-ink-800 dark:text-ink-50">{{ name }}</h4>
36
+ <span v-if="isoReference" class="text-xs font-mono text-ink-400 dark:text-ink-500">
37
+ {{ isoReference }}
38
+ </span>
39
+ </header>
40
+ <p class="text-sm text-ink-600 dark:text-ink-200 mb-4">{{ summary }}</p>
41
+ <ul class="dimension-values space-y-3">
42
+ <li v-for="v in values" :key="v.value" class="dimension-value-row">
43
+ <div class="flex items-center gap-3">
44
+ <div class="dimension-visual">
45
+ <slot name="visual" :value="v.value" />
46
+ </div>
47
+ <div class="flex-1 min-w-0">
48
+ <div class="flex items-baseline gap-2 flex-wrap">
49
+ <span class="font-medium text-ink-800 dark:text-ink-100">{{ v.label }}</span>
50
+ <code class="text-xs text-ink-400 dark:text-ink-500 font-mono">{{ v.value }}</code>
51
+ </div>
52
+ <p class="text-sm text-ink-600 dark:text-ink-300 mt-0.5">{{ v.meaning }}</p>
53
+ </div>
54
+ </div>
55
+ </li>
56
+ </ul>
57
+ </article>
58
+ </template>
59
+
60
+ <style scoped>
61
+ .dimension-card {
62
+ scroll-margin-top: 5rem;
63
+ }
64
+ .dimension-visual {
65
+ flex-shrink: 0;
66
+ width: 48px;
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ }
71
+ </style>
@@ -0,0 +1,59 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * LearnCard — the standard topic container across all /learn pages.
4
+ *
5
+ * Layout: title + ISO reference on top, then a flexible body. Optional
6
+ * named slots:
7
+ * - `diagram` — a small visual (live SVG, badge samples, etc.)
8
+ * - `example` — a concrete real-world example
9
+ * Optional `learnMoreUrl` renders a "Learn more on glossarist.org"
10
+ * link at the bottom — keeps concept-browser reader-focused while
11
+ * bridging to the canonical author docs on glossarist.org.
12
+ *
13
+ * Anchored at the `id` prop so URLs like `/learn/relationships#partitive`
14
+ * deep-link to the right card.
15
+ */
16
+ defineProps<{
17
+ id?: string;
18
+ title: string;
19
+ isoReference?: string;
20
+ learnMoreUrl?: string;
21
+ learnMoreLabel?: string;
22
+ }>();
23
+ </script>
24
+
25
+ <template>
26
+ <article :id="id" class="learn-card card p-5 scroll-mt-20">
27
+ <header class="flex items-baseline justify-between gap-3 mb-3 flex-wrap">
28
+ <h3 class="text-lg font-semibold text-ink-800 dark:text-ink-50">{{ title }}</h3>
29
+ <span v-if="isoReference" class="text-xs font-mono text-ink-400 dark:text-ink-500">
30
+ {{ isoReference }}
31
+ </span>
32
+ </header>
33
+ <div v-if="$slots.diagram" class="learn-card-diagram mb-3">
34
+ <slot name="diagram" />
35
+ </div>
36
+ <div class="learn-card-body text-sm text-ink-600 dark:text-ink-200">
37
+ <slot />
38
+ </div>
39
+ <div v-if="$slots.example" class="learn-card-example mt-3 pt-3 border-t border-ink-100 dark:border-ink-700 text-xs text-ink-500 dark:text-ink-400">
40
+ <slot name="example" />
41
+ </div>
42
+ <footer v-if="learnMoreUrl" class="learn-card-footer mt-3 text-xs">
43
+ <a :href="learnMoreUrl" target="_blank" rel="noopener"
44
+ class="text-ink-500 dark:text-ink-400 hover:text-ink-800 dark:hover:text-ink-100 underline">
45
+ {{ learnMoreLabel ?? 'Learn more on glossarist.org' }} ↗
46
+ </a>
47
+ </footer>
48
+ </article>
49
+ </template>
50
+
51
+ <style scoped>
52
+ .learn-card {
53
+ scroll-margin-top: 5rem;
54
+ }
55
+ .learn-card-diagram {
56
+ display: flex;
57
+ justify-content: center;
58
+ }
59
+ </style>
@@ -35,6 +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
39
  ] as const;
39
40
 
40
41
  export type BuiltinPageType = typeof PAGE_TYPES[number]['type'];
@@ -12,6 +12,110 @@ nav.about: About
12
12
  nav.sources: Sources
13
13
  nav.navigation: Navigation
14
14
  nav.datasets: Datasets
15
+ nav.relationTypes: Relation Types
16
+ nav.learn: Learn
17
+
18
+ # Learn section (reader-focused teaching)
19
+ learn.intro: This section explains what you see on this site and what each label means. For canonical methodology docs (authoring datasets, ISO alignment), see glossarist.org.
20
+ learn.glossaristOrgNote: Methodology documentation for dataset authors lives on glossarist.org.
21
+ learn.example: Example
22
+ learn.learnMore: Learn more on glossarist.org
23
+ learn.relationships.title: Relationships
24
+ learn.relationships.short: How to read the bilateral edges, partitive rake, and general pipe-and-thread.
25
+ learn.relationships.intro: Every line and label on the Relation Sphere, the detail page, and the graph view maps to a typed semantic relation. This page explains what each kind means and how to read its visual encoding.
26
+ learn.relationships.bilateralSection: Bilateral relations (one-to-one)
27
+ learn.relationships.bilateralIntro: One-to-one typed relations between two concepts. Drawn as straight colored lines with arrowheads in the sphere.
28
+ learn.relationships.partitiveSection: Partitive hyperedge (one-to-many)
29
+ learn.relationships.partitiveTitle: partitive relation
30
+ learn.relationships.partitiveBody: A whole/part decomposition. The comprehensive concept is the whole; each member is a part. Rendered as a rake — a stem from the comprehensive to a perpendicular spine, with teeth dropping to each partitive member.
31
+ learn.relationships.generalSection: General hyperedge (one-to-many)
32
+ learn.relationships.generalTitle: general relation
33
+ learn.relationships.generalBody: A genus/species decomposition. The comprehensive concept is the genus; each member is a coordinate species differing along a shared dimension. Rendered as a tree branch — a thick parent pipe from the comprehensive to a middle node, with thin threads radiating to each member.
34
+ learn.relationships.criterionTitle: Criterion of subdivision
35
+ learn.relationships.criterionBody: "The dimension along which coordinate concepts differ. Shown once on the parent pipe body. Optional: when no criterion is described, no label is drawn."
36
+ learn.relationships.thickThinTitle: Thick/thin hierarchy
37
+ learn.relationships.thickThinBody: ISO 704 traditionally uses thicker lines to mark different criteria of subdivision (multidimensionality). The pipe-and-thread honors this with a thick parent pipe (3.2px) and thinner child threads (1.0px) — the tree-branch metaphor (trunk vs twigs).
38
+ learn.dimensions.sectionTitle: Partitive dimensions (how to read the rake teeth)
39
+ learn.dimensions.presence.title: Presence
40
+ learn.dimensions.presence.summary: Whether the part must be present (compulsory) or may be absent (optional).
41
+ learn.dimensions.presence.required.label: required
42
+ learn.dimensions.presence.required.meaning: The part must be present. Drawn as a solid line.
43
+ learn.dimensions.presence.optional.label: optional
44
+ learn.dimensions.presence.optional.meaning: The part may be absent. Drawn as a dashed line.
45
+ learn.dimensions.count.title: Count
46
+ learn.dimensions.count.summary: How many instances of the part occur in the whole.
47
+ learn.dimensions.count.exactly_one.label: exactly one
48
+ learn.dimensions.count.exactly_one.meaning: One and only one. Drawn as a single line.
49
+ learn.dimensions.count.at_least_one.label: at least one
50
+ learn.dimensions.count.at_least_one.meaning: One or more. Drawn as a double line, the secondary dashed ("possibly more").
51
+ learn.dimensions.count.multiple.label: multiple
52
+ learn.dimensions.count.multiple.meaning: Two or more. Drawn as a double line, both the same style as the primary.
53
+ learn.dimensions.delimiting.title: Delimiting
54
+ learn.dimensions.delimiting.summary: Whether the part reflects a delimiting characteristic — one that distinguishes the whole from coordinate concepts.
55
+ learn.dimensions.delimiting.false.label: not delimiting
56
+ learn.dimensions.delimiting.false.meaning: An ordinary part. Drawn at normal stroke width (1.5px).
57
+ learn.dimensions.delimiting.true.label: delimiting
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.designations.title: Designations
60
+ learn.designations.short: Terms and symbols — preferred, admitted, deprecated, full form, abbreviation, acronym, initialism.
61
+ learn.designations.intro: Concepts are designated by terms, proper names, and symbols. This page explains the vocabulary you'll see on each concept page.
62
+ learn.designations.normativeSection: Normative status
63
+ learn.designations.normativeIntro: Each term carries a normative rating from the standardizing body. There can be MORE THAN ONE of each rating — preferred terms aren't unique.
64
+ learn.designations.preferred.label: preferred term
65
+ learn.designations.preferred.meaning: The primary term for the concept, rated by a standardizing body.
66
+ learn.designations.preferred.multipleNote: There can be more than one preferred term.
67
+ learn.designations.admitted.label: admitted term
68
+ learn.designations.admitted.meaning: A synonym for the preferred term, but not rated as preferred.
69
+ learn.designations.admitted.multipleNote: There can be more than one admitted term.
70
+ learn.designations.deprecated.label: deprecated term
71
+ learn.designations.deprecated.meaning: A synonym for the preferred term, but rated as undesired.
72
+ learn.designations.deprecated.multipleNote: There can be more than one deprecated term.
73
+ learn.designations.kindsSection: Kinds and forms of term
74
+ learn.designations.fullForm.label: full form
75
+ learn.designations.fullForm.meaning: The complete representation of a designation.
76
+ learn.designations.abbreviatedForm.label: abbreviated form
77
+ learn.designations.abbreviatedForm.meaning: A representation resulting from the omission of any part of the full form.
78
+ learn.designations.abbreviatedForm.multipleNote: For some full forms, two or more abbreviated forms may exist.
79
+ learn.designations.borrowed.label: borrowed term
80
+ learn.designations.borrowed.meaning: A term taken from another language or from another domain.
81
+ learn.designations.variant.label: variant
82
+ learn.designations.variant.meaning: One of the alternative forms of a designation (e.g. spelling differences across regions).
83
+ learn.designations.typesSection: Designation types
84
+ learn.designations.typesIntro: Three top-level types — terms, proper names, and symbols. Within terms, abbreviations split further into acronyms vs initialisms based on how they're pronounced.
85
+ learn.designations.term.label: term (expression)
86
+ learn.designations.term.meaning: A spoken or written designation — the most common type.
87
+ learn.designations.acronym.label: acronym
88
+ learn.designations.acronym.meaning: An abbreviated form pronounced as a single word.
89
+ learn.designations.initialism.label: initialism
90
+ learn.designations.initialism.meaning: An abbreviated form pronounced letter by letter.
91
+ learn.designations.symbol.label: symbol
92
+ learn.designations.symbol.meaning: A graphical or mathematical designation governed by ISO 80000 / IEC 60027.
93
+ learn.statuses.title: Statuses
94
+ learn.statuses.short: Entry status (valid/withdrawn/draft), normative status, source status — what each badge means.
95
+ learn.statuses.intro: Status badges appear throughout the site. This page is a quick reference for what each one means.
96
+ learn.statuses.entrySection: Entry status
97
+ learn.statuses.entry.valid.label: valid
98
+ learn.statuses.entry.valid.meaning: The concept entry is currently in force.
99
+ learn.statuses.entry.withdrawn.label: withdrawn
100
+ learn.statuses.entry.withdrawn.meaning: The entry has been removed from active use.
101
+ learn.statuses.entry.draft.label: draft
102
+ learn.statuses.entry.draft.meaning: The entry is under development and not yet finalized.
103
+ learn.statuses.entry.superseded.label: superseded
104
+ learn.statuses.entry.superseded.meaning: The entry has been replaced by a newer one.
105
+ learn.statuses.normativeSection: Normative status
106
+ learn.statuses.normativeTitle: preferred / admitted / deprecated
107
+ learn.statuses.normativeSummary: Per-term rating (preferred, admitted, deprecated). Each rating can apply to more than one term per concept.
108
+ learn.statuses.normativeGoDeeper: See designation types for examples
109
+ learn.statuses.sourceSection: Source status
110
+ learn.statuses.sourceIntro: Each citation carries a status describing how faithfully it reproduces the source.
111
+ learn.statuses.source.identical.label: identical
112
+ learn.statuses.source.identical.meaning: The definition matches the source verbatim.
113
+ learn.statuses.source.modified.label: modified
114
+ learn.statuses.source.modified.meaning: The definition is adapted from the source with editorial changes.
115
+ learn.statuses.source.restyled.label: restyled
116
+ learn.statuses.source.restyled.meaning: The definition preserves the source meaning but rewords it heavily.
117
+ learn.statuses.source.lineage.label: lineage
118
+ learn.statuses.source.lineage.meaning: The source is the historical ancestor of this concept, not a direct citation.
15
119
 
16
120
  # Home page
17
121
  home.search: Search
@@ -213,6 +317,8 @@ sphere.tight: tight
213
317
  sphere.loose: loose
214
318
  sphere.redraw: Redraw
215
319
  sphere.focus: FOCUS
320
+ sphere.toggleMute: Toggle visibility
321
+ sphere.learnAbout: Learn about this relation type
216
322
  concept.detailView: Detail
217
323
  concept.relationSphere: Relation Sphere
218
324
  concept.permalink: Permalink