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

453 lines
No EOL
12 KiB
Markdown

# 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:
```bash
cp Config.example.toml Config.toml
```
3. Install development dependencies:
```bash
just install-deps
```
4. Verify setup:
```bash
just test
just clippy
```
### Development Workflow
We use `just` for common development tasks:
```bash
# 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:
```rust
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](https://ryhl.io/blog/actors-with-tokio/)
- **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:
```bash
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
```slint
// 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:**
```rust
// 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
```slint
// shared/ui/loading/loading.slint
export component LoadingSpinner {
// Base loading component
}
```
2. **Compose in Widgets**: Create business-specific blocks
```slint
// 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
```slint
// 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
```slint
// 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:
```slint
// ❌ 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
```bash
just test
```
### Integration Tests
- Test UI components with mock data
- Test service layer integration
- Use feature flags for test environments
### Feature Testing
```bash
# 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
### Recommended Tools
- **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:
```bash
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:
```bash
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! 🚀