Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ The v0.9.23+ prebuilt `wayscriber` packages require glibc 2.39 and GTK 4.12 —
- Overlay Session panel, configurator Session tab, tray toggle, and CLI overrides
- See [Session manager and persistence](#session-manager-and-persistence) for the full workflow

### Toolbars and UI
- Floating toolbars (<kbd>F9</kbd> toggles visibility; <kbd>F2</kbd> cycles the top strip full → micro chip → hidden)
### Toolbar and UI
- Unified floating toolbar (<kbd>F9</kbd> toggles visibility; <kbd>F2</kbd> cycles the top strip full → micro chip → hidden)
- Two toolbar frontends: GTK4-rendered bars on layer-shell compositors (Hyprland, KWin, Wayfire, River, ...), with automatic fallback to the built-in Cairo bars everywhere else (GNOME xdg fallback, forced-inline mode, builds without the `toolbar-gtk` feature)
- Pick a frontend explicitly with `ui.toolbar.backend = "auto" | "gtk" | "builtin"` or `WAYSCRIBER_TOOLBAR_BACKEND`
- Preset slots, icon or text modes
Expand Down Expand Up @@ -922,7 +922,7 @@ pick_screen_color = ["I"]
| Cancel action | <kbd>Right-click</kbd> (while drawing) / <kbd>Escape</kbd> |
| Context menu | <kbd>Right-click</kbd> (idle) / <kbd>Shift+F10</kbd> / <kbd>Menu</kbd>, <kbd>Arrow keys</kbd> + <kbd>Enter</kbd>/<kbd>Space</kbd> |
| Edit selected text/note | <kbd>Enter</kbd> (single selection) |
| Toggle toolbars | <kbd>F9</kbd> |
| Toggle toolbar | <kbd>F9</kbd> |
| Cycle top strip (full → micro → hidden) | <kbd>F2</kbd> |
| Help overlay | <kbd>F1</kbd> / <kbd>F10</kbd> |
| Quick reference | <kbd>Shift+F1</kbd> |
Expand Down
8 changes: 6 additions & 2 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ toggle_zoom_chip = []
# (unbound; also in the command palette)
toggle_focus_mode = []

# Toggle toolbars (show/hide top and side together)
# Toggle the unified top toolbar
toggle_toolbar = ["F9"]

# Cycle the top toolbar's display: full strip -> micro chip -> hidden
Expand Down Expand Up @@ -444,6 +444,10 @@ help_overlay_context_filter = true
# Show compositor capabilities warning toast each time the overlay starts
show_capabilities_warning = true

# Show automatic first-run guidance, discovery tips, and shortcut coaching.
# The guided tour remains available manually when this is false.
show_onboarding_hints = true

# Command palette action toast duration (ms)
command_palette_toast_duration_ms = 1500

Expand All @@ -466,7 +470,7 @@ multi_monitor_enabled = true
radial_menu_mouse_binding = "middle"

# ───────────────────────────────────────────────────────────────────────────────
# Floating Toolbars (F9 toggles; F2 cycles full -> micro -> hidden)
# Floating Toolbar (F9 toggles; F2 cycles full -> micro -> hidden)
# ───────────────────────────────────────────────────────────────────────────────

[ui.toolbar]
Expand Down
6 changes: 6 additions & 0 deletions configurator/src/app/pages/ui/general.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ pub(super) fn build(sender: &ComponentSender<ConfiguratorApp>) -> BuiltPage {
|app| app.draft.ui_show_capabilities_warning,
|value| Message::ToggleChanged(ToggleField::UiShowCapabilitiesWarning, value),
)
.switch_row(
"Show automatic guidance and tips",
"Controls first-run guidance, discovery tips, and shortcut coaching. The guided tour remains available manually.",
|app| app.draft.ui_show_onboarding_hints,
|value| Message::ToggleChanged(ToggleField::UiShowOnboardingHints, value),
)
.entry_row(
"Command palette toast (ms)",
|app| app.draft.ui_command_palette_toast_duration_ms.clone(),
Expand Down
8 changes: 8 additions & 0 deletions configurator/src/app/search/terms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,14 @@ pub(super) const UI_GENERAL_TERMS: &[&str] = &[
"enable context menu",
"show capabilities warning toast",
"capabilities warning",
"show automatic guidance and tips",
"onboarding",
"hints",
"onboarding hints",
"show_onboarding_hints",
"tutorial",
"guidance",
"tips",
"command palette toast",
];
pub(super) const UI_TOOLBAR_TERMS: &[&str] = &[
Expand Down
27 changes: 22 additions & 5 deletions configurator/src/app/search/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,13 +582,30 @@ fn exact_pdf_background_field_label_matches_pdf_section() {

#[test]
fn exact_general_ui_field_labels_match_general_ui_section() {
let (mut app, _effects) = ConfiguratorApp::new_app();
app.search_query = SearchQuery::new("focus loss");
for query in [
"focus loss",
"show automatic guidance and tips",
"onboarding",
"tutorial",
"guidance",
"tips",
"hints",
"onboarding hints",
"show_onboarding_hints",
] {
let (mut app, _effects) = ConfiguratorApp::new_app();
app.search_query = SearchQuery::new(query);

let summary = app.search_summary();
let ui = summary.tab(TabId::Ui).expect("ui match");
let summary = app.search_summary();
let ui = summary
.tab(TabId::Ui)
.unwrap_or_else(|| panic!("UI should match visible General UI wording: {query}"));

assert!(ui.area_matches(SearchArea::UiGeneral));
assert!(
ui.area_matches(SearchArea::UiGeneral),
"query should reveal General UI: {query}"
);
}
}

#[test]
Expand Down
15 changes: 15 additions & 0 deletions configurator/src/app/startup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,21 @@ mod tests {
assert!(!app.startup_search_focus_pending);
}

#[test]
fn the_onboarding_hint_destination_lands_on_the_general_ui_setting() {
let launched =
wayscriber::configurator_destination::onboarding_hints_destination().as_arg();
let (app, _dir, _path) = app_launched_with(&["--open", &launched]);

assert_eq!(app.active_tab, TabId::Ui);
assert_eq!(app.search_query.raw(), "Show automatic guidance and tips");
let summary = app.search_summary();
let ui = summary
.tab(TabId::Ui)
.expect("General UI destination match");
assert!(ui.area_matches(crate::app::search::SearchArea::UiGeneral));
}

/// The shortcut-action row: subtab plus action search.
#[test]
fn a_keybinding_action_destination_lands_on_its_subtab_with_the_term_searched() {
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/config/draft/from_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ impl ConfigDraft {
ui_show_page_badge_with_status_bar: config.ui.show_floating_badge_always,
ui_show_frozen_badge: config.ui.show_frozen_badge,
ui_show_capabilities_warning: config.ui.show_capabilities_warning,
ui_show_onboarding_hints: config.ui.show_onboarding_hints,
ui_context_menu_enabled: config.ui.context_menu.enabled,
ui_preferred_output: config.ui.preferred_output.clone().unwrap_or_default(),
ui_xdg_fullscreen: config.ui.xdg_fullscreen,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/config/draft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ pub struct ConfigDraft {
pub ui_show_page_badge_with_status_bar: bool,
pub ui_show_frozen_badge: bool,
pub ui_show_capabilities_warning: bool,
pub ui_show_onboarding_hints: bool,
pub ui_context_menu_enabled: bool,
pub ui_preferred_output: String,
pub ui_xdg_fullscreen: bool,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/config/setters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ impl ConfigDraft {
ToggleField::UiShowStatusAbout => self.ui_show_status_about = value,
ToggleField::UiShowFrozenBadge => self.ui_show_frozen_badge = value,
ToggleField::UiShowCapabilitiesWarning => self.ui_show_capabilities_warning = value,
ToggleField::UiShowOnboardingHints => self.ui_show_onboarding_hints = value,
ToggleField::UiShowStatusBoardBadge => self.ui_show_status_board_badge = value,
ToggleField::UiShowStatusPageBadge => self.ui_show_status_page_badge = value,
ToggleField::UiShowToolbarHint => self.ui_show_toolbar_hint = value,
Expand Down
16 changes: 16 additions & 0 deletions configurator/src/models/config/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,22 @@ fn config_draft_round_trips_status_bar_interactive() {
.expect("status bar interactive round trip");
assert!(!round_trip.ui.status_bar_interactive);
}

#[test]
fn config_draft_round_trips_automatic_onboarding_preference() {
let config = Config::default();
let mut draft = ConfigDraft::from_config(&config);
assert!(
draft.ui_show_onboarding_hints,
"automatic guidance defaults on"
);

draft.set_toggle(ToggleField::UiShowOnboardingHints, false);
let round_trip = draft
.to_config(&config)
.expect("onboarding preference should round trip");
assert!(!round_trip.ui.show_onboarding_hints);
}
use super::super::{ColorMode, NamedColorOption};
use super::{ConfigDraft, RenderProfileSelectionOption};
use wayscriber::config::{
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/config/to_config/ui.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ impl ConfigDraft {
config.ui.show_floating_badge_always = self.ui_show_page_badge_with_status_bar;
config.ui.show_frozen_badge = self.ui_show_frozen_badge;
config.ui.show_capabilities_warning = self.ui_show_capabilities_warning;
config.ui.show_onboarding_hints = self.ui_show_onboarding_hints;
config.ui.context_menu.enabled = self.ui_context_menu_enabled;
let preferred_output = self.ui_preferred_output.trim();
config.ui.preferred_output = if preferred_output.is_empty() {
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/fields/toggles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub enum ToggleField {
UiShowStatusAbout,
UiShowFrozenBadge,
UiShowCapabilitiesWarning,
UiShowOnboardingHints,
UiHelpOverlayContextFilter,
UiContextMenuEnabled,
UiXdgFullscreen,
Expand Down
20 changes: 18 additions & 2 deletions docs/CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,13 @@ show_frozen_badge = false
# Filter help overlay sections based on enabled features
help_overlay_context_filter = true

# Show compositor capability warnings when the overlay starts
show_capabilities_warning = true

# Show automatic first-run guidance, discovery tips, and shortcut coaching.
# The guided tour remains available manually when this is false.
show_onboarding_hints = true

# Command palette action toast duration (ms)
command_palette_toast_duration_ms = 1500

Expand Down Expand Up @@ -977,7 +984,7 @@ wayscriber --light-draw-off

Use `--light-draw-on` on key/button press and `--light-draw-off` on release for a non-sticky draw-while-held shortcut. The raw `--daemon-action` form remains available for scripts.

### `[ui.toolbar]` - Floating Toolbars
### `[ui.toolbar]` - Floating Toolbar

Controls the unified top toolbar (<kbd>F9</kbd> toggles visibility; <kbd>F2</kbd> cycles the top strip full → micro → hidden).

Expand Down Expand Up @@ -1170,6 +1177,8 @@ top_controls = [
- **Polygon tools**: Full mode shows Triangle, Parallelogram, Rhombus, Regular Polygon, and Freeform Polygon under the compact Polygons picker. Simple mode exposes them in the Shapes picker.
- **Context-aware UI**: `context_aware_ui` shows/hides tool-specific controls (colors, thickness, arrow labels, etc.) based on the active tool; disable to always show all controls.
- **Preset toasts**: `show_preset_toasts` enables toast confirmations for preset apply/save/clear.
- **Automatic guidance**: `show_onboarding_hints` controls first-run cards, discovery tips, and shortcut coaching. Set it to `false` to disable all automatic tutorials; the guided tour remains available manually. Completed profiles migrated from onboarding versions before v6 are not enrolled in the later status-bar, Canvas, and zoom tip series.
- **Capability warnings**: `show_capabilities_warning` independently controls compositor limitation warnings; disabling tutorials does not hide safety, configuration, or capability diagnostics.
- **Tool preview**: `show_tool_preview` toggles the cursor bubble.
- **Offsets**: `top_offset` and `top_offset_y` are the authored default top-toolbar position. Dragging the strip saves its position as a runtime preference in `runtime-ui.toml` and leaves these untouched; editing one here again takes over from the saved drag.
- **Force inline**: `force_inline` (or `WAYSCRIBER_FORCE_INLINE_TOOLBARS`) skips layer-shell toolbars.
Expand Down Expand Up @@ -1631,6 +1640,13 @@ one is a tidy-up that records your decision in the file.
`CURRENT_CONFIG_REVISION` bump. A default is only ever offered to an action a configuration omits,
and only where the key is free, so a new or moved default cannot land on a shortcut a user bound to
something else (#293, #315); the skipped-default diagnostic reports the stand-down instead.
That informational notice is acknowledged in the profile's
`$XDG_DATA_HOME/wayscriber/onboarding.toml` state and appears once for each exact set of skipped
bindings; a later, different skipped default can notify once again. Actual
parse errors, invalid bindings, and conflicts continue to report on every start until they are fixed.
For the historical toolbar pair, either accept the revision-2 proposal (`F9` toggles the unified top
toolbar and `F2` cycles full → micro → hidden) or explicitly write
`cycle_toolbar_display = []` to keep `F2`/`F9` on `toggle_toolbar` without another notice.
What is still required: the new default must not collide with another shipped default
(`default_keybindings_have_no_conflicts` guards that), and
`default_bindings_match_the_checked_in_snapshot` holds a snapshot of every shipped default and fails
Expand Down Expand Up @@ -1787,7 +1803,7 @@ toggle_zoom_chip = []
# (unbound; also in the command palette)
toggle_focus_mode = []

# Toggle toolbars (show/hide top and side together).
# Toggle the unified top toolbar.
# Note: F2 moved to cycle_toolbar_display; hiding is still reachable via
# the cycle, and explicit user configs keep whatever they bound.
toggle_toolbar = ["F9"]
Expand Down
33 changes: 31 additions & 2 deletions src/backend/wayland/backend/state_init/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::config::{
use crate::input::InputState;
use crate::input::state::{Toast, ToastPriority};
use crate::notification;
use crate::onboarding::OnboardingStore;

/// How long the keybinding warnings stay up. All of them are on the long side:
/// they describe a config problem the user has to leave the overlay to fix.
Expand Down Expand Up @@ -255,19 +256,28 @@ pub(super) fn notify_invalid_keybindings(
pub(super) fn notify_skipped_default_shortcuts(
input_state: &mut InputState,
tokio_handle: &tokio::runtime::Handle,
onboarding: &mut OnboardingStore,
skipped: &[DefaultShortcutSkipped],
) {
if skipped.is_empty() {
if skipped.is_empty() || !onboarding.persistence_available() {
return;
}

input_state.push_toast(
let notice_id = skipped_default_notice_id(skipped);
if onboarding.startup_notice_acknowledged(&notice_id) {
return;
}

let outcome = input_state.push_toast(
ToastPriority::Action,
"keybindings.skipped-default",
Toast::info(skipped_default_toast(skipped))
.action("Shortcuts", Action::OpenConfiguratorKeybindings)
.duration_ms(KEYBINDING_CONFLICT_TOAST_MS),
);
if !outcome.accepted() || onboarding.acknowledge_startup_notice(&notice_id).is_err() {
return;
}
notification::send_notification_with_timeout_async(
tokio_handle,
"New Default Shortcuts".to_string(),
Expand All @@ -277,6 +287,21 @@ pub(super) fn notify_skipped_default_shortcuts(
);
}

fn skipped_default_notice_id(skipped: &[DefaultShortcutSkipped]) -> String {
let mut entries = skipped
.iter()
.map(|entry| {
let skipped_action = entry.config_key().unwrap_or("runtime_action");
let claimed_by =
crate::config::KeybindingsConfig::config_key_for_action(entry.claimed_by())
.unwrap_or("runtime_action");
format!("{}:{skipped_action}:{claimed_by}", entry.binding())
})
.collect::<Vec<_>>();
entries.sort_unstable();
format!("skipped-default:{}", entries.join(","))
}

fn keybinding_conflict_toast(conflicts: &[KeybindingConflictResolution]) -> String {
keybinding_toast(
"Shortcut conflict",
Expand Down Expand Up @@ -495,6 +520,10 @@ mod tests {
loaded.keybindings.skipped_default_shortcuts[0].binding(),
"F2"
);
assert_eq!(
skipped_default_notice_id(&loaded.keybindings.skipped_default_shortcuts),
"skipped-default:F2:cycle_toolbar_display:toggle_toolbar"
);

let toast = skipped_default_toast(&loaded.keybindings.skipped_default_shortcuts);
assert!(toast.contains("F2"), "unexpected toast: {toast}");
Expand Down
Loading
Loading