coradoc-html 1.1.7
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/lib/coradoc/html/base.rb +157 -0
- data/lib/coradoc/html/config.rb +467 -0
- data/lib/coradoc/html/converter_base.rb +177 -0
- data/lib/coradoc/html/converters/admonition.rb +180 -0
- data/lib/coradoc/html/converters/attribute.rb +68 -0
- data/lib/coradoc/html/converters/attribute_reference.rb +60 -0
- data/lib/coradoc/html/converters/audio.rb +165 -0
- data/lib/coradoc/html/converters/base.rb +615 -0
- data/lib/coradoc/html/converters/bibliography.rb +82 -0
- data/lib/coradoc/html/converters/bibliography_entry.rb +108 -0
- data/lib/coradoc/html/converters/block_image.rb +72 -0
- data/lib/coradoc/html/converters/bold.rb +34 -0
- data/lib/coradoc/html/converters/break.rb +32 -0
- data/lib/coradoc/html/converters/comment_block.rb +42 -0
- data/lib/coradoc/html/converters/comment_line.rb +54 -0
- data/lib/coradoc/html/converters/cross_reference.rb +59 -0
- data/lib/coradoc/html/converters/document.rb +108 -0
- data/lib/coradoc/html/converters/example.rb +114 -0
- data/lib/coradoc/html/converters/highlight.rb +34 -0
- data/lib/coradoc/html/converters/include.rb +68 -0
- data/lib/coradoc/html/converters/inline_image.rb +41 -0
- data/lib/coradoc/html/converters/italic.rb +34 -0
- data/lib/coradoc/html/converters/line_break.rb +31 -0
- data/lib/coradoc/html/converters/link.rb +46 -0
- data/lib/coradoc/html/converters/list_item.rb +75 -0
- data/lib/coradoc/html/converters/listing.rb +99 -0
- data/lib/coradoc/html/converters/literal.rb +102 -0
- data/lib/coradoc/html/converters/monospace.rb +34 -0
- data/lib/coradoc/html/converters/open.rb +78 -0
- data/lib/coradoc/html/converters/ordered.rb +53 -0
- data/lib/coradoc/html/converters/paragraph.rb +46 -0
- data/lib/coradoc/html/converters/quote.rb +113 -0
- data/lib/coradoc/html/converters/reviewer_comment.rb +74 -0
- data/lib/coradoc/html/converters/reviewer_note.rb +134 -0
- data/lib/coradoc/html/converters/section.rb +90 -0
- data/lib/coradoc/html/converters/sidebar.rb +113 -0
- data/lib/coradoc/html/converters/source.rb +137 -0
- data/lib/coradoc/html/converters/source_code.rb +16 -0
- data/lib/coradoc/html/converters/span.rb +61 -0
- data/lib/coradoc/html/converters/strikethrough.rb +34 -0
- data/lib/coradoc/html/converters/subscript.rb +34 -0
- data/lib/coradoc/html/converters/superscript.rb +34 -0
- data/lib/coradoc/html/converters/table.rb +85 -0
- data/lib/coradoc/html/converters/table_cell.rb +203 -0
- data/lib/coradoc/html/converters/table_row.rb +45 -0
- data/lib/coradoc/html/converters/template_html_converter.rb +105 -0
- data/lib/coradoc/html/converters/term.rb +58 -0
- data/lib/coradoc/html/converters/text_element.rb +44 -0
- data/lib/coradoc/html/converters/underline.rb +34 -0
- data/lib/coradoc/html/converters/unordered.rb +47 -0
- data/lib/coradoc/html/converters/verse.rb +105 -0
- data/lib/coradoc/html/converters/video.rb +179 -0
- data/lib/coradoc/html/element_mapping.rb +210 -0
- data/lib/coradoc/html/entity.rb +137 -0
- data/lib/coradoc/html/input/cleaner.rb +163 -0
- data/lib/coradoc/html/input/config.rb +79 -0
- data/lib/coradoc/html/input/converters/a.rb +90 -0
- data/lib/coradoc/html/input/converters/aside.rb +23 -0
- data/lib/coradoc/html/input/converters/audio.rb +50 -0
- data/lib/coradoc/html/input/converters/base.rb +116 -0
- data/lib/coradoc/html/input/converters/blockquote.rb +25 -0
- data/lib/coradoc/html/input/converters/br.rb +19 -0
- data/lib/coradoc/html/input/converters/bypass.rb +83 -0
- data/lib/coradoc/html/input/converters/code.rb +25 -0
- data/lib/coradoc/html/input/converters/div.rb +25 -0
- data/lib/coradoc/html/input/converters/dl.rb +106 -0
- data/lib/coradoc/html/input/converters/drop.rb +28 -0
- data/lib/coradoc/html/input/converters/em.rb +23 -0
- data/lib/coradoc/html/input/converters/figure.rb +58 -0
- data/lib/coradoc/html/input/converters/h.rb +76 -0
- data/lib/coradoc/html/input/converters/head.rb +30 -0
- data/lib/coradoc/html/input/converters/hr.rb +20 -0
- data/lib/coradoc/html/input/converters/ignore.rb +22 -0
- data/lib/coradoc/html/input/converters/img.rb +110 -0
- data/lib/coradoc/html/input/converters/li.rb +35 -0
- data/lib/coradoc/html/input/converters/mark.rb +21 -0
- data/lib/coradoc/html/input/converters/markup.rb +107 -0
- data/lib/coradoc/html/input/converters/math.rb +46 -0
- data/lib/coradoc/html/input/converters/ol.rb +46 -0
- data/lib/coradoc/html/input/converters/p.rb +81 -0
- data/lib/coradoc/html/input/converters/pass_through.rb +19 -0
- data/lib/coradoc/html/input/converters/pre.rb +59 -0
- data/lib/coradoc/html/input/converters/q.rb +24 -0
- data/lib/coradoc/html/input/converters/strong.rb +22 -0
- data/lib/coradoc/html/input/converters/sub.rb +40 -0
- data/lib/coradoc/html/input/converters/sup.rb +40 -0
- data/lib/coradoc/html/input/converters/table.rb +64 -0
- data/lib/coradoc/html/input/converters/td.rb +70 -0
- data/lib/coradoc/html/input/converters/text.rb +67 -0
- data/lib/coradoc/html/input/converters/th.rb +20 -0
- data/lib/coradoc/html/input/converters/tr.rb +28 -0
- data/lib/coradoc/html/input/converters/video.rb +53 -0
- data/lib/coradoc/html/input/converters.rb +122 -0
- data/lib/coradoc/html/input/errors.rb +22 -0
- data/lib/coradoc/html/input/html_converter.rb +170 -0
- data/lib/coradoc/html/input/plugin.rb +169 -0
- data/lib/coradoc/html/input/plugins/plateau.rb +229 -0
- data/lib/coradoc/html/input/postprocessor.rb +31 -0
- data/lib/coradoc/html/input.rb +68 -0
- data/lib/coradoc/html/output.rb +95 -0
- data/lib/coradoc/html/renderer.rb +409 -0
- data/lib/coradoc/html/spa.rb +309 -0
- data/lib/coradoc/html/static.rb +293 -0
- data/lib/coradoc/html/template_config.rb +151 -0
- data/lib/coradoc/html/template_helpers.rb +58 -0
- data/lib/coradoc/html/template_locator.rb +114 -0
- data/lib/coradoc/html/theme/base.rb +231 -0
- data/lib/coradoc/html/theme/classic_renderer.rb +390 -0
- data/lib/coradoc/html/theme/modern/components/ui_components.rb +344 -0
- data/lib/coradoc/html/theme/modern/css_generator.rb +311 -0
- data/lib/coradoc/html/theme/modern/javascript_generator.rb +314 -0
- data/lib/coradoc/html/theme/modern/serializers/document_serializer.rb +382 -0
- data/lib/coradoc/html/theme/modern/tailwind_config_builder.rb +164 -0
- data/lib/coradoc/html/theme/modern/vue_template_generator.rb +374 -0
- data/lib/coradoc/html/theme/modern_renderer.rb +250 -0
- data/lib/coradoc/html/theme/registry.rb +153 -0
- data/lib/coradoc/html/theme.rb +13 -0
- data/lib/coradoc/html/transform/from_core_model.rb +32 -0
- data/lib/coradoc/html/transform/to_core_model.rb +39 -0
- data/lib/coradoc/html/version.rb +7 -0
- data/lib/coradoc/html.rb +255 -0
- metadata +264 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Html
|
|
5
|
+
module Theme
|
|
6
|
+
class ModernRenderer
|
|
7
|
+
# Build Tailwind CSS configuration
|
|
8
|
+
module TailwindConfigBuilder
|
|
9
|
+
class << self
|
|
10
|
+
# Build Tailwind configuration
|
|
11
|
+
#
|
|
12
|
+
# @param config [Hash] Theme configuration
|
|
13
|
+
# @return [String] Tailwind configuration script
|
|
14
|
+
def build(config)
|
|
15
|
+
primary = config[:primary_color] || '#6366f1'
|
|
16
|
+
accent = config[:accent_color] || '#8b5cf6'
|
|
17
|
+
|
|
18
|
+
# Parse hex colors to RGB for opacity variants
|
|
19
|
+
primary_rgb = hex_to_rgb(primary)
|
|
20
|
+
accent_rgb = hex_to_rgb(accent)
|
|
21
|
+
|
|
22
|
+
<<~JS
|
|
23
|
+
tailwind.config = {
|
|
24
|
+
darkMode: 'class',
|
|
25
|
+
theme: {
|
|
26
|
+
extend: {
|
|
27
|
+
colors: {
|
|
28
|
+
primary: {
|
|
29
|
+
DEFAULT: '#{primary}',
|
|
30
|
+
rgb: '#{primary_rgb}',
|
|
31
|
+
50: '#{adjust_color(primary, 40)}',
|
|
32
|
+
100: '#{adjust_color(primary, 30)}',
|
|
33
|
+
200: '#{adjust_color(primary, 20)}',
|
|
34
|
+
300: '#{adjust_color(primary, 10)}',
|
|
35
|
+
400: '#{adjust_color(primary, 5)}',
|
|
36
|
+
500: '#{primary}',
|
|
37
|
+
600: '#{adjust_color(primary, -5)}',
|
|
38
|
+
700: '#{adjust_color(primary, -10)}',
|
|
39
|
+
800: '#{adjust_color(primary, -20)}',
|
|
40
|
+
900: '#{adjust_color(primary, -30)}',
|
|
41
|
+
},
|
|
42
|
+
accent: {
|
|
43
|
+
DEFAULT: '#{accent}',
|
|
44
|
+
rgb: '#{accent_rgb}',
|
|
45
|
+
50: '#{adjust_color(accent, 40)}',
|
|
46
|
+
100: '#{adjust_color(accent, 30)}',
|
|
47
|
+
200: '#{adjust_color(accent, 20)}',
|
|
48
|
+
300: '#{adjust_color(accent, 10)}',
|
|
49
|
+
400: '#{adjust_color(accent, 5)}',
|
|
50
|
+
500: '#{accent}',
|
|
51
|
+
600: '#{adjust_color(accent, -5)}',
|
|
52
|
+
700: '#{adjust_color(accent, -10)}',
|
|
53
|
+
800: '#{adjust_color(accent, -20)}',
|
|
54
|
+
900: '#{adjust_color(accent, -30)}',
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
fontFamily: {
|
|
58
|
+
sans: [
|
|
59
|
+
'system-ui',
|
|
60
|
+
'-apple-system',
|
|
61
|
+
'BlinkMacSystemFont',
|
|
62
|
+
'Segoe UI',
|
|
63
|
+
'Roboto',
|
|
64
|
+
'sans-serif',
|
|
65
|
+
],
|
|
66
|
+
mono: [
|
|
67
|
+
'ui-monospace',
|
|
68
|
+
'SFMono-Regular',
|
|
69
|
+
'Menlo',
|
|
70
|
+
'Monaco',
|
|
71
|
+
'Consolas',
|
|
72
|
+
'monospace',
|
|
73
|
+
],
|
|
74
|
+
},
|
|
75
|
+
maxWidth: {
|
|
76
|
+
'content': '#{config[:content_width] || '65ch'}',
|
|
77
|
+
'sidebar': '#{config[:sidebar_width] || '280px'}',
|
|
78
|
+
},
|
|
79
|
+
animation: {
|
|
80
|
+
'fade-in': 'fadeIn #{config[:animation_duration] || '300ms'} ease-out',
|
|
81
|
+
'slide-up': 'slideUp #{config[:animation_duration] || '300ms'} ease-out',
|
|
82
|
+
'slide-down': 'slideDown #{config[:animation_duration] || '300ms'} ease-out',
|
|
83
|
+
'scale-in': 'scaleIn #{config[:animation_duration] || '300ms'} ease-out',
|
|
84
|
+
},
|
|
85
|
+
keyframes: {
|
|
86
|
+
fadeIn: {
|
|
87
|
+
'0%': { opacity: '0' },
|
|
88
|
+
'100%': { opacity: '1' },
|
|
89
|
+
},
|
|
90
|
+
slideUp: {
|
|
91
|
+
'0%': { transform: 'translateY(10px)', opacity: '0' },
|
|
92
|
+
'100%': { transform: 'translateY(0)', opacity: '1' },
|
|
93
|
+
},
|
|
94
|
+
slideDown: {
|
|
95
|
+
'0%': { transform: 'translateY(-10px)', opacity: '0' },
|
|
96
|
+
'100%': { transform: 'translateY(0)', opacity: '1' },
|
|
97
|
+
},
|
|
98
|
+
scaleIn: {
|
|
99
|
+
'0%': { transform: 'scale(0.95)', opacity: '0' },
|
|
100
|
+
'100%': { transform: 'scale(1)', opacity: '1' },
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
}
|
|
106
|
+
JS
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
private
|
|
110
|
+
|
|
111
|
+
# Convert hex color to RGB format
|
|
112
|
+
#
|
|
113
|
+
# @param hex [String] Hex color code
|
|
114
|
+
# @return [String] RGB format
|
|
115
|
+
def hex_to_rgb(hex)
|
|
116
|
+
hex = hex.delete('#')
|
|
117
|
+
case hex.length
|
|
118
|
+
when 3
|
|
119
|
+
r, g, b = hex.chars.map { |c| "#{c}#{c}".hex }
|
|
120
|
+
when 6
|
|
121
|
+
r = hex[0..1].hex
|
|
122
|
+
g = hex[2..3].hex
|
|
123
|
+
b = hex[4..5].hex
|
|
124
|
+
else
|
|
125
|
+
return '99, 102, 241' # Default to indigo-500
|
|
126
|
+
end
|
|
127
|
+
"#{r}, #{g}, #{b}"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Adjust color lightness
|
|
131
|
+
#
|
|
132
|
+
# @param hex [String] Hex color code
|
|
133
|
+
# @param amount [Integer] Amount to adjust (-100 to 100)
|
|
134
|
+
# @return [String] Adjusted hex color
|
|
135
|
+
def adjust_color(hex, amount)
|
|
136
|
+
# Simple color adjustment
|
|
137
|
+
hex = hex.delete('#')
|
|
138
|
+
r = hex[0..1].hex
|
|
139
|
+
g = hex[2..3].hex
|
|
140
|
+
b = hex[4..5].hex
|
|
141
|
+
|
|
142
|
+
amount = amount.to_i
|
|
143
|
+
if amount.positive?
|
|
144
|
+
# Lighten
|
|
145
|
+
factor = 1 + (amount / 100.0)
|
|
146
|
+
r = [(r * factor).round, 255].min
|
|
147
|
+
g = [(g * factor).round, 255].min
|
|
148
|
+
b = [(b * factor).round, 255].min
|
|
149
|
+
elsif amount.negative?
|
|
150
|
+
# Darken
|
|
151
|
+
factor = 1 - (amount.abs / 100.0)
|
|
152
|
+
r = [(r * factor).round, 0].max
|
|
153
|
+
g = [(g * factor).round, 0].max
|
|
154
|
+
b = [(b * factor).round, 0].max
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
format('%02x%02x%02x', r, g, b)
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
module Html
|
|
5
|
+
module Theme
|
|
6
|
+
class ModernRenderer
|
|
7
|
+
# Vue component templates for rendering document elements
|
|
8
|
+
module VueTemplates
|
|
9
|
+
class << self
|
|
10
|
+
# Generate Vue component template for a given element type
|
|
11
|
+
#
|
|
12
|
+
# @param type [String] Element type
|
|
13
|
+
# @return [String] Vue component template
|
|
14
|
+
def template_for(type)
|
|
15
|
+
case type
|
|
16
|
+
when 'document' then document_template
|
|
17
|
+
when 'section' then section_template
|
|
18
|
+
when 'paragraph' then paragraph_template
|
|
19
|
+
when 'admonition' then admonition_template
|
|
20
|
+
when 'list' then list_template
|
|
21
|
+
when 'block' then block_template
|
|
22
|
+
when 'table' then table_template
|
|
23
|
+
when 'image' then image_template
|
|
24
|
+
when 'link' then link_template
|
|
25
|
+
when 'xref' then cross_reference_template
|
|
26
|
+
else
|
|
27
|
+
generic_template
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Document component template
|
|
32
|
+
#
|
|
33
|
+
# @return [String] Document template
|
|
34
|
+
def document_template
|
|
35
|
+
<<~VUE
|
|
36
|
+
<div class="document-wrapper">
|
|
37
|
+
<header v-if="document.header" class="document-header mb-8">
|
|
38
|
+
<h1 class="text-4xl font-bold text-gray-900 dark:text-white">
|
|
39
|
+
{{ document.header.title?.text || document.title || 'Untitled Document' }}
|
|
40
|
+
</h1>
|
|
41
|
+
<p v-if="document.header?.author" class="text-gray-600 dark:text-gray-400 mt-2">
|
|
42
|
+
{{ document.header.author }}
|
|
43
|
+
</p>
|
|
44
|
+
</header>
|
|
45
|
+
|
|
46
|
+
<div class="toc-toggle mb-4 flex justify-end">
|
|
47
|
+
<button
|
|
48
|
+
@click="tocCollapsed = !tocCollapsed"
|
|
49
|
+
class="px-4 py-2 rounded-lg bg-white dark:bg-gray-800 shadow-md hover:shadow-lg transition-all flex items-center gap-2"
|
|
50
|
+
>
|
|
51
|
+
<svg class="w-5 h-5" :class="{ 'rotate-180': !tocCollapsed }" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
52
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
|
|
53
|
+
</svg>
|
|
54
|
+
<span>{{ tocCollapsed ? 'Show' : 'Hide' }} Contents</span>
|
|
55
|
+
</button>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div class="flex gap-8">
|
|
59
|
+
<!-- TOC Sidebar -->
|
|
60
|
+
<aside v-if="showToc" class="toc-sidebar" :class="{ 'collapsed': tocCollapsed }">
|
|
61
|
+
<nav class="toc-nav">
|
|
62
|
+
<h3 class="text-lg font-semibold mb-4 text-gray-900 dark:text-white">Contents</h3>
|
|
63
|
+
<ul class="space-y-1">
|
|
64
|
+
<li
|
|
65
|
+
v-for="item in tocItems"
|
|
66
|
+
:key="item.id"
|
|
67
|
+
@click="scrollToSection(item.id)"
|
|
68
|
+
class="toc-item"
|
|
69
|
+
:class="{ 'active': activeSection === item.id }"
|
|
70
|
+
:style="{ paddingLeft: (item.level * 0.75) + 'rem' }"
|
|
71
|
+
>
|
|
72
|
+
{{ item.title }}
|
|
73
|
+
</li>
|
|
74
|
+
</ul>
|
|
75
|
+
</nav>
|
|
76
|
+
</aside>
|
|
77
|
+
|
|
78
|
+
<!-- Main Content -->
|
|
79
|
+
<main class="flex-1 min-w-0">
|
|
80
|
+
<article class="prose prose-lg dark:prose-invert max-w-none">
|
|
81
|
+
<template v-for="(section, index) in document.sections" :key="section.id || index">
|
|
82
|
+
<component :is="'section-' + section.type" :data="section" v-if="section.type === 'section'" />
|
|
83
|
+
<component :is="'element-' + section.type" :data="section" v-else />
|
|
84
|
+
</template>
|
|
85
|
+
</article>
|
|
86
|
+
</main>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
VUE
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Section component template
|
|
93
|
+
#
|
|
94
|
+
# @return [String] Section template
|
|
95
|
+
def section_template
|
|
96
|
+
<<~VUE
|
|
97
|
+
<section :id="data.id" class="section scroll-mt-20">
|
|
98
|
+
<h1 v-if="data.level === 1" class="section-title text-3xl font-bold mb-4">{{ data.title?.text || data.title }}</h1>
|
|
99
|
+
<h2 v-else-if="data.level === 2" class="section-title text-2xl font-semibold mb-3">{{ data.title?.text || data.title }}</h2>
|
|
100
|
+
<h3 v-else-if="data.level === 3" class="section-title text-xl font-semibold mb-2">{{ data.title?.text || data.title }}</h3>
|
|
101
|
+
<h4 v-else-if="data.level === 4" class="section-title text-lg font-semibold mb-2">{{ data.title?.text || data.title }}</h4>
|
|
102
|
+
<h5 v-else-if="data.level === 5" class="section-title text-base font-semibold mb-1">{{ data.title?.text || data.title }}</h5>
|
|
103
|
+
<h2 v-else class="section-title text-2xl font-semibold mb-3">{{ data.title?.text || data.title }}</h2>
|
|
104
|
+
|
|
105
|
+
<div class="section-content">
|
|
106
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
107
|
+
<component :is="'element-' + item.type" :data="item" />
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<template v-if="data.sections && data.sections.length > 0">
|
|
111
|
+
<template v-for="(subsection, index) in data.sections" :key="subsection.id || index">
|
|
112
|
+
<component :is="'section-section'" :data="subsection" />
|
|
113
|
+
</template>
|
|
114
|
+
</template>
|
|
115
|
+
</div>
|
|
116
|
+
</section>
|
|
117
|
+
VUE
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Paragraph component template
|
|
121
|
+
#
|
|
122
|
+
# @return [String] Paragraph template
|
|
123
|
+
def paragraph_template
|
|
124
|
+
<<~VUE
|
|
125
|
+
<p :id="data.id" class="paragraph leading-relaxed">
|
|
126
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
127
|
+
<component :is="'inline-' + item.type" v-if="item.type" :data="item" />
|
|
128
|
+
<span v-else>{{ item.content || item }}</span>
|
|
129
|
+
</template>
|
|
130
|
+
</p>
|
|
131
|
+
VUE
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Admonition component template
|
|
135
|
+
#
|
|
136
|
+
# @return [String] Admonition template
|
|
137
|
+
def admonition_template
|
|
138
|
+
<<~VUE
|
|
139
|
+
<div :id="data.id" :class="['admonition', 'admonition-' + data.style.toLowerCase()]">
|
|
140
|
+
<div class="admonition-title font-semibold mb-2 flex items-center gap-2">
|
|
141
|
+
<span class="admonition-icon">{{ admonitionIcon(data.style) }}</span>
|
|
142
|
+
<span>{{ data.title || admonitionTitle(data.style) }}</span>
|
|
143
|
+
</div>
|
|
144
|
+
<div class="admonition-content">
|
|
145
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
146
|
+
<component :is="'element-' + item.type" :data="item" />
|
|
147
|
+
</template>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
VUE
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# List component template
|
|
154
|
+
#
|
|
155
|
+
# @return [String] List template
|
|
156
|
+
def list_template
|
|
157
|
+
<<~VUE
|
|
158
|
+
<div :id="data.id" class="list-wrapper">
|
|
159
|
+
<ul v-if="data.list_type === 'unordered'" class="list-disc list-inside space-y-1">
|
|
160
|
+
<li v-for="(item, index) in data.items" :key="item.id || index" class="list-item">
|
|
161
|
+
<template v-for="(content, idx) in item.content" :key="content.id || idx">
|
|
162
|
+
<component :is="'element-' + content.type" :data="content" />
|
|
163
|
+
</template>
|
|
164
|
+
</li>
|
|
165
|
+
</ul>
|
|
166
|
+
|
|
167
|
+
<ol v-else-if="data.list_type === 'ordered'" class="list-decimal list-inside space-y-1">
|
|
168
|
+
<li v-for="(item, index) in data.items" :key="item.id || index" class="list-item">
|
|
169
|
+
<template v-for="(content, idx) in item.content" :key="content.id || idx">
|
|
170
|
+
<component :is="'element-' + content.type" :data="content" />
|
|
171
|
+
</template>
|
|
172
|
+
</li>
|
|
173
|
+
</ol>
|
|
174
|
+
|
|
175
|
+
<dl v-else-if="data.list_type === 'definition'" class="space-y-2">
|
|
176
|
+
<template v-for="(item, index) in data.items" :key="item.id || index">
|
|
177
|
+
<div class="definition-item">
|
|
178
|
+
<dt class="font-semibold">{{ item.terms?.join(', ') }}</dt>
|
|
179
|
+
<dd class="ml-4">
|
|
180
|
+
<template v-for="(content, idx) in item.content" :key="content.id || idx">
|
|
181
|
+
<component :is="'element-' + content.type" :data="content" />
|
|
182
|
+
</template>
|
|
183
|
+
</dd>
|
|
184
|
+
</div>
|
|
185
|
+
</template>
|
|
186
|
+
</dl>
|
|
187
|
+
</div>
|
|
188
|
+
VUE
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Block component template (listing, literal, example, quote)
|
|
192
|
+
#
|
|
193
|
+
# @return [String] Block template
|
|
194
|
+
def block_template
|
|
195
|
+
<<~VUE
|
|
196
|
+
<div :id="data.id" :class="['block', 'block-' + data.block_type.toLowerCase()]">
|
|
197
|
+
<div v-if="data.title" class="block-title font-semibold mb-2">{{ data.title }}</div>
|
|
198
|
+
|
|
199
|
+
<div v-if="data.block_type === 'listing' || data.block_type === 'literal'" class="bg-gray-100 dark:bg-gray-800 rounded-lg p-4 overflow-x-auto">
|
|
200
|
+
<pre class="whitespace-pre-wrap">{{ blockContent(data) }}</pre>
|
|
201
|
+
<button
|
|
202
|
+
@click="copyCode(blockContent(data), $event)"
|
|
203
|
+
class="copy-code-button"
|
|
204
|
+
>
|
|
205
|
+
Copy
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
|
|
209
|
+
<blockquote v-else-if="data.block_type === 'quote'" class="border-l-4 border-primary-500 pl-4 italic">
|
|
210
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
211
|
+
<component :is="'element-' + item.type" :data="item" />
|
|
212
|
+
</template>
|
|
213
|
+
</blockquote>
|
|
214
|
+
|
|
215
|
+
<div v-else class="block-content">
|
|
216
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
217
|
+
<component :is="'element-' + item.type" :data="item" />
|
|
218
|
+
</template>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
VUE
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# Table component template
|
|
225
|
+
#
|
|
226
|
+
# @return [String] Table template
|
|
227
|
+
def table_template
|
|
228
|
+
<<~VUE
|
|
229
|
+
<div :id="data.id" class="table-wrapper overflow-x-auto my-4">
|
|
230
|
+
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
|
231
|
+
<caption v-if="data.caption" class="caption-bottom text-sm text-gray-600 dark:text-gray-400 py-2">
|
|
232
|
+
{{ data.caption }}
|
|
233
|
+
</caption>
|
|
234
|
+
|
|
235
|
+
<thead v-if="data.header && data.header.length > 0" class="bg-gray-50 dark:bg-gray-800">
|
|
236
|
+
<tr>
|
|
237
|
+
<th
|
|
238
|
+
v-for="(cell, index) in data.header[0].cells"
|
|
239
|
+
:key="cell.id || index"
|
|
240
|
+
class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider"
|
|
241
|
+
:colspan="cell.colspan"
|
|
242
|
+
:rowspan="cell.rowspan"
|
|
243
|
+
>
|
|
244
|
+
{{ cellContent(cell) }}
|
|
245
|
+
</th>
|
|
246
|
+
</tr>
|
|
247
|
+
</thead>
|
|
248
|
+
|
|
249
|
+
<tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
|
|
250
|
+
<tr v-for="(row, rowIndex) in data.body" :key="row.id || rowIndex" class="hover:bg-gray-50 dark:hover:bg-gray-800">
|
|
251
|
+
<td
|
|
252
|
+
v-for="(cell, cellIndex) in row.cells"
|
|
253
|
+
:key="cell.id || cellIndex"
|
|
254
|
+
class="px-4 py-2 whitespace-nowrap text-sm text-gray-900 dark:text-gray-100"
|
|
255
|
+
:colspan="cell.colspan"
|
|
256
|
+
:rowspan="cell.rowspan"
|
|
257
|
+
>
|
|
258
|
+
<template v-for="(item, index) in cell.content" :key="item.id || index">
|
|
259
|
+
<component :is="'element-' + item.type" :data="item" />
|
|
260
|
+
</template>
|
|
261
|
+
</td>
|
|
262
|
+
</tr>
|
|
263
|
+
</tbody>
|
|
264
|
+
</table>
|
|
265
|
+
</div>
|
|
266
|
+
VUE
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Image component template
|
|
270
|
+
#
|
|
271
|
+
# @return [String] Image template
|
|
272
|
+
def image_template
|
|
273
|
+
<<~VUE
|
|
274
|
+
<figure :id="data.id" :class="data.inline ? 'inline-image' : 'block-image'" class="my-4">
|
|
275
|
+
<img
|
|
276
|
+
:src="data.src"
|
|
277
|
+
:alt="data.alt || ''"
|
|
278
|
+
:title="data.title"
|
|
279
|
+
:width="data.width"
|
|
280
|
+
:height="data.height"
|
|
281
|
+
:class="data.inline ? 'inline max-h-6 align-middle' : 'w-full rounded-lg shadow-lg'"
|
|
282
|
+
/>
|
|
283
|
+
<figcaption v-if="data.title" class="text-center text-sm text-gray-600 dark:text-gray-400 mt-2">
|
|
284
|
+
{{ data.title }}
|
|
285
|
+
</figcaption>
|
|
286
|
+
</figure>
|
|
287
|
+
VUE
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Link component template
|
|
291
|
+
#
|
|
292
|
+
# @return [String] Link template
|
|
293
|
+
def link_template
|
|
294
|
+
<<~VUE
|
|
295
|
+
<a
|
|
296
|
+
:href="data.href"
|
|
297
|
+
:target="data.target || '_blank'"
|
|
298
|
+
:rel="data.target === '_blank' ? 'noopener noreferrer' : null"
|
|
299
|
+
class="text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-300 underline"
|
|
300
|
+
>
|
|
301
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
302
|
+
<component :is="'inline-' + item.type" v-if="item.type" :data="item" />
|
|
303
|
+
<span v-else>{{ item.content || item }}</span>
|
|
304
|
+
</template>
|
|
305
|
+
</a>
|
|
306
|
+
VUE
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# Cross reference component template
|
|
310
|
+
#
|
|
311
|
+
# @return [String] Cross reference template
|
|
312
|
+
def cross_reference_template
|
|
313
|
+
<<~VUE
|
|
314
|
+
<a
|
|
315
|
+
:href="'#' + data.target"
|
|
316
|
+
@click.prevent="scrollToSection(data.target)"
|
|
317
|
+
class="text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-300 underline cursor-pointer"
|
|
318
|
+
>
|
|
319
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
320
|
+
<component :is="'inline-' + item.type" v-if="item.type" :data="item" />
|
|
321
|
+
<span v-else>{{ item.content || item }}</span>
|
|
322
|
+
</template>
|
|
323
|
+
</a>
|
|
324
|
+
VUE
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
# Generic component template (fallback)
|
|
328
|
+
#
|
|
329
|
+
# @return [String] Generic template
|
|
330
|
+
def generic_template
|
|
331
|
+
<<~VUE
|
|
332
|
+
<div :id="data.id" :class="['element', 'element-' + data.type]">
|
|
333
|
+
<template v-if="data.content">
|
|
334
|
+
<template v-for="(item, index) in data.content" :key="item.id || index">
|
|
335
|
+
<component :is="'element-' + item.type" :data="item" v-if="item.type" />
|
|
336
|
+
<span v-else>{{ item.content || item }}</span>
|
|
337
|
+
</template>
|
|
338
|
+
</template>
|
|
339
|
+
<span v-else>{{ data.content || data }}</span>
|
|
340
|
+
</div>
|
|
341
|
+
VUE
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
private
|
|
345
|
+
|
|
346
|
+
# Get admonition icon
|
|
347
|
+
#
|
|
348
|
+
# @param style [String] Admonition style
|
|
349
|
+
# @return [String] Icon emoji
|
|
350
|
+
def admonition_icon(style)
|
|
351
|
+
case style&.downcase
|
|
352
|
+
when 'note' then '📝'
|
|
353
|
+
when 'tip' then '💡'
|
|
354
|
+
when 'warning' then '⚠️'
|
|
355
|
+
when 'caution' then '🔥'
|
|
356
|
+
when 'important' then '❗'
|
|
357
|
+
else
|
|
358
|
+
'ℹ️'
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
# Get admonition title
|
|
363
|
+
#
|
|
364
|
+
# @param style [String] Admonition style
|
|
365
|
+
# @return [String] Title text
|
|
366
|
+
def admonition_title(style)
|
|
367
|
+
style&.capitalize || 'Note'
|
|
368
|
+
end
|
|
369
|
+
end
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|