Compare commits
3 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d540c6bb9 | ||
|
|
e27c497df8 | ||
|
|
03517d9b0f |
9 changed files with 52 additions and 19 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -40,3 +40,6 @@ logs/
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
# Local hygiene notes (not for commit)
|
# Local hygiene notes (not for commit)
|
||||||
|
|
||||||
|
# graphify knowledge-graph output (local tool cache, not for commit)
|
||||||
|
graphify-out/
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ breadsearch indexes your files so you can find things instantly, without
|
||||||
digging through folders.
|
digging through folders.
|
||||||
|
|
||||||
- Open it from BOS Settings, or run it directly.
|
- Open it from BOS Settings, or run it directly.
|
||||||
- Type a few letters of a filename — results update as you type.
|
- Describe what you are looking for in plain language — breadsearch ranks files by meaning, not by letters in a filename. Results update as you type.
|
||||||
|
|
||||||
- [Run]breadsearch|Open File Search[/Run]
|
- [Run]breadsearch|Open File Search[/Run]
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@ nothing here is mandatory either, click around if you'd rather.
|
||||||
- [Show Keybind]super + space[/Show Keybind] opens the launcher (breadbox)
|
- [Show Keybind]super + space[/Show Keybind] opens the launcher (breadbox)
|
||||||
- [Show Keybind]super + e[/Show Keybind] opens files (nautilus)
|
- [Show Keybind]super + e[/Show Keybind] opens files (nautilus)
|
||||||
- [Show Keybind]super + b[/Show Keybind] opens the browser
|
- [Show Keybind]super + b[/Show Keybind] opens the browser
|
||||||
- [Show Keybind]super + u[/Show Keybind] opens notes
|
- [Show Keybind]super + u[/Show Keybind] opens notes (breadpad)
|
||||||
|
- [Show Keybind]super + ,[/Show Keybind] opens BOS Settings
|
||||||
- [Show Keybind]super + l[/Show Keybind] locks the screen
|
- [Show Keybind]super + l[/Show Keybind] locks the screen
|
||||||
|
|
||||||
## Screenshots
|
## Screenshots
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,23 @@ pub enum Difficulty {
|
||||||
#[derive(serde::Deserialize, Clone, Default)]
|
#[derive(serde::Deserialize, Clone, Default)]
|
||||||
pub struct GuideMeta {
|
pub struct GuideMeta {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
|
/// Not read by any UI yet — reserved for a future difficulty badge.
|
||||||
|
/// Real content already sets it, so kept rather than dropped.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub difficulty: Difficulty,
|
pub difficulty: Difficulty,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tags: Vec<String>,
|
pub tags: Vec<String>,
|
||||||
/// Guide dir-names in the same or another category. Unresolvable entries
|
/// Guide dir-names in the same or another category. Unresolvable entries
|
||||||
/// are silently dropped when rendering "related guides" — never a crash.
|
/// are silently dropped when rendering "related guides" — never a crash.
|
||||||
|
/// Not read by any UI yet — the "related guides" render path doesn't
|
||||||
|
/// exist. Real content already sets it, so kept rather than dropped.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub related: Vec<String>,
|
pub related: Vec<String>,
|
||||||
|
/// Not read by any UI yet — reserved for a future "N min read" hint.
|
||||||
|
/// Real content already sets it, so kept rather than dropped.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub estimated_minutes: u32,
|
pub estimated_minutes: u32,
|
||||||
/// Overrides the parent directory name as the guide's category, if set.
|
/// Overrides the parent directory name as the guide's category, if set.
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,10 @@ pub enum Success {
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Clone)]
|
#[derive(serde::Deserialize, Clone)]
|
||||||
pub struct Step {
|
pub struct Step {
|
||||||
|
/// Not read by any Rust code yet — steps are matched by index, not id.
|
||||||
|
/// Real tour content already sets it (content authors use it to keep
|
||||||
|
/// track of steps), so kept rather than dropped.
|
||||||
|
#[allow(dead_code)]
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ pub struct SymptomOption {
|
||||||
pub struct Fix {
|
pub struct Fix {
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub command: String,
|
pub command: String,
|
||||||
|
/// Not read by `ui::troubleshoot_wizard` yet — the "Run fix" button
|
||||||
|
/// currently executes unconditionally regardless of this flag. Real
|
||||||
|
/// symptom content already sets it, so kept rather than dropped; the
|
||||||
|
/// wizard should gate on it before this is treated as safe.
|
||||||
|
#[allow(dead_code)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub requires_confirm: bool,
|
pub requires_confirm: bool,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,6 @@ pub struct Client {
|
||||||
pub struct Monitor {
|
pub struct Monitor {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub x: i32,
|
|
||||||
pub y: i32,
|
|
||||||
pub width: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub focused: bool,
|
pub focused: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ pub fn self_heal() {
|
||||||
pub fn on_tour_event(id: &str) {
|
pub fn on_tour_event(id: &str) {
|
||||||
let hit = STATE.with(|cell| {
|
let hit = STATE.with(|cell| {
|
||||||
let mut borrow = cell.borrow_mut();
|
let mut borrow = cell.borrow_mut();
|
||||||
let Some(state) = borrow.as_mut() else { return None };
|
let state = borrow.as_mut()?;
|
||||||
// Only a *visibly* confirmed step blocks a re-fire. `confirmed` gets
|
// Only a *visibly* confirmed step blocks a re-fire. `confirmed` gets
|
||||||
// set below, before `render_step` runs, so that render actually
|
// set below, before `render_step` runs, so that render actually
|
||||||
// paints the "done" state instead of the stale pre-confirmation one
|
// paints the "done" state instead of the stale pre-confirmation one
|
||||||
|
|
|
||||||
|
|
@ -30,13 +30,33 @@ thread_local! {
|
||||||
/// over D-Bus to this primary instance by a second `breadhelp` launch — so
|
/// over D-Bus to this primary instance by a second `breadhelp` launch — so
|
||||||
/// it must build the window at most once and reuse it thereafter.
|
/// it must build the window at most once and reuse it thereafter.
|
||||||
pub fn present(app: &Application, action: Action) {
|
pub fn present(app: &Application, action: Action) {
|
||||||
|
// Crash-recover keybinds even if this invocation never builds a window
|
||||||
|
// (silent every-login autostart).
|
||||||
|
tour::self_heal();
|
||||||
|
|
||||||
|
// Once onboarding is done, autostart only needs a warm process so
|
||||||
|
// Super+/ is instant. GTK/theme/font load is most of that cost and is
|
||||||
|
// paid by staying resident (~14 MiB PSS, no window). Building tabs
|
||||||
|
// adds ~2 MiB of dead weight for a session that may never open help.
|
||||||
|
let is_bare_silent_autostart = action.autostart
|
||||||
|
&& action.screenshot.is_none()
|
||||||
|
&& !action.force_onboard
|
||||||
|
&& action.tour_event.is_none()
|
||||||
|
&& action.suggest.is_none()
|
||||||
|
&& State::load().onboarding_completed();
|
||||||
|
if is_bare_silent_autostart {
|
||||||
|
// No window means nothing holds GApplication open. Forget the hold
|
||||||
|
// guard so the primary instance lives for the session; logout is
|
||||||
|
// what ends it. Without this, the next Super+/ would cold-start.
|
||||||
|
std::mem::forget(app.hold());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
HANDLE.with(|cell| {
|
HANDLE.with(|cell| {
|
||||||
let mut just_built = false;
|
|
||||||
{
|
{
|
||||||
let mut cell_ref = cell.borrow_mut();
|
let mut cell_ref = cell.borrow_mut();
|
||||||
if cell_ref.is_none() {
|
if cell_ref.is_none() {
|
||||||
*cell_ref = Some(build(app));
|
*cell_ref = Some(build(app));
|
||||||
just_built = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let cell_ref = cell.borrow();
|
let cell_ref = cell.borrow();
|
||||||
|
|
@ -62,19 +82,14 @@ pub fn present(app: &Application, action: Action) {
|
||||||
if let Some(s) = crate::services::breadd::resolve(id) {
|
if let Some(s) = crate::services::breadd::resolve(id) {
|
||||||
handle.home.set_suggestion(Some(&s.text));
|
handle.home.set_suggestion(Some(&s.text));
|
||||||
}
|
}
|
||||||
// Only focus the window the first time this process builds it
|
// Background daemon event — never steal focus. A `--suggest`
|
||||||
// (i.e. breadhelp wasn't already running) — a background daemon
|
// can now be the first invocation of the session because
|
||||||
// event shouldn't steal focus from whatever the user is doing.
|
// silent autostart no longer builds a window.
|
||||||
if just_built {
|
|
||||||
show_window(&handle.window, action.autostart);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every-login autostart builds the window (so the app is ready to
|
// Bare silent autostart (onboarding done) already returned above.
|
||||||
// respond to SUPER+/ instantly) but only shows UI on a genuine
|
// Autostart that reaches here is a genuine first run.
|
||||||
// first run — never on later logins. First run is now the in-window
|
|
||||||
// day-zero wizard; the live tour starts when that finishes.
|
|
||||||
let onboarded = State::load().onboarding_completed();
|
let onboarded = State::load().onboarding_completed();
|
||||||
if action.autostart && onboarded {
|
if action.autostart && onboarded {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue