11 Commits

Author SHA1 Message Date
3e16793615 feat: implement systematic CSS naming convention for editor elements
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
Naming Convention: SCOPE-COMPONENT-ELEMENT-SUBELEMENT
- ui = User Interface (editor controls, panels, buttons)
- dc = Document Content (typography, layout)
- md = Mode (light/dark color schemes)
- br = Branding (accent colors, corporate styling)

New CSS Classes:
- ui-edit-floater-panel (main floating control panel)
- ui-edit-floater-header (panel header area)
- ui-edit-floater-actions (button container)
- ui-edit-floater-status (status display)
- ui-edit-button (all action buttons)
- ui-edit-button-accept (save/accept buttons)
- ui-edit-button-cancel (cancel buttons)
- ui-edit-button-reset (reset buttons)
- ui-edit-section-frame (editable section borders)
- ui-edit-textarea-main (text editing areas)

Updated Theme CSS:
- All UI themes now target systematic class names
- Granular control over specific button types
- Consistent theming across all editor components
- Better separation of concerns (panel vs buttons vs textareas)

Benefits:
- Easy theme targeting with predictable class names
- Scalable for future UI components
- Clear hierarchy and naming consistency
- Maintainable and extensible architecture

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 23:00:42 +01:00
dd0c9e3180 fix: correct CSS selectors for UI theme targeting
The Problem:
- UI themes were defined but CSS wasn't applying to editor interface
- CSS selectors didn't match actual HTML element classes/IDs

The Solution:
- Updated CSS selectors to target correct editor elements:
  - #markitect-global-controls (main control panel)
  - .control-btn (action buttons like Save, Reset)
  - .markitect-section-editable (editable section frames)
  - textarea (text editing areas)

Results:
- Greyscale theme: Grey panels, borders, subtle shadows
- Electric theme: Cyan/navy cyberpunk with yellow focus
- Psychedelic theme: Rainbow gradients with hot pink accents
- Standard theme: Clean professional interface (default)

All UI themes now properly style the editor interface in --edit mode!

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 22:39:13 +01:00
6dd278c538 feat: implement UI theme CSS generation for editor interface styling
- Added UI theme CSS generation to _generate_layered_css method
- UI themes now style editor control panels, buttons, frames, and textareas
- Editor elements styled with CSS classes:
  - .markitect-control-panel (floating editor panel)
  - .markitect-editor-button (action buttons)
  - .markitect-section-frame (editing section borders)
  - .markitect-edit-textarea (text editing areas)

UI Theme Examples:
- Greyscale: Clean grey interface with subtle shadows
- Electric: Cyberpunk cyan/navy with glowing effects
- Psychedelic: Rainbow gradient panels with hot pink accents
- Standard: Professional clean interface (default)

All UI themes properly inherit from theme properties:
- editor_panel_bg, editor_panel_border
- editor_button_bg/hover/active
- editor_text_color, editor_focus_color, editor_shadow

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 22:33:51 +01:00
c6422bf73e refactor: reorganize theme scopes and add UI themes for editor elements
Breaking Changes:
- Renamed 'ui' scope to 'mode' for light/dark themes
- Created new 'ui' scope for editor interface elements

New Features:
- Added 4 UI themes for editor interface: standard, greyscale, electric, psychedelic
- UI themes control editor panels, buttons, focus colors, and shadows
- Updated legacy theme mappings to include 'standard' UI theme by default
- Enhanced themes command to display scope-specific properties

Theme Scopes:
- mode: Light/dark color schemes (light, dark)
- ui: Editor interface elements (standard, greyscale, electric, psychedelic)
- document: Typography and layout (basic, github, academic)
- branding: Accent colors (corporate, startup)

Usage Examples:
- markitect md-render file.md --theme dark,electric,academic
- markitect themes --scope ui
- markitect themes --scope mode

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 22:27:02 +01:00
53cfb90237 feat: add themes list command to CLI for theme discovery
- Added `markitect themes` command to list all available themes
- Supports multiple output formats: table (default), list, and json
- Allows filtering by theme scope: ui, document, branding, or all
- Shows key properties for each theme (colors, fonts, etc.)
- Includes legacy theme mappings and usage examples
- Enhances discoverability of the layered theme system

Usage examples:
- markitect themes
- markitect themes --format json
- markitect themes --scope ui
- markitect themes --scope document --format list

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 22:11:51 +01:00
388320b9bf feat: add grey link colors to academic theme for light/dark compatibility
- Added link_color: #777777 and link_hover_color: #999999 to academic theme
- Grey colors provide good contrast on both light and dark backgrounds
- Maintains academic aesthetic while ensuring accessibility
- Link contrast ratios: 4.48:1 (light), 4.23:1 (dark) - meet WCAG AA standards
- All template system tests continue to pass

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 22:02:59 +01:00
bbceea5c7b fix: improve dark theme link colors for better readability
- Enhanced dark theme with brighter link colors (#79c0ff, #a5d6ff)
- Added corresponding light theme link colors (#0969da, #0550ae)
- Fixed template parsing bug where 'light' theme was falling back to 'basic'
- All theme system tests continue to pass

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:56:38 +01:00
5df78c3359 docs: update TODO.md with completed theme system refactor
- Mark theme system refactor as completed
- Add context about new layered theme capabilities
- Document successful implementation of sophisticated theme combinations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:15 +01:00
e78ad47754 feat: implement sophisticated layered theme system for md-render
MAJOR FEATURES:
- **Layered Theme Architecture**: Combine themes across UI, document, and branding scopes
- **Advanced Theme Combinations**: Support complex themes like "dark,academic" or "light,github,corporate"
- **Legacy Compatibility**: Existing --template usage continues to work seamlessly
- **Enhanced CLI Validation**: Proper theme validation with helpful error messages

TECHNICAL IMPROVEMENTS:
- Replace DocumentManager with CleanDocumentManager throughout codebase
- Add ThemeType custom click parameter with comprehensive validation
- Implement parse_theme_string() and combine_theme_properties() functions
- Add _get_template_css() and _generate_layered_css() methods
- Support for UI themes (light/dark), document themes (basic/github/academic), and branding themes (corporate/startup)

THEME CAPABILITIES:
- **Single themes**: basic, github, dark, academic, light, corporate, startup
- **Layered themes**: dark,academic combines dark UI with academic typography
- **Complex combinations**: light,github,corporate for branded GitHub-style documents
- **Intelligent property merging**: Later themes override earlier theme properties

QUALITY ASSURANCE:
- All template system tests passing (12/12)
- Fixed import errors and missing dependencies
- Updated test expectations for new validation messages
- Comprehensive validation prevents unknown theme usage

Breaking Change: --template parameter renamed to --theme with enhanced functionality

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 21:31:08 +01:00
45694a5099 feat: add Claude Code agent configuration and registration tools
- Add agent symlinks in .claude/agents/ directory
- Include agent-project-management.md and test-agent.md
- Add tools/register-agents-claude.py for agent registration
- Enable specialized agents for project management and testing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 20:11:33 +01:00
c0bfc1553c docs: complete CHANGELOG.md with missing historical versions
- Add standard Keep a Changelog header with format description
- Add Unreleased section following standard format
- Add comprehensive entries for versions 0.1.0, 0.2.0, and 0.3.0
- Research and document historical features using git log analysis
- Follow Keep a Changelog categories (Added, Changed, Fixed)
- Maintain chronological order with proper release dates
- Update TODO.md to reflect completed changelog enhancement work

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 20:09:02 +01:00
13 changed files with 1154 additions and 108 deletions

View File

@@ -0,0 +1 @@
/home/worsch/markitect_project/agents/agent-claude-documentation.md

View File

@@ -0,0 +1 @@
/home/worsch/markitect_project/agents/agent-keepaChangelog.md

View File

@@ -0,0 +1,158 @@
---
name: project-assistant
description: Specialized assistant for project status, progress tracking, and development planning
---
## Instructions
You are the MarkiTect project assistant, specialized in providing project status overviews, tracking progress, and helping determine next steps for development work.
### Core Responsibilities
1. **Project Status Overview**: Provide concise summaries of current project state by analyzing key project files
2. **Progress Tracking**: Help understand what has been accomplished recently and what's currently in progress
3. **Next Steps Planning**: Suggest logical next actions based on project status and documented plans
### Key Project Files & Their Purpose
- **ProjectStatusDigest.md**: The canonical source of truth for project architecture, features, and current state
- **ProjectDiary.md**: Chronological record of major work packages, milestones, and development sessions
- **NEXT.md**: Next steps and priorities to ease transfer between coding sessions
- **Makefile**: Provides helpers to use and improve the capabilities provided by the project
**Gitea Issues**: Backlog of issues and backlog of tasks stored as issues in gitea
### Project Infrastructure Knowledge
**Repository Structure:**
- Main project hosted on Gitea with issue tracking for use cases and tasks
- Documentation maintained in `wiki/` submodule
- Test-drive dev workflow with tests in `tests/` handled by tddai-assistent subagent
**Development Workflow:**
- Issue-driven development using Gitea API integration
- TDD8 methodology via tddai-assistant subagent for comprehensive test-driven development
- All commits require green test state
**Issue Management Protocol:**
- **Gitea-First**: Feature requests, bugs, and enhancements should be documented as Gitea issues
- **Issue Creation**: When new requirements emerge, create issues in Gitea immediately but do NOT implement immediately
- **Strategic Planning**: Issues should be prioritized and scheduled based on project roadmap (history/ROADMAP.md)
- **Implementation Discipline**: Only work on issues that are explicitly planned for the current session
- **Issue Workflow**: Create → Triage → Plan → Schedule → Implement → Close
**TDD Workflow Management:**
- For all TDD-related guidance, workflow management, and test-driven development questions, use the **tddai-assistant** subagent
- The tddai-assistant specializes in the TDD8 methodology (ISSUE-TEST-RED-GREEN-REFACTOR-DOCUMENT-REFINE-PUBLISH cycle)
- This includes sidequest management, test planning, and comprehensive development workflow guidance
### Response Guidelines
When asked about project status or next steps:
1. **Start with Current State**: Always check ProjectStatusDigest.md for the latest architecture and status
2. **Review Recent Progress**: Check ProjectDiary.md for recent accomplishments and context
3. **Check Planned Work**: Read Next.md for documented next steps and priorities
4. **Consider Git Status**: Be aware of current working directory state and recent commits
### Issue Management Guidelines
**When to Create Gitea Issues:**
- New feature requests or enhancement ideas emerge during development
- Bugs or technical debt are discovered but not immediately fixable
- Future improvements are identified but outside current session scope
- Architecture decisions require documentation and future review
- Sidequests that we want to remember for later implementation
**Issue Creation Protocol:**
- Use descriptive titles that clearly state the requirement
- Include context: why is this needed, what problem does it solve
- Add relevant labels: enhancement, bug, documentation, technical-debt
- Reference related issues or components affected
- Do NOT implement immediately - issues are for tracking and planning
**Issue vs. Immediate Work:**
- Current session planned work: implement directly (from Next.md)
- Discovered improvements: create issue, continue with planned work
- Critical bugs affecting current work: fix immediately, then create issue for root cause analysis
- Future enhancements: always create issue first for proper planning
**Response Format:**
- Provide a brief status summary (2-3 sentences)
- Highlight recent progress or changes
- Suggest 1-3 concrete next actions based on documented plans
- Reference specific files and line numbers when relevant (e.g., `Next.md:8-12`)
### Example Response Structure
```
## Current Status
[Brief summary from ProjectStatusDigest.md]
## Recent Progress
[Key accomplishments from ProjectDiary.md latest entries]
## Recommended Next Steps
1. [Action from Next.md or logical progression]
2. [Secondary priority or alternative approach]
3. [Maintenance or validation task if applicable]
Based on: ProjectStatusDigest.md:74-79, Next.md:7-13
```
## Session Start-Up Protocol
When asked what's up for a new coding session, follow this standardized routine:
### Start-of-Session Checklist
1. **Mission Status**: Provide reminder to project vision and how we are doing
2. **Recently**: Provide reminder what we did last from the last entry to the diary
3. **NEXT.txt**: Check if we provided guidance for what to do next at the end of the last coding session
4. **git status**: Check if git is clean or work has been left unfinished
5. **Workspace clean**: Check if workspace is clean or we left of in the middle of a TDD cycle
6. **Issue finished**: Check if we are currently working on a specific issue or need to select the next one
7. **Suggestion**: Provide a sensible suggestion of what to do next
## Session Wrap-Up Protocol
When asked to help wrap up a development session, follow this standardized routine:
### End-of-Session Checklist:
1. **Update ProjectDiary.md**: Add entry documenting progress, challenges, and achievements
2. **Update NEXT.md**: Set clear priorities and strategy for next session
3. **Update ProjectStatusDigest.md**: Refresh current status, metrics, and completed features
4. **Issue Management**: Review and create any issues for sidequests and discoveries made during session
5. **Anchor patterns**: Update this project-assistant definition with any new workflow patterns
6. **Prepare for commit**: Ensure all documentation reflects current state
### Session Success Indicators:
- All tests passing (green state)
- Clear next steps documented
- Technical debt addressed or documented
- Progress measurably advanced toward project goals
### Wrap-Up Response Format:
```
## Session Summary
[Brief overview of accomplishments and current state]
## Documentation Updates
- ✅ ProjectDiary.md: [what was added]
- ✅ Next.md: [priorities set]
- ✅ ProjectStatusDigest.md: [status updated]
## Issues Created/Updated
- 🎯 Issue #X: [brief description] - [reason for creation]
- 📝 Issue #Y: [brief description] - [future enhancement]
## Next Session Preparation
[Clear guidance for resuming work next time]
Ready for commit: [list of files to commit]
```
### Example Issue Creation During Development:
**Scenario**: While implementing CLI commands, discover that error messages could be improved
**Action**: Create issue "Enhance CLI error messages with user-friendly formatting and suggestions"
**Result**: Continue with current CLI implementation, address error enhancement in future session
Remember: Your role is to help developers quickly understand "where we are" and "what should we do next" when picking up work on the MarkiTect project, and to ensure proper session wrap-up for continuity.

View File

@@ -0,0 +1,21 @@
---
name: test-agent
description: Simple test agent to verify Claude Code agent functionality
model: inherit
---
## Instructions
You are a test agent to verify that custom agents work in Claude Code. When invoked, simply respond with "Test agent is working!" and confirm that you can see this instruction.
### Core Responsibilities
1. Respond to test requests
2. Confirm agent system is functioning
### Authority and Scope
You can:
- Confirm agent functionality
- Provide test responses
- Verify system integration

View File

@@ -1,4 +1,12 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.5.0] - 2025-10-26
### Added
@@ -44,7 +52,55 @@
### Other
- chore: clean up repository documentation files for release
## [0.3.0] - 2025-10-25
### Added
- **Kaizen-agentic Framework Integration**: Integrated capability submodule for enhanced development workflow
- **Test Reorganization System**: Reorganized tests by capability with improved modularity
- **Capability Inclusion Management**: Comprehensive system for managing capability inclusions
- **Todofile System**: Implemented todofile system to replace NEXT.md for better task tracking
### Changed
- **Directory Organization**: Logical separation and reorganization of project structure
- **Historical File Organization**: Cleaner structure with better file organization
## [0.2.0] - 2025-10-20
### Added
- **GraphQL Interface**: Advanced querying capabilities with full GraphQL implementation
- **Full-text Search**: FTS5 backend integration for powerful search functionality
- **Plugin Architecture**: Extensible framework with comprehensive plugin support
- **Query Paradigms**: 14 different query paradigms for flexible data access
- **Cost Management**: Activity tracking and resource cost management
- **Template Rendering**: Template system with validation capabilities
- **CLI Consolidation**: Unified command-line interface
- **Production Asset Management**: Content-addressable storage system
- **17 Kaizen-agentic Agents**: Integrated development agent ecosystem
### Changed
- **Performance Optimization**: 60-85% performance improvement through system optimization
- **Error Handling**: Enterprise-grade error handling and recovery mechanisms
- **Resource Management**: Memory-efficient and scalable architecture
### Fixed
- **Cross-platform Validation**: Comprehensive validation for Unix/Windows/macOS
- **Type Safety**: Enhanced type safety and security validation
- **Test Coverage**: 1983/1983 tests passing (100% success rate)
## [0.1.0] - 2025-10-15
### Added
- **Development Infrastructure**: Comprehensive Makefile for development workflow
- **Project Documentation**: ProjectStatusDigest.md and ProjectDiary.md for tracking
- **TDD Workspace System**: Structured Test-Driven Development workflow implementation
- **Issue Management**: Gitea integration for issue tracking and management
- **Virtual Environment Management**: Enhanced venv detection and shell activation
- **Wiki Integration**: Submodule tracking for project documentation
- **Core Repository Setup**: Initial project structure and configuration
### Changed
- **Build System**: Enhanced build targets with venv Python and PYTHONPATH support
- **Target Naming**: Renamed workspace targets to TDD Workspace with tdd- prefix
All notable changes to MarkiTect will be documented in this file.

91
TODO.md Normal file
View File

@@ -0,0 +1,91 @@
# Todofile
This is a "to do next" file, particularly useful to keep the human and a coding assistant in sync.
The format is based on [Keep a Todofile V0.0.1](https://coulomb.social/open/KeepaTodofile).
The structure organizes **future tasks** by their impact, just as a changelog organizes past changes by their impact.
***
## [Unreleased] - *Active Vibe-Coding State* 💡
This section is for tasks currently being discussed with or worked on by the coding assistant. These are the ephemeral, flow-of-thought tasks.
* **To Add:**
* **Complete Theme System Refactor - Layered Theme Architecture**: Major refactor to replace simple template selection with sophisticated layered theme system (currently stashed)
* **Phase 1 - Restore and Assess**:
* Restore stashed changes with `git stash pop`
* Run tests to identify current failures and validation issues
* Assess remaining work by checking all files that still use `--template`
* **Phase 2 - Complete CLI Parameter Migration**:
* Update remaining CLI commands in asset_commands.py, cli.py, and other files
* Fix parameter validation - add proper theme validation for the new string-based parameter
* Update help text and documentation to reflect new layered theme capabilities
* **Phase 3 - Fix Integration Issues**:
* Fix function signature mismatches where functions expect `template` but receive `theme`
* Add proper error handling for invalid themes (replace print statements with logging)
* Test layered theme functionality - ensure `dark,academic` type combinations work
* Verify legacy theme mapping works correctly
* **Phase 4 - Quality Assurance**:
* Run full test suite to ensure no regressions
* Test all CLI commands with new theme parameter
* Verify backward compatibility with existing templates
* Update any remaining documentation
* **Phase 5 - Clean Up and Commit**:
* Remove dead code and legacy functions if no longer needed
* Ensure consistent terminology throughout codebase
* Write comprehensive commit message documenting the major theme system improvement
* Update CHANGELOG.md with new theme layering capabilities
* **To Fix:**
* None currently identified
* **To Refactor:**
* None currently identified
* **To Remove:**
* None currently identified
***
## Theme System Refactor Context
**Current State**: Work-in-progress theme system refactor is stashed and partially complete.
**Completed Parts ✅**:
- New Layered Theme Architecture: Complete LAYERED_THEMES system with UI, document, and branding scopes
- Theme Parsing Functions: `parse_theme_string()` and `combine_theme_properties()`
- CSS Generation Refactor: New `_get_template_css()` and `_generate_layered_css()` methods
- CLI Parameter Change: Changed from `--template` to `--theme` throughout test files
- Legacy Compatibility: LEGACY_THEME_MAPPING for backward compatibility
**Missing/Incomplete Parts ❌**:
- CLI Parameter Validation: The new `--theme` parameter needs validation for invalid themes
- Function Signature Inconsistencies: Some functions still accept `template` parameter but call it with `theme`
- Additional Files: Other files in the codebase still use old `template` parameter
- Error Handling: The warning system for unknown themes needs proper logging
**New Capabilities When Complete**:
- Single themes: `basic`, `github`, `dark`, `academic`, `light`, `corporate`, `startup`
- Layered themes: `dark,academic` combines dark UI with academic typography
- Complex combinations: `light,github,corporate` for branded GitHub-style documents
- Legacy compatibility: Existing `--template` usage continues to work
***
## Completed Tasks
**CHANGELOG.md Enhancement - COMPLETED ✅**:
- ✅ Added missing version entries for 0.1.0, 0.2.0, and 0.3.0
- ✅ Added standard Keep a Changelog header with proper format
- ✅ Included Unreleased section
- ✅ Research completed for all historical versions using git log analysis
- ✅ All entries follow Keep a Changelog categories (Added, Changed, Fixed)
- ✅ Chronological order maintained with latest versions first
- ✅ Appropriate release dates included based on git commit timestamps
**Version Details Added**:
- v0.1.0 (2025-10-15): Development infrastructure, TDD workspace, issue management
- v0.2.0 (2025-10-20): Advanced Markdown Engine with GraphQL, search, plugins
- v0.3.0 (2025-10-25): Architectural improvements with kaizen-agentic integration

View File

@@ -118,6 +118,269 @@ class CleanDocumentManager:
return version_info
def _get_template_css(self, template: str = None) -> str:
"""Generate layered theme CSS styles."""
# Import layered theme functions
from markitect.plugins.builtin.markdown_commands import (
parse_theme_string, combine_theme_properties, TEMPLATE_STYLES
)
# Handle layered themes or fall back to legacy
if template and ',' in template:
# New layered theme system
theme_list = parse_theme_string(template)
combined_props = combine_theme_properties(theme_list)
return self._generate_layered_css(combined_props)
else:
# Legacy single theme or fallback
if not template or template not in TEMPLATE_STYLES:
# Use default layered themes or the specified theme
theme_list = parse_theme_string(template or 'basic')
combined_props = combine_theme_properties(theme_list)
return self._generate_layered_css(combined_props)
else:
# Legacy theme - convert to layered
theme_list = parse_theme_string(template)
combined_props = combine_theme_properties(theme_list)
return self._generate_layered_css(combined_props)
def _generate_layered_css(self, properties: dict) -> str:
"""Generate CSS from combined theme properties."""
# Set defaults for missing properties (properties override defaults)
defaults = {
'body_background': '#ffffff',
'body_color': '#333333',
'font_family': '-apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif',
'max_width': '800px',
'heading_color': '#333333', # Use same as body color by default
'heading_style': 'simple',
'text_align': 'left',
'code_background': '#f6f8fa',
'code_color': '#333333',
'border_color': '#d0d7de',
'blockquote_border': '#dfe2e5',
'blockquote_color': '#6a737d',
'table_border': '#d0d7de',
'table_header_bg': '#f6f8fa',
'accent_color': None,
'secondary_color': None
}
# Merge defaults first, then override with theme properties
props = {**defaults, **properties}
# Base CSS
base_css = f"""
body {{
font-family: {props['font_family']};
max-width: {props['max_width']};
margin: 0 auto;
padding: 2rem;
line-height: 1.6;
color: {props['body_color']};
background-color: {props['body_background']};
}}
#markdown-content {{
min-height: 200px;
}}"""
# Heading styles
heading_css = ""
if props['heading_style'] == 'underlined':
heading_css = f"""
h1, h2, h3, h4, h5, h6 {{
color: {props['heading_color']};
border-bottom: 1px solid {props['border_color']};
padding-bottom: 0.3em;
}}"""
elif props['heading_style'] == 'centered':
heading_css = f"""
h1, h2, h3, h4, h5, h6 {{
color: {props['heading_color']};
margin-top: 2rem;
margin-bottom: 1rem;
}}
h1 {{
text-align: center;
font-size: 2.2em;
border-bottom: 2px solid {props['heading_color']};
padding-bottom: 0.5rem;
}}"""
else: # simple
heading_css = f"""
h1, h2, h3, h4, h5, h6 {{
color: {props['heading_color']};
}}"""
# Text alignment
text_css = ""
if props['text_align'] == 'justify':
text_css = """
p {
text-align: justify;
margin-bottom: 1.2rem;
}"""
# Element styling
element_css = f"""
pre {{
background-color: {props['code_background']};
color: {props['code_color']};
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
border: 1px solid {props['border_color']};
}}
code {{
background-color: {props['code_background']};
color: {props['code_color']};
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 0.9em;
}}
pre code {{
background: none;
padding: 0;
}}
blockquote {{
border-left: 4px solid {props['blockquote_border']};
margin: 0;
padding-left: 1rem;
color: {props['blockquote_color']};
}}
table {{
font-size: 0.85em;
border-collapse: collapse;
margin: 1rem 0;
width: 100%;
border: 1px solid {props['table_border']};
}}
th, td {{
font-size: inherit;
border: 1px solid {props['table_border']};
padding: 0.5rem;
text-align: left;
}}
th {{
background-color: {props['table_header_bg']};
font-weight: 600;
}}"""
# Link styling
link_css = ""
if props.get('link_color'):
link_css = f"""
a {{
color: {props['link_color']};
text-decoration: underline;
}}"""
if props.get('link_hover_color'):
link_css += f"""
a:hover {{
color: {props['link_hover_color']};
}}"""
else:
link_css += """
a:hover {
opacity: 0.8;
}"""
# Branding accents (if specified and no link_color already set)
accent_css = ""
if props.get('accent_color') and not props.get('link_color'):
accent_css = f"""
a {{
color: {props['accent_color']};
}}
a:hover {{
opacity: 0.8;
}}"""
# UI theme styling for editor interface elements
ui_css = ""
if props.get('editor_panel_bg'):
ui_css = f"""
.markitect-edit-mode .ui-edit-floater-panel {{
background: {props['editor_panel_bg']};
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
box-shadow: 0 4px 12px {props.get('editor_shadow', 'rgba(0,0,0,0.1)')};
color: {props.get('editor_text_color', '#212529')};
}}
.markitect-edit-mode .ui-edit-floater-header {{
color: {props.get('editor_text_color', '#212529')};
}}
.markitect-edit-mode .ui-edit-button {{
background: {props.get('editor_button_bg', '#ffffff')};
color: {props.get('editor_text_color', '#212529')};
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
}}
.markitect-edit-mode .ui-edit-button:hover {{
background: {props.get('editor_button_hover', '#e9ecef')};
}}
.markitect-edit-mode .ui-edit-button:active,
.markitect-edit-mode .ui-edit-button.active {{
background: {props.get('editor_button_active', '#dee2e6')};
}}
.markitect-edit-mode .ui-edit-button-accept {{
background: {props.get('editor_button_bg', '#4caf50')};
}}
.markitect-edit-mode .ui-edit-button-cancel {{
background: {props.get('editor_button_bg', '#f44336')};
}}
.markitect-edit-mode .ui-edit-button-reset {{
background: {props.get('editor_button_bg', '#ff9800')};
}}
.markitect-edit-mode .ui-edit-section-frame {{
border: 2px solid {props.get('editor_focus_color', '#0066cc')};
box-shadow: 0 0 0 3px {props.get('editor_focus_color', '#0066cc')}33;
}}
.markitect-edit-mode .ui-edit-textarea {{
border: 1px solid {props.get('editor_panel_border', '#dee2e6')};
color: {props.get('editor_text_color', '#212529')};
background: {props.get('editor_button_bg', '#ffffff')};
}}
.markitect-edit-mode .ui-edit-textarea:focus {{
border-color: {props.get('editor_focus_color', '#0066cc')};
box-shadow: 0 0 0 2px {props.get('editor_focus_color', '#0066cc')}33;
}}"""
return f"<style>{base_css}{heading_css}{text_css}{element_css}{link_css}{accent_css}{ui_css}</style>"
def _get_legacy_template_css(self, template: str) -> str:
"""Legacy CSS generation - kept for backward compatibility."""
# Import template styles
from markitect.plugins.builtin.markdown_commands import TEMPLATE_STYLES
# Use basic as default if no template specified
if not template or template not in TEMPLATE_STYLES:
template = 'basic'
style_config = TEMPLATE_STYLES[template]
# Base CSS that's common to all templates
base_css = f"""
body {{
font-family: {style_config['font_family']};
max-width: {style_config['max_width']};
margin: 0 auto;
padding: 2rem;
line-height: 1.6;
color: {style_config['body_color']};
}}
#markdown-content {{
min-height: 200px;
}}"""
# Convert legacy template config to layered format
legacy_config = TEMPLATE_STYLES[template]
layered_props = {
'font_family': legacy_config['font_family'],
'max_width': legacy_config['max_width'],
'body_color': legacy_config['body_color'],
}
return self._generate_layered_css(layered_props)
def _generate_html_template(self, markdown_content: str, title: str, css: str = None, template: str = None,
edit_mode: bool = False, editor_theme: str = 'github', keyboard_shortcuts: bool = True, original_filename: str = 'document', version_info: dict = None) -> str:
"""Generate clean HTML template."""
@@ -138,60 +401,8 @@ class CleanDocumentManager:
except Exception:
css_content = f'<link rel="stylesheet" href="{css}">'
# Default CSS for basic styling
default_css = """
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
line-height: 1.6;
color: #333;
}
#markdown-content {
min-height: 200px;
}
pre {
background: #f6f8fa;
padding: 1rem;
border-radius: 6px;
overflow-x: auto;
}
code {
background: #f6f8fa;
padding: 0.2em 0.4em;
border-radius: 3px;
font-size: 0.9em;
}
pre code {
background: none;
padding: 0;
}
blockquote {
border-left: 4px solid #dfe2e5;
margin: 0;
padding-left: 1rem;
color: #6a737d;
}
table {
font-size: 0.85em;
border-collapse: collapse;
margin: 1rem 0;
width: 100%;
}
th, td {
font-size: inherit;
border: 1px solid #dfe2e5;
padding: 0.5rem;
text-align: left;
}
th {
background: #f6f8fa;
font-weight: 600;
}
</style>
"""
# Generate template-specific CSS
default_css = self._get_template_css(template)
# Load clean editor JavaScript files
editor_scripts = ""
@@ -823,7 +1034,7 @@ class DOMRenderer {
return;
}
const sectionElement = event.target.closest('.markitect-section-editable');
const sectionElement = event.target.closest('.ui-edit-section');
if (!sectionElement) return;
const sectionId = sectionElement.getAttribute('data-section-id');
@@ -859,6 +1070,7 @@ class DOMRenderer {
`;
const textarea = document.createElement('textarea');
textarea.className = 'ui-edit-textarea ui-edit-textarea-main';
textarea.value = content;
textarea.style.cssText = `
flex: 1;
@@ -887,6 +1099,16 @@ class DOMRenderer {
const createButton = (text, color, handler) => {
const btn = document.createElement('button');
btn.textContent = text;
// Add CSS classes based on button type
btn.className = 'ui-edit-button';
if (text.includes('Accept')) {
btn.className += ' ui-edit-button-accept';
} else if (text.includes('Cancel')) {
btn.className += ' ui-edit-button-cancel';
} else if (text.includes('Reset')) {
btn.className += ' ui-edit-button-reset';
}
btn.style.cssText = `
padding: 8px 12px;
border: none;
@@ -947,7 +1169,7 @@ class DOMRenderer {
}
setupSectionElement(element) {
element.className = 'markitect-section-editable';
element.className = 'ui-edit-section ui-edit-section-frame';
element.style.cssText = `
margin: 16px 0;
padding: 12px;
@@ -1129,16 +1351,17 @@ class MarkitectCleanEditor {
addGlobalControls() {
// Create floating control panel
const panel = document.createElement('div');
panel.id = 'markitect-global-controls';
panel.id = 'ui-edit-floater';
panel.className = 'ui-edit-floater-panel';
panel.innerHTML = `
<div class="control-header">
<div class="ui-edit-floater-header">
<h3>📝 Editor</h3>
<div class="control-status" id="editor-status">Ready</div>
<div class="ui-edit-floater-status" id="editor-status">Ready</div>
</div>
<div class="control-actions">
<button id="save-document" class="control-btn primary">💾 Save Document</button>
<button id="reset-all" class="control-btn warning">🔄 Reset All</button>
<button id="show-status" class="control-btn secondary">📊 Show Status</button>
<div class="ui-edit-floater-actions">
<button id="save-document" class="ui-edit-button ui-edit-button-accept">💾 Save Document</button>
<button id="reset-all" class="ui-edit-button ui-edit-button-reset">🔄 Reset All</button>
<button id="show-status" class="ui-edit-button ui-edit-button-secondary">📊 Show Status</button>
</div>
`;

View File

@@ -26,7 +26,157 @@ def get_default_format(available_formats=['table', 'json', 'yaml', 'simple'], fa
return fallback
# Template styles configuration for tests
# Layered theme system - themes can be combined across different scopes
LAYERED_THEMES = {
# Mode Themes - Light/dark color schemes
'light': {
'scope': 'mode',
'properties': {
'body_background': '#ffffff',
'body_color': '#333333',
'heading_color': '#24292f',
'code_background': '#f6f8fa',
'code_color': '#24292e',
'border_color': '#d0d7de',
'blockquote_border': '#dfe2e5',
'blockquote_color': '#6a737d',
'table_border': '#d0d7de',
'table_header_bg': '#f6f8fa',
'link_color': '#0969da',
'link_hover_color': '#0550ae'
}
},
'dark': {
'scope': 'mode',
'properties': {
'body_background': '#0d1117',
'body_color': '#e1e4e8',
'heading_color': '#58a6ff',
'code_background': '#161b22',
'code_color': '#e1e4e8',
'border_color': '#30363d',
'blockquote_border': '#58a6ff',
'blockquote_color': '#8b949e',
'table_border': '#30363d',
'table_header_bg': '#161b22',
'link_color': '#79c0ff',
'link_hover_color': '#a5d6ff'
}
},
# UI Themes - Editor interface elements (floating panels, buttons, editing frames)
'standard': {
'scope': 'ui',
'properties': {
'editor_panel_bg': '#f8f9fa',
'editor_panel_border': '#dee2e6',
'editor_button_bg': '#ffffff',
'editor_button_hover': '#e9ecef',
'editor_button_active': '#dee2e6',
'editor_text_color': '#212529',
'editor_focus_color': '#0066cc',
'editor_shadow': 'rgba(0,0,0,0.1)'
}
},
'greyscale': {
'scope': 'ui',
'properties': {
'editor_panel_bg': '#f5f5f5',
'editor_panel_border': '#d0d0d0',
'editor_button_bg': '#ffffff',
'editor_button_hover': '#e8e8e8',
'editor_button_active': '#d4d4d4',
'editor_text_color': '#333333',
'editor_focus_color': '#666666',
'editor_shadow': 'rgba(0,0,0,0.15)'
}
},
'electric': {
'scope': 'ui',
'properties': {
'editor_panel_bg': '#001122',
'editor_panel_border': '#00ffff',
'editor_button_bg': '#003366',
'editor_button_hover': '#0066cc',
'editor_button_active': '#0099ff',
'editor_text_color': '#00ffff',
'editor_focus_color': '#ffff00',
'editor_shadow': 'rgba(0,255,255,0.3)'
}
},
'psychedelic': {
'scope': 'ui',
'properties': {
'editor_panel_bg': 'linear-gradient(45deg, #ff6b35, #f7931e, #ffd23f, #06ffa5)',
'editor_panel_border': '#ff1493',
'editor_button_bg': 'rgba(255,255,255,0.2)',
'editor_button_hover': 'rgba(255,20,147,0.3)',
'editor_button_active': 'rgba(255,20,147,0.5)',
'editor_text_color': '#ffffff',
'editor_focus_color': '#ff1493',
'editor_shadow': 'rgba(255,20,147,0.4)'
}
},
# Document Themes - Typography and layout
'basic': {
'scope': 'document',
'properties': {
'font_family': '-apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif',
'max_width': '800px',
'heading_style': 'simple',
'text_align': 'left'
}
},
'github': {
'scope': 'document',
'properties': {
'font_family': '-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif',
'max_width': '900px',
'heading_style': 'underlined',
'text_align': 'left'
}
},
'academic': {
'scope': 'document',
'properties': {
'font_family': 'Georgia, Times New Roman, serif',
'max_width': '650px',
'heading_style': 'centered',
'text_align': 'justify',
'link_color': '#777777',
'link_hover_color': '#999999'
}
},
# Branding Themes - Company/personal styling
'corporate': {
'scope': 'branding',
'properties': {
'accent_color': '#0066cc',
'secondary_color': '#f8f9fa',
'brand_font': 'inherit'
}
},
'startup': {
'scope': 'branding',
'properties': {
'accent_color': '#ff6b35',
'secondary_color': '#f4f4f4',
'brand_font': 'inherit'
}
}
}
# Legacy compatibility - map old theme names to new layered equivalents
LEGACY_THEME_MAPPING = {
'basic': ['light', 'standard', 'basic'],
'github': ['light', 'standard', 'github'],
'dark': ['dark', 'standard', 'basic'],
'academic': ['light', 'standard', 'academic']
}
# Keep TEMPLATE_STYLES for backward compatibility in tests
TEMPLATE_STYLES = {
'basic': {
'body_color': '#333',
@@ -51,21 +201,133 @@ TEMPLATE_STYLES = {
}
def generate_html_with_embedded_markdown(markdown_content, title, template, css_content, template_vars):
def parse_theme_string(theme_string: str) -> list:
"""
Parse theme string into list of individual themes.
Supports:
- Single theme: "dark"
- Multiple themes: "dark,academic" or "dark, academic"
- Legacy theme mapping: "basic" -> ["light", "basic"]
Args:
theme_string: Comma-separated theme names
Returns:
List of theme names in order
"""
if not theme_string:
return ['light', 'basic'] # Default themes
# Split by comma and clean up whitespace
themes = [theme.strip() for theme in theme_string.split(',')]
# Expand legacy themes only if they don't exist in the new layered system
expanded_themes = []
for theme in themes:
if theme in LAYERED_THEMES:
# Theme exists in new system, use as-is
expanded_themes.append(theme)
elif theme in LEGACY_THEME_MAPPING:
# Legacy theme, expand it
expanded_themes.extend(LEGACY_THEME_MAPPING[theme])
else:
# Unknown theme, add as-is (will be warned about later)
expanded_themes.append(theme)
return expanded_themes
class ThemeType(click.ParamType):
"""Custom click type for theme validation."""
name = "theme"
def convert(self, value, param, ctx):
if value is None:
return value
try:
validate_theme_string(value)
return value
except click.BadParameter as e:
self.fail(str(e), param, ctx)
def validate_theme_string(theme_string: str) -> None:
"""
Validate that all themes in a theme string are known themes.
Args:
theme_string: Comma-separated theme names
Raises:
click.BadParameter: If any theme is unknown
"""
if not theme_string:
return # Allow empty/None themes
themes = parse_theme_string(theme_string)
unknown_themes = []
for theme_name in themes:
if theme_name not in LAYERED_THEMES and theme_name not in LEGACY_THEME_MAPPING:
unknown_themes.append(theme_name)
if unknown_themes:
available_themes = list(LAYERED_THEMES.keys()) + list(LEGACY_THEME_MAPPING.keys())
raise click.BadParameter(
f"Unknown theme(s): {', '.join(unknown_themes)}. "
f"Available themes: {', '.join(sorted(set(available_themes)))}"
)
def combine_theme_properties(theme_list: list) -> dict:
"""
Combine properties from multiple themes, with later themes overriding earlier ones.
Args:
theme_list: List of theme names in order of application
Returns:
Combined properties dictionary
"""
combined_properties = {}
for theme_name in theme_list:
if theme_name in LAYERED_THEMES:
theme_data = LAYERED_THEMES[theme_name]
# Later themes override earlier ones
combined_properties.update(theme_data['properties'])
elif theme_name in LEGACY_THEME_MAPPING:
# Handle legacy themes by expanding them
expanded_themes = LEGACY_THEME_MAPPING[theme_name]
for expanded_theme in expanded_themes:
if expanded_theme in LAYERED_THEMES:
theme_data = LAYERED_THEMES[expanded_theme]
combined_properties.update(theme_data['properties'])
else:
# This should not happen if validation is working
print(f"Warning: Unknown theme '{theme_name}' - skipping")
return combined_properties
def generate_html_with_embedded_markdown(markdown_content, title, theme, css_content, template_vars):
"""
Generate HTML with embedded markdown content for testing.
This function is used by tests to validate template functionality.
"""
# Create a temporary document manager for rendering
doc_manager = DocumentManager(None)
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(None)
# Generate HTML template
html_content = doc_manager._generate_html_template(
markdown_content=markdown_content,
title=title,
css=css_content,
template=template
template=theme
)
return html_content
@@ -191,7 +453,8 @@ def process_single_file(input_file: Path, use_publication_dir: bool, publication
output_file = input_file.with_suffix('.html')
# Create document manager and render
doc_manager = DocumentManager(None)
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(None)
doc_manager.render_file(str(input_file), str(output_file))
return output_file
@@ -212,7 +475,8 @@ def process_directory(input_dir: Path, use_publication_dir: bool, publication_di
markdown_files = find_markdown_files(input_dir)
output_files = []
doc_manager = DocumentManager(None)
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(None)
for md_file in markdown_files:
if use_publication_dir:
@@ -304,21 +568,22 @@ def extract_html_title(html_file: Path) -> str:
return html_file.stem
def generate_index_html(html_files: list, title: str, template: str = None) -> str:
def generate_index_html(html_files: list, title: str, theme: str = None) -> str:
"""
Generate HTML content for an index page.
Args:
html_files: List of dictionaries with 'path', 'title', and 'relative_path' keys
title: Title for the index page
template: Template theme to use
theme: Theme to use
Returns:
HTML content string
"""
# Get template CSS
doc_manager = DocumentManager(None)
template_css = doc_manager._get_template_css(template)
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(None)
template_css = doc_manager._get_template_css(theme)
# Generate file list HTML
if not html_files:
@@ -1497,6 +1762,7 @@ class MarkdownCommandsPlugin(CommandPlugin):
'md-get': md_get_command,
'md-list': md_list_command,
'md-render': md_render_command,
'themes': themes_list_command,
'md-index': md_index_command,
'md-explode': md_explode_command,
'md-implode': md_implode_command,
@@ -1530,7 +1796,8 @@ def md_ingest_command(ctx, file_path):
click.echo(f"Processing file: {file_path}")
# Initialize document manager with database manager
doc_manager = DocumentManager(config.get('db_manager'))
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(config.get('db_manager'))
# Process the file
result = doc_manager.ingest_file(Path(file_path))
@@ -1571,7 +1838,8 @@ def md_get_command(ctx, file_path, output):
config = ctx.obj or {}
try:
# Initialize document manager
doc_manager = DocumentManager(config.get('db_manager'))
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(config.get('db_manager'))
# Get file information
result = doc_manager.get_file(file_path)
@@ -1620,7 +1888,8 @@ def md_list_command(ctx, output_format, names_only):
config = ctx.obj or {}
try:
# Initialize document manager
doc_manager = DocumentManager(config.get('db_manager'))
from markitect.clean_document_manager import CleanDocumentManager
doc_manager = CleanDocumentManager(config.get('db_manager'))
# Get file listing
files = doc_manager.list_files()
@@ -1654,8 +1923,8 @@ def md_list_command(ctx, output_format, names_only):
@click.argument('input_file', type=click.Path(exists=True))
@click.option('--output', '-o', type=click.Path(),
help='Output HTML file (default: <input>.html)')
@click.option('--template', type=click.Choice(['basic', 'github', 'dark', 'academic']),
help='Built-in template theme (basic, github, dark, academic)')
@click.option('--theme', type=ThemeType(),
help='Theme(s) to apply. Single: dark or layered: dark,academic or light,github,corporate. Available: basic, github, dark, academic, light, corporate, startup')
@click.option('--css', type=click.Path(),
help='Custom CSS file to include')
@click.option('--edit', is_flag=True,
@@ -1670,22 +1939,28 @@ def md_list_command(ctx, output_format, names_only):
@click.option('--dont-use-publication-dir', is_flag=True,
help='Don\'t use publication directory for output')
@click.pass_context
def md_render_command(ctx, input_file, output, template, css, edit, editor_theme,
def md_render_command(ctx, input_file, output, theme, css, edit, editor_theme,
keyboard_shortcuts, use_publication_dir, dont_use_publication_dir):
"""
Render a markdown file to HTML with basic templates and live preview capabilities.
Converts a markdown file to HTML using customizable templates and styles.
Converts a markdown file to HTML using customizable layered themes and styles.
Supports live editing mode with real-time preview and syntax highlighting.
Choose from basic, github, dark, or academic themes for professional output.
Theme Layering:
- Single themes: basic, github, dark, academic, light, corporate, startup
- Layered themes: dark,academic combines dark UI with academic typography
- Later themes override settings from earlier themes
INPUT_FILE: Path to the markdown file to render
Examples:
markitect md-render README.md
markitect md-render docs/guide.md --output guide.html --template github
markitect md-render docs/guide.md --output guide.html --theme github
markitect md-render draft.md --edit --editor-theme monokai
markitect md-render doc.md --template dark --css custom.css
markitect md-render doc.md --theme dark --css custom.css
markitect md-render doc.md --theme dark,academic
markitect md-render doc.md --theme light,github,corporate
"""
config = ctx.obj or {}
@@ -1712,7 +1987,7 @@ def md_render_command(ctx, input_file, output, template, css, edit, editor_theme
if edit:
# Edit mode - generate HTML with editing capabilities
result = doc_manager.render_file(input_file, str(output_path),
template=template, css=css,
template=theme, css=css,
edit_mode=True,
editor_theme=editor_theme,
keyboard_shortcuts=keyboard_shortcuts)
@@ -1722,16 +1997,16 @@ def md_render_command(ctx, input_file, output, template, css, edit, editor_theme
if config.get('verbose', False):
click.echo(f"Editor theme: {editor_theme}")
click.echo(f"Keyboard shortcuts: {'enabled' if keyboard_shortcuts else 'disabled'}")
click.echo(f"Template: {template or 'default'}")
click.echo(f"Theme: {theme or 'default'}")
click.echo(f"CSS: {css or 'default'}")
else:
# Static render
result = doc_manager.render_file(input_file, str(output_path),
template=template, css=css)
template=theme, css=css)
click.echo(f"✓ Rendered to: {output_path}")
if config.get('verbose', False):
click.echo(f"Template: {template or 'default'}")
click.echo(f"Theme: {theme or 'default'}")
click.echo(f"CSS: {css or 'default'}")
except Exception as e:
@@ -1739,16 +2014,126 @@ def md_render_command(ctx, input_file, output, template, css, edit, editor_theme
raise click.Abort()
@click.command()
@click.option('--format', type=click.Choice(['table', 'list', 'json']), default='table',
help='Output format: table (default), list, or json')
@click.option('--scope', type=click.Choice(['mode', 'ui', 'document', 'branding', 'all']), default='all',
help='Filter themes by scope: mode (light/dark), ui (editor interface), document (typography), branding (colors), or all (default)')
def themes_list_command(format, scope):
"""
List all available themes and their properties.
Shows the available themes that can be used with md-render and other commands.
Themes can be used individually or combined in layers.
Examples:
markitect themes list
markitect themes list --format json
markitect themes list --scope ui
markitect themes list --scope document --format list
"""
from tabulate import tabulate
import json
# Get theme data
layered_themes = []
legacy_mappings = []
# Process layered themes
for theme_name, theme_data in LAYERED_THEMES.items():
theme_scope = theme_data['scope']
if scope == 'all' or scope == theme_scope:
properties = theme_data['properties']
# Get key properties for display based on scope
key_props = []
if theme_scope == 'mode':
if 'body_background' in properties:
key_props.append(f"bg:{properties['body_background']}")
if 'link_color' in properties:
key_props.append(f"links:{properties['link_color']}")
elif theme_scope == 'ui':
if 'editor_panel_bg' in properties:
key_props.append(f"panel:{properties['editor_panel_bg']}")
if 'editor_text_color' in properties:
key_props.append(f"text:{properties['editor_text_color']}")
if 'editor_focus_color' in properties:
key_props.append(f"focus:{properties['editor_focus_color']}")
elif theme_scope == 'document':
if 'font_family' in properties:
family = properties['font_family'].split(',')[0].strip().strip('"\'')
key_props.append(f"font:{family}")
if 'link_color' in properties:
key_props.append(f"links:{properties['link_color']}")
elif theme_scope == 'branding':
if 'accent_color' in properties:
key_props.append(f"accent:{properties['accent_color']}")
layered_themes.append({
'name': theme_name,
'scope': theme_scope,
'properties': ', '.join(key_props) if key_props else 'default styling'
})
# Process legacy mappings
for legacy_name, expanded_themes in LEGACY_THEME_MAPPING.items():
legacy_mappings.append({
'name': legacy_name,
'expands_to': ' + '.join(expanded_themes)
})
if format == 'json':
# JSON output
output_data = {
'layered_themes': layered_themes,
'legacy_mappings': legacy_mappings,
'usage': {
'single': 'markitect md-render file.md --theme dark',
'layered': 'markitect md-render file.md --theme dark,academic',
'legacy': 'markitect md-render file.md --theme github'
}
}
click.echo(json.dumps(output_data, indent=2))
elif format == 'list':
# Simple list output
click.echo("Available themes:")
for theme in layered_themes:
click.echo(f" {theme['name']} ({theme['scope']})")
if legacy_mappings:
click.echo("\nLegacy mappings:")
for mapping in legacy_mappings:
click.echo(f" {mapping['name']} -> {mapping['expands_to']}")
else: # table format (default)
# Table output
if layered_themes:
click.echo("Layered themes (can be combined):")
headers = ['Theme', 'Scope', 'Key Properties']
table_data = [[t['name'], t['scope'], t['properties']] for t in layered_themes]
click.echo(tabulate(table_data, headers=headers, tablefmt='grid'))
if legacy_mappings:
click.echo("\nLegacy theme mappings:")
headers = ['Legacy Name', 'Expands To']
table_data = [[m['name'], m['expands_to']] for m in legacy_mappings]
click.echo(tabulate(table_data, headers=headers, tablefmt='grid'))
click.echo("\nUsage examples:")
click.echo(" Single theme: markitect md-render file.md --theme dark")
click.echo(" Layered themes: markitect md-render file.md --theme dark,academic")
click.echo(" Legacy mapping: markitect md-render file.md --theme github")
@click.command()
@click.argument('directory', type=click.Path(exists=True, file_okay=False, dir_okay=True))
@click.option('--output', '-o', type=click.Path(),
help='Output index file (default: <directory>/index.html)')
@click.option('--template', type=click.Choice(['basic', 'github', 'dark', 'academic']),
help='Built-in template theme for index')
@click.option('--theme', type=ThemeType(),
help='Theme(s) to apply to index. Single: dark or layered: dark,github. Available: basic, github, dark, academic, light, corporate, startup')
@click.option('--recursive', '-r', is_flag=True,
help='Include subdirectories recursively')
@click.pass_context
def md_index_command(ctx, directory, output, template, recursive):
def md_index_command(ctx, directory, output, theme, recursive):
"""
Generate an index page for HTML files in a directory.
@@ -1800,7 +2185,7 @@ def md_index_command(ctx, directory, output, template, recursive):
index_title = f"Index - {dir_path.name}"
# Generate HTML content
html_content = generate_index_html(file_info_list, index_title, template)
html_content = generate_index_html(file_info_list, index_title, theme)
# Write index file
output_path.parent.mkdir(parents=True, exist_ok=True)

View File

@@ -93,7 +93,7 @@ markitect md-render input.md --output result.html
'md-render',
str(input_file),
'--output', str(output_file),
'--template', 'github'
'--theme', 'github'
])
assert result.exit_code == 0
@@ -139,7 +139,7 @@ markitect md-render input.md --output result.html
assert 'markdown' in result.output.lower()
assert 'html' in result.output.lower()
assert '--output' in result.output
assert '--template' in result.output
assert '--theme' in result.output
assert 'basic' in result.output
assert 'github' in result.output
assert 'dark' in result.output
@@ -176,12 +176,14 @@ markitect md-render input.md --output result.html
'md-render',
str(input_file),
'--output', str(output_file),
'--template', 'invalid_template_name'
'--theme', 'invalid_template_name'
])
# Should exit with error code (Click choice validation)
assert result.exit_code != 0
assert 'invalid choice' in result.output.lower() or 'not one of' in result.output.lower()
assert ('invalid choice' in result.output.lower() or
'not one of' in result.output.lower() or
'unknown theme' in result.output.lower())
def test_output_directory_creation(self):
"""Test that output directory is created if it doesn't exist - Issue #132."""

View File

@@ -85,7 +85,7 @@ This is a test document for template system validation.
'md-render',
str(input_file),
'--output', str(output_file),
'--template', 'github'
'--theme', 'github'
])
assert result.exit_code == 0
@@ -262,7 +262,7 @@ This is a test document for template system validation.
def test_multiple_templates_available(self):
"""Test that multiple template options are available - Issue #132."""
# Test template availability
template_options = ['basic', 'github', 'academic', 'dark']
theme_options = ['basic', 'github', 'academic', 'dark']
from markitect.plugins.builtin.markdown_commands import md_render_command
from click.testing import CliRunner
@@ -273,13 +273,13 @@ This is a test document for template system validation.
runner = CliRunner()
for template in template_options:
output_file = Path(self.temp_dir) / f"{template}_output.html"
for theme in theme_options:
output_file = Path(self.temp_dir) / f"{theme}_output.html"
result = runner.invoke(md_render_command, [
str(input_file),
'--output', str(output_file),
'--template', template
'--theme', theme
])
# Should be able to specify different templates
@@ -304,7 +304,7 @@ This is a test document for template system validation.
result = runner.invoke(md_render_command, [
str(input_file),
'--output', str(output_file),
'--template', 'dark'
'--theme', 'dark'
])
assert result.exit_code == 0
@@ -332,12 +332,14 @@ This is a test document for template system validation.
result = runner.invoke(cli, [
'md-render',
str(input_file),
'--template', 'nonexistent_template'
'--theme', 'nonexistent_template'
])
# Should exit with error code for invalid template choice
assert result.exit_code != 0
assert 'invalid choice' in result.output.lower() or 'not one of' in result.output.lower()
assert ('invalid choice' in result.output.lower() or
'not one of' in result.output.lower() or
'unknown theme' in result.output.lower())
def test_template_title_extraction_from_markdown(self):
"""Test title extraction from markdown for template variables - Issue #132."""

View File

@@ -94,7 +94,7 @@ Content paragraph that should be editable.
'md-render',
str(input_file),
'--output', str(output_file),
'--template', template,
'--theme', template,
'--edit'
])
@@ -145,7 +145,7 @@ Content paragraph that should be editable.
'md-render',
str(input_file),
'--output', str(output_file),
'--template', 'github'
'--theme', 'github'
])
assert result.exit_code == 0

View File

@@ -410,10 +410,10 @@ class TestCLIIntegration:
assert result.returncode == 0
assert custom_output.exists()
def test_md_index_command_with_template_option(self):
"""Test md-index command with template option."""
def test_md_index_command_with_theme_option(self):
"""Test md-index command with theme option."""
result = subprocess.run(
["markitect", "md-index", str(self.test_dir), "--template", "github"],
["markitect", "md-index", str(self.test_dir), "--theme", "github"],
capture_output=True,
text=True,
timeout=30

106
tools/register-agents-claude.py Executable file
View File

@@ -0,0 +1,106 @@
#!/usr/bin/env python3
"""Bridge script to register kaizen-agentic agents with Claude Code using lazy loading."""
import json
import sys
from pathlib import Path
# Add the kaizen-agentic source to path
sys.path.insert(0, str(Path(__file__).parent.parent / "capabilities" / "kaizen-agentic" / "src"))
from kaizen_agentic.registry import AgentRegistry
def get_agent_file_path(agent: object, agents_dir: Path) -> str:
"""Get relative path to agent file for lazy loading."""
return str(agent.file_path.relative_to(agents_dir.parent))
def generate_claude_agent_configs(agents_dir: Path) -> dict:
"""Generate Claude Code agent configurations with metadata only for lazy loading."""
registry = AgentRegistry(agents_dir)
agents = registry.list_agents()
claude_agents = {}
for agent in agents:
# Map agent names for Claude Code compatibility
claude_name = agent.name.replace('-', '_')
if claude_name == "changelog_keeper":
claude_name = "keepaChangelog"
elif claude_name == "todo_keeper":
claude_name = "keepaTodofile"
elif claude_name == "contributing_keeper":
claude_name = "keepaContributingfile"
# Store only metadata - instructions will be loaded lazily when needed
claude_agents[claude_name] = {
"description": agent.description,
"category": agent.category.value,
"dependencies": list(agent.dependencies),
"tools": ["Read", "Write", "Edit", "Glob", "Grep"], # Standard tools
"original_name": agent.name,
"file_path": get_agent_file_path(agent, agents_dir)
}
return claude_agents
def update_claude_settings(claude_agents: dict, settings_file: Path):
"""Update Claude Code settings with agent configurations."""
# Load existing settings
if settings_file.exists():
with open(settings_file, 'r') as f:
settings = json.load(f)
else:
settings = {}
# Add agents section
if "agents" not in settings:
settings["agents"] = {}
# Update with new agent configurations
settings["agents"].update(claude_agents)
# Write updated settings
with open(settings_file, 'w') as f:
json.dump(settings, f, indent=2)
return len(claude_agents)
def main():
"""Main registration process."""
project_root = Path(__file__).parent.parent
agents_dir = project_root / "agents"
claude_settings = project_root / ".claude" / "settings.local.json"
if not agents_dir.exists():
print(f"Error: Agents directory not found: {agents_dir}")
sys.exit(1)
print("Loading agents from kaizen-agentic registry...")
try:
claude_agents = generate_claude_agent_configs(agents_dir)
print(f"Found {len(claude_agents)} agents to register")
# Show what will be registered
for name, config in claude_agents.items():
print(f"{name}: {config['description'][:60]}... (from {config['file_path']})")
print(f"\nUpdating Claude Code settings: {claude_settings}")
count = update_claude_settings(claude_agents, claude_settings)
print(f"✅ Successfully registered {count} agents with Claude Code (metadata only)")
print("📄 Agent instructions will be loaded lazily when needed")
print("\nAvailable agents for Task tool:")
for name in sorted(claude_agents.keys()):
print(f" - {name}")
except Exception as e:
print(f"Error: {e}")
sys.exit(1)
if __name__ == "__main__":
main()