eidosui 0.2.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
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.
- eidos/__init__.py +0 -2
- eidos/components/headers.py +45 -6
- eidos/components/navigation.py +78 -0
- eidos/css/styles.css +137 -44
- eidos/js/eidos.js +112 -0
- eidos/plugins/__init__.py +1 -0
- eidos/plugins/markdown/__init__.py +21 -0
- eidos/plugins/markdown/components.py +53 -0
- eidos/plugins/markdown/css/markdown.css +283 -0
- eidos/plugins/markdown/extensions/__init__.py +1 -0
- eidos/plugins/markdown/extensions/alerts.py +134 -0
- eidos/plugins/markdown/renderer.py +58 -0
- eidos/tags.py +162 -67
- eidos/utils.py +16 -7
- {eidosui-0.2.0.dist-info → eidosui-0.4.0.dist-info}/METADATA +8 -5
- eidosui-0.4.0.dist-info/RECORD +22 -0
- eidosui-0.2.0.dist-info/RECORD +0 -13
- {eidosui-0.2.0.dist-info → eidosui-0.4.0.dist-info}/WHEEL +0 -0
- {eidosui-0.2.0.dist-info → eidosui-0.4.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,283 @@
|
|
1
|
+
/* EidosUI Markdown Styles - Theme-aware markdown rendering */
|
2
|
+
|
3
|
+
/* Base container */
|
4
|
+
.eidos-md {
|
5
|
+
color: var(--color-text);
|
6
|
+
line-height: var(--line-height-relaxed);
|
7
|
+
font-size: var(--font-size-base);
|
8
|
+
}
|
9
|
+
|
10
|
+
/* Headings */
|
11
|
+
.eidos-md h1 {
|
12
|
+
font-size: var(--font-size-4xl);
|
13
|
+
font-weight: var(--font-weight-bold);
|
14
|
+
color: var(--color-text);
|
15
|
+
margin-top: var(--space-2xl);
|
16
|
+
margin-bottom: var(--space-lg);
|
17
|
+
line-height: var(--line-height-tight);
|
18
|
+
}
|
19
|
+
|
20
|
+
.eidos-md h2 {
|
21
|
+
font-size: var(--font-size-3xl);
|
22
|
+
font-weight: var(--font-weight-semibold);
|
23
|
+
color: var(--color-text);
|
24
|
+
margin-top: var(--space-xl);
|
25
|
+
margin-bottom: var(--space-md);
|
26
|
+
line-height: var(--line-height-tight);
|
27
|
+
}
|
28
|
+
|
29
|
+
.eidos-md h3 {
|
30
|
+
font-size: var(--font-size-2xl);
|
31
|
+
font-weight: var(--font-weight-semibold);
|
32
|
+
color: var(--color-text);
|
33
|
+
margin-top: var(--space-lg);
|
34
|
+
margin-bottom: var(--space-md);
|
35
|
+
line-height: var(--line-height-snug);
|
36
|
+
}
|
37
|
+
|
38
|
+
.eidos-md h4 {
|
39
|
+
font-size: var(--font-size-xl);
|
40
|
+
font-weight: var(--font-weight-medium);
|
41
|
+
color: var(--color-text);
|
42
|
+
margin-top: var(--space-lg);
|
43
|
+
margin-bottom: var(--space-sm);
|
44
|
+
line-height: var(--line-height-snug);
|
45
|
+
}
|
46
|
+
|
47
|
+
.eidos-md h5 {
|
48
|
+
font-size: var(--font-size-lg);
|
49
|
+
font-weight: var(--font-weight-medium);
|
50
|
+
color: var(--color-text);
|
51
|
+
margin-top: var(--space-md);
|
52
|
+
margin-bottom: var(--space-sm);
|
53
|
+
line-height: var(--line-height-normal);
|
54
|
+
}
|
55
|
+
|
56
|
+
.eidos-md h6 {
|
57
|
+
font-size: var(--font-size-base);
|
58
|
+
font-weight: var(--font-weight-medium);
|
59
|
+
color: var(--color-text);
|
60
|
+
margin-top: var(--space-md);
|
61
|
+
margin-bottom: var(--space-sm);
|
62
|
+
line-height: var(--line-height-normal);
|
63
|
+
}
|
64
|
+
|
65
|
+
/* Paragraphs */
|
66
|
+
.eidos-md p {
|
67
|
+
margin-top: 0;
|
68
|
+
margin-bottom: var(--space-md);
|
69
|
+
}
|
70
|
+
|
71
|
+
/* Links */
|
72
|
+
.eidos-md a {
|
73
|
+
color: var(--color-primary);
|
74
|
+
text-decoration: underline;
|
75
|
+
text-underline-offset: 2px;
|
76
|
+
transition: color var(--transition-fast);
|
77
|
+
}
|
78
|
+
|
79
|
+
.eidos-md a:hover {
|
80
|
+
color: var(--color-primary-hover);
|
81
|
+
}
|
82
|
+
|
83
|
+
/* Lists */
|
84
|
+
.eidos-md ul {
|
85
|
+
margin-top: 0;
|
86
|
+
margin-bottom: var(--space-md);
|
87
|
+
padding-left: var(--space-lg);
|
88
|
+
list-style-type: disc;
|
89
|
+
}
|
90
|
+
|
91
|
+
.eidos-md ol {
|
92
|
+
margin-top: 0;
|
93
|
+
margin-bottom: var(--space-md);
|
94
|
+
padding-left: var(--space-lg);
|
95
|
+
list-style-type: decimal;
|
96
|
+
}
|
97
|
+
|
98
|
+
.eidos-md li {
|
99
|
+
margin-bottom: var(--space-xs);
|
100
|
+
}
|
101
|
+
|
102
|
+
/* Nested lists */
|
103
|
+
.eidos-md ul ul {
|
104
|
+
list-style-type: circle;
|
105
|
+
margin-top: var(--space-xs);
|
106
|
+
margin-bottom: 0;
|
107
|
+
}
|
108
|
+
|
109
|
+
.eidos-md ul ul ul {
|
110
|
+
list-style-type: square;
|
111
|
+
}
|
112
|
+
|
113
|
+
.eidos-md ol ol {
|
114
|
+
list-style-type: lower-alpha;
|
115
|
+
margin-top: var(--space-xs);
|
116
|
+
margin-bottom: 0;
|
117
|
+
}
|
118
|
+
|
119
|
+
.eidos-md ul ol,
|
120
|
+
.eidos-md ol ul {
|
121
|
+
margin-top: var(--space-xs);
|
122
|
+
margin-bottom: 0;
|
123
|
+
}
|
124
|
+
|
125
|
+
/* Code */
|
126
|
+
.eidos-md code {
|
127
|
+
background-color: var(--color-surface);
|
128
|
+
padding: calc(var(--space-xs) / 2) var(--space-xs);
|
129
|
+
border-radius: var(--radius-sm);
|
130
|
+
font-size: var(--font-size-sm);
|
131
|
+
font-family: ui-monospace, SFMono-Regular, "SF Mono", Consolas, "Liberation Mono", Menlo, monospace;
|
132
|
+
}
|
133
|
+
|
134
|
+
.eidos-md pre {
|
135
|
+
background-color: var(--color-surface);
|
136
|
+
border: var(--border) solid var(--color-border);
|
137
|
+
border-radius: var(--radius-md);
|
138
|
+
padding: var(--space-md);
|
139
|
+
overflow-x: auto;
|
140
|
+
margin-top: 0;
|
141
|
+
margin-bottom: var(--space-md);
|
142
|
+
}
|
143
|
+
|
144
|
+
.eidos-md pre code {
|
145
|
+
background-color: transparent;
|
146
|
+
padding: 0;
|
147
|
+
border-radius: 0;
|
148
|
+
font-size: var(--font-size-sm);
|
149
|
+
}
|
150
|
+
|
151
|
+
/* Blockquotes */
|
152
|
+
.eidos-md blockquote {
|
153
|
+
border-left: var(--border-4) solid var(--color-primary);
|
154
|
+
padding-left: var(--space-md);
|
155
|
+
margin: var(--space-lg) 0;
|
156
|
+
color: var(--color-text-muted);
|
157
|
+
}
|
158
|
+
|
159
|
+
.eidos-md blockquote p:last-child {
|
160
|
+
margin-bottom: 0;
|
161
|
+
}
|
162
|
+
|
163
|
+
/* Tables */
|
164
|
+
.eidos-md table {
|
165
|
+
width: 100%;
|
166
|
+
border-collapse: collapse;
|
167
|
+
margin: var(--space-lg) 0;
|
168
|
+
font-size: var(--font-size-sm);
|
169
|
+
}
|
170
|
+
|
171
|
+
.eidos-md th {
|
172
|
+
background-color: var(--color-surface);
|
173
|
+
font-weight: var(--font-weight-semibold);
|
174
|
+
padding: var(--space-sm) var(--space-md);
|
175
|
+
border-bottom: var(--border-2) solid var(--color-border);
|
176
|
+
text-align: left;
|
177
|
+
}
|
178
|
+
|
179
|
+
.eidos-md td {
|
180
|
+
padding: var(--space-sm) var(--space-md);
|
181
|
+
border-bottom: var(--border) solid var(--color-border);
|
182
|
+
}
|
183
|
+
|
184
|
+
.eidos-md tr:hover {
|
185
|
+
background-color: rgba(0, 0, 0, var(--opacity-5));
|
186
|
+
}
|
187
|
+
|
188
|
+
/* Horizontal rules */
|
189
|
+
.eidos-md hr {
|
190
|
+
border: none;
|
191
|
+
border-top: var(--border) solid var(--color-border);
|
192
|
+
margin: var(--space-xl) 0;
|
193
|
+
}
|
194
|
+
|
195
|
+
/* Images */
|
196
|
+
.eidos-md img {
|
197
|
+
max-width: 100%;
|
198
|
+
height: auto;
|
199
|
+
border-radius: var(--radius-md);
|
200
|
+
margin: var(--space-md) 0;
|
201
|
+
}
|
202
|
+
|
203
|
+
/* Strong and emphasis */
|
204
|
+
.eidos-md strong {
|
205
|
+
font-weight: var(--font-weight-semibold);
|
206
|
+
color: var(--color-text);
|
207
|
+
}
|
208
|
+
|
209
|
+
.eidos-md em {
|
210
|
+
font-style: italic;
|
211
|
+
}
|
212
|
+
|
213
|
+
/* Definition lists */
|
214
|
+
.eidos-md dl {
|
215
|
+
margin: var(--space-md) 0;
|
216
|
+
}
|
217
|
+
|
218
|
+
.eidos-md dt {
|
219
|
+
font-weight: var(--font-weight-semibold);
|
220
|
+
margin-top: var(--space-md);
|
221
|
+
}
|
222
|
+
|
223
|
+
.eidos-md dd {
|
224
|
+
margin-left: var(--space-lg);
|
225
|
+
margin-bottom: var(--space-sm);
|
226
|
+
}
|
227
|
+
|
228
|
+
/* GitHub-style Alerts */
|
229
|
+
.eidos-alert {
|
230
|
+
padding: var(--space-md);
|
231
|
+
border-radius: var(--radius-md);
|
232
|
+
margin: var(--space-lg) 0;
|
233
|
+
border: var(--border) solid;
|
234
|
+
}
|
235
|
+
|
236
|
+
.eidos-alert-header {
|
237
|
+
display: flex;
|
238
|
+
align-items: center;
|
239
|
+
gap: var(--space-sm);
|
240
|
+
margin-bottom: var(--space-sm);
|
241
|
+
font-weight: var(--font-weight-semibold);
|
242
|
+
}
|
243
|
+
|
244
|
+
.eidos-alert-icon {
|
245
|
+
font-size: var(--font-size-lg);
|
246
|
+
}
|
247
|
+
|
248
|
+
.eidos-alert-title {
|
249
|
+
font-size: var(--font-size-base);
|
250
|
+
}
|
251
|
+
|
252
|
+
.eidos-alert-content {
|
253
|
+
margin-left: calc(var(--font-size-lg) + var(--space-sm));
|
254
|
+
}
|
255
|
+
|
256
|
+
.eidos-alert-content > *:last-child {
|
257
|
+
margin-bottom: 0;
|
258
|
+
}
|
259
|
+
|
260
|
+
/* Alert variants */
|
261
|
+
.eidos-alert-info {
|
262
|
+
background-color: var(--color-info-light);
|
263
|
+
border-color: var(--color-info);
|
264
|
+
color: var(--color-info-dark);
|
265
|
+
}
|
266
|
+
|
267
|
+
.eidos-alert-success {
|
268
|
+
background-color: var(--color-success-light);
|
269
|
+
border-color: var(--color-success);
|
270
|
+
color: var(--color-success-dark);
|
271
|
+
}
|
272
|
+
|
273
|
+
.eidos-alert-warning {
|
274
|
+
background-color: var(--color-warning-light);
|
275
|
+
border-color: var(--color-warning);
|
276
|
+
color: var(--color-warning-dark);
|
277
|
+
}
|
278
|
+
|
279
|
+
.eidos-alert-error {
|
280
|
+
background-color: var(--color-error-light);
|
281
|
+
border-color: var(--color-error);
|
282
|
+
color: var(--color-error-dark);
|
283
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
# Markdown plugin extensions
|
@@ -0,0 +1,134 @@
|
|
1
|
+
"""GitHub-style alerts extension for markdown"""
|
2
|
+
|
3
|
+
import re
|
4
|
+
from markdown.extensions import Extension
|
5
|
+
from markdown.blockprocessors import BlockProcessor
|
6
|
+
import xml.etree.ElementTree as etree
|
7
|
+
from xml.etree.ElementTree import SubElement
|
8
|
+
|
9
|
+
|
10
|
+
class AlertBlockProcessor(BlockProcessor):
|
11
|
+
"""Process GitHub-style alert blocks"""
|
12
|
+
|
13
|
+
# Pattern to match > [!TYPE] at the start of a blockquote
|
14
|
+
RE_ALERT = re.compile(r'^> \[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\]', re.MULTILINE)
|
15
|
+
|
16
|
+
# Alert type configurations
|
17
|
+
ALERT_TYPES = {
|
18
|
+
'NOTE': {
|
19
|
+
'class': 'eidos-alert eidos-alert-info',
|
20
|
+
'icon': 'ℹ️',
|
21
|
+
'title': 'Note'
|
22
|
+
},
|
23
|
+
'TIP': {
|
24
|
+
'class': 'eidos-alert eidos-alert-success',
|
25
|
+
'icon': '💡',
|
26
|
+
'title': 'Tip'
|
27
|
+
},
|
28
|
+
'IMPORTANT': {
|
29
|
+
'class': 'eidos-alert eidos-alert-warning',
|
30
|
+
'icon': '❗',
|
31
|
+
'title': 'Important'
|
32
|
+
},
|
33
|
+
'WARNING': {
|
34
|
+
'class': 'eidos-alert eidos-alert-warning',
|
35
|
+
'icon': '⚠️',
|
36
|
+
'title': 'Warning'
|
37
|
+
},
|
38
|
+
'CAUTION': {
|
39
|
+
'class': 'eidos-alert eidos-alert-error',
|
40
|
+
'icon': '🔴',
|
41
|
+
'title': 'Caution'
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
def test(self, parent, block):
|
46
|
+
"""Test if the block is a GitHub-style alert"""
|
47
|
+
return bool(self.RE_ALERT.match(block))
|
48
|
+
|
49
|
+
def run(self, parent, blocks):
|
50
|
+
"""Process the alert block"""
|
51
|
+
block = blocks.pop(0)
|
52
|
+
|
53
|
+
# Extract alert type
|
54
|
+
match = self.RE_ALERT.match(block)
|
55
|
+
if not match:
|
56
|
+
return False
|
57
|
+
|
58
|
+
alert_type = match.group(1)
|
59
|
+
alert_config = self.ALERT_TYPES.get(alert_type, self.ALERT_TYPES['NOTE'])
|
60
|
+
|
61
|
+
# Create the alert container
|
62
|
+
alert_div = SubElement(parent, 'div')
|
63
|
+
alert_div.set('class', alert_config['class'])
|
64
|
+
|
65
|
+
# Add the header with icon and title
|
66
|
+
header = SubElement(alert_div, 'div')
|
67
|
+
header.set('class', 'eidos-alert-header')
|
68
|
+
|
69
|
+
icon_span = SubElement(header, 'span')
|
70
|
+
icon_span.set('class', 'eidos-alert-icon')
|
71
|
+
icon_span.text = alert_config['icon']
|
72
|
+
|
73
|
+
title_span = SubElement(header, 'span')
|
74
|
+
title_span.set('class', 'eidos-alert-title')
|
75
|
+
title_span.text = alert_config['title']
|
76
|
+
|
77
|
+
# Process the content
|
78
|
+
content_div = SubElement(alert_div, 'div')
|
79
|
+
content_div.set('class', 'eidos-alert-content')
|
80
|
+
|
81
|
+
# Remove the alert marker and process the remaining content
|
82
|
+
content = self.RE_ALERT.sub('', block)
|
83
|
+
|
84
|
+
# Handle multi-line content
|
85
|
+
lines = content.split('\n')
|
86
|
+
processed_lines = []
|
87
|
+
|
88
|
+
for line in lines:
|
89
|
+
# Remove leading '>' from each line
|
90
|
+
if line.startswith('>'):
|
91
|
+
line = line[1:].lstrip()
|
92
|
+
processed_lines.append(line)
|
93
|
+
|
94
|
+
# Join the content and parse it
|
95
|
+
content_text = '\n'.join(processed_lines).strip()
|
96
|
+
|
97
|
+
# Parse the content as markdown
|
98
|
+
if content_text:
|
99
|
+
# Create a temporary element to hold parsed content
|
100
|
+
temp_element = etree.Element('div')
|
101
|
+
self.parser.parseBlocks(temp_element, [content_text])
|
102
|
+
|
103
|
+
# Move all children to our content div
|
104
|
+
for child in temp_element:
|
105
|
+
content_div.append(child)
|
106
|
+
|
107
|
+
# If no children were added, add the text directly
|
108
|
+
if len(content_div) == 0:
|
109
|
+
p = SubElement(content_div, 'p')
|
110
|
+
p.text = content_text
|
111
|
+
|
112
|
+
# Continue processing subsequent blocks that might be part of the alert
|
113
|
+
while blocks and blocks[0].startswith('>'):
|
114
|
+
continuation = blocks.pop(0)
|
115
|
+
# Remove leading '>' and process
|
116
|
+
continuation_text = continuation[1:].lstrip() if continuation.startswith('>') else continuation
|
117
|
+
|
118
|
+
if continuation_text:
|
119
|
+
p = SubElement(content_div, 'p')
|
120
|
+
p.text = continuation_text
|
121
|
+
|
122
|
+
return True
|
123
|
+
|
124
|
+
|
125
|
+
class AlertExtension(Extension):
|
126
|
+
"""Add GitHub-style alerts to markdown"""
|
127
|
+
|
128
|
+
def extendMarkdown(self, md):
|
129
|
+
"""Add the alert processor to the markdown instance"""
|
130
|
+
md.parser.blockprocessors.register(
|
131
|
+
AlertBlockProcessor(md.parser),
|
132
|
+
'github_alerts',
|
133
|
+
175 # Priority - process before blockquote
|
134
|
+
)
|
@@ -0,0 +1,58 @@
|
|
1
|
+
"""Core markdown rendering with theme integration"""
|
2
|
+
|
3
|
+
import markdown
|
4
|
+
from typing import Optional, List, Union
|
5
|
+
from .extensions.alerts import AlertExtension
|
6
|
+
|
7
|
+
|
8
|
+
class MarkdownRenderer:
|
9
|
+
"""Core markdown rendering with theme integration"""
|
10
|
+
|
11
|
+
def __init__(self, extensions: Optional[List[Union[str, markdown.Extension]]] = None):
|
12
|
+
"""Initialize the renderer with optional extensions.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
extensions: List of markdown extension names or instances to enable
|
16
|
+
"""
|
17
|
+
self.extensions = extensions or []
|
18
|
+
# Add some useful default extensions
|
19
|
+
default_extensions = [
|
20
|
+
'fenced_code',
|
21
|
+
'tables',
|
22
|
+
'nl2br',
|
23
|
+
'sane_lists',
|
24
|
+
AlertExtension() # Add GitHub-style alerts
|
25
|
+
]
|
26
|
+
self.extensions.extend(default_extensions)
|
27
|
+
|
28
|
+
# Initialize the markdown processor
|
29
|
+
self.md = markdown.Markdown(extensions=self.extensions)
|
30
|
+
|
31
|
+
def render(self, markdown_text: str) -> str:
|
32
|
+
"""Convert markdown to themed HTML.
|
33
|
+
|
34
|
+
Args:
|
35
|
+
markdown_text: Raw markdown text to render
|
36
|
+
|
37
|
+
Returns:
|
38
|
+
HTML string wrapped with eidos-md class for styling
|
39
|
+
"""
|
40
|
+
# Reset the markdown processor to clear any state
|
41
|
+
self.md.reset()
|
42
|
+
|
43
|
+
# Convert markdown to HTML
|
44
|
+
html_content = self.md.convert(markdown_text)
|
45
|
+
|
46
|
+
# Wrap in a div with our markdown class for styling
|
47
|
+
return f'<div class="eidos-md">{html_content}</div>'
|
48
|
+
|
49
|
+
def add_extension(self, extension: str) -> None:
|
50
|
+
"""Add a markdown extension.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
extension: Name of the markdown extension to add
|
54
|
+
"""
|
55
|
+
if extension not in self.extensions:
|
56
|
+
self.extensions.append(extension)
|
57
|
+
# Recreate the markdown processor with new extensions
|
58
|
+
self.md = markdown.Markdown(extensions=self.extensions)
|