PyRST Development Guide¶
This guide covers local development setup, workflows, testing, and contributing to PyRST.
Prerequisites¶
Required Software¶
Python: 3.14 or higher
uv: Python package manager (installation guide)
Modern browser: Chrome, Firefox, or Safari (for testing)
Git: For version control
Recommended Tools¶
Code editor: VS Code, PyCharm, or similar
Browser DevTools: For debugging JavaScript
Python debugger: For debugging Python code
Local Development Setup¶
1. Clone the Repository¶
git clone https://github.com/JacobCoffee/pyrst.git
cd pyrst
2. Install Dependencies¶
make install
This will:
Create a virtual environment
Install Python dependencies (docutils, pytest, ruff, etc.)
Sync dependencies from
pyproject.toml
Alternative (without make):
uv sync
3. Start Development Server¶
make serve
This starts a local HTTP server at http://localhost:8000 serving the src/pyrst/ directory.
Alternative (without make):
cd src/pyrst/
uv run python -m http.server 8000
4. Open in Browser¶
Navigate to: http://localhost:8000
The application should load and initialize Pyodide (may take 10-30 seconds on first load).
Development Workflow¶
Typical Development Cycle¶
Make code changes in your editor
Refresh browser to see changes (no build step!)
Check browser console for JavaScript errors
Test functionality manually
Run tests (when available)
Commit changes with meaningful messages
Hot Reloading¶
PyRST doesn’t have hot reloading built in (no build step). To see changes:
Save file in editor
Refresh browser (Cmd+R / Ctrl+R)
Tip: Use browser DevTools “Disable cache” option for faster iteration.
Browser DevTools¶
Console Tab:
JavaScript errors and warnings
Console.log output from app.js
Pyodide initialization logs
Network Tab:
Pyodide package downloads
CDN resource loading
Check for 404s or slow loads
Application Tab:
localStorage inspection (panel sizes)
View stored preferences
Elements Tab:
Inspect DOM structure
Debug CSS issues
View computed styles
Project Structure¶
pyrst/
├── src/pyrst/ # Main application source
│ ├── index.html # HTML structure
│ ├── app.js # Core JavaScript logic
│ ├── app_export.js # Export functionality
│ ├── error-highlighting.js # Error detection
│ ├── main.py # Python RST conversion
│ ├── sample.rst # Sample content
│ ├── __init__.py # Python package marker
│ └── css/
│ ├── editor.css # Editor styling
│ ├── preview.css # Preview styling
│ └── resizable.css # Resizable panel styling
├── docs/ # Documentation
│ ├── architecture.rst
│ ├── features.rst
│ ├── development.rst # This file
│ ├── api.rst
│ └── toolbar.rst
├── tests/ # Test files
│ └── (future test files)
├── pyproject.toml # Python project config
├── uv.lock # Dependency lock file
├── Makefile # Build commands
├── README.md # Project README
├── CHANGELOG.md # Version history
├── CONTRIBUTING.md # Contribution guide
└── TODO.md # Future improvements
Key Files¶
index.html:
Main HTML structure
Loads CSS and JavaScript
Defines editor, preview, toolbar, error panel
app.js:
Pyodide initialization
Toolbar button handlers
Resizable panel logic
RST conversion orchestration
app_export.js:
Copy to clipboard functions
Download file functions
Toast notifications
error-highlighting.js:
Parse docutils system messages
Error panel management
Line highlighting and navigation
main.py:
convert_rst(rst_text)functiondocutils integration
Error handling
CSS files:
Modular CSS for different concerns
Tailwind classes used in HTML
Custom styles for RST elements
Building and Testing¶
No Build Step Required¶
PyRST is a static site with no build step:
HTML/CSS/JS served directly
Pyodide loaded from CDN
No transpilation needed
Running Tests¶
Current Status: Tests are in development.
Future Testing:
# Unit tests
pytest tests/
# Pyodide tests (in-browser)
pytest --pyodide tests/
Manual Testing Checklist:
[ ] Toolbar buttons insert correct RST syntax
[ ] Preview updates after typing
[ ] Errors appear in error panel
[ ] Error line highlighting works
[ ] Click error to jump to line
[ ] Resizable panels save/load sizes
[ ] Copy RST to clipboard
[ ] Copy HTML to clipboard
[ ] Download RST file
[ ] Download HTML file
[ ] Download PDF (print dialog)
[ ] Toast notifications appear
[ ] Loading screen shows during init
[ ] Sample content loads correctly
Code Quality Tools¶
Ruff (Python linter/formatter):
uv run ruff check src/
uv run ruff format src/
Browser Console:
Check for JavaScript errors
Warning about deprecated APIs
Pyodide error messages
Code Style Guidelines¶
Python¶
Style: Follow PEP 8 with ruff defaults
Guidelines:
Type hints on function signatures
Docstrings for public functions
4-space indentation
Max line length: 100 characters
Use descriptive variable names
Example:
def convert_rst(rst_text: str) -> str:
"""Convert reStructuredText to HTML.
Args:
rst_text: Input RST content
Returns:
HTML output string
"""
# Implementation
JavaScript¶
Style: Modern ES6+ JavaScript
Guidelines:
Use const/let, avoid var
Arrow functions for callbacks
Template literals for strings
Async/await for promises
JSDoc comments for complex functions
4-space indentation
Descriptive function names
Example:
/**
* Convert RST text to HTML using Pyodide
* @param {string} rstText - RST input
* @returns {Promise<string>} HTML output
*/
async function convertRST(rstText) {
pyodide.globals.set('rst_input', rstText);
const htmlString = await pyodide.runPythonAsync(`
publish_string(rst_input, writer_name="html5").decode("utf-8")
`);
return htmlString;
}
HTML¶
Style: Semantic HTML5
Guidelines:
Use semantic elements (header, main, section, footer)
Include ARIA labels for accessibility
Tailwind classes for styling
Meaningful IDs and class names
CSS¶
Style: BEM-like naming, modular
Guidelines:
One CSS file per concern (editor, preview, resizable)
Descriptive class names
Mobile-first approach
Use CSS variables for colors (future)
Adding New Features¶
2. Export Format¶
Example: Adding JSON export
Step 1: Add button (index.html)
<button id="btn-download-json" class="export-btn" title="Download JSON">
<i data-feather="download"></i>
<span class="text-xs ml-1">JSON</span>
</button>
Step 2: Add export function (app_export.js)
function downloadJSONFile() {
const editor = document.getElementById('editor');
const data = { rst: editor.value, timestamp: Date.now() };
const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'document.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
showToast('JSON file downloaded!', 'success');
}
Step 3: Register handler (app_export.js)
function initExportButtons() {
// ... existing buttons
document.getElementById('btn-download-json')
.addEventListener('click', downloadJSONFile);
}
3. New CSS Theme¶
Example: Adding dark mode
Step 1: Add theme toggle button (index.html)
Step 2: Create dark mode CSS variables (editor.css)
body.dark-mode {
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
/* ... more variables */
}
Step 3: Add toggle function (app.js)
function toggleDarkMode() {
document.body.classList.toggle('dark-mode');
localStorage.setItem('darkMode', document.body.classList.contains('dark-mode'));
}
4. Python Function¶
Example: Adding custom RST directive support
Step 1: Update main.py
def convert_rst(rst_text: str) -> str:
"""Convert RST with custom directives."""
from docutils import nodes
from docutils.parsers.rst import Directive
# Define custom directive
class CustomDirective(Directive):
# Implementation
pass
# Register directive
directives.register_directive('custom', CustomDirective)
# Convert as usual
return publish_string(rst_text, writer_name="html5").decode("utf-8")
Step 2: Test in editor
Troubleshooting¶
Pyodide Won’t Load¶
Symptom: Infinite loading screen
Possible Causes:
Network issues (CDN unreachable)
Browser doesn’t support WASM
JavaScript errors during init
Solutions:
Check browser console for errors
Verify internet connection
Try different browser
Clear browser cache
Check Pyodide CDN status
Debug:
// Add logging in initPyodide()
console.log('Step 1: Loading Pyodide');
pyodide = await loadPyodide();
console.log('Step 2: Pyodide loaded');
Preview Not Updating¶
Symptom: Typing in editor doesn’t update preview
Possible Causes:
JavaScript error in conversion
Event listener not attached
Debounce too long
Solutions:
Check console for errors
Verify
editor.addEventListener('input', ...)is calledReduce debounce delay temporarily
Check if Pyodide is initialized
Debug:
editor.addEventListener('input', (e) => {
console.log('Input event fired:', e.target.value.length);
debouncedConvert(e.target.value);
});
Errors Not Showing¶
Symptom: Known RST errors don’t appear in error panel
Possible Causes:
System messages hidden by docutils settings
Parsing regex not matching
Error panel hidden
Solutions:
Check
report_levelin main.py (should be low to show warnings)Test
parseSystemMessages()with sample HTMLVerify error panel CSS (not
display: none)
Debug:
function convertRST(rstText) {
const html = await convertRST(rstText);
console.log('HTML output:', html);
const errors = parseSystemMessages(html);
console.log('Parsed errors:', errors);
}
localStorage Not Persisting¶
Symptom: Panel sizes reset on refresh
Possible Causes:
Browser in private/incognito mode
localStorage disabled
Storage quota exceeded
Wrong key name
Solutions:
Exit private browsing
Check browser settings
Clear old localStorage data
Verify key:
pyrst-panel-sizes
Debug:
console.log('Saved sizes:', localStorage.getItem('pyrst-panel-sizes'));
savePanelSizes();
console.log('New sizes:', localStorage.getItem('pyrst-panel-sizes'));
CSS Not Loading¶
Symptom: Unstyled or broken layout
Possible Causes:
Wrong file path
CSS syntax error
Tailwind CDN blocked
Cache issue
Solutions:
Check Network tab for 404s
Validate CSS syntax
Check Content Security Policy
Hard refresh (Cmd+Shift+R)
Debug:
# Verify CSS files exist
ls -la src/pyrst/css/
Performance Issues¶
Symptom: Slow typing or laggy preview
Possible Causes:
Very large document
Complex RST directives
Too many error highlights
Browser memory issue
Solutions:
Increase debounce delay
Optimize
highlightEditorLines()Limit max highlights
Close other browser tabs
Optimization:
// Increase debounce from 300ms to 500ms
const debouncedConvert = debounce(async (text) => {
// ...
}, 500);
Deployment¶
Static Hosting¶
PyRST is a static site and can be deployed to:
GitHub Pages
Netlify
Vercel
Cloudflare Pages
AWS S3 + CloudFront
Any static file server
Deployment Steps (GitHub Pages Example)¶
Push to GitHub:
git push origin mainConfigure GitHub Pages:
Repository Settings → Pages
Source: Deploy from branch
Branch:
main, folder:/src/pyrst
Access site:
URL:
https://username.github.io/pyrst/
Build Optimization (Optional)¶
For production, consider:
Minify CSS: Inline critical CSS, minify files
Service Worker: Cache Pyodide for offline use
Preload Resources: Add
<link rel="preload">for CDN resourcesLazy Loading: Load Pyodide on first edit (not on page load)
Contributing¶
See CONTRIBUTING.md for:
Code of conduct
Pull request process
Coding standards
Review guidelines
Getting Help¶
Issues: GitHub Issues
Discussions: GitHub Discussions
Email: jacob@z7x.org
Last Updated: 2025-11-15