pyact-cli 0.2.0__tar.gz → 0.4.0__tar.gz

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,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyact-cli
3
+ Version: 0.4.0
4
+ Summary: A CLI tool to compile .pamd files to Markdown.
5
+ Home-page: https://github.com/Abstergo2003/PyAct
6
+ Author: Abstergo2003
7
+ Author-email:
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Dynamic: author
14
+ Dynamic: classifier
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: requires-python
19
+ Dynamic: summary
20
+
21
+ <div align="center">
22
+ <h1>⚡ PyAct CLI</h1>
23
+ <p><b>The engine behind dynamic Markdown.</b></p>
24
+ </div>
25
+
26
+ The **PyAct CLI** allows you to compile `.pamd` files into standard Markdown (`.md`). It evaluates your Python logic, injects your variables, and builds complex markdown elements effortlessly.
27
+
28
+ ## 🚀 Installation
29
+
30
+ Install it directly from PyPI:
31
+ ```bash
32
+ pip install pyact-cli
33
+ ```
34
+
35
+ ## 📖 How it works
36
+
37
+ A `.pamd` file is split into a **Python cell** and a **Markdown cell**.
38
+
39
+ ### 1. Write your Python logic
40
+ Define a `context()` function that returns the variables you want to inject. You can even use the built-in `pamd_helpers` to do heavy lifting!
41
+
42
+ ```python
43
+ import pamd_helpers
44
+
45
+ def context():
46
+ # Automatically converts the lambda to LaTeX!
47
+ equation = pamd_helpers.equation(lambda x: x**2, [5])
48
+
49
+ return {
50
+ "title": "My PyAct Document",
51
+ "math": equation
52
+ }
53
+ ```
54
+
55
+ ### 2. Write your Markdown
56
+ Use `<ctx>` tags to inject your Python variables anywhere in your text.
57
+
58
+ ```markdown
59
+ # <ctx>title</ctx>
60
+
61
+ Here is my dynamically generated equation:
62
+ <ctx>math</ctx>
63
+
64
+ You can also include other pamd files as templates!
65
+ <tmp>path/to/another_file</tmp>
66
+ ```
67
+
68
+ ### 3. Compile!
69
+ Run the compiler from your terminal to generate your final Markdown document:
70
+ ```bash
71
+ pyact path/to/your_file.pamd -o output.md
72
+ ```
73
+
74
+ That's it! You now have a standard Markdown file ready to be shared, published, or converted to PDF.
@@ -0,0 +1,54 @@
1
+ <div align="center">
2
+ <h1>⚡ PyAct CLI</h1>
3
+ <p><b>The engine behind dynamic Markdown.</b></p>
4
+ </div>
5
+
6
+ The **PyAct CLI** allows you to compile `.pamd` files into standard Markdown (`.md`). It evaluates your Python logic, injects your variables, and builds complex markdown elements effortlessly.
7
+
8
+ ## 🚀 Installation
9
+
10
+ Install it directly from PyPI:
11
+ ```bash
12
+ pip install pyact-cli
13
+ ```
14
+
15
+ ## 📖 How it works
16
+
17
+ A `.pamd` file is split into a **Python cell** and a **Markdown cell**.
18
+
19
+ ### 1. Write your Python logic
20
+ Define a `context()` function that returns the variables you want to inject. You can even use the built-in `pamd_helpers` to do heavy lifting!
21
+
22
+ ```python
23
+ import pamd_helpers
24
+
25
+ def context():
26
+ # Automatically converts the lambda to LaTeX!
27
+ equation = pamd_helpers.equation(lambda x: x**2, [5])
28
+
29
+ return {
30
+ "title": "My PyAct Document",
31
+ "math": equation
32
+ }
33
+ ```
34
+
35
+ ### 2. Write your Markdown
36
+ Use `<ctx>` tags to inject your Python variables anywhere in your text.
37
+
38
+ ```markdown
39
+ # <ctx>title</ctx>
40
+
41
+ Here is my dynamically generated equation:
42
+ <ctx>math</ctx>
43
+
44
+ You can also include other pamd files as templates!
45
+ <tmp>path/to/another_file</tmp>
46
+ ```
47
+
48
+ ### 3. Compile!
49
+ Run the compiler from your terminal to generate your final Markdown document:
50
+ ```bash
51
+ pyact path/to/your_file.pamd -o output.md
52
+ ```
53
+
54
+ That's it! You now have a standard Markdown file ready to be shared, published, or converted to PDF.
@@ -0,0 +1 @@
1
+ __version__ = "0.4.0"
@@ -7,6 +7,8 @@ def main():
7
7
  parser = argparse.ArgumentParser(description="PyAct CLI - PAMD to Markdown compiler")
8
8
  parser.add_argument("input", help="Path to the main .pamd file")
9
9
  parser.add_argument("-o", "--output", help="Output file path (default prints to stdout)")
10
+ parser.add_argument("--docx", help="Also generate a DOCX file at this path")
11
+ parser.add_argument("--css", help="Optional CSS file path to style the DOCX")
10
12
 
11
13
  args = parser.parse_args()
12
14
 
@@ -27,6 +29,26 @@ def main():
27
29
  print(f"Successfully compiled to {args.output}")
28
30
  else:
29
31
  print(content)
32
+
33
+ if args.docx:
34
+ from .mdTOword import style_parser, markdown_parser, docx_writer
35
+ styles = {}
36
+ if args.css:
37
+ styles = style_parser(args.css)
38
+ else:
39
+ # Try to look for style.css in the active directory
40
+ local_css = os.path.join(directory, "style.css")
41
+ if os.path.exists(local_css):
42
+ styles = style_parser(local_css)
43
+ else:
44
+ # Fallback to the default one packaged in pyact
45
+ default_css = os.path.join(os.path.dirname(__file__), "style.css")
46
+ styles = style_parser(default_css)
47
+
48
+ blocks = markdown_parser(content)
49
+ docx_writer(blocks, styles, args.docx)
50
+ print(f"Successfully compiled DOCX to {args.docx}")
51
+
30
52
  except Exception as e:
31
53
  print(f"Error: {e}", file=sys.stderr)
32
54
  sys.exit(1)
@@ -0,0 +1,54 @@
1
+ import re
2
+ import json
3
+
4
+ def css_to_dict(css_string: str) -> dict:
5
+ """Parses a CSS string into a Python dictionary."""
6
+ # Remove CSS comments
7
+ css_string = re.sub(r'/\*[\s\S]*?\*/', '', css_string)
8
+
9
+ # Match selectors and their corresponding blocks
10
+ pattern = r'([^{]+)\{([^}]+)\}'
11
+ matches = re.findall(pattern, css_string)
12
+
13
+ css_dict = {}
14
+ for selector, block in matches:
15
+ selector = selector.strip()
16
+
17
+ # Parse individual CSS properties
18
+ rules = {}
19
+ for line in block.split(';'):
20
+ line = line.strip()
21
+ if not line:
22
+ continue
23
+ if ':' in line:
24
+ key, val = line.split(':', 1)
25
+ rules[key.strip()] = val.strip()
26
+
27
+ # Handle comma-separated selectors (e.g., 'h1, h2, h3')
28
+ for sel in selector.split(','):
29
+ sel = sel.strip()
30
+ if sel:
31
+ if sel not in css_dict:
32
+ css_dict[sel] = {}
33
+ css_dict[sel].update(rules)
34
+
35
+ return css_dict
36
+
37
+ def parse_css_file(file_path: str, as_json_string: bool = False):
38
+ """
39
+ Reads a CSS file and converts it to a dictionary map.
40
+ If as_json_string is True, returns a formatted JSON string instead.
41
+ """
42
+ with open(file_path, 'r', encoding='utf-8') as f:
43
+ css_string = f.read()
44
+
45
+ css_dict = css_to_dict(css_string)
46
+
47
+ if as_json_string:
48
+ return json.dumps(css_dict, indent=4)
49
+ return css_dict
50
+
51
+ if __name__ == "__main__":
52
+ # Example usage:
53
+ # print(parse_css_file("style.css", as_json_string=True))
54
+ pass
@@ -0,0 +1,425 @@
1
+ import re
2
+ from docx import Document
3
+ from docx.shared import Pt, RGBColor
4
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
5
+ from docx.oxml import parse_xml
6
+ from docx.oxml.ns import nsdecls
7
+ import math2docx
8
+ from . import css2json
9
+
10
+ def style_parser(css_path):
11
+ """
12
+ Reads a CSS file and converts it into a style dictionary using css2json.
13
+ """
14
+ try:
15
+ return css2json.parse_css_file(css_path)
16
+ except Exception as e:
17
+ print(f"Warning: Could not parse CSS ({e})")
18
+ return {}
19
+
20
+ def markdown_parser(md_text):
21
+ """
22
+ Parses Markdown text into a list of structured blocks.
23
+ Supported blocks: heading, paragraph, list, table, math
24
+ """
25
+ blocks = []
26
+ lines = md_text.split('\n')
27
+
28
+ current_table = []
29
+
30
+ for line in lines:
31
+ line_s = line.strip()
32
+
33
+ # Table Parsing
34
+ if line_s.startswith('|') and line_s.endswith('|'):
35
+ current_table.append(line_s)
36
+ continue
37
+ elif current_table:
38
+ blocks.append(('table', current_table))
39
+ current_table = []
40
+
41
+ if not line_s:
42
+ continue
43
+
44
+ # Heading Parsing
45
+ if line_s.startswith('#'):
46
+ level = len(line_s) - len(line_s.lstrip('#'))
47
+ text = line_s.lstrip('#').strip()
48
+ blocks.append(('heading', level, text))
49
+
50
+ # List Parsing
51
+ elif line_s.startswith('- ') or line_s.startswith('* '):
52
+ text = line_s[2:].strip()
53
+ blocks.append(('list', text, 'unordered'))
54
+
55
+ elif re.match(r'^\d+\.\s+', line_s):
56
+ m = re.match(r'^\d+\.\s+(.*)', line_s)
57
+ blocks.append(('list', m.group(1), 'ordered'))
58
+
59
+ # Math Block Parsing
60
+ elif line_s.startswith('$$') and line_s.endswith('$$'):
61
+ blocks.append(('math', line_s.strip('$').strip()))
62
+
63
+ # Standard Paragraph
64
+ else:
65
+ blocks.append(('paragraph', line_s))
66
+
67
+ if current_table:
68
+ blocks.append(('table', current_table))
69
+
70
+ return blocks
71
+
72
+ def _apply_css_to_run(run, css_rules):
73
+ """Helper to apply CSS rules to a docx run or font."""
74
+ font = run.font if hasattr(run, 'font') else run
75
+
76
+ if 'color' in css_rules:
77
+ hex_col = css_rules['color'].replace('#', '').strip()
78
+ if len(hex_col) == 6:
79
+ try:
80
+ r, g, b = tuple(int(hex_col[i:i+2], 16) for i in (0, 2, 4))
81
+ font.color.rgb = RGBColor(r, g, b)
82
+ except ValueError:
83
+ pass
84
+
85
+ if 'font-size' in css_rules:
86
+ size = css_rules['font-size'].replace('pt', '').strip()
87
+ try:
88
+ font.size = Pt(float(size))
89
+ except ValueError:
90
+ pass
91
+
92
+ if 'font-family' in css_rules:
93
+ font.name = css_rules['font-family'].strip("'\"")
94
+
95
+ if 'font-weight' in css_rules:
96
+ font.bold = (css_rules['font-weight'].lower() == 'bold')
97
+
98
+ if 'font-style' in css_rules:
99
+ font.italic = (css_rules['font-style'].lower() == 'italic')
100
+
101
+ if 'text-decoration' in css_rules:
102
+ dec = css_rules['text-decoration'].lower()
103
+ if dec == 'underline':
104
+ font.underline = True
105
+ elif dec == 'line-through':
106
+ font.strike = True
107
+ elif dec == 'none':
108
+ font.underline = False
109
+ font.strike = False
110
+
111
+ if 'text-transform' in css_rules:
112
+ trans = css_rules['text-transform'].lower()
113
+ if trans == 'uppercase':
114
+ font.all_caps = True
115
+ elif trans == 'small-caps':
116
+ font.small_caps = True
117
+
118
+ if 'background-color' in css_rules:
119
+ # Maps text background to highlight color index
120
+ from docx.enum.text import WD_COLOR_INDEX
121
+ color_map = {
122
+ 'auto': WD_COLOR_INDEX.AUTO, 'black': WD_COLOR_INDEX.BLACK, 'blue': WD_COLOR_INDEX.BLUE,
123
+ 'bright-green': WD_COLOR_INDEX.BRIGHT_GREEN, 'dark-blue': WD_COLOR_INDEX.DARK_BLUE,
124
+ 'dark-red': WD_COLOR_INDEX.DARK_RED, 'dark-yellow': WD_COLOR_INDEX.DARK_YELLOW,
125
+ 'gray-25': WD_COLOR_INDEX.GRAY_25, 'gray-50': WD_COLOR_INDEX.GRAY_50,
126
+ 'green': WD_COLOR_INDEX.GREEN, 'pink': WD_COLOR_INDEX.PINK, 'red': WD_COLOR_INDEX.RED,
127
+ 'teal': WD_COLOR_INDEX.TEAL, 'turquoise': WD_COLOR_INDEX.TURQUOISE,
128
+ 'violet': WD_COLOR_INDEX.VIOLET, 'white': WD_COLOR_INDEX.WHITE, 'yellow': WD_COLOR_INDEX.YELLOW
129
+ }
130
+ bg_col = css_rules['background-color'].lower()
131
+ if bg_col in color_map:
132
+ font.highlight_color = color_map[bg_col]
133
+
134
+ def _apply_paragraph_formatting(p, css_rules):
135
+ """Helper to apply CSS rules to paragraph formatting."""
136
+ fmt = p.paragraph_format
137
+
138
+ if 'text-align' in css_rules:
139
+ align = css_rules['text-align'].lower()
140
+ if align == 'center':
141
+ p.alignment = WD_ALIGN_PARAGRAPH.CENTER
142
+ elif align == 'right':
143
+ p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
144
+ elif align == 'justify':
145
+ p.alignment = WD_ALIGN_PARAGRAPH.JUSTIFY
146
+ elif align == 'distribute':
147
+ p.alignment = WD_ALIGN_PARAGRAPH.DISTRIBUTE
148
+ elif align == 'left':
149
+ p.alignment = WD_ALIGN_PARAGRAPH.LEFT
150
+
151
+ if 'margin-top' in css_rules:
152
+ val = css_rules['margin-top'].replace('pt', '').strip()
153
+ try:
154
+ fmt.space_before = Pt(float(val))
155
+ except ValueError:
156
+ pass
157
+
158
+ if 'margin-bottom' in css_rules:
159
+ val = css_rules['margin-bottom'].replace('pt', '').strip()
160
+ try:
161
+ fmt.space_after = Pt(float(val))
162
+ except ValueError:
163
+ pass
164
+
165
+ if 'line-height' in css_rules:
166
+ try:
167
+ fmt.line_spacing = float(css_rules['line-height'])
168
+ except ValueError:
169
+ pass
170
+
171
+ if 'page-break-before' in css_rules:
172
+ if css_rules['page-break-before'].lower() == 'always':
173
+ fmt.page_break_before = True
174
+
175
+ import docx.opc.constants
176
+ from docx.oxml.shared import OxmlElement, qn
177
+ import urllib.request
178
+ import io
179
+ import random
180
+
181
+ def _add_internal_hyperlink(paragraph, text, anchor):
182
+ """Adds a clickable internal hyperlink to a bookmark anchor in the docx."""
183
+ hyperlink = OxmlElement('w:hyperlink')
184
+ hyperlink.set(qn('w:anchor'), anchor)
185
+
186
+ new_run = OxmlElement('w:r')
187
+ rPr = OxmlElement('w:rPr')
188
+
189
+ # Make it superscript since it's usually a footnote marker
190
+ vertAlign = OxmlElement('w:vertAlign')
191
+ vertAlign.set(qn('w:val'), 'superscript')
192
+ rPr.append(vertAlign)
193
+
194
+ # Make it blue
195
+ c = OxmlElement('w:color')
196
+ c.set(qn('w:val'), '0563C1')
197
+ rPr.append(c)
198
+
199
+ new_run.append(rPr)
200
+
201
+ text_elem = OxmlElement('w:t')
202
+ text_elem.text = text
203
+ new_run.append(text_elem)
204
+
205
+ hyperlink.append(new_run)
206
+ paragraph._p.append(hyperlink)
207
+
208
+ def _add_bookmark(paragraph, text, anchor):
209
+ """Creates a bookmark anchor in the document and places text inside it."""
210
+ # We need a random ID to prevent collisions
211
+ bm_id = str(random.randint(10000, 99999))
212
+
213
+ bm_start = OxmlElement('w:bookmarkStart')
214
+ bm_start.set(qn('w:id'), bm_id)
215
+ bm_start.set(qn('w:name'), anchor)
216
+ paragraph._p.append(bm_start)
217
+
218
+ run = paragraph.add_run(text)
219
+
220
+ bm_end = OxmlElement('w:bookmarkEnd')
221
+ bm_end.set(qn('w:id'), bm_id)
222
+ paragraph._p.append(bm_end)
223
+ return run
224
+
225
+ def _add_hyperlink(paragraph, text, url, styles, tag):
226
+ """Adds a real hyperlink to a docx paragraph."""
227
+ part = paragraph.part
228
+ r_id = part.relate_to(url, docx.opc.constants.RELATIONSHIP_TYPE.HYPERLINK, is_external=True)
229
+
230
+ hyperlink = OxmlElement('w:hyperlink')
231
+ hyperlink.set(qn('r:id'), r_id)
232
+
233
+ new_run = OxmlElement('w:r')
234
+ rPr = OxmlElement('w:rPr')
235
+
236
+ c = OxmlElement('w:color')
237
+ if 'hyperlink' in styles and 'color' in styles['hyperlink']:
238
+ color_hex = styles['hyperlink']['color'].replace('#', '').strip()
239
+ c.set(qn('w:val'), color_hex)
240
+ else:
241
+ c.set(qn('w:val'), '0563C1')
242
+ rPr.append(c)
243
+
244
+ u = OxmlElement('w:u')
245
+ if 'hyperlink' in styles and 'text-decoration' in styles['hyperlink'] and styles['hyperlink']['text-decoration'] == 'none':
246
+ pass
247
+ else:
248
+ u.set(qn('w:val'), 'single')
249
+ rPr.append(u)
250
+
251
+ new_run.append(rPr)
252
+
253
+ text_elem = OxmlElement('w:t')
254
+ text_elem.text = text
255
+ new_run.append(text_elem)
256
+
257
+ hyperlink.append(new_run)
258
+ paragraph._p.append(hyperlink)
259
+
260
+ def _add_image(paragraph, url, caption, styles):
261
+ """Adds an inline image fetched from URL."""
262
+ try:
263
+ req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
264
+ with urllib.request.urlopen(req) as response:
265
+ image_stream = io.BytesIO(response.read())
266
+
267
+ run = paragraph.add_run()
268
+
269
+ # Apply CSS dimensions if available
270
+ width = None
271
+ if 'image' in styles and 'width' in styles['image']:
272
+ w_str = styles['image']['width']
273
+ if 'in' in w_str: width = docx.shared.Inches(float(w_str.replace('in', '').strip()))
274
+
275
+ if width:
276
+ run.add_picture(image_stream, width=width)
277
+ else:
278
+ run.add_picture(image_stream, width=docx.shared.Inches(4))
279
+
280
+ except Exception as e:
281
+ paragraph.add_run(f"[Image Failed: {url}]")
282
+
283
+ def _process_inline(paragraph, text, styles, tag):
284
+ """Parses text for images, links, math, and bold/italic."""
285
+ # Simple regex to split by math, image, or link. Captures the match as a group.
286
+ # Group 5: Footnote definition [^1]: ...
287
+ # Group 6: Footnote annotation [^1]
288
+ pattern = r'(\$\$.*?\$\$)|(!\[.*?\]\(.*?\))|(\[.*?\]\(.*?\))|(<span.*?>.*?</span>)|(\[\^.*?\]:.*?$)|(\[\^.*?\])'
289
+ parts = re.split(pattern, text)
290
+
291
+ for part in parts:
292
+ if not part: continue
293
+
294
+ if part.startswith('$$') and part.endswith('$$'):
295
+ math_text = part.strip('$').strip()
296
+ try:
297
+ math2docx.add_math(paragraph, math_text)
298
+ except Exception:
299
+ run = paragraph.add_run(part)
300
+ if tag in styles: _apply_css_to_run(run, styles[tag])
301
+
302
+ elif part.startswith('![') and part.endswith(')'):
303
+ m = re.match(r'!\[(.*?)\]\((.*?)\)', part)
304
+ if m:
305
+ _add_image(paragraph, m.group(2), m.group(1), styles)
306
+
307
+ elif part.startswith('[') and part.endswith(')'):
308
+ m = re.match(r'\[(.*?)\]\((.*?)\)', part)
309
+ if m:
310
+ _add_hyperlink(paragraph, m.group(1), m.group(2), styles, tag)
311
+
312
+ elif part.startswith('<span') and part.endswith('</span>'):
313
+ m = re.match(r'<span.*?>(.*?)</span>', part)
314
+ if m:
315
+ caption_text = m.group(1)
316
+ run = paragraph.add_run(caption_text)
317
+ run.italic = True
318
+ paragraph.alignment = docx.enum.text.WD_ALIGN_PARAGRAPH.CENTER
319
+ if tag in styles: _apply_css_to_run(run, styles[tag])
320
+
321
+ elif part.startswith('[^') and ']:' in part:
322
+ # Footnote definition e.g. [^1]: The text
323
+ m = re.match(r'\[\^(.*?)\]:\s*(.*)', part)
324
+ if m:
325
+ fn_id = m.group(1)
326
+ fn_text = m.group(2)
327
+ # Create a bookmark here for the annotation to jump to
328
+ run = _add_bookmark(paragraph, f"[{fn_id}]: {fn_text}", f"footnote_{fn_id}")
329
+ if tag in styles: _apply_css_to_run(run, styles[tag])
330
+
331
+ elif part.startswith('[^') and part.endswith(']'):
332
+ # Footnote annotation e.g. [^1]
333
+ m = re.match(r'\[\^(.*?)\]', part)
334
+ if m:
335
+ fn_id = m.group(1)
336
+ _add_internal_hyperlink(paragraph, f"[{fn_id}]", f"footnote_{fn_id}")
337
+
338
+ else:
339
+ # Handle plain text + bold/italic
340
+ subparts = re.split(r'(\*\*.*?\*\*|\*.*?\*)', part)
341
+ for subpart in subparts:
342
+ if not subpart: continue
343
+ if subpart.startswith('**') and subpart.endswith('**'):
344
+ run = paragraph.add_run(subpart[2:-2])
345
+ run.bold = True
346
+ if tag in styles: _apply_css_to_run(run, styles[tag])
347
+ elif subpart.startswith('*') and subpart.endswith('*'):
348
+ run = paragraph.add_run(subpart[1:-1])
349
+ run.italic = True
350
+ if tag in styles: _apply_css_to_run(run, styles[tag])
351
+ else:
352
+ run = paragraph.add_run(subpart)
353
+ if tag in styles: _apply_css_to_run(run, styles[tag])
354
+
355
+ def docx_writer(blocks: list, styles: dict, output_file: str):
356
+ """
357
+ Translates parsed markdown blocks into a Word document and applies CSS styling.
358
+ """
359
+ doc = Document()
360
+
361
+ # Global body style
362
+ if 'body' in styles:
363
+ normal_style = doc.styles['Normal']
364
+ _apply_css_to_run(normal_style, styles['body'])
365
+ _apply_paragraph_formatting(normal_style, styles['body'])
366
+
367
+ for block in blocks:
368
+ btype = block[0]
369
+
370
+ if btype == 'heading':
371
+ level, text = block[1], block[2]
372
+ p = doc.add_heading('', level=level)
373
+ _process_inline(p, text, styles, f'h{level}')
374
+ if f'h{level}' in styles:
375
+ _apply_paragraph_formatting(p, styles[f'h{level}'])
376
+
377
+ elif btype == 'paragraph':
378
+ text = block[1]
379
+ p = doc.add_paragraph()
380
+ if 'p' in styles:
381
+ _apply_paragraph_formatting(p, styles['p'])
382
+ _process_inline(p, text, styles, 'p')
383
+
384
+ elif btype == 'list':
385
+ text = block[1]
386
+ list_type = block[2] if len(block) > 2 else 'unordered'
387
+ style = 'List Number' if list_type == 'ordered' else 'List Bullet'
388
+ p = doc.add_paragraph(style=style)
389
+ if 'list' in styles:
390
+ _apply_paragraph_formatting(p, styles['list'])
391
+ _process_inline(p, text, styles, 'list')
392
+
393
+ elif btype == 'math':
394
+ math_text = block[1]
395
+ p = doc.add_paragraph()
396
+ try:
397
+ math2docx.add_math(p, math_text)
398
+ if 'equation' in styles:
399
+ _apply_paragraph_formatting(p, styles['equation'])
400
+ except Exception:
401
+ p.add_run(f"$$ {math_text} $$")
402
+
403
+ elif btype == 'table':
404
+ table_lines = block[1]
405
+ rows = [
406
+ [cell.strip() for cell in line.strip('|').split('|')]
407
+ for line in table_lines if '---' not in line
408
+ ]
409
+ if rows:
410
+ table = doc.add_table(rows=len(rows), cols=len(rows[0]))
411
+ for r_idx, row in enumerate(rows):
412
+ for c_idx, val in enumerate(row):
413
+ table.cell(r_idx, c_idx).text = val
414
+ if 'table' in styles:
415
+ pass
416
+
417
+ doc.save(output_file)
418
+
419
+
420
+ if __name__ == "__main__":
421
+ # Example Usage:
422
+ styles = style_parser('engine/style.css')
423
+ md_blocks = markdown_parser('## Hello World\nThis is a **bold** test.')
424
+ docx_writer(md_blocks, styles, 'output.docx')
425
+
@@ -0,0 +1,74 @@
1
+ Metadata-Version: 2.4
2
+ Name: pyact-cli
3
+ Version: 0.4.0
4
+ Summary: A CLI tool to compile .pamd files to Markdown.
5
+ Home-page: https://github.com/Abstergo2003/PyAct
6
+ Author: Abstergo2003
7
+ Author-email:
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.8
12
+ Description-Content-Type: text/markdown
13
+ Dynamic: author
14
+ Dynamic: classifier
15
+ Dynamic: description
16
+ Dynamic: description-content-type
17
+ Dynamic: home-page
18
+ Dynamic: requires-python
19
+ Dynamic: summary
20
+
21
+ <div align="center">
22
+ <h1>⚡ PyAct CLI</h1>
23
+ <p><b>The engine behind dynamic Markdown.</b></p>
24
+ </div>
25
+
26
+ The **PyAct CLI** allows you to compile `.pamd` files into standard Markdown (`.md`). It evaluates your Python logic, injects your variables, and builds complex markdown elements effortlessly.
27
+
28
+ ## 🚀 Installation
29
+
30
+ Install it directly from PyPI:
31
+ ```bash
32
+ pip install pyact-cli
33
+ ```
34
+
35
+ ## 📖 How it works
36
+
37
+ A `.pamd` file is split into a **Python cell** and a **Markdown cell**.
38
+
39
+ ### 1. Write your Python logic
40
+ Define a `context()` function that returns the variables you want to inject. You can even use the built-in `pamd_helpers` to do heavy lifting!
41
+
42
+ ```python
43
+ import pamd_helpers
44
+
45
+ def context():
46
+ # Automatically converts the lambda to LaTeX!
47
+ equation = pamd_helpers.equation(lambda x: x**2, [5])
48
+
49
+ return {
50
+ "title": "My PyAct Document",
51
+ "math": equation
52
+ }
53
+ ```
54
+
55
+ ### 2. Write your Markdown
56
+ Use `<ctx>` tags to inject your Python variables anywhere in your text.
57
+
58
+ ```markdown
59
+ # <ctx>title</ctx>
60
+
61
+ Here is my dynamically generated equation:
62
+ <ctx>math</ctx>
63
+
64
+ You can also include other pamd files as templates!
65
+ <tmp>path/to/another_file</tmp>
66
+ ```
67
+
68
+ ### 3. Compile!
69
+ Run the compiler from your terminal to generate your final Markdown document:
70
+ ```bash
71
+ pyact path/to/your_file.pamd -o output.md
72
+ ```
73
+
74
+ That's it! You now have a standard Markdown file ready to be shared, published, or converted to PDF.
@@ -4,6 +4,8 @@ pamd_helpers/__init__.py
4
4
  pyact/__init__.py
5
5
  pyact/cli.py
6
6
  pyact/core.py
7
+ pyact/css2json.py
8
+ pyact/mdTOword.py
7
9
  pyact/py2tex.py
8
10
  pyact_cli.egg-info/PKG-INFO
9
11
  pyact_cli.egg-info/SOURCES.txt
@@ -5,13 +5,13 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="pyact-cli",
8
- version="0.2.0",
9
- author="Your Name",
10
- author_email="your.email@example.com",
8
+ version="0.4.0",
9
+ author="Abstergo2003",
10
+ author_email="",
11
11
  description="A CLI tool to compile .pamd files to Markdown.",
12
12
  long_description=long_description,
13
13
  long_description_content_type="text/markdown",
14
- url="https://github.com/yourusername/pyact",
14
+ url="https://github.com/Abstergo2003/PyAct",
15
15
  packages=find_packages(),
16
16
  classifiers=[
17
17
  "Programming Language :: Python :: 3",
pyact_cli-0.2.0/PKG-INFO DELETED
@@ -1,44 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pyact-cli
3
- Version: 0.2.0
4
- Summary: A CLI tool to compile .pamd files to Markdown.
5
- Home-page: https://github.com/yourusername/pyact
6
- Author: Your Name
7
- Author-email: your.email@example.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
- Dynamic: author
14
- Dynamic: author-email
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: requires-python
20
- Dynamic: summary
21
-
22
- # PyAct CLI
23
-
24
- PyAct CLI is a tool designed to process `.pamd` files, which bundle Python logic and Markdown content together, similar to a Jupyter Notebook. This compiler resolves variables and dependencies to generate standard Markdown output.
25
-
26
- ## Installation
27
-
28
- You can install it directly from source (or after uploading to PyPI):
29
-
30
- ```bash
31
- pip install pyact-cli
32
- ```
33
-
34
- ## Usage
35
-
36
- Compile a `.pamd` file and output to the terminal:
37
- ```bash
38
- pyact path/to/your_file.pamd
39
- ```
40
-
41
- Compile and save the output to a new Markdown file:
42
- ```bash
43
- pyact path/to/your_file.pamd -o output.md
44
- ```
pyact_cli-0.2.0/README.md DELETED
@@ -1,23 +0,0 @@
1
- # PyAct CLI
2
-
3
- PyAct CLI is a tool designed to process `.pamd` files, which bundle Python logic and Markdown content together, similar to a Jupyter Notebook. This compiler resolves variables and dependencies to generate standard Markdown output.
4
-
5
- ## Installation
6
-
7
- You can install it directly from source (or after uploading to PyPI):
8
-
9
- ```bash
10
- pip install pyact-cli
11
- ```
12
-
13
- ## Usage
14
-
15
- Compile a `.pamd` file and output to the terminal:
16
- ```bash
17
- pyact path/to/your_file.pamd
18
- ```
19
-
20
- Compile and save the output to a new Markdown file:
21
- ```bash
22
- pyact path/to/your_file.pamd -o output.md
23
- ```
@@ -1 +0,0 @@
1
- __version__ = "0.2.0"
@@ -1,44 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: pyact-cli
3
- Version: 0.2.0
4
- Summary: A CLI tool to compile .pamd files to Markdown.
5
- Home-page: https://github.com/yourusername/pyact
6
- Author: Your Name
7
- Author-email: your.email@example.com
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: License :: OSI Approved :: MIT License
10
- Classifier: Operating System :: OS Independent
11
- Requires-Python: >=3.8
12
- Description-Content-Type: text/markdown
13
- Dynamic: author
14
- Dynamic: author-email
15
- Dynamic: classifier
16
- Dynamic: description
17
- Dynamic: description-content-type
18
- Dynamic: home-page
19
- Dynamic: requires-python
20
- Dynamic: summary
21
-
22
- # PyAct CLI
23
-
24
- PyAct CLI is a tool designed to process `.pamd` files, which bundle Python logic and Markdown content together, similar to a Jupyter Notebook. This compiler resolves variables and dependencies to generate standard Markdown output.
25
-
26
- ## Installation
27
-
28
- You can install it directly from source (or after uploading to PyPI):
29
-
30
- ```bash
31
- pip install pyact-cli
32
- ```
33
-
34
- ## Usage
35
-
36
- Compile a `.pamd` file and output to the terminal:
37
- ```bash
38
- pyact path/to/your_file.pamd
39
- ```
40
-
41
- Compile and save the output to a new Markdown file:
42
- ```bash
43
- pyact path/to/your_file.pamd -o output.md
44
- ```
File without changes
File without changes
File without changes