Skip to main content

cinch_web/
ext.rs

1//! Extension trait for domain-specific web UI rendering.
2//!
3//! Domain crates implement [`WebExtensionRenderer`] to provide structured data
4//! that the Next.js frontend can render. Unlike the TUI renderer (which returns
5//! ratatui `Span`s), this returns JSON-serializable data.
6
7use cinch_rs::ui::{QuestionChoice, UiExtension};
8use serde::Serialize;
9
10/// Trait for domain-specific web UI rendering.
11///
12/// Domain crates implement this to provide structured data that the
13/// Next.js frontend can render as status badges, extension panels,
14/// and choice metadata.
15///
16/// # Example
17///
18/// ```ignore
19/// struct MyWebRenderer;
20///
21/// impl WebExtensionRenderer for MyWebRenderer {
22///     fn status_fields(&self, ext: &dyn UiExtension) -> Vec<StatusField> {
23///         vec![StatusField {
24///             label: "Count".into(),
25///             value: "42".into(),
26///             variant: "info".into(),
27///         }]
28///     }
29/// }
30/// ```
31pub trait WebExtensionRenderer: Send + Sync {
32    /// Extra status fields to display in the status bar.
33    ///
34    /// Returns key-value pairs rendered as badges in the web UI.
35    fn status_fields(&self, ext: &dyn UiExtension) -> Vec<StatusField> {
36        let _ = ext;
37        vec![]
38    }
39
40    /// Serialize the current extension state for WebSocket broadcast.
41    ///
42    /// Called after relevant events to push domain-specific state to clients.
43    fn to_ws_json(&self, ext: &dyn UiExtension) -> Option<serde_json::Value> {
44        let _ = ext;
45        None
46    }
47
48    /// Optional metadata to display alongside each question choice.
49    ///
50    /// For example, a tweet agent might return character count and a color
51    /// variant indicating whether the tweet is within the 280-char limit.
52    fn choice_metadata(
53        &self,
54        ext: &dyn UiExtension,
55        choice: &QuestionChoice,
56    ) -> Option<ChoiceMetadata> {
57        let _ = (ext, choice);
58        None
59    }
60}
61
62/// A key-value status field displayed as a badge in the web UI status bar.
63#[derive(Clone, Debug, Serialize)]
64pub struct StatusField {
65    /// Short label (e.g., "Drafted").
66    pub label: String,
67    /// Display value (e.g., "3").
68    pub value: String,
69    /// CSS class variant for styling (e.g., "info", "success", "warning", "error").
70    pub variant: String,
71}
72
73/// Metadata displayed alongside a question choice in the web UI.
74#[derive(Clone, Debug, Serialize)]
75pub struct ChoiceMetadata {
76    /// Display text (e.g., "142 chars").
77    pub text: String,
78    /// CSS class variant for styling.
79    pub variant: String,
80}
81
82/// No-op renderer for agents with no domain-specific web UI.
83pub struct NoWebExtension;
84
85impl WebExtensionRenderer for NoWebExtension {}