From 8aec483f6d5904cfac027241f2a84f230fc769b2 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 07:57:11 -0700 Subject: [PATCH 01/16] Update pyproject.toml for PyPI publishing - Add py-modules configuration for direct module import - Bump version to 0.2.0 - Add author information - Update repository URLs --- pyproject.toml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 943a299..fc6bdfa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,14 +2,18 @@ requires = ["setuptools>=61.0", "wheel"] build-backend = "setuptools.build_meta" +[tool.setuptools] +py-modules = ["repo_to_single_page"] + [project] name = "rendergit" -version = "0.1.0" +version = "0.2.0" description = "Flatten a GitHub repo into a single static HTML page for fast skimming and Ctrl+F" readme = "README.md" license = {text = "Apache-2.0"} authors = [ {name = "Andrej Karpathy"}, + {name = "Eden Chan", email = "edenchan717@gmail.com"}, ] dependencies = [ "pygments", @@ -32,5 +36,5 @@ classifiers = [ rendergit = "repo_to_single_page:main" [project.urls] -Homepage = "https://github.com/karpathy/rendergit" -Repository = "https://github.com/karpathy/rendergit" \ No newline at end of file +Homepage = "https://github.com/eden-chan/rendergit" +Repository = "https://github.com/eden-chan/rendergit" \ No newline at end of file From 6fa5d36318c3b4e10fd37b7c4ad5cca33f94a855 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 08:14:46 -0700 Subject: [PATCH 02/16] Add web deployment configuration for Render - Add Flask web app that reuses original CLI code - Add requirements.txt for web dependencies - Add render.yaml for Render deployment - Add deployment documentation - Add .gitignore for Python projects - Add MANIFEST.in for package distribution --- .gitignore | 76 ++++++++++++++++++++ DEPLOY.md | 183 +++++++++++++++++++++++++++++++++++++++++++++++ MANIFEST.in | 3 + app.py | 157 ++++++++++++++++++++++++++++++++++++++++ render.yaml | 11 +++ requirements.txt | 4 ++ 6 files changed, 434 insertions(+) create mode 100644 .gitignore create mode 100644 DEPLOY.md create mode 100644 MANIFEST.in create mode 100644 app.py create mode 100644 render.yaml create mode 100644 requirements.txt diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f754059 --- /dev/null +++ b/.gitignore @@ -0,0 +1,76 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Flask stuff: +instance/ +.webassets-cache + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# IDEs +.vscode/ +.idea/ +*.swp +*.swo +*~ +.DS_Store + +# Project specific +*.html +temp/ +tmp/ \ No newline at end of file diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..aaf27c3 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,183 @@ +# Deployment & Publishing Guide for rendergit + +## Quick Start + +```bash +# 1. Build package for PyPI +python -m build + +# 2. Upload to PyPI +python -m twine upload dist/* + +# 3. Deploy to Render +git push origin main +# Then connect repo on render.com +``` + +## 📦 Publishing to PyPI + +### First-time setup + +1. **Create PyPI account** + - Go to https://pypi.org/account/register/ + - Verify your email + +2. **Install build tools** + ```bash + pip install --upgrade pip build twine + ``` + +3. **Create API token** + - Go to https://pypi.org/manage/account/token/ + - Create a token with scope "Entire account" + - Save the token securely + +### Building and Publishing + +1. **Update version** in `pyproject.toml` (currently 0.2.0) + +2. **Build the package** + ```bash + python -m build + ``` + This creates `dist/` directory with wheel and source distribution + +3. **Upload to TestPyPI (optional, for testing)** + ```bash + python -m twine upload --repository testpypi dist/* + ``` + Test install: `pip install -i https://test.pypi.org/simple/ rendergit` + +4. **Upload to PyPI** + ```bash + python -m twine upload dist/* + ``` + Enter your PyPI username: `__token__` + Enter your password: `[paste your API token]` + +5. **Verify installation** + ```bash + pip install rendergit + rendergit --help + ``` + +## 🚀 Deploying to Render + +### Quick Deploy (Recommended) + +1. **Push to GitHub** + ```bash + git add . + git commit -m "Add web deployment configuration" + git push origin main + ``` + +2. **Deploy on Render** + - Go to https://render.com + - Sign up/Login with GitHub + - Click "New +" → "Web Service" + - Connect your GitHub repo + - Render will auto-detect the `render.yaml` configuration + - Click "Create Web Service" + +3. **Your app will be live at:** + ``` + https://rendergit.onrender.com + ``` + +### Manual Deploy Alternative + +If you prefer manual configuration: + +1. On Render dashboard: + - New → Web Service + - Connect GitHub repo + - Configure: + - **Name**: rendergit + - **Environment**: Python + - **Build Command**: `pip install -r requirements.txt` + - **Start Command**: `gunicorn app:app` + - **Plan**: Free + +## 🛠️ Local Development + +### Running the CLI locally +```bash +# Install in development mode +pip install -e . + +# Test the CLI +rendergit https://github.com/karpathy/nanoGPT +``` + +### Running the web app locally +```bash +# Install dependencies +pip install -r requirements.txt + +# Run Flask development server +python app.py + +# Or with gunicorn (production-like) +gunicorn app:app --bind 0.0.0.0:5000 +``` + +Visit http://localhost:5000 + +## 🔧 Configuration + +### Environment Variables (for Render) + +You can set these in Render dashboard → Environment: + +- `PORT`: Auto-set by Render +- `PYTHON_VERSION`: Set in render.yaml (3.11.0) + +### Updating the package + +1. Make changes to code +2. Update version in `pyproject.toml` +3. Build and publish to PyPI (see above) +4. Push to GitHub (auto-deploys to Render) + +## 📝 File Structure + +``` +rendergit/ +├── rendergit/ # Package directory +│ ├── __init__.py # Package initialization +│ └── cli.py # CLI implementation (renamed from repo_to_single_page.py) +├── app.py # Flask web application +├── pyproject.toml # Package configuration +├── requirements.txt # Web app dependencies +├── render.yaml # Render deployment config +├── README.md # Project documentation +├── DEPLOY.md # This file +└── .gitignore # Git ignore rules +``` + +## 🐛 Troubleshooting + +### PyPI Upload Issues +- **Authentication failed**: Check API token is correct +- **Version exists**: Increment version in pyproject.toml +- **Missing files**: Ensure `python -m build` ran successfully + +### Render Deployment Issues +- **Build failed**: Check requirements.txt has all dependencies +- **App crashes**: Check logs in Render dashboard +- **Slow cold starts**: Normal on free tier, upgrades available + +### Local Development Issues +- **Import errors**: Run `pip install -e .` from project root +- **Git not found**: Ensure git is installed and in PATH + +## 🔗 Links + +- **PyPI Package**: https://pypi.org/project/rendergit/ +- **Live Web App**: https://rendergit.onrender.com +- **GitHub Repo**: https://github.com/yourusername/rendergit + +## 📄 License + +Apache 2.0 - See LICENSE file \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..ec6228a --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README.md +include LICENSE +include repo_to_single_page.py \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..8b7c042 --- /dev/null +++ b/app.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python3 +""" +Minimal Flask web app for rendergit - reuses original CLI code +""" + +import os +import tempfile +import shutil +from flask import Flask, request, send_file, jsonify +from pathlib import Path + +# Import everything from the original CLI +from repo_to_single_page import ( + git_clone, git_head_commit, walk_files, tree_command, + render_html, MAX_DEFAULT_BYTES +) + +app = Flask(__name__) + +HTML = ''' + + + + + + rendergit + + + +

🚀 rendergit

+

Flatten any GitHub repository into a single HTML page

+ + + +
+ +
+

Install locally: pip install rendergit

+

CLI usage: rendergit https://github.com/user/repo

+
+ + + + +''' + +@app.route('/') +def index(): + return HTML + +@app.route('/process', methods=['POST']) +def process(): + try: + repo_url = request.json.get('url', '').strip() + if not repo_url or 'github.com' not in repo_url: + return 'Invalid URL', 400 + + with tempfile.TemporaryDirectory() as tmpdir: + # Clone repo + repo_dir = os.path.join(tmpdir, 'repo') + git_clone(repo_url, repo_dir) + + # Get metadata + commit = git_head_commit(repo_dir) + repo_name = repo_url.rstrip('/').split('/')[-1].replace('.git', '') + + # Walk files + files = walk_files(repo_dir, MAX_DEFAULT_BYTES) + + # Get tree + tree_output = tree_command(repo_dir) + + # Generate HTML using original function + html_content = render_html(repo_url, repo_name, commit, files, tree_output) + + # Write and send + html_file = os.path.join(tmpdir, 'output.html') + with open(html_file, 'w', encoding='utf-8') as f: + f.write(html_content) + + return send_file(html_file, as_attachment=True, + download_name=f'{repo_name}.html', + mimetype='text/html') + except Exception as e: + return str(e), 500 + +if __name__ == '__main__': + port = int(os.environ.get('PORT', 5000)) + app.run(host='0.0.0.0', port=port) \ No newline at end of file diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000..9547b10 --- /dev/null +++ b/render.yaml @@ -0,0 +1,11 @@ +services: + - type: web + name: rendergit + runtime: python + plan: free + buildCommand: "pip install -r requirements.txt" + startCommand: "gunicorn app:app" + envVars: + - key: PYTHON_VERSION + value: 3.11.0 + autoDeploy: true \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c8eec3a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +flask==3.0.0 +pygments==2.17.2 +markdown==3.5.1 +gunicorn==21.2.0 \ No newline at end of file From 41b461fb19fbfb579c906b2505256fe1023753ed Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 08:26:16 -0700 Subject: [PATCH 03/16] Fix imports to match original repo_to_single_page.py functions --- app.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index 8b7c042..f1ac163 100644 --- a/app.py +++ b/app.py @@ -11,8 +11,8 @@ # Import everything from the original CLI from repo_to_single_page import ( - git_clone, git_head_commit, walk_files, tree_command, - render_html, MAX_DEFAULT_BYTES + git_clone, git_head_commit, collect_files, try_tree_command, + build_html, MAX_DEFAULT_BYTES, main ) app = Flask(__name__) @@ -132,14 +132,12 @@ def process(): commit = git_head_commit(repo_dir) repo_name = repo_url.rstrip('/').split('/')[-1].replace('.git', '') - # Walk files - files = walk_files(repo_dir, MAX_DEFAULT_BYTES) - - # Get tree - tree_output = tree_command(repo_dir) + # Collect files + repo_path = Path(repo_dir) + files = collect_files(repo_path, MAX_DEFAULT_BYTES) # Generate HTML using original function - html_content = render_html(repo_url, repo_name, commit, files, tree_output) + html_content = build_html(repo_url, repo_dir, commit, files) # Write and send html_file = os.path.join(tmpdir, 'output.html') From c6f4ecdaf6cd5faf97b625c8cc562c94e9a60927 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 08:29:35 -0700 Subject: [PATCH 04/16] Add direct URL rendering support - access repos via /github.com/user/repo --- app.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/app.py b/app.py index f1ac163..ea4e297 100644 --- a/app.py +++ b/app.py @@ -68,10 +68,17 @@
+

Direct URL:

Install locally: pip install rendergit

CLI usage: rendergit https://github.com/user/repo

+ + + # Keep only last N repos if exceeded + if len(metadata['repos']) > MAX_CACHED_REPOS: + sorted_repos = sorted(metadata['repos'].items(), + key=lambda x: x[1]['timestamp']) + for key, _ in sorted_repos[:-MAX_CACHED_REPOS]: + del metadata['repos'][key] + cache_file = CACHE_DIR / f"{key}.html" + cache_file.unlink(missing_ok=True) - - - -''' + save_metadata(metadata) @app.route('/') def index(): - return HTML + """Show homepage with repo cards""" + metadata = load_metadata() + repos = metadata.get('repos', {}) + + # Sort by most recent + sorted_repos = sorted(repos.items(), + key=lambda x: x[1]['timestamp'], + reverse=True) + + cards_html = '' + for key, info in sorted_repos[:20]: # Show last 20 + time_ago = datetime.fromtimestamp(info['timestamp']).strftime('%Y-%m-%d %H:%M') + cards_html += f''' +
+

{info['name']}

+

{info['url']}

+

+ ⭐ {info.get('stars', 'N/A')} + 📅 Cached: {time_ago} +

+
+ ''' + + if not cards_html: + cards_html = '

No repositories cached yet. Try this example

' + + return f''' + + + + + + rendergit + + + +
+

🚀 rendergit

+

Flatten GitHub repositories into single HTML pages

+ +
+ + +
+ +
+ {cards_html} +
+ +
+

Direct URL: {request.host_url}github.com/user/repo

+

Install locally: pip install rendergit

+
+
+ + + + + ''' @app.route('/') def render_repo(repo_path): - """Direct URL rendering: /github.com/user/repo or /https://github.com/user/repo""" + """Render a repository with caching""" try: - # Handle different URL formats + # Parse URL if repo_path.startswith('https://github.com/') or repo_path.startswith('http://github.com/'): repo_url = repo_path.replace('http://', 'https://') elif repo_path.startswith('github.com/'): repo_url = f'https://{repo_path}' else: - return f'Invalid path. Use format: {request.host_url}github.com/user/repo', 400 + return f'Invalid path. Use: {request.host_url}github.com/user/repo', 400 + + # Check cache + cache_key = get_cache_key(repo_url) + cache_file = CACHE_DIR / f"{cache_key}.html" + metadata = load_metadata() + # Check if cached and not expired + if cache_file.exists() and cache_key in metadata['repos']: + cache_info = metadata['repos'][cache_key] + if time.time() - cache_info['timestamp'] < (CACHE_TTL_HOURS * 3600): + # Update access time + cache_info['last_accessed'] = time.time() + save_metadata(metadata) + + # Return cached content + with open(cache_file, 'r', encoding='utf-8') as f: + return f.read() + + # Generate fresh content with tempfile.TemporaryDirectory() as tmpdir: - # Clone repo repo_dir = os.path.join(tmpdir, 'repo') git_clone(repo_url, repo_dir) - # Get metadata commit = git_head_commit(repo_dir) - - # Collect files repo_path_obj = Path(repo_dir) files = collect_files(repo_path_obj, MAX_DEFAULT_BYTES) - # Generate HTML using original function html_content = build_html(repo_url, Path(repo_dir), commit, files) - return html_content - - except Exception as e: - return f'

Error

{str(e)}

Usage: {request.host_url}github.com/user/repo

', 500 - -@app.route('/process', methods=['POST']) -def process(): - try: - repo_url = request.json.get('url', '').strip() - if not repo_url or 'github.com' not in repo_url: - return 'Invalid URL', 400 - - with tempfile.TemporaryDirectory() as tmpdir: - # Clone repo - repo_dir = os.path.join(tmpdir, 'repo') - git_clone(repo_url, repo_dir) - - # Get metadata - commit = git_head_commit(repo_dir) - repo_name = repo_url.rstrip('/').split('/')[-1].replace('.git', '') + # Cache the result + with open(cache_file, 'w', encoding='utf-8') as f: + f.write(html_content) - # Collect files - repo_path = Path(repo_dir) - files = collect_files(repo_path, MAX_DEFAULT_BYTES) + # Update metadata + repo_name = repo_url.rstrip('/').split('/')[-1] + metadata['repos'][cache_key] = { + 'url': repo_url, + 'path': repo_path, + 'name': repo_name, + 'timestamp': time.time(), + 'last_accessed': time.time(), + 'commit': commit[:8] if commit else 'unknown' + } + save_metadata(metadata) - # Generate HTML using original function - html_content = build_html(repo_url, Path(repo_dir), commit, files) + # Cleanup old cache periodically + if len(metadata['repos']) % 10 == 0: + cleanup_old_cache() - # Write and send - html_file = os.path.join(tmpdir, 'output.html') - with open(html_file, 'w', encoding='utf-8') as f: - f.write(html_content) + return html_content - return send_file(html_file, as_attachment=True, - download_name=f'{repo_name}.html', - mimetype='text/html') except Exception as e: - return str(e), 500 + return f''' + + +

Error

+

{str(e)}

+

Usage: {request.host_url}github.com/user/repo

+ ← Back to home + + + ''', 500 if __name__ == '__main__': port = int(os.environ.get('PORT', 5000)) From 47744ce44270c215d4670061fe906fa8ad8f2b70 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 08:53:09 -0700 Subject: [PATCH 08/16] Improve UI with example buttons and mobile responsiveness - Add quick example buttons for popular repos (nanoGPT, Whisper, etc) - Improve mobile layout with responsive grid and flex layouts - Reduce max width from 1200px to 900px for better desktop viewing - Add responsive font sizes using clamp() - Style improvements for cards and empty state - Add tryExample() function to autofill and render --- app.py | 115 +++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 15 deletions(-) diff --git a/app.py b/app.py index f3b86eb..052f71b 100644 --- a/app.py +++ b/app.py @@ -100,7 +100,10 @@ def index(): ''' if not cards_html: - cards_html = '

No repositories cached yet. Try this example

' + cards_html = '''
+

No repositories rendered yet.

+

Try one of the examples above or enter your own GitHub URL!

+
''' return f''' @@ -110,32 +113,40 @@ def index(): rendergit @@ -230,6 +298,18 @@ def index(): +
+

Try these examples:

+
+ + + + + +
+
+ +

Recently Rendered

{cards_html}
@@ -249,6 +329,11 @@ def index(): window.location.href = '/' + path; }} }} + + function tryExample(url) {{ + document.getElementById('url').value = url; + go(); + }} @@ -273,7 +358,7 @@ def render_repo(repo_path): # Check if cached and not expired if cache_file.exists() and cache_key in metadata['repos']: - cache_info = metadata['repos'][cache_key] + cache_info = metadata['repos'][cache_key] if time.time() - cache_info['timestamp'] < (CACHE_TTL_HOURS * 3600): # Update access time cache_info['last_accessed'] = time.time() From 3a3880f59438c5aad6aece6ea2e5a6fd1bf31e98 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 08:55:04 -0700 Subject: [PATCH 09/16] Remove unnecessary metadata from repo cards - Remove stars display (not fetched from API) - Remove cached timestamp display - Simplify card design with just name and URL - Improve URL styling with ellipsis for long URLs - Add subtle shadow on card hover --- app.py | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/app.py b/app.py index 052f71b..facf0c7 100644 --- a/app.py +++ b/app.py @@ -87,15 +87,10 @@ def index(): cards_html = '' for key, info in sorted_repos[:20]: # Show last 20 - time_ago = datetime.fromtimestamp(info['timestamp']).strftime('%Y-%m-%d %H:%M') cards_html += f'''

{info['name']}

{info['url']}

-

- ⭐ {info.get('stars', 'N/A')} - 📅 Cached: {time_ago} -

''' @@ -210,14 +205,15 @@ def index(): border: 1px solid #30363d; border-radius: 8px; padding: 15px; - transition: transform 0.2s; + transition: all 0.2s; }} .card:hover {{ transform: translateY(-2px); border-color: #58a6ff; + box-shadow: 0 4px 12px rgba(88, 166, 255, 0.1); }} .card h3 {{ - margin: 0 0 10px 0; + margin: 0 0 8px 0; font-size: 1.1rem; }} .card a {{ @@ -228,18 +224,12 @@ def index(): text-decoration: underline; }} .url {{ - color: #8b949e; - font-size: 12px; - margin: 5px 0; - word-break: break-all; - }} - .meta {{ - display: flex; - flex-wrap: wrap; - gap: 15px; - margin-top: 10px; + color: #6e7681; font-size: 13px; - color: #8b949e; + margin: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; }} .empty {{ text-align: center; From 3132ad9d7d673e17048919cbffc3d443f71817a6 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 08:56:59 -0700 Subject: [PATCH 10/16] Add clickable cards and loading animation - Make entire card clickable to navigate to repo - Add loading page with spinner and progress steps - Show loading animation while repo is being processed - Loading page auto-redirects to actual processing - Clean card styling with hover effects --- app.py | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 120 insertions(+), 10 deletions(-) diff --git a/app.py b/app.py index facf0c7..9dd4ad2 100644 --- a/app.py +++ b/app.py @@ -74,6 +74,111 @@ def cleanup_old_cache(): save_metadata(metadata) +@app.route('/loading/') +def loading_page(repo_path): + """Show loading page while repo is being processed""" + repo_name = repo_path.split('/')[-1] + return f''' + + + + + + Loading {repo_name} - rendergit + + + +
+

🚀 rendergit

+
+

Processing {repo_name}

+
+
📥 Cloning repository...
+
📂 Collecting files...
+
🎨 Rendering HTML...
+
💾 Caching result...
+
+

This may take a few moments for large repositories

+
+ + + + ''' + @app.route('/') def index(): """Show homepage with repo cards""" @@ -88,10 +193,12 @@ def index(): cards_html = '' for key, info in sorted_repos[:20]: # Show last 20 cards_html += f''' -
-

{info['name']}

-

{info['url']}

-
+ +
+

{info['name']}

+

{info['url']}

+
+
''' if not cards_html: @@ -200,12 +307,17 @@ def index(): gap: 15px; margin-top: 20px; }} + .card-link {{ + text-decoration: none; + display: block; + }} .card {{ background: #161b22; border: 1px solid #30363d; border-radius: 8px; padding: 15px; transition: all 0.2s; + cursor: pointer; }} .card:hover {{ transform: translateY(-2px); @@ -215,13 +327,7 @@ def index(): .card h3 {{ margin: 0 0 8px 0; font-size: 1.1rem; - }} - .card a {{ color: #58a6ff; - text-decoration: none; - }} - .card a:hover {{ - text-decoration: underline; }} .url {{ color: #6e7681; @@ -358,6 +464,10 @@ def render_repo(repo_path): with open(cache_file, 'r', encoding='utf-8') as f: return f.read() + # If not processing flag, show loading page first + if 'process' not in request.args: + return loading_page(repo_path) + # Generate fresh content with tempfile.TemporaryDirectory() as tmpdir: repo_dir = os.path.join(tmpdir, 'repo') From 773f23629eb2d2105b73c79975a461af12a0686e Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 09:15:18 -0700 Subject: [PATCH 11/16] Update examples - remove AutoGPT and use ggml-org/llama.cpp --- app.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app.py b/app.py index 9dd4ad2..24b15b7 100644 --- a/app.py +++ b/app.py @@ -399,8 +399,7 @@ def index():
- - +
From 014b72319057dcd635efdd7f773cb02bcef854fb Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 09:18:34 -0700 Subject: [PATCH 12/16] Remove examples section - recently rendered repos serve as examples now --- app.py | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/app.py b/app.py index 24b15b7..c65756c 100644 --- a/app.py +++ b/app.py @@ -272,35 +272,6 @@ def index(): button:hover {{ background: #2ea043; }} - .examples {{ - text-align: center; - margin: 30px 0; - padding: 20px; - background: #161b22; - border-radius: 8px; - }} - .examples p {{ - margin: 0 0 15px 0; - color: #8b949e; - }} - .example-buttons {{ - display: flex; - flex-wrap: wrap; - gap: 10px; - justify-content: center; - }} - .example-btn {{ - padding: 8px 16px; - background: #21262d; - border: 1px solid #30363d; - font-size: 14px; - transition: all 0.2s; - }} - .example-btn:hover {{ - background: #30363d; - border-color: #58a6ff; - transform: translateY(-1px); - }} .cards {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); @@ -374,12 +345,6 @@ def index(): .cards {{ grid-template-columns: 1fr; }} - .example-buttons {{ - flex-direction: column; - }} - .example-btn {{ - width: 100%; - }} }} @@ -394,16 +359,6 @@ def index(): -
-

Try these examples:

-
- - - - -
-
-

Recently Rendered

{cards_html} @@ -424,11 +379,6 @@ def index(): window.location.href = '/' + path; }} }} - - function tryExample(url) {{ - document.getElementById('url').value = url; - go(); - }} From 5aacc6547fff2d404ce3df973a4148174370ef6f Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 09:38:19 -0700 Subject: [PATCH 13/16] Fix import path for Render deployment --- app.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.py b/app.py index c65756c..b2188b2 100644 --- a/app.py +++ b/app.py @@ -4,6 +4,7 @@ """ import os +import sys import json import time import hashlib @@ -12,6 +13,9 @@ from flask import Flask, request, Response from pathlib import Path +# Ensure current directory is in path for imports +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) + # Import from original CLI from repo_to_single_page import ( git_clone, git_head_commit, collect_files, From 87087b904bc4080c188f2ce3f0dfd9c360591bc1 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 09:49:40 -0700 Subject: [PATCH 14/16] Add instructional copy with clipboard buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Better 'How to Use' section with clear instructions - Separate Web and CLI usage sections - Copy to clipboard buttons for each code example - Visual feedback when copying (button changes to ✓ Copied!) - Improved styling with code blocks - Works with older browsers fallback --- app.py | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 9 deletions(-) diff --git a/app.py b/app.py index b2188b2..135d787 100644 --- a/app.py +++ b/app.py @@ -324,21 +324,57 @@ def index(): color: #58a6ff; }} .info {{ - max-width: 600px; + max-width: 700px; margin: 40px auto; - text-align: center; color: #8b949e; - padding: 20px; + padding: 25px; background: #161b22; border-radius: 8px; }} - code {{ + .info h3 {{ + text-align: center; + }} + .usage-section {{ + margin: 20px 0; + }} + .usage-section p {{ + margin: 8px 0; + }} + .code-block {{ + display: flex; + align-items: center; + gap: 10px; background: #0d1117; - padding: 4px 8px; - border-radius: 3px; - font-size: 0.9em; + padding: 10px; + border-radius: 6px; + margin: 10px 0; + }} + .code-block code {{ + flex: 1; + font-family: monospace; + color: #79c0ff; + font-size: 14px; word-break: break-all; }} + .copy-btn {{ + padding: 6px 12px; + background: #21262d; + border: 1px solid #30363d; + color: #c9d1d9; + border-radius: 4px; + cursor: pointer; + font-size: 12px; + white-space: nowrap; + transition: all 0.2s; + }} + .copy-btn:hover {{ + background: #30363d; + border-color: #58a6ff; + }} + .copy-btn.copied {{ + background: #238636; + border-color: #238636; + }} @media (max-width: 640px) {{ .input-group {{ flex-direction: column; @@ -369,8 +405,30 @@ def index():
-

Direct URL: {request.host_url}github.com/user/repo

-

Install locally: pip install rendergit

+

How to Use

+ +
+

🌐 Web Version

+

Just replace user/repo with any GitHub repository:

+
+ {request.host_url}github.com/karpathy/nanoGPT + +
+
+ +
+

💻 CLI Version

+

Install once:

+
+ pip install rendergit + +
+

Then use anywhere:

+
+ rendergit https://github.com/karpathy/nanoGPT + +
+
@@ -383,6 +441,40 @@ def index(): window.location.href = '/' + path; }} }} + + function copyToClipboard(elementId) {{ + const text = document.getElementById(elementId).textContent; + navigator.clipboard.writeText(text).then(() => {{ + // Find the button that was clicked + const btn = event.target; + const originalText = btn.textContent; + btn.textContent = '✓ Copied!'; + btn.classList.add('copied'); + + setTimeout(() => {{ + btn.textContent = originalText; + btn.classList.remove('copied'); + }}, 2000); + }}).catch(err => {{ + // Fallback for older browsers + const textArea = document.createElement('textarea'); + textArea.value = text; + document.body.appendChild(textArea); + textArea.select(); + document.execCommand('copy'); + document.body.removeChild(textArea); + + const btn = event.target; + const originalText = btn.textContent; + btn.textContent = '✓ Copied!'; + btn.classList.add('copied'); + + setTimeout(() => {{ + btn.textContent = originalText; + btn.classList.remove('copied'); + }}, 2000); + }}); + }} From 9dd833c763beb459556393ba433d702d86c819a6 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 09:54:09 -0700 Subject: [PATCH 15/16] Make usage instructions more compact - Remove headers and explanatory text - Single row per method (Web/CLI) - Minimal copy buttons (just clipboard icon) - Smaller padding and margins - Cleaner, more scannable layout - Faster copy feedback (checkmark for 1.5s) --- app.py | 82 +++++++++++++++++++++++++--------------------------------- 1 file changed, 35 insertions(+), 47 deletions(-) diff --git a/app.py b/app.py index 135d787..98d6098 100644 --- a/app.py +++ b/app.py @@ -324,51 +324,51 @@ def index(): color: #58a6ff; }} .info {{ - max-width: 700px; - margin: 40px auto; - color: #8b949e; - padding: 25px; + max-width: 600px; + margin: 30px auto; + padding: 15px; background: #161b22; border-radius: 8px; }} - .info h3 {{ - text-align: center; - }} - .usage-section {{ - margin: 20px 0; + .usage-row {{ + display: flex; + align-items: center; + gap: 12px; + margin: 10px 0; }} - .usage-section p {{ - margin: 8px 0; + .usage-label {{ + color: #8b949e; + font-size: 14px; + min-width: 60px; }} .code-block {{ + flex: 1; display: flex; align-items: center; - gap: 10px; + gap: 8px; background: #0d1117; - padding: 10px; + padding: 8px 12px; border-radius: 6px; - margin: 10px 0; }} .code-block code {{ flex: 1; font-family: monospace; color: #79c0ff; - font-size: 14px; - word-break: break-all; + font-size: 13px; }} .copy-btn {{ - padding: 6px 12px; - background: #21262d; + padding: 4px 8px; + background: transparent; border: 1px solid #30363d; - color: #c9d1d9; + color: #8b949e; border-radius: 4px; cursor: pointer; - font-size: 12px; - white-space: nowrap; + font-size: 16px; + line-height: 1; transition: all 0.2s; }} .copy-btn:hover {{ - background: #30363d; + background: #21262d; border-color: #58a6ff; }} .copy-btn.copied {{ @@ -405,28 +405,19 @@ def index():
-

How to Use

- -
-

🌐 Web Version

-

Just replace user/repo with any GitHub repository:

+
+ 🌐 Web
- {request.host_url}github.com/karpathy/nanoGPT - + {request.host_url}github.com/user/repo +
-
-

💻 CLI Version

-

Install once:

+
+ 💻 CLI
pip install rendergit - -
-

Then use anywhere:

-
- rendergit https://github.com/karpathy/nanoGPT - +
@@ -445,16 +436,14 @@ def index(): function copyToClipboard(elementId) {{ const text = document.getElementById(elementId).textContent; navigator.clipboard.writeText(text).then(() => {{ - // Find the button that was clicked const btn = event.target; - const originalText = btn.textContent; - btn.textContent = '✓ Copied!'; + btn.textContent = '✓'; btn.classList.add('copied'); setTimeout(() => {{ - btn.textContent = originalText; + btn.textContent = '📋'; btn.classList.remove('copied'); - }}, 2000); + }}, 1500); }}).catch(err => {{ // Fallback for older browsers const textArea = document.createElement('textarea'); @@ -465,14 +454,13 @@ def index(): document.body.removeChild(textArea); const btn = event.target; - const originalText = btn.textContent; - btn.textContent = '✓ Copied!'; + btn.textContent = '✓'; btn.classList.add('copied'); setTimeout(() => {{ - btn.textContent = originalText; + btn.textContent = '📋'; btn.classList.remove('copied'); - }}, 2000); + }}, 1500); }}); }} From f95b8e7b47c6624828416542b22f66a11b665e08 Mon Sep 17 00:00:00 2001 From: Eden Chan Date: Thu, 21 Aug 2025 10:00:00 -0700 Subject: [PATCH 16/16] Simplify usage examples - Remove emoji icons from Web/CLI labels - Show actual nanoGPT example for both web and CLI - Combine pip install and usage in one CLI command - More concrete, ready-to-use examples --- app.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app.py b/app.py index 98d6098..0b993f8 100644 --- a/app.py +++ b/app.py @@ -339,7 +339,7 @@ def index(): .usage-label {{ color: #8b949e; font-size: 14px; - min-width: 60px; + min-width: 40px; }} .code-block {{ flex: 1; @@ -406,18 +406,18 @@ def index():
- 🌐 Web + Web
- {request.host_url}github.com/user/repo + {request.host_url}github.com/karpathy/nanoGPT
- 💻 CLI + CLI
- pip install rendergit - + pip install rendergit && rendergit https://github.com/karpathy/nanoGPT +