feat: add Dioxus and GPUI UI prototypes for framework comparison

Dioxus (ui-dioxus/): 2,169 lines, WebView mode (same wry as Tauri),
  Catppuccin theme, 12 components, agor-core integration, compiles clean.
  Evolution path — keeps xterm.js, gradual migration from Tauri.

GPUI (ui-gpui/): 2,490 lines, GPU-accelerated rendering, alacritty_terminal
  for native terminal, 17 files, Catppuccin palette, demo data.
  Revolution path — pure Rust UI, 120fps target, no WebView.

Both are standalone (not in workspace), share agor-core backend.
Created for side-by-side comparison to inform framework decision.
This commit is contained in:
Hibryda 2026-03-19 06:05:58 +01:00
parent 90c7315336
commit f3d2ca78ba
34 changed files with 17467 additions and 0 deletions

136
ui-dioxus/src/main.rs Normal file
View file

@ -0,0 +1,136 @@
/// Agent Orchestrator — Dioxus 0.7 Desktop Prototype
///
/// This prototype demonstrates the core Agent Orchestrator experience
/// built with Dioxus 0.7 (desktop mode via wry/WebKit2GTK).
///
/// Architecture comparison vs Tauri+Svelte:
///
/// | Aspect | Tauri+Svelte (current) | Dioxus (this prototype) |
/// |---------------------|-------------------------------|-------------------------------|
/// | UI rendering | WebView (Svelte -> HTML) | WebView (RSX -> HTML) |
/// | State management | Svelte 5 runes ($state) | Dioxus signals (use_signal) |
/// | Backend bridge | Tauri IPC (invoke/listen) | Direct Rust (no IPC!) |
/// | Terminal | xterm.js via WebView | xterm.js via WebView (same) |
/// | Reactivity model | Compiler-based (Svelte 5) | Runtime signals (fine-grained)|
/// | Component model | .svelte files (HTML+JS+CSS) | Rust functions (rsx! macro) |
/// | Type safety | TypeScript (compile-time) | Rust (compile-time, stronger) |
/// | Hot reload | Vite HMR | dx serve (RSX hot reload) |
///
/// Key advantage: no IPC serialization boundary. Backend state (PtyManager,
/// SidecarManager) is directly accessible from UI code as typed Rust values.
#[allow(dead_code)]
mod backend;
mod components;
mod state;
mod theme;
use dioxus::prelude::*;
use components::command_palette::CommandPalette;
use components::project_grid::ProjectGrid;
use components::settings::SettingsPanel;
use components::sidebar::Sidebar;
use components::status_bar::{FleetState, StatusBar};
use state::demo_projects;
fn main() {
dioxus::LaunchBuilder::new()
.with_cfg(dioxus::desktop::Config::new()
.with_window(
dioxus::desktop::WindowBuilder::new()
.with_title("Agent Orchestrator — Dioxus Prototype")
.with_inner_size(dioxus::desktop::LogicalSize::new(1400.0, 900.0))
)
.with_custom_head(format!(
"<style>{}</style>",
theme::generate_css()
))
.with_background_color((30, 30, 46, 255)) // --ctp-base
)
.launch(App);
}
/// Root application component.
///
/// Layout: sidebar rail | optional drawer | project grid | status bar
/// Same structure as the Svelte app's App.svelte.
#[allow(non_snake_case)]
fn App() -> Element {
// Global UI state
let mut settings_open = use_signal(|| false);
let mut palette_open = use_signal(|| false);
// Projects — in the real app, loaded from groups.json via groups-bridge
let projects = use_signal(|| demo_projects());
// Fleet state — derived from all project health stores
let fleet = FleetState {
running: 1,
idle: 1,
stalled: 0,
total_cost: 0.1694,
total_tokens: 48_700,
project_count: projects.read().len(),
};
// Keyboard shortcuts
let on_keydown = move |e: KeyboardEvent| {
let modifiers = e.modifiers();
let is_ctrl = modifiers.ctrl();
match e.key() {
// Ctrl+K: Command palette
Key::Character(ref c) if is_ctrl && c == "k" => {
palette_open.set(true);
}
// Ctrl+,: Toggle settings
Key::Character(ref c) if is_ctrl && c == "," => {
let current = *settings_open.read();
settings_open.set(!current);
}
// Ctrl+B: Toggle sidebar drawer
Key::Character(ref c) if is_ctrl && c == "b" => {
let current = *settings_open.read();
settings_open.set(!current);
}
// Escape: close overlays
Key::Escape => {
if *palette_open.read() {
palette_open.set(false);
} else if *settings_open.read() {
settings_open.set(false);
}
}
_ => {}
}
};
rsx! {
div {
class: "app-shell",
onkeydown: on_keydown,
tabindex: "0",
// Main body: sidebar + optional drawer + workspace
div { class: "app-body",
// Left sidebar rail
Sidebar { settings_open: settings_open }
// Optional drawer panel (settings)
if *settings_open.read() {
SettingsPanel {}
}
// Main workspace: project grid
ProjectGrid { projects: projects.read().clone() }
}
// Bottom status bar
StatusBar { fleet: fleet }
// Command palette overlay
CommandPalette { visible: palette_open }
}
}
}