- copilot-instructions.md → ObsidianDragon-agent/copilot-instructions.md - ARCHITECTURE.md → ObsidianDragon-agent/ARCHITECTURE.md - Symlinks at original locations preserve Copilot auto-discovery
7.1 KiB
ObsidianDragon — Coding Standards & Architecture
Project Identity
ObsidianDragon is a native C++17 desktop wallet for the DragonX (DRGX) cryptocurrency.
It uses ImGui (immediate-mode GUI) with SDL3, ships an embedded full-node daemon (dragonxd),
and includes an integrated CPU miner (xmrig-hac). Licensed GPLv3.
Platforms: Linux (x86_64), Windows (x86_64, cross-compiled via MinGW), macOS (universal arm64+x86_64).
Code Style
Naming
- Classes/structs/enums:
CamelCase—RPCClient,WalletState,NavPage - Member variables:
snake_case_with trailing underscore —rpc_,connection_status_,fast_worker_ - Functions/methods:
camelCase—tryConnect(),isConnected(),drainResults() - Constants:
UPPER_SNAKEfor macros,camelCasefor const vars —REFRESH_INTERVAL,DEFAULT_FEE - Enum values:
CamelCase—NavPage::Overview,State::Running - Type aliases:
using MainCb = std::function<void()>;
Formatting
- 4-space indentation, no tabs
- Opening brace on same line:
void foo() { #pragma once(not include guards)- Include order: self header, standard library, third-party, project headers
Memory & Ownership
std::unique_ptr<T>for ownership — never rawnew/delete- Raw pointers for non-owning references (e.g., function parameters)
- RAII for all resource management (threads, handles, mutexes)
Documentation
///Doxygen comments on public APIs in headers- Implementation files: explain why, not what
- Use
DEBUG_LOGF()for debug logging, notprintf/std::cout
Threading Model
Main thread — ImGui render loop (App::update()), all UI code, drainResults() callbacks.
Never block this thread with network I/O or disk I/O.
worker_ (RPCWorker) — General-purpose background thread for batched RPC polling.
Executes the refreshData() cycle every 5 seconds.
fast_worker_ (RPCWorker) — Fast-lane background thread for latency-sensitive operations:
console commands, mining hashrate polls. Avoids head-of-line blocking behind main refresh batch.
Monitor threads — EmbeddedDaemon and XmrigManager each spawn a monitor thread
to read stdout/stderr pipes from their child processes.
Work Submission Pattern
// Post work to background thread; return a callback for the main thread
worker_->post([this]() -> rpc::RPCWorker::MainCb {
json result = rpc_->call("getinfo"); // Runs on worker thread (blocking OK)
return [this, result]() {
state_.connected = true; // Runs on main thread via drainResults()
};
});
drainResults() is called each frame in App::update() to execute completed callbacks.
RPC Architecture
Dual-client design:
rpc_+worker_— main polling (balance, transactions, sync status)fast_rpc_+fast_worker_— console commands, mining stats
Both are RPCClient instances wrapping libcurl for JSON-RPC over HTTPS.
Callback types:
using Callback = std::function<void(const json&)>;
using ErrorCallback = std::function<void(const std::string&)>;
Error flow: RPC errors throw std::runtime_error → caught in doRPC() → invokes
ErrorCallback → surfaces via Notifications::instance().error(msg).
Connection state: state_.connected (app-level, set in onConnected()) vs
rpc_->isConnected() (per-client, set after successful getinfo()). These can differ —
always check the right one for the context.
Connection Lifecycle
[Disconnected] → tryConnect() every 5s
→ auto-detect DRAGONX.conf (host, port, rpcuser, rpcpassword)
→ if no config: start embedded daemon → retry
→ post async rpc_->connect() to worker_
→ success: onConnected() → state_.connected = true
→ auth 401: try .cookie auth → retry
→ failure: onDisconnected(reason) → may restart daemon
UI Patterns
Page Routing
NavPage enum in src/ui/sidebar.h defines all pages. App::update() dispatches
via switch on current_page_ to render functions like RenderBalanceTab(this) or
console_tab_.render(...).
Dialogs
Modal dialogs use show_* boolean flags (e.g., show_import_key_, show_backup_).
Set the flag to true to open; the dialog renders in App::update() and clears
the flag when dismissed.
Notifications
Notifications::instance().success("Transaction sent");
Notifications::instance().error("Insufficient funds");
Toast-style popups in the bottom-right corner.
Lock Screen & Loading Overlay
- Lock screen gates all interaction when
state_.isLocked() - Loading overlay shown while daemon isn't ready, gates all tabs except Peers, Console, Settings
Platform Abstractions
Detection: #ifdef _WIN32 / #elif __APPLE__ / #elif __linux__
Platform-specific files live in src/platform/ (Windows only: DX11 context, backdrop effects).
Shared platform utilities in src/util/platform.h — openUrl(), getHomeDir(), getDragonXDataDir().
Rendering backends:
- Windows: DirectX 11 (
imgui_impl_dx11) - macOS/Linux: OpenGL 3 (
imgui_impl_opengl3) + GLAD loader
Build System
CMake 3.20+, C++17. Single CMakeLists.txt at root.
FetchContent dependencies: SDL3, nlohmann/json v3.11.3, toml++ v3.4.0, libcurl (Windows only) Bundled libraries: ImGui, GLAD, miniz, libsodium (pre-built per platform), qrcode
macOS: Universal binary (arm64+x86_64), deployment target 11.0 (Big Sur+).
Set CMAKE_OSX_DEPLOYMENT_TARGET and CMAKE_OSX_ARCHITECTURES before project().
Build commands:
./build.sh # Dev build
./build.sh --mac-release # macOS universal .app + DMG
./build.sh --linux-release # Linux AppImage + zip
./build.sh --win-release # Windows cross-compile
Theme System
UI layout values are defined in res/themes/ui.toml and accessed via schema::UI() —
never hardcode pixel sizes in C++ code.
Skins are self-contained TOML files in res/themes/ defining color palettes.
scripts/expand_themes.py merges layout sections from ui.toml into each skin at build time.
Colors support CSS-style variables: --primary, --surface, --on-surface-medium, resolved
by ColorVarResolver at runtime.
Internationalization
All user-visible strings use TR("key") macro → dragonx::util::tr() → lookup in
I18n singleton. Never hardcode English strings in UI code.
Language files: res/lang/{locale}.json (runtime) with embedded fallback compiled into binary.
Supported: en, es, zh, ru, de, fr, pt, ja, ko.
Key Rules
- Never block the main thread — all network/disk I/O goes through
worker_->post() - Never hardcode pixel sizes — use
ui.tomlschema values viaschema::UI() - Always use
TR()for strings — no hardcoded English in UI rendering code - Use
ICON_MD_*for icons — Material Design icon font, never raw Unicode - Use
std::unique_ptr— no rawnew/deletein application code - Check the right connection state —
state_.connectedfor app-level,rpc_->isConnected()for per-client - Test all three platforms — build must pass Linux; cross-compile test if touching platform code