@uniweb/scholar 0.2.0 → 0.2.2
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/README.md +42 -32
- package/package.json +19 -6
- package/src/bibliography/formatters/index.js +62 -23
- package/src/bibliography/index.js +8 -29
- package/src/bibliography/normalize.js +234 -0
- package/src/bibliography/parsers/index.js +35 -1
- package/src/citations/Bibliography.jsx +26 -54
- package/src/citations/Citation.jsx +35 -145
- package/src/citations/CitationProvider.jsx +83 -85
- package/src/components/AuthorList.jsx +65 -40
- package/src/components/BibtexBlock.jsx +1 -1
- package/src/components/CiteButton.jsx +78 -45
- package/src/components/DoiLink.jsx +27 -21
- package/src/index.js +15 -5
- package/src/math/Equation.jsx +45 -75
- package/src/math/index.js +19 -4
- package/src/bibliography/formatters/apa.js +0 -352
- package/src/bibliography/formatters/chicago.js +0 -339
- package/src/bibliography/formatters/ieee.js +0 -332
- package/src/bibliography/formatters/mla.js +0 -335
- package/src/bibliography/parsers/bibtex.js +0 -405
- package/src/bibliography/utils/authors.js +0 -277
- package/src/bibliography/utils/dates.js +0 -228
- package/src/bibliography/utils/index.js +0 -8
- package/src/math/Math.jsx +0 -115
- package/src/math/katex-loader.js +0 -105
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Date Formatting Utilities
|
|
3
|
-
*
|
|
4
|
-
* Functions for formatting dates in various citation styles.
|
|
5
|
-
*
|
|
6
|
-
* @module @uniweb/scholar/bibliography/utils/dates
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Month names for formatting
|
|
11
|
-
*/
|
|
12
|
-
const MONTHS_FULL = [
|
|
13
|
-
'January',
|
|
14
|
-
'February',
|
|
15
|
-
'March',
|
|
16
|
-
'April',
|
|
17
|
-
'May',
|
|
18
|
-
'June',
|
|
19
|
-
'July',
|
|
20
|
-
'August',
|
|
21
|
-
'September',
|
|
22
|
-
'October',
|
|
23
|
-
'November',
|
|
24
|
-
'December',
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
const MONTHS_SHORT = [
|
|
28
|
-
'Jan.',
|
|
29
|
-
'Feb.',
|
|
30
|
-
'Mar.',
|
|
31
|
-
'Apr.',
|
|
32
|
-
'May',
|
|
33
|
-
'June',
|
|
34
|
-
'July',
|
|
35
|
-
'Aug.',
|
|
36
|
-
'Sep.',
|
|
37
|
-
'Oct.',
|
|
38
|
-
'Nov.',
|
|
39
|
-
'Dec.',
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Parse a date from various formats
|
|
44
|
-
*
|
|
45
|
-
* @param {string|number|Date|Object} date - Date input
|
|
46
|
-
* @returns {{ year?: number, month?: number, day?: number }}
|
|
47
|
-
*/
|
|
48
|
-
export function parseDate(date) {
|
|
49
|
-
if (!date) return {}
|
|
50
|
-
|
|
51
|
-
// Already structured
|
|
52
|
-
if (typeof date === 'object' && !(date instanceof Date)) {
|
|
53
|
-
return {
|
|
54
|
-
year: date.year ?? date.Year,
|
|
55
|
-
month: date.month ?? date.Month,
|
|
56
|
-
day: date.day ?? date.Day,
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// JavaScript Date
|
|
61
|
-
if (date instanceof Date) {
|
|
62
|
-
return {
|
|
63
|
-
year: date.getFullYear(),
|
|
64
|
-
month: date.getMonth() + 1,
|
|
65
|
-
day: date.getDate(),
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Number (year only)
|
|
70
|
-
if (typeof date === 'number') {
|
|
71
|
-
return { year: date }
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// String parsing
|
|
75
|
-
const str = String(date).trim()
|
|
76
|
-
|
|
77
|
-
// ISO format: YYYY-MM-DD
|
|
78
|
-
const isoMatch = str.match(/^(\d{4})(?:-(\d{1,2})(?:-(\d{1,2}))?)?/)
|
|
79
|
-
if (isoMatch) {
|
|
80
|
-
return {
|
|
81
|
-
year: parseInt(isoMatch[1], 10),
|
|
82
|
-
month: isoMatch[2] ? parseInt(isoMatch[2], 10) : undefined,
|
|
83
|
-
day: isoMatch[3] ? parseInt(isoMatch[3], 10) : undefined,
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Year only
|
|
88
|
-
const yearMatch = str.match(/^\d{4}$/)
|
|
89
|
-
if (yearMatch) {
|
|
90
|
-
return { year: parseInt(str, 10) }
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Month Year: "January 2024", "Jan 2024"
|
|
94
|
-
const monthYearMatch = str.match(
|
|
95
|
-
/^(\w+)\s+(\d{4})$/
|
|
96
|
-
)
|
|
97
|
-
if (monthYearMatch) {
|
|
98
|
-
const monthName = monthYearMatch[1].toLowerCase()
|
|
99
|
-
const monthIndex = MONTHS_FULL.findIndex(
|
|
100
|
-
(m) => m.toLowerCase().startsWith(monthName.slice(0, 3))
|
|
101
|
-
)
|
|
102
|
-
return {
|
|
103
|
-
year: parseInt(monthYearMatch[2], 10),
|
|
104
|
-
month: monthIndex !== -1 ? monthIndex + 1 : undefined,
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
return {}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Format date for APA style
|
|
113
|
-
*
|
|
114
|
-
* @param {Object} date - Parsed date object
|
|
115
|
-
* @returns {string}
|
|
116
|
-
*/
|
|
117
|
-
export function formatApaDate(date) {
|
|
118
|
-
const parsed = parseDate(date)
|
|
119
|
-
|
|
120
|
-
if (!parsed.year) return 'n.d.'
|
|
121
|
-
|
|
122
|
-
if (parsed.month && parsed.day) {
|
|
123
|
-
return `${parsed.year}, ${MONTHS_FULL[parsed.month - 1]} ${parsed.day}`
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (parsed.month) {
|
|
127
|
-
return `${parsed.year}, ${MONTHS_FULL[parsed.month - 1]}`
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return String(parsed.year)
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Format date for MLA style
|
|
135
|
-
*
|
|
136
|
-
* @param {Object} date - Parsed date object
|
|
137
|
-
* @returns {string}
|
|
138
|
-
*/
|
|
139
|
-
export function formatMlaDate(date) {
|
|
140
|
-
const parsed = parseDate(date)
|
|
141
|
-
|
|
142
|
-
if (!parsed.year) return ''
|
|
143
|
-
|
|
144
|
-
if (parsed.month && parsed.day) {
|
|
145
|
-
return `${parsed.day} ${MONTHS_SHORT[parsed.month - 1]} ${parsed.year}`
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (parsed.month) {
|
|
149
|
-
return `${MONTHS_SHORT[parsed.month - 1]} ${parsed.year}`
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
return String(parsed.year)
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Format date for Chicago style
|
|
157
|
-
*
|
|
158
|
-
* @param {Object} date - Parsed date object
|
|
159
|
-
* @returns {string}
|
|
160
|
-
*/
|
|
161
|
-
export function formatChicagoDate(date) {
|
|
162
|
-
const parsed = parseDate(date)
|
|
163
|
-
|
|
164
|
-
if (!parsed.year) return 'n.d.'
|
|
165
|
-
|
|
166
|
-
if (parsed.month && parsed.day) {
|
|
167
|
-
return `${MONTHS_FULL[parsed.month - 1]} ${parsed.day}, ${parsed.year}`
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (parsed.month) {
|
|
171
|
-
return `${MONTHS_FULL[parsed.month - 1]} ${parsed.year}`
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return String(parsed.year)
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Format date for IEEE style
|
|
179
|
-
*
|
|
180
|
-
* @param {Object} date - Parsed date object
|
|
181
|
-
* @returns {string}
|
|
182
|
-
*/
|
|
183
|
-
export function formatIeeeDate(date) {
|
|
184
|
-
const parsed = parseDate(date)
|
|
185
|
-
|
|
186
|
-
if (!parsed.year) return ''
|
|
187
|
-
|
|
188
|
-
if (parsed.month) {
|
|
189
|
-
return `${MONTHS_SHORT[parsed.month - 1]} ${parsed.year}`
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return String(parsed.year)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Format date for a specific style
|
|
197
|
-
*
|
|
198
|
-
* @param {any} date - Date input
|
|
199
|
-
* @param {Object} [options]
|
|
200
|
-
* @param {string} [options.style='apa'] - Citation style
|
|
201
|
-
* @returns {string}
|
|
202
|
-
*/
|
|
203
|
-
export function formatDate(date, options = {}) {
|
|
204
|
-
const { style = 'apa' } = options
|
|
205
|
-
|
|
206
|
-
switch (style) {
|
|
207
|
-
case 'apa':
|
|
208
|
-
return formatApaDate(date)
|
|
209
|
-
case 'mla':
|
|
210
|
-
return formatMlaDate(date)
|
|
211
|
-
case 'chicago':
|
|
212
|
-
return formatChicagoDate(date)
|
|
213
|
-
case 'ieee':
|
|
214
|
-
return formatIeeeDate(date)
|
|
215
|
-
default:
|
|
216
|
-
return formatApaDate(date)
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Get year from date
|
|
222
|
-
*
|
|
223
|
-
* @param {any} date - Date input
|
|
224
|
-
* @returns {number|undefined}
|
|
225
|
-
*/
|
|
226
|
-
export function getYear(date) {
|
|
227
|
-
return parseDate(date).year
|
|
228
|
-
}
|
package/src/math/Math.jsx
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Math Component
|
|
3
|
-
*
|
|
4
|
-
* Renders LaTeX math expressions using KaTeX.
|
|
5
|
-
* Supports both inline and display (block) modes.
|
|
6
|
-
*
|
|
7
|
-
* @module @uniweb/scholar/math/Math
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useEffect, useState } from 'react'
|
|
11
|
-
import { loadKatex } from './katex-loader.js'
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Math - Render LaTeX math expressions
|
|
15
|
-
*
|
|
16
|
-
* @param {Object} props
|
|
17
|
-
* @param {string} props.children - LaTeX expression to render
|
|
18
|
-
* @param {boolean} [props.display=false] - Display mode (block) vs inline
|
|
19
|
-
* @param {string} [props.className] - Additional CSS classes
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* // Inline math
|
|
23
|
-
* <Math>E = mc^2</Math>
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* // Display (block) math
|
|
27
|
-
* <Math display>
|
|
28
|
-
* \int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}
|
|
29
|
-
* </Math>
|
|
30
|
-
*/
|
|
31
|
-
export function Math({ children, display = false, className, ...props }) {
|
|
32
|
-
const [html, setHtml] = useState(null)
|
|
33
|
-
const [error, setError] = useState(null)
|
|
34
|
-
|
|
35
|
-
// Extract LaTeX string from children
|
|
36
|
-
const latex = typeof children === 'string' ? children.trim() : ''
|
|
37
|
-
|
|
38
|
-
useEffect(() => {
|
|
39
|
-
if (!latex) return
|
|
40
|
-
|
|
41
|
-
let cancelled = false
|
|
42
|
-
|
|
43
|
-
async function render() {
|
|
44
|
-
const katex = await loadKatex()
|
|
45
|
-
if (cancelled || !katex) return
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const rendered = katex.renderToString(latex, {
|
|
49
|
-
displayMode: display,
|
|
50
|
-
throwOnError: false,
|
|
51
|
-
errorColor: '#cc0000',
|
|
52
|
-
})
|
|
53
|
-
if (!cancelled) {
|
|
54
|
-
setHtml(rendered)
|
|
55
|
-
setError(null)
|
|
56
|
-
}
|
|
57
|
-
} catch (e) {
|
|
58
|
-
if (!cancelled) {
|
|
59
|
-
setError(e.message)
|
|
60
|
-
setHtml(null)
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
render()
|
|
66
|
-
|
|
67
|
-
return () => {
|
|
68
|
-
cancelled = true
|
|
69
|
-
}
|
|
70
|
-
}, [latex, display])
|
|
71
|
-
|
|
72
|
-
// Error state
|
|
73
|
-
if (error) {
|
|
74
|
-
return (
|
|
75
|
-
<span
|
|
76
|
-
className={className}
|
|
77
|
-
style={{ color: '#cc0000', fontFamily: 'monospace' }}
|
|
78
|
-
title={error}
|
|
79
|
-
{...props}
|
|
80
|
-
>
|
|
81
|
-
{latex}
|
|
82
|
-
</span>
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Loading state - show raw LaTeX in code format
|
|
87
|
-
if (!html) {
|
|
88
|
-
return (
|
|
89
|
-
<code className={className} {...props}>
|
|
90
|
-
{latex}
|
|
91
|
-
</code>
|
|
92
|
-
)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Rendered math
|
|
96
|
-
if (display) {
|
|
97
|
-
return (
|
|
98
|
-
<div
|
|
99
|
-
className={className}
|
|
100
|
-
dangerouslySetInnerHTML={{ __html: html }}
|
|
101
|
-
{...props}
|
|
102
|
-
/>
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return (
|
|
107
|
-
<span
|
|
108
|
-
className={className}
|
|
109
|
-
dangerouslySetInnerHTML={{ __html: html }}
|
|
110
|
-
{...props}
|
|
111
|
-
/>
|
|
112
|
-
)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export default Math
|
package/src/math/katex-loader.js
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KaTeX Lazy Loader
|
|
3
|
-
*
|
|
4
|
-
* Provides lazy loading of KaTeX with module-level caching.
|
|
5
|
-
* KaTeX is only loaded when math rendering is actually needed.
|
|
6
|
-
*
|
|
7
|
-
* @module @uniweb/scholar/math/katex-loader
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
// Module-level state for singleton pattern
|
|
11
|
-
let katexInstance = null
|
|
12
|
-
let katexLoadPromise = null
|
|
13
|
-
let cssInjected = false
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* CDN URL for KaTeX CSS (matches the katex version in package.json)
|
|
17
|
-
*/
|
|
18
|
-
const KATEX_CSS_URL = 'https://cdn.jsdelivr.net/npm/katex@0.16.0/dist/katex.min.css'
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Inject KaTeX CSS into the document head
|
|
22
|
-
* Only injects once per page load
|
|
23
|
-
*/
|
|
24
|
-
function injectKatexCSS() {
|
|
25
|
-
if (cssInjected || typeof document === 'undefined') return
|
|
26
|
-
|
|
27
|
-
const styleId = 'uniweb-katex-css'
|
|
28
|
-
|
|
29
|
-
// Check if already injected
|
|
30
|
-
if (document.getElementById(styleId)) {
|
|
31
|
-
cssInjected = true
|
|
32
|
-
return
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const link = document.createElement('link')
|
|
36
|
-
link.id = styleId
|
|
37
|
-
link.rel = 'stylesheet'
|
|
38
|
-
link.href = KATEX_CSS_URL
|
|
39
|
-
link.crossOrigin = 'anonymous'
|
|
40
|
-
document.head.appendChild(link)
|
|
41
|
-
|
|
42
|
-
cssInjected = true
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Load KaTeX lazily
|
|
47
|
-
*
|
|
48
|
-
* Returns cached instance if already loaded, otherwise loads
|
|
49
|
-
* KaTeX dynamically and injects its CSS.
|
|
50
|
-
*
|
|
51
|
-
* @returns {Promise<Object>} The KaTeX module
|
|
52
|
-
*/
|
|
53
|
-
export async function loadKatex() {
|
|
54
|
-
if (katexInstance) return katexInstance
|
|
55
|
-
if (katexLoadPromise) return katexLoadPromise
|
|
56
|
-
|
|
57
|
-
katexLoadPromise = (async () => {
|
|
58
|
-
try {
|
|
59
|
-
const katex = await import('katex')
|
|
60
|
-
katexInstance = katex.default || katex
|
|
61
|
-
|
|
62
|
-
// Inject CSS when KaTeX is loaded
|
|
63
|
-
injectKatexCSS()
|
|
64
|
-
|
|
65
|
-
return katexInstance
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.warn('[scholar/math] Failed to load KaTeX:', error)
|
|
68
|
-
katexLoadPromise = null
|
|
69
|
-
return null
|
|
70
|
-
}
|
|
71
|
-
})()
|
|
72
|
-
|
|
73
|
-
return katexLoadPromise
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Check if KaTeX is already loaded
|
|
78
|
-
*
|
|
79
|
-
* @returns {boolean} True if KaTeX is loaded and ready
|
|
80
|
-
*/
|
|
81
|
-
export function isKatexLoaded() {
|
|
82
|
-
return katexInstance !== null
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Render LaTeX to HTML string
|
|
87
|
-
*
|
|
88
|
-
* @param {string} latex - LaTeX string to render
|
|
89
|
-
* @param {Object} options - KaTeX options
|
|
90
|
-
* @returns {Promise<string|null>} Rendered HTML or null on error
|
|
91
|
-
*/
|
|
92
|
-
export async function renderLatex(latex, options = {}) {
|
|
93
|
-
const katex = await loadKatex()
|
|
94
|
-
if (!katex) return null
|
|
95
|
-
|
|
96
|
-
try {
|
|
97
|
-
return katex.renderToString(latex, {
|
|
98
|
-
throwOnError: false,
|
|
99
|
-
...options,
|
|
100
|
-
})
|
|
101
|
-
} catch (error) {
|
|
102
|
-
console.warn('[scholar/math] LaTeX render error:', error.message)
|
|
103
|
-
return null
|
|
104
|
-
}
|
|
105
|
-
}
|