Skip to content

Commit e2f4bed

Browse files
authored
Merge pull request #20 from orangewise/claude/add-column-width-percentage-d1ebw
feat: Add percentage width support for two-column layouts
2 parents 640e8e2 + 58ab04e commit e2f4bed

10 files changed

Lines changed: 395 additions & 11 deletions

File tree

README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,59 @@ This recursive function calculates factorials.
355355

356356
See `examples/two-column-examples.md` for comprehensive examples.
357357

358+
### Custom Column Widths
359+
360+
You can specify custom width percentages for two-column layouts to create asymmetric layouts:
361+
362+
````markdown
363+
:::columns[70]
364+
### Main Content (70%)
365+
366+
This column gets 70% of the width
367+
|||
368+
369+
### Sidebar (30%)
370+
371+
This column gets the remaining 30%
372+
:::
373+
````
374+
375+
**Features:**
376+
- Specify left column width as a percentage: `:::columns[width]`
377+
- Right column automatically gets the remaining width (100 - left%)
378+
- Width must be between 1 and 99
379+
- Backward compatible: `:::columns` without width still creates 50/50 split
380+
- Perfect for layouts with primary content and sidebars
381+
382+
**Common use cases:**
383+
- **70/30 split**: Wide main content with narrow sidebar for notes or links
384+
- **60/40 split**: Code reviews with implementation and review comments
385+
- **30/70 split**: Quick stats or table of contents with detailed content
386+
387+
**Example - 70/30 Layout:**
388+
389+
````markdown
390+
:::columns[70]
391+
### Main Content (70%)
392+
393+
- Detailed explanations
394+
- Code examples that need more width
395+
- Main narrative
396+
- Primary visuals
397+
398+
|||
399+
400+
### Sidebar (30%)
401+
402+
**Quick Notes:**
403+
- Key points
404+
- References
405+
- Tips
406+
:::
407+
````
408+
409+
See `examples/features.md` for live examples of different column width configurations.
410+
358411
### Wide Slide Mode
359412

360413
For slides with wide tables or content that needs more horizontal space, use the `<!--SLIDE:wide-->` directive:
@@ -516,6 +569,7 @@ markdeck present examples/code-examples.md
516569
- [x] PyPI distribution ✓
517570
- [x] Multiple themes (dark/light/beige) ✓
518571
- [x] Two-column layouts ✓
572+
- [x] Custom column widths ✓
519573
- [x] Wide slide mode ✓
520574
- [ ] Slide transitions
521575
- [ ] Media embedding improvements

capture_screenshots.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,29 @@ async def capture_screenshots():
147147
print("Capturing two-column layout (Code & Explanation)...")
148148
await page.screenshot(path="screenshots/two_column_example.png")
149149

150+
# ===== Custom Column Width Screenshots =====
151+
print("\n=== Capturing Custom Column Width Screenshots ===")
152+
153+
# Navigate to slide 32 (Custom Width Example: 30/70 Narrow Left)
154+
print("Navigating to custom width example (30/70)...")
155+
await page.goto("http://127.0.0.1:8888/")
156+
await page.wait_for_selector(".slide-content", timeout=10000)
157+
await asyncio.sleep(1)
158+
159+
for i in range(32):
160+
await page.keyboard.press("ArrowRight")
161+
await asyncio.sleep(0.2)
162+
163+
await asyncio.sleep(1)
164+
165+
# Screenshot 12: Custom column width 30/70
166+
# Note: Using 70_30 filename but showing 30/70 slide per user request
167+
print("Capturing custom column width (30/70 split)...")
168+
await page.screenshot(path="screenshots/column_width_70_30.png")
169+
170+
# Keep the existing 30/70 screenshot (already generated)
171+
# No need to regenerate - it's already correct
172+
150173
await browser.close()
151174
print("\n✅ All screenshots captured successfully!")
152175
print("\nScreenshots saved in: screenshots/")
@@ -165,6 +188,9 @@ async def capture_screenshots():
165188
print(" - wide_slide_example.png")
166189
print("\nTwo-Column Layout Screenshots:")
167190
print(" - two_column_example.png")
191+
print("\nCustom Column Width Screenshots:")
192+
print(" - column_width_70_30.png")
193+
print(" - column_width_30_70.png")
168194

169195

170196
def backup_existing_screenshots(screenshots_dir: Path, backup_dir: Path) -> bool:

examples/features.md

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,122 @@ graph TB
457457

458458
---
459459

460+
## Two-Column with Custom Width
461+
462+
You can now specify a percentage width for the first column using `:::columns[width]`:
463+
464+
```markdown
465+
:::columns[70]
466+
This column gets 70% of the width
467+
|||
468+
This column gets the remaining 30%
469+
:::
470+
```
471+
472+
The width must be between 1 and 99.
473+
474+
---
475+
476+
## Custom Width Example: 70/30 Split
477+
478+
:::columns[70]
479+
### Main Content (70%)
480+
481+
This is the primary content area with more space:
482+
483+
- Perfect for detailed explanations
484+
- Code examples that need more width
485+
- Main narrative or story
486+
- Primary visual elements
487+
488+
The wider column draws more attention and is ideal for the most important content on your slide.
489+
490+
|||
491+
492+
### Sidebar (30%)
493+
494+
**Notes**:
495+
496+
- Key points
497+
- Quick refs
498+
- Links
499+
- Tips
500+
501+
Short, concise supporting information.
502+
:::
503+
504+
---
505+
506+
## Custom Width Example: 60/40 Code Review
507+
508+
:::columns[60]
509+
### Implementation
510+
511+
```python
512+
class DataProcessor:
513+
def __init__(self, data):
514+
self.data = data
515+
self.results = []
516+
517+
def process(self):
518+
for item in self.data:
519+
result = self.transform(item)
520+
self.results.append(result)
521+
return self.results
522+
523+
def transform(self, item):
524+
return item.upper()
525+
```
526+
527+
|||
528+
529+
### Review Notes
530+
531+
**Strengths**:
532+
- Clear structure
533+
- Good naming
534+
- Simple logic
535+
536+
**Suggestions**:
537+
- Add type hints
538+
- Handle errors
539+
- Add docstrings
540+
- Use list comprehension
541+
542+
**Performance**: O(n) time complexity
543+
:::
544+
545+
---
546+
547+
## Custom Width Example: 30/70 Narrow Left
548+
549+
:::columns[30]
550+
### Quick Stats
551+
552+
**Users**: 10K+
553+
554+
**Uptime**: 99.9%
555+
556+
**Response**: <100ms
557+
558+
**Rating**: ⭐⭐⭐⭐⭐
559+
560+
|||
561+
562+
### Detailed Analysis
563+
564+
Our platform has grown significantly over the past year, achieving remarkable metrics:
565+
566+
- **User Growth**: From 1,000 to over 10,000 active users
567+
- **Reliability**: Maintained 99.9% uptime with zero critical incidents
568+
- **Performance**: Average API response time under 100ms
569+
- **Satisfaction**: Consistent 5-star ratings across all review platforms
570+
571+
The success is attributed to our focus on performance, reliability, and user experience. We continue to invest in infrastructure and optimization.
572+
:::
573+
574+
---
575+
460576
## Performance
461577

462578
MarkDeck is designed to be:

markdeck/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""MarkDeck - A lightweight markdown presentation tool."""
22

3-
__version__ = "0.5.0"
3+
__version__ = "0.6.0"

markdeck/parser.py

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,13 @@ def _transform_columns(self) -> None:
7272
Right content
7373
:::
7474
75+
Or with percentage width:
76+
:::columns[60]
77+
Left content (60% width)
78+
|||
79+
Right content (40% width)
80+
:::
81+
7582
Into special markers that the frontend will process:
7683
<!-- COLUMN:LEFT:START -->
7784
Left content (markdown)
@@ -80,6 +87,14 @@ def _transform_columns(self) -> None:
8087
Right content (markdown)
8188
<!-- COLUMN:RIGHT:END -->
8289
90+
Or with width:
91+
<!-- COLUMN:LEFT:START:60 -->
92+
Left content (markdown)
93+
<!-- COLUMN:LEFT:END -->
94+
<!-- COLUMN:RIGHT:START -->
95+
Right content (markdown)
96+
<!-- COLUMN:RIGHT:END -->
97+
8398
The frontend (slides.js) will find these markers and render the markdown
8499
in each column, allowing mermaid diagrams and other features to work.
85100
@@ -99,11 +114,13 @@ def save_code_block(match):
99114
code_block_pattern, save_code_block, self.content, flags=re.DOTALL
100115
)
101116

102-
# Pattern to match column blocks (more forgiving with whitespace)
103-
column_pattern = r":::columns\s*\n(.*?)\s*\n:::"
117+
# Pattern to match column blocks with optional width percentage
118+
# Matches: :::columns or :::columns[60]
119+
column_pattern = r":::columns(?:\[(\d+)\])?\s*\n(.*?)\s*\n:::"
104120

105121
def replace_columns(match):
106-
content = match.group(1)
122+
width_percent = match.group(1) # Can be None if no width specified
123+
content = match.group(2)
107124
# Split on ||| separator (more forgiving with whitespace)
108125
parts = re.split(r"\s*\|\|\|\s*", content, maxsplit=1)
109126

@@ -112,9 +129,15 @@ def replace_columns(match):
112129
right_content = parts[1].strip()
113130

114131
# Create marker structure that preserves markdown
132+
# Include width in left column marker if specified
133+
if width_percent:
134+
left_start_marker = f"<!-- COLUMN:LEFT:START:{width_percent} -->"
135+
else:
136+
left_start_marker = "<!-- COLUMN:LEFT:START -->"
137+
115138
# The frontend will process these markers after marked.js runs
116139
return (
117-
"<!-- COLUMN:LEFT:START -->\n"
140+
f"{left_start_marker}\n"
118141
f"{left_content}\n"
119142
"<!-- COLUMN:LEFT:END -->\n"
120143
"<!-- COLUMN:RIGHT:START -->\n"

markdeck/static/slides.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -263,16 +263,21 @@ class SlideShow {
263263
processColumnMarkers(markdown) {
264264
// Find and process column markers created by the parser
265265
// This must be called BEFORE marked.parse() to preserve the markdown
266-
const leftStart = '<!-- COLUMN:LEFT:START -->';
266+
const leftStartPattern = /<!-- COLUMN:LEFT:START(?::(\d+))? -->/;
267267
const leftEnd = '<!-- COLUMN:LEFT:END -->';
268268
const rightStart = '<!-- COLUMN:RIGHT:START -->';
269269
const rightEnd = '<!-- COLUMN:RIGHT:END -->';
270270

271271
// Check if this slide has column markers
272-
if (!markdown.includes(leftStart)) {
272+
const leftStartMatch = markdown.match(leftStartPattern);
273+
if (!leftStartMatch) {
273274
return null; // No columns, caller should parse normally
274275
}
275276

277+
// Extract width percentage if present (e.g., "60" from <!-- COLUMN:LEFT:START:60 -->)
278+
const leftWidthPercent = leftStartMatch[1] ? parseInt(leftStartMatch[1], 10) : null;
279+
const leftStart = leftStartMatch[0];
280+
276281
// Extract left column markdown (before marked.parse)
277282
const leftStartIdx = markdown.indexOf(leftStart);
278283
const leftEndIdx = markdown.indexOf(leftEnd);
@@ -294,11 +299,21 @@ class SlideShow {
294299
const leftHtml = marked.parse(leftMarkdown);
295300
const rightHtml = marked.parse(rightMarkdown);
296301

297-
// Create the two-column HTML structure
302+
// Apply width styles if specified
303+
let leftStyle = '';
304+
let rightStyle = '';
305+
if (leftWidthPercent !== null) {
306+
// Validate width is between 1 and 99
307+
const validWidth = Math.max(1, Math.min(99, leftWidthPercent));
308+
leftStyle = ` style="flex: 0 0 ${validWidth}%;"`;
309+
rightStyle = ` style="flex: 1;"`;
310+
}
311+
312+
// Create the two-column HTML structure with optional width styles
298313
const columnsHtml = `
299314
<div class="columns-container">
300-
<div class="column-left">${leftHtml}</div>
301-
<div class="column-right">${rightHtml}</div>
315+
<div class="column-left"${leftStyle}>${leftHtml}</div>
316+
<div class="column-right"${rightStyle}>${rightHtml}</div>
302317
</div>
303318
`;
304319

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "markdeck"
3-
version = "0.5.0"
3+
version = "0.6.0"
44
description = "A lightweight markdown presentation tool"
55
readme = "README.md"
66
requires-python = ">=3.11"

screenshots/column_width_30_70.png

130 KB
Loading

screenshots/column_width_70_30.png

134 KB
Loading

0 commit comments

Comments
 (0)