Thank you for your interest in contributing to Yole! This document provides guidelines for contributing to this cross-platform text editor project.
- Android Studio latest stable version (for Android development)
- Xcode (for iOS development)
- IntelliJ IDEA or similar (for Desktop/Web development)
- Git for version control
- Kotlin 2.0.20 (managed by Gradle)
- Java 11+ (JDK 11 for shared module, JDK 21 for desktopApp)
- Android SDK 24-35 (API levels)
- Go 1.24+ (for Challenges, Containers, and sibling Go modules)
- Docker or Podman (for release builds and security scanning)
- Fork the repository on GitHub
- Clone your fork locally:
git clone --recursive https://github.com/yourusername/yole.git cd yole - Ensure the 10 extracted KMP modules are available as sibling directories (see Composite Build Setup below)
- Open the project in your preferred IDE
- Let Gradle sync and download dependencies
- Build the project to verify setup:
./gradlew :shared:desktopTest # Primary test command (no Android SDK needed) ./gradlew :desktopApp:run # Desktop app ./gradlew :androidApp:assembleDebug # Android (requires ANDROID_SDK_ROOT) ./gradlew :webApp:wasmJsBrowserRun # Web (Wasm)
Yole consumes 10 extracted Kotlin Multiplatform modules via includeBuild() in settings.gradle.kts. Each module lives in a sibling directory:
parent-directory/
Yole/ # This repository
RateLimiter-KMP/ # digital.vasic.ratelimiter
Concurrency-KMP/ # digital.vasic.concurrency
UI-Components-KMP/ # digital.vasic.uicomponents
Auth-KMP/ # digital.vasic.auth
Security-KMP/ # digital.vasic.security
Document-KMP/ # digital.vasic.document
Config-KMP/ # digital.vasic.config
Database-KMP/ # digital.vasic.database
Storage-KMP/ # digital.vasic.storage
Formatters-KMP/ # digital.vasic.formatters
Clone each module from its repository. Gradle will automatically resolve them via composite builds. If a module directory is missing, the build will fail with a clear error indicating which includeBuild() path could not be found.
When contributing to an extracted module, make changes in the module's own repository and test integration by running ./gradlew :shared:desktopTest from the Yole root.
main: Stable release branchdevelop: Development branch (if exists)feature/*: Feature-specific branchesbugfix/*: Bug fix brancheshotfix/*: Critical fixes for releases
- Follow Kotlin coding conventions
- Use camelCase for variables and functions
- Use PascalCase for classes and objects
- Use UPPER_SNAKE_CASE for constants
- Keep lines under 120 characters when possible
- Standard Android imports
- AndroidX imports
- Third-party library imports
- Project imports (
digital.vasic.yole.*,net.gsantner.opoc.*)
All source files must include SPDX license header:
/*
* SPDX-FileCopyrightText: 2017-2025 Gregor Santner <gsantner AT mailbox DOT org>
* SPDX-FileCopyrightText: 2025 Your Name <your@email.com>
* SPDX-License-Identifier: Apache 2.0
*/# Run all tests
./gradlew testFlavorDefaultDebugUnitTest
# Run specific test
./gradlew testFlavorDefaultDebugUnitTest --tests "digital.vasic.yole.format.todotxt.TodoTxtQuerySyntaxTests.ParseQuery"
# Run tests for specific module
./gradlew :format-markdown:testDebugUnitTest- Use JUnit 4 with AssertJ assertions
- Test class names should end with
TestsorTest - Write unit tests for business logic in shared modules
- Write instrumented tests for Android-specific code
- Aim for high test coverage on core functionality
IMPORTANT: This project uses Kotlin DSL only for Gradle build configuration.
- ✅ Use
build.gradle.kts(Kotlin DSL) - ❌ Do NOT use
build.gradle(Groovy DSL) - All modules must use
.gradle.ktsfiles - Legacy Groovy build files have been removed
Rationale: Kotlin DSL provides:
- Type-safe build scripts
- Better IDE support
- Kotlin language features
- Consistency with project language
# Build debug APK
./gradlew assembleFlavorDefaultDebug
# Build release APK
./gradlew assembleFlavorDefaultRelease
# Run lint checks
./gradlew lintFlavorDefaultDebug
# Clean build
./gradlew clean- Run tests: Ensure all tests pass
- Run lint: Fix any lint warnings/errors
- Format code: Use Android Studio's auto-reformat
- Build successfully: Verify project builds without errors
- Test manually: Test your changes on device/emulator
Yole uses Kotlin Multiplatform (KMP) for cross-platform development:
├── shared/ # Shared KMP code (PRIMARY MODULE)
│ ├── src/
│ │ ├── commonMain/ # Common Kotlin code
│ │ │ └── kotlin/digital/vasic/yole/
│ │ │ ├── format/ # Format system
│ │ │ │ ├── FormatRegistry.kt
│ │ │ │ ├── TextParser.kt
│ │ │ │ ├── markdown/MarkdownParser.kt
│ │ │ │ ├── todotxt/TodoTxtParser.kt
│ │ │ │ └── ... (all 18 format parsers)
│ │ │ └── model/ # Document model
│ │ ├── androidMain/ # Android-specific implementations
│ │ ├── desktopMain/ # Desktop-specific implementations
│ │ ├── iosMain/ # iOS-specific implementations
│ │ └── wasmJsMain/ # Web-specific implementations
│
├── androidApp/ # Android application (Compose UI)
├── desktopApp/ # Desktop application (Compose Desktop)
├── iosApp/ # iOS application (SwiftUI + KMP)
├── webApp/ # Web application (Compose for Web)
│
├── commons/ # Android-specific utilities
└── core/ # Legacy encryption utilities
digital.vasic.yole.format.*: Format system (parsers, registry)digital.vasic.yole.model.*: Document model and data structuresnet.gsantner.opoc.*: Shared utility libraries (legacy)
All public APIs must have KDoc documentation:
/**
* Parses text content into a structured document.
*
* This parser supports the following features:
* - Feature 1 description
* - Feature 2 description
*
* @param content The raw text content to parse
* @param options Optional parsing configuration (default: empty map)
* @return Parsed document with HTML representation and metadata
* @throws IllegalArgumentException if content is empty
*
* @see TextParser for the common parser interface
* @see FormatRegistry for format registration
*
* @sample digital.vasic.yole.format.markdown.MarkdownParserTest.basicParsing
*/
fun parse(content: String, options: Map<String, Any> = emptyMap()): ParsedDocument {
// Implementation
}KDoc Guidelines:
- Purpose: First sentence describes what the function/class does
- Details: Additional paragraphs explain behavior, features, edge cases
- @param: Document all parameters with clear descriptions
- @return: Describe return value and its structure
- @throws: Document exceptions that may be thrown
- @see: Link to related APIs
- @sample: Reference example code when helpful
- Code examples: Include usage examples for complex APIs
Follow conventional commit format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code formatting (no logic change)refactor: Code refactoringtest: Adding/updating testschore: Build process, dependencies
Examples:
feat(markdown): add support for footnotes
Implement GFM footnote syntax [^1] for Markdown parser.
Includes rendering in HTML preview and syntax highlighting.
Closes #123
---
fix(todotxt): correct priority parsing for tasks without dates
Priority was incorrectly parsed when creation date was missing.
Now handles all valid todo.txt priority formats.
Fixes #456
---
docs(api): add KDoc to FormatRegistry methods
Complete documentation coverage for format registration,
detection, and retrieval methods.
Part of Phase 3 documentation initiative.
Guidelines:
- Subject line: 50 characters or less, imperative mood
- Body: Wrap at 72 characters, explain what and why (not how)
- Footer: Reference issues/PRs (
Closes #123,Fixes #456,Part of #789) - Scope: Module or component name (markdown, todotxt, ui, etc.)
1. Create Parser Class in shared/src/commonMain/kotlin/digital/vasic/yole/format/[formatname]/
package digital.vasic.yole.format.myformat
import digital.vasic.yole.format.ParsedDocument
import digital.vasic.yole.format.TextParser
/**
* Parser for MyFormat text files.
*
* MyFormat is a lightweight markup language for [purpose].
* Supports: [list key features]
*
* @see <a href="https://myformat.org/spec">MyFormat Specification</a>
*/
class MyFormatParser : TextParser {
override val formatId: String = "myformat"
override val displayName: String = "MyFormat"
override val fileExtensions: List<String> = listOf(".myf", ".myformat")
override fun parse(content: String, options: Map<String, Any>): ParsedDocument {
// 1. Detect format (optional validation)
// 2. Parse syntax into structure
// 3. Convert to HTML
// 4. Extract metadata
return ParsedDocument(
html = convertToHtml(content),
metadata = extractMetadata(content),
sourceFormat = formatId
)
}
private fun convertToHtml(content: String): String {
// Implement conversion logic
}
private fun extractMetadata(content: String): Map<String, Any> {
// Extract title, author, tags, etc.
}
}2. Register Format in FormatRegistry.kt:
init {
registerFormat(
TextFormat(
id = "myformat",
name = "MyFormat",
extensions = listOf(".myf", ".myformat"),
mimeType = "text/x-myformat",
parser = { MyFormatParser() }
)
)
}3. Write Tests in shared/src/commonTest/kotlin/digital/vasic/yole/format/myformat/
class MyFormatParserTest {
private val parser = MyFormatParser()
@Test
fun `parse basic syntax`() {
val content = """
# Header
Body text
""".trimIndent()
val result = parser.parse(content)
assertThat(result.html).contains("<h1>Header</h1>")
assertThat(result.html).contains("<p>Body text</p>")
}
@Test
fun `detect format by extension`() {
assertThat(parser.fileExtensions).contains(".myf")
}
}4. Create User Documentation in docs/user-guide/formats/myformat.md
Include:
- Format overview and use cases
- Complete syntax reference with examples
- Yole-specific features and limitations
- Best practices
- External resources
5. Update Format Index in docs/user-guide/formats/README.md
Add your format to the list with description and link.
- ✅ Basic syntax parsing test
- ✅ HTML conversion test
- ✅ Metadata extraction test
- ✅ Edge case handling (empty content, malformed syntax)
- ✅ Format detection by extension
- ✅ Integration with FormatRegistry
Simple formats: Look at PlaintextParser.kt for minimal implementation
Complex formats: Study MarkdownParser.kt for full-featured parser
Structured data: Check CsvParser.kt for table handling
- Search existing issues first
- Use the bug report template
- Include device info, Android version, and steps to reproduce
- Provide screenshots if applicable
- Search existing discussions and issues
- Use the feature request template
- Describe the use case and expected behavior
- Consider if it fits the project's scope
- Fork and create a feature branch
- Write clean, tested code following guidelines
- Update documentation if needed
- Submit a pull request with clear description
- Respond to code review feedback
- Improve existing documentation
- Add examples for new features
- Update README and guides
- Fix typos and clarify unclear sections
- Contribute via Crowdin
- Report translation issues in GitHub issues
- Help maintain language quality
- Rebase your branch on latest
main - Test thoroughly on multiple devices if possible
- Update documentation for any user-facing changes
- Add tests for new functionality
- Run full test suite and fix any failures
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
- [ ] Unit tests pass
- [ ] Manual testing completed
- [ ] Lint checks pass
## Checklist
- [ ] Code follows project style guidelines
- [ ] Self-review completed
- [ ] Documentation updated
- [ ] Tests added/updated- Be responsive to reviewer feedback
- Explain complex changes clearly
- Address all review comments
- Keep PRs focused and reasonably sized
- Be respectful and constructive
- Welcome newcomers and help them learn
- Focus on what is best for the project
- Show empathy towards other community members
- Provide specific, actionable feedback
- Explain the reasoning behind suggestions
- Acknowledge good work and improvements
- Be patient with different perspectives
- AGENTS.md: Development guide for agents
- ARCHITECTURE.md: Project architecture
- README.md: General project information
- GitHub Discussions: Community discussions
- Create a GitHub issue for bugs and feature requests
- Start a discussion for questions and ideas
- Check existing issues and discussions before posting
Yole includes a Go-based challenge framework in the Challenges/ submodule for cross-platform validation testing.
./gradlew runChallenges # Run all challenges via Gradle
cd Challenges && go test ./... # Run challenge tests directly- Create a YAML challenge file in
Challenges/banks/ - Define challenge metadata: name, platform targets, difficulty, expected outcomes
- Implement challenge validation logic in Go
- Add tests covering the new challenge bank
- Run
go vet ./...andgo test ./... -race -count=1before submitting
security/-- Security-focused challenges (path traversal, injection, etc.)format-edge-cases/-- Parser edge case validationprotocol-resilience/-- Network protocol failure mode testing
Yole has several Go modules as submodules and sibling projects:
| Module | Location | Purpose |
|---|---|---|
Challenges/ |
Submodule | Cross-platform testing framework |
Containers/ |
Submodule | Container orchestration (Challenges dependency) |
HelixQA/ |
Submodule | Autonomous QA with testbank, ticket, evidence packages |
DocProcessor |
Sibling | Document processing pipeline |
LLMOrchestrator |
Sibling | LLM agent orchestration |
VisionEngine |
Sibling | Computer vision analysis |
- Go 1.24+ required
- All code must pass
go vet ./... - All tests must pass with race detector:
go test ./... -race -count=1 - No flaky tests allowed (pre-existing known flaky tests are documented in CLAUDE.md)
- Follow standard Go project layout conventions
- Add benchmarks for performance-sensitive code
All new features and bug fixes must include appropriate tests. Yole uses 16 test types:
- Unit tests: Test individual functions and methods in isolation
- Integration tests: Test interactions between components
| Change Type | Required Test Types |
|---|---|
| New format parser | Unit, integration, edge-case/supremacy, fuzz |
| Protocol service change | Unit, integration, stress, resilience, security |
| UI change | Unit, accessibility, snapshot |
| Performance change | Unit, performance baseline, load |
| Security fix | Unit, security, fuzz |
| Concurrency change | Unit, stress, non-blocking |
- JUnit4 runner: Use
runBlocking<Unit> { }(notrunTest). JUnit4 requiresUnitreturn type. - MockK is JVM-only: Available in
desktopTestandandroidUnitTest, NOT incommonTestorwasmJsTest. - jvmTarget: Must be
"11"in all JVM compilations. - No test removal: Tests must never be removed, disabled, or skipped. Fix root causes instead.
./gradlew :shared:desktopTest # Primary test command
make test-shared # Makefile shortcut
./gradlew test koverHtmlReport # All tests with coverageBy contributing to Yole, you agree that your contributions will be licensed under the same license as the project (Apache 2.0 for code, CC0 1.0 for translations and samples).
Thank you for contributing to Yole! Your help makes this project better for everyone.