markitect-main/markitect/plugins/builtin/markdown_commands.py

1023 lines
38 KiB
Python
Raw Normal View History

"""
Markdown commands plugin for MarkiTect.
This plugin provides the core markdown file operations with md- prefixes,
replacing the legacy unprefixed commands for better namespace consistency.
"""
import click
import json
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
import os
import tempfile
from pathlib import Path
from typing import Dict, Any
from markitect.plugins.base import CommandPlugin, PluginMetadata, PluginType
from markitect.plugins.decorators import register_plugin
from markitect.document_manager import DocumentManager
from markitect.serializer import ASTSerializer
# Simple helper function - avoiding circular imports
def get_default_format(available_formats=['table', 'json', 'yaml', 'simple'], fallback='simple'):
"""Get the default output format - simplified version for plugin."""
return fallback
@register_plugin("markdown_commands")
class MarkdownCommandsPlugin(CommandPlugin):
"""Plugin providing core markdown file operations."""
@property
def metadata(self) -> PluginMetadata:
return PluginMetadata(
name="markdown_commands",
version="1.0.0",
description="Core markdown file operations (ingest, get, list) with md- prefixes",
author="MarkiTect Core Team",
plugin_type=PluginType.COMMAND,
markitect_version=">=0.1.0"
)
def get_commands(self) -> Dict[str, Any]:
"""Return the markdown commands with md- prefixes."""
return {
'md-ingest': md_ingest_command,
'md-get': md_get_command,
'md-list': md_list_command,
'md-render': md_render_command
}
# Define commands as standalone functions
@click.command()
@click.argument('file_path', type=click.Path(exists=True))
@click.pass_context
def md_ingest_command(ctx, file_path):
"""
Process and store a markdown file.
Ingests a markdown file into the MarkiTect system, parsing its content,
extracting front matter, generating AST cache, and storing metadata
in the database.
FILE_PATH: Path to the markdown file to process
Examples:
markitect md-ingest README.md
markitect md-ingest docs/guide.md
"""
config = ctx.obj or {}
try:
if config.get('verbose', False):
click.echo(f"Processing file: {file_path}")
# Initialize document manager with database manager
doc_manager = DocumentManager(config.get('db_manager'))
# Process the file
result = doc_manager.ingest_file(file_path)
if config.get('verbose', False):
click.echo(f"Processing results:")
click.echo(f" File: {result['metadata']['filename']}")
click.echo(f" AST nodes: {len(result['ast'])} nodes")
click.echo(f" Cache file: {result['ast_cache_path']}")
click.echo(f" Parse time: {result['parse_time']:.2f}s")
click.echo(f" Cache time: {result['cache_time']:.2f}s")
click.echo(f"✓ Successfully ingested: {Path(file_path).name}")
except Exception as e:
click.echo(f"Error processing file: {e}", err=True)
raise click.Abort()
@click.command()
@click.argument('file_path', type=str)
@click.option('--output', '-o', type=click.Path(), help='Output file path (default: stdout)')
@click.pass_context
def md_get_command(ctx, file_path, output):
"""
Retrieve and output a processed markdown file.
Loads the file from the database and AST cache, then serializes it back
to markdown format. Supports outputting to file or stdout.
FILE_PATH: Name of the file to retrieve
Examples:
markitect md-get README.md
markitect md-get docs/guide.md --output modified_guide.md
"""
config = ctx.obj or {}
try:
if config.get('verbose', False):
click.echo(f"Retrieving file: {file_path}")
db_manager = config.get('db_manager')
# Get file information from database
file_info = db_manager.get_markdown_file(file_path)
if not file_info:
click.echo(f"File not found in database: {file_path}", err=True)
click.echo("Use 'markitect md-ingest' to process the file first.", err=True)
raise click.Abort()
# Load AST from cache
cache_filename = f"{file_path}.ast.json"
cache_path = Path('.ast_cache') / cache_filename
if not cache_path.exists():
click.echo(f"AST cache not found: {cache_path}", err=True)
click.echo("Try re-ingesting the file to regenerate cache.", err=True)
raise click.Abort()
# Read AST from cache
import json
with open(cache_path, 'r', encoding='utf-8') as f:
ast = json.load(f)
# Parse front matter from database
front_matter = None
if file_info.get('front_matter'):
try:
front_matter = eval(file_info['front_matter'])
except (ValueError, TypeError, SyntaxError):
if config.get('verbose', False):
click.echo("Warning: Could not parse front matter", err=True)
# Serialize AST back to markdown
serializer = ASTSerializer()
markdown_content = serializer.serialize_to_markdown(ast, front_matter)
# Output to file or stdout
if output:
output_path = Path(output)
output_path.parent.mkdir(parents=True, exist_ok=True)
with open(output_path, 'w', encoding='utf-8') as f:
f.write(markdown_content)
click.echo(f"✓ File written to: {output_path}")
else:
click.echo(markdown_content)
if config.get('verbose', False):
click.echo(f"Retrieved {len(ast)} AST tokens", err=True)
except Exception as e:
click.echo(f"Error retrieving file: {e}", err=True)
raise click.Abort()
@click.command()
@click.option('--format', 'output_format', type=click.Choice(['table', 'json', 'yaml', 'simple']),
default=lambda: get_default_format(['table', 'json', 'yaml', 'simple']), help='Output format')
@click.option('--names-only', is_flag=True, help='Show only filenames (no metadata)')
@click.pass_context
def md_list_command(ctx, output_format, names_only):
"""
List all stored markdown files and their status.
Shows all markdown files that have been processed and stored
in the MarkiTect database with their basic metadata.
Examples:
markitect md-list
markitect md-list --format table
markitect md-list --format json
markitect md-list --names-only
"""
config = ctx.obj or {}
try:
if config.get('verbose', False):
click.echo("Retrieving all stored files...")
db_manager = config.get('db_manager')
files = db_manager.list_markdown_files()
if not files:
click.echo("No files found in database.")
click.echo("Use 'markitect md-ingest <file>' to add files.")
return
# Handle names-only option
if names_only:
for file_info in files:
click.echo(file_info['filename'])
return
# Handle different output formats
if output_format == 'simple':
# Original emoji format
click.echo(f"Found {len(files)} file(s):")
click.echo()
for file_info in files:
click.echo(f"📄 {file_info['filename']}")
if config.get('verbose', False):
click.echo(f" Created: {file_info['created_at']}")
if file_info.get('front_matter'):
try:
front_matter = eval(file_info['front_matter'])
if front_matter:
click.echo(f" Front matter: {list(front_matter.keys())}")
except (ValueError, TypeError, SyntaxError):
click.echo(f" Front matter: (parsing error)")
click.echo()
else:
# Use structured format (table, json, yaml)
if output_format == 'json':
import json
click.echo(json.dumps(files, indent=2, default=str))
elif output_format == 'yaml':
import yaml
click.echo(yaml.dump(files, default_flow_style=False))
else: # table format (default)
# Simple table output
click.echo(f"Found {len(files)} file(s):")
click.echo(f"{'Filename':<30} {'Created':<20}")
click.echo("-" * 50)
for file_info in files:
click.echo(f"{file_info['filename']:<30} {file_info['created_at']:<20}")
except Exception as e:
click.echo(f"Error listing files: {e}", err=True)
raise click.Abort()
@click.command()
@click.argument('input_file', type=click.Path(exists=True))
@click.option('--output', '-o', type=click.Path(), help='Output HTML file path (defaults to input filename with .html extension)')
@click.option('--template', type=click.Choice(['basic', 'github', 'academic', 'dark']),
default='basic', help='HTML template: basic (default), github, academic, or dark theme')
@click.option('--css', type=click.Path(exists=True), help='Custom CSS file to inject into the template')
@click.option('--edit', is_flag=True, help='Enable instant markdown editing capabilities in the generated HTML')
@click.option('--editor-theme', type=click.Choice(['light', 'dark']), default='light',
help='Editor interface theme (light or dark)')
@click.option('--keyboard-shortcuts', is_flag=True, help='Enable keyboard shortcuts for editing actions')
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
@click.option('--use-publication-dir', is_flag=True, help='Force single files to use publication directory')
@click.option('--dont-use-publication-dir', is_flag=True, help='Force directory processing to place HTML next to MD files')
@click.pass_context
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
def md_render_command(ctx, input_file, output, template, css, edit, editor_theme, keyboard_shortcuts, use_publication_dir, dont_use_publication_dir):
"""
Generate HTML with client-side JavaScript markdown rendering.
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
Creates self-contained HTML files that include markdown content as JavaScript data
and render in the browser using client-side markdown parsing with marked.js.
Supports both single files and directory processing.
The generated HTML includes:
Embedded markdown content as JavaScript payload
Client-side rendering with marked.js from CDN
YAML front matter support and metadata extraction
Multiple responsive template options
Custom CSS injection capability
Optional instant editing capabilities with --edit flag
Graceful fallback if JavaScript fails
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
INPUT_FILE: Path to the markdown file or directory to render
Publication Directory:
Default publication directory: ~/Notes/
Override with MARKITECT_PUBLICATION_DIR environment variable
Single files: HTML generated next to MD file by default
Directories: HTML generated in publication directory with preserved structure
Flags:
--use-publication-dir: Force single files to use publication directory
--dont-use-publication-dir: Force directory processing to place HTML next to MD files
Available Templates:
basic (default) - Clean, minimal design with system fonts
github - GitHub-style appearance with heading underlines
academic - Academic paper style with serif fonts and justified text
dark - GitHub dark mode inspired theme with dark background
Examples:
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Single file - HTML next to MD file
markitect md-render README.md
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Single file - HTML in publication directory
markitect md-render README.md --use-publication-dir
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Directory - HTML in publication directory with structure
markitect md-render docs/
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Directory - HTML next to each MD file
markitect md-render docs/ --dont-use-publication-dir
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Custom publication directory
MARKITECT_PUBLICATION_DIR=/tmp/pub markitect md-render docs/
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Directory with custom template
markitect md-render docs/ --template github --edit
"""
config = ctx.obj or {}
try:
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
input_path = Path(input_file)
# Validate flags
if use_publication_dir and dont_use_publication_dir:
click.echo("Error: Cannot use both --use-publication-dir and --dont-use-publication-dir flags together", err=True)
raise click.Abort()
# Get publication directory
publication_dir = get_publication_directory()
if config.get('verbose', False):
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
click.echo(f"Input: {input_path}")
click.echo(f"Publication directory: {publication_dir}")
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Check if input is a directory or file
if input_path.is_dir():
# Directory processing
use_pub_dir = not dont_use_publication_dir # Default to publication dir for directories
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
if config.get('verbose', False):
click.echo(f"Processing directory: {input_path}")
click.echo(f"Use publication directory: {use_pub_dir}")
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
# Find all markdown files
md_files = find_markdown_files(input_path)
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
if not md_files:
click.echo(f"No markdown files found in directory: {input_path}")
return
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
processed_count = 0
for md_file in md_files:
try:
# Determine output path for this file
if use_pub_dir:
ensure_publication_directory(publication_dir)
output_path = get_relative_output_path(md_file, input_path, publication_dir)
# Ensure subdirectory exists
output_path.parent.mkdir(parents=True, exist_ok=True)
else:
output_path = md_file.with_suffix('.html')
# Process the markdown file
_render_single_markdown_file(
md_file, output_path, template, css, edit, editor_theme,
keyboard_shortcuts, config
)
processed_count += 1
if config.get('verbose', False):
click.echo(f"{md_file}{output_path}")
except Exception as e:
click.echo(f" ✗ Error processing {md_file}: {e}", err=True)
click.echo(f"✓ Processed {processed_count} markdown file(s)")
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
else:
# Single file processing
use_pub_dir = use_publication_dir # Default to next to file for single files
if config.get('verbose', False):
click.echo(f"Processing single file: {input_path}")
click.echo(f"Use publication directory: {use_pub_dir}")
# Determine output path
if output:
output_path = Path(output)
elif use_pub_dir:
ensure_publication_directory(publication_dir)
output_path = publication_dir / get_output_filename(input_path)
else:
output_path = input_path.with_suffix('.html')
# Process the single file
_render_single_markdown_file(
input_path, output_path, template, css, edit, editor_theme,
keyboard_shortcuts, config
)
click.echo(f"✓ HTML generated: {output_path}")
except Exception as e:
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
click.echo(f"Error: {e}", err=True)
raise click.Abort()
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
def _render_single_markdown_file(input_path, output_path, template, css, edit, editor_theme, keyboard_shortcuts, config):
"""Render a single markdown file to HTML."""
# Read markdown file
markdown_content = input_path.read_text(encoding='utf-8')
# Extract front matter if present
front_matter = {}
if markdown_content.startswith('---\n'):
parts = markdown_content.split('---\n', 2)
if len(parts) >= 3:
try:
import yaml
front_matter = yaml.safe_load(parts[1]) or {}
markdown_content = parts[2]
except ImportError:
# Fallback without yaml parsing
pass
# Generate title from first heading or filename
title = front_matter.get('title', input_path.stem)
lines = markdown_content.strip().split('\n')
for line in lines:
if line.startswith('# '):
title = line[2:].strip()
break
# Load custom CSS if provided
css_content = ""
if css:
css_path = Path(css)
css_content = css_path.read_text(encoding='utf-8')
# Generate HTML with embedded markdown
html_content = generate_html_with_embedded_markdown(
markdown_content, title, template, css_content, front_matter, edit, editor_theme, keyboard_shortcuts
)
# Ensure output directory exists
output_path.parent.mkdir(parents=True, exist_ok=True)
# Write HTML file
output_path.write_text(html_content, encoding='utf-8')
# Template definitions for cleaner code organization
TEMPLATE_STYLES = {
'basic': {
'body_color': '#333',
'body_bg': '',
'heading_color': '#2c3e50',
'heading_border': '',
'code_bg': '#f4f4f4',
'code_border': '',
'blockquote_border': '#ddd',
'blockquote_color': '#666',
'font_family': '-apple-system, BlinkMacSystemFont, \'Segoe UI\', \'Roboto\', \'Helvetica\', \'Arial\', sans-serif',
'max_width': '800px',
'text_align': ''
},
'github': {
'body_color': '#24292e',
'body_bg': 'background-color: #ffffff;',
'heading_color': '#1f2328',
'heading_border': 'border-bottom: 1px solid #d0d7de; padding-bottom: 0.3em;',
'code_bg': '#f4f4f4',
'code_border': '',
'blockquote_border': '#ddd',
'blockquote_color': '#666',
'font_family': '-apple-system, BlinkMacSystemFont, \'Segoe UI\', \'Roboto\', \'Helvetica\', \'Arial\', sans-serif',
'max_width': '800px',
'text_align': ''
},
'academic': {
'body_color': '#333',
'body_bg': '',
'heading_color': '#2c3e50',
'heading_border': '',
'code_bg': '#f4f4f4',
'code_border': '',
'blockquote_border': '#ddd',
'blockquote_color': '#666',
'font_family': '"Times New Roman", Times, serif',
'max_width': '900px',
'text_align': 'text-align: justify;'
},
'dark': {
'body_color': '#e1e4e8',
'body_bg': 'background-color: #0d1117;',
'heading_color': '#58a6ff',
'heading_border': 'border-bottom: 1px solid #21262d; padding-bottom: 0.3em;',
'code_bg': '#161b22',
'code_border': 'border: 1px solid #21262d;',
'blockquote_border': '#58a6ff',
'blockquote_color': '#8b949e',
'font_family': '-apple-system, BlinkMacSystemFont, \'Segoe UI\', \'Roboto\', \'Helvetica\', \'Arial\', sans-serif',
'max_width': '800px',
'text_align': ''
}
}
def generate_html_with_embedded_markdown(markdown_content, title, template, css_content, front_matter, edit=False, editor_theme='light', keyboard_shortcuts=False):
"""Generate HTML with embedded markdown content for client-side rendering.
Args:
markdown_content: The markdown content to embed
title: Page title
template: Template name (basic, github, academic, dark)
css_content: Custom CSS content to inject
front_matter: YAML front matter dictionary
edit: Enable editing capabilities
editor_theme: Editor theme (light or dark)
keyboard_shortcuts: Enable keyboard shortcuts
"""
# Get template styles or default to basic
styles = TEMPLATE_STYLES.get(template, TEMPLATE_STYLES['basic'])
# Build editor styles if editing is enabled
editor_styles = ""
if edit:
editor_styles = '''
/* Markitect Editor Styles */
.markitect-floating-header {{
position: fixed;
top: 10px;
right: 10px;
background: rgba(0, 123, 255, 0.9);
color: white;
padding: 10px 20px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
box-shadow: 0 2px 10px rgba(0,0,0,0.2);
z-index: 1000;
display: none;
}}
.markitect-floating-header.show {{
display: block;
}}
.markitect-section-editable {{
position: relative;
cursor: pointer;
transition: background-color 0.2s;
}}
.markitect-section-editable:hover {{
background-color: rgba(0, 123, 255, 0.1);
}}
.markitect-section-modified {{
border-left: 4px solid #007bff;
padding-left: 16px;
}}
.markitect-edit-interface {{
margin: 15px 0;
padding: 20px;
border: 2px dashed #007bff;
border-radius: 8px;
background: #f8f9fa;
}}
.markitect-edit-textarea {{
width: 100%;
min-height: 150px;
font-family: 'Courier New', Consolas, monospace;
font-size: 14px;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
resize: vertical;
}}
.markitect-edit-actions {{
margin-top: 10px;
text-align: right;
}}
.markitect-edit-btn {{
margin-left: 10px;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}}
.markitect-btn-apply {{
background-color: #28a745;
color: white;
}}
.markitect-btn-reset {{
background-color: #ffc107;
color: #212529;
}}
.markitect-btn-cancel {{
background-color: #6c757d;
color: white;
}}
.markitect-btn-save {{
background-color: #007bff;
color: white;
padding: 10px 20px;
margin-left: 15px;
}}
'''
if editor_theme == 'dark':
editor_styles += '''
/* Dark theme overrides */
.markitect-edit-interface {{
background: #2d2d2d;
border-color: #666;
}}
.markitect-edit-textarea {{
background: #1a1a1a;
color: #f0f0f0;
border-color: #666;
}}
'''
# HTML template with style variables
html_template = '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{title}</title>
<style>
body {{
font-family: {font_family};
line-height: 1.6;
max-width: {max_width};
margin: 0 auto;
padding: 20px;
color: {body_color};
{body_bg}
{text_align}
}}
#markdown-content {{
margin: 0;
}}
h1, h2, h3, h4, h5, h6 {{
color: {heading_color};
{heading_border}
}}
pre {{
background-color: {code_bg};
{code_border}
padding: 15px;
border-radius: 5px;
overflow-x: auto;
}}
code {{
background-color: {code_bg};
{code_border}
padding: 2px 4px;
border-radius: 3px;
}}
blockquote {{
border-left: 4px solid {blockquote_border};
margin: 0;
padding-left: 20px;
color: {blockquote_color};
}}
{css_content}
{editor_styles}
</style>
</head>
<body>
<div id="markdown-content"></div>
{editor_html}
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<script>
// Embedded markdown payload
const markdownContent = {markdown_json};
const frontMatter = {front_matter_json};
{editor_config}
// Render markdown on page load
document.addEventListener('DOMContentLoaded', function() {{
if (typeof marked !== 'undefined') {{
document.getElementById('markdown-content').innerHTML = marked.parse(markdownContent);
}} else {{
// Fallback if marked.js fails to load
document.getElementById('markdown-content').innerHTML =
'<pre>' + markdownContent.replace(/</g, '&lt;').replace(/>/g, '&gt;') + '</pre>';
}}
}});
</script>
{editor_scripts}
</body>
</html>'''
# Build editor HTML components if editing is enabled
editor_html = ""
editor_scripts = ""
editor_config = ""
if edit:
editor_config = '''
// Editor configuration
window.MARKITECT_EDIT_MODE = true;
window.MARKITECT_EDITOR_CONFIG = {
theme: \'''' + editor_theme + '''\',
keyboardShortcuts: ''' + ('true' if keyboard_shortcuts else 'false') + '''
};'''
editor_html = '''
<!-- Floating header for change tracking -->
<div id="markitect-floating-header" class="markitect-floating-header">
<span id="markitect-change-count">0 sections changed</span>
<button class="markitect-edit-btn markitect-btn-save" onclick="MarkitectEditor.saveDocument()">Save Document</button>
</div>
'''
# Basic JavaScript editor implementation
editor_scripts = '''
<script>
// Basic Markitect Editor Implementation
class MarkitectEditor {
constructor(markdownContent, containerId) {
this.originalContent = markdownContent;
this.modifiedSections = new Map();
this.container = document.getElementById(containerId);
this.changeCount = 0;
this.init();
}
init() {
this.setupSectionHandlers();
this.createFloatingHeader();
}
setupSectionHandlers() {
// Add click handlers to rendered sections
const sections = this.container.querySelectorAll('h1, h2, h3, h4, h5, h6, p, ul, ol, blockquote, pre');
sections.forEach((section, index) => {
section.classList.add('markitect-section-editable');
section.setAttribute('data-section-id', `section-${index}`);
section.addEventListener('click', (e) => this.enableSectionEditing(e.target));
});
}
createFloatingHeader() {
this.floatingHeader = document.getElementById('markitect-floating-header');
this.changeCountElement = document.getElementById('markitect-change-count');
}
enableSectionEditing(section) {
// Prevent multiple edit interfaces
if (document.querySelector('.markitect-edit-interface')) {
return;
}
const sectionId = section.getAttribute('data-section-id');
const originalHtml = section.outerHTML;
// Extract approximate markdown for this section
let sectionMarkdown = this.extractSectionMarkdown(section);
// Create edit interface
const editInterface = document.createElement('div');
editInterface.className = 'markitect-edit-interface';
editInterface.innerHTML = `
<div style="margin-bottom: 10px; font-weight: bold;">Editing ${section.tagName.toLowerCase()}:</div>
<div style="margin-bottom: 10px; padding: 10px; background: #e9ecef; border-radius: 4px;">
${originalHtml}
</div>
<textarea class="markitect-edit-textarea" placeholder="Enter markdown for this section...">${sectionMarkdown}</textarea>
<div class="markitect-edit-actions">
<button class="markitect-edit-btn markitect-btn-cancel" onclick="MarkitectEditor.cancelEdit('${sectionId}')">Cancel</button>
<button class="markitect-edit-btn markitect-btn-reset" onclick="MarkitectEditor.resetSection('${sectionId}')">Reset</button>
<button class="markitect-edit-btn markitect-btn-apply" onclick="MarkitectEditor.applyChanges('${sectionId}')">Apply</button>
</div>
`;
// Insert edit interface after the section
section.parentNode.insertBefore(editInterface, section.nextSibling);
editInterface.querySelector('textarea').focus();
}
extractSectionMarkdown(section) {
// Basic extraction - convert HTML back to approximate markdown
const tagName = section.tagName.toLowerCase();
let text = section.textContent || section.innerText || '';
switch(tagName) {
case 'h1': return `# ${text}`;
case 'h2': return `## ${text}`;
case 'h3': return `### ${text}`;
case 'h4': return `#### ${text}`;
case 'h5': return `##### ${text}`;
case 'h6': return `###### ${text}`;
case 'p': return text;
case 'blockquote': return `> ${text}`;
case 'pre': return `\\`\\`\\`\\n${text}\\n\\`\\`\\``;
default: return text;
}
}
static applyChanges(sectionId) {
const editInterface = document.querySelector('.markitect-edit-interface');
const textarea = editInterface.querySelector('textarea');
const newMarkdown = textarea.value;
// Find the original section
const section = document.querySelector(`[data-section-id="${sectionId}"]`);
// Parse new markdown and update section
if (typeof marked !== 'undefined') {
const newHtml = marked.parse(newMarkdown);
const tempDiv = document.createElement('div');
tempDiv.innerHTML = newHtml;
// Replace section content
if (tempDiv.firstElementChild) {
const newSection = tempDiv.firstElementChild;
newSection.classList.add('markitect-section-editable', 'markitect-section-modified');
newSection.setAttribute('data-section-id', sectionId);
newSection.addEventListener('click', (e) => window.markitectEditor.enableSectionEditing(e.target));
section.parentNode.replaceChild(newSection, section);
}
}
// Track change
window.markitectEditor.modifiedSections.set(sectionId, newMarkdown);
window.markitectEditor.updateChangeCount();
// Remove edit interface
editInterface.remove();
}
static cancelEdit(sectionId) {
const editInterface = document.querySelector('.markitect-edit-interface');
editInterface.remove();
}
static resetSection(sectionId) {
const textarea = document.querySelector('.markitect-edit-interface textarea');
const section = document.querySelector(`[data-section-id="${sectionId}"]`);
textarea.value = window.markitectEditor.extractSectionMarkdown(section);
}
updateChangeCount() {
this.changeCount = this.modifiedSections.size;
this.changeCountElement.textContent = `${this.changeCount} section${this.changeCount !== 1 ? 's' : ''} changed`;
if (this.changeCount > 0) {
this.floatingHeader.classList.add('show');
} else {
this.floatingHeader.classList.remove('show');
}
}
static saveDocument() {
// Generate modified markdown document
let modifiedDocument = window.markdownContent;
// This is a simplified implementation
// In a full implementation, we would properly reconstruct the document
// Create download
const blob = new Blob([modifiedDocument], { type: 'text/markdown' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'modified-document.md';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
alert('Document download initiated! Note: This is a basic implementation.');
}
}
// Initialize editor when page loads if edit mode is enabled
document.addEventListener('DOMContentLoaded', function() {
if (window.MARKITECT_EDIT_MODE) {
// Wait for markdown to render first
setTimeout(() => {
window.markitectEditor = new MarkitectEditor(markdownContent, 'markdown-content');
}, 100);
}
});
// Keyboard shortcuts
if (window.MARKITECT_EDITOR_CONFIG && window.MARKITECT_EDITOR_CONFIG.keyboardShortcuts) {
document.addEventListener('keydown', function(e) {
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case 's':
e.preventDefault();
MarkitectEditor.saveDocument();
break;
case 'z':
// Undo functionality could be implemented here
break;
}
}
if (e.key === 'Escape') {
const editInterface = document.querySelector('.markitect-edit-interface');
if (editInterface) {
editInterface.remove();
}
}
});
}
</script>
'''
# Format template with styles and content
return html_template.format(
title=title,
css_content=css_content,
editor_styles=editor_styles,
editor_html=editor_html,
editor_scripts=editor_scripts,
editor_config=editor_config,
markdown_json=json.dumps(markdown_content),
front_matter_json=json.dumps(front_matter),
**styles
feat: implement instant markdown base and publication directory - Issue #135 Complete TDD8 implementation of publication directory support for md-render command: CORE FEATURES: • Publication directory management with ~/Notes/ default • MARKITECT_PUBLICATION_DIR environment variable override • Single file processing with --use-publication-dir flag • Directory processing with --dont-use-publication-dir flag • Recursive directory traversal with structure preservation • Automatic directory creation and path normalization IMPLEMENTATION DETAILS: • Extended md-render command with new CLI flags • Added 9 new helper functions for directory/file processing • Support for both single files and directory inputs • Comprehensive error handling and validation • Maintains backward compatibility CLI FLAGS ADDED: • --use-publication-dir: Force single files to use publication directory • --dont-use-publication-dir: Force directory processing to place HTML next to MD BEHAVIOR: • Single files: HTML next to MD by default, publication dir with flag • Directories: HTML in publication dir by default, next to MD with flag • Environment variable MARKITECT_PUBLICATION_DIR overrides default TESTING: • 18 comprehensive tests covering all functionality • Publication directory management (4 tests) • Single file processing (3 tests) • Directory processing (4 tests) • CLI integration (4 tests) • Edge cases (3 tests) • 100% test pass rate TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-07 12:47:59 +02:00
)
# Publication directory management functions for Issue #135
def get_publication_directory():
"""Get the publication directory from environment variable or default."""
pub_dir = os.environ.get('MARKITECT_PUBLICATION_DIR')
if pub_dir:
return normalize_publication_path(pub_dir)
return Path.home() / "Notes"
def normalize_publication_path(path_str):
"""Normalize publication directory path with tilde expansion and absolute resolution."""
path = Path(path_str)
if str(path).startswith('~'):
path = path.expanduser()
return path.resolve()
def ensure_publication_directory(pub_dir):
"""Ensure publication directory exists, creating it if necessary."""
pub_dir = Path(pub_dir)
pub_dir.mkdir(parents=True, exist_ok=True)
return pub_dir
def get_output_filename(input_file):
"""Get HTML output filename from markdown input filename."""
return input_file.stem + ".html"
def find_markdown_files(directory):
"""Recursively find all markdown files in a directory."""
directory = Path(directory)
md_files = []
for pattern in ['*.md', '*.markdown']:
md_files.extend(directory.rglob(pattern))
return sorted(md_files)
def get_relative_output_path(source_file, base_dir, output_dir):
"""Calculate relative output path preserving directory structure."""
source_file = Path(source_file)
base_dir = Path(base_dir)
output_dir = Path(output_dir)
# Get relative path from base directory
relative_path = source_file.relative_to(base_dir)
# Change extension to .html
relative_path = relative_path.with_suffix('.html')
# Combine with output directory
return output_dir / relative_path
def process_single_file(input_file, use_publication_dir, publication_dir):
"""Process a single markdown file, generate HTML, and return the output path."""
input_file = Path(input_file)
if not input_file.exists():
raise FileNotFoundError(f"Input file not found: {input_file}")
if use_publication_dir:
ensure_publication_directory(publication_dir)
output_file = publication_dir / get_output_filename(input_file)
else:
output_file = input_file.with_suffix('.html')
# Actually generate the HTML file
_render_single_markdown_file(
input_file, output_file, 'basic', None, False, 'light', False, {}
)
return output_file
def process_directory(input_dir, use_publication_dir, publication_dir):
"""Process all markdown files in a directory, generate HTML files, and return list of output paths."""
input_dir = Path(input_dir)
if not input_dir.exists() or not input_dir.is_dir():
raise NotADirectoryError(f"Input directory not found: {input_dir}")
md_files = find_markdown_files(input_dir)
output_files = []
for md_file in md_files:
if use_publication_dir:
ensure_publication_directory(publication_dir)
output_file = get_relative_output_path(md_file, input_dir, publication_dir)
# Ensure subdirectory exists
output_file.parent.mkdir(parents=True, exist_ok=True)
else:
output_file = md_file.with_suffix('.html')
# Actually generate the HTML file
_render_single_markdown_file(
md_file, output_file, 'basic', None, False, 'light', False, {}
)
output_files.append(output_file)
return output_files