Files
ObsidianDragon/ObsidianDragon-agent/copilot-instructions.md
DanS 5ebccceffc refactor: move AI/agent files into ObsidianDragon-agent/
- copilot-instructions.md → ObsidianDragon-agent/copilot-instructions.md
- ARCHITECTURE.md → ObsidianDragon-agent/ARCHITECTURE.md
- Symlinks at original locations preserve Copilot auto-discovery
2026-04-04 11:29:12 -05:00

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: CamelCaseRPCClient, WalletState, NavPage
  • Member variables: snake_case_ with trailing underscore — rpc_, connection_status_, fast_worker_
  • Functions/methods: camelCasetryConnect(), isConnected(), drainResults()
  • Constants: UPPER_SNAKE for macros, camelCase for const vars — REFRESH_INTERVAL, DEFAULT_FEE
  • Enum values: CamelCaseNavPage::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 raw new/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, not printf/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 threadsEmbeddedDaemon 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.hopenUrl(), 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

  1. Never block the main thread — all network/disk I/O goes through worker_->post()
  2. Never hardcode pixel sizes — use ui.toml schema values via schema::UI()
  3. Always use TR() for strings — no hardcoded English in UI rendering code
  4. Use ICON_MD_* for icons — Material Design icon font, never raw Unicode
  5. Use std::unique_ptr — no raw new/delete in application code
  6. Check the right connection statestate_.connected for app-level, rpc_->isConnected() for per-client
  7. Test all three platforms — build must pass Linux; cross-compile test if touching platform code