@uniweb/runtime 0.8.7 → 0.8.9

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/src/Ref/Ref.jsx DELETED
@@ -1,139 +0,0 @@
1
- /**
2
- * Cross-reference renderer. The framework registers `<Ref>` as a default
3
- * inset component so the `[#id]` author syntax (recognized by
4
- * content-reader) compiles to `inset_ref { component: 'Ref', key: <id> }`
5
- * and falls through to this renderer regardless of which foundation is
6
- * active.
7
- *
8
- * Resolution flow at render time:
9
- * 1. Read `block.website.xref.entries[id]` — the per-document
10
- * registry the framework populated from {#id} attributes.
11
- * 2. Pick the active xref-style preset (foundation default + document
12
- * override) and the kind metadata for the registered entry.
13
- * 3. Render via the style's label + counter + locator.
14
- *
15
- * Multi-ref clusters (`[#a;#b]`) split on `;` and render same-kind
16
- * groups using the style's `labelPlural`. Mixed-kind clusters fall
17
- * back to comma-separated singular rendering with a console warning.
18
- *
19
- * Missing ids render `[?<id-with-typo>]` so the failing key is visible
20
- * in the output — easier to debug than the bare `[?]` we use for
21
- * missing cite keys.
22
- *
23
- * @module @uniweb/kit/Ref
24
- */
25
-
26
- import React from 'react'
27
- import { resolveXrefStyle, getKindMeta } from '../xref-styles.js'
28
-
29
- function splitKeys(raw) {
30
- return String(raw || '')
31
- .split(';')
32
- .map((k) => k.trim().replace(/^#/, ''))
33
- .filter(Boolean)
34
- }
35
-
36
- function formatLocator(params) {
37
- const { page, locator, label = 'page' } = params || {}
38
- const value = page || locator
39
- if (!value) return ''
40
- const labels = {
41
- page: 'p.',
42
- chapter: 'chap.',
43
- section: '§',
44
- paragraph: '¶',
45
- }
46
- const lab = labels[label] || `${label}.`
47
- return ` (${lab} ${value})`
48
- }
49
-
50
- function renderEntry(entry, kindMeta) {
51
- // Bare "Figure 3" / "§3.2" — label + separator + counter.
52
- const label = kindMeta?.label || ''
53
- const sep = kindMeta?.sep ?? ' '
54
- const counter = entry.counterText
55
- return label ? `${label}${sep}${counter}` : counter
56
- }
57
-
58
- function renderGroupSameKind(entries, kindMeta) {
59
- if (entries.length === 1) {
60
- return renderEntry(entries[0], kindMeta)
61
- }
62
- // Plural label + comma-separated counters with " and " before the last.
63
- const label = kindMeta?.labelPlural || kindMeta?.label || ''
64
- const sep = kindMeta?.sep ?? ' '
65
- const counters = entries.map((e) => e.counterText)
66
- let body
67
- if (counters.length === 2) {
68
- body = counters.join(' and ')
69
- } else {
70
- body = counters.slice(0, -1).join(', ') + ', and ' + counters[counters.length - 1]
71
- }
72
- return label ? `${label}${sep}${body}` : body
73
- }
74
-
75
- export function Ref({ params, block }) {
76
- const website = block?.website
77
- const xref = website?.xref || website?.config?.xref || null
78
- const entries = xref?.entries || {}
79
- const styleName = website?.config?.book?.xrefStyle || 'humanities'
80
- const style = resolveXrefStyle(styleName, website?.config)
81
-
82
- const ids = splitKeys(params?.key)
83
- if (ids.length === 0) {
84
- return <span className="xref xref--missing" title="No id">[?]</span>
85
- }
86
-
87
- const resolved = ids.map((id) => {
88
- const entry = entries[id]
89
- return entry ? { id, entry, kindMeta: getKindMeta(style, entry.kind) } : { id, missing: true }
90
- })
91
-
92
- // Single missing-id fast path — rich placeholder with the failing key.
93
- if (resolved.length === 1 && resolved[0].missing) {
94
- return (
95
- <span className="xref xref--missing" title={`Missing label: ${resolved[0].id}`}>
96
- [?{resolved[0].id}]
97
- </span>
98
- )
99
- }
100
-
101
- // Group consecutive entries by kind. Mixed-kind clusters fall back to
102
- // comma-separated singular rendering.
103
- const allKinds = resolved.filter((r) => !r.missing).map((r) => r.entry.kind)
104
- const sameKind = allKinds.every((k) => k === allKinds[0])
105
-
106
- const locator = formatLocator(params)
107
-
108
- if (!sameKind) {
109
- if (typeof console !== 'undefined') {
110
- // eslint-disable-next-line no-console
111
- console.warn(
112
- `[xref] mixed-kind cluster (${[...new Set(allKinds)].join(', ')}) — falling back to comma-separated rendering`,
113
- )
114
- }
115
- const parts = resolved.map((r) =>
116
- r.missing
117
- ? `[?${r.id}]`
118
- : renderEntry(r.entry, r.kindMeta),
119
- )
120
- return <span className="xref">{parts.join(', ')}{locator}</span>
121
- }
122
-
123
- // Same-kind path — group, render with labelPlural when multiple.
124
- const onlyResolved = resolved.filter((r) => !r.missing)
125
- const text = onlyResolved.length > 0
126
- ? renderGroupSameKind(
127
- onlyResolved.map((r) => r.entry),
128
- onlyResolved[0].kindMeta,
129
- )
130
- : ''
131
-
132
- // Append placeholders for any missing keys at the end of the cluster.
133
- const missingTail = resolved.filter((r) => r.missing).map((r) => `[?${r.id}]`).join(', ')
134
- const body = [text, missingTail].filter(Boolean).join(', ')
135
-
136
- return <span className="xref">{body}{locator}</span>
137
- }
138
-
139
- export default Ref
package/src/Ref/index.js DELETED
@@ -1,2 +0,0 @@
1
- export { Ref } from './Ref.jsx'
2
- export { default } from './Ref.jsx'
@@ -1,103 +0,0 @@
1
- /**
2
- * Cross-reference style catalog.
3
- *
4
- * Cross-references are framework-managed (parallel to citestyle's nine
5
- * citation styles, but framework-internal because there's no CSL-for-
6
- * xrefs standard). Foundations pick a preset; documents may override
7
- * per-kind.
8
- *
9
- * Each preset is a map from kind name to:
10
- * {
11
- * label, // singular label, e.g. "Figure"
12
- * labelPlural, // plural label, e.g. "Figures"
13
- * counter, // 'arabic' | 'hierarchical' | null (no counter)
14
- * sep, // separator between label and counter ("" | " ")
15
- * }
16
- *
17
- * The catalog ships four presets:
18
- * - humanities (default — "Figure 3", "§3.2", "Equation 1")
19
- * - engineering ("Fig. 3", "Sec. 3.2", "Eq. 1")
20
- * - german ("Abb. 3", "Abschn. 3.2", "Gl. 1")
21
- * - plain (counters only — "3", "3.2", "1")
22
- *
23
- * Foundations override the preset by declaring `xref.kinds` in
24
- * foundation.js (additive: new kinds and overrides for existing kinds).
25
- * Documents override per-kind via `book.xref.<kind>:` in document.yml.
26
- */
27
-
28
- const HUMANITIES = {
29
- figure: { label: 'Figure', labelPlural: 'Figures', counter: 'arabic', sep: ' ' },
30
- equation: { label: 'Equation', labelPlural: 'Equations', counter: 'arabic', sep: ' ' },
31
- section: { label: '§', labelPlural: '§§', counter: 'hierarchical', sep: '' },
32
- table: { label: 'Table', labelPlural: 'Tables', counter: 'arabic', sep: ' ' },
33
- }
34
-
35
- const ENGINEERING = {
36
- figure: { label: 'Fig.', labelPlural: 'Figs.', counter: 'arabic', sep: ' ' },
37
- equation: { label: 'Eq.', labelPlural: 'Eqs.', counter: 'arabic', sep: ' ' },
38
- section: { label: 'Sec.', labelPlural: 'Secs.', counter: 'hierarchical', sep: ' ' },
39
- table: { label: 'Tab.', labelPlural: 'Tabs.', counter: 'arabic', sep: ' ' },
40
- }
41
-
42
- const GERMAN = {
43
- figure: { label: 'Abb.', labelPlural: 'Abb.', counter: 'arabic', sep: ' ' },
44
- equation: { label: 'Gl.', labelPlural: 'Gl.', counter: 'arabic', sep: ' ' },
45
- section: { label: 'Abschn.', labelPlural: 'Abschn.', counter: 'hierarchical', sep: ' ' },
46
- table: { label: 'Tab.', labelPlural: 'Tab.', counter: 'arabic', sep: ' ' },
47
- }
48
-
49
- const PLAIN = {
50
- figure: { label: '', labelPlural: '', counter: 'arabic', sep: '' },
51
- equation: { label: '', labelPlural: '', counter: 'arabic', sep: '' },
52
- section: { label: '', labelPlural: '', counter: 'hierarchical', sep: '' },
53
- table: { label: '', labelPlural: '', counter: 'arabic', sep: '' },
54
- }
55
-
56
- export const XREF_STYLES = {
57
- humanities: HUMANITIES,
58
- engineering: ENGINEERING,
59
- german: GERMAN,
60
- plain: PLAIN,
61
- }
62
-
63
- export const DEFAULT_XREF_STYLE = 'humanities'
64
-
65
- /**
66
- * Resolve the active xref-style for a document. Reads:
67
- * - `book.xrefStyle:` in document config (preset name).
68
- * - `book.xref.<kind>:` per-kind overrides on top of the preset.
69
- * - Foundation-declared kinds (`foundation.xref.kinds`) extend the
70
- * preset with whatever the foundation provides.
71
- *
72
- * Returns a plain map keyed by kind name; per-kind values are merged
73
- * objects (preset + foundation extensions + document overrides).
74
- */
75
- export function resolveXrefStyle(presetName, config) {
76
- const preset = XREF_STYLES[presetName] || XREF_STYLES[DEFAULT_XREF_STYLE]
77
- const merged = { ...preset }
78
-
79
- // Foundation extensions: kinds declared by the foundation (e.g.
80
- // theorem, lemma, proof for a math foundation).
81
- const foundationKinds = globalThis.uniweb?.foundationConfig?.xref?.kinds
82
- if (foundationKinds && typeof foundationKinds === 'object') {
83
- for (const [kind, meta] of Object.entries(foundationKinds)) {
84
- merged[kind] = { ...merged[kind], ...meta }
85
- }
86
- }
87
-
88
- // Document overrides: `book.xref.<kind>:` granular tweaks.
89
- const docOverrides = config?.book?.xref || config?.xref || null
90
- if (docOverrides && typeof docOverrides === 'object') {
91
- for (const [kind, meta] of Object.entries(docOverrides)) {
92
- if (meta && typeof meta === 'object') {
93
- merged[kind] = { ...merged[kind], ...meta }
94
- }
95
- }
96
- }
97
-
98
- return merged
99
- }
100
-
101
- export function getKindMeta(style, kind) {
102
- return style?.[kind] || null
103
- }