diff --git a/TODO.md b/TODO.md index f624bf05..3441196e 100644 --- a/TODO.md +++ b/TODO.md @@ -12,31 +12,38 @@ The structure organizes **future tasks** by their impact, just as a changelog or This section is for tasks currently being discussed with or worked on by the coding assistant. These are the ephemeral, flow-of-thought tasks. -**๐Ÿงช TESTDRIVE-JSUI CAPABILITY EXTRACTION (2025-11-09) - IN PROGRESS**: -Safely extracting JavaScript UI framework functionality into a dedicated capability while protecting existing hard-won UI functionality and integrating JavaScript tests into the main Python test suite. +**๐Ÿ”ง FIX BROKEN ISSUE-FACADE CAPABILITY (2025-11-10) - COMPLETED โœ…**: +Successfully fixed the issue-facade capability by restructuring the package organization to match pyproject.toml expectations. -**๐Ÿ“‹ Workplan**: [docs/workplan-testdrive-jsui-capability.md](docs/workplan-testdrive-jsui-capability.md) +**๐Ÿ“‹ Actions Completed**: +- [x] Diagnosed package structure mismatch (pyproject.toml expected `issue_tracker` package but code was in flat structure) +- [x] Fixed module import issue by creating `issue_tracker/` package and moving `cli/`, `core/`, `backends/` into it +- [x] Verified capability functionality is restored (CLI commands working, package installs correctly) +- [x] Tested with actual issue access (CLI prompts for backend configuration as expected) -**Current Status**: Implementing Phase 1 - Foundation Setup -- [ ] Create capability directory structure -- [ ] Setup pyproject.toml with dependencies -- [ ] Create package.json with Jest configuration -- [ ] Implement Python-JavaScript bridge -- [ ] Create capability Makefile -- [ ] Write basic README documentation +**๐ŸŽจ IMPLEMENT DOCUMENT STYLING (Issue #166) - COMPLETED โœ…**: +Successfully implemented Substack-style document theme for enhanced long-form reading experience. -**Objectives**: -- ๐Ÿ”’ Zero-risk migration with copy-first approach -- ๐Ÿงช Integrate JavaScript tests into main Python test suite -- ๐Ÿ—๏ธ Clean capability architecture for JavaScript framework -- ๐Ÿ“Š Enhanced CI/CD integration for JavaScript testing -- ๐Ÿš€ Future extensibility for JavaScript framework evolution +**๐Ÿ“‹ Features Implemented**: +- [x] Substack document theme in layered theme system +- [x] Spectral serif font for body text (optimized for long-form reading) +- [x] Lora sans-serif font for headings (clean typography hierarchy) +- [x] Warm cream background (#FAF9F1) for reduced eye strain +- [x] Bronze accent color (#b08d57) for visual hierarchy +- [x] 680px max-width for optimal reading line length +- [x] 1.6 line-height for comfortable reading +- [x] Complete TDD test coverage (6/6 tests passing) +- [x] CLI integration: `markitect md-render --theme substack` +- [x] Layered theme compatibility with existing mode/UI themes -**Safety Mechanisms**: -- Copy-first approach (never move until verified) -- Dual-track testing during migration -- Gradual integration with rollback options -- Comprehensive test verification at each step +**Implementation Details**: +- Added to `LAYERED_THEMES` as document-scope theme +- Extended CSS generation to support `heading_font_family` property +- Maintained backward compatibility with `TEMPLATE_STYLES` +- Full integration with existing theme system architecture + +**๐Ÿงช TESTDRIVE-JSUI CAPABILITY EXTRACTION (2025-11-09) - COMPLETED โœ…**: +Successfully extracted JavaScript UI framework functionality into a dedicated capability with complete automated test integration. The capability now provides 68 JavaScript tests + 11 Python integration tests for comprehensive testing coverage. **๐Ÿ—๏ธ MAJOR ARCHITECTURE REFACTORING (2025-11-03) - COMPLETED โœ…**: Successfully completed comprehensive JavaScript refactoring using Test-Driven Development methodology. diff --git a/cli/__init__.py b/issue_tracker/cli/__init__.py similarity index 100% rename from cli/__init__.py rename to issue_tracker/cli/__init__.py diff --git a/cli/commands/__init__.py b/issue_tracker/cli/commands/__init__.py similarity index 100% rename from cli/commands/__init__.py rename to issue_tracker/cli/commands/__init__.py diff --git a/cli/commands/config.py b/issue_tracker/cli/commands/config.py similarity index 100% rename from cli/commands/config.py rename to issue_tracker/cli/commands/config.py diff --git a/cli/core.py b/issue_tracker/cli/core.py similarity index 100% rename from cli/core.py rename to issue_tracker/cli/core.py diff --git a/cli/presenters/__init__.py b/issue_tracker/cli/presenters/__init__.py similarity index 100% rename from cli/presenters/__init__.py rename to issue_tracker/cli/presenters/__init__.py diff --git a/cli/presenters/config.py b/issue_tracker/cli/presenters/config.py similarity index 100% rename from cli/presenters/config.py rename to issue_tracker/cli/presenters/config.py diff --git a/cli/presenters/formatters.py b/issue_tracker/cli/presenters/formatters.py similarity index 100% rename from cli/presenters/formatters.py rename to issue_tracker/cli/presenters/formatters.py diff --git a/cli/presenters/views.py b/issue_tracker/cli/presenters/views.py similarity index 100% rename from cli/presenters/views.py rename to issue_tracker/cli/presenters/views.py diff --git a/markitect/clean_document_manager.py b/markitect/clean_document_manager.py index 161e4058..1a829030 100644 --- a/markitect/clean_document_manager.py +++ b/markitect/clean_document_manager.py @@ -263,11 +263,16 @@ class CleanDocumentManager: }}""" # Heading styles + heading_font_style = "" + if 'heading_font_family' in props and props['heading_font_family']: + heading_font_style = f"font-family: {props['heading_font_family']};" + heading_css = "" if props['heading_style'] == 'underlined': heading_css = f""" h1, h2, h3, h4, h5, h6 {{ color: {props['heading_color']}; + {heading_font_style} border-bottom: 1px solid {props['border_color']}; padding-bottom: 0.3em; }}""" @@ -275,6 +280,7 @@ class CleanDocumentManager: heading_css = f""" h1, h2, h3, h4, h5, h6 {{ color: {props['heading_color']}; + {heading_font_style} margin-top: 2rem; margin-bottom: 1rem; }} @@ -288,6 +294,7 @@ class CleanDocumentManager: heading_css = f""" h1, h2, h3, h4, h5, h6 {{ color: {props['heading_color']}; + {heading_font_style} }}""" # Text alignment diff --git a/markitect/plugins/builtin/markdown_commands.py b/markitect/plugins/builtin/markdown_commands.py index be73f27d..2481fd6e 100644 --- a/markitect/plugins/builtin/markdown_commands.py +++ b/markitect/plugins/builtin/markdown_commands.py @@ -180,6 +180,27 @@ LAYERED_THEMES = { 'link_hover_color': '#999999' } }, + 'substack': { + 'scope': 'document', + 'properties': { + 'font_family': 'Spectral, Georgia, "Times New Roman", serif', + 'heading_font_family': 'Lora, -apple-system, BlinkMacSystemFont, sans-serif', + 'max_width': '680px', + 'body_background': '#FAF9F1', + 'body_color': '#333333', + 'heading_color': '#333333', + 'text_align': 'left', + 'line_height': '1.6', + 'heading_style': 'simple', + 'accent_color': '#b08d57', + 'link_color': '#b08d57', + 'link_hover_color': '#8b6c42', + 'code_background': '#f5f4ed', + 'code_color': '#333333', + 'blockquote_border': '#b08d57', + 'blockquote_color': '#666666' + } + }, # Branding Themes - Company/personal styling 'corporate': { @@ -205,7 +226,8 @@ LEGACY_THEME_MAPPING = { 'basic': ['light', 'standard', 'basic'], 'github': ['light', 'standard', 'github'], 'dark': ['dark', 'standard', 'basic'], - 'academic': ['light', 'standard', 'academic'] + 'academic': ['light', 'standard', 'academic'], + 'substack': ['light', 'standard', 'substack'] } # Keep TEMPLATE_STYLES for backward compatibility in tests @@ -229,6 +251,11 @@ TEMPLATE_STYLES = { 'body_color': '#333', 'font_family': 'Georgia, Times New Roman, serif', 'max_width': '650px' + }, + 'substack': { + 'body_color': '#333333', + 'font_family': 'Spectral, Georgia, "Times New Roman", serif', + 'max_width': '680px' } } diff --git a/tests/test_issue_166_substack_theme.py b/tests/test_issue_166_substack_theme.py new file mode 100644 index 00000000..20bbd4a5 --- /dev/null +++ b/tests/test_issue_166_substack_theme.py @@ -0,0 +1,183 @@ +""" +Tests for Issue #166: Document theme that mimics substack fonts and fontsizes + +This module tests the implementation of a Substack-style document theme +for enhanced long-form reading experience. +""" + +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 TestIssue166SubstackTheme: + """Test Substack theme implementation for long-form reading.""" + + def setup_method(self): + """Set up test environment.""" + # Create temporary directory for test outputs + self.temp_dir = tempfile.mkdtemp() + self.markdown_content = """# The Mythology of Ireland + +This is a test document for validating the Substack theme implementation. +The theme should provide excellent readability for longer essays online. + +## Typography and Layout + +The Substack style emphasizes: +- Serif fonts for body text (Spectral) +- Sans-serif fonts for headings (Lora) +- Generous line spacing for readability +- Warm color palette with soft cream background + +### Key Design Principles + +1. **Readability First**: Typography optimized for long-form content +2. **Visual Breathing Room**: Generous margins and spacing +3. **Print-Inspired**: Traditional typographic principles +4. **Eye Comfort**: Soft, low-contrast color scheme + +> "The best interfaces are almost invisible to the user. They don't get in the way of the content." +> โ€” A design principle that guides Substack's approach + +This longer paragraph demonstrates how the theme handles extended body text. +It should be comfortable to read with appropriate line height, font size, +and spacing that reduces eye strain during extended reading sessions. +""" + + def teardown_method(self): + """Clean up test environment.""" + # Clean up temporary files + import shutil + shutil.rmtree(self.temp_dir, ignore_errors=True) + + def test_substack_theme_available_in_layered_themes(self): + """Test that substack theme is available in LAYERED_THEMES - Issue #166.""" + from markitect.plugins.builtin.markdown_commands import LAYERED_THEMES + + # Substack theme should be available as a document theme + assert 'substack' in LAYERED_THEMES + substack_theme = LAYERED_THEMES['substack'] + + # Should be scoped as a document theme + assert substack_theme['scope'] == 'document' + + # Should have required typography properties + properties = substack_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_substack_theme_typography_properties(self): + """Test that substack theme has correct typography properties - Issue #166.""" + from markitect.plugins.builtin.markdown_commands import LAYERED_THEMES + + substack_theme = LAYERED_THEMES['substack'] + properties = substack_theme['properties'] + + # Should use serif font for body text (Spectral) + assert 'Spectral' in properties['font_family'] + + # Should use sans-serif for headings (Lora) + assert 'Lora' in properties['heading_font_family'] + + # Should have appropriate max width for readability + 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 600 <= width_value <= 750 # Good range for long-form reading + + # Should have warm cream background + assert properties['body_background'] == '#FAF9F1' + + def test_substack_theme_legacy_compatibility(self): + """Test that substack theme works with legacy TEMPLATE_STYLES - Issue #166.""" + from markitect.plugins.builtin.markdown_commands import TEMPLATE_STYLES + + # Should be available in legacy template styles for backward compatibility + assert 'substack' in TEMPLATE_STYLES + substack_legacy = TEMPLATE_STYLES['substack'] + + # Should have required legacy properties + assert 'body_color' in substack_legacy + assert 'font_family' in substack_legacy + assert 'max_width' in substack_legacy + + def test_substack_theme_cli_integration(self): + """Test Substack theme through CLI md-render command - Issue #166.""" + input_file = Path(self.temp_dir) / "substack_test.md" + input_file.write_text(self.markdown_content) + + output_file = Path(self.temp_dir) / "substack_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', 'substack' + ]) + + assert result.exit_code == 0, f"CLI command failed: {result.output}" + assert output_file.exists() + + html_content = output_file.read_text() + + # Should contain Substack-specific styling + assert 'Spectral' in html_content # Body font + assert 'Lora' in html_content # Heading font + assert '#FAF9F1' in html_content # Background color + + def test_substack_theme_html_generation(self): + """Test HTML generation with Substack theme - Issue #166.""" + from markitect.plugins.builtin.markdown_commands import generate_html_with_embedded_markdown + + test_markdown = "# Test Article\n\nThis is a test paragraph for the Substack theme." + title = "Substack Theme Test" + + html = generate_html_with_embedded_markdown( + test_markdown, title, "substack", "", {} + ) + + # Should generate valid HTML + assert '' in html + assert title in html + + # Should include Substack styling + assert 'Spectral' in html # Body font should be present + assert 'Lora' in html # Heading font should be present + assert '#FAF9F1' in html # Background color should be present + + def test_substack_theme_layered_combination(self): + """Test Substack theme combines properly with other layer themes - Issue #166.""" + from markitect.plugins.builtin.markdown_commands import parse_theme_string, combine_theme_properties + + # Test combining substack document theme with light mode and standard UI + theme_list = parse_theme_string("light,standard,substack") + + assert 'light' in theme_list # Mode theme + assert 'standard' in theme_list # UI theme + assert 'substack' 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 substack document theme \ No newline at end of file