Ziya/CONTRIBUTING.md
2025-07-08 14:57:51 +07:00

12 KiB

Contributing to Ziya-Slint

Thank you for your interest in contributing to Ziya-Slint! This guide outlines our development approach, coding standards, and best practices for the desktop trading application.

Project Overview

Ziya-Slint is a desktop trading application built with Rust and Slint UI framework, designed for cryptocurrency trading with real-time market data and portfolio management.

Development Philosophy

We follow these core principles:

  1. KISS Principle - Keep code simple and straightforward
  2. Explicit Error Handling - Never use dummy types or unfinished code
  3. Real-world Ready - All code should be production-ready
  4. Async-first - Prefer tokio over std for async operations
  5. Type Safety - Leverage Rust's type system for correctness
  6. Desktop UX - Native desktop app behavior and performance

Getting Started

Prerequisites

  • Rust 2024 Edition (see rust-toolchain.toml)
  • Just command runner (for development tasks)
  • Redis (for data caching)
  • Git and Git-cliff (for development workflow)

Initial Setup

  1. Clone the repository
  2. Copy configuration files:
    cp Config.example.toml Config.toml
    
  3. Install development dependencies:
    just install-deps
    
  4. Verify setup:
    just test
    just clippy
    

Development Workflow

We use just for common development tasks:

# List all available commands
just

# Development with hot reloading
just dev

# Building
just build              # Build with dev features
just build-prod         # Build with prod features

# Quality assurance
just test               # Run tests
just clippy             # Run linter
just fmt                # Format code
just check              # Check code for errors
just clean              # Clean build artifacts

Coding Standards

Error Handling

We use strict error handling patterns. Never use:

  • Dummy types or results
  • Placeholder implementations
  • TODO comments in production code

Always use our error handling pattern:

use crate::err_with_loc;
use crate::error::Result;

// Import the Result type
use crate::error::Result;

// Use map_err before ? operator
let result = some_operation()
    .map_err(|e| {
        error!("operation_failed: {}", e);
        err_with_loc!(AppError::OperationFailed(format!("operation_failed: {}", e)))
    })?;

Async Programming

  • Use tokio instead of std for async operations
  • Follow the actors pattern from this guide
  • Never use tokio::spawn with UI components directly
  • Always use slint::invoke_from_event_loop for UI updates from background tasks

Code Formatting

We use rustfmt with specific configuration (see rustfmt.toml):

  • Max width: 120 characters
  • Use field init shorthand
  • Reorder imports and impl items
  • Group imports by StdExternalCrate
  • Prefer same line braces

Run formatting:

just fmt

Architecture: Feature-Sliced Design (FSD)

We follow Feature-Sliced Design (FSD) methodology for frontend architecture:

FSD Layer Structure

ui/
├── app/           # Application layer - root, global setup
├── pages/         # Page layer - complete screens  
├── widgets/       # Widget layer - composite UI blocks
├── entities/      # Entity layer - business entities
├── features/      # Feature layer - user interactions
└── shared/        # Shared layer - reusable resources
    ├── ui/        # UI components
    ├── types/     # Type definitions
    └── design-system/ # Theme, tokens, etc.

FSD Rules & Guidelines

  1. Import Rule: Higher layers can only import from lower layers

    • app/pages/widgets/entities/features/shared/
    • Never import from higher layers
  2. Layer Responsibilities:

    • App: Global providers, routing, application setup
    • Pages: Complete screens, page-level logic
    • Widgets: Composite UI blocks (navigation, forms, cards)
    • Entities: Business entities (token, user, market data)
    • Features: User interactions (login, trading, portfolio management)
    • Shared: Reusable resources (UI kit, utilities, constants)
  3. Slicing by Features: Each feature should be self-contained

    features/
    ├── authentication/
    │   ├── ui/           # Login components
    │   ├── model/        # Auth state
    │   └── api/          # Auth API calls
    └── trading/
        ├── ui/           # Trading components  
        ├── model/        # Trading state
        └── api/          # Trading API calls
    
  4. Component Composition: Build from bottom-up

    // shared/ui/button/
    export component Button { /* base button */ }
    
    // widgets/navigation/
    import { Button } from "../../shared/ui/button/";
    export component NavigationWidget { 
        Button { /* composed navigation */ }
    }
    
    // pages/dashboard/
    import { NavigationWidget } from "../../widgets/navigation/";
    export component Dashboard {
        NavigationWidget { /* page composition */ }
    }
    
  5. State Management: Follow unidirectional data flow

    • Global state in app/ layer
    • Feature state in respective features/ slices
    • Pass data down, emit events up
  6. Design System: Centralized in shared/design-system/

    • Theme tokens and variables
    • Consistent spacing, colors, typography
    • Reusable UI components

Slint UI Development

Threading & State Management

Critical Pattern for Slint + Tokio:

// Background work in Rust
tokio::spawn(async move {
    let result = do_background_work().await;
   
    // Update UI from main thread following FSD data flow
    let _ = slint::invoke_from_event_loop(move || {
        if let Some(ui) = ui_weak.upgrade() {
            // Update flows from app → pages → widgets → shared
            ui.update_global_state(result);
        }
    });
});

Component Development Guidelines

  1. Start with Shared Layer: Build reusable components first

    // shared/ui/loading/loading.slint
    export component LoadingSpinner {
        // Base loading component
    }
    
  2. Compose in Widgets: Create business-specific blocks

    // widgets/token-card/index.slint
    import { LoadingSpinner } from "../../shared/ui/loading/";
    export component TokenCard {
        LoadingSpinner { /* token-specific loading */ }
    }
    
  3. Integrate in Pages: Assemble complete screens

    // pages/dashboard/index.slint
    import { TokenCard } from "../../widgets/token-card/";
    export component Dashboard {
        TokenCard { /* dashboard context */ }
    }
    
  4. Wire in App: Handle global state and routing

    // app/index.slint
    import { Dashboard } from "../pages/dashboard/";
    export component App {
        if current-page == "dashboard": Dashboard { }
    }
    

File Naming Conventions

  • Use index.slint for main component exports
  • Use kebab-case for file names: token-card.slint
  • Use PascalCase for component names: TokenCard

Property & Callback Flow

  • Properties flow down: app → pages → widgets → shared
  • Callbacks flow up: shared → widgets → pages → app
  • Keep callback interfaces simple and focused

FSD Best Practices

Do:

  • Keep components focused on single responsibility
  • Use semantic component names that reflect business domain
  • Extract common patterns to shared/ layer
  • Implement loading and error states consistently
  • Use TypeScript-like typing through Slint properties
  • Follow consistent import paths relative to FSD structure

Don't:

  • Import from higher layers (breaks FSD hierarchy)
  • Put business logic directly in UI components
  • Create circular dependencies between features
  • Hardcode values that should be in design system
  • Mix concerns (UI logic with business logic)
  • Create deep nesting beyond FSD layers

Common Anti-patterns to Avoid:

// ❌ BAD: Widget importing from pages
// widgets/header/index.slint
import { Dashboard } from "../../pages/dashboard/"; // Wrong!

// ❌ BAD: Shared component with business logic
// shared/ui/button/index.slint
export component Button {
    // Don't put trading logic here!
    clicked => { execute_trade(); } 
}

// ✅ GOOD: Proper callback delegation
// shared/ui/button/index.slint
export component Button {
       callback clicked();
}

// ✅ GOOD: Business logic in appropriate layer
// features/trading/ui/trade-button.slint
import { Button } from "../../../shared/ui/button/";
export component TradeButton {
    Button {
        clicked => { handle_trade_click(); }
       }
   }

Testing

Unit Tests

just test

Integration Tests

  • Test UI components with mock data
  • Test service layer integration
  • Use feature flags for test environments

Feature Testing

# Run with specific features
cargo test --features dev
cargo test --features prod

UI/UX Principles

We follow comprehensive usability guidelines:

  1. 10 Usability Heuristics - Nielsen's principles
  2. Gestalt Principles - Visual hierarchy and grouping
  3. Accessibility - Support for different user needs
  4. Simplicity - Minimize cognitive load
  5. Desktop Patterns - Native desktop app behavior

Key Requirements:

  • Responsive design within window constraints (1080x800 - 1920x1080)
  • Proper error messaging in plain language
  • Loading states with progress indication
  • Keyboard navigation support
  • Native desktop interactions

Submission Guidelines

Branch Naming

Use descriptive names:

  • feat/shared/button-component - New shared component
  • feat/pages/dashboard-redesign - Page-level changes
  • feat/features/trading-flow - Feature implementation
  • fix/widgets/token-card-loading - Bug fixes
  • refactor/shared/design-system - Code improvements

Commit Messages

Be descriptive:

feat(shared): add loading spinner component

- Implement reusable loading spinner in shared/ui
- Add animation and theme support
- Export from shared layer for use in widgets

fix(pages): resolve dashboard layout overflow

- Fix token card grid overflow in dashboard
- Improve responsive behavior for small windows
- Add proper scrolling for token list

Code Review Checklist

General:

  • Follows error handling patterns
  • Uses tokio for async operations
  • Includes proper logging
  • Formatted with rustfmt
  • Passes all tests
  • Updates documentation if needed

FSD-Specific:

  • Components placed in correct FSD layer
  • Import dependencies only from lower layers
  • Properties flow down, callbacks flow up
  • No direct business logic in UI components
  • Reusable components in shared/ layer
  • Feature-specific logic in features/ layer

UI/UX:

  • Follows design system patterns
  • Implements proper loading states
  • Handles error states gracefully
  • Supports keyboard navigation
  • Responsive within window constraints

Development Environment

  • IDE: VS Code with rust-analyzer and Slint extension
  • Debugging: Use tracing for structured logging
  • File Watching: watchexec for auto-reload during development
  • Hot Reloading: just dev for development workflow

Common Issues

Slint Compilation Errors

If you encounter field offset errors with watchexec:

cargo clean
# Then rebuild

Threading Issues

Remember: Slint UI components are not Send. Always use slint::invoke_from_event_loop for UI updates from background tasks.

Hot Reloading

For the best development experience:

just dev

This watches both src/ and ui/ directories and automatically rebuilds.

Documentation

  • Component APIs: Document with clear examples
  • Architecture: Update this guide when patterns change
  • Configuration: Document all config options
  • UI Components: Maintain design system documentation

Getting Help

  1. Check existing documentation
  2. Search closed issues on GitHub
  3. Ask in project discussions
  4. Check Slint documentation for UI issues

Code of Conduct

  • Be respectful and inclusive
  • Focus on constructive feedback
  • Help others learn and grow
  • Maintain professional communication

By following these guidelines, you help maintain code quality and ensure smooth collaboration on the Ziya-Slint desktop application. Thank you for contributing! 🚀