wingtip 0.4.1__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.
Files changed (44) hide show
  1. wingtip/__init__.py +0 -0
  2. wingtip/fonts/fira_sans.ttf +0 -0
  3. wingtip/fonts/poppins.ttf +0 -0
  4. wingtip/generate_card.py +125 -0
  5. wingtip/latex_extension.py +19 -0
  6. wingtip/main.py +1983 -0
  7. wingtip/serve.py +175 -0
  8. wingtip/static/css/admonitions.css +151 -0
  9. wingtip/static/css/custom.css +348 -0
  10. wingtip/static/js/search.js +228 -0
  11. wingtip/static/vendor/auto-render.min.js +1 -0
  12. wingtip/static/vendor/clipboard.min.js +7 -0
  13. wingtip/static/vendor/dark.css +886 -0
  14. wingtip/static/vendor/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  15. wingtip/static/vendor/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  16. wingtip/static/vendor/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  17. wingtip/static/vendor/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  18. wingtip/static/vendor/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  19. wingtip/static/vendor/fonts/KaTeX_Main-Bold.woff2 +0 -0
  20. wingtip/static/vendor/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  21. wingtip/static/vendor/fonts/KaTeX_Main-Italic.woff2 +0 -0
  22. wingtip/static/vendor/fonts/KaTeX_Main-Regular.woff2 +0 -0
  23. wingtip/static/vendor/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  24. wingtip/static/vendor/fonts/KaTeX_Math-Italic.woff2 +0 -0
  25. wingtip/static/vendor/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  26. wingtip/static/vendor/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  27. wingtip/static/vendor/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  28. wingtip/static/vendor/fonts/KaTeX_Script-Regular.woff2 +0 -0
  29. wingtip/static/vendor/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  30. wingtip/static/vendor/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  31. wingtip/static/vendor/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  32. wingtip/static/vendor/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  33. wingtip/static/vendor/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  34. wingtip/static/vendor/fonts/material-icons.ttf +0 -0
  35. wingtip/static/vendor/iconify.min.js +13 -0
  36. wingtip/static/vendor/katex.min.css +1 -0
  37. wingtip/static/vendor/katex.min.js +1 -0
  38. wingtip/static/vendor/light.css +886 -0
  39. wingtip/static/vendor/material-icons.css +20 -0
  40. wingtip/template.html +1281 -0
  41. wingtip-0.4.1.dist-info/METADATA +342 -0
  42. wingtip-0.4.1.dist-info/RECORD +44 -0
  43. wingtip-0.4.1.dist-info/WHEEL +4 -0
  44. wingtip-0.4.1.dist-info/entry_points.txt +2 -0
wingtip/__init__.py ADDED
File without changes
Binary file
Binary file
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env python3
2
+
3
+ # wingtip/generate_card.py
4
+ # Generate social card for GitHub Pages
5
+
6
+ import pathlib
7
+ import os
8
+ import re
9
+ import urllib.request
10
+ from PIL import Image, ImageDraw, ImageFont
11
+
12
+ def get_google_font(font_name):
13
+ """Download and cache a Google Font"""
14
+ # Convert font name to Google Fonts API format
15
+ api_name = font_name.replace(' ', '+')
16
+
17
+ # Create fonts cache directory
18
+ cache_dir = pathlib.Path(__file__).parent / "fonts"
19
+ cache_dir.mkdir(exist_ok=True)
20
+
21
+ # Check if font is already cached
22
+ font_file = cache_dir / f"{font_name.lower().replace(' ', '_')}.ttf"
23
+ if font_file.exists():
24
+ return str(font_file)
25
+
26
+ try:
27
+ # Get font CSS URL from Google Fonts API
28
+ css_url = f"https://fonts.googleapis.com/css2?family={api_name}&display=swap"
29
+ req = urllib.request.Request(css_url, headers={'User-Agent': 'Mozilla/5.0'})
30
+ css = urllib.request.urlopen(req).read().decode('utf-8')
31
+
32
+ # Extract TTF URL from CSS
33
+ ttf_url = re.search(r'src: url\((.+?\.ttf)\)', css)
34
+ if not ttf_url:
35
+ return None
36
+
37
+ # Download TTF file
38
+ ttf_url = ttf_url.group(1)
39
+ urllib.request.urlretrieve(ttf_url, font_file)
40
+ return str(font_file)
41
+ except Exception as e:
42
+ print(f"Failed to download Google Font {font_name}: {e}")
43
+ return None
44
+
45
+ def generate_social_card(title, tagline, theme="light", font="Poppins", logo=None):
46
+ size = (1200, 630)
47
+ bg_color = "#f6ede3" # Warm background color
48
+ fg_color = "#000000" # Black text
49
+ card = Image.new("RGB", size, bg_color)
50
+ draw = ImageDraw.Draw(card)
51
+
52
+ # Try to find or download the font
53
+ try:
54
+ # First try the specified font as a path
55
+ font_path = font
56
+ if not os.path.exists(font_path):
57
+ # Then try bundled fonts directory
58
+ font_path = os.path.join(os.path.dirname(__file__), "fonts", font)
59
+
60
+ # If not found, try downloading from Google Fonts
61
+ if not os.path.exists(font_path):
62
+ google_font = get_google_font(font)
63
+ if google_font:
64
+ font_path = google_font
65
+ else:
66
+ # Fallback to Arial
67
+ font_path = "Arial"
68
+
69
+ title_font = ImageFont.truetype(font_path, 72)
70
+ tagline_font = ImageFont.truetype(font_path, 40)
71
+ except OSError:
72
+ # If all else fails, use default bitmap font
73
+ title_font = ImageFont.load_default()
74
+ tagline_font = ImageFont.load_default()
75
+
76
+ # Load and resize custom logo
77
+ logo_width = 0 # Default if logo fails to load
78
+
79
+ # Logo is opt-in and project-relative. Hardcoding "wingtip-logo.png" meant
80
+ # every user's build printed a failure for an asset only this repo has.
81
+ logo_path = logo
82
+ if logo_path and not os.path.exists(logo_path):
83
+ logo_path = None
84
+
85
+ try:
86
+ if not logo_path:
87
+ raise FileNotFoundError("no logo configured")
88
+ logo_img = Image.open(logo_path)
89
+
90
+ # Convert to RGBA if needed
91
+ if logo_img.mode != 'RGBA':
92
+ logo_img = logo_img.convert('RGBA')
93
+
94
+ # Make logo full height of card
95
+ logo_height = size[1] # Full height
96
+ ratio = logo_img.width / logo_img.height
97
+ logo_width = int(logo_height * ratio)
98
+ logo_img = logo_img.resize((logo_width, logo_height), Image.Resampling.LANCZOS)
99
+
100
+ # Position logo flush with left edge
101
+ logo_y = 0 # Top edge
102
+ logo_x = 0 # Left edge
103
+
104
+ # Paste logo with alpha channel
105
+ card.paste(logo_img, (logo_x, logo_y), logo_img)
106
+ except FileNotFoundError:
107
+ pass # No logo: text starts from the margin. Not an error.
108
+ except Exception as e:
109
+ print(f"Warning: could not render logo '{logo_path}': {e}")
110
+
111
+ # Position text to the right of logo with spacing
112
+ text_x = logo_width + 0 # 0px gap from logo
113
+ text_y = size[1] // 3 # Start text 1/3 down from top
114
+
115
+ # Draw title
116
+ title_bbox = draw.textbbox((0, 0), title, font=title_font)
117
+ title_height = title_bbox[3] - title_bbox[1]
118
+ draw.text((text_x, text_y), title, font=title_font, fill=fg_color)
119
+
120
+ # Draw tagline below title with 40px gap
121
+ draw.text((text_x, text_y + title_height + 40), tagline, font=tagline_font, fill=fg_color)
122
+
123
+ output = pathlib.Path("docs/site/social-card.png")
124
+ output.parent.mkdir(parents=True, exist_ok=True)
125
+ card.save(output)
@@ -0,0 +1,19 @@
1
+ import re
2
+ import markdown
3
+ from markdown.preprocessors import Preprocessor
4
+
5
+ class LaTeXPreservationExtension(markdown.Extension):
6
+ def extendMarkdown(self, md):
7
+ md.preprocessors.register(LaTeXPreservationPreprocessor(md), 'latex_preservation', 25)
8
+
9
+ class LaTeXPreservationPreprocessor(Preprocessor):
10
+ def run(self, lines):
11
+ text = '\n'.join(lines)
12
+ # Replace LaTeX delimiters with unique placeholders
13
+ text = re.sub(r'\\\[(.*?)\\\]', lambda m: f'DISPLAYMATH_START{m.group(1)}DISPLAYMATH_END', text)
14
+ text = re.sub(r'\\\((.*?)\\\)', lambda m: f'INLINEMATH_START{m.group(1)}INLINEMATH_END', text)
15
+ # Split back into lines
16
+ return text.split('\n')
17
+
18
+ def makeExtension(*args, **kwargs):
19
+ return LaTeXPreservationExtension(*args, **kwargs)