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:
- KISS Principle - Keep code simple and straightforward
- Explicit Error Handling - Never use dummy types or unfinished code
- Real-world Ready - All code should be production-ready
- Async-first - Prefer tokio over std for async operations
- Type Safety - Leverage Rust's type system for correctness
- 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
- Clone the repository
- Copy configuration files:
cp Config.example.toml Config.toml - Install development dependencies:
just install-deps - 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::spawnwith UI components directly - Always use
slint::invoke_from_event_loopfor 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
-
Import Rule: Higher layers can only import from lower layers
- ✅
app/→pages/→widgets/→entities/→features/→shared/ - ❌ Never import from higher layers
- ✅
-
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)
-
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 -
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 */ } } -
State Management: Follow unidirectional data flow
- Global state in
app/layer - Feature state in respective
features/slices - Pass data down, emit events up
- Global state in
-
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
-
Start with Shared Layer: Build reusable components first
// shared/ui/loading/loading.slint export component LoadingSpinner { // Base loading component } -
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 */ } } -
Integrate in Pages: Assemble complete screens
// pages/dashboard/index.slint import { TokenCard } from "../../widgets/token-card/"; export component Dashboard { TokenCard { /* dashboard context */ } } -
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.slintfor 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:
- 10 Usability Heuristics - Nielsen's principles
- Gestalt Principles - Visual hierarchy and grouping
- Accessibility - Support for different user needs
- Simplicity - Minimize cognitive load
- 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 componentfeat/pages/dashboard-redesign- Page-level changesfeat/features/trading-flow- Feature implementationfix/widgets/token-card-loading- Bug fixesrefactor/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:
watchexecfor auto-reload during development - Hot Reloading:
just devfor 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
- Check existing documentation
- Search closed issues on GitHub
- Ask in project discussions
- 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! 🚀