-
Notifications
You must be signed in to change notification settings - Fork 117
Development‐Guide
git clone https://github.com/YOUR_USERNAME/openhamclock.git
cd openhamclock
npm ci
git checkout Staging
# Terminal 1 — Backend
node server.js
# Terminal 2 — Frontend (hot reload)
npm run devAll PRs target the
Stagingbranch, notmain. We merge Staging → main on a weekly release cycle.
src/
├── components/ # React UI panels (DXClusterPanel, SolarPanel, etc.)
├── hooks/ # Data fetching hooks (useDXCluster, usePOTASpots, etc.)
├── plugins/layers/ # Map layer plugins (satellites, VOACAP, RBN, etc.)
├── layouts/ # Page layouts (Modern, Classic, Dockable)
├── contexts/ # React contexts (RigContext)
├── utils/ # Pure utility functions (callsign, geo, filters)
├── lang/ # i18n translation files (15 languages)
└── styles/ # CSS files
server.js # Express backend — all API routes, SSE, MQTT, data proxying
rig-listener/ # Standalone USB rig control agent
rig-bridge/ # Plugin-based rig control with web UI
wsjtx-relay/ # WSJT-X UDP → HTTPS relay agent
Full architecture: docs/ARCHITECTURE.md
Prettier enforces consistent formatting. A pre-commit hook auto-formats staged files.
npm run format # Format everything
npm run format:check # Check without writing (what CI runs)Style: single quotes, semicolons, 2-space indent, 120-char line width, trailing commas.
Each panel is a self-contained React component in src/components/:
export const MyPanel = ({ data, loading, onSpotClick }) => {
if (loading) return <div>Loading...</div>;
if (!data?.length) return <div>No data</div>;
return (
<div style={{ color: 'var(--text-primary)' }}>
{data.map((item) => (
<div key={item.id} onClick={() => onSpotClick?.(item)}>
{item.callsign} — {item.freq}
</div>
))}
</div>
);
};Wire it into all three layouts (Modern, Classic, Dockable).
Each data source has a hook in src/hooks/:
export const useMyData = () => {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
const res = await fetch('/api/mydata');
if (res.ok) setData(await res.json());
setLoading(false);
};
fetchData();
const interval = setInterval(fetchData, 30000);
return () => clearInterval(interval);
}, []);
return { data, loading };
};Create src/plugins/layers/useMyLayer.js:
export const metadata = {
id: 'my_layer',
name: 'My Layer',
description: 'What this layer shows',
category: 'amateur',
defaultEnabled: false,
defaultOpacity: 0.7,
version: '1.0.0',
};
export function useLayer({ map, enabled, opacity }) {
useEffect(() => {
if (!map || !enabled) return;
// Add Leaflet layers here
return () => { /* cleanup */ };
}, [map, enabled]);
}The layer registry auto-discovers plugins — no manual registration needed.
All external APIs are proxied through server.js with caching:
let myCache = { data: null, timestamp: 0 };
const MY_TTL = 5 * 60 * 1000;
app.get('/api/mydata', async (req, res) => {
if (myCache.data && Date.now() - myCache.timestamp < MY_TTL) {
return res.json(myCache.data);
}
const data = await fetch('https://api.example.com/data').then(r => r.json());
myCache = { data, timestamp: Date.now() };
res.json(data);
});Important: Every cache needs a TTL and a size cap. The server handles 2,000+ concurrent connections.
Three themes: dark, light, retro. Never hardcode colors — use CSS variables:
// ✅ Good
<div style={{ color: 'var(--accent-cyan)', background: 'var(--bg-panel)' }}>
// ❌ Bad
<div style={{ color: '#00ddff', background: '#1a1a2e' }}>Key variables: --bg-primary, --bg-secondary, --bg-tertiary, --bg-panel, --border-color, --text-primary, --text-secondary, --text-muted, --accent-amber, --accent-green, --accent-red, --accent-cyan
Translation files are in src/lang/. When adding new user-facing strings:
- Add the key to
en.jsonwith the English text - For non-English files, either translate properly or omit the key (i18next falls back to English automatically)
- Do not put English text in non-English files — it masks missing translations
feature/my-new-panel
fix/pota-frequency-display
docs/update-readme
- App loads without console errors
- Works in Dark, Light, and Retro themes
- Responsive at different screen sizes
- If touching
server.js: caches have TTLs and size caps - If adding an API route: includes caching and error handling
- If adding a panel: wired into all three layouts
- Existing features still work
Comment /assign on any GitHub issue to self-assign it. Comment /close to close a resolved issue.
OpenHamClock · GitHub · Report an Issue · 73 de K0CJH