chore: monorepo inicial con arje + minga + yahweh absorbidos
Workspace en 4 ejes (core/modules/apps/shared):
- core/: 24 crates de arje (Init systemd-compatible: ente-card, ente-zero,
ente-kernel, ente-bus, ente-cas, ente-soma, ente-wasm, ente-snapshot,
ente-brain, ente-echo, ente-policy-provider, + 12 crates *-compat)
- modules/semantic_dht/: 5 crates de minga (minga-core con AST/CAS/MST,
minga-p2p con libp2p Kad, minga-store, minga-vfs, minga-cli)
- modules/ui_engine/: 11 crates de yahweh (libs/{core,theme,bus,providers},
widgets/{tree,splitter,tabs,tiled,container_core,text_input})
- apps/: 5 crates de yahweh (file_explorer, database_explorer, text_viewer,
image_viewer, yahweh-shell)
- shared_wit/protocol.wit: handshake/lifecycle inicial
Cargo.toml unificado: thiserror bumped a 2 (transparente para arje), tokio
"full", paths intra-workspace de yahweh redirigidos a su nueva ubicación.
cargo check --workspace: 0 errores, 17 warnings (dead code preexistente).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
//! `yahweh_theme` — paleta de colores y backgrounds compartidos.
|
||||
//!
|
||||
//! El `Theme` se instala como `Global` de GPUI. Los widgets lo leen vía
|
||||
//! `cx.global::<Theme>()` durante su `render`, y se subscriben con
|
||||
//! `cx.observe_global::<Theme>(…)` para auto-redibujarse cuando cambia.
|
||||
//!
|
||||
//! Filosofía: el theme es **dato puro** (sin lógica de UI). No conoce a
|
||||
//! ningún widget concreto. Cada widget pide slots semánticos (panel_bg,
|
||||
//! row_hover, accent…) sin acoplarse a colores hex específicos.
|
||||
|
||||
use gpui::{Background, Global, Hsla, hsla, linear_color_stop, linear_gradient};
|
||||
|
||||
/// Paleta semántica del theme. Cada slot tiene un nombre funcional, no
|
||||
/// cromático — así los widgets piden "fondo de panel" sin acoplarse a
|
||||
/// "azul oscuro".
|
||||
///
|
||||
/// Convención de slots:
|
||||
/// - `bg_*` se devuelve como `Background` (soporta gradientes); los widgets
|
||||
/// lo pasan a `.bg(...)` directamente.
|
||||
/// - `fg_*`, `accent`, `border` son `Hsla` (colores planos para texto y
|
||||
/// ornamentos).
|
||||
/// - `bg_row_*` son `Hsla` porque las filas de una lista virtualizada se
|
||||
/// beneficiarían poco de un gradiente individual.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Theme {
|
||||
pub name: &'static str,
|
||||
pub is_dark: bool,
|
||||
|
||||
// Fondos.
|
||||
pub bg_app: Background,
|
||||
pub bg_panel: Background,
|
||||
pub bg_panel_alt: Background,
|
||||
pub bg_row_hover: Hsla,
|
||||
pub bg_row_active: Hsla,
|
||||
|
||||
// Foregrounds.
|
||||
pub fg_text: Hsla,
|
||||
pub fg_muted: Hsla,
|
||||
pub fg_disabled: Hsla,
|
||||
|
||||
// Acentos y ornamentos.
|
||||
pub accent: Hsla,
|
||||
pub accent_strong: Hsla,
|
||||
pub border: Hsla,
|
||||
pub border_strong: Hsla,
|
||||
|
||||
/// Marker colors para indicar "este file está abierto en container N".
|
||||
/// Paleta circular — el N-ésimo container usa `marker_palette[n % len]`.
|
||||
pub marker_palette: Vec<Hsla>,
|
||||
}
|
||||
|
||||
impl Global for Theme {}
|
||||
|
||||
impl Theme {
|
||||
pub fn global(cx: &gpui::App) -> &Self {
|
||||
cx.global::<Self>()
|
||||
}
|
||||
|
||||
/// Default — primer preset de `all()`. La Shell lo carga al boot si no
|
||||
/// hay otro persistido.
|
||||
pub fn install_default(cx: &mut gpui::App) {
|
||||
cx.set_global(Self::nebula());
|
||||
}
|
||||
|
||||
/// Reemplaza el theme global. GPUI notifica a todos los `observe_global`
|
||||
/// suscriptores en el siguiente frame.
|
||||
pub fn set(cx: &mut gpui::App, theme: Self) {
|
||||
cx.set_global(theme);
|
||||
}
|
||||
|
||||
/// Lista todos los presets en orden estable. Usado por el switcher para
|
||||
/// ciclar y por el menú de "Tema" cuando lo agreguemos.
|
||||
pub fn all() -> Vec<Self> {
|
||||
vec![
|
||||
Self::nebula(),
|
||||
Self::aurora(),
|
||||
Self::sunset(),
|
||||
Self::flat_dark(),
|
||||
Self::solarized_light(),
|
||||
Self::high_contrast(),
|
||||
]
|
||||
}
|
||||
|
||||
/// Devuelve el preset cuyo `name` matchea (case-insensitive). `None` si
|
||||
/// el nombre no existe — útil para validar input de usuario al cargar
|
||||
/// preferencias persistidas.
|
||||
pub fn by_name(name: &str) -> Option<Self> {
|
||||
Self::all()
|
||||
.into_iter()
|
||||
.find(|t| t.name.eq_ignore_ascii_case(name))
|
||||
}
|
||||
|
||||
/// Próximo preset en la rotación de `all()`. Si `current` no está, se
|
||||
/// vuelve al primero. La rotación es circular (último → primero).
|
||||
pub fn next_after(current: &str) -> Self {
|
||||
let all = Self::all();
|
||||
let idx = all.iter().position(|t| t.name == current);
|
||||
match idx {
|
||||
Some(i) => all[(i + 1) % all.len()].clone(),
|
||||
None => all[0].clone(),
|
||||
}
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Presets
|
||||
// =====================================================================
|
||||
|
||||
/// **Nebula** — default. Gradiente vertical violáceo profundo → teal
|
||||
/// medianoche. Pensado para sentirse moderno y descansado de noche.
|
||||
pub fn nebula() -> Self {
|
||||
let bg_app = linear_gradient(
|
||||
165.0,
|
||||
linear_color_stop(hsla(265.0 / 360.0, 0.38, 0.07, 1.0), 0.0),
|
||||
linear_color_stop(hsla(195.0 / 360.0, 0.42, 0.09, 1.0), 1.0),
|
||||
);
|
||||
let bg_panel = linear_gradient(
|
||||
165.0,
|
||||
linear_color_stop(hsla(245.0 / 360.0, 0.28, 0.10, 1.0), 0.0),
|
||||
linear_color_stop(hsla(210.0 / 360.0, 0.30, 0.12, 1.0), 1.0),
|
||||
);
|
||||
let bg_panel_alt = linear_gradient(
|
||||
165.0,
|
||||
linear_color_stop(hsla(255.0 / 360.0, 0.25, 0.13, 1.0), 0.0),
|
||||
linear_color_stop(hsla(220.0 / 360.0, 0.27, 0.14, 1.0), 1.0),
|
||||
);
|
||||
|
||||
Self {
|
||||
name: "Nebula",
|
||||
is_dark: true,
|
||||
bg_app,
|
||||
bg_panel,
|
||||
bg_panel_alt,
|
||||
bg_row_hover: hsla(220.0 / 360.0, 0.30, 0.20, 0.45),
|
||||
bg_row_active: hsla(280.0 / 360.0, 0.55, 0.28, 0.65),
|
||||
fg_text: hsla(210.0 / 360.0, 0.35, 0.88, 1.0),
|
||||
fg_muted: hsla(215.0 / 360.0, 0.22, 0.58, 1.0),
|
||||
fg_disabled: hsla(215.0 / 360.0, 0.10, 0.40, 1.0),
|
||||
accent: hsla(280.0 / 360.0, 0.65, 0.65, 1.0),
|
||||
accent_strong: hsla(285.0 / 360.0, 0.78, 0.74, 1.0),
|
||||
border: hsla(225.0 / 360.0, 0.20, 0.22, 1.0),
|
||||
border_strong: hsla(280.0 / 360.0, 0.40, 0.45, 1.0),
|
||||
marker_palette: vec![
|
||||
hsla(280.0 / 360.0, 0.65, 0.55, 0.45),
|
||||
hsla(195.0 / 360.0, 0.65, 0.50, 0.45),
|
||||
hsla(35.0 / 360.0, 0.75, 0.55, 0.45),
|
||||
hsla(135.0 / 360.0, 0.55, 0.50, 0.45),
|
||||
hsla(0.0, 0.60, 0.55, 0.45),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// **Aurora** — verdes-cian-azul, evoca aurora boreal. Más frío que
|
||||
/// Nebula, contraste alto.
|
||||
pub fn aurora() -> Self {
|
||||
let bg_app = linear_gradient(
|
||||
190.0,
|
||||
linear_color_stop(hsla(170.0 / 360.0, 0.45, 0.06, 1.0), 0.0),
|
||||
linear_color_stop(hsla(220.0 / 360.0, 0.50, 0.09, 1.0), 1.0),
|
||||
);
|
||||
let bg_panel = linear_gradient(
|
||||
190.0,
|
||||
linear_color_stop(hsla(165.0 / 360.0, 0.32, 0.10, 1.0), 0.0),
|
||||
linear_color_stop(hsla(215.0 / 360.0, 0.36, 0.12, 1.0), 1.0),
|
||||
);
|
||||
let bg_panel_alt = linear_gradient(
|
||||
190.0,
|
||||
linear_color_stop(hsla(170.0 / 360.0, 0.30, 0.13, 1.0), 0.0),
|
||||
linear_color_stop(hsla(220.0 / 360.0, 0.32, 0.15, 1.0), 1.0),
|
||||
);
|
||||
|
||||
Self {
|
||||
name: "Aurora",
|
||||
is_dark: true,
|
||||
bg_app,
|
||||
bg_panel,
|
||||
bg_panel_alt,
|
||||
bg_row_hover: hsla(180.0 / 360.0, 0.40, 0.22, 0.50),
|
||||
bg_row_active: hsla(160.0 / 360.0, 0.55, 0.30, 0.65),
|
||||
fg_text: hsla(180.0 / 360.0, 0.20, 0.92, 1.0),
|
||||
fg_muted: hsla(185.0 / 360.0, 0.18, 0.62, 1.0),
|
||||
fg_disabled: hsla(185.0 / 360.0, 0.10, 0.40, 1.0),
|
||||
accent: hsla(150.0 / 360.0, 0.70, 0.55, 1.0),
|
||||
accent_strong: hsla(160.0 / 360.0, 0.85, 0.65, 1.0),
|
||||
border: hsla(195.0 / 360.0, 0.25, 0.20, 1.0),
|
||||
border_strong: hsla(160.0 / 360.0, 0.55, 0.45, 1.0),
|
||||
marker_palette: vec![
|
||||
hsla(150.0 / 360.0, 0.75, 0.50, 0.45),
|
||||
hsla(195.0 / 360.0, 0.70, 0.50, 0.45),
|
||||
hsla(225.0 / 360.0, 0.70, 0.55, 0.45),
|
||||
hsla(85.0 / 360.0, 0.65, 0.50, 0.45),
|
||||
hsla(330.0 / 360.0, 0.65, 0.55, 0.45),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// **Sunset** — naranjas-rosas-violetas profundos. Cálido, alto contraste
|
||||
/// con texto claro.
|
||||
pub fn sunset() -> Self {
|
||||
let bg_app = linear_gradient(
|
||||
170.0,
|
||||
linear_color_stop(hsla(20.0 / 360.0, 0.50, 0.08, 1.0), 0.0),
|
||||
linear_color_stop(hsla(310.0 / 360.0, 0.45, 0.10, 1.0), 1.0),
|
||||
);
|
||||
let bg_panel = linear_gradient(
|
||||
170.0,
|
||||
linear_color_stop(hsla(15.0 / 360.0, 0.32, 0.12, 1.0), 0.0),
|
||||
linear_color_stop(hsla(315.0 / 360.0, 0.30, 0.13, 1.0), 1.0),
|
||||
);
|
||||
let bg_panel_alt = linear_gradient(
|
||||
170.0,
|
||||
linear_color_stop(hsla(20.0 / 360.0, 0.30, 0.15, 1.0), 0.0),
|
||||
linear_color_stop(hsla(320.0 / 360.0, 0.28, 0.16, 1.0), 1.0),
|
||||
);
|
||||
|
||||
Self {
|
||||
name: "Sunset",
|
||||
is_dark: true,
|
||||
bg_app,
|
||||
bg_panel,
|
||||
bg_panel_alt,
|
||||
bg_row_hover: hsla(25.0 / 360.0, 0.40, 0.25, 0.45),
|
||||
bg_row_active: hsla(5.0 / 360.0, 0.55, 0.32, 0.65),
|
||||
fg_text: hsla(30.0 / 360.0, 0.30, 0.92, 1.0),
|
||||
fg_muted: hsla(25.0 / 360.0, 0.20, 0.62, 1.0),
|
||||
fg_disabled: hsla(25.0 / 360.0, 0.10, 0.42, 1.0),
|
||||
accent: hsla(15.0 / 360.0, 0.78, 0.62, 1.0),
|
||||
accent_strong: hsla(355.0 / 360.0, 0.85, 0.68, 1.0),
|
||||
border: hsla(15.0 / 360.0, 0.25, 0.25, 1.0),
|
||||
border_strong: hsla(355.0 / 360.0, 0.55, 0.45, 1.0),
|
||||
marker_palette: vec![
|
||||
hsla(15.0 / 360.0, 0.80, 0.55, 0.45),
|
||||
hsla(310.0 / 360.0, 0.65, 0.55, 0.45),
|
||||
hsla(45.0 / 360.0, 0.80, 0.55, 0.45),
|
||||
hsla(285.0 / 360.0, 0.65, 0.60, 0.45),
|
||||
hsla(355.0 / 360.0, 0.70, 0.55, 0.45),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// **Flat Dark** — sin gradientes, paleta cool gris-azulado. Para quien
|
||||
/// prefiere monocromía. Útil para contrastar visualmente con los temas
|
||||
/// de gradiente.
|
||||
pub fn flat_dark() -> Self {
|
||||
let bg_app: Background = hsla(220.0 / 360.0, 0.15, 0.09, 1.0).into();
|
||||
let bg_panel: Background = hsla(220.0 / 360.0, 0.15, 0.12, 1.0).into();
|
||||
let bg_panel_alt: Background = hsla(220.0 / 360.0, 0.15, 0.14, 1.0).into();
|
||||
Self {
|
||||
name: "Flat Dark",
|
||||
is_dark: true,
|
||||
bg_app,
|
||||
bg_panel,
|
||||
bg_panel_alt,
|
||||
bg_row_hover: hsla(220.0 / 360.0, 0.20, 0.20, 1.0),
|
||||
bg_row_active: hsla(220.0 / 360.0, 0.40, 0.30, 1.0),
|
||||
fg_text: hsla(210.0 / 360.0, 0.20, 0.85, 1.0),
|
||||
fg_muted: hsla(215.0 / 360.0, 0.15, 0.55, 1.0),
|
||||
fg_disabled: hsla(215.0 / 360.0, 0.10, 0.40, 1.0),
|
||||
accent: hsla(210.0 / 360.0, 0.70, 0.55, 1.0),
|
||||
accent_strong: hsla(210.0 / 360.0, 0.85, 0.65, 1.0),
|
||||
border: hsla(220.0 / 360.0, 0.15, 0.20, 1.0),
|
||||
border_strong: hsla(220.0 / 360.0, 0.30, 0.35, 1.0),
|
||||
marker_palette: vec![
|
||||
hsla(210.0 / 360.0, 0.65, 0.55, 0.40),
|
||||
hsla(160.0 / 360.0, 0.55, 0.50, 0.40),
|
||||
hsla(30.0 / 360.0, 0.75, 0.55, 0.40),
|
||||
hsla(0.0, 0.55, 0.55, 0.40),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// **Solarized Light** — preset claro inspirado en la paleta clásica de
|
||||
/// Schoonover. Sin gradientes (en light un gradiente sutil pasa
|
||||
/// desapercibido y solo introduce ruido).
|
||||
pub fn solarized_light() -> Self {
|
||||
let bg_app: Background = hsla(44.0 / 360.0, 0.87, 0.94, 1.0).into();
|
||||
let bg_panel: Background = hsla(46.0 / 360.0, 0.42, 0.88, 1.0).into();
|
||||
let bg_panel_alt: Background = hsla(46.0 / 360.0, 0.42, 0.92, 1.0).into();
|
||||
Self {
|
||||
name: "Solarized Light",
|
||||
is_dark: false,
|
||||
bg_app,
|
||||
bg_panel,
|
||||
bg_panel_alt,
|
||||
bg_row_hover: hsla(46.0 / 360.0, 0.45, 0.80, 0.65),
|
||||
bg_row_active: hsla(45.0 / 360.0, 0.55, 0.72, 0.85),
|
||||
fg_text: hsla(196.0 / 360.0, 0.13, 0.30, 1.0),
|
||||
fg_muted: hsla(196.0 / 360.0, 0.13, 0.45, 1.0),
|
||||
fg_disabled: hsla(196.0 / 360.0, 0.10, 0.62, 1.0),
|
||||
accent: hsla(205.0 / 360.0, 0.69, 0.42, 1.0),
|
||||
accent_strong: hsla(205.0 / 360.0, 0.82, 0.38, 1.0),
|
||||
border: hsla(46.0 / 360.0, 0.30, 0.78, 1.0),
|
||||
border_strong: hsla(205.0 / 360.0, 0.40, 0.55, 1.0),
|
||||
marker_palette: vec![
|
||||
hsla(205.0 / 360.0, 0.69, 0.42, 0.30),
|
||||
hsla(175.0 / 360.0, 0.74, 0.32, 0.30),
|
||||
hsla(45.0 / 360.0, 1.00, 0.36, 0.30),
|
||||
hsla(331.0 / 360.0, 0.74, 0.42, 0.30),
|
||||
hsla(18.0 / 360.0, 0.89, 0.40, 0.30),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
/// **High Contrast** — accesibilidad. Negro puro con texto blanco y
|
||||
/// ornamentos amarillo/verde fuertes. Suficientemente diferente para
|
||||
/// notar inmediatamente al usar el switcher.
|
||||
pub fn high_contrast() -> Self {
|
||||
let bg_app: Background = hsla(0.0, 0.0, 0.0, 1.0).into();
|
||||
let bg_panel: Background = hsla(0.0, 0.0, 0.05, 1.0).into();
|
||||
let bg_panel_alt: Background = hsla(0.0, 0.0, 0.10, 1.0).into();
|
||||
Self {
|
||||
name: "High Contrast",
|
||||
is_dark: true,
|
||||
bg_app,
|
||||
bg_panel,
|
||||
bg_panel_alt,
|
||||
bg_row_hover: hsla(60.0 / 360.0, 1.00, 0.50, 0.35),
|
||||
bg_row_active: hsla(120.0 / 360.0, 1.00, 0.40, 0.55),
|
||||
fg_text: hsla(0.0, 0.0, 1.0, 1.0),
|
||||
fg_muted: hsla(0.0, 0.0, 0.75, 1.0),
|
||||
fg_disabled: hsla(0.0, 0.0, 0.50, 1.0),
|
||||
accent: hsla(60.0 / 360.0, 1.00, 0.60, 1.0),
|
||||
accent_strong: hsla(60.0 / 360.0, 1.00, 0.75, 1.0),
|
||||
border: hsla(0.0, 0.0, 0.30, 1.0),
|
||||
border_strong: hsla(60.0 / 360.0, 1.00, 0.60, 1.0),
|
||||
marker_palette: vec![
|
||||
hsla(60.0 / 360.0, 1.00, 0.55, 0.50),
|
||||
hsla(120.0 / 360.0, 1.00, 0.50, 0.50),
|
||||
hsla(180.0 / 360.0, 1.00, 0.55, 0.50),
|
||||
hsla(0.0, 1.00, 0.60, 0.50),
|
||||
hsla(300.0 / 360.0, 1.00, 0.65, 0.50),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user