diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..74e374d --- /dev/null +++ b/docs/api.md @@ -0,0 +1,223 @@ +# API Documentation + +This document provides detailed documentation for the GemReader application's internal APIs and code structure. + +## Package Structure + +- `main` (cmd/gemreader/main.go): Application entry point and command-line interface +- `config` (internal/config/config.go): Configuration loading and management +- `tui` (internal/tui/tui.go): Terminal user interface implementation + +## Main Package + +### main.go + +#### Execute() function + +```go +func Execute() +``` + +- Initializes and executes the Cobra command +- Handles command execution errors +- Exits the application with appropriate error code if execution fails + +#### rootCmd variable + +```go +var rootCmd = &cobra.Command +``` + +- Main Cobra command for the application +- Uses format: `gemreader [file]` +- Allows 0 or 1 arguments (the markdown file to view) +- Handles file reading and TUI initialization +- If no file is provided, attempts to use the default file from config + +#### validateFilePath() function + +```go +func validateFilePath(inputPath string) (string, error) +``` + +- Validates file paths to prevent directory traversal attacks +- Cleans and resolves absolute paths +- Ensures the resolved path is within the current working directory +- Returns error if path traversal is detected + +#### validateFile() function + +```go +func validateFile(filePath string) error +``` + +- Performs security checks on files before loading +- Validates file size (max 10MB) +- Ensures file is a supported type (.md, .markdown, .txt, or no extension) + +## Config Package + +### config.go + +#### Config struct + +```go +type Config struct { + Title string `mapstructure:"title"` + DefaultFile string `mapstructure:"default_file"` + ShowTOC bool `mapstructure:"show_toc"` +} +``` + +- Stores application configuration +- Supports Title, DefaultFile, and ShowTOC fields +- Tagged for Viper configuration mapping + +#### LoadConfig() function + +```go +func LoadConfig() (config Config, err error) +``` + +- Loads configuration from config.toml file +- Searches for config file in current directory +- Supports TOML format +- Returns Config struct and error if any +- Uses Viper for configuration management +- Returns defaults if config file doesn't exist + +## TUI Package + +### tui.go + +#### model struct + +```go +type model struct { + content string + rawLines []string + toc []tocEntry + selectedTocIndex int + + tocViewport viewport.Model + contentViewport viewport.Model + + activePane pane + helpVisible bool + config config.Config + windowWidth int + windowHeight int +} +``` + +The model struct contains all state information for the TUI: + +- `content`: Original markdown content as string +- `rawLines`: Original content split by newlines for TOC generation +- `toc`: Table of contents entries +- `selectedTocIndex`: Index of currently selected TOC entry +- `tocViewport`: Bubble Tea viewport for TOC pane +- `contentViewport`: Bubble Tea viewport for content pane +- `activePane`: Currently active pane (TOC or content) +- `helpVisible`: Whether help text is visible +- `config`: Application configuration +- `windowWidth`, `windowHeight`: Terminal dimensions + +#### pane type + +```go +type pane int + +const ( + tocPane pane = iota + contentPane +) +``` + +- Represents the two panes in the UI +- `tocPane`: Left pane showing table of contents +- `contentPane`: Right pane showing markdown content + +#### tocEntry struct + +```go +type tocEntry struct { + level int + text string + line int +} +``` + +- Represents a single entry in the table of contents +- `level`: Header level (1 for #, 2 for ##, etc.) +- `text`: Text content of the header +- `line`: Line number in the original document + +#### NewModel() function + +```go +func NewModel(content string, cfg config.Config) model +``` + +- Creates a new TUI model with the given content and configuration +- Splits content into lines for TOC generation +- Generates table of contents from headers +- Initializes viewport models +- Sets up initial state with content pane as active + +#### generateTOC() function + +```go +func generateTOC(lines []string) []tocEntry +``` + +- Scans content lines for markdown headers +- Creates tocEntry for each header found +- Determines header level based on number of # characters +- Returns list of all headers in document order + +#### (m model) Init() method + +```go +func (m model) Init() tea.Cmd +``` + +- Bubble Tea initialization method +- Returns nil command (no initial command needed) + +#### (m model) Update() method + +```go +func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) +``` + +- Bubble Tea update method for handling messages and user input +- Handles window resize events and recalculates pane dimensions +- Processes keyboard input for navigation +- Switches between TOC and content panes with Tab key +- Handles TOC navigation and jumping to selected entries +- Updates both viewport models +- Supports all navigation commands + +#### (m model) View() method + +```go +func (m model) View() string +``` + +- Bubble Tea view method for rendering the UI +- Applies styles to active and inactive panes +- Renders both TOC and content panes side by side +- Shows help text at the bottom if visible +- Returns string representation of current UI state + +#### (m model) renderTOC() method + +```go +func (m model) renderTOC() string +``` + +- Renders the table of contents view +- Shows headers with proper indentation based on level +- Highlights the currently selected entry with '>' +- Shows "No table of contents found." if no headers exist diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..60e4f8f --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,161 @@ +# Project Architecture + +This document describes the architecture and structure of the GemReader application. + +## Overview + +GemReader is a terminal-based markdown viewer built with Go using the Bubble Tea framework for the terminal user interface. The application reads markdown files, renders them for terminal display, and provides navigation capabilities. + +## Architecture Pattern + +The application follows a modular architecture with clear separation of concerns: + +- **Presentation Layer**: Bubble Tea-based TUI in the `tui` package +- **Business Logic**: Navigation and content handling in the `tui` package +- **Configuration**: Viper-based configuration loading in the `config` package +- **Entry Point**: Command-line interface in the `main` package + +## Package Structure + +### `cmd/gemreader/main.go` + +The main entry point of the application that: + +- Defines the command-line interface using Cobra +- Handles command-line argument validation +- Reads the markdown file specified by the user +- Loads application configuration +- Initializes and runs the TUI + +### `internal/config/config.go` + +Configuration management that: + +- Defines the application configuration structure +- Loads configuration from config.toml using Viper +- Provides configuration data to other parts of the application +- Handles configuration loading errors gracefully + +### `internal/tui/tui.go` + +Terminal user interface implementation that: + +- Implements the Bubble Tea model for TUI functionality +- Handles all user interactions and navigation +- Manages dual-pane interface (TOC and content) +- Renders content for terminal display using viewports +- Generates and manages table of contents from markdown headers +- Provides pane switching functionality + +## Design Patterns + +### Model-View-Update (MVU) Pattern + +The TUI uses the Bubble Tea framework which implements the MVU pattern: + +- **Model**: The state of the application (model struct) +- **Update**: Functions that modify the state based on messages (keyboard input, etc.) +- **View**: Function that renders the current state to the terminal + +### Viewport Pattern + +The TUI uses dual viewports for the split-screen interface: + +- **TOC Viewport**: Dedicated scrolling region for table of contents +- **Content Viewport**: Dedicated scrolling region for markdown content +- Both viewports are managed by the Bubble Tea framework + +### Dependency Management + +The application uses Go modules for dependency management with the following key dependencies: + +- `github.com/charmbracelet/bubbletea`: TUI framework +- `github.com/charmbracelet/lipgloss`: Styling library for terminal interfaces +- `github.com/charmbracelet/bubbles/viewport`: Viewport component for scrolling +- `github.com/spf13/cobra`: Command-line interface framework +- `github.com/spf13/viper`: Configuration management +- `github.com/MichaelMure/go-term-markdown`: Markdown rendering for terminals + +## Data Flow + +1. **Initialization**: `main.go` parses command-line arguments and loads configuration +2. **Content Loading**: Markdown file is read and rendered for terminal display +3. **Model Creation**: TUI model is created with content and configuration +4. **Event Loop**: Bubble Tea enters event loop handling user input +5. **State Updates**: User actions update the model state via the Update method +6. **Rendering**: Changes to model trigger View method to refresh display + +## File Organization + +``` +gemreader/ +├── cmd/ +│ └── gemreader/ +│ └── main.go # Application entry point +├── internal/ +│ ├── config/ +│ │ └── config.go # Configuration loading +│ └── tui/ +│ └── tui.go # Terminal user interface +├── docs/ # Documentation files +│ ├── configuration.md +│ ├── navigation.md +│ ├── api.md +│ └── architecture.md +├── config.toml # Default configuration +├── sample.md # Sample markdown file +├── go.mod # Go module file +├── go.sum # Go module checksums +├── README.md # Main documentation file +└── CONTRIBUTING.md # Contribution guide +``` + +## Component Responsibilities + +### Main Component + +- Command-line argument parsing and validation +- File loading and error handling +- Configuration loading +- TUI initialization and execution + +### Config Component + +- Loading configuration from TOML file +- Providing configuration to other components +- Handling configuration errors gracefully + +### TUI Component + +- Managing application state +- Handling user input and navigation +- Rendering the dual-pane interface +- Managing pane switching between TOC and content +- Content display and positioning using viewports +- Generating and displaying table of contents + +## Security Considerations + +- The application only reads files specified by the user +- No network connections are made during normal operation +- Configuration loading is restricted to local files +- Input validation is handled by the underlying libraries + +## Performance Characteristics + +- Content is loaded entirely into memory at startup +- Navigation operations are performed in memory without file access +- Markdown rendering happens once at startup +- TOC generation happens once at startup +- Dual viewport system provides efficient scrolling in both panes + +## Extensibility Points + +The architecture supports extension through: + +- Configuration options (new fields in Config struct) +- Enhanced dual-pane functionality +- Additional keyboard controls (in Update method) +- New content processing features (in TUI package) +- Enhanced viewport behaviors and styling +- Additional TOC features and navigation options diff --git a/docs/building.md b/docs/building.md new file mode 100644 index 0000000..ae4f7f2 --- /dev/null +++ b/docs/building.md @@ -0,0 +1,353 @@ +# Building GemReader + +This document provides detailed instructions on how to build the GemReader application for different platforms, with a focus on creating executable files (especially .exe files for Windows). + +## Prerequisites + +Before building GemReader, ensure you have the following installed: + +- Go 1.24 or higher +- Git (for cloning the repository) +- A terminal/command prompt +- For Windows: A modern version of Windows that supports Go compilation + +## Building for Windows (.exe) + +### Basic Build + +To build the application for Windows and create an .exe file, run the following command: + +```bash +go build -o gemreader.exe cmd/gemreader/main.go +``` + +This will create a `gemreader.exe` file in your current directory. + +### Cross-compilation from Other Platforms + +If you're building from a non-Windows platform (Linux or macOS) and want to create a Windows executable, use these commands: + +```bash +GOOS=windows GOARCH=amd64 go build -o gemreader.exe cmd/gemreader/main.go +``` + +### Advanced Build Options + +#### Building with release flags (optimized) + +For a more optimized build with smaller file size: + +```bash +go build -ldflags "-s -w" -o gemreader.exe cmd/gemreader/main.go +``` + +- `-s`: Omit the symbol table and debug information +- `-w`: Omit the DWARF symbol table + +#### Building with version information + +To embed version information in the executable: + +```bash +go build -ldflags "-X main.Version=1.0.0 -s -w" -o gemreader.exe cmd/gemreader/main.go +``` + +## Building for Other Platforms + +This section covers building the application on Windows for other operating systems using Go's cross-compilation feature. + +### From Windows to Linux + +To build for Linux from a Windows machine, you have several options: + +**Option 1: Using PowerShell (Recommended)** + +```powershell +# For 64-bit Linux +$env:GOOS="linux"; $env:GOARCH="amd64"; go build -o gemreader-linux cmd/gemreader/main.go + +# For 32-bit Linux +$env:GOOS="linux"; $env:GOARCH="386"; go build -o gemreader-linux-386 cmd/gemreader/main.go + +# For ARM64 Linux +$env:GOOS="linux"; $env:GOARCH="arm64"; go build -o gemreader-linux-arm64 cmd/gemreader/main.go +``` + +**Option 2: Using Command Prompt (CMD)** +In Command Prompt, you need to set the environment variables persistently for the session: + +```batch +go env -w GOOS=linux +go env -w GOARCH=amd64 +go build -o gemreader-linux cmd/gemreader/main.go + +# Reset to default after building +go env -w GOOS=windows +go env -w GOARCH=amd64 +``` + +**Option 3: Temporary environment variables in PowerShell (One-liner)** + +```powershell +$env:GOOS="linux"; $env:GOARCH="amd64"; go build -o gemreader-linux cmd/gemreader/main.go; $env:GOOS="windows"; $env:GOARCH="amd64" +``` + +### From Windows to macOS + +To build for macOS from a Windows machine: + +**Using PowerShell:** + +```powershell +# For Intel-based Macs +$env:GOOS="darwin"; $env:GOARCH="amd64"; go build -o gemreader-mac cmd/gemreader/main.go + +# For Apple Silicon Macs (M1/M2/M3) +$env:GOOS="darwin"; $env:GOARCH="arm64"; go build -o gemreader-mac-arm64 cmd/gemreader/main.go +``` + +**Using Command Prompt:** + +```batch +go env -w GOOS=darwin +go env -w GOARCH=amd64 +go build -o gemreader-mac cmd/gemreader/main.go + +# Reset to default after building +go env -w GOOS=windows +go env -w GOARCH=amd64 +``` + +### Using PowerShell + +If you're using PowerShell instead of Command Prompt, the syntax is slightly different: + +```powershell +# For Linux ARM64 +$env:GOOS="linux"; $env:GOARCH="arm64"; go build -o gemreader-linux-arm64 cmd/gemreader/main.go + +# For macOS +$env:GOOS="darwin"; $env:GOARCH="arm64"; go build -o gemreader-mac-arm64 cmd/gemreader/main.go +``` + +### Common GOOS and GOARCH Values + +| GOOS | GOARCH | Platform | +| ------- | ------ | ---------------------------- | +| windows | amd64 | 64-bit Windows | +| windows | 386 | 32-bit Windows | +| windows | arm64 | 64-bit Windows on ARM | +| linux | amd64 | 64-bit Linux | +| linux | 386 | 32-bit Linux | +| linux | arm64 | 64-bit Linux on ARM | +| darwin | amd64 | 64-bit macOS (Intel) | +| darwin | arm64 | 64-bit macOS (Apple Silicon) | + +### Building for Multiple Platforms + +You can create a batch script to build for multiple platforms at once. Here's an example `build-all.bat`: + +```batch +@echo off +echo Building GemReader for multiple platforms... + +REM Create dist directory if it doesn't exist +if not exist dist mkdir dist + +echo Building for Windows (64-bit)... +go env -w GOOS=windows +go env -w GOARCH=amd64 +go build -ldflags "-s -w" -o dist/gemreader-windows-amd64.exe cmd/gemreader/main.go + +echo Building for Linux (64-bit)... +go env -w GOOS=linux +go env -w GOARCH=amd64 +go build -ldflags "-s -w" -o dist/gemreader-linux-amd64 cmd/gemreader/main.go + +echo Building for macOS (Intel)... +go env -w GOOS=darwin +go env -w GOARCH=amd64 +go build -ldflags "-s -w" -o dist/gemreader-darwin-amd64 cmd/gemreader/main.go + +echo Building for macOS (Apple Silicon)... +go env -w GOOS=darwin +go env -w GOARCH=arm64 +go build -ldflags "-s -w" -o dist/gemreader-darwin-arm64 cmd/gemreader/main.go + +echo Resetting GOOS and GOARCH to default... +go env -w GOOS=windows +go env -w GOARCH=amd64 + +echo Build process completed! +pause +``` + +Alternatively, you can create a PowerShell script `build-all.ps1` for more reliable cross-platform building: + +```powershell +Write-Host "Building GemReader for multiple platforms..." + +# Create dist directory if it doesn't exist +if (!(Test-Path dist)) { + New-Item -ItemType Directory -Path dist +} + +# Build for Windows +Write-Host "Building for Windows (64-bit)..." +$env:GOOS="windows" +$env:GOARCH="amd64" +go build -ldflags "-s -w" -o dist/gemreader-windows-amd64.exe cmd/gemreader/main.go + +# Build for Linux +Write-Host "Building for Linux (64-bit)..." +$env:GOOS="linux" +$env:GOARCH="amd64" +go build -ldflags "-s -w" -o dist/gemreader-linux-amd64 cmd/gemreader/main.go + +# Build for macOS Intel +Write-Host "Building for macOS (Intel)..." +$env:GOOS="darwin" +$env:GOARCH="amd64" +go build -ldflags "-s -w" -o dist/gemreader-darwin-amd64 cmd/gemreader/main.go + +# Build for macOS Apple Silicon +Write-Host "Building for macOS (Apple Silicon)..." +$env:GOOS="darwin" +$env:GOARCH="arm64" +go build -ldflags "-s -w" -o dist/gemreader-darwin-arm64 cmd/gemreader/main.go + +Write-Host "Build process completed!" +``` + +## Using Build Scripts + +For more complex builds, you can create a build script. Here's an example `build.bat` for Windows: + +```batch +@echo off +echo Building GemReader for Windows... + +REM Build the application +go build -ldflags "-s -w" -o gemreader.exe cmd/gemreader/main.go + +if %ERRORLEVEL% == 0 ( + echo Build successful! gemreader.exe has been created. +) else ( + echo Build failed! + exit /b %ERRORLEVEL% +) +``` + +And a `build.sh` script for Unix-like systems: + +```bash +#!/bin/bash +echo "Building GemReader..." + +# Build the application +go build -ldflags "-s -w" -o gemreader cmd/gemreader/main.go + +if [ $? -eq 0 ]; then + echo "Build successful! gemreader has been created." +else + echo "Build failed!" + exit 1 +fi +``` + +## Build Targets with Go Releaser (Alternative Approach) + +If the project uses GoReleaser or similar tools, you can use: + +```bash +# Install GoReleaser if not already installed +go install github.com/goreleaser/goreleaser@latest + +# Build all targets defined in .goreleaser.yml +goreleaser build --single-target --snapshot +``` + +## Troubleshooting Build Issues + +### Common Issues + +#### Missing Dependencies + +If you encounter dependency issues, run: + +```bash +go mod tidy +``` + +This will ensure all dependencies are downloaded and the go.mod file is updated. + +#### CGO Issues + +If your build fails due to CGO issues (uncommon for this project), you can disable CGO: + +```bash +CGO_ENABLED=0 go build -o gemreader.exe cmd/gemreader/main.go +``` + +#### Large Executable Size + +If the resulting executable is too large, consider these options: + +1. Use the `-ldflags "-s -w"` options as mentioned above + +2. Use UPX to compress the executable (third-party tool): + + ```bash + upx --best gemreader.exe + ``` + +### Checking Build Information + +To verify your build information, you can use: + +```bash +go version -m gemreader.exe +``` + +This will show build information about the executable. + +## Production Builds + +For production releases, consider using these flags to create a minimal executable: + +```bash +go build \ + -ldflags="-s -w -X main.Version=1.0.0" \ + -o gemreader.exe \ + cmd/gemreader/main.go +``` + +## Verifying the Build + +After building, you can test your executable by running: + +```bash +# On Windows +.\gemreader.exe sample.md + +# Or with a different markdown file +.\gemreader.exe path\to\your\markdown\file.md +``` + +## Build Artifacts + +The build process will create: + +- `gemreader.exe` (or platform-specific executable) +- The executable is self-contained and doesn't require additional files to run +- Configuration files like `config.toml` and `sample.md` can be distributed separately if needed + +## Creating an Installer (Optional) + +For distribution purposes, you may want to create an installer for Windows using tools like: + +- NSIS (Nullsoft Scriptable Install System) +- Inno Setup +- GoReleaser with nfpm for cross-platform packaging + +This is outside the scope of this document but worth considering for production releases. \ No newline at end of file diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..83b5743 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,63 @@ +# Configuration Guide + +GemReader uses a TOML-based configuration file to customize the application behavior. The configuration file is named `config.toml` and should be placed in the same directory where you run the application. + +## Default Configuration File + +The default `config.toml` file looks like this: + +```toml +title = "GemReader" +``` + +## Available Configuration Options + +### title + +- **Type**: String +- **Default**: "GemReader" +- **Description**: Sets the title displayed at the top of the application interface + +### default_file + +- **Type**: String +- **Default**: "" (empty) +- **Description**: Sets the default markdown file to open when no file is provided as a command-line argument + +## Configuration Loading + +The application attempts to load the configuration file when it starts. If the file is not found or contains errors, the application will continue to run with default settings. + +The configuration is loaded using the [Viper](https://github.com/spf13/viper) library, which supports: + +1. Loading from `config.toml` in the current directory +2. Environment variable overrides (with `GEMREADER_` prefix) +3. Default fallback values + +## Example Advanced Configuration + +While the current version only supports the title configuration, future versions may support additional options: + +```toml +title = "My Markdown Viewer" + +# These options are planned for future releases: +# theme = "dark" # Color theme (dark/light) +# toc_enabled = true # Whether to generate table of contents +# line_numbers = false # Show line numbers +# wrap_text = true # Wrap long lines +``` + +## Troubleshooting + +### Configuration File Not Found + +If the application starts but the custom title isn't displayed, ensure that: + +1. The `config.toml` file exists in the current working directory +2. The file has the correct format and valid TOML syntax +3. The application has read permissions for the file + +### Invalid Configuration + +If the configuration file contains invalid TOML syntax, the application will log an error and use default values instead. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..e6a87da --- /dev/null +++ b/docs/index.md @@ -0,0 +1,42 @@ +# GemReader Documentation + +Welcome to the GemReader documentation. This collection of documents provides comprehensive information about the application. + +## Table of Contents + +### Getting Started + +- [README](../README.md) - Main project overview, installation, and usage +- [Configuration Guide](configuration.md) - How to configure the application + +### User Guides + +- [Navigation & Controls](navigation.md) - Detailed keyboard controls and navigation + +### Development & Building + +- [Building Guide](building.md) - Instructions for building the application for different platforms + +### Technical Documentation + +- [API Documentation](api.md) - Internal API and code structure +- [Architecture](architecture.md) - Project architecture and design patterns + +### Contributing + +- [Contribution Guide](../CONTRIBUTING.md) - How to contribute to the project + +## Quick Links + +- [Report an Issue](https://github.com/your-repo/gemreader/issues) +- [Source Code](https://github.com/your-repo/gemreader) +- [Go Documentation](https://pkg.go.dev/gemreader) + +## Need Help? + +If you can't find what you're looking for in these documents: + +1. Check the [README](../README.md) for basic usage information +2. Review the [Navigation Guide](navigation.md) for help with controls +3. Look at the [Building Guide](building.md) for compilation instructions +4. Open an issue in the GitHub repository