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.
@@ -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)