Skip to content

feat(trios-ui + trios-ext/EX-01): Вынести UI в trios-ui по кольцам UR-00..UR-08 — EX-01 становится DOM-мостом #243

@gHashTag

Description

@gHashTag

🎨 feat(trios-ui): Ring-архитектура UI — EX-01 как DOM-мост к trios-ui

Принцип: trios-ext — хрупкий узел. НЕ ТРОГАЕМ ничего кроме EX-01.
EX-01 (dom.rs) перестаёт строить UI сам — он монтирует trios-ui::mount_app() в DOM.
Весь UI (компоненты, стейт, стили) живёт в crates/trios-ui.


🔴 Проблема

Сейчас crates/trios-ext/src/dom.rs строит UI императивно через raw web-sys:

  • document.create_element("div") — вручную
  • set_inner_html(...) — строками
  • CSS инлайном как const STYLE: &str
  • Нет реактивности, нет компонентов, нет state management

Это значит любое UI изменение = трогаем trios-ext = риск сломать EX-00/EX-02/EX-03.
3 дня простоя из-за этой архитектурной ошибки.


🏗️ Целевая архитектура (по #238)

trios-ext (SILVER/BRONZE — не трогать кроме EX-01)
├── EX-00: lib.rs, bg.rs          ← WASM entry, Chrome bootstrap — НЕ ТРОГАТЬ
├── EX-01: dom.rs                 ← DOM-мост: вызывает trios_ui::mount_app()
│         ↕ зависит от ↕
│       trios-ui (новый crate)
│       ├── UR-00: State atoms    ← Jotai-style: use_signal, use_context
│       ├── UR-01: Theme tokens   ← цвета (#D4AF37 gold, #000 bg, #0A0A0A surface)
│       ├── UR-02: Primitives     ← Button, Input, Badge, Spinner
│       ├── UR-03: Layout         ← Sidebar, Header, Tabs (замена tabs из dom.rs)
│       ├── UR-04: Chat           ← ChatHistory, ChatInput, MessageBubble
│       ├── UR-05: AgentList      ← AgentCard, StatusBadge
│       ├── UR-06: McpPanel       ← ToolList, ResultView
│       ├── UR-07: Settings       ← ConfigPanel
│       ├── UR-08: AppShell       ← корневой компонент, AtomProvider
│       └── BR-APP: WASM entry    ← pub fn mount_app()
├── EX-02: mcp.rs                 ← MCP клиент — НЕ ТРОГАТЬ
├── EX-03: src/bridge/            ← Bridge — НЕ ТРОГАТЬ
└── BR-EXT: WASM артефакт         ← финал

📁 Что сейчас в dom.rs (EX-01) — и куда переезжает

Код в dom.rs Переезжает в
const STYLE: &str (CSS токены: #D4AF37, #000, tabs) trios-ui/rings/UR-01 — Theme tokens
build_ui() — header, tabs, sections trios-ui/rings/UR-03 — Layout + trios-ui/rings/UR-08 — AppShell
append_message(), #messages div trios-ui/rings/UR-04 — Chat
set_agent_list() trios-ui/rings/UR-05 — AgentList
set_tool_list() trios-ui/rings/UR-06 — McpPanel
set_status() trios-ui/rings/UR-00 — State (McpConnectionState)
setup_tab_listeners() trios-ui/rings/UR-03 — Tabs (реактивно через Signal)
setup_chat_listener()crate::mcp::mcp_send_chat() trios-ui/rings/UR-04 — ChatInput вызывает колбэк из EX-02

После рефакторинга dom.rs (EX-01) содержит ТОЛЬКО:

// crates/trios-ext/src/dom.rs — ПОСЛЕ
use wasm_bindgen::prelude::*;

/// EX-01: DOM bridge — монтирует trios-ui в браузерный DOM
/// Знает только: куда монтировать. Не знает: что рисовать.
pub fn mount() -> Result<(), JsValue> {
    trios_ui::mount_app();
    Ok(())
}

🔄 State Management — Jotai-style в Dioxus (UR-00)

Аналог Jotai через Dioxus Signal<T> + use_context:

// trios-ui/rings/UR-00/src/lib.rs
use dioxus::prelude::*;

#[derive(Clone, Debug, PartialEq)]
pub enum McpConnectionState {
    Connecting,
    Connected,
    Disconnected,
    Error(String),
}

#[derive(Clone, Debug)]
pub struct ChatMessage {
    pub role: String,   // "you" | "agent" | "error"
    pub body: String,
    pub ts:   String,
}

#[derive(Clone, Debug)]
pub struct AgentState {
    pub id:     String,
    pub name:   String,
    pub status: String, // "Idle" | "Working" | "Error"
}

/// Глобальные атомы — провайдятся через AtomProvider (UR-08)
pub fn use_mcp_state()    -> Signal<McpConnectionState> { use_context() }
pub fn use_chat_messages()-> Signal<Vec<ChatMessage>>   { use_context() }
pub fn use_agents()       -> Signal<Vec<AgentState>>    { use_context() }
pub fn use_tool_list()    -> Signal<Vec<String>>        { use_context() }

🎨 Цвета (из dom.rs → UR-01)

// trios-ui/rings/UR-01/src/lib.rs
pub mod color {
    pub const BG:       &str = "#000000";
    pub const SURFACE:  &str = "#0A0A0A";
    pub const BORDER:   &str = "#1A1A1A";
    pub const GOLD:     &str = "#D4AF37";  // Trinity gold
    pub const TEXT:     &str = "#FFFFFF";
    pub const MUTED:    &str = "#666666";
    pub const ERROR:    &str = "#FF6B6B";
    pub const AGENT_BG: &str = "#0D1117";
}

🔌 Связь EX-01 ↔ trios-ui ↔ EX-02 (MCP)

UI не знает про MCP напрямую. EX-01 передаёт колбэк:

// trios-ui/rings/BR-APP/src/lib.rs
use dioxus::prelude::*;

/// Точка входа — вызывается из EX-01 dom.rs
/// on_send_chat: колбэк из EX-02 (mcp::mcp_send_chat)
pub fn mount_app() {
    dioxus::launch(AppShell);
}

/// Версия с колбэком для интеграции с EX-02
pub fn mount_app_with_mcp(on_chat: impl Fn(String) + 'static) {
    // передаём колбэк через context
    dioxus::launch(move || rsx! { AppShell { on_chat: on_chat } });
}

EX-01 вызывает:

// crates/trios-ext/src/dom.rs — финальный вид
pub fn mount() -> Result<(), JsValue> {
    trios_ui::mount_app_with_mcp(|msg| {
        let _ = crate::mcp::mcp_send_chat(&msg);
    });
    Ok(())
}

📦 Ring-by-ring задачи

UR-00 — State

  • AgentState, ChatMessage, McpConnectionState типы
  • use_agents(), use_chat_messages(), use_mcp_state(), use_tool_list() атомы
  • AtomProvider компонент (провайдит все сигналы)

UR-01 — Theme

  • color::* константы (перенести из dom.rs STYLE)
  • spacing::* константы
  • CSS как Dioxus inline styles (не raw string)

UR-02 — Primitives

  • Button { label, on_click, variant }
  • Input { placeholder, on_enter, value }
  • Badge { text, color }
  • StatusDot { state: McpConnectionState }

UR-03 — Layout

  • Header { title, status } (заменяет <header> из build_ui)
  • Tabs { tabs, active, on_change } (заменяет setup_tab_listeners)
  • TabContent { id, children }

UR-04 — Chat

  • ChatHistory {} — список ChatMessage из UR-00 атома
  • MessageBubble { role, body } (классы: "you", "agent", "error")
  • ChatInput {} — Input с on_enter → колбэк on_chat
  • Scroll-to-bottom при новом сообщении

UR-05 — AgentList

  • AgentCard { agent: AgentState }
  • AgentBoard {} — список агентов из атома
  • Цвета статусов: Idle=#666, Working=#D4AF37, Error=#FF6B6B

UR-06 — McpPanel

  • ToolList {} — список tools из атома
  • ResultView { text }

UR-07 — Settings

  • ConnectionConfig { url, on_change } (порт trios-server)

UR-08 — AppShell

  • AtomProvider {} — use_context_provider для всех сигналов
  • AppShell { on_chat: Callback<String> } — корень
  • Три таба: Chat | Agents | MCP Tools

BR-APP — WASM entry

  • pub fn mount_app()
  • pub fn mount_app_with_mcp(on_chat: impl Fn(String) + 'static)
  • cargo check -p trios-ui --target wasm32-unknown-unknown = 0 errors

EX-01 изменение (ТОЛЬКО ЭТО в trios-ext)

  • dom.rs сокращается до ~10 строк: вызов trios_ui::mount_app_with_mcp(...)
  • style.css удаляется (стили переехали в UR-01)
  • В Cargo.toml добавлена зависимость trios-ui = { path = "../trios-ui" }
  • EX-00, EX-02, EX-03, BR-EXT — не изменены ни на байт

✅ Acceptance Criteria

  • crates/trios-ui/rings/UR-00..UR-08 + BR-APP созданы
  • Каждое кольцо: src/lib.rs + Cargo.toml + README.md + TASK.md + AGENTS.md (I5)
  • cargo check -p trios-ui --target wasm32-unknown-unknown = 0 errors
  • cargo clippy -p trios-ui = 0 warnings
  • dom.rs (EX-01) = ≤ 15 строк, вызывает trios_ui::mount_app_with_mcp
  • style.css удалён из trios-ext
  • cargo build -p trios-ext --target wasm32-unknown-unknown = 0 errors
  • Chrome Extension: сайдбар открывается, Chat/Agents/MCP Tools работают
  • lib.rs, bg.rs, mcp.rs, bridge/, extension/ — git diff = пусто (не тронуты)
  • .trinity/experience/trios-ui-YYYY-MM-DD.md записан

🚫 Запрещено

  • ❌ Трогать src/lib.rs в trios-ext
  • ❌ Трогать src/bg.rs в trios-ext
  • ❌ Трогать src/mcp.rs в trios-ext
  • ❌ Трогать src/bridge/ в trios-ext
  • ❌ Трогать extension/manifest.json, background.js, content.js
  • ❌ Использовать document.create_element в trios-ui (только Dioxus RSX)
  • ❌ Использовать wasm-pack (только wasm-bindgen-cli)
  • ❌ Писать JS бизнес-логику

🔒 Инварианты

ID Правило
I1 cargo build exits 0
I3 clippy 0 warnings
I5 Каждое кольцо: README + TASK + AGENTS
I15 Zero handwritten JS в WASM crates
ARCH-EXT trios-ext git diff содержит только: Cargo.toml (+1 строка) + dom.rs (переписан ≤15 строк) + style.css (удалён)
ARCH-UI trios-ui не импортирует ничего из trios-ext (однонаправленная зависимость)

🔗 References


Priority: P0 — 3 дня простоя UI
Kingdom: UI/Browser
Soul-name агента: "UIWeaver" — ткёт интерфейс отдельно от коннектора. Знает: где граница между UI и связью.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions