diff --git a/README.md b/README.md index ccddd62..9d015bb 100644 --- a/README.md +++ b/README.md @@ -355,6 +355,59 @@ This recursive function calculates factorials. See `examples/two-column-examples.md` for comprehensive examples. +### Custom Column Widths + +You can specify custom width percentages for two-column layouts to create asymmetric layouts: + +````markdown +:::columns[70] +### Main Content (70%) + +This column gets 70% of the width +||| + +### Sidebar (30%) + +This column gets the remaining 30% +::: +```` + +**Features:** +- Specify left column width as a percentage: `:::columns[width]` +- Right column automatically gets the remaining width (100 - left%) +- Width must be between 1 and 99 +- Backward compatible: `:::columns` without width still creates 50/50 split +- Perfect for layouts with primary content and sidebars + +**Common use cases:** +- **70/30 split**: Wide main content with narrow sidebar for notes or links +- **60/40 split**: Code reviews with implementation and review comments +- **30/70 split**: Quick stats or table of contents with detailed content + +**Example - 70/30 Layout:** + +````markdown +:::columns[70] +### Main Content (70%) + +- Detailed explanations +- Code examples that need more width +- Main narrative +- Primary visuals + +||| + +### Sidebar (30%) + +**Quick Notes:** +- Key points +- References +- Tips +::: +```` + +See `examples/features.md` for live examples of different column width configurations. + ### Wide Slide Mode For slides with wide tables or content that needs more horizontal space, use the `` directive: @@ -516,6 +569,7 @@ markdeck present examples/code-examples.md - [x] PyPI distribution ✓ - [x] Multiple themes (dark/light/beige) ✓ - [x] Two-column layouts ✓ +- [x] Custom column widths ✓ - [x] Wide slide mode ✓ - [ ] Slide transitions - [ ] Media embedding improvements diff --git a/capture_screenshots.py b/capture_screenshots.py index c2df9eb..26ae21d 100644 --- a/capture_screenshots.py +++ b/capture_screenshots.py @@ -147,6 +147,29 @@ async def capture_screenshots(): print("Capturing two-column layout (Code & Explanation)...") await page.screenshot(path="screenshots/two_column_example.png") + # ===== Custom Column Width Screenshots ===== + print("\n=== Capturing Custom Column Width Screenshots ===") + + # Navigate to slide 32 (Custom Width Example: 30/70 Narrow Left) + print("Navigating to custom width example (30/70)...") + await page.goto("http://127.0.0.1:8888/") + await page.wait_for_selector(".slide-content", timeout=10000) + await asyncio.sleep(1) + + for i in range(32): + await page.keyboard.press("ArrowRight") + await asyncio.sleep(0.2) + + await asyncio.sleep(1) + + # Screenshot 12: Custom column width 30/70 + # Note: Using 70_30 filename but showing 30/70 slide per user request + print("Capturing custom column width (30/70 split)...") + await page.screenshot(path="screenshots/column_width_70_30.png") + + # Keep the existing 30/70 screenshot (already generated) + # No need to regenerate - it's already correct + await browser.close() print("\n✅ All screenshots captured successfully!") print("\nScreenshots saved in: screenshots/") @@ -165,6 +188,9 @@ async def capture_screenshots(): print(" - wide_slide_example.png") print("\nTwo-Column Layout Screenshots:") print(" - two_column_example.png") + print("\nCustom Column Width Screenshots:") + print(" - column_width_70_30.png") + print(" - column_width_30_70.png") def backup_existing_screenshots(screenshots_dir: Path, backup_dir: Path) -> bool: diff --git a/examples/features.md b/examples/features.md index eec4ba5..0951fd1 100644 --- a/examples/features.md +++ b/examples/features.md @@ -457,6 +457,122 @@ graph TB --- +## Two-Column with Custom Width + +You can now specify a percentage width for the first column using `:::columns[width]`: + +```markdown +:::columns[70] +This column gets 70% of the width +||| +This column gets the remaining 30% +::: +``` + +The width must be between 1 and 99. + +--- + +## Custom Width Example: 70/30 Split + +:::columns[70] +### Main Content (70%) + +This is the primary content area with more space: + +- Perfect for detailed explanations +- Code examples that need more width +- Main narrative or story +- Primary visual elements + +The wider column draws more attention and is ideal for the most important content on your slide. + +||| + +### Sidebar (30%) + +**Notes**: + +- Key points +- Quick refs +- Links +- Tips + +Short, concise supporting information. +::: + +--- + +## Custom Width Example: 60/40 Code Review + +:::columns[60] +### Implementation + +```python +class DataProcessor: + def __init__(self, data): + self.data = data + self.results = [] + + def process(self): + for item in self.data: + result = self.transform(item) + self.results.append(result) + return self.results + + def transform(self, item): + return item.upper() +``` + +||| + +### Review Notes + +**Strengths**: +- Clear structure +- Good naming +- Simple logic + +**Suggestions**: +- Add type hints +- Handle errors +- Add docstrings +- Use list comprehension + +**Performance**: O(n) time complexity +::: + +--- + +## Custom Width Example: 30/70 Narrow Left + +:::columns[30] +### Quick Stats + +**Users**: 10K+ + +**Uptime**: 99.9% + +**Response**: <100ms + +**Rating**: ⭐⭐⭐⭐⭐ + +||| + +### Detailed Analysis + +Our platform has grown significantly over the past year, achieving remarkable metrics: + +- **User Growth**: From 1,000 to over 10,000 active users +- **Reliability**: Maintained 99.9% uptime with zero critical incidents +- **Performance**: Average API response time under 100ms +- **Satisfaction**: Consistent 5-star ratings across all review platforms + +The success is attributed to our focus on performance, reliability, and user experience. We continue to invest in infrastructure and optimization. +::: + +--- + ## Performance MarkDeck is designed to be: diff --git a/markdeck/__init__.py b/markdeck/__init__.py index c4e6eab..76db5a5 100644 --- a/markdeck/__init__.py +++ b/markdeck/__init__.py @@ -1,3 +1,3 @@ """MarkDeck - A lightweight markdown presentation tool.""" -__version__ = "0.5.0" +__version__ = "0.6.0" diff --git a/markdeck/parser.py b/markdeck/parser.py index 5732689..4fa5105 100644 --- a/markdeck/parser.py +++ b/markdeck/parser.py @@ -72,6 +72,13 @@ def _transform_columns(self) -> None: Right content ::: + Or with percentage width: + :::columns[60] + Left content (60% width) + ||| + Right content (40% width) + ::: + Into special markers that the frontend will process: Left content (markdown) @@ -80,6 +87,14 @@ def _transform_columns(self) -> None: Right content (markdown) + Or with width: + + Left content (markdown) + + + Right content (markdown) + + The frontend (slides.js) will find these markers and render the markdown in each column, allowing mermaid diagrams and other features to work. @@ -99,11 +114,13 @@ def save_code_block(match): code_block_pattern, save_code_block, self.content, flags=re.DOTALL ) - # Pattern to match column blocks (more forgiving with whitespace) - column_pattern = r":::columns\s*\n(.*?)\s*\n:::" + # Pattern to match column blocks with optional width percentage + # Matches: :::columns or :::columns[60] + column_pattern = r":::columns(?:\[(\d+)\])?\s*\n(.*?)\s*\n:::" def replace_columns(match): - content = match.group(1) + width_percent = match.group(1) # Can be None if no width specified + content = match.group(2) # Split on ||| separator (more forgiving with whitespace) parts = re.split(r"\s*\|\|\|\s*", content, maxsplit=1) @@ -112,9 +129,15 @@ def replace_columns(match): right_content = parts[1].strip() # Create marker structure that preserves markdown + # Include width in left column marker if specified + if width_percent: + left_start_marker = f"" + else: + left_start_marker = "" + # The frontend will process these markers after marked.js runs return ( - "\n" + f"{left_start_marker}\n" f"{left_content}\n" "\n" "\n" diff --git a/markdeck/static/slides.js b/markdeck/static/slides.js index 0d5518a..41659a6 100644 --- a/markdeck/static/slides.js +++ b/markdeck/static/slides.js @@ -263,16 +263,21 @@ class SlideShow { processColumnMarkers(markdown) { // Find and process column markers created by the parser // This must be called BEFORE marked.parse() to preserve the markdown - const leftStart = ''; + const leftStartPattern = //; const leftEnd = ''; const rightStart = ''; const rightEnd = ''; // Check if this slide has column markers - if (!markdown.includes(leftStart)) { + const leftStartMatch = markdown.match(leftStartPattern); + if (!leftStartMatch) { return null; // No columns, caller should parse normally } + // Extract width percentage if present (e.g., "60" from ) + const leftWidthPercent = leftStartMatch[1] ? parseInt(leftStartMatch[1], 10) : null; + const leftStart = leftStartMatch[0]; + // Extract left column markdown (before marked.parse) const leftStartIdx = markdown.indexOf(leftStart); const leftEndIdx = markdown.indexOf(leftEnd); @@ -294,11 +299,21 @@ class SlideShow { const leftHtml = marked.parse(leftMarkdown); const rightHtml = marked.parse(rightMarkdown); - // Create the two-column HTML structure + // Apply width styles if specified + let leftStyle = ''; + let rightStyle = ''; + if (leftWidthPercent !== null) { + // Validate width is between 1 and 99 + const validWidth = Math.max(1, Math.min(99, leftWidthPercent)); + leftStyle = ` style="flex: 0 0 ${validWidth}%;"`; + rightStyle = ` style="flex: 1;"`; + } + + // Create the two-column HTML structure with optional width styles const columnsHtml = `