From afe6bcf6fe70409cc2809eccd7fb3c1d314cd69d Mon Sep 17 00:00:00 2001 From: tegwick Date: Mon, 10 Nov 2025 11:04:51 +0100 Subject: [PATCH] feat: implement ChatGPT document theme for compact interactive reading (Issue #165) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive ChatGPT-style document theme optimized for modern interactive content: **Theme Features:** - Inter font family for clean, modern sans-serif typography - Compact 580px width for chat-like reading experience - High contrast (#1f1f1f text on white background) - ChatGPT signature green (#10a37f) accent color - Tight 1.5 line height for efficient information density - Modern 8px border radius for contemporary feel - Optimized code block styling with proper monospace fonts **Technical Implementation:** - Added 'chatgpt' theme to LAYERED_THEMES system (document scope) - Full backward compatibility with TEMPLATE_STYLES and LEGACY_THEME_MAPPING - CLI integration: `markitect md-render --theme chatgpt` - Proper theme layering support (combines with light/dark modes) **Quality Assurance:** - Comprehensive 9-test suite covering all functionality (9/9 passing) - Verified HTML generation and CSS styling - Tested CLI integration and theme combinations - Full compatibility with existing theme architecture Successfully closes Issue #165 with compact, readable layout optimized for interactive content following ChatGPT's interface design principles. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../plugins/builtin/markdown_commands.py | 34 ++- tests/test_issue_165_chatgpt_theme.py | 254 ++++++++++++++++++ 2 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 tests/test_issue_165_chatgpt_theme.py diff --git a/markitect/plugins/builtin/markdown_commands.py b/markitect/plugins/builtin/markdown_commands.py index 2481fd6e..2929f8a3 100644 --- a/markitect/plugins/builtin/markdown_commands.py +++ b/markitect/plugins/builtin/markdown_commands.py @@ -201,6 +201,32 @@ LAYERED_THEMES = { 'blockquote_color': '#666666' } }, + 'chatgpt': { + 'scope': 'document', + 'properties': { + 'font_family': 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + 'heading_font_family': 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + 'max_width': '580px', + 'body_background': '#ffffff', + 'body_color': '#1f1f1f', + 'heading_color': '#1f1f1f', + 'text_align': 'left', + 'line_height': '1.5', + 'heading_style': 'minimal', + 'accent_color': '#10a37f', + 'link_color': '#10a37f', + 'link_hover_color': '#0d8c6d', + 'code_background': '#f7f7f7', + 'code_color': '#1f1f1f', + 'code_font_family': '"SF Mono", Monaco, Inconsolata, "Roboto Mono", Consolas, "Courier New", monospace', + 'font_size': '15px', + 'heading_margin': '1.2em 0 0.6em 0', + 'paragraph_margin': '1em 0', + 'border_radius': '8px', + 'blockquote_border': '#10a37f', + 'blockquote_color': '#6b7280' + } + }, # Branding Themes - Company/personal styling 'corporate': { @@ -227,7 +253,8 @@ LEGACY_THEME_MAPPING = { 'github': ['light', 'standard', 'github'], 'dark': ['dark', 'standard', 'basic'], 'academic': ['light', 'standard', 'academic'], - 'substack': ['light', 'standard', 'substack'] + 'substack': ['light', 'standard', 'substack'], + 'chatgpt': ['light', 'standard', 'chatgpt'] } # Keep TEMPLATE_STYLES for backward compatibility in tests @@ -256,6 +283,11 @@ TEMPLATE_STYLES = { 'body_color': '#333333', 'font_family': 'Spectral, Georgia, "Times New Roman", serif', 'max_width': '680px' + }, + 'chatgpt': { + 'body_color': '#1f1f1f', + 'font_family': 'Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + 'max_width': '580px' } } diff --git a/tests/test_issue_165_chatgpt_theme.py b/tests/test_issue_165_chatgpt_theme.py new file mode 100644 index 00000000..576cde64 --- /dev/null +++ b/tests/test_issue_165_chatgpt_theme.py @@ -0,0 +1,254 @@ +""" +Tests for Issue #165: Document theme that mimics ChatGPT chat interface fonts and fontsizes + +This module tests the implementation of a ChatGPT-style document theme +for compact, readable content optimized for interactive reading. +""" + +import pytest +import tempfile +import os +from pathlib import Path +from unittest.mock import patch, MagicMock +import json + +# Add project root to path for imports +import sys +project_root = Path(__file__).parent.parent +sys.path.insert(0, str(project_root)) + + +class TestIssue165ChatGPTTheme: + """Test ChatGPT theme implementation for compact, interactive reading.""" + + def setup_method(self): + """Set up test environment.""" + # Create temporary directory for test outputs + self.temp_dir = tempfile.mkdtemp() + self.markdown_content = """# AI-Powered Document Processing + +This is a test document for validating the ChatGPT theme implementation. +The theme should provide compact, efficient layout optimized for interactive reading. + +## Key Features + +The ChatGPT style emphasizes: +- Sans-serif fonts for all text (Inter) +- Compact layout with efficient spacing +- High contrast for clarity +- Modern system font stack + +### Design Principles + +1. **Efficiency First**: Compact layout maximizes information density +2. **Clean Typography**: Modern sans-serif fonts for digital readability +3. **High Contrast**: Dark text on light background for clarity +4. **Interactive Feel**: Layout optimized for conversation-like reading + +> "The best chat interfaces feel immediate and conversational." +> — A principle guiding ChatGPT's interface design + +```python +def process_document(text): + # Code blocks should use monospace fonts + return enhanced_text +``` + +This paragraph demonstrates how the theme handles body text with the compact +spacing and modern typography that makes content feel immediate and engaging. +""" + + def teardown_method(self): + """Clean up test environment.""" + # Clean up temporary files + import shutil + shutil.rmtree(self.temp_dir, ignore_errors=True) + + def test_chatgpt_theme_available_in_layered_themes(self): + """Test that chatgpt theme is available in LAYERED_THEMES - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import LAYERED_THEMES + + # ChatGPT theme should be available as a document theme + assert 'chatgpt' in LAYERED_THEMES + chatgpt_theme = LAYERED_THEMES['chatgpt'] + + # Should be scoped as a document theme + assert chatgpt_theme['scope'] == 'document' + + # Should have required typography properties + properties = chatgpt_theme['properties'] + assert 'font_family' in properties + assert 'max_width' in properties + assert 'body_background' in properties + assert 'heading_font_family' in properties + + def test_chatgpt_theme_typography_properties(self): + """Test that chatgpt theme has correct typography properties - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import LAYERED_THEMES + + chatgpt_theme = LAYERED_THEMES['chatgpt'] + properties = chatgpt_theme['properties'] + + # Should use Inter font for body text + assert 'Inter' in properties['font_family'] + + # Should use Inter for headings too (consistent sans-serif) + assert 'Inter' in properties['heading_font_family'] + + # Should have compact max width for chat-like feel + max_width = properties['max_width'] + # Convert to int if it has px suffix + if max_width.endswith('px'): + width_value = int(max_width[:-2]) + else: + width_value = int(max_width) + assert 550 <= width_value <= 620 # Compact range for chat-like reading + + # Should have clean white background + assert properties['body_background'] == '#ffffff' + + # Should have dark text for high contrast + assert properties['body_color'] == '#1f1f1f' + + # Should have compact line height + assert properties['line_height'] == '1.5' + + # Should use ChatGPT's signature green accent + assert properties['accent_color'] == '#10a37f' + + def test_chatgpt_theme_code_styling(self): + """Test ChatGPT theme code block styling - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import LAYERED_THEMES + + chatgpt_theme = LAYERED_THEMES['chatgpt'] + properties = chatgpt_theme['properties'] + + # Should have monospace font for code + assert 'code_font_family' in properties + assert 'SF Mono' in properties['code_font_family'] or 'Monaco' in properties['code_font_family'] + + # Should have light gray code background + assert properties['code_background'] == '#f7f7f7' + assert properties['code_color'] == '#1f1f1f' + + def test_chatgpt_theme_legacy_compatibility(self): + """Test that chatgpt theme works with legacy TEMPLATE_STYLES - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import TEMPLATE_STYLES + + # Should be available in legacy template styles for backward compatibility + assert 'chatgpt' in TEMPLATE_STYLES + chatgpt_legacy = TEMPLATE_STYLES['chatgpt'] + + # Should have required legacy properties + assert 'body_color' in chatgpt_legacy + assert 'font_family' in chatgpt_legacy + assert 'max_width' in chatgpt_legacy + + # Legacy values should match layered theme + assert chatgpt_legacy['body_color'] == '#1f1f1f' + assert 'Inter' in chatgpt_legacy['font_family'] + + def test_chatgpt_theme_legacy_mapping(self): + """Test ChatGPT theme legacy mapping - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import LEGACY_THEME_MAPPING + + # Should have legacy mapping + assert 'chatgpt' in LEGACY_THEME_MAPPING + mapping = LEGACY_THEME_MAPPING['chatgpt'] + + # Should map to light + standard + chatgpt + assert mapping == ['light', 'standard', 'chatgpt'] + + def test_chatgpt_theme_cli_integration(self): + """Test ChatGPT theme through CLI md-render command - Issue #165.""" + input_file = Path(self.temp_dir) / "chatgpt_test.md" + input_file.write_text(self.markdown_content) + + output_file = Path(self.temp_dir) / "chatgpt_test.html" + + from markitect.cli import cli + from click.testing import CliRunner + + runner = CliRunner() + result = runner.invoke(cli, [ + 'md-render', + str(input_file), + '--output', str(output_file), + '--theme', 'chatgpt' + ]) + + assert result.exit_code == 0, f"CLI command failed: {result.output}" + assert output_file.exists() + + html_content = output_file.read_text() + + # Should contain ChatGPT-specific styling + assert 'Inter' in html_content # Font family + assert '#1f1f1f' in html_content # Text color + assert '#10a37f' in html_content # Accent color + + def test_chatgpt_theme_html_generation(self): + """Test HTML generation with ChatGPT theme - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import generate_html_with_embedded_markdown + + test_markdown = "# AI Assistant\n\nThis is a test conversation for the ChatGPT theme." + title = "ChatGPT Theme Test" + + html = generate_html_with_embedded_markdown( + test_markdown, title, "chatgpt", "", {} + ) + + # Should generate valid HTML + assert '' in html + assert title in html + + # Should include ChatGPT styling + assert 'Inter' in html # Font should be present + assert '#1f1f1f' in html # Text color should be present + assert '#10a37f' in html # Accent color should be present + + def test_chatgpt_theme_layered_combination(self): + """Test ChatGPT theme combines properly with other layer themes - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import parse_theme_string, combine_theme_properties + + # Test combining chatgpt document theme with light mode and standard UI + theme_list = parse_theme_string("light,standard,chatgpt") + + assert 'light' in theme_list # Mode theme + assert 'standard' in theme_list # UI theme + assert 'chatgpt' in theme_list # Document theme + + # Should be able to apply layered themes + combined_styles = combine_theme_properties(theme_list) + + # Should include properties from all themes + assert 'body_background' in combined_styles # From light theme + assert 'editor_panel_bg' in combined_styles # From standard UI theme + assert 'font_family' in combined_styles # From chatgpt document theme + + # ChatGPT-specific properties should be present + assert 'Inter' in combined_styles['font_family'] + assert combined_styles['accent_color'] == '#10a37f' + + def test_chatgpt_theme_compact_layout_properties(self): + """Test ChatGPT theme compact layout features - Issue #165.""" + from markitect.plugins.builtin.markdown_commands import LAYERED_THEMES + + chatgpt_theme = LAYERED_THEMES['chatgpt'] + properties = chatgpt_theme['properties'] + + # Should have compact spacing properties + assert 'font_size' in properties + assert properties['font_size'] == '15px' # Standard readable size + + # Should have minimal heading margins + assert 'heading_margin' in properties + assert '1.2em' in properties['heading_margin'] # Compact margins + + # Should have compact paragraph spacing + assert 'paragraph_margin' in properties + assert '1em' in properties['paragraph_margin'] # Tight spacing + + # Should have border radius for modern look + assert 'border_radius' in properties + assert properties['border_radius'] == '8px' \ No newline at end of file