| from jinja2 import Template |
| from pygments import highlight |
| from pygments.lexers import get_lexer_by_name, guess_lexer |
| from pygments.formatters import HtmlFormatter |
| from pygments.util import ClassNotFound |
| import os |
| import html as html_module |
| from markupsafe import Markup |
|
|
| def get_pygments_css(): |
| """Get CSS for syntax highlighting - a more complete and styled version.""" |
| return """ |
| .c { color: #9ca3af; font-style: italic; } /* Comment */ |
| .c1 { color: #9ca3af; font-style: italic; } /* Comment.Single */ |
| .cm { color: #9ca3af; font-style: italic; } /* Comment.Multiline */ |
| .cp { color: #9ca3af; font-style: italic; } /* Comment.Preproc */ |
| .cs { color: #9ca3af; font-style: italic; } /* Comment.Special */ |
| .k { color: #db2777; font-weight: 500; } /* Keyword */ |
| .kc { color: #db2777; font-weight: 500; } /* Keyword.Constant */ |
| .kd { color: #db2777; font-weight: 500; } /* Keyword.Declaration */ |
| .kn { color: #db2777; font-weight: 500; } /* Keyword.Namespace */ |
| .kp { color: #db2777; font-weight: 500; } /* Keyword.Pseudo */ |
| .kr { color: #db2777; font-weight: 500; } /* Keyword.Reserved */ |
| .kt { color: #9333ea; font-weight: 500; } /* Keyword.Type */ |
| .n { color: #1f2937; } /* Name */ |
| .na { color: #4f46e5; } /* Name.Attribute */ |
| .nb { color: #059669; } /* Name.Builtin */ |
| .nc { color: #4f46e5; font-weight: 500; } /* Name.Class */ |
| .nf { color: #4f46e5; font-weight: 500; } /* Name.Function */ |
| .nx { color: #1f2937; } /* Name.Other */ |
| .s { color: #059669; } /* String */ |
| .s1 { color: #059669; } /* String.Single */ |
| .s2 { color: #059669; } /* String.Double */ |
| .p { color: #6b7280; } /* Punctuation */ |
| .m { color: #059669; } /* Number */ |
| .mi { color: #059669; } /* Number.Integer */ |
| .o { color: #db2777; } /* Operator */ |
| .ow { color: #db2777; font-weight: 500; } /* Operator.Word */ |
| .w { color: #d1d5db; } /* Text.Whitespace */ |
| """ |
|
|
| from markupsafe import Markup |
|
|
| def format_code_with_pygments(code_text, language='python'): |
| """ |
| Highlights a code snippet using Pygments and wraps each line to preserve |
| indentation and allow for individual line styling. |
| """ |
| try: |
| lexer = get_lexer_by_name(language, stripall=True) |
| except ClassNotFound: |
| lexer = get_lexer_by_name('text') |
|
|
| formatter = HtmlFormatter(nowrap=True) |
| highlighted_html = highlight(code_text, lexer, formatter) |
|
|
| |
| raw_lines = code_text.rstrip().split('\n') |
| |
| highlighted_lines = highlighted_html.rstrip().split('\n') |
|
|
| |
| if len(raw_lines) != len(highlighted_lines): |
| wrapped_html = [f'<span class="code-line">{line}</span>' for line in highlighted_lines] |
| return Markup('\n'.join(wrapped_html)) |
|
|
| wrapped_lines = [] |
| for i, raw_line in enumerate(raw_lines): |
| |
| leading_spaces = len(raw_line) - len(raw_line.lstrip(' ')) |
| |
| |
| indentation = ' ' * leading_spaces |
| |
| |
| line_content = highlighted_lines[i].lstrip() if raw_line.lstrip() else " " |
|
|
| wrapped_lines.append(f'<span class="code-line">{indentation}{line_content}</span>') |
|
|
| return Markup('\n'.join(wrapped_lines)) |
|
|
| def render_template(template_path, data): |
| """Render Jinja2 template with data.""" |
| with open(template_path, 'r', encoding='utf-8') as f: |
| template = Template(f.read()) |
| return template.render(**data) |
|
|
| def extract_code_blocks(content): |
| """Extract code blocks from markdown-style content.""" |
| import re |
| code_blocks = [] |
| pattern = r'```(\w+)?\n(.*?)```' |
| matches = re.findall(pattern, content, re.DOTALL) |
| |
| for lang, code in matches: |
| code_blocks.append({ |
| 'language': lang if lang else 'python', |
| 'code': code.strip() |
| }) |
| |
| return code_blocks |
|
|
| def format_bullet_points(content): |
| """ |
| Formats bullet points from content, converting simple markdown (bold, inline code) to HTML. |
| Code snippets are expected to be handled before this function is called. |
| """ |
| import re |
| lines = content.strip().split('\n') |
| bullets = [] |
|
|
| for line in lines: |
| stripped_line = line.strip() |
| |
| if stripped_line.startswith(('-', '*', '•')): |
| bullet_text = re.sub(r'^[\-\*•]\s*', '', stripped_line) |
| |
| bullet_text = re.sub(r'\*\*(.*?)\*\*', r'<strong>\1</strong>', bullet_text) |
| |
| bullet_text = re.sub(r'`([^`]+)`', r'<code class="inline-code-span">\1</code>', bullet_text) |
| bullets.append(bullet_text) |
| elif stripped_line: |
| bullets.append(stripped_line) |
|
|
| return { |
| 'bullets': bullets, |
| 'code_snippet': None |
| } |