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

View file

@ -0,0 +1,151 @@
/// Settings panel — drawer that slides out from the sidebar.
///
/// Mirrors the Svelte app's SettingsTab.svelte: theme selection, font controls,
/// provider configuration. Uses the same drawer pattern (18rem wide,
/// mantle background).
use dioxus::prelude::*;
#[component]
pub fn SettingsPanel() -> Element {
let mut theme = use_signal(|| "Catppuccin Mocha".to_string());
let mut ui_font = use_signal(|| "Inter".to_string());
let mut term_font = use_signal(|| "JetBrains Mono".to_string());
let mut default_shell = use_signal(|| "/bin/bash".to_string());
rsx! {
div { class: "drawer-panel",
div { class: "drawer-title", "Settings" }
// Appearance section
div { class: "settings-section",
div { class: "settings-section-title", "Appearance" }
div { class: "settings-row",
span { class: "settings-label", "Theme" }
select {
class: "settings-select",
value: "{theme}",
onchange: move |e| theme.set(e.value()),
option { value: "Catppuccin Mocha", "Catppuccin Mocha" }
option { value: "Catppuccin Macchiato", "Catppuccin Macchiato" }
option { value: "Tokyo Night", "Tokyo Night" }
option { value: "Dracula", "Dracula" }
option { value: "Nord", "Nord" }
}
}
div { class: "settings-row",
span { class: "settings-label", "UI Font" }
select {
class: "settings-select",
value: "{ui_font}",
onchange: move |e| ui_font.set(e.value()),
option { value: "Inter", "Inter" }
option { value: "system-ui", "System UI" }
option { value: "IBM Plex Sans", "IBM Plex Sans" }
}
}
div { class: "settings-row",
span { class: "settings-label", "Terminal Font" }
select {
class: "settings-select",
value: "{term_font}",
onchange: move |e| term_font.set(e.value()),
option { value: "JetBrains Mono", "JetBrains Mono" }
option { value: "Fira Code", "Fira Code" }
option { value: "Cascadia Code", "Cascadia Code" }
}
}
}
// Defaults section
div { class: "settings-section",
div { class: "settings-section-title", "Defaults" }
div { class: "settings-row",
span { class: "settings-label", "Shell" }
select {
class: "settings-select",
value: "{default_shell}",
onchange: move |e| default_shell.set(e.value()),
option { value: "/bin/bash", "/bin/bash" }
option { value: "/bin/zsh", "/bin/zsh" }
option { value: "/usr/bin/fish", "/usr/bin/fish" }
}
}
}
// Providers section
div { class: "settings-section",
div { class: "settings-section-title", "Providers" }
ProviderCard {
name: "Claude",
model: "claude-sonnet-4-20250514",
status: "Available",
accent: "var(--ctp-mauve)",
}
ProviderCard {
name: "Codex",
model: "gpt-5.4",
status: "Available",
accent: "var(--ctp-green)",
}
ProviderCard {
name: "Ollama",
model: "qwen3:8b",
status: "Not running",
accent: "var(--ctp-peach)",
}
}
}
}
}
#[component]
fn ProviderCard(
name: String,
model: String,
status: String,
accent: String,
) -> Element {
let is_available = status == "Available";
let badge_bg = if is_available {
"color-mix(in srgb, var(--ctp-green) 15%, transparent)"
} else {
"color-mix(in srgb, var(--ctp-overlay0) 15%, transparent)"
};
let badge_color = if is_available {
"var(--ctp-green)"
} else {
"var(--ctp-overlay0)"
};
let badge_style = format!(
"font-size: 0.625rem; padding: 0.0625rem 0.3125rem; border-radius: 0.1875rem; background: {badge_bg}; color: {badge_color};"
);
rsx! {
div {
style: "background: var(--ctp-surface0); border-radius: 0.375rem; padding: 0.5rem 0.75rem; margin-bottom: 0.5rem;",
div {
style: "display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.25rem;",
span {
style: "font-weight: 600; font-size: 0.8125rem; color: {accent};",
"{name}"
}
span {
style: "{badge_style}",
"{status}"
}
}
div {
style: "font-size: 0.6875rem; color: var(--ctp-overlay1); font-family: var(--term-font-family);",
"{model}"
}
}
}
}