Skip to content

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

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

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 и связью.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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