gui: add a placeholder notes search bar to the header
Build Debug APK / build (push) Failing after 17m21s
Build Debug APK / build (push) Failing after 17m21s
Adds NotesSearchBar as its own component (rust/gui-app/src/agents/notes/search_bar.rs) so a richer search interface can grow there later. Moves the note title/timestamps out of the shared header into a compact bordered row atop the content column, freeing the header for the "Notes" label plus the search bar, which is left-aligned with the content column and capped at 500px wide.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
//! executor and folds back into the view via `Entity::update`.
|
||||
|
||||
pub mod editor;
|
||||
pub mod search_bar;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -25,6 +26,7 @@ use synchronicity::node::NodePubkey;
|
||||
use synchronicity::stores::Store;
|
||||
|
||||
use self::editor::{NoteEditor, NoteEditorEvent};
|
||||
use self::search_bar::NotesSearchBar;
|
||||
use crate::agent_sidebar::{AgentSidebar, View};
|
||||
use crate::components::button::button;
|
||||
use crate::components::hierarchical_sidebar::{
|
||||
@@ -143,6 +145,8 @@ pub struct NotesView {
|
||||
_context_menu_sub: Option<Subscription>,
|
||||
dialog: Option<Dialog>,
|
||||
error: Option<SharedString>,
|
||||
/// Placeholder search bar shown at the right edge of the header.
|
||||
search_bar: Entity<NotesSearchBar>,
|
||||
}
|
||||
|
||||
impl NotesView {
|
||||
@@ -179,6 +183,8 @@ impl NotesView {
|
||||
})
|
||||
.detach();
|
||||
|
||||
let search_bar = cx.new(NotesSearchBar::new);
|
||||
|
||||
let mut this = Self {
|
||||
store,
|
||||
node,
|
||||
@@ -201,6 +207,7 @@ impl NotesView {
|
||||
_context_menu_sub: None,
|
||||
dialog: None,
|
||||
error: None,
|
||||
search_bar,
|
||||
};
|
||||
this.reload_notebooks(None, cx);
|
||||
this
|
||||
@@ -662,12 +669,22 @@ impl Render for NotesView {
|
||||
self.ensure_context_menu(window, cx);
|
||||
|
||||
let dragging = self.sidebar_resize.is_dragging();
|
||||
let content_column = div()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.flex_1()
|
||||
.min_w(px(0.))
|
||||
.min_h(px(0.))
|
||||
.when_some(self.note_meta.as_ref(), |c, meta| {
|
||||
c.child(self.render_note_meta_row(&theme, meta, tz))
|
||||
})
|
||||
.child(self.render_content(&theme));
|
||||
let root = div()
|
||||
.relative()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.size_full()
|
||||
.child(self.render_header(&theme, tz))
|
||||
.child(self.render_header(&theme))
|
||||
.child(
|
||||
div()
|
||||
.flex()
|
||||
@@ -682,7 +699,7 @@ impl Render for NotesView {
|
||||
cx,
|
||||
|this| &mut this.sidebar_resize,
|
||||
))
|
||||
.child(self.render_content(&theme)),
|
||||
.child(content_column),
|
||||
)
|
||||
.when_some(self.render_context_menu(), |c, menu| c.child(menu))
|
||||
.when(self.dialog.is_some(), |c| c.child(self.render_dialog(&theme, cx)))
|
||||
@@ -692,7 +709,12 @@ impl Render for NotesView {
|
||||
}
|
||||
|
||||
impl NotesView {
|
||||
fn render_header(&self, theme: &Theme, tz: Option<chrono_tz::Tz>) -> impl IntoElement + use<> {
|
||||
/// The top bar: the "Notes" label over the sidebar column, and the search
|
||||
/// bar starting at the content column's left edge (same as the note meta
|
||||
/// row below it). The note meta (title + timestamps) used to live here but
|
||||
/// now sits in its own compact row atop the content column, so it stays
|
||||
/// clear of the sidebar.
|
||||
fn render_header(&self, theme: &Theme) -> impl IntoElement + use<> {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
@@ -703,7 +725,7 @@ impl NotesView {
|
||||
.border_color(theme.border_default)
|
||||
.child(
|
||||
// The "Notes" label sits over the sidebar column; its width plus the
|
||||
// resize divider lines up the note meta with the content pane's edge.
|
||||
// resize divider lines up the content column below with its edge.
|
||||
div()
|
||||
.w(self.sidebar_resize.width() + px(resizable::DIVIDER_WIDTH))
|
||||
.flex_none()
|
||||
@@ -713,16 +735,16 @@ impl NotesView {
|
||||
.text_color(theme.text_primary)
|
||||
.child("Notes"),
|
||||
)
|
||||
.when_some(self.note_meta.as_ref(), |c, meta| {
|
||||
c.child(self.render_note_meta(theme, meta, tz))
|
||||
})
|
||||
.child(div().flex_1().min_w(px(0.)).flex().px_4().child(self.search_bar.clone()))
|
||||
}
|
||||
|
||||
/// The active note's title with its created and last-modified timestamps
|
||||
/// beneath, shown in the header bar starting at the content pane's left edge.
|
||||
/// A compact row atop the content column showing the open note's title and
|
||||
/// its created/modified timestamps on a single line, bordered below to
|
||||
/// separate it from the note body. Bounded on the left by the sidebar since
|
||||
/// it lives inside the content column, not the shared header.
|
||||
/// The title is read live from [`Self::items`] (the tree's single source of
|
||||
/// truth) so an inline rename is reflected without re-selecting the note.
|
||||
fn render_note_meta(
|
||||
fn render_note_meta_row(
|
||||
&self,
|
||||
theme: &Theme,
|
||||
meta: &NoteMeta,
|
||||
@@ -735,15 +757,18 @@ impl NotesView {
|
||||
.map(|info| info.title.clone())
|
||||
.unwrap_or_else(|| DEFAULT_ITEM_NAME.into());
|
||||
div()
|
||||
.flex_none()
|
||||
.flex()
|
||||
.flex_col()
|
||||
.justify_center()
|
||||
.gap_1()
|
||||
.flex_1()
|
||||
.min_w_0()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.gap_3()
|
||||
.h(px(36.))
|
||||
.px_4()
|
||||
.border_b_1()
|
||||
.border_color(theme.border_default)
|
||||
.child(
|
||||
div()
|
||||
.flex_none()
|
||||
.text_base()
|
||||
.font_weight(FontWeight::BOLD)
|
||||
.text_color(theme.text_primary)
|
||||
@@ -755,7 +780,8 @@ impl NotesView {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.gap_4()
|
||||
.flex_none()
|
||||
.gap_3()
|
||||
.text_size(px(12.))
|
||||
.text_color(theme.text_secondary)
|
||||
.child(div().child(SharedString::from(format!(
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
//! The notes search bar shown at the right edge of the header.
|
||||
//!
|
||||
//! Currently just a text field with no wired-up behavior — a placeholder for
|
||||
//! what should grow into a richer search interface (scoping to a notebook,
|
||||
//! filters, live results, etc.). It's kept as its own component from the start
|
||||
//! so that growth happens here rather than in [`super::NotesView`].
|
||||
|
||||
use gpui::{Context, Entity, IntoElement, Render, Window, div, prelude::*, px};
|
||||
|
||||
use crate::components::text_input::TextInput;
|
||||
|
||||
/// A search field this wide gives room for a richer query (filters, scoped
|
||||
/// terms) while staying well under the ~600px line length past which a
|
||||
/// single-line field gets hard to scan; it also keeps the bar from
|
||||
/// stretching across ultrawide monitors.
|
||||
const MAX_WIDTH: f32 = 500.;
|
||||
|
||||
pub struct NotesSearchBar {
|
||||
input: Entity<TextInput>,
|
||||
}
|
||||
|
||||
impl NotesSearchBar {
|
||||
pub fn new(cx: &mut Context<Self>) -> Self {
|
||||
let input = cx.new(|cx| TextInput::new(cx, "Search notes…"));
|
||||
Self { input }
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for NotesSearchBar {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
div()
|
||||
.flex()
|
||||
.flex_row()
|
||||
.items_center()
|
||||
.w_full()
|
||||
.max_w(px(MAX_WIDTH))
|
||||
.min_w(px(0.))
|
||||
.child(self.input.clone())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user