Compare commits
13 Commits
3e651adcfb
...
v0.6.0
| Author | SHA1 | Date | |
|---|---|---|---|
| be14322b13 | |||
| 86689c451c | |||
| 3e16793615 | |||
| dd0c9e3180 | |||
| 6dd278c538 | |||
| c6422bf73e | |||
| 53cfb90237 | |||
| 388320b9bf | |||
| bbceea5c7b | |||
| 5df78c3359 | |||
| e78ad47754 | |||
| 45694a5099 | |||
| c0bfc1553c |
1
.claude/agents/agent-claude-documentation.md
Symbolic link
1
.claude/agents/agent-claude-documentation.md
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/worsch/markitect_project/agents/agent-claude-documentation.md
|
||||
1
.claude/agents/agent-keepaChangelog.md
Symbolic link
1
.claude/agents/agent-keepaChangelog.md
Symbolic link
@@ -0,0 +1 @@
|
||||
/home/worsch/markitect_project/agents/agent-keepaChangelog.md
|
||||
158
.claude/agents/agent-project-management.md
Normal file
158
.claude/agents/agent-project-management.md
Normal 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.
|
||||
21
.claude/agents/test-agent.md
Normal file
21
.claude/agents/test-agent.md
Normal 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
|
||||
85
CHANGELOG.md
85
CHANGELOG.md
@@ -1,4 +1,40 @@
|
||||
# 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.6.0] - 2025-10-28
|
||||
|
||||
### Added
|
||||
- **Custom Status Modal System**: Professional theme-consistent status dialogs replacing browser alerts with proper branding and accessibility
|
||||
- **HTML Generation Dogtag**: Automatic attribution with timestamp and username linking for generated HTML documents
|
||||
- **Enhanced Link Navigation**: All document links now open in new tabs without triggering edit mode for improved user experience
|
||||
- **Comprehensive UI Framework Documentation**: Complete guide (UserInterfaceFramework.md) for consistent UI development patterns
|
||||
- **Database Integration**: Added store_document method to CleanDocumentManager with proper front matter parsing
|
||||
- **Enhanced AST Processing**: Improved title extraction from front matter and heading detection with cache file generation
|
||||
|
||||
### Changed
|
||||
- **Complete Document Manager Cleanup**: Removed 2000+ lines of legacy code while maintaining full backward compatibility
|
||||
- **Clean Architecture Implementation**: DocumentManager now extends CleanDocumentManager with clean wrapper pattern
|
||||
- **Improved Error Handling**: Enhanced validation and graceful error recovery throughout the system
|
||||
- **Standardized CSS Naming**: Consistent class naming conventions across all UI components
|
||||
|
||||
### Fixed
|
||||
- **Test Suite Compatibility**: Updated all tests to work with clean implementation architecture
|
||||
- **JavaScript Syntax Issues**: Resolved template literal and string escaping problems in generated HTML
|
||||
- **Link Behavior**: Fixed issue where document links were incorrectly triggering edit mode
|
||||
- **Front Matter Parsing**: Proper integration with FrontMatterParser for metadata extraction
|
||||
|
||||
### Technical
|
||||
- Added --nodogtag CLI option for clean output when attribution is not desired
|
||||
- Enhanced ingest_file method with proper title extraction from front matter and headings
|
||||
- Implemented theme-aware modal overlay patterns with proper CSS styling
|
||||
- Fixed CSS escape sequences and JavaScript syntax validation issues
|
||||
|
||||
## [0.5.0] - 2025-10-26
|
||||
|
||||
### Added
|
||||
@@ -44,7 +80,54 @@
|
||||
### 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
|
||||
|
||||
All notable changes to MarkiTect will be documented in this file.
|
||||
### 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
|
||||
|
||||
xxx
|
||||
|
||||
91
TODO.md
Normal file
91
TODO.md
Normal 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
|
||||
338
UserInterfaceFramework.md
Normal file
338
UserInterfaceFramework.md
Normal file
@@ -0,0 +1,338 @@
|
||||
# User Interface Framework Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This document defines the canonical terminology and specifications for all UI components in the Markitect markdown editor interface. This framework establishes a common vocabulary for interface evolution discussions and future development.
|
||||
|
||||
## Component Architecture
|
||||
|
||||
The editor interface consists of 6 main components organized in layers:
|
||||
|
||||
### Layer Priority (Z-Index)
|
||||
1. **Toast Notifications** (z-index: 10001) - Highest priority
|
||||
2. **Editor Floating Action Panel** (z-index: 1000) - High priority
|
||||
3. **Modal Dialogs** (z-index: 999) - Modal layer
|
||||
4. **Inline Section Editors** (z-index: 100) - Contextual editing
|
||||
5. **Document Canvas** (z-index: 1) - Content layer
|
||||
6. **Background** (z-index: 0) - Base layer
|
||||
|
||||
---
|
||||
|
||||
## 1. Editor Floating Action Panel
|
||||
|
||||
**Component Name**: `Editor`
|
||||
**Type**: Floating action panel with status indicator
|
||||
**Location**: Top-right corner (fixed positioning)
|
||||
|
||||
### Description
|
||||
A persistent control hub providing document-level actions and real-time status feedback. Always visible and contextually aware of editing state.
|
||||
|
||||
### Technical Specifications
|
||||
- **Container ID**: `ui-edit-floater`
|
||||
- **CSS Classes**: `ui-edit-floater-panel`
|
||||
- **Position**: `position: fixed; top: 20px; right: 20px;`
|
||||
- **Z-Index**: 1000
|
||||
- **Update Frequency**: Status refreshes every 2 seconds via `setInterval`
|
||||
|
||||
### Components
|
||||
1. **Header Section**
|
||||
- **Title**: "📝 Editor" (emoji + text)
|
||||
- **Status Display**: Dynamic text showing current state
|
||||
|
||||
2. **Action Buttons**
|
||||
- **💾 Save Document** (green accept style)
|
||||
- **🔄 Reset All** (orange reset style)
|
||||
- **📊 Show Status** (grey secondary style)
|
||||
|
||||
### Status States
|
||||
- `"Ready"` - Default idle state
|
||||
- `"Editing [N] section(s)"` - Active editing in progress
|
||||
- `"[N] section(s) modified"` - Unsaved changes exist
|
||||
- `"All sections saved ✓"` - All work is saved (with checkmark)
|
||||
|
||||
### Theme Integration
|
||||
- Colors and styling adapt to selected UI theme (standard, greyscale, electric, psychedelic)
|
||||
- Header text color matches theme text color
|
||||
- Panel background follows theme panel styling
|
||||
|
||||
---
|
||||
|
||||
## 2. Toast Notification System
|
||||
|
||||
**Component Name**: `Toast`
|
||||
**Type**: Auto-dismissing temporary status messages
|
||||
**Location**: Top-center (horizontally centered)
|
||||
|
||||
### Description
|
||||
Provides immediate visual feedback for user actions through temporary, non-blocking messages that appear and automatically disappear.
|
||||
|
||||
### Technical Specifications
|
||||
- **Position**: `position: fixed; top: 20px; left: 50%; transform: translateX(-50%);`
|
||||
- **Z-Index**: 10001 (highest priority)
|
||||
- **Auto-Dismiss**: 3 seconds
|
||||
- **Max Width**: 400px
|
||||
- **Animation**: Smooth appear/disappear
|
||||
|
||||
### Message Types
|
||||
1. **Success Toast** (green `#28a745`)
|
||||
- "Document saved as: [filename]"
|
||||
- "✂️ Section split into [N] sections!"
|
||||
|
||||
2. **Info Toast** (blue `#007acc`)
|
||||
- "Document reset to original structure"
|
||||
|
||||
3. **Error Toast** (red `#dc3545`)
|
||||
- Error condition messages
|
||||
|
||||
### Visual Styling
|
||||
- **Shape**: Rounded corners (4px border-radius)
|
||||
- **Typography**: White text, 14px font size, center aligned
|
||||
- **Shadow**: `0 2px 8px rgba(0,0,0,0.2)`
|
||||
- **Padding**: `12px 20px`
|
||||
|
||||
---
|
||||
|
||||
## 3. Document Canvas
|
||||
|
||||
**Component Name**: `Document` or `Canvas`
|
||||
**Type**: Main content rendering area
|
||||
**Location**: Central content area
|
||||
|
||||
### Description
|
||||
The primary workspace where markdown content is rendered and made interactive for editing. Displays content as formatted HTML while providing editing affordances.
|
||||
|
||||
### Technical Specifications
|
||||
- **Container ID**: `markdown-content`
|
||||
- **CSS Classes**: Content uses semantic classes (`ui-edit-section`)
|
||||
- **Layout**: Responsive, centered with max-width constraints
|
||||
- **Interaction**: Click-to-edit paradigm
|
||||
|
||||
### Section Elements
|
||||
Each content section is individually interactive:
|
||||
- **Hover Effect**: Subtle background (`rgba(0, 0, 0, 0.02)`) and border hint
|
||||
- **Click Target**: Entire section area is clickable
|
||||
- **Visual Feedback**: Smooth transitions (0.2s ease)
|
||||
- **Section Types**: Headings, paragraphs, lists, code blocks, blockquotes
|
||||
|
||||
### Content Rendering
|
||||
- **Primary**: Uses `marked.js` for markdown parsing
|
||||
- **Fallback**: Basic HTML conversion if library fails
|
||||
- **Graceful Degradation**: Always displays content, even with errors
|
||||
|
||||
---
|
||||
|
||||
## 4. Inline Section Editor
|
||||
|
||||
**Component Name**: `Section Editor` or `Inline Editor`
|
||||
**Type**: Contextual editing widget
|
||||
**Location**: Replaces section content during editing
|
||||
|
||||
### Description
|
||||
A contextual editing interface that appears when a section is activated for editing. Provides textarea input and action controls for section-level operations.
|
||||
|
||||
### Technical Specifications
|
||||
- **Container CSS**: `ui-edit-inline-panel`
|
||||
- **Layout**: Horizontal flex layout (textarea + button column)
|
||||
- **Theme Integration**: Inherits floating panel styling from active UI theme
|
||||
- **Focus Management**: Auto-focus on textarea when activated
|
||||
|
||||
### Components
|
||||
1. **Textarea**
|
||||
- **CSS Classes**: `ui-edit-textarea ui-edit-textarea-main`
|
||||
- **Font**: Monospace font family for code editing
|
||||
- **Features**: Vertical resize, focus styling, theme-aware colors
|
||||
|
||||
2. **Action Buttons** (vertical column)
|
||||
- **✓ Accept** (`ui-edit-button-accept`) - Save changes
|
||||
- **✗ Cancel** (`ui-edit-button-cancel`) - Discard changes
|
||||
- **🔄 Reset** (`ui-edit-button-reset`) - Restore original content
|
||||
|
||||
### Behavior
|
||||
- **Multi-Section**: Supports multiple concurrent section editing
|
||||
- **State Persistence**: Maintains editing state until explicitly resolved
|
||||
- **Keyboard Support**: Planned for future enhancement
|
||||
- **Auto-Split**: Automatically splits sections when new headings are added
|
||||
|
||||
---
|
||||
|
||||
## 5. Status Information Modal
|
||||
|
||||
**Component Name**: `Status Modal` or `Info Dialog`
|
||||
**Type**: Modal dialog for comprehensive status display
|
||||
**Location**: Center screen (modal overlay)
|
||||
|
||||
### Description
|
||||
Provides detailed information about the current editing session, including version info, document statistics, file details, and help documentation.
|
||||
|
||||
### Current Implementation
|
||||
- **Method**: Browser native `alert()` (temporary solution)
|
||||
- **Trigger**: "📊 Show Status" button in floating action panel
|
||||
- **Content**: Multi-section formatted text
|
||||
|
||||
### Information Sections
|
||||
1. **Application Header**
|
||||
- Application name and version
|
||||
- Git commit info and development status
|
||||
|
||||
2. **File Information**
|
||||
- Generated save filename
|
||||
- Source filename
|
||||
- Current URL
|
||||
|
||||
3. **Document Statistics**
|
||||
- Total sections count
|
||||
- Modified sections count
|
||||
- Currently editing sections count
|
||||
- Unsaved changes indicator
|
||||
|
||||
4. **Help Documentation**
|
||||
- Section behavior explanation
|
||||
- Editing controls reference
|
||||
- Keyboard shortcuts (future)
|
||||
|
||||
### Future Enhancement Plan
|
||||
**Target**: Replace browser alert with custom modal dialog
|
||||
- **Styling**: Theme-aware modal with proper typography
|
||||
- **Interaction**: Close button, better formatting
|
||||
- **Features**: Copy-to-clipboard, expandable sections
|
||||
- **Accessibility**: Proper ARIA labels, keyboard navigation
|
||||
|
||||
---
|
||||
|
||||
## 6. Confirmation Dialog
|
||||
|
||||
**Component Name**: `Confirmation Dialog`
|
||||
**Type**: Modal confirmation for destructive actions
|
||||
**Location**: Center screen (modal overlay)
|
||||
|
||||
### Description
|
||||
Provides user confirmation for potentially destructive operations that cannot be easily undone.
|
||||
|
||||
### Current Implementation
|
||||
- **Method**: Browser native `confirm()` (temporary solution)
|
||||
- **Trigger**: "🔄 Reset All" button in floating action panel
|
||||
- **Message**: "Reset all content to original markdown? This will lose all edits and remove split sections."
|
||||
|
||||
### Use Cases
|
||||
- **Reset All Sections**: Complete document reset to original state
|
||||
- **Future**: Delete operations, bulk changes, file operations
|
||||
|
||||
### Future Enhancement Plan
|
||||
**Target**: Replace browser confirm with custom modal dialog
|
||||
- **Styling**: Theme-aware modal with clear action buttons
|
||||
- **Features**:
|
||||
- Clear primary/secondary action buttons
|
||||
- Detailed consequence explanation
|
||||
- Optional "Don't ask again" for non-critical confirmations
|
||||
- **Accessibility**: Proper focus management, keyboard support
|
||||
|
||||
---
|
||||
|
||||
## Design Principles
|
||||
|
||||
### 1. **Theme Consistency**
|
||||
All components must adapt to the selected UI theme:
|
||||
- **Standard**: Light grey palette with blue accents
|
||||
- **Greyscale**: Monochromatic grey scale
|
||||
- **Electric**: Dark blue with cyan/yellow accents and glow effects
|
||||
- **Psychedelic**: Vibrant gradient backgrounds with white text
|
||||
|
||||
### 2. **Non-Blocking Interactions**
|
||||
- **Toast notifications**: Auto-dismiss, don't require user action
|
||||
- **Floating action panel**: Always accessible, doesn't block content
|
||||
- **Inline editors**: Contextual, don't interfere with other sections
|
||||
|
||||
### 3. **Graceful Degradation**
|
||||
- **Content always visible**: Even if JavaScript fails
|
||||
- **Progressive enhancement**: Core functionality works without advanced features
|
||||
- **Fallback implementations**: Basic browser dialogs until custom implementations ready
|
||||
|
||||
### 4. **Responsive Design**
|
||||
- **Mobile-first**: Components adapt to smaller screens
|
||||
- **Touch-friendly**: Appropriate touch targets and gestures
|
||||
- **Scalable**: Works across different zoom levels and resolutions
|
||||
|
||||
### 5. **Accessibility**
|
||||
- **Keyboard navigation**: All interactive elements accessible via keyboard
|
||||
- **Screen reader support**: Proper ARIA labels and semantic markup
|
||||
- **High contrast**: Sufficient color contrast ratios in all themes
|
||||
- **Focus management**: Clear focus indicators and logical tab order
|
||||
|
||||
---
|
||||
|
||||
## Development Conventions
|
||||
|
||||
### CSS Class Naming
|
||||
**Pattern**: `{scope}-{component}-{element}-{modifier}`
|
||||
|
||||
**Scopes**:
|
||||
- `ui` - User interface elements
|
||||
- `md` - Mode (light/dark)
|
||||
- `dc` - Document content
|
||||
- `br` - Branding
|
||||
|
||||
**Examples**:
|
||||
- `ui-edit-floater-panel`
|
||||
- `ui-edit-button-accept`
|
||||
- `ui-edit-textarea-main`
|
||||
- `ui-edit-section-frame`
|
||||
|
||||
### JavaScript Event Naming
|
||||
**Pattern**: `{action}-{target}`
|
||||
|
||||
**Examples**:
|
||||
- `edit-started`
|
||||
- `changes-accepted`
|
||||
- `section-split`
|
||||
- `content-updated`
|
||||
|
||||
### Component State Management
|
||||
- **Centralized**: Section state managed by `SectionManager`
|
||||
- **Event-driven**: Components communicate via events
|
||||
- **Immutable updates**: State changes create new state objects
|
||||
- **Consistent**: Same patterns across all components
|
||||
|
||||
---
|
||||
|
||||
## Future Enhancement Roadmap
|
||||
|
||||
### Phase 1: Modal System Replacement
|
||||
- Replace browser `alert()` and `confirm()` with custom implementations
|
||||
- Add proper theme integration and accessibility features
|
||||
- Implement keyboard navigation and focus management
|
||||
|
||||
### Phase 2: Enhanced Interactions
|
||||
- Add keyboard shortcuts for common operations
|
||||
- Implement drag-and-drop section reordering
|
||||
- Add section templates and quick-insert functionality
|
||||
|
||||
### Phase 3: Advanced Features
|
||||
- Multi-document editing with tabs
|
||||
- Real-time collaboration indicators
|
||||
- Advanced search and replace within sections
|
||||
- Export options beyond basic markdown
|
||||
|
||||
### Phase 4: Performance Optimization
|
||||
- Virtual scrolling for large documents
|
||||
- Lazy loading of section editors
|
||||
- Optimized rendering for mobile devices
|
||||
- Advanced caching strategies
|
||||
|
||||
---
|
||||
|
||||
## Component Integration Matrix
|
||||
|
||||
| Component | Theme Aware | Mobile Ready | Keyboard Nav | Touch Friendly | Accessible |
|
||||
|-----------|-------------|--------------|--------------|----------------|------------|
|
||||
| Editor Panel | ✅ Yes | ⚠️ Partial | ❌ Planned | ⚠️ Basic | ⚠️ Partial |
|
||||
| Toast System | ❌ No | ✅ Yes | ❌ N/A | ✅ Yes | ⚠️ Basic |
|
||||
| Document Canvas | ✅ Yes | ✅ Yes | ⚠️ Partial | ✅ Yes | ✅ Yes |
|
||||
| Section Editor | ✅ Yes | ⚠️ Partial | ⚠️ Basic | ⚠️ Basic | ⚠️ Partial |
|
||||
| Status Modal | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No |
|
||||
| Confirmation | ❌ No | ❌ No | ❌ No | ❌ No | ❌ No |
|
||||
|
||||
**Legend**: ✅ Full Support | ⚠️ Partial/Needs Work | ❌ Not Implemented
|
||||
|
||||
---
|
||||
|
||||
This framework provides the foundation for consistent UI development and evolution. All future interface changes should reference these component definitions and maintain the established patterns and conventions.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -26,7 +26,165 @@ 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)',
|
||||
'editor_accept_bg': '#888888',
|
||||
'editor_accept_hover': '#777777',
|
||||
'editor_cancel_bg': '#999999',
|
||||
'editor_cancel_hover': '#808080',
|
||||
'editor_reset_bg': '#aaaaaa',
|
||||
'editor_reset_hover': '#999999',
|
||||
'editor_secondary_bg': '#bbbbbb',
|
||||
'editor_secondary_hover': '#aaaaaa'
|
||||
}
|
||||
},
|
||||
'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': '0 0 20px rgba(0,255,255,0.5), 0 0 40px rgba(255,255,0,0.2)'
|
||||
}
|
||||
},
|
||||
'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 +209,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 +461,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 +483,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 +576,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 +1770,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 +1804,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 +1846,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 +1896,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 +1931,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,
|
||||
@@ -1669,23 +1946,31 @@ def md_list_command(ctx, output_format, names_only):
|
||||
help='Use publication directory for output')
|
||||
@click.option('--dont-use-publication-dir', is_flag=True,
|
||||
help='Don\'t use publication directory for output')
|
||||
@click.option('--nodogtag', is_flag=True,
|
||||
help='Don\'t add HTML generation dogtag at end of document')
|
||||
@click.pass_context
|
||||
def md_render_command(ctx, input_file, output, template, css, edit, editor_theme,
|
||||
keyboard_shortcuts, use_publication_dir, dont_use_publication_dir):
|
||||
def md_render_command(ctx, input_file, output, theme, css, edit, editor_theme,
|
||||
keyboard_shortcuts, use_publication_dir, dont_use_publication_dir, nodogtag):
|
||||
"""
|
||||
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,26 +1997,28 @@ 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)
|
||||
keyboard_shortcuts=keyboard_shortcuts,
|
||||
nodogtag=nodogtag)
|
||||
|
||||
click.echo(f"✓ Rendered with interactive editing capabilities to: {output_path}")
|
||||
|
||||
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,
|
||||
nodogtag=nodogtag)
|
||||
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 +2026,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 +2197,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)
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "markitect"
|
||||
version = "0.5.0"
|
||||
version = "0.6.0"
|
||||
description = "Advanced Markdown engine for structured content"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.8"
|
||||
|
||||
@@ -74,7 +74,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
# Should execute successfully
|
||||
assert result.exit_code == 0
|
||||
@@ -99,7 +99,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
@@ -128,7 +128,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
@@ -155,7 +155,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
@@ -185,7 +185,7 @@ This is a **test** document with some *italic* text and a [link](https://example
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
# Should handle empty file gracefully
|
||||
assert result.exit_code == 0
|
||||
@@ -221,7 +221,7 @@ And some inline `code` too.
|
||||
from click.testing import CliRunner
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file)])
|
||||
result = runner.invoke(md_render_command, [str(input_file), '--output', str(output_file), '--nodogtag'])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert output_file.exists()
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -73,9 +73,9 @@ Content paragraph that should be editable.
|
||||
html_content = output_file.read_text()
|
||||
|
||||
# Should include editor library and edit mode flag
|
||||
assert 'markitect-floating-header' in html_content
|
||||
assert 'ui-edit-floater-panel' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_edit_flag_with_all_templates(self):
|
||||
"""Test --edit flag works with all template types - Issue #133."""
|
||||
@@ -94,7 +94,7 @@ Content paragraph that should be editable.
|
||||
'md-render',
|
||||
str(input_file),
|
||||
'--output', str(output_file),
|
||||
'--template', template,
|
||||
'--theme', template,
|
||||
'--edit'
|
||||
])
|
||||
|
||||
@@ -103,8 +103,8 @@ Content paragraph that should be editable.
|
||||
|
||||
html_content = output_file.read_text()
|
||||
# Should work with template styles
|
||||
assert 'markitect-floating-header' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'ui-edit-floater-panel' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_editor_library_loading_configuration(self):
|
||||
"""Test editor library loading and configuration options - Issue #133."""
|
||||
@@ -145,7 +145,8 @@ Content paragraph that should be editable.
|
||||
'md-render',
|
||||
str(input_file),
|
||||
'--output', str(output_file),
|
||||
'--template', 'github'
|
||||
'--theme', 'github',
|
||||
'--nodogtag'
|
||||
])
|
||||
|
||||
assert result.exit_code == 0
|
||||
@@ -155,7 +156,7 @@ Content paragraph that should be editable.
|
||||
|
||||
# Should NOT include editor library without --edit flag
|
||||
assert 'markitect-editor' not in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' not in html_content
|
||||
assert 'const MARKITECT_EDIT_MODE = true' not in html_content
|
||||
|
||||
# Should include existing functionality
|
||||
assert 'marked.min.js' in html_content
|
||||
@@ -208,8 +209,8 @@ Content paragraph that should be editable.
|
||||
|
||||
# Should include both custom CSS and editor
|
||||
assert 'Courier New' in html_content
|
||||
assert 'markitect-floating-header' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'ui-edit-floater-panel' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_large_document_editing_performance(self):
|
||||
"""Test editing flag with large markdown documents - Issue #133."""
|
||||
@@ -236,7 +237,7 @@ Content paragraph that should be editable.
|
||||
|
||||
# Should handle large documents gracefully
|
||||
assert len(html_content) > 20000 # Should be substantial (adjusted from 50k)
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
|
||||
def test_front_matter_preservation_with_editing(self):
|
||||
@@ -273,7 +274,7 @@ This content should be editable while preserving front matter.
|
||||
|
||||
# Should preserve front matter in JavaScript payload and include editing
|
||||
assert 'Test Author' in html_content or 'Editable Document' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
|
||||
def test_error_handling_invalid_edit_options(self):
|
||||
@@ -316,7 +317,7 @@ This content should be editable while preserving front matter.
|
||||
html_content = output_file.read_text()
|
||||
|
||||
# Should include bundled editor (not relying on CDN)
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
# The implementation uses bundled JavaScript, not CDN, so no fallback needed
|
||||
|
||||
@@ -343,7 +344,7 @@ This content should be editable while preserving front matter.
|
||||
# Should include mobile-friendly meta tags
|
||||
assert 'viewport' in html_content
|
||||
assert 'width=device-width' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
|
||||
def test_keyboard_shortcuts_configuration(self):
|
||||
"""Test keyboard shortcuts can be configured for editing - Issue #133."""
|
||||
@@ -430,4 +431,4 @@ def example_function():
|
||||
|
||||
# Should detect and mark various section types
|
||||
assert 'data-section' in html_content or 'markitect-section-editable' in html_content
|
||||
assert 'MarkitectEditor' in html_content
|
||||
assert 'MarkitectCleanEditor' in html_content
|
||||
@@ -233,7 +233,7 @@ class TestIndexPageGeneration:
|
||||
{"path": self.test_dir / "doc1.html", "title": "Document One", "relative_path": "doc1.html"}
|
||||
]
|
||||
|
||||
html_content = generate_index_html(html_files, "Test Index", template="github")
|
||||
html_content = generate_index_html(html_files, "Test Index", theme="github")
|
||||
|
||||
parser = SimpleHTMLParser()
|
||||
parser.feed(html_content)
|
||||
@@ -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
|
||||
|
||||
@@ -17,14 +17,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_generates_valid_javascript(self):
|
||||
"""Test that edit mode generates syntactically valid JavaScript."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
# Test markdown content
|
||||
test_content = "# Test Header\n\nThis is a test paragraph.\n\n## Section 2\n\nAnother paragraph."
|
||||
@@ -64,14 +60,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_contains_required_functions(self):
|
||||
"""Test that edit mode HTML contains all required JavaScript functions."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -81,12 +73,11 @@ class TestEditModeRegression:
|
||||
|
||||
# Check for critical functions that must be present
|
||||
required_functions = [
|
||||
'MarkitectEditor',
|
||||
'updateStatus',
|
||||
'reportEditModeError',
|
||||
'makeContentEditable',
|
||||
'handleSectionClick',
|
||||
'editSection'
|
||||
'MarkitectCleanEditor',
|
||||
'SectionManager',
|
||||
'Section',
|
||||
'DOMRenderer',
|
||||
'initializeCleanEditor'
|
||||
]
|
||||
|
||||
for func_name in required_functions:
|
||||
@@ -94,14 +85,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_no_broken_string_literals(self):
|
||||
"""Test that there are no broken string literals in the generated JavaScript."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -126,14 +113,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_proper_brace_escaping(self):
|
||||
"""Test that braces are properly escaped in f-string templates."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -157,14 +140,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_template_literal_syntax(self):
|
||||
"""Test that template literals are properly escaped."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -188,14 +167,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_contains_content_div(self):
|
||||
"""Test that edit mode HTML contains the markdown-content div."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -210,14 +185,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_error_handling_elements(self):
|
||||
"""Test that edit mode includes proper error handling UI elements."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -225,22 +196,18 @@ class TestEditModeRegression:
|
||||
edit_mode=True
|
||||
)
|
||||
|
||||
# Should contain error handling elements
|
||||
assert 'id="markitect-control-panel"' in html_content
|
||||
assert 'id="status-message"' in html_content
|
||||
assert 'id="error-details"' in html_content
|
||||
assert 'reportEditModeError' in html_content
|
||||
# Should contain clean editor elements
|
||||
assert 'MARKITECT_EDIT_MODE' in html_content
|
||||
assert 'class="markitect-edit-mode"' in html_content
|
||||
assert 'initializeCleanEditor' in html_content
|
||||
assert 'console.error' in html_content # Error handling
|
||||
|
||||
def test_edit_mode_vs_normal_mode_differences(self):
|
||||
"""Test that edit mode and normal mode generate different output appropriately."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
test_content = "# Test Header\n\nTest content."
|
||||
|
||||
# Generate both modes
|
||||
@@ -265,14 +232,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_edit_mode_javascript_execution_flow(self):
|
||||
"""Test the logical flow of JavaScript execution in edit mode."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -288,9 +251,9 @@ class TestEditModeRegression:
|
||||
flow_elements = [
|
||||
'DOMContentLoaded', # Event listener setup
|
||||
'MARKITECT_EDIT_MODE', # Mode check
|
||||
'new MarkitectEditor', # Editor instantiation
|
||||
'makeContentEditable', # Content enhancement
|
||||
'handleSectionClick' # Interaction handler
|
||||
'initializeCleanEditor', # Editor initialization
|
||||
'marked.parse', # Content rendering
|
||||
'MarkitectCleanEditor' # Clean editor class
|
||||
]
|
||||
|
||||
for element in flow_elements:
|
||||
@@ -298,14 +261,10 @@ class TestEditModeRegression:
|
||||
|
||||
def test_newline_escaping_in_javascript_strings(self):
|
||||
"""Test that newlines in JavaScript strings are properly escaped."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -333,14 +292,10 @@ class TestEditModeIntegration:
|
||||
|
||||
def test_save_functionality_javascript_presence(self):
|
||||
"""Test that the save functionality JavaScript is properly included."""
|
||||
from markitect.document_manager import DocumentManager
|
||||
from markitect.clean_document_manager import CleanDocumentManager
|
||||
|
||||
# Create a mock DocumentManager to avoid database dependency
|
||||
class MockDatabaseManager:
|
||||
pass
|
||||
|
||||
doc_manager = DocumentManager.__new__(DocumentManager)
|
||||
doc_manager.database_manager = MockDatabaseManager()
|
||||
# Create a CleanDocumentManager
|
||||
doc_manager = CleanDocumentManager()
|
||||
|
||||
html_content = doc_manager._generate_html_template(
|
||||
title="Test",
|
||||
@@ -350,9 +305,9 @@ class TestEditModeIntegration:
|
||||
|
||||
# Check for save-related functionality
|
||||
save_elements = [
|
||||
'Save & Download', # Button text
|
||||
'markitectEditor.save()', # Save function call
|
||||
'getMarkdownContent', # Content extraction
|
||||
'💾 Save Document', # Button text from clean implementation
|
||||
'generateSaveFilename', # Save filename generation
|
||||
'getDocumentMarkdown', # Content extraction
|
||||
'Blob', # File creation
|
||||
'download' # Download attribute
|
||||
]
|
||||
|
||||
106
tools/register-agents-claude.py
Executable file
106
tools/register-agents-claude.py
Executable 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()
|
||||
Reference in New Issue
Block a user