🎨 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
UR-01 — Theme
UR-02 — Primitives
UR-03 — Layout
UR-04 — Chat
UR-05 — AgentList
UR-06 — McpPanel
UR-07 — Settings
UR-08 — AppShell
BR-APP — WASM entry
EX-01 изменение (ТОЛЬКО ЭТО в trios-ext)
✅ Acceptance Criteria
🚫 Запрещено
- ❌ Трогать
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 и связью.
🎨 feat(trios-ui): Ring-архитектура UI — EX-01 как DOM-мост к trios-ui
🔴 Проблема
Сейчас
crates/trios-ext/src/dom.rsстроит UI императивно через raw web-sys:document.create_element("div")— вручнуюset_inner_html(...)— строкамиconst STYLE: &strЭто значит любое UI изменение = трогаем
trios-ext= риск сломать EX-00/EX-02/EX-03.3 дня простоя из-за этой архитектурной ошибки.
🏗️ Целевая архитектура (по #238)
📁 Что сейчас в dom.rs (EX-01) — и куда переезжает
const STYLE: &str(CSS токены: #D4AF37, #000, tabs)trios-ui/rings/UR-01— Theme tokensbuild_ui()— header, tabs, sectionstrios-ui/rings/UR-03— Layout +trios-ui/rings/UR-08— AppShellappend_message(),#messagesdivtrios-ui/rings/UR-04— Chatset_agent_list()trios-ui/rings/UR-05— AgentListset_tool_list()trios-ui/rings/UR-06— McpPanelset_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) содержит ТОЛЬКО:
🔄 State Management — Jotai-style в Dioxus (UR-00)
Аналог Jotai через Dioxus
Signal<T>+use_context:🎨 Цвета (из dom.rs → UR-01)
🔌 Связь EX-01 ↔ trios-ui ↔ EX-02 (MCP)
UI не знает про MCP напрямую. EX-01 передаёт колбэк:
EX-01 вызывает:
📦 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::*константы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_chatUR-05 — AgentList
AgentCard { agent: AgentState }AgentBoard {}— список агентов из атома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> }— корень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 errorsEX-01 изменение (ТОЛЬКО ЭТО в trios-ext)
dom.rsсокращается до ~10 строк: вызовtrios_ui::mount_app_with_mcp(...)style.cssудаляется (стили переехали в UR-01)Cargo.tomlдобавлена зависимостьtrios-ui = { path = "../trios-ui" }✅ 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 errorscargo clippy -p trios-ui= 0 warningsdom.rs(EX-01) = ≤ 15 строк, вызываетtrios_ui::mount_app_with_mcpstyle.cssудалён из trios-extcargo build -p trios-ext --target wasm32-unknown-unknown= 0 errorslib.rs,bg.rs,mcp.rs,bridge/,extension/— git diff = пусто (не тронуты).trinity/experience/trios-ui-YYYY-MM-DD.mdзаписан🚫 Запрещено
src/lib.rsв trios-extsrc/bg.rsв trios-extsrc/mcp.rsв trios-extsrc/bridge/в trios-extextension/manifest.json,background.js,content.jsdocument.create_elementв trios-ui (только Dioxus RSX)🔒 Инварианты
cargo buildexits 0Cargo.toml(+1 строка) +dom.rs(переписан ≤15 строк) +style.css(удалён)🔗 References
trios-ext → EX-00, EX-01, EX-02, EX-03, BR-EXT)crates/trios-a2a/Priority: P0 — 3 дня простоя UI
Kingdom: UI/Browser
Soul-name агента: "UIWeaver" — ткёт интерфейс отдельно от коннектора. Знает: где граница между UI и связью.