@life-and-dev/mdsite 0.0.3
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 +228 -0
- package/bin/mdsite.js +3 -0
- package/dist/commands/commands.test.d.ts +1 -0
- package/dist/commands/commands.test.js +302 -0
- package/dist/commands/commands.test.js.map +1 -0
- package/dist/commands/generate.d.ts +1 -0
- package/dist/commands/generate.js +20 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +16 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/prepare.d.ts +1 -0
- package/dist/commands/prepare.js +111 -0
- package/dist/commands/prepare.js.map +1 -0
- package/dist/commands/prepare.test.d.ts +1 -0
- package/dist/commands/prepare.test.js +131 -0
- package/dist/commands/prepare.test.js.map +1 -0
- package/dist/commands/preview.d.ts +5 -0
- package/dist/commands/preview.js +62 -0
- package/dist/commands/preview.js.map +1 -0
- package/dist/commands/start.d.ts +5 -0
- package/dist/commands/start.js +50 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/stop.d.ts +1 -0
- package/dist/commands/stop.js +26 -0
- package/dist/commands/stop.js.map +1 -0
- package/dist/commands/workflows.test.d.ts +1 -0
- package/dist/commands/workflows.test.js +183 -0
- package/dist/commands/workflows.test.js.map +1 -0
- package/dist/config/default-mdsite-config.d.ts +2 -0
- package/dist/config/default-mdsite-config.js +82 -0
- package/dist/config/default-mdsite-config.js.map +1 -0
- package/dist/config/default-mdsite-config.test.d.ts +1 -0
- package/dist/config/default-mdsite-config.test.js +42 -0
- package/dist/config/default-mdsite-config.test.js.map +1 -0
- package/dist/config/mdsite-config.d.ts +42 -0
- package/dist/config/mdsite-config.js +74 -0
- package/dist/config/mdsite-config.js.map +1 -0
- package/dist/config/mdsite-config.test.d.ts +1 -0
- package/dist/config/mdsite-config.test.js +115 -0
- package/dist/config/mdsite-config.test.js.map +1 -0
- package/dist/config/menu.d.ts +3 -0
- package/dist/config/menu.js +58 -0
- package/dist/config/menu.js.map +1 -0
- package/dist/config/menu.test.d.ts +1 -0
- package/dist/config/menu.test.js +53 -0
- package/dist/config/menu.test.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +99 -0
- package/dist/index.js.map +1 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +133 -0
- package/dist/index.test.js.map +1 -0
- package/dist/process/child-process.d.ts +9 -0
- package/dist/process/child-process.js +142 -0
- package/dist/process/child-process.js.map +1 -0
- package/dist/process/child-process.test.d.ts +1 -0
- package/dist/process/child-process.test.js +131 -0
- package/dist/process/child-process.test.js.map +1 -0
- package/dist/process/runtime-state.d.ts +16 -0
- package/dist/process/runtime-state.js +42 -0
- package/dist/process/runtime-state.js.map +1 -0
- package/dist/process/runtime-state.test.d.ts +1 -0
- package/dist/process/runtime-state.test.js +57 -0
- package/dist/process/runtime-state.test.js.map +1 -0
- package/dist/renderer/mdsite-nuxt.d.ts +22 -0
- package/dist/renderer/mdsite-nuxt.js +194 -0
- package/dist/renderer/mdsite-nuxt.js.map +1 -0
- package/dist/renderer/mdsite-nuxt.test.d.ts +1 -0
- package/dist/renderer/mdsite-nuxt.test.js +226 -0
- package/dist/renderer/mdsite-nuxt.test.js.map +1 -0
- package/mdsite-nuxt/app/app.vue +7 -0
- package/mdsite-nuxt/app/assets/css/bible-tooltips.css +87 -0
- package/mdsite-nuxt/app/assets/css/markdown.css +195 -0
- package/mdsite-nuxt/app/assets/css/print.css +133 -0
- package/mdsite-nuxt/app/components/AppBar.vue +138 -0
- package/mdsite-nuxt/app/components/AppFooter.vue +88 -0
- package/mdsite-nuxt/app/components/AppNavigation.vue +162 -0
- package/mdsite-nuxt/app/components/AppTableOfContents.vue +149 -0
- package/mdsite-nuxt/app/components/BreadcrumbNav.vue +139 -0
- package/mdsite-nuxt/app/components/NavigationTree.vue +169 -0
- package/mdsite-nuxt/app/components/SearchBox.vue +212 -0
- package/mdsite-nuxt/app/components/TocItem.vue +75 -0
- package/mdsite-nuxt/app/components/TreeNode.vue +264 -0
- package/mdsite-nuxt/app/components/content/MarkdownAlert.vue +42 -0
- package/mdsite-nuxt/app/components/content/Mermaid.vue +103 -0
- package/mdsite-nuxt/app/components/content/ProseA.vue +57 -0
- package/mdsite-nuxt/app/components/content/ProseBlockquote.vue +14 -0
- package/mdsite-nuxt/app/components/content/ProsePre.vue +49 -0
- package/mdsite-nuxt/app/components/content/ProseTable.vue +143 -0
- package/mdsite-nuxt/app/composables/useAppTheme.ts +89 -0
- package/mdsite-nuxt/app/composables/useBibleTooltips.ts +21 -0
- package/mdsite-nuxt/app/composables/useBreadcrumbs.ts +70 -0
- package/mdsite-nuxt/app/composables/useContentPostProcessing.ts +89 -0
- package/mdsite-nuxt/app/composables/useNavigationTree.ts +174 -0
- package/mdsite-nuxt/app/composables/useSearchIndex.ts +47 -0
- package/mdsite-nuxt/app/composables/useSearchRelevance.test.ts +255 -0
- package/mdsite-nuxt/app/composables/useSearchRelevance.ts +207 -0
- package/mdsite-nuxt/app/composables/useSidebarState.ts +62 -0
- package/mdsite-nuxt/app/composables/useSiteConfig.ts +37 -0
- package/mdsite-nuxt/app/composables/useSourceEdit.ts +69 -0
- package/mdsite-nuxt/app/composables/useTableOfContents.ts +141 -0
- package/mdsite-nuxt/app/composables/useTableParser.ts +106 -0
- package/mdsite-nuxt/app/composables/useTooltipConfig.ts +20 -0
- package/mdsite-nuxt/app/config/themes.ts +17 -0
- package/mdsite-nuxt/app/layouts/default.vue +376 -0
- package/mdsite-nuxt/app/pages/[...slug].vue +56 -0
- package/mdsite-nuxt/app/pages/index.vue +54 -0
- package/mdsite-nuxt/app/plugins/bible-tooltips.client.ts +401 -0
- package/mdsite-nuxt/app/plugins/bible-tooltips.test.ts +284 -0
- package/mdsite-nuxt/app/types/content.d.ts +10 -0
- package/mdsite-nuxt/app/types/table.ts +57 -0
- package/mdsite-nuxt/app/utils/bible-book-names.ts +37 -0
- package/mdsite-nuxt/app/utils/bible-verse-utils.test.ts +148 -0
- package/mdsite-nuxt/app/utils/bible-verse-utils.ts +236 -0
- package/mdsite-nuxt/content.config.ts +18 -0
- package/mdsite-nuxt/example.config.yml +67 -0
- package/mdsite-nuxt/nuxt.config.ts +247 -0
- package/mdsite-nuxt/package-lock.json +15922 -0
- package/mdsite-nuxt/package.json +50 -0
- package/mdsite-nuxt/playwright.config.js +47 -0
- package/mdsite-nuxt/public/robots.txt +2 -0
- package/mdsite-nuxt/scripts/generate-favicons.ts +192 -0
- package/mdsite-nuxt/scripts/generate-indices.ts +508 -0
- package/mdsite-nuxt/scripts/renderer-hooks.test.ts +277 -0
- package/mdsite-nuxt/scripts/renderer-hooks.ts +141 -0
- package/mdsite-nuxt/scripts/start.test.ts +164 -0
- package/mdsite-nuxt/scripts/start.ts +55 -0
- package/mdsite-nuxt/scripts/sync-content.ts +504 -0
- package/mdsite-nuxt/tsconfig.json +18 -0
- package/mdsite-nuxt/utils/mdsite-config.ts +240 -0
- package/mdsite-nuxt/vitest.config.ts +7 -0
- package/package.json +36 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript types for table parsing and v-data-table integration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* v-data-table header configuration
|
|
7
|
+
*/
|
|
8
|
+
export interface TableHeader {
|
|
9
|
+
/** Display title for the column */
|
|
10
|
+
title: string
|
|
11
|
+
/** Unique key for accessing data */
|
|
12
|
+
key: string
|
|
13
|
+
/** Whether column is sortable (default: true) */
|
|
14
|
+
sortable?: boolean
|
|
15
|
+
/** Column alignment */
|
|
16
|
+
align?: 'start' | 'end' | 'center'
|
|
17
|
+
/** Column width */
|
|
18
|
+
width?: string | number
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* v-data-table item (row data)
|
|
23
|
+
* Dynamic object with keys matching header keys
|
|
24
|
+
*/
|
|
25
|
+
export type TableItem = Record<string, any>
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parsed table structure ready for v-data-table
|
|
29
|
+
*/
|
|
30
|
+
export interface ParsedTable {
|
|
31
|
+
/** Column headers */
|
|
32
|
+
headers: TableHeader[]
|
|
33
|
+
/** Row data */
|
|
34
|
+
items: TableItem[]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Raw table cell data during parsing
|
|
39
|
+
*/
|
|
40
|
+
export interface TableCell {
|
|
41
|
+
/** Text content of cell */
|
|
42
|
+
text: string
|
|
43
|
+
/** HTML content of cell (for complex formatting) */
|
|
44
|
+
html: string
|
|
45
|
+
/** Column index */
|
|
46
|
+
colIndex: number
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Raw table row data during parsing
|
|
51
|
+
*/
|
|
52
|
+
export interface TableRow {
|
|
53
|
+
/** Cells in this row */
|
|
54
|
+
cells: TableCell[]
|
|
55
|
+
/** Row index */
|
|
56
|
+
rowIndex: number
|
|
57
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bible book names whitelist (66 books)
|
|
3
|
+
* Used for detecting Bible references while avoiding false positives
|
|
4
|
+
*/
|
|
5
|
+
export const BIBLE_BOOK_NAMES = [
|
|
6
|
+
'Genesis', 'Exodus', 'Leviticus', 'Numbers', 'Deuteronomy', 'Joshua', 'Judges', 'Ruth',
|
|
7
|
+
'1 Samuel', '2 Samuel', '1 Kings', '2 Kings', '1 Chronicles', '2 Chronicles',
|
|
8
|
+
'Ezra', 'Nehemiah', 'Esther', 'Job', 'Psalm', 'Psalms', 'Proverbs', 'Ecclesiastes',
|
|
9
|
+
'Song of Solomon', 'Isaiah', 'Jeremiah', 'Lamentations', 'Ezekiel', 'Daniel',
|
|
10
|
+
'Hosea', 'Joel', 'Amos', 'Obadiah', 'Jonah', 'Micah', 'Nahum', 'Habakkuk',
|
|
11
|
+
'Zephaniah', 'Haggai', 'Zechariah', 'Malachi',
|
|
12
|
+
'Matthew', 'Mark', 'Luke', 'John', 'Acts', 'Romans',
|
|
13
|
+
'1 Corinthians', '2 Corinthians', 'Galatians', 'Ephesians', 'Philippians',
|
|
14
|
+
'Colossians', '1 Thessalonians', '2 Thessalonians', '1 Timothy', '2 Timothy',
|
|
15
|
+
'Titus', 'Philemon', 'Hebrews', 'James', '1 Peter', '2 Peter',
|
|
16
|
+
'1 John', '2 John', '3 John', 'Jude', 'Revelation'
|
|
17
|
+
] as const
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates Bible reference detection patterns using book name whitelist
|
|
21
|
+
* @returns Array of RegExp patterns for matching Bible references (with optional translation in brackets)
|
|
22
|
+
*/
|
|
23
|
+
export function createBibleReferencePatterns(): RegExp[] {
|
|
24
|
+
const bookPattern = BIBLE_BOOK_NAMES.join('|').replace(/\s/g, '\\s+')
|
|
25
|
+
const translationPattern = '(?:\\s*\\([A-Z]+\\))?' // Optional (ESV), (KJV), etc.
|
|
26
|
+
|
|
27
|
+
return [
|
|
28
|
+
// Cross-chapter range: "2 Corinthians 4:16-5:9 (ESV)"
|
|
29
|
+
new RegExp(`\\b(${bookPattern})\\s+(\\d+):(\\d+)-(\\d+):(\\d+)${translationPattern}\\b`, 'g'),
|
|
30
|
+
// Same chapter range: "John 3:16-18 (ESV)" - ensure we don't match if followed by digits (prevents matching "3:16-5" in "3:16-5:9")
|
|
31
|
+
new RegExp(`\\b(${bookPattern})\\s+(\\d+):(\\d+)-(\\d+)${translationPattern}\\b(?!\\d)`, 'g'),
|
|
32
|
+
// Single verse: "John 3:16 (ESV)" - ensure we don't match if followed by dash or digits (allows punctuation like colon, period, comma)
|
|
33
|
+
new RegExp(`\\b(${bookPattern})\\s+(\\d+):(\\d+)${translationPattern}\\b(?![\\d-])`, 'g'),
|
|
34
|
+
// Chapter only: "John 3 (ESV)", "Psalm 23 (ESV)" - ensure not followed by colon
|
|
35
|
+
new RegExp(`\\b(${bookPattern})\\s+(\\d+)${translationPattern}\\b(?!\\s*:)`, 'g')
|
|
36
|
+
]
|
|
37
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { createBibleHubInterlinearUrl, parseReference } from './bible-verse-utils'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Unit tests for BibleHub interlinear URL generation
|
|
6
|
+
*/
|
|
7
|
+
describe('BibleHub Interlinear URL Generation', () => {
|
|
8
|
+
it('should create URL for single verse reference', () => {
|
|
9
|
+
const url = createBibleHubInterlinearUrl('John 3:16')
|
|
10
|
+
expect(url).toBe('https://biblehub.com/interlinear/john/3-16.htm')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should create URL for chapter-only reference', () => {
|
|
14
|
+
const url = createBibleHubInterlinearUrl('Psalm 23')
|
|
15
|
+
expect(url).toBe('https://biblehub.com/interlinear/psalm/23.htm')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('should create URL for verse range (use first verse)', () => {
|
|
19
|
+
const url = createBibleHubInterlinearUrl('John 3:16-18')
|
|
20
|
+
expect(url).toBe('https://biblehub.com/interlinear/john/3-16.htm')
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('should handle book names with spaces', () => {
|
|
24
|
+
const url = createBibleHubInterlinearUrl('1 Corinthians 13:4')
|
|
25
|
+
expect(url).toBe('https://biblehub.com/interlinear/1_corinthians/13-4.htm')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should handle book names with numbers', () => {
|
|
29
|
+
const url = createBibleHubInterlinearUrl('2 Timothy 3:16')
|
|
30
|
+
expect(url).toBe('https://biblehub.com/interlinear/2_timothy/3-16.htm')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('should handle multi-word book names', () => {
|
|
34
|
+
const url = createBibleHubInterlinearUrl('Song of Solomon 2:1')
|
|
35
|
+
expect(url).toBe('https://biblehub.com/interlinear/song_of_solomon/2-1.htm')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
it('should handle cross-chapter ranges (use first reference)', () => {
|
|
39
|
+
const url = createBibleHubInterlinearUrl('2 Corinthians 4:16-5:9')
|
|
40
|
+
expect(url).toBe('https://biblehub.com/interlinear/2_corinthians/4-16.htm')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should handle Genesis references', () => {
|
|
44
|
+
const url = createBibleHubInterlinearUrl('Genesis 1:1')
|
|
45
|
+
expect(url).toBe('https://biblehub.com/interlinear/genesis/1-1.htm')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('should handle Revelation references', () => {
|
|
49
|
+
const url = createBibleHubInterlinearUrl('Revelation 1:5')
|
|
50
|
+
expect(url).toBe('https://biblehub.com/interlinear/revelation/1-5.htm')
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('should handle Ecclesiastes references', () => {
|
|
54
|
+
const url = createBibleHubInterlinearUrl('Ecclesiastes 1:2')
|
|
55
|
+
expect(url).toBe('https://biblehub.com/interlinear/ecclesiastes/1-2.htm')
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('should handle references with multiple spaces', () => {
|
|
59
|
+
const url = createBibleHubInterlinearUrl('1 Corinthians 13:4')
|
|
60
|
+
expect(url).toBe('https://biblehub.com/interlinear/1_corinthians/13-4.htm')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('should fallback to main page for invalid reference', () => {
|
|
64
|
+
const url = createBibleHubInterlinearUrl('InvalidBook')
|
|
65
|
+
expect(url).toBe('https://biblehub.com/interlinear/')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('should fallback to main page for empty reference', () => {
|
|
69
|
+
const url = createBibleHubInterlinearUrl('')
|
|
70
|
+
expect(url).toBe('https://biblehub.com/interlinear/')
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('should normalize book names to lowercase', () => {
|
|
74
|
+
const url = createBibleHubInterlinearUrl('JOHN 3:16')
|
|
75
|
+
expect(url).toBe('https://biblehub.com/interlinear/john/3-16.htm')
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it('should handle chapter with large verse number', () => {
|
|
79
|
+
const url = createBibleHubInterlinearUrl('Psalm 119:105')
|
|
80
|
+
expect(url).toBe('https://biblehub.com/interlinear/psalm/119-105.htm')
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Unit tests for Bible reference parsing with translation extraction
|
|
86
|
+
*/
|
|
87
|
+
describe('parseReference', () => {
|
|
88
|
+
it('should parse reference with ESV translation', () => {
|
|
89
|
+
const result = parseReference('John 3:16 (ESV)')
|
|
90
|
+
expect(result.reference).toBe('John 3:16')
|
|
91
|
+
expect(result.translation).toBe('ESV')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('should parse reference with KJV translation', () => {
|
|
95
|
+
const result = parseReference('Genesis 1:1 (KJV)')
|
|
96
|
+
expect(result.reference).toBe('Genesis 1:1')
|
|
97
|
+
expect(result.translation).toBe('KJV')
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('should parse reference with NIV translation', () => {
|
|
101
|
+
const result = parseReference('Matthew 5:3-12 (NIV)')
|
|
102
|
+
expect(result.reference).toBe('Matthew 5:3-12')
|
|
103
|
+
expect(result.translation).toBe('NIV')
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
it('should default to ESV when no translation specified', () => {
|
|
107
|
+
const result = parseReference('John 3:16')
|
|
108
|
+
expect(result.reference).toBe('John 3:16')
|
|
109
|
+
expect(result.translation).toBe('ESV')
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('should default to ESV for chapter-only reference', () => {
|
|
113
|
+
const result = parseReference('Psalm 23')
|
|
114
|
+
expect(result.reference).toBe('Psalm 23')
|
|
115
|
+
expect(result.translation).toBe('ESV')
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
it('should handle extra whitespace around translation', () => {
|
|
119
|
+
const result = parseReference('John 3:16 (ESV) ')
|
|
120
|
+
expect(result.reference).toBe('John 3:16')
|
|
121
|
+
expect(result.translation).toBe('ESV')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should handle references with extra whitespace', () => {
|
|
125
|
+
const result = parseReference(' John 3:16 ')
|
|
126
|
+
expect(result.reference).toBe('John 3:16')
|
|
127
|
+
expect(result.translation).toBe('ESV')
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('should parse cross-chapter range with translation', () => {
|
|
131
|
+
const result = parseReference('2 Corinthians 4:16-5:9 (ESV)')
|
|
132
|
+
expect(result.reference).toBe('2 Corinthians 4:16-5:9')
|
|
133
|
+
expect(result.translation).toBe('ESV')
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('should handle translation codes with varying lengths', () => {
|
|
137
|
+
const result = parseReference('John 3:16 (NKJV)')
|
|
138
|
+
expect(result.reference).toBe('John 3:16')
|
|
139
|
+
expect(result.translation).toBe('NKJV')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('should not parse lowercase translations', () => {
|
|
143
|
+
// Lowercase should not match - treated as no translation
|
|
144
|
+
const result = parseReference('John 3:16 (esv)')
|
|
145
|
+
expect(result.reference).toBe('John 3:16 (esv)')
|
|
146
|
+
expect(result.translation).toBe('ESV')
|
|
147
|
+
})
|
|
148
|
+
})
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utility functions for Bible verse handling with Bolls.life API
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Bolls.life API response format for single verse
|
|
7
|
+
*/
|
|
8
|
+
interface BollsVerseResponse {
|
|
9
|
+
pk: number
|
|
10
|
+
verse: number
|
|
11
|
+
text: string
|
|
12
|
+
comment?: string // HTML cross-references
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ProcessedBibleVerse {
|
|
16
|
+
text: string
|
|
17
|
+
translation: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ParsedReference {
|
|
21
|
+
reference: string // Reference without translation (e.g., "John 3:16")
|
|
22
|
+
translation: string // Translation code (e.g., "ESV", defaults to "ESV")
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Standard Bible book numbering (1-66)
|
|
27
|
+
* Used by Bolls.life API
|
|
28
|
+
*/
|
|
29
|
+
const BOOK_NUMBERS: Record<string, number> = {
|
|
30
|
+
// Old Testament (1-39)
|
|
31
|
+
'genesis': 1, 'gen': 1,
|
|
32
|
+
'exodus': 2, 'exo': 2, 'exod': 2,
|
|
33
|
+
'leviticus': 3, 'lev': 3,
|
|
34
|
+
'numbers': 4, 'num': 4,
|
|
35
|
+
'deuteronomy': 5, 'deut': 5, 'deu': 5,
|
|
36
|
+
'joshua': 6, 'josh': 6, 'jos': 6,
|
|
37
|
+
'judges': 7, 'judg': 7, 'jdg': 7,
|
|
38
|
+
'ruth': 8, 'rut': 8,
|
|
39
|
+
'1 samuel': 9, '1samuel': 9, '1sam': 9, '1sa': 9, 'i samuel': 9,
|
|
40
|
+
'2 samuel': 10, '2samuel': 10, '2sam': 10, '2sa': 10, 'ii samuel': 10,
|
|
41
|
+
'1 kings': 11, '1kings': 11, '1kgs': 11, '1ki': 11, 'i kings': 11,
|
|
42
|
+
'2 kings': 12, '2kings': 12, '2kgs': 12, '2ki': 12, 'ii kings': 12,
|
|
43
|
+
'1 chronicles': 13, '1chronicles': 13, '1chr': 13, '1ch': 13, 'i chronicles': 13,
|
|
44
|
+
'2 chronicles': 14, '2chronicles': 14, '2chr': 14, '2ch': 14, 'ii chronicles': 14,
|
|
45
|
+
'ezra': 15, 'ezr': 15,
|
|
46
|
+
'nehemiah': 16, 'neh': 16,
|
|
47
|
+
'esther': 17, 'est': 17, 'esth': 17,
|
|
48
|
+
'job': 18,
|
|
49
|
+
'psalm': 19, 'psalms': 19, 'psa': 19, 'ps': 19,
|
|
50
|
+
'proverbs': 20, 'prov': 20, 'pro': 20,
|
|
51
|
+
'ecclesiastes': 21, 'eccl': 21, 'ecc': 21, 'eccles': 21,
|
|
52
|
+
'song of solomon': 22, 'song': 22, 'sos': 22, 'song of songs': 22,
|
|
53
|
+
'isaiah': 23, 'isa': 23,
|
|
54
|
+
'jeremiah': 24, 'jer': 24,
|
|
55
|
+
'lamentations': 25, 'lam': 25,
|
|
56
|
+
'ezekiel': 26, 'ezek': 26, 'eze': 26,
|
|
57
|
+
'daniel': 27, 'dan': 27,
|
|
58
|
+
'hosea': 28, 'hos': 28,
|
|
59
|
+
'joel': 29, 'joe': 29,
|
|
60
|
+
'amos': 30, 'amo': 30,
|
|
61
|
+
'obadiah': 31, 'obad': 31, 'oba': 31,
|
|
62
|
+
'jonah': 32, 'jon': 32,
|
|
63
|
+
'micah': 33, 'mic': 33,
|
|
64
|
+
'nahum': 34, 'nah': 34, 'nam': 34,
|
|
65
|
+
'habakkuk': 35, 'hab': 35,
|
|
66
|
+
'zephaniah': 36, 'zeph': 36, 'zep': 36,
|
|
67
|
+
'haggai': 37, 'hag': 37,
|
|
68
|
+
'zechariah': 38, 'zech': 38, 'zec': 38,
|
|
69
|
+
'malachi': 39, 'mal': 39,
|
|
70
|
+
// New Testament (40-66)
|
|
71
|
+
'matthew': 40, 'matt': 40, 'mat': 40, 'mt': 40,
|
|
72
|
+
'mark': 41, 'mar': 41, 'mk': 41, 'mrk': 41,
|
|
73
|
+
'luke': 42, 'luk': 42, 'lk': 42,
|
|
74
|
+
'john': 43, 'joh': 43, 'jn': 43,
|
|
75
|
+
'acts': 44, 'act': 44,
|
|
76
|
+
'romans': 45, 'rom': 45, 'rm': 45,
|
|
77
|
+
'1 corinthians': 46, '1corinthians': 46, '1cor': 46, '1co': 46, 'i corinthians': 46,
|
|
78
|
+
'2 corinthians': 47, '2corinthians': 47, '2cor': 47, '2co': 47, 'ii corinthians': 47,
|
|
79
|
+
'galatians': 48, 'gal': 48,
|
|
80
|
+
'ephesians': 49, 'eph': 49,
|
|
81
|
+
'philippians': 50, 'phil': 50, 'php': 50,
|
|
82
|
+
'colossians': 51, 'col': 51,
|
|
83
|
+
'1 thessalonians': 52, '1thessalonians': 52, '1thess': 52, '1th': 52, 'i thessalonians': 52,
|
|
84
|
+
'2 thessalonians': 53, '2thessalonians': 53, '2thess': 53, '2th': 53, 'ii thessalonians': 53,
|
|
85
|
+
'1 timothy': 54, '1timothy': 54, '1tim': 54, '1ti': 54, 'i timothy': 54,
|
|
86
|
+
'2 timothy': 55, '2timothy': 55, '2tim': 55, '2ti': 55, 'ii timothy': 55,
|
|
87
|
+
'titus': 56, 'tit': 56,
|
|
88
|
+
'philemon': 57, 'philem': 57, 'phm': 57,
|
|
89
|
+
'hebrews': 58, 'heb': 58,
|
|
90
|
+
'james': 59, 'jas': 59, 'jam': 59,
|
|
91
|
+
'1 peter': 60, '1peter': 60, '1pet': 60, '1pe': 60, 'i peter': 60,
|
|
92
|
+
'2 peter': 61, '2peter': 61, '2pet': 61, '2pe': 61, 'ii peter': 61,
|
|
93
|
+
'1 john': 62, '1john': 62, '1jn': 62, 'i john': 62,
|
|
94
|
+
'2 john': 63, '2john': 63, '2jn': 63, 'ii john': 63,
|
|
95
|
+
'3 john': 64, '3john': 64, '3jn': 63, 'iii john': 64,
|
|
96
|
+
'jude': 65, 'jud': 65,
|
|
97
|
+
'revelation': 66, 'rev': 66,
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get standard book number (1-66) from book name
|
|
102
|
+
* @param bookName - Bible book name (case-insensitive)
|
|
103
|
+
* @returns Book number or null if not found
|
|
104
|
+
*/
|
|
105
|
+
export function getBookNumber(bookName: string): number | null {
|
|
106
|
+
const normalized = bookName.toLowerCase().trim()
|
|
107
|
+
return BOOK_NUMBERS[normalized] ?? null
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Parse a Bible reference and extract translation
|
|
112
|
+
* @param fullReference - Full reference with optional translation (e.g., "John 3:16 (KJV)" or "John 3:16")
|
|
113
|
+
* @returns Parsed reference object with reference and translation
|
|
114
|
+
*/
|
|
115
|
+
export function parseReference(fullReference: string): ParsedReference {
|
|
116
|
+
// Match translation in parentheses at the end: (ESV), (KJV), etc.
|
|
117
|
+
const match = fullReference.match(/^(.+?)\s*\(([A-Z]+)\)\s*$/)
|
|
118
|
+
|
|
119
|
+
if (match && match[1] && match[2]) {
|
|
120
|
+
return {
|
|
121
|
+
reference: match[1].trim(),
|
|
122
|
+
translation: match[2]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// No translation specified, default to ESV
|
|
127
|
+
return {
|
|
128
|
+
reference: fullReference.trim(),
|
|
129
|
+
translation: 'ESV'
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const MAX_VERSES = 4
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Strip HTML tags from text, preserving content
|
|
137
|
+
* @param html - HTML string
|
|
138
|
+
* @returns Plain text
|
|
139
|
+
*/
|
|
140
|
+
function stripHtml(html: string): string {
|
|
141
|
+
return html
|
|
142
|
+
.replace(/<S>\d+<\/S>/g, '') // Remove Strong's numbers
|
|
143
|
+
.replace(/<i>(.*?)<\/i>/g, '$1') // Keep italicized content
|
|
144
|
+
.replace(/<a[^>]*>(.*?)<\/a>/g, '$1') // Keep link text
|
|
145
|
+
.replace(/<[^>]+>/g, '') // Remove all other tags
|
|
146
|
+
.replace(/\s+/g, ' ') // Normalize whitespace
|
|
147
|
+
.trim()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Process Bolls.life API response for single verse
|
|
152
|
+
* @param data - Response from Bolls.life API
|
|
153
|
+
* @param translation - Translation code
|
|
154
|
+
* @returns Processed verse with stripped HTML
|
|
155
|
+
*/
|
|
156
|
+
export function processBollsVerse(data: BollsVerseResponse, translation: string): ProcessedBibleVerse {
|
|
157
|
+
const text = stripHtml(data.text)
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
text,
|
|
161
|
+
translation: translation.toUpperCase()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Process Bolls.life API response for verse range
|
|
167
|
+
* @param data - Array of verses from Bolls.life API
|
|
168
|
+
* @param translation - Translation code
|
|
169
|
+
* @param startVerse - Starting verse number (optional, for range filtering)
|
|
170
|
+
* @param endVerse - Ending verse number (optional, for range filtering)
|
|
171
|
+
* @returns Processed verses with truncation if needed
|
|
172
|
+
*/
|
|
173
|
+
export function processBollsVerseRange(
|
|
174
|
+
data: BollsVerseResponse[],
|
|
175
|
+
translation: string,
|
|
176
|
+
startVerse?: number,
|
|
177
|
+
endVerse?: number
|
|
178
|
+
): ProcessedBibleVerse {
|
|
179
|
+
let verses = data
|
|
180
|
+
|
|
181
|
+
// Filter to verse range if specified
|
|
182
|
+
if (startVerse !== undefined) {
|
|
183
|
+
const end = endVerse ?? startVerse
|
|
184
|
+
verses = data.filter(v => v.verse >= startVerse && v.verse <= end)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Truncate to first 4 verses if needed
|
|
188
|
+
const wasTruncated = verses.length > MAX_VERSES
|
|
189
|
+
const limitedVerses = verses.slice(0, MAX_VERSES)
|
|
190
|
+
|
|
191
|
+
// Join and strip HTML
|
|
192
|
+
const text = limitedVerses
|
|
193
|
+
.map(v => stripHtml(v.text))
|
|
194
|
+
.join(' ')
|
|
195
|
+
|
|
196
|
+
const finalText = wasTruncated ? text + ' ...' : text
|
|
197
|
+
|
|
198
|
+
return {
|
|
199
|
+
text: finalText,
|
|
200
|
+
translation: translation.toUpperCase()
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Create BibleHub interlinear URL from a Bible reference
|
|
206
|
+
* @param reference - Bible reference (e.g., "John 3:16", "Genesis 1:1-3", "Psalm 23")
|
|
207
|
+
* @returns BibleHub interlinear URL
|
|
208
|
+
*/
|
|
209
|
+
export function createBibleHubInterlinearUrl(reference: string): string {
|
|
210
|
+
// Parse reference: "Book Chapter:Verse" or "Book Chapter:Verse-Verse2" or "Book Chapter"
|
|
211
|
+
const match = reference.match(/^(.+?)\s+(\d+)(?::(\d+))?(?:-(\d+))?/)
|
|
212
|
+
|
|
213
|
+
if (!match) {
|
|
214
|
+
// Fallback to main interlinear page if we can't parse
|
|
215
|
+
return 'https://biblehub.com/interlinear/'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const [, book, chapter, verse] = match
|
|
219
|
+
|
|
220
|
+
// Type guard: ensure book and chapter exist
|
|
221
|
+
if (!book || !chapter) {
|
|
222
|
+
return 'https://biblehub.com/interlinear/'
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Normalize book name for URL (lowercase, replace spaces with underscores)
|
|
226
|
+
const bookSlug = book.toLowerCase().replace(/\s+/g, '_')
|
|
227
|
+
|
|
228
|
+
// Build URL
|
|
229
|
+
if (verse) {
|
|
230
|
+
// Specific verse: /interlinear/book/chapter-verse.htm
|
|
231
|
+
return `https://biblehub.com/interlinear/${bookSlug}/${chapter}-${verse}.htm`
|
|
232
|
+
} else {
|
|
233
|
+
// Chapter only: /interlinear/book/chapter.htm
|
|
234
|
+
return `https://biblehub.com/interlinear/${bookSlug}/${chapter}.htm`
|
|
235
|
+
}
|
|
236
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineCollection, defineContentConfig } from '@nuxt/content'
|
|
2
|
+
import { loadMdsiteConfigSync } from './utils/mdsite-config.js'
|
|
3
|
+
|
|
4
|
+
const { contentDir } = loadMdsiteConfigSync()
|
|
5
|
+
|
|
6
|
+
export default defineContentConfig({
|
|
7
|
+
collections: {
|
|
8
|
+
content: defineCollection({
|
|
9
|
+
type: 'page',
|
|
10
|
+
source: {
|
|
11
|
+
cwd: contentDir,
|
|
12
|
+
include: '**/*.md',
|
|
13
|
+
exclude: ['**/*.draft.md'],
|
|
14
|
+
prefix: '/'
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
})
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
content:
|
|
2
|
+
git:
|
|
3
|
+
branch: main
|
|
4
|
+
path: ../example # Path where git content is cloned/checked out relative to this file
|
|
5
|
+
repo: https://github.com/life-and-dev/md-site # If missing, assume content is part of this git repo
|
|
6
|
+
path: ../example/docs # Path where md files are located relative to this file
|
|
7
|
+
features:
|
|
8
|
+
bibleTooltips: true
|
|
9
|
+
sourceEdit: false
|
|
10
|
+
site:
|
|
11
|
+
canonical: 'https://life-and-dev.github.io/md-site'
|
|
12
|
+
name: 'Example Site'
|
|
13
|
+
themes:
|
|
14
|
+
light:
|
|
15
|
+
colors:
|
|
16
|
+
primary: '#802020'
|
|
17
|
+
secondary: '#ffffff'
|
|
18
|
+
selected: '#802020'
|
|
19
|
+
error: '#d64d5b'
|
|
20
|
+
warning: '#c29e4a'
|
|
21
|
+
info: '#548af7'
|
|
22
|
+
success: '#6aab73'
|
|
23
|
+
background: '#f9f9f9'
|
|
24
|
+
surface: '#ffffff'
|
|
25
|
+
surface-rail: '#f5f5f5'
|
|
26
|
+
surface-appbar: '#f0f0f0'
|
|
27
|
+
on-surface-rail: '#2a2a2a'
|
|
28
|
+
on-surface-appbar: '#000000'
|
|
29
|
+
on-background: '#292929'
|
|
30
|
+
on-surface: '#292929'
|
|
31
|
+
on-primary: '#ffffff'
|
|
32
|
+
on-secondary: '#ffffff'
|
|
33
|
+
on-selectable: '#292929'
|
|
34
|
+
on-selected: '#ffffff'
|
|
35
|
+
on-error: '#ffffff'
|
|
36
|
+
on-warning: '#ffffff'
|
|
37
|
+
on-info: '#ffffff'
|
|
38
|
+
on-success: '#ffffff'
|
|
39
|
+
outline: '#d7d7d7'
|
|
40
|
+
outline-bars: '#f4f4f4'
|
|
41
|
+
dark:
|
|
42
|
+
colors:
|
|
43
|
+
primary: '#af5f5f'
|
|
44
|
+
secondary: '#302d2b'
|
|
45
|
+
selected: '#af5f5f'
|
|
46
|
+
error: '#d64d5b'
|
|
47
|
+
warning: '#c29e4a'
|
|
48
|
+
info: '#548af7'
|
|
49
|
+
success: '#6aab73'
|
|
50
|
+
background: '#161616'
|
|
51
|
+
surface: '#0d0d0d'
|
|
52
|
+
surface-rail: '#252525'
|
|
53
|
+
surface-appbar: '#282828'
|
|
54
|
+
on-surface-rail: '#d0d0d0'
|
|
55
|
+
on-surface-appbar: '#ffffff'
|
|
56
|
+
on-background: '#d1d1d1'
|
|
57
|
+
on-surface: '#d1d1d1'
|
|
58
|
+
on-primary: '#111111'
|
|
59
|
+
on-secondary: '#111111'
|
|
60
|
+
on-selectable: '#d1d1d1'
|
|
61
|
+
on-selected: '#ffffff'
|
|
62
|
+
on-error: '#ffffff'
|
|
63
|
+
on-warning: '#ffffff'
|
|
64
|
+
on-info: '#ffffff'
|
|
65
|
+
on-success: '#ffffff'
|
|
66
|
+
outline: '#303030'
|
|
67
|
+
outline-bars: '#161616'
|