Thank you for your interest in contributing to the BMLT Server Activity Report! This document provides guidelines and information for contributors.
- Getting Started
- Development Setup
- Project Structure
- Development Guidelines
- Testing
- Code Style
- Pull Request Process
- Architecture Overview
- Node.js 24 or higher
- npm or yarn
- Git
-
Fork the repository
# Click the "Fork" button on GitHub, then clone your fork git clone https://github.com/YOUR_USERNAME/activity.git cd activity
-
Install dependencies
npm install
-
Start development server
npm run dev
-
Create a feature branch
git checkout -b feature/your-feature-name
src/
βββ components/ # Svelte components
β βββ ActivityReport.svelte # Main report component
β βββ ActivityTable.svelte # Paginated activity table
β βββ ConfigModal.svelte # Configuration dialog
β βββ DarkMode.svelte # Dark mode toggle component
β βββ Filters.svelte # Search and filter controls
β βββ SettingsModal.svelte # Language settings dialog
β βββ Stats.svelte # Statistics display
βββ lib/
β βββ services/ # API service layers
β β βββ bmltApi.ts # BMLT change data fetching
β β βββ serverList.ts # Server and service body discovery
β βββ stores/ # State management
β β βββ config.svelte.ts # Config store with localStorage persistence
β β βββ localization.ts # i18n translations store (en/es)
β βββ utils/ # Utility functions
β β βββ dataProcessing.ts # Data transformation and aggregation
β β βββ detailsFormatter.ts # Format change details for display
β β βββ diff.ts # Generate line-by-line diffs
β βββ types.ts # TypeScript type definitions
βββ tests/ # Test files
This project uses Svelte 5's new runes-based reactivity system:
$state()- Declare reactive state variables$derived()/$derived.by()- Create computed values that automatically update$bindable()- Create two-way bindable props in components$effect()- Run side effects when dependencies change
Avoid the old $: reactive syntax - it's deprecated in Svelte 5.
<script lang="ts">
interface Props {
initialValue: number;
}
let { initialValue }: Props = $props();
// Reactive state
let count = $state(initialValue);
// Derived/computed value
let doubled = $derived(count * 2);
// Effect
$effect(() => {
console.log('Count changed:', count);
});
function increment() {
count++;
}
</script>
<button onclick={increment}>
Count: {count} (Doubled: {doubled})
</button>- Config Store (
config.svelte.ts): Uses class-based pattern with private$statefields and getters/setters that persist to localStorage - Localization Store (
localization.ts): Wrapslocalized-stringswith Svelte store for reactive translations - Component-local state should use
$state()runes
- Use the
BmltClientfrombmlt-query-clientpackage - Date ranges should be formatted as ISO date strings (
YYYY-MM-DD) - Service body IDs must be parsed as integers when passing to API
- Handle errors gracefully - API calls may fail due to network or server issues
When adding new UI text:
- Add keys to both
enandesobjects insrc/lib/stores/localization.ts - Keep keys in alphabetical order (enforced by ESLint)
- Use the
$translationsstore in components:<script lang="ts"> import { translations } from '../lib/stores/localization'; </script> <h1>{$translations.title}</h1>
When adding new UI elements:
- Use Tailwind's
dark:variant for dark mode styles - For text:
text-gray-900 dark:text-gray-100 - For backgrounds:
bg-white dark:bg-gray-900 - For borders:
border-gray-200 dark:border-gray-700 - Use
text-blue-600 dark:text-blue-400for accent colors (service bodies, links, icons)
npm test # Run all tests once
npm run test:watch # Watch mode for development
npm run test:ui # Interactive Vitest UI
npm run coverage # Generate coverage reportWe use Vitest + Testing Library for Svelte. Tests should be colocated in the src/tests/ directory.
import { describe, test, expect } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import userEvent from '@testing-library/user-event';
import MyComponent from '../../components/MyComponent.svelte';
describe('MyComponent', () => {
test('renders correctly', () => {
render(MyComponent, { props: { title: 'Test' } });
expect(screen.getByText('Test')).toBeInTheDocument();
});
test('handles user interaction', async () => {
const user = userEvent.setup();
render(MyComponent, { props: { title: 'Test' } });
const button = screen.getByRole('button');
await user.click(button);
expect(screen.getByText('Clicked')).toBeInTheDocument();
});
});import { describe, test, expect } from 'vitest';
import { myUtilityFunction } from '../lib/utils/myUtil';
describe('myUtilityFunction', () => {
test('processes data correctly', () => {
const input = { foo: 'bar' };
const result = myUtilityFunction(input);
expect(result).toEqual({ foo: 'BAR' });
});
});- Maintain minimum 93% coverage for all new code
- All new features must include tests
- Bug fixes should include regression tests
- Use TypeScript for all new code
- Define interfaces for component props
- Add types to function parameters and return values
- Use
typefor unions/intersections,interfacefor object shapes
The project uses Prettier with these settings:
- Line width: 200 characters
- Single quotes
- Semicolons required
- No trailing commas
- 2-space indentation
Format your code before committing:
npm run formatESLint is configured with strict rules:
npm run lintFix auto-fixable issues:
npm run lint -- --fixOrganize imports in this order:
- Svelte imports (
svelte,svelte/store) - External libraries
- Internal components
- Internal utilities/services
- Types
- Styles
import { onMount } from 'svelte';
import { Button, Modal } from 'flowbite-svelte';
import MyComponent from './MyComponent.svelte';
import { fetchData } from '../lib/services/api';
import type { MyType } from '../lib/types';
import './styles.css';-
Ensure your code follows all guidelines
npm run lint # Must pass npm test # All tests must pass npm run format # Format code
-
Update documentation
- Add JSDoc comments for new functions
- Update README.md if adding user-facing features
- Update CONTRIBUTING.md if changing dev processes
-
Write a clear PR description
- Explain what changes you made and why
- Reference any related issues
- Include screenshots for UI changes
- List any breaking changes
-
PR Title Format
- Use conventional commits format:
feat: add Spanish translationsfix: correct date formatting in tabledocs: update README with new featurestest: add tests for ActivityTable componentrefactor: simplify data processing logicchore: update dependencies
- Use conventional commits format:
-
Wait for review
- Address any feedback from maintainers
- Keep your branch up to date with main
- Be responsive to comments
flowchart TD
Start([User Opens App]) --> CheckConfig{Config\nExists?}
CheckConfig -->|No| ShowModal[Show Config Modal]
CheckConfig -->|Yes| LoadData[Load Activity Data]
ShowModal --> FetchServers[Fetch Server List\nfrom GitHub]
FetchServers --> SelectServer[User Selects Server]
SelectServer --> FetchBodies[Fetch Service Bodies\nfrom BMLT API]
FetchBodies --> SelectBodies[User Selects\nService Bodies]
SelectBodies --> SaveConfig[Save to localStorage]
SaveConfig --> LoadData
LoadData --> FetchChanges[Fetch Changes\nfrom BMLT API]
FetchChanges --> ProcessData[Process & Group\nChange Data]
ProcessData --> Display[Display Activity Report]
Display --> Stats[Statistics Section]
Display --> Filters[Filter Controls]
Display --> Table[Activity Table]
Stats --> ShowTotal[Total Changes]
Stats --> ShowUsers[Active Users]
Stats --> ShowTypes[Change Types]
Filters --> Search[Search Input]
Filters --> TypeFilter[Change Type Filter]
Filters --> UserFilter[User Filter]
Table --> Paginate[Paginated Results]
Paginate --> ClickRow{User Clicks\nRow?}
ClickRow -->|Yes| ShowDiff[Show Diff Modal]
ShowDiff --> Display
ClickRow -->|No| Display
Display --> Configure{User Clicks\nConfigure?}
Configure -->|Yes| ShowModal
Configure -->|No| Display
style Start fill:#e1f5ff
style Display fill:#d4edda
style ShowModal fill:#fff3cd
style ShowDiff fill:#f8d7da
style LoadData fill:#cfe2ff
- Configuration - Loaded from localStorage on app start
- Server Discovery - Fetched from GitHub aggregator repository
- Change Fetching - Retrieved via bmlt-query-client library
- Data Processing - Groups changes by user and calculates statistics
- Client-side Filtering - Applied in-memory for instant results
- Persistent Settings - Language, theme, and config saved to localStorage
bmlt-activity-report-config- Main configuration objectactivityLanguage- Language preference (enores)color-theme- Theme preference (lightordark)
- For bugs or feature requests: Open an issue
- For questions: Start a discussion
- For security issues: Email security@bmlt.app
Please be respectful and constructive in all interactions. We're all here to make the BMLT ecosystem better!
Thank you for contributing! π