1use ratatui::Frame;
7use ratatui::layout::Rect;
8use ratatui::style::Style;
9use ratatui::widgets::Paragraph;
10
11use crate::components::Component;
12use crate::components::backlinks_panel::QueryPanel;
13use crate::components::drawer_views::{LinksPanel, OutlinePanel, TagsPanel};
14use crate::components::event_state::EventState;
15use crate::components::events::{AppTx, InputEvent};
16use crate::components::panel::panel_block;
17use crate::components::sidebar::SidebarComponent;
18use crate::settings::themes::Theme;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum DrawerView {
24 Files,
25 Find,
26 Tags,
27 Links,
28 Outline,
29 Config,
30}
31
32impl DrawerView {
33 pub fn label(&self) -> &'static str {
35 match self {
36 DrawerView::Files => "FILES",
37 DrawerView::Find => "FIND",
38 DrawerView::Tags => "TAGS",
39 DrawerView::Links => "LINKS",
40 DrawerView::Outline => "OUTLINE",
41 DrawerView::Config => "CFG",
42 }
43 }
44}
45
46#[derive(Default, Clone)]
49pub struct ConfigInfo {
50 pub theme_name: String,
51 pub leader_key: String,
52 pub preferences_key: String,
53 pub leader_timeout_ms: u64,
54 pub config_path: String,
55}
56
57pub struct DrawerHost {
61 active: DrawerView,
62 sidebar: SidebarComponent,
63 query: QueryPanel,
64 tags: TagsPanel,
65 links: LinksPanel,
66 outline: OutlinePanel,
67 config_info: ConfigInfo,
69}
70
71impl DrawerHost {
72 pub fn new(
73 sidebar: SidebarComponent,
74 query: QueryPanel,
75 tags: TagsPanel,
76 links: LinksPanel,
77 outline: OutlinePanel,
78 ) -> Self {
79 Self {
80 active: DrawerView::Files,
81 sidebar,
82 query,
83 tags,
84 links,
85 outline,
86 config_info: ConfigInfo::default(),
87 }
88 }
89
90 pub fn set_config_info(&mut self, info: ConfigInfo) {
92 self.config_info = info;
93 }
94
95 pub fn active_view(&self) -> DrawerView {
96 self.active
97 }
98
99 pub fn is_text_input(&self) -> bool {
104 matches!(self.active, DrawerView::Find)
105 }
106
107 pub fn set_view(&mut self, view: DrawerView) {
108 self.active = view;
109 }
110
111 pub fn sidebar(&self) -> &SidebarComponent {
114 &self.sidebar
115 }
116 pub fn sidebar_mut(&mut self) -> &mut SidebarComponent {
117 &mut self.sidebar
118 }
119 pub fn query(&self) -> &QueryPanel {
120 &self.query
121 }
122 pub fn query_mut(&mut self) -> &mut QueryPanel {
123 &mut self.query
124 }
125 pub fn tags_mut(&mut self) -> &mut TagsPanel {
126 &mut self.tags
127 }
128 pub fn links_mut(&mut self) -> &mut LinksPanel {
129 &mut self.links
130 }
131 pub fn outline_mut(&mut self) -> &mut OutlinePanel {
132 &mut self.outline
133 }
134
135 pub fn hint_shortcuts(&self) -> Vec<(String, String)> {
136 match self.active {
137 DrawerView::Files => self.sidebar.hint_shortcuts(),
138 DrawerView::Find => self.query.hint_shortcuts(),
139 DrawerView::Tags => self.tags.hint_shortcuts(),
140 DrawerView::Links => self.links.hint_shortcuts(),
141 DrawerView::Outline => self.outline.hint_shortcuts(),
142 DrawerView::Config => vec![
143 ("t/⏎".into(), "Theme picker".into()),
144 ("p".into(), "Preferences".into()),
145 ],
146 }
147 }
148
149 pub fn handle_input(&mut self, event: &InputEvent, tx: &AppTx) -> EventState {
150 match self.active {
151 DrawerView::Files => self.sidebar.handle_input(event, tx),
152 DrawerView::Find => {
153 if let InputEvent::Key(key) = event {
156 self.query.handle_key(key, tx)
157 } else {
158 EventState::NotConsumed
159 }
160 }
161 DrawerView::Tags => self.tags.handle_input(event, tx),
162 DrawerView::Links => self.links.handle_input(event, tx),
163 DrawerView::Outline => self.outline.handle_input(event, tx),
164 DrawerView::Config => {
165 if let InputEvent::Key(key) = event {
166 use ratatui::crossterm::event::KeyCode;
167 match key.code {
168 KeyCode::Char('t') | KeyCode::Enter => {
169 tx.send(crate::components::events::AppEvent::ExecuteLeaderAction(
170 crate::keys::leader::LeaderAction::VaultTheme,
171 ))
172 .ok();
173 return EventState::Consumed;
174 }
175 KeyCode::Char('p') => {
176 tx.send(crate::components::events::AppEvent::OpenScreen(
177 crate::components::events::ScreenEvent::OpenPreferences,
178 ))
179 .ok();
180 return EventState::Consumed;
181 }
182 _ => {}
183 }
184 }
185 EventState::NotConsumed
186 }
187 }
188 }
189
190 pub fn handle_mouse(&mut self, event: &InputEvent, tx: &AppTx) {
191 let InputEvent::Mouse(mouse) = event else {
192 return;
193 };
194 match self.active {
195 DrawerView::Find => {
198 self.query.handle_mouse(mouse, tx);
199 }
200 _ => {
201 self.handle_input(event, tx);
202 }
203 }
204 }
205
206 pub fn render(&mut self, f: &mut Frame, rect: Rect, theme: &Theme, focused: bool) {
207 match self.active {
208 DrawerView::Files => self.sidebar.render(f, rect, theme, focused),
209 DrawerView::Find => self.query.render(f, rect, theme, focused),
210 DrawerView::Tags => self.tags.render(f, rect, theme, focused),
211 DrawerView::Links => self.links.render(f, rect, theme, focused),
212 DrawerView::Outline => self.outline.render(f, rect, theme, focused),
213 DrawerView::Config => {
214 let block = panel_block("Config", theme, focused);
215 let inner = block.inner(rect);
216 f.render_widget(block, rect);
217 let info = &self.config_info;
218 let label = Style::default().fg(theme.gray.to_ratatui());
219 let value = Style::default().fg(theme.fg.to_ratatui());
220 let keycap = Style::default().fg(theme.yellow.to_ratatui());
221 let lines = vec![
222 ratatui::text::Line::from(vec![
223 ratatui::text::Span::styled(" theme ", label),
224 ratatui::text::Span::styled(info.theme_name.clone(), value),
225 ]),
226 ratatui::text::Line::from(vec![
227 ratatui::text::Span::styled(" leader ", label),
228 ratatui::text::Span::styled(info.leader_key.clone(), value),
229 ]),
230 ratatui::text::Line::from(vec![
231 ratatui::text::Span::styled(" prefs ", label),
232 ratatui::text::Span::styled(info.preferences_key.clone(), value),
233 ]),
234 ratatui::text::Line::from(vec![
235 ratatui::text::Span::styled(" timeout ", label),
236 ratatui::text::Span::styled(
237 format!("{} ms (which-key reveal)", info.leader_timeout_ms),
238 value,
239 ),
240 ]),
241 ratatui::text::Line::from(vec![
242 ratatui::text::Span::styled(" config ", label),
243 ratatui::text::Span::styled(info.config_path.clone(), value),
244 ]),
245 ratatui::text::Line::default(),
246 ratatui::text::Line::from(vec![
247 ratatui::text::Span::styled(" t ", keycap),
248 ratatui::text::Span::styled("theme picker", label),
249 ]),
250 ratatui::text::Line::from(vec![
251 ratatui::text::Span::styled(" p ", keycap),
252 ratatui::text::Span::styled("preferences", label),
253 ]),
254 ];
255 f.render_widget(Paragraph::new(lines), inner);
256 }
257 }
258 }
259}