Skip to main content

ragrig_gpui/model/
attachment.rs

1//! Composer attachments.
2
3use gpui_kit::assets::IconName;
4
5/// How an attachment chip behaves.
6#[derive(Clone, Debug, PartialEq, Eq)]
7pub enum AttachmentKind {
8    /// A context toggle, rendered selected while `on`.
9    Toggle {
10        /// Current state.
11        on: bool,
12    },
13    /// Context waiting for the next question; it can be dropped.
14    Pending,
15    /// A ready artefact with a host-named action, plus a drop button.
16    Offer {
17        /// Action caption, e.g. `Insert`.
18        action: String,
19    },
20}
21
22/// One chip above the composer.
23#[derive(Clone, Debug, PartialEq, Eq)]
24pub struct Attachment {
25    /// Stable identifier reported back on interaction.
26    pub id: String,
27    /// What the chip offers.
28    pub kind: AttachmentKind,
29    /// Icon shown before the label.
30    pub icon: IconName,
31    /// Chip text, e.g. `draft.md`.
32    pub label: String,
33    /// Optional tooltip.
34    pub hint: Option<String>,
35    /// Whether the chip accepts interaction.
36    pub enabled: bool,
37}
38
39impl Attachment {
40    /// A document-context toggle, initially off.
41    pub fn document(id: impl Into<String>, label: impl Into<String>) -> Self {
42        Self {
43            id: id.into(),
44            kind: AttachmentKind::Toggle { on: false },
45            icon: IconName::FileText,
46            label: label.into(),
47            hint: None,
48            enabled: true,
49        }
50    }
51
52    /// A bibliography-context toggle, initially off.
53    pub fn bibliography(id: impl Into<String>, label: impl Into<String>) -> Self {
54        Self {
55            id: id.into(),
56            kind: AttachmentKind::Toggle { on: false },
57            icon: IconName::BookOpen,
58            label: label.into(),
59            hint: None,
60            enabled: true,
61        }
62    }
63
64    /// Context that waits for the next question.
65    pub fn pending(id: impl Into<String>, label: impl Into<String>) -> Self {
66        Self {
67            id: id.into(),
68            kind: AttachmentKind::Pending,
69            icon: IconName::Paperclip,
70            label: label.into(),
71            hint: None,
72            enabled: true,
73        }
74    }
75
76    /// A ready artefact with an action button.
77    pub fn offer(
78        id: impl Into<String>,
79        label: impl Into<String>,
80        action: impl Into<String>,
81    ) -> Self {
82        Self {
83            id: id.into(),
84            kind: AttachmentKind::Offer {
85                action: action.into(),
86            },
87            icon: IconName::FileText,
88            label: label.into(),
89            hint: None,
90            enabled: true,
91        }
92    }
93
94    /// Set the toggle state (meaningful for [`AttachmentKind::Toggle`]).
95    pub fn on(mut self, on: bool) -> Self {
96        if let AttachmentKind::Toggle { on: state } = &mut self.kind {
97            *state = on;
98        }
99        self
100    }
101
102    /// Set the tooltip.
103    pub fn hint(mut self, hint: impl Into<String>) -> Self {
104        self.hint = Some(hint.into());
105        self
106    }
107
108    /// Enable or disable the whole chip.
109    pub fn enabled(mut self, enabled: bool) -> Self {
110        self.enabled = enabled;
111        self
112    }
113
114    /// Returns whether this is a selected toggle.
115    pub fn is_on(&self) -> bool {
116        matches!(self.kind, AttachmentKind::Toggle { on: true })
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn toggle_state_is_readable_and_settable() {
126        let attachment = Attachment::document("draft", "draft.md");
127        assert!(!attachment.is_on());
128        assert!(attachment.clone().on(true).is_on());
129    }
130
131    #[test]
132    fn kinds_carry_their_own_action() {
133        let offer = Attachment::offer("refs", "references.bib", "Insert");
134        assert_eq!(
135            offer.kind,
136            AttachmentKind::Offer {
137                action: "Insert".to_owned()
138            }
139        );
140        assert_eq!(
141            Attachment::pending("region", "Selection").kind,
142            AttachmentKind::Pending
143        );
144    }
145}