Add complete keyboard navigation.
This commit is contained in:
@@ -8,7 +8,7 @@ The project is under active development. Core blogging workflows are broadly ava
|
|||||||
|
|
||||||
## Available Features
|
## Available Features
|
||||||
|
|
||||||
- Native Iced desktop workspace with a localized native macOS menu and in-window Linux/Windows menu bar, tabs, anchored editor popovers, automatically paged post/media sidebars with locale-aware post dates, calendar months, and relative-dated entity lists, row deletion, dialogs, embedded Wry previews, a live Pico CSS theme editor, direct Preferences navigation, and per-project restart restoration of the active activity, shell visibility, and open editor tabs. The in-window menu supports Alt mnemonics and arrow-key navigation. Its shared task UI keeps queued work off blocking workers, presents active local and remote work plus recent history in bDS2-style grouped progress cards, and supports cooperative cancellation without oversubscribing background work.
|
- Native Iced desktop workspace with a localized native macOS menu and in-window Linux/Windows menu bar, tabs, anchored editor popovers, automatically paged post/media sidebars with locale-aware post dates, calendar months, and relative-dated entity lists, row deletion, dialogs, embedded Wry previews, a live Pico CSS theme editor, direct Preferences navigation, and per-project restart restoration of the active activity, shell visibility, and open editor tabs. Every enabled control participates in Tab/Shift-Tab focus traversal and Enter/Space activation; holding Control+Option on macOS or Control+Alt elsewhere reveals direct shortcuts plus stable two/three-letter codes for visible generic controls, filters those codes as they are typed, and lets Escape cancel while Option/Alt alone remains available for character entry. The in-window menu supports Alt mnemonics and arrow-key navigation. Its shared task UI keeps queued work off blocking workers, presents active local and remote work plus recent history in bDS2-style grouped progress cards, and supports cooperative cancellation without oversubscribing background work.
|
||||||
- Post and translation authoring with change-aware draft/published/archive lifecycle, file-backed change discard, canonical draft reopening after manual translation edits, non-disruptive automatic translation, desktop archive/unarchive actions, in-place published-frontmatter updates, metadata, tags, categories, cursor-preserving link and media insertion, live link/backlink graphs, media, and batch gallery-image import.
|
- Post and translation authoring with change-aware draft/published/archive lifecycle, file-backed change discard, canonical draft reopening after manual translation edits, non-disruptive automatic translation, desktop archive/unarchive actions, in-place published-frontmatter updates, metadata, tags, categories, cursor-preserving link and media insertion, live link/backlink graphs, media, and batch gallery-image import.
|
||||||
- Media import including HEIC/HEIF decoding, q80 WebP thumbnails (plus q85 AI JPEGs), metadata translations, filters, validation, post assignment, and sequential drag-and-drop insertion into post editors.
|
- Media import including HEIC/HEIF decoding, q80 WebP thumbnails (plus q85 AI JPEGs), metadata translations, filters, validation, post assignment, and sequential drag-and-drop insertion into post editors.
|
||||||
- WordPress WXR migration with saved analyses, HTML-to-Markdown and shortcode conversion, conflict/taxonomy review, recoverable 500-item execution batches, media-parent linking, progress reporting, and optional AI-assisted taxonomy mapping.
|
- WordPress WXR migration with saved analyses, HTML-to-Markdown and shortcode conversion, conflict/taxonomy review, recoverable 500-item execution batches, media-parent linking, progress reporting, and optional AI-assisted taxonomy mapping.
|
||||||
|
|||||||
@@ -238,6 +238,7 @@ pub enum Message {
|
|||||||
// Settings
|
// Settings
|
||||||
SetOfflineMode(bool),
|
SetOfflineMode(bool),
|
||||||
SetUiLocale(UiLocale),
|
SetUiLocale(UiLocale),
|
||||||
|
KeyboardNavigation(crate::components::keyboard::Navigation),
|
||||||
ToggleLocaleDropdown,
|
ToggleLocaleDropdown,
|
||||||
ToggleProjectDropdown,
|
ToggleProjectDropdown,
|
||||||
|
|
||||||
@@ -1457,6 +1458,10 @@ impl BdsApp {
|
|||||||
|
|
||||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||||
match message {
|
match message {
|
||||||
|
Message::KeyboardNavigation(navigation) => match navigation {
|
||||||
|
crate::components::keyboard::Navigation::Next => iced::widget::focus_next(),
|
||||||
|
crate::components::keyboard::Navigation::Previous => iced::widget::focus_previous(),
|
||||||
|
},
|
||||||
Message::WindowCloseRequested => {
|
Message::WindowCloseRequested => {
|
||||||
self.persist_project_ui_state();
|
self.persist_project_ui_state();
|
||||||
flush_embeddings_and_exit(std::process::exit)
|
flush_embeddings_and_exit(std::process::exit)
|
||||||
@@ -3869,7 +3874,11 @@ impl BdsApp {
|
|||||||
) && (action != MenuAction::DisconnectServer || self.remote_client.is_some())
|
) && (action != MenuAction::DisconnectServer || self.remote_client.is_some())
|
||||||
});
|
});
|
||||||
|
|
||||||
native_edit::native_edit(content, Arc::clone(&self.native_edit_commands)).into()
|
crate::components::keyboard::scope(native_edit::native_edit(
|
||||||
|
content,
|
||||||
|
Arc::clone(&self.native_edit_commands),
|
||||||
|
))
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscription(&self) -> Subscription<Message> {
|
pub fn subscription(&self) -> Subscription<Message> {
|
||||||
@@ -3894,6 +3903,15 @@ impl BdsApp {
|
|||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
let window_close_sub = window::close_requests().map(|_| Message::WindowCloseRequested);
|
let window_close_sub = window::close_requests().map(|_| Message::WindowCloseRequested);
|
||||||
|
let keyboard_navigation_sub = iced::event::listen_with(|event, status, _id| {
|
||||||
|
let iced::Event::Keyboard(iced::keyboard::Event::KeyPressed { key, modifiers, .. }) =
|
||||||
|
event
|
||||||
|
else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
crate::components::keyboard::tab_navigation(&key, modifiers, status)
|
||||||
|
.map(Message::KeyboardNavigation)
|
||||||
|
});
|
||||||
|
|
||||||
// Global mouse tracking for sidebar resize dragging.
|
// Global mouse tracking for sidebar resize dragging.
|
||||||
// The 4px drag handle mouse_area only fires on_press; move/release
|
// The 4px drag handle mouse_area only fires on_press; move/release
|
||||||
@@ -3950,6 +3968,7 @@ impl BdsApp {
|
|||||||
toast_tick,
|
toast_tick,
|
||||||
file_drop_sub,
|
file_drop_sub,
|
||||||
window_close_sub,
|
window_close_sub,
|
||||||
|
keyboard_navigation_sub,
|
||||||
drag_sub,
|
drag_sub,
|
||||||
menu_interaction_sub,
|
menu_interaction_sub,
|
||||||
menu_expand_tick,
|
menu_expand_tick,
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ use iced::widget::{
|
|||||||
};
|
};
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Length, Shadow, Theme, Vector};
|
use iced::{Alignment, Background, Border, Color, Element, Length, Shadow, Theme, Vector};
|
||||||
|
|
||||||
|
use super::keyboard;
|
||||||
|
|
||||||
/// Standard form field label color.
|
/// Standard form field label color.
|
||||||
pub const LABEL_COLOR: Color = rgb8(0xB5, 0xBA, 0xC4);
|
pub const LABEL_COLOR: Color = rgb8(0xB5, 0xBA, 0xC4);
|
||||||
pub const SECTION_COLOR: Color = rgb8(0x9D, 0xA5, 0xB4);
|
pub const SECTION_COLOR: Color = rgb8(0x9D, 0xA5, 0xB4);
|
||||||
@@ -243,10 +245,13 @@ where
|
|||||||
.size(12)
|
.size(12)
|
||||||
.color(LABEL_COLOR)
|
.color(LABEL_COLOR)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
|
keyboard::focusable(
|
||||||
pick_list(list, selected.cloned(), on_select)
|
pick_list(list, selected.cloned(), on_select)
|
||||||
.padding([8, 10])
|
.padding([8, 10])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.style(select_style),
|
.style(select_style),
|
||||||
|
true,
|
||||||
|
),
|
||||||
]
|
]
|
||||||
.spacing(6)
|
.spacing(6)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
@@ -259,10 +264,13 @@ pub fn labeled_checkbox<'a, Message: Clone + 'a>(
|
|||||||
is_checked: bool,
|
is_checked: bool,
|
||||||
on_toggle: impl Fn(bool) -> Message + 'a,
|
on_toggle: impl Fn(bool) -> Message + 'a,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
|
keyboard::focusable(
|
||||||
checkbox(label, is_checked)
|
checkbox(label, is_checked)
|
||||||
.on_toggle(on_toggle)
|
.on_toggle(on_toggle)
|
||||||
.size(16)
|
.size(16)
|
||||||
.text_size(14)
|
.text_size(14),
|
||||||
|
true,
|
||||||
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1329
crates/bds-ui/src/components/keyboard.rs
Normal file
1329
crates/bds-ui/src/components/keyboard.rs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
|||||||
pub mod inputs;
|
pub mod inputs;
|
||||||
|
pub mod keyboard;
|
||||||
pub mod native_edit;
|
pub mod native_edit;
|
||||||
pub mod popover;
|
pub mod popover;
|
||||||
pub mod webview;
|
pub mod webview;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
Column, button, column, container, horizontal_space, mouse_area, row, scrollable, text,
|
Column, column, container, horizontal_space, mouse_area, row, scrollable, text,
|
||||||
};
|
};
|
||||||
use iced::{
|
use iced::{
|
||||||
Alignment, Background, Border, Color, Element, Length, Shadow, Subscription, Theme, Vector,
|
Alignment, Background, Border, Color, Element, Length, Shadow, Subscription, Theme, Vector,
|
||||||
@@ -10,7 +10,7 @@ use muda::accelerator::{Accelerator, CMD_OR_CTRL, Code, Modifiers};
|
|||||||
use muda::{Menu, MenuEvent, MenuId, MenuItem, PredefinedMenuItem, Submenu};
|
use muda::{Menu, MenuEvent, MenuId, MenuItem, PredefinedMenuItem, Submenu};
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
use crate::components::{inputs, popover};
|
use crate::components::{inputs, keyboard, popover};
|
||||||
use crate::state::tabs::TabType;
|
use crate::state::tabs::TabType;
|
||||||
use bds_core::i18n::{UiLocale, translate};
|
use bds_core::i18n::{UiLocale, translate};
|
||||||
|
|
||||||
@@ -640,9 +640,12 @@ fn window_menu_popup<'a>(
|
|||||||
]
|
]
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.spacing(16);
|
.spacing(16);
|
||||||
let mut item = button(content).padding([6, 8]).width(Length::Fill).style(
|
let mut item = keyboard::button(content)
|
||||||
move |theme, status| menu_item_style(selected == Some(action), theme, status),
|
.padding([6, 8])
|
||||||
);
|
.width(Length::Fill)
|
||||||
|
.style(move |theme, status| {
|
||||||
|
menu_item_style(selected == Some(action), theme, status)
|
||||||
|
});
|
||||||
if is_enabled {
|
if is_enabled {
|
||||||
item = item.on_press(Message::WindowMenu(WindowMenuEvent::Action(action)));
|
item = item.on_press(Message::WindowMenu(WindowMenuEvent::Action(action)));
|
||||||
}
|
}
|
||||||
@@ -692,7 +695,7 @@ pub fn window_menu_view<'a>(
|
|||||||
label
|
label
|
||||||
};
|
};
|
||||||
let trigger = mouse_area(
|
let trigger = mouse_area(
|
||||||
button(text(label).size(12))
|
keyboard::button(text(label).size(12))
|
||||||
.padding([5, 8])
|
.padding([5, 8])
|
||||||
.style(move |theme, status| menu_button_style(active, theme, status))
|
.style(move |theme, status| menu_button_style(active, theme, status))
|
||||||
.on_press(Message::WindowMenu(WindowMenuEvent::Toggle(
|
.on_press(Message::WindowMenu(WindowMenuEvent::Toggle(
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Column, Space, button, column, container, svg, text, tooltip};
|
use iced::widget::{Column, Space, column, container, svg, text, tooltip};
|
||||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -28,6 +30,21 @@ fn icon_svg(view: SidebarView) -> &'static [u8] {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn access_key(view: SidebarView) -> char {
|
||||||
|
match view {
|
||||||
|
SidebarView::Posts => '1',
|
||||||
|
SidebarView::Pages => '2',
|
||||||
|
SidebarView::Media => '3',
|
||||||
|
SidebarView::Scripts => '4',
|
||||||
|
SidebarView::Templates => '5',
|
||||||
|
SidebarView::Tags => '6',
|
||||||
|
SidebarView::Chat => '7',
|
||||||
|
SidebarView::Import => '8',
|
||||||
|
SidebarView::Git => '9',
|
||||||
|
SidebarView::Settings => '0',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Top group of activity items.
|
/// Top group of activity items.
|
||||||
const TOP_ACTIVITIES: &[SidebarView] = &[
|
const TOP_ACTIVITIES: &[SidebarView] = &[
|
||||||
SidebarView::Posts,
|
SidebarView::Posts,
|
||||||
@@ -108,7 +125,7 @@ pub fn view(
|
|||||||
.height(Length::Fixed(24.0))
|
.height(Length::Fixed(24.0))
|
||||||
.opacity(if is_active { 1.0_f32 } else { 0.4_f32 });
|
.opacity(if is_active { 1.0_f32 } else { 0.4_f32 });
|
||||||
|
|
||||||
let btn = button(
|
let btn = iced::widget::button(
|
||||||
container(icon)
|
container(icon)
|
||||||
.center_x(Length::Fixed(48.0))
|
.center_x(Length::Fixed(48.0))
|
||||||
.center_y(Length::Fixed(48.0)),
|
.center_y(Length::Fixed(48.0)),
|
||||||
@@ -140,13 +157,18 @@ pub fn view(
|
|||||||
|
|
||||||
// Wrap in tooltip per layout.allium ActivityButton.label_key
|
// Wrap in tooltip per layout.allium ActivityButton.label_key
|
||||||
let tip_text = t(locale, view.i18n_key());
|
let tip_text = t(locale, view.i18n_key());
|
||||||
|
keyboard::focusable(
|
||||||
tooltip(
|
tooltip(
|
||||||
btn_row,
|
btn_row,
|
||||||
text(tip_text).size(12).shaping(Shaping::Advanced),
|
text(tip_text).size(12).shaping(Shaping::Advanced),
|
||||||
tooltip::Position::Right,
|
tooltip::Position::Right,
|
||||||
)
|
)
|
||||||
.gap(4)
|
.gap(4)
|
||||||
.style(inputs::tooltip_style)
|
.style(inputs::tooltip_style),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.hotkey(access_key(view))
|
||||||
|
.hint_position(keyboard::HintPosition::BottomTrailing)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::f32::consts::{FRAC_PI_2, TAU};
|
use std::f32::consts::{FRAC_PI_2, TAU};
|
||||||
|
|
||||||
@@ -7,9 +8,7 @@ use bds_core::engine::chat_surfaces::{
|
|||||||
};
|
};
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
use iced::widget::canvas::{self, Path, Stroke, path};
|
use iced::widget::canvas::{self, Path, Stroke, path};
|
||||||
use iced::widget::{
|
use iced::widget::{Space, checkbox, column, container, row, scrollable, text, text_editor};
|
||||||
Space, button, checkbox, column, container, row, scrollable, text, text_editor,
|
|
||||||
};
|
|
||||||
use iced::{
|
use iced::{
|
||||||
Alignment, Color, Element, Length, Point, Radians, Rectangle, Renderer, Size, Theme, mouse,
|
Alignment, Color, Element, Length, Point, Radians, Rectangle, Renderer, Size, Theme, mouse,
|
||||||
};
|
};
|
||||||
@@ -76,7 +75,7 @@ fn surface_view<'a>(
|
|||||||
}
|
}
|
||||||
if dismissible {
|
if dismissible {
|
||||||
header = header.push(
|
header = header.push(
|
||||||
button(text(t(locale, "chat.surface.dismiss")))
|
keyboard::button(text(t(locale, "chat.surface.dismiss")))
|
||||||
.on_press(Message::ChatSurfaceDismissed(surface.id.clone()))
|
.on_press(Message::ChatSurfaceDismissed(surface.id.clone()))
|
||||||
.padding([4, 8])
|
.padding([4, 8])
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
@@ -113,7 +112,7 @@ fn surface_content<'a>(
|
|||||||
.iter()
|
.iter()
|
||||||
.fold(row![].spacing(8), |actions, item| {
|
.fold(row![].spacing(8), |actions, item| {
|
||||||
actions.push(
|
actions.push(
|
||||||
button(text(item.label.clone()))
|
keyboard::button(text(item.label.clone()))
|
||||||
.on_press(Message::ChatSurfaceAction {
|
.on_press(Message::ChatSurfaceAction {
|
||||||
surface_id: surface.id.clone(),
|
surface_id: surface.id.clone(),
|
||||||
action: item.action.clone(),
|
action: item.action.clone(),
|
||||||
@@ -196,14 +195,17 @@ fn form<'a>(
|
|||||||
let surface_id = surface.id.clone();
|
let surface_id = surface.id.clone();
|
||||||
let key = field.key.clone();
|
let key = field.key.clone();
|
||||||
let control: Element<'a, Message> = match field.input_type {
|
let control: Element<'a, Message> = match field.input_type {
|
||||||
FormInputType::Checkbox => checkbox(label, field.value.as_bool().unwrap_or(false))
|
FormInputType::Checkbox => keyboard::focusable(
|
||||||
|
checkbox(label, field.value.as_bool().unwrap_or(false))
|
||||||
.on_toggle(move |value| Message::ChatSurfaceFieldChanged {
|
.on_toggle(move |value| Message::ChatSurfaceFieldChanged {
|
||||||
surface_id: surface_id.clone(),
|
surface_id: surface_id.clone(),
|
||||||
field: key.clone(),
|
field: key.clone(),
|
||||||
value: value.into(),
|
value: value.into(),
|
||||||
})
|
})
|
||||||
.size(16)
|
.size(16)
|
||||||
.text_size(13)
|
.text_size(13),
|
||||||
|
true,
|
||||||
|
)
|
||||||
.into(),
|
.into(),
|
||||||
FormInputType::Select => {
|
FormInputType::Select => {
|
||||||
let selected = field.options.iter().find(|option| {
|
let selected = field.options.iter().find(|option| {
|
||||||
@@ -278,7 +280,7 @@ fn form<'a>(
|
|||||||
}
|
}
|
||||||
if let Some(action) = &surface.submit_action {
|
if let Some(action) = &surface.submit_action {
|
||||||
children.push(
|
children.push(
|
||||||
button(text(
|
keyboard::button(text(
|
||||||
surface
|
surface
|
||||||
.submit_label
|
.submit_label
|
||||||
.clone()
|
.clone()
|
||||||
@@ -437,7 +439,7 @@ fn tabs<'a>(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.fold(row![].spacing(6), |controls, (index, tab)| {
|
.fold(row![].spacing(6), |controls, (index, tab)| {
|
||||||
controls.push(
|
controls.push(
|
||||||
button(text(tab.label.clone()))
|
keyboard::button(text(tab.label.clone()))
|
||||||
.on_press(Message::ChatSurfaceTabSelected {
|
.on_press(Message::ChatSurfaceTabSelected {
|
||||||
surface_id: surface.id.clone(),
|
surface_id: surface.id.clone(),
|
||||||
index,
|
index,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
@@ -8,7 +9,7 @@ use bds_core::i18n::UiLocale;
|
|||||||
use bds_core::model::{ChatConversation, ChatMessage, ChatRole};
|
use bds_core::model::{ChatConversation, ChatMessage, ChatRole};
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
Space, button, column, container, markdown, row, scrollable, text, text_editor, text_input,
|
Space, column, container, markdown, row, scrollable, text, text_editor, text_input,
|
||||||
};
|
};
|
||||||
use iced::{Alignment, Color, Element, Length};
|
use iced::{Alignment, Color, Element, Length};
|
||||||
|
|
||||||
@@ -212,7 +213,7 @@ pub fn view<'a>(
|
|||||||
text(t(locale, "chat.unavailable.guidance"))
|
text(t(locale, "chat.unavailable.guidance"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.color(inputs::SECTION_COLOR),
|
.color(inputs::SECTION_COLOR),
|
||||||
button(text(t(locale, "chat.unavailable.openSettings")))
|
keyboard::button(text(t(locale, "chat.unavailable.openSettings")))
|
||||||
.on_press(Message::OpenSettingsSection(
|
.on_press(Message::OpenSettingsSection(
|
||||||
crate::views::settings_view::SettingsSection::AI,
|
crate::views::settings_view::SettingsSection::AI,
|
||||||
))
|
))
|
||||||
@@ -254,11 +255,11 @@ pub fn view<'a>(
|
|||||||
.size(18)
|
.size(18)
|
||||||
.padding([7, 9])
|
.padding([7, 9])
|
||||||
.style(inputs::field_style),
|
.style(inputs::field_style),
|
||||||
button(text(t(locale, "chat.rename.action")))
|
keyboard::button(text(t(locale, "chat.rename.action")))
|
||||||
.on_press(Message::ChatRename)
|
.on_press(Message::ChatRename)
|
||||||
.padding([8, 12])
|
.padding([8, 12])
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
button(text(t(locale, "common.delete")))
|
keyboard::button(text(t(locale, "common.delete")))
|
||||||
.on_press(Message::ChatDelete(state.conversation.id.clone()))
|
.on_press(Message::ChatDelete(state.conversation.id.clone()))
|
||||||
.padding([8, 12])
|
.padding([8, 12])
|
||||||
.style(inputs::danger_button),
|
.style(inputs::danger_button),
|
||||||
@@ -349,7 +350,8 @@ pub fn view<'a>(
|
|||||||
} else {
|
} else {
|
||||||
t(locale, "chat.send")
|
t(locale, "chat.send")
|
||||||
};
|
};
|
||||||
let mut send_button = button(text(send_label))
|
let mut send_button =
|
||||||
|
keyboard::button(text(send_label))
|
||||||
.padding([8, 16])
|
.padding([8, 16])
|
||||||
.style(if state.streaming {
|
.style(if state.streaming {
|
||||||
inputs::danger_button
|
inputs::danger_button
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use iced::widget::{Space, button, column, container, row, scrollable, text, tooltip};
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::{Space, column, container, row, scrollable, text, tooltip};
|
||||||
use iced::{Alignment, Background, Color, Element, Length, Theme};
|
use iced::{Alignment, Background, Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -339,7 +340,7 @@ fn recent_posts<'a>(posts: &'a [DashboardRecentPost], locale: UiLocale) -> Eleme
|
|||||||
.map(|post| {
|
.map(|post| {
|
||||||
container(
|
container(
|
||||||
row![
|
row![
|
||||||
button(
|
keyboard::button(
|
||||||
column![
|
column![
|
||||||
text(post.title.clone()).size(14).color(Color::WHITE),
|
text(post.title.clone()).size(14).color(Color::WHITE),
|
||||||
text(post.date.clone())
|
text(post.date.clone())
|
||||||
@@ -357,7 +358,7 @@ fn recent_posts<'a>(posts: &'a [DashboardRecentPost], locale: UiLocale) -> Eleme
|
|||||||
}))
|
}))
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
status_badge(&post.status),
|
status_badge(&post.status),
|
||||||
button(text(t(locale, "dashboard.pin")).size(12))
|
keyboard::button(text(t(locale, "dashboard.pin")).size(12))
|
||||||
.on_press(Message::OpenTab(Tab {
|
.on_press(Message::OpenTab(Tab {
|
||||||
id: post.post_id.clone(),
|
id: post.post_id.clone(),
|
||||||
tab_type: TabType::Post,
|
tab_type: TabType::Post,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::{HashMap, hash_map::DefaultHasher};
|
use std::collections::{HashMap, hash_map::DefaultHasher};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
@@ -5,7 +6,7 @@ use std::path::{Path, PathBuf};
|
|||||||
use std::time::{Duration, Instant, UNIX_EPOCH};
|
use std::time::{Duration, Instant, UNIX_EPOCH};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
use iced::widget::{button, column, container, markdown, row, scrollable, text};
|
use iced::widget::{column, container, markdown, row, scrollable, text};
|
||||||
use iced::{Element, Length};
|
use iced::{Element, Length};
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
@@ -170,7 +171,7 @@ pub fn view(state: &DocumentationState, locale: UiLocale) -> Element<'_, Message
|
|||||||
.into(),
|
.into(),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
button(text(t(locale, "common.refresh")).size(13))
|
keyboard::button(text(t(locale, "common.refresh")).size(13))
|
||||||
.on_press(Message::DocumentationRefresh(state.kind))
|
.on_press(Message::DocumentationRefresh(state.kind))
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use bds_core::engine::embedding::DuplicateSearchResult;
|
use bds_core::engine::embedding::DuplicateSearchResult;
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, checkbox, column, container, row, scrollable, text};
|
use iced::widget::{Space, checkbox, column, container, row, scrollable, text};
|
||||||
use iced::{Color, Element, Length};
|
use iced::{Color, Element, Length};
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
@@ -23,19 +24,20 @@ pub struct DuplicatesState {
|
|||||||
|
|
||||||
pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
||||||
let refresh = if state.is_loading {
|
let refresh = if state.is_loading {
|
||||||
button(text(t(locale, "duplicates.searching")).size(13)).style(inputs::secondary_button)
|
keyboard::button(text(t(locale, "duplicates.searching")).size(13))
|
||||||
|
.style(inputs::secondary_button)
|
||||||
} else {
|
} else {
|
||||||
button(text(t(locale, "common.refresh")).size(13))
|
keyboard::button(text(t(locale, "common.refresh")).size(13))
|
||||||
.on_press(Message::DuplicatesRefresh)
|
.on_press(Message::DuplicatesRefresh)
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
}
|
}
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
|
|
||||||
let dismiss_checked = if state.selected.is_empty() || state.is_loading {
|
let dismiss_checked = if state.selected.is_empty() || state.is_loading {
|
||||||
button(text(t(locale, "duplicates.dismissChecked")).size(13))
|
keyboard::button(text(t(locale, "duplicates.dismissChecked")).size(13))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
} else {
|
} else {
|
||||||
button(
|
keyboard::button(
|
||||||
text(tw(
|
text(tw(
|
||||||
locale,
|
locale,
|
||||||
"duplicates.dismissCheckedCount",
|
"duplicates.dismissCheckedCount",
|
||||||
@@ -64,12 +66,12 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
|||||||
.into(),
|
.into(),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
button(text(t(locale, "duplicates.checkAll")).size(13))
|
keyboard::button(text(t(locale, "duplicates.checkAll")).size(13))
|
||||||
.on_press(Message::DuplicatesCheckAll)
|
.on_press(Message::DuplicatesCheckAll)
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "duplicates.uncheckAll")).size(13))
|
keyboard::button(text(t(locale, "duplicates.uncheckAll")).size(13))
|
||||||
.on_press(Message::DuplicatesUncheckAll)
|
.on_press(Message::DuplicatesUncheckAll)
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
@@ -119,6 +121,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
|||||||
};
|
};
|
||||||
pairs = pairs.push(inputs::card(
|
pairs = pairs.push(inputs::card(
|
||||||
row![
|
row![
|
||||||
|
keyboard::focusable(
|
||||||
checkbox("", checked)
|
checkbox("", checked)
|
||||||
.on_toggle({
|
.on_toggle({
|
||||||
let a = pair.post_id_a.clone();
|
let a = pair.post_id_a.clone();
|
||||||
@@ -126,7 +129,9 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
|||||||
move |_| Message::DuplicatesToggle(a.clone(), b.clone())
|
move |_| Message::DuplicatesToggle(a.clone(), b.clone())
|
||||||
})
|
})
|
||||||
.size(16),
|
.size(16),
|
||||||
button(
|
true,
|
||||||
|
),
|
||||||
|
keyboard::button(
|
||||||
text(pair.title_a.clone())
|
text(pair.title_a.clone())
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -135,7 +140,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
|||||||
.padding([5, 8])
|
.padding([5, 8])
|
||||||
.style(inputs::disclosure_button),
|
.style(inputs::disclosure_button),
|
||||||
text("→").size(14).color(inputs::LABEL_COLOR),
|
text("→").size(14).color(inputs::LABEL_COLOR),
|
||||||
button(
|
keyboard::button(
|
||||||
text(pair.title_b.clone())
|
text(pair.title_b.clone())
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -149,7 +154,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
|||||||
} else {
|
} else {
|
||||||
Color::from_rgb(0.55, 0.76, 0.92)
|
Color::from_rgb(0.55, 0.76, 0.92)
|
||||||
}),
|
}),
|
||||||
button(text(t(locale, "duplicates.dismiss")).size(12))
|
keyboard::button(text(t(locale, "duplicates.dismiss")).size(12))
|
||||||
.on_press(Message::DuplicatesDismiss(
|
.on_press(Message::DuplicatesDismiss(
|
||||||
pair.post_id_a.clone(),
|
pair.post_id_a.clone(),
|
||||||
pair.post_id_b.clone()
|
pair.post_id_b.clone()
|
||||||
@@ -163,7 +168,7 @@ pub fn view(state: &DuplicatesState, locale: UiLocale) -> Element<'_, Message> {
|
|||||||
}
|
}
|
||||||
if state.result.has_more {
|
if state.result.has_more {
|
||||||
pairs = pairs.push(
|
pairs = pairs.push(
|
||||||
button(text(t(locale, "duplicates.showMore")).size(13))
|
keyboard::button(text(t(locale, "duplicates.showMore")).size(13))
|
||||||
.on_press(Message::DuplicatesShowMore)
|
.on_press(Message::DuplicatesShowMore)
|
||||||
.padding([7, 16])
|
.padding([7, 16])
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use bds_core::engine::git::{
|
use bds_core::engine::git::{
|
||||||
@@ -6,7 +7,7 @@ use bds_core::engine::git::{
|
|||||||
};
|
};
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
use iced::widget::text::{Shaping, Wrapping};
|
use iced::widget::text::{Shaping, Wrapping};
|
||||||
use iced::widget::{Space, button, column, container, row, scrollable, text, text_input, tooltip};
|
use iced::widget::{Space, column, container, row, scrollable, text, text_input, tooltip};
|
||||||
use iced::{Alignment, Background, Color, Element, Font, Length};
|
use iced::{Alignment, Background, Color, Element, Font, Length};
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
@@ -152,7 +153,7 @@ pub fn sidebar_view(
|
|||||||
text(t(locale, "git.notRepository"))
|
text(t(locale, "git.notRepository"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.color(Color::from_rgb(0.6, 0.6, 0.65)),
|
.color(Color::from_rgb(0.6, 0.6, 0.65)),
|
||||||
button(text(t(locale, "git.initialize")).size(12))
|
keyboard::button(text(t(locale, "git.initialize")).size(12))
|
||||||
.on_press(Message::GitInitialize)
|
.on_press(Message::GitInitialize)
|
||||||
.padding([5, 8])
|
.padding([5, 8])
|
||||||
.style(inputs::primary_button),
|
.style(inputs::primary_button),
|
||||||
@@ -180,7 +181,7 @@ pub fn sidebar_view(
|
|||||||
let network_running = state.network_run.is_some();
|
let network_running = state.network_run.is_some();
|
||||||
let network_button =
|
let network_button =
|
||||||
|key: &'static str, icon: &'static str, message: Message| -> Element<'static, Message> {
|
|key: &'static str, icon: &'static str, message: Message| -> Element<'static, Message> {
|
||||||
let mut control = button(text(icon).size(16).shaping(Shaping::Advanced))
|
let mut control = keyboard::button(text(icon).size(16).shaping(Shaping::Advanced))
|
||||||
.width(Length::Fixed(30.0))
|
.width(Length::Fixed(30.0))
|
||||||
.height(Length::Fixed(28.0))
|
.height(Length::Fixed(28.0))
|
||||||
.padding(0)
|
.padding(0)
|
||||||
@@ -189,13 +190,16 @@ pub fn sidebar_view(
|
|||||||
control = control.on_press(message);
|
control = control.on_press(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
keyboard::focusable(
|
||||||
tooltip(
|
tooltip(
|
||||||
control,
|
control,
|
||||||
text(t(locale, key)).size(12),
|
text(t(locale, key)).size(12),
|
||||||
tooltip::Position::Bottom,
|
tooltip::Position::Bottom,
|
||||||
)
|
)
|
||||||
.gap(4)
|
.gap(4)
|
||||||
.style(inputs::tooltip_style)
|
.style(inputs::tooltip_style),
|
||||||
|
!offline_mode && !network_running,
|
||||||
|
)
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
let actions = row![
|
let actions = row![
|
||||||
@@ -240,7 +244,7 @@ pub fn sidebar_view(
|
|||||||
.padding([5, 7])
|
.padding([5, 7])
|
||||||
.style(inputs::field_style),
|
.style(inputs::field_style),
|
||||||
{
|
{
|
||||||
let commit = button(text(t(locale, "git.commit")).size(11))
|
let commit = keyboard::button(text(t(locale, "git.commit")).size(11))
|
||||||
.padding([5, 7])
|
.padding([5, 7])
|
||||||
.style(inputs::primary_button);
|
.style(inputs::primary_button);
|
||||||
if state.files.is_empty() || state.commit_message.trim().is_empty() {
|
if state.files.is_empty() || state.commit_message.trim().is_empty() {
|
||||||
@@ -265,7 +269,7 @@ pub fn sidebar_view(
|
|||||||
content.extend(state.history.iter().take(20).map(history_button));
|
content.extend(state.history.iter().take(20).map(history_button));
|
||||||
}
|
}
|
||||||
content.push(
|
content.push(
|
||||||
button(text(t(locale, "git.pruneLfs")).size(11))
|
keyboard::button(text(t(locale, "git.pruneLfs")).size(11))
|
||||||
.on_press(Message::GitPruneLfs)
|
.on_press(Message::GitPruneLfs)
|
||||||
.padding([4, 7])
|
.padding([4, 7])
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
@@ -303,7 +307,7 @@ fn error_view(error: Option<&str>) -> Element<'static, Message> {
|
|||||||
|
|
||||||
fn status_button(file: &GitFileStatus) -> Element<'static, Message> {
|
fn status_button(file: &GitFileStatus) -> Element<'static, Message> {
|
||||||
let path = file.path.clone();
|
let path = file.path.clone();
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(path.clone()).size(11),
|
text(path.clone()).size(11),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
@@ -322,7 +326,7 @@ fn history_button(commit: &GitCommit) -> Element<'static, Message> {
|
|||||||
let hash = commit.hash.clone();
|
let hash = commit.hash.clone();
|
||||||
let subject = commit.subject.clone().unwrap_or_else(|| hash.clone());
|
let subject = commit.subject.clone().unwrap_or_else(|| hash.clone());
|
||||||
let short = hash.chars().take(7).collect::<String>();
|
let short = hash.chars().take(7).collect::<String>();
|
||||||
button(
|
keyboard::button(
|
||||||
column![
|
column![
|
||||||
text(subject.clone()).size(11),
|
text(subject.clone()).size(11),
|
||||||
row![
|
row![
|
||||||
@@ -399,7 +403,7 @@ fn network_output(run: &GitNetworkRunState, locale: UiLocale) -> Element<'static
|
|||||||
.wrapping(Wrapping::Word)
|
.wrapping(Wrapping::Word)
|
||||||
)
|
)
|
||||||
.padding(6),
|
.padding(6),
|
||||||
button(text(t(locale, "common.cancel")).size(11))
|
keyboard::button(text(t(locale, "common.cancel")).size(11))
|
||||||
.on_press(Message::CancelTask(
|
.on_press(Message::CancelTask(
|
||||||
crate::state::navigation::TaskSource::Local,
|
crate::state::navigation::TaskSource::Local,
|
||||||
run.task_id,
|
run.task_id,
|
||||||
@@ -445,7 +449,7 @@ pub fn diff_view(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|change| {
|
.map(|change| {
|
||||||
let selected = state.selected_path.as_deref() == Some(&change.path);
|
let selected = state.selected_path.as_deref() == Some(&change.path);
|
||||||
let button = button(text(change.path.clone()).size(11))
|
let button = keyboard::button(text(change.path.clone()).size(11))
|
||||||
.padding([4, 7])
|
.padding([4, 7])
|
||||||
.style(if selected {
|
.style(if selected {
|
||||||
inputs::primary_button
|
inputs::primary_button
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -5,7 +6,7 @@ use bds_core::model::{
|
|||||||
ImportCandidate, ImportDefinition, ImportExecutionResult, ImportItemKind, ImportItemStatus,
|
ImportCandidate, ImportDefinition, ImportExecutionResult, ImportItemKind, ImportItemStatus,
|
||||||
ImportPhase, ImportProgress, ImportReport, ImportResolution, TaxonomyKind,
|
ImportPhase, ImportProgress, ImportReport, ImportResolution, TaxonomyKind,
|
||||||
};
|
};
|
||||||
use iced::widget::{Space, button, column, container, progress_bar, row, scrollable, text};
|
use iced::widget::{Space, column, container, progress_bar, row, scrollable, text};
|
||||||
use iced::{Alignment, Color, Element, Length};
|
use iced::{Alignment, Color, Element, Length};
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
@@ -50,6 +51,25 @@ impl ImportEditorState {
|
|||||||
error: None,
|
error: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn section_is_visible(&self, section: ImportSection) -> bool {
|
||||||
|
let Some(report) = &self.report else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
match section {
|
||||||
|
ImportSection::Conflicts => report
|
||||||
|
.posts
|
||||||
|
.iter()
|
||||||
|
.chain(&report.pages)
|
||||||
|
.chain(&report.media)
|
||||||
|
.any(|item| item.status == ImportItemStatus::Conflict),
|
||||||
|
ImportSection::Posts => !report.posts.is_empty(),
|
||||||
|
ImportSection::Pages => !report.pages.is_empty(),
|
||||||
|
ImportSection::Media => !report.media.is_empty(),
|
||||||
|
ImportSection::Taxonomy => !report.taxonomies.is_empty(),
|
||||||
|
ImportSection::Macros => !report.macros.is_empty(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
@@ -62,6 +82,32 @@ pub enum ImportSection {
|
|||||||
Macros,
|
Macros,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImportSection {
|
||||||
|
pub fn access_key(self) -> char {
|
||||||
|
match self {
|
||||||
|
Self::Conflicts => 'c',
|
||||||
|
Self::Posts => 'o',
|
||||||
|
Self::Pages => 'a',
|
||||||
|
Self::Media => 'm',
|
||||||
|
Self::Taxonomy => 't',
|
||||||
|
Self::Macros => 'r',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_access_key(key: char) -> Option<Self> {
|
||||||
|
[
|
||||||
|
Self::Conflicts,
|
||||||
|
Self::Posts,
|
||||||
|
Self::Pages,
|
||||||
|
Self::Media,
|
||||||
|
Self::Taxonomy,
|
||||||
|
Self::Macros,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find(|section| section.access_key() == key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ImportEditorMsg {
|
pub enum ImportEditorMsg {
|
||||||
NameChanged(String),
|
NameChanged(String),
|
||||||
@@ -120,13 +166,13 @@ pub fn view<'a>(state: &'a ImportEditorState, locale: UiLocale) -> Element<'a, M
|
|||||||
column![
|
column![
|
||||||
row![
|
row![
|
||||||
name,
|
name,
|
||||||
button(text(t(locale, "modal.confirmDelete.delete")))
|
keyboard::button(text(t(locale, "modal.confirmDelete.delete")))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
(!busy).then_some(Message::ImportEditor(ImportEditorMsg::DeleteRequested,))
|
(!busy).then_some(Message::ImportEditor(ImportEditorMsg::DeleteRequested,))
|
||||||
)
|
)
|
||||||
.padding([8, 12])
|
.padding([8, 12])
|
||||||
.style(inputs::danger_button),
|
.style(inputs::danger_button),
|
||||||
button(text(t(locale, "import.analyze")))
|
keyboard::button(text(t(locale, "import.analyze")))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
(!busy && state.definition.wxr_file_path.is_some())
|
(!busy && state.definition.wxr_file_path.is_some())
|
||||||
.then_some(Message::ImportEditor(ImportEditorMsg::Analyze),)
|
.then_some(Message::ImportEditor(ImportEditorMsg::Analyze),)
|
||||||
@@ -290,7 +336,7 @@ fn path_row<'a>(
|
|||||||
]
|
]
|
||||||
.spacing(5)
|
.spacing(5)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "common.open")))
|
keyboard::button(text(t(locale, "common.open")))
|
||||||
.on_press(Message::ImportEditor(action))
|
.on_press(Message::ImportEditor(action))
|
||||||
.padding([7, 12])
|
.padding([7, 12])
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
@@ -457,7 +503,7 @@ fn execute_toolbar<'a>(
|
|||||||
.into(),
|
.into(),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
button(text(t(locale, "import.autoMap")))
|
keyboard::button(text(t(locale, "import.autoMap")))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
(!state.is_analyzing && !state.is_executing)
|
(!state.is_analyzing && !state.is_executing)
|
||||||
.then_some(Message::ImportEditor(ImportEditorMsg::AutoMapTaxonomy)),
|
.then_some(Message::ImportEditor(ImportEditorMsg::AutoMapTaxonomy)),
|
||||||
@@ -465,7 +511,7 @@ fn execute_toolbar<'a>(
|
|||||||
.padding([8, 12])
|
.padding([8, 12])
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.into(),
|
.into(),
|
||||||
button(text(tw(
|
keyboard::button(text(tw(
|
||||||
locale,
|
locale,
|
||||||
"import.execute",
|
"import.execute",
|
||||||
&[("count", &count.to_string())],
|
&[("count", &count.to_string())],
|
||||||
@@ -514,7 +560,7 @@ fn section<'a>(
|
|||||||
) -> Vec<Element<'a, Message>> {
|
) -> Vec<Element<'a, Message>> {
|
||||||
let expanded = state.expanded.contains(§ion);
|
let expanded = state.expanded.contains(§ion);
|
||||||
let header = inputs::card(
|
let header = inputs::card(
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(if expanded { "▾" } else { "▸" }).size(12),
|
text(if expanded { "▾" } else { "▸" }).size(12),
|
||||||
text(t(locale, title_key)).size(13),
|
text(t(locale, title_key)).size(13),
|
||||||
@@ -524,6 +570,7 @@ fn section<'a>(
|
|||||||
.on_press(Message::ImportEditor(ImportEditorMsg::ToggleSection(
|
.on_press(Message::ImportEditor(ImportEditorMsg::ToggleSection(
|
||||||
section,
|
section,
|
||||||
)))
|
)))
|
||||||
|
.hotkey(section.access_key())
|
||||||
.padding([6, 8])
|
.padding([6, 8])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.style(inputs::disclosure_button),
|
.style(inputs::disclosure_button),
|
||||||
@@ -673,7 +720,7 @@ fn taxonomy_rows<'a>(
|
|||||||
}),
|
}),
|
||||||
))
|
))
|
||||||
.width(Length::Fixed(260.0)),
|
.width(Length::Fixed(260.0)),
|
||||||
button(text(t(locale, "common.clear")))
|
keyboard::button(text(t(locale, "common.clear")))
|
||||||
.on_press_maybe(item.mapped_to.is_some().then_some(Message::ImportEditor(
|
.on_press_maybe(item.mapped_to.is_some().then_some(Message::ImportEditor(
|
||||||
ImportEditorMsg::SetTaxonomyMapping {
|
ImportEditorMsg::SetTaxonomyMapping {
|
||||||
kind,
|
kind,
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, column, container, image, row, scrollable, text};
|
use iced::widget::{Space, column, container, image, row, scrollable, text};
|
||||||
use iced::{Color, Element, Length};
|
use iced::{Color, Element, Length};
|
||||||
|
|
||||||
use bds_core::i18n::{self, UiLocale};
|
use bds_core::i18n::{self, UiLocale};
|
||||||
@@ -271,7 +272,7 @@ pub fn view<'a>(
|
|||||||
.style(status_bar::dropdown_bg)
|
.style(status_bar::dropdown_bg)
|
||||||
.into();
|
.into();
|
||||||
let quick_actions_button: Element<'a, Message> =
|
let quick_actions_button: Element<'a, Message> =
|
||||||
button(text(t(locale, "editor.quickActions")).size(13))
|
keyboard::button(text(t(locale, "editor.quickActions")).size(13))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
ai_enabled.then_some(Message::MediaEditor(MediaEditorMsg::ToggleQuickActions)),
|
ai_enabled.then_some(Message::MediaEditor(MediaEditorMsg::ToggleQuickActions)),
|
||||||
)
|
)
|
||||||
@@ -290,17 +291,17 @@ pub fn view<'a>(
|
|||||||
vec![text(state.original_name.clone()).size(18).into()],
|
vec![text(state.original_name.clone()).size(18).into()],
|
||||||
vec![
|
vec![
|
||||||
quick_actions,
|
quick_actions,
|
||||||
button(text(t(locale, "editor.replaceFile")).size(13))
|
keyboard::button(text(t(locale, "editor.replaceFile")).size(13))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::ReplaceFile))
|
.on_press(Message::MediaEditor(MediaEditorMsg::ReplaceFile))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "common.save")).size(13))
|
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::Save))
|
.on_press(Message::MediaEditor(MediaEditorMsg::Save))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::Delete))
|
.on_press(Message::MediaEditor(MediaEditorMsg::Delete))
|
||||||
.style(inputs::danger_button)
|
.style(inputs::danger_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
@@ -324,7 +325,7 @@ pub fn view<'a>(
|
|||||||
} else {
|
} else {
|
||||||
Color::from_rgb(0.55, 0.58, 0.65)
|
Color::from_rgb(0.55, 0.58, 0.65)
|
||||||
};
|
};
|
||||||
button(text(label).size(12).shaping(Shaping::Advanced).color(color))
|
keyboard::button(text(label).size(12).shaping(Shaping::Advanced).color(color))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::SwitchLanguage(
|
.on_press(Message::MediaEditor(MediaEditorMsg::SwitchLanguage(
|
||||||
flag.language.clone(),
|
flag.language.clone(),
|
||||||
)))
|
)))
|
||||||
@@ -430,7 +431,7 @@ pub fn view<'a>(
|
|||||||
.size(12)
|
.size(12)
|
||||||
.color(Color::from_rgb(0.55, 0.58, 0.65)),
|
.color(Color::from_rgb(0.55, 0.58, 0.65)),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "editor.linkToPost")).size(12))
|
keyboard::button(text(t(locale, "editor.linkToPost")).size(12))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::TogglePostPicker))
|
.on_press(Message::MediaEditor(MediaEditorMsg::TogglePostPicker))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([4, 10]),
|
.padding([4, 10]),
|
||||||
@@ -457,7 +458,7 @@ pub fn view<'a>(
|
|||||||
.post_picker_results
|
.post_picker_results
|
||||||
.iter()
|
.iter()
|
||||||
.map(|post| {
|
.map(|post| {
|
||||||
button(text(post.title.clone()).size(12))
|
keyboard::button(text(post.title.clone()).size(12))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::LinkPost(
|
.on_press(Message::MediaEditor(MediaEditorMsg::LinkPost(
|
||||||
post.post_id.clone(),
|
post.post_id.clone(),
|
||||||
)))
|
)))
|
||||||
@@ -497,13 +498,13 @@ pub fn view<'a>(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|post| {
|
.map(|post| {
|
||||||
row![
|
row![
|
||||||
button(text(post.title.clone()).size(12))
|
keyboard::button(text(post.title.clone()).size(12))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::OpenLinkedPost(
|
.on_press(Message::MediaEditor(MediaEditorMsg::OpenLinkedPost(
|
||||||
post.post_id.clone()
|
post.post_id.clone()
|
||||||
)))
|
)))
|
||||||
.padding([4, 0]),
|
.padding([4, 0]),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "editor.unlinkMedia")).size(11))
|
keyboard::button(text(t(locale, "editor.unlinkMedia")).size(11))
|
||||||
.on_press(Message::MediaEditor(MediaEditorMsg::UnlinkPost(
|
.on_press(Message::MediaEditor(MediaEditorMsg::UnlinkPost(
|
||||||
post.post_id.clone()
|
post.post_id.clone()
|
||||||
)))
|
)))
|
||||||
@@ -569,7 +570,7 @@ fn quick_action_item<'a>(
|
|||||||
msg: MediaEditorMsg,
|
msg: MediaEditorMsg,
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
button(text(label).size(12).shaping(Shaping::Advanced))
|
keyboard::button(text(label).size(12).shaping(Shaping::Advanced))
|
||||||
.on_press_maybe(enabled.then_some(Message::MediaEditor(msg)))
|
.on_press_maybe(enabled.then_some(Message::MediaEditor(msg)))
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(status_bar::dropdown_item)
|
.style(status_bar::dropdown_item)
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use bds_core::engine::menu::{MenuItem, MenuItemKind};
|
use bds_core::engine::menu::{MenuItem, MenuItemKind};
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
Space, button, column, container, mouse_area, row, scrollable, svg, text, text_input, tooltip,
|
Space, column, container, mouse_area, row, scrollable, svg, text, text_input, tooltip,
|
||||||
};
|
};
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Point, Theme};
|
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Point, Theme};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
@@ -810,19 +812,22 @@ fn toolbar_icon(
|
|||||||
} else {
|
} else {
|
||||||
secondary_button
|
secondary_button
|
||||||
};
|
};
|
||||||
let mut control = button(text(glyph).size(18))
|
let mut control = keyboard::button(text(glyph).size(18))
|
||||||
.width(Length::Fixed(38.0))
|
.width(Length::Fixed(38.0))
|
||||||
.height(Length::Fixed(34.0))
|
.height(Length::Fixed(34.0))
|
||||||
.style(style);
|
.style(style);
|
||||||
if enabled {
|
if enabled {
|
||||||
control = control.on_press(Message::MenuEditor(message));
|
control = control.on_press(Message::MenuEditor(message));
|
||||||
}
|
}
|
||||||
|
keyboard::focusable(
|
||||||
tooltip(
|
tooltip(
|
||||||
control,
|
control,
|
||||||
text(t(locale, key)).size(12),
|
text(t(locale, key)).size(12),
|
||||||
tooltip::Position::Bottom,
|
tooltip::Position::Bottom,
|
||||||
)
|
)
|
||||||
.gap(4)
|
.gap(4),
|
||||||
|
enabled,
|
||||||
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -856,7 +861,7 @@ fn tree_item<'a>(
|
|||||||
let collapsed = state.collapsed.contains(&item.id);
|
let collapsed = state.collapsed.contains(&item.id);
|
||||||
let id = item.id.clone();
|
let id = item.id.clone();
|
||||||
let toggle: Element<'_, Message> = if is_submenu {
|
let toggle: Element<'_, Message> = if is_submenu {
|
||||||
button(text(if collapsed { "▸" } else { "▾" }).size(14))
|
keyboard::button(text(if collapsed { "▸" } else { "▾" }).size(14))
|
||||||
.on_press(Message::MenuEditor(MenuEditorMsg::ToggleExpanded(
|
.on_press(Message::MenuEditor(MenuEditorMsg::ToggleExpanded(
|
||||||
id.clone(),
|
id.clone(),
|
||||||
)))
|
)))
|
||||||
@@ -884,7 +889,7 @@ fn tree_item<'a>(
|
|||||||
} else {
|
} else {
|
||||||
item.label.clone()
|
item.label.clone()
|
||||||
};
|
};
|
||||||
let label_button = button(
|
let label_button = keyboard::button(
|
||||||
row![
|
row![
|
||||||
kind_icon(&item.kind),
|
kind_icon(&item.kind),
|
||||||
text(label).size(14),
|
text(label).size(14),
|
||||||
@@ -1016,7 +1021,7 @@ fn draft_editor<'a>(
|
|||||||
.filter(|page| page_matches_query(page, &draft.query))
|
.filter(|page| page_matches_query(page, &draft.query))
|
||||||
{
|
{
|
||||||
choices = choices.push(
|
choices = choices.push(
|
||||||
button(text(page.title.clone()))
|
keyboard::button(text(page.title.clone()))
|
||||||
.on_press(Message::MenuEditor(MenuEditorMsg::ChoosePage(
|
.on_press(Message::MenuEditor(MenuEditorMsg::ChoosePage(
|
||||||
page.id.clone(),
|
page.id.clone(),
|
||||||
)))
|
)))
|
||||||
@@ -1032,7 +1037,7 @@ fn draft_editor<'a>(
|
|||||||
.filter(|name| category_matches_query(name, &draft.query))
|
.filter(|name| category_matches_query(name, &draft.query))
|
||||||
{
|
{
|
||||||
choices = choices.push(
|
choices = choices.push(
|
||||||
button(text(category.clone()))
|
keyboard::button(text(category.clone()))
|
||||||
.on_press(Message::MenuEditor(MenuEditorMsg::ChooseCategory(
|
.on_press(Message::MenuEditor(MenuEditorMsg::ChooseCategory(
|
||||||
category.clone(),
|
category.clone(),
|
||||||
)))
|
)))
|
||||||
@@ -1047,10 +1052,10 @@ fn draft_editor<'a>(
|
|||||||
DraftKind::Category => "menuEditor.useCategory",
|
DraftKind::Category => "menuEditor.useCategory",
|
||||||
};
|
};
|
||||||
let actions = row![
|
let actions = row![
|
||||||
button(text(t(locale, submit_label)))
|
keyboard::button(text(t(locale, submit_label)))
|
||||||
.on_press(Message::MenuEditor(MenuEditorMsg::SubmitDraft))
|
.on_press(Message::MenuEditor(MenuEditorMsg::SubmitDraft))
|
||||||
.style(primary_button),
|
.style(primary_button),
|
||||||
button(text(t(locale, "common.cancel")))
|
keyboard::button(text(t(locale, "common.cancel")))
|
||||||
.on_press(Message::MenuEditor(MenuEditorMsg::CancelDraft))
|
.on_press(Message::MenuEditor(MenuEditorMsg::CancelDraft))
|
||||||
.style(secondary_button),
|
.style(secondary_button),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||||
use iced::{Color, Element, Length};
|
use iced::{Color, Element, Length};
|
||||||
|
|
||||||
use bds_core::engine::metadata_diff::{DiffReport, RepairDirection};
|
use bds_core::engine::metadata_diff::{DiffReport, RepairDirection};
|
||||||
@@ -18,10 +19,11 @@ pub struct MetadataDiffState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, Message> {
|
pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, Message> {
|
||||||
let run = button(text(t(locale, "metadataDiff.run")).size(13))
|
let run = keyboard::button(text(t(locale, "metadataDiff.run")).size(13))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
(!state.is_running && !state.is_repairing).then_some(Message::RunMetadataDiff),
|
(!state.is_running && !state.is_repairing).then_some(Message::RunMetadataDiff),
|
||||||
)
|
)
|
||||||
|
.hotkey('r')
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
let mut content = column![
|
let mut content = column![
|
||||||
@@ -65,7 +67,7 @@ pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, M
|
|||||||
)
|
)
|
||||||
});
|
});
|
||||||
let actions = row![
|
let actions = row![
|
||||||
button(text(t(locale, "metadataDiff.fileToDb")).size(12))
|
keyboard::button(text(t(locale, "metadataDiff.fileToDb")).size(12))
|
||||||
.on_press_maybe((!state.is_repairing).then_some(
|
.on_press_maybe((!state.is_repairing).then_some(
|
||||||
Message::RepairMetadataDiffItem {
|
Message::RepairMetadataDiffItem {
|
||||||
index,
|
index,
|
||||||
@@ -74,7 +76,7 @@ pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, M
|
|||||||
))
|
))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([5, 10]),
|
.padding([5, 10]),
|
||||||
button(text(t(locale, "metadataDiff.dbToFile")).size(12))
|
keyboard::button(text(t(locale, "metadataDiff.dbToFile")).size(12))
|
||||||
.on_press_maybe((!state.is_repairing).then_some(
|
.on_press_maybe((!state.is_repairing).then_some(
|
||||||
Message::RepairMetadataDiffItem {
|
Message::RepairMetadataDiffItem {
|
||||||
index,
|
index,
|
||||||
@@ -116,7 +118,7 @@ pub fn view<'a>(state: &'a MetadataDiffState, locale: UiLocale) -> Element<'a, M
|
|||||||
.align_y(iced::Alignment::Center);
|
.align_y(iced::Alignment::Center);
|
||||||
if orphan.reason == "file_without_db_entry" {
|
if orphan.reason == "file_without_db_entry" {
|
||||||
item = item.push(
|
item = item.push(
|
||||||
button(text(t(locale, "metadataDiff.importOrphan")).size(12))
|
keyboard::button(text(t(locale, "metadataDiff.importOrphan")).size(12))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
(!state.is_repairing)
|
(!state.is_repairing)
|
||||||
.then_some(Message::ImportMetadataOrphan(index)),
|
.then_some(Message::ImportMetadataOrphan(index)),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{
|
use iced::widget::{Space, checkbox, column, container, image, row, scrollable, text, text_input};
|
||||||
Space, button, checkbox, column, container, image, row, scrollable, text, text_input,
|
|
||||||
};
|
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Length, Shadow, Theme, Vector};
|
use iced::{Alignment, Background, Border, Color, Element, Length, Shadow, Theme, Vector};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -282,7 +282,7 @@ pub fn view(
|
|||||||
let selected = selected_project_id.as_deref() == Some(project.id.as_str());
|
let selected = selected_project_id.as_deref() == Some(project.id.as_str());
|
||||||
let marker = if selected { "●" } else { "○" };
|
let marker = if selected { "●" } else { "○" };
|
||||||
project_rows = project_rows.push(
|
project_rows = project_rows.push(
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(marker).size(12),
|
text(marker).size(12),
|
||||||
text(project.name.clone()).size(13),
|
text(project.name.clone()).size(13),
|
||||||
@@ -307,12 +307,12 @@ pub fn view(
|
|||||||
content = content.push(project_rows);
|
content = content.push(project_rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
let cancel = button(text(t(locale, "remoteConnection.cancel")).size(13))
|
let cancel = keyboard::button(text(t(locale, "remoteConnection.cancel")).size(13))
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style);
|
.style(cancel_button_style);
|
||||||
let action = if !connected {
|
let action = if !connected {
|
||||||
let button = button(
|
let button = keyboard::button(
|
||||||
text(if connecting {
|
text(if connecting {
|
||||||
t(locale, "remoteConnection.connecting")
|
t(locale, "remoteConnection.connecting")
|
||||||
} else {
|
} else {
|
||||||
@@ -328,7 +328,7 @@ pub fn view(
|
|||||||
button.on_press(Message::RemoteConnectRequested)
|
button.on_press(Message::RemoteConnectRequested)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let button = button(text(t(locale, "remoteConnection.open")).size(13))
|
let button = keyboard::button(text(t(locale, "remoteConnection.open")).size(13))
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(confirm_button_style);
|
.style(confirm_button_style);
|
||||||
if selected_project_id.is_some() {
|
if selected_project_id.is_some() {
|
||||||
@@ -393,7 +393,7 @@ pub fn view(
|
|||||||
|
|
||||||
let on_confirm_clone = on_confirm.clone();
|
let on_confirm_clone = on_confirm.clone();
|
||||||
let buttons = row![
|
let buttons = row![
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "modal.confirmDelete.cancel"))
|
text(t(locale, "modal.confirmDelete.cancel"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -402,7 +402,7 @@ pub fn view(
|
|||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "modal.confirmDelete.delete"))
|
text(t(locale, "modal.confirmDelete.delete"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -438,7 +438,7 @@ pub fn view(
|
|||||||
|
|
||||||
let on_confirm_clone = on_confirm.clone();
|
let on_confirm_clone = on_confirm.clone();
|
||||||
let buttons = row![
|
let buttons = row![
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "modal.confirm.cancel"))
|
text(t(locale, "modal.confirm.cancel"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -447,7 +447,7 @@ pub fn view(
|
|||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "modal.confirm.confirm"))
|
text(t(locale, "modal.confirm.confirm"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -474,12 +474,12 @@ pub fn view(
|
|||||||
|
|
||||||
ModalState::SearchIndexRepair => {
|
ModalState::SearchIndexRepair => {
|
||||||
let buttons = row![
|
let buttons = row![
|
||||||
button(text(t(locale, "searchIndexRepair.later")).size(13))
|
keyboard::button(text(t(locale, "searchIndexRepair.later")).size(13))
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "searchIndexRepair.rebuildNow")).size(13))
|
keyboard::button(text(t(locale, "searchIndexRepair.rebuildNow")).size(13))
|
||||||
.on_press(Message::ConfirmModal(ConfirmAction::RebuildSearchIndex))
|
.on_press(Message::ConfirmModal(ConfirmAction::RebuildSearchIndex))
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(confirm_button_style),
|
.style(confirm_button_style),
|
||||||
@@ -520,7 +520,7 @@ pub fn view(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut actions = row![
|
let mut actions = row![
|
||||||
button(text(t(locale, "find.next")))
|
keyboard::button(text(t(locale, "find.next")))
|
||||||
.on_press(Message::FindNext)
|
.on_press(Message::FindNext)
|
||||||
.style(inputs::primary_button),
|
.style(inputs::primary_button),
|
||||||
]
|
]
|
||||||
@@ -528,12 +528,12 @@ pub fn view(
|
|||||||
if show_replace {
|
if show_replace {
|
||||||
actions = actions
|
actions = actions
|
||||||
.push(
|
.push(
|
||||||
button(text(t(locale, "find.replace")))
|
keyboard::button(text(t(locale, "find.replace")))
|
||||||
.on_press(Message::ReplaceCurrent)
|
.on_press(Message::ReplaceCurrent)
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
)
|
)
|
||||||
.push(
|
.push(
|
||||||
button(text(t(locale, "find.replaceAll")))
|
keyboard::button(text(t(locale, "find.replaceAll")))
|
||||||
.on_press(Message::ReplaceAll)
|
.on_press(Message::ReplaceAll)
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
);
|
);
|
||||||
@@ -582,7 +582,7 @@ pub fn view(
|
|||||||
} else {
|
} else {
|
||||||
content.push(Space::with_height(16.0)).push(row![
|
content.push(Space::with_height(16.0)).push(row![
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "tasks.cancelTask")).size(13))
|
keyboard::button(text(t(locale, "tasks.cancelTask")).size(13))
|
||||||
.on_press(Message::CancelTask(
|
.on_press(Message::CancelTask(
|
||||||
crate::state::navigation::TaskSource::Local,
|
crate::state::navigation::TaskSource::Local,
|
||||||
task_id,
|
task_id,
|
||||||
@@ -607,7 +607,7 @@ pub fn view(
|
|||||||
external_url,
|
external_url,
|
||||||
external_text,
|
external_text,
|
||||||
} => {
|
} => {
|
||||||
let internal_tab = button(
|
let internal_tab = keyboard::button(
|
||||||
text(t(locale, "modal.postInsertLink.tabInternal"))
|
text(t(locale, "modal.postInsertLink.tabInternal"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -623,7 +623,7 @@ pub fn view(
|
|||||||
.padding([8, 16])
|
.padding([8, 16])
|
||||||
.style(cancel_button_style);
|
.style(cancel_button_style);
|
||||||
|
|
||||||
let external_tab = button(
|
let external_tab = keyboard::button(
|
||||||
text(t(locale, "modal.postInsertLink.tabExternal"))
|
text(t(locale, "modal.postInsertLink.tabExternal"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -666,7 +666,7 @@ pub fn view(
|
|||||||
let mut column = column![search_input, Space::with_height(12.0)];
|
let mut column = column![search_input, Space::with_height(12.0)];
|
||||||
for link in results {
|
for link in results {
|
||||||
column = column.push(
|
column = column.push(
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
column![
|
column![
|
||||||
text(link.title.clone())
|
text(link.title.clone())
|
||||||
@@ -749,7 +749,7 @@ pub fn view(
|
|||||||
Space::with_height(12.0),
|
Space::with_height(12.0),
|
||||||
row![
|
row![
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "modal.postInsertLink.insert")))
|
keyboard::button(text(t(locale, "modal.postInsertLink.insert")))
|
||||||
.on_press(Message::PostEditor(
|
.on_press(Message::PostEditor(
|
||||||
PostEditorMsg::PostInsertLinkExternalInsert
|
PostEditorMsg::PostInsertLinkExternalInsert
|
||||||
))
|
))
|
||||||
@@ -760,7 +760,7 @@ pub fn view(
|
|||||||
.spacing(8)
|
.spacing(8)
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
let create_post_btn: Element<'static, Message> = button(
|
let create_post_btn: Element<'static, Message> = keyboard::button(
|
||||||
text(t(locale, "modal.postInsertLink.createPost"))
|
text(t(locale, "modal.postInsertLink.createPost"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -792,7 +792,7 @@ pub fn view(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let buttons = row![
|
let buttons = row![
|
||||||
button(cancel_text)
|
keyboard::button(cancel_text)
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
@@ -903,7 +903,7 @@ pub fn view(
|
|||||||
.spacing(4)
|
.spacing(4)
|
||||||
.align_x(Alignment::Center);
|
.align_x(Alignment::Center);
|
||||||
|
|
||||||
let btn = button(media_col)
|
let btn = keyboard::button(media_col)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::PostInsertMediaSelected(
|
.on_press(Message::PostEditor(PostEditorMsg::PostInsertMediaSelected(
|
||||||
m.id.clone(),
|
m.id.clone(),
|
||||||
)))
|
)))
|
||||||
@@ -963,7 +963,7 @@ pub fn view(
|
|||||||
.shaping(Shaping::Advanced);
|
.shaping(Shaping::Advanced);
|
||||||
|
|
||||||
let buttons = row![
|
let buttons = row![
|
||||||
button(cancel_text)
|
keyboard::button(cancel_text)
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
@@ -1045,7 +1045,7 @@ pub fn view(
|
|||||||
.spacing(4)
|
.spacing(4)
|
||||||
.align_x(Alignment::Center);
|
.align_x(Alignment::Center);
|
||||||
|
|
||||||
let btn = button(media_col)
|
let btn = keyboard::button(media_col)
|
||||||
.on_press(Message::PostEditor(
|
.on_press(Message::PostEditor(
|
||||||
PostEditorMsg::PostGalleryImageSelected(index),
|
PostEditorMsg::PostGalleryImageSelected(index),
|
||||||
))
|
))
|
||||||
@@ -1082,7 +1082,7 @@ pub fn view(
|
|||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced);
|
.shaping(Shaping::Advanced);
|
||||||
|
|
||||||
let close_button = button(close_text)
|
let close_button = keyboard::button(close_text)
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style);
|
.style(cancel_button_style);
|
||||||
@@ -1106,7 +1106,7 @@ pub fn view(
|
|||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.center_x(Length::Fill),
|
.center_x(Length::Fill),
|
||||||
row![
|
row![
|
||||||
button(text("<"))
|
keyboard::button(text("<"))
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::PostGalleryPrevious))
|
.on_press(Message::PostEditor(PostEditorMsg::PostGalleryPrevious))
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
@@ -1115,12 +1115,12 @@ pub fn view(
|
|||||||
.size(12)
|
.size(12)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(">"))
|
keyboard::button(text(">"))
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::PostGalleryNext))
|
.on_press(Message::PostEditor(PostEditorMsg::PostGalleryNext))
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
Space::with_width(12.0),
|
Space::with_width(12.0),
|
||||||
button(text(t(locale, "modal.postGallery.backToGrid")))
|
keyboard::button(text(t(locale, "modal.postGallery.backToGrid")))
|
||||||
.on_press(Message::PostEditor(
|
.on_press(Message::PostEditor(
|
||||||
PostEditorMsg::PostGalleryCloseLightbox
|
PostEditorMsg::PostGalleryCloseLightbox
|
||||||
))
|
))
|
||||||
@@ -1166,13 +1166,15 @@ pub fn view(
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(index, field)| {
|
.map(|(index, field)| {
|
||||||
let toggle =
|
let toggle = keyboard::focusable(
|
||||||
checkbox(field.label.clone(), field.accepted)
|
checkbox(field.label.clone(), field.accepted)
|
||||||
.on_toggle_maybe((!field.locked).then_some(move |value| {
|
.on_toggle_maybe((!field.locked).then_some(move |value| {
|
||||||
Message::ToggleAiSuggestionField(index, value)
|
Message::ToggleAiSuggestionField(index, value)
|
||||||
}))
|
}))
|
||||||
.size(16)
|
.size(16)
|
||||||
.text_size(13);
|
.text_size(13),
|
||||||
|
!field.locked,
|
||||||
|
);
|
||||||
container(
|
container(
|
||||||
column![
|
column![
|
||||||
toggle,
|
toggle,
|
||||||
@@ -1211,12 +1213,12 @@ pub fn view(
|
|||||||
.collect::<Vec<Element<'static, Message>>>();
|
.collect::<Vec<Element<'static, Message>>>();
|
||||||
|
|
||||||
let buttons = row![
|
let buttons = row![
|
||||||
button(text(t(locale, "common.cancel")).size(13))
|
keyboard::button(text(t(locale, "common.cancel")).size(13))
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "modal.aiSuggestions.applySelected")).size(13))
|
keyboard::button(text(t(locale, "modal.aiSuggestions.applySelected")).size(13))
|
||||||
.on_press(Message::ApplyAiSuggestions(target, fields))
|
.on_press(Message::ApplyAiSuggestions(target, fields))
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(confirm_button_style),
|
.style(confirm_button_style),
|
||||||
@@ -1275,7 +1277,7 @@ pub fn view(
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
let status = language.existing_status.clone().unwrap_or_default();
|
let status = language.existing_status.clone().unwrap_or_default();
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(format!("{} {}", language.flag_emoji, language.name))
|
text(format!("{} {}", language.flag_emoji, language.name))
|
||||||
.size(13)
|
.size(13)
|
||||||
@@ -1302,7 +1304,7 @@ pub fn view(
|
|||||||
Space::with_height(12.0),
|
Space::with_height(12.0),
|
||||||
column(rows).spacing(6),
|
column(rows).spacing(6),
|
||||||
Space::with_height(16.0),
|
Space::with_height(16.0),
|
||||||
button(text(t(locale, "common.cancel")).size(13))
|
keyboard::button(text(t(locale, "common.cancel")).size(13))
|
||||||
.on_press(Message::DismissModal)
|
.on_press(Message::DismissModal)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.style(cancel_button_style),
|
.style(cancel_button_style),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, column, container, progress_bar, row, scrollable, text};
|
use iced::widget::{Space, column, container, progress_bar, row, scrollable, text};
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Font, Length, Theme};
|
use iced::{Alignment, Background, Border, Color, Element, Font, Length, Theme};
|
||||||
|
|
||||||
use bds_core::engine::git::GitCommit;
|
use bds_core::engine::git::GitCommit;
|
||||||
@@ -80,7 +82,7 @@ fn task_row(
|
|||||||
rows.push(
|
rows.push(
|
||||||
row![
|
row![
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(text(t(locale, "tasks.cancelTask")).size(10))
|
keyboard::button(text(t(locale, "tasks.cancelTask")).size(10))
|
||||||
.on_press(Message::CancelTask(snapshot.source, snapshot.id))
|
.on_press(Message::CancelTask(snapshot.source, snapshot.id))
|
||||||
.padding([3, 8])
|
.padding([3, 8])
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
@@ -267,7 +269,7 @@ pub fn view(
|
|||||||
// Tab header — per layout.allium: tasks, output, post_links (only when
|
// Tab header — per layout.allium: tasks, output, post_links (only when
|
||||||
// active editor tab is a post), git_log (only when active tab is post or
|
// active editor tab is a post), git_log (only when active tab is post or
|
||||||
// media).
|
// media).
|
||||||
let tasks_btn = button(
|
let tasks_btn = keyboard::button(
|
||||||
text(t(locale, "common.tasks"))
|
text(t(locale, "common.tasks"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -280,7 +282,7 @@ pub fn view(
|
|||||||
tab_inactive
|
tab_inactive
|
||||||
});
|
});
|
||||||
|
|
||||||
let output_btn = button(
|
let output_btn = keyboard::button(
|
||||||
text(t(locale, "panel.output"))
|
text(t(locale, "panel.output"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -293,7 +295,7 @@ pub fn view(
|
|||||||
tab_inactive
|
tab_inactive
|
||||||
});
|
});
|
||||||
|
|
||||||
let close_btn = button(text("\u{2715}").size(12).shaping(Shaping::Advanced))
|
let close_btn = keyboard::button(text("\u{2715}").size(12).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::TogglePanel)
|
.on_press(Message::TogglePanel)
|
||||||
.padding([4, 6])
|
.padding([4, 6])
|
||||||
.style(close_btn_style);
|
.style(close_btn_style);
|
||||||
@@ -301,7 +303,7 @@ pub fn view(
|
|||||||
let mut tab_row: Vec<Element<'static, Message>> = vec![tasks_btn.into(), output_btn.into()];
|
let mut tab_row: Vec<Element<'static, Message>> = vec![tasks_btn.into(), output_btn.into()];
|
||||||
|
|
||||||
if active_tab_is_post {
|
if active_tab_is_post {
|
||||||
let post_links_btn = button(
|
let post_links_btn = keyboard::button(
|
||||||
text(t(locale, "panel.postLinks"))
|
text(t(locale, "panel.postLinks"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -317,7 +319,7 @@ pub fn view(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if active_tab_is_post_or_media {
|
if active_tab_is_post_or_media {
|
||||||
let git_log_btn = button(
|
let git_log_btn = keyboard::button(
|
||||||
text(t(locale, "panel.gitLog"))
|
text(t(locale, "panel.gitLog"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -372,7 +374,7 @@ pub fn view(
|
|||||||
let collapsed = collapsed_task_groups.contains(group_id);
|
let collapsed = collapsed_task_groups.contains(group_id);
|
||||||
let group_name = snapshot.group_name.as_deref().unwrap_or(group_id);
|
let group_name = snapshot.group_name.as_deref().unwrap_or(group_id);
|
||||||
items.push(
|
items.push(
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(if collapsed { "\u{25b8}" } else { "\u{25be}" }).size(11),
|
text(if collapsed { "\u{25b8}" } else { "\u{25be}" }).size(11),
|
||||||
text(format!("{} ({})", group_name, members.len()))
|
text(format!("{} ({})", group_name, members.len()))
|
||||||
@@ -524,7 +526,7 @@ pub fn view(
|
|||||||
let hash = commit.hash.clone();
|
let hash = commit.hash.clone();
|
||||||
let subject = commit.subject.clone().unwrap_or_else(|| hash.clone());
|
let subject = commit.subject.clone().unwrap_or_else(|| hash.clone());
|
||||||
let short = hash.chars().take(7).collect::<String>();
|
let short = hash.chars().take(7).collect::<String>();
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(short).size(11).font(iced::Font::MONOSPACE),
|
text(short).size(11).font(iced::Font::MONOSPACE),
|
||||||
text(subject.clone()).size(11),
|
text(subject.clone()).size(11),
|
||||||
@@ -560,7 +562,7 @@ pub fn view(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn post_link_button(locale: UiLocale, link: &ResolvedPostLink) -> Element<'static, Message> {
|
fn post_link_button(locale: UiLocale, link: &ResolvedPostLink) -> Element<'static, Message> {
|
||||||
button(text(link.title.clone()).size(11).shaping(Shaping::Advanced))
|
keyboard::button(text(link.title.clone()).size(11).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::OpenTab(Tab {
|
.on_press(Message::OpenTab(Tab {
|
||||||
id: link.post_id.clone(),
|
id: link.post_id.clone(),
|
||||||
title: if link.title.is_empty() {
|
title: if link.title.is_empty() {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicU64, Ordering};
|
use std::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
|
||||||
use iced::widget::text::{Shaping, Wrapping};
|
use iced::widget::text::{Shaping, Wrapping};
|
||||||
use iced::widget::{Column, Space, button, column, container, row, scrollable, text, text_input};
|
use iced::widget::{Column, Space, column, container, row, scrollable, text, text_input};
|
||||||
use iced::{Color, Element, Length, Theme};
|
use iced::{Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::{self, UiLocale};
|
use bds_core::i18n::{self, UiLocale};
|
||||||
@@ -489,7 +491,7 @@ pub fn view<'a>(
|
|||||||
format!("{}{}", truncate_header_title(&state.title), dirty_indicator)
|
format!("{}{}", truncate_header_title(&state.title), dirty_indicator)
|
||||||
};
|
};
|
||||||
|
|
||||||
let quick_actions_button: Element<'a, Message> = button(
|
let quick_actions_button: Element<'a, Message> = keyboard::button(
|
||||||
text(t(locale, "editor.quickActions"))
|
text(t(locale, "editor.quickActions"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -569,7 +571,7 @@ pub fn view<'a>(
|
|||||||
let mut header_action_items: Vec<Element<'a, Message>> = vec![
|
let mut header_action_items: Vec<Element<'a, Message>> = vec![
|
||||||
status_badge(locale, &state.status),
|
status_badge(locale, &state.status),
|
||||||
quick_actions,
|
quick_actions,
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "common.save"))
|
text(t(locale, "common.save"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -581,7 +583,7 @@ pub fn view<'a>(
|
|||||||
];
|
];
|
||||||
if state.status == PostStatus::Draft {
|
if state.status == PostStatus::Draft {
|
||||||
header_action_items.push(
|
header_action_items.push(
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "editor.publish"))
|
text(t(locale, "editor.publish"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -594,7 +596,7 @@ pub fn view<'a>(
|
|||||||
}
|
}
|
||||||
if !on_translation && state.status == PostStatus::Draft && state.published_at.is_some() {
|
if !on_translation && state.status == PostStatus::Draft && state.published_at.is_some() {
|
||||||
header_action_items.push(
|
header_action_items.push(
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "editor.discard"))
|
text(t(locale, "editor.discard"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -606,7 +608,7 @@ pub fn view<'a>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
header_action_items.push(
|
header_action_items.push(
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "modal.confirmDelete.delete"))
|
text(t(locale, "modal.confirmDelete.delete"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -643,13 +645,14 @@ pub fn view<'a>(
|
|||||||
} else {
|
} else {
|
||||||
format!("\u{25B6} {}", t(locale, "editor.metadata"))
|
format!("\u{25B6} {}", t(locale, "editor.metadata"))
|
||||||
};
|
};
|
||||||
let meta_toggle = button(
|
let meta_toggle = keyboard::button(
|
||||||
text(meta_toggle_label)
|
text(meta_toggle_label)
|
||||||
.size(12)
|
.size(12)
|
||||||
.color(inputs::SECTION_COLOR)
|
.color(inputs::SECTION_COLOR)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
)
|
)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::ToggleMetadata))
|
.on_press(Message::PostEditor(PostEditorMsg::ToggleMetadata))
|
||||||
|
.hotkey('m')
|
||||||
.padding([8, 10])
|
.padding([8, 10])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.style(inputs::disclosure_button);
|
.style(inputs::disclosure_button);
|
||||||
@@ -663,7 +666,7 @@ pub fn view<'a>(
|
|||||||
for flag in &flags {
|
for flag in &flags {
|
||||||
let lang = flag.language.clone();
|
let lang = flag.language.clone();
|
||||||
let label = flag.flag_emoji.to_string();
|
let label = flag.flag_emoji.to_string();
|
||||||
let btn = button(text(label).size(14).shaping(Shaping::Advanced))
|
let btn = keyboard::button(text(label).size(14).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::SwitchLanguage(lang)))
|
.on_press(Message::PostEditor(PostEditorMsg::SwitchLanguage(lang)))
|
||||||
.padding([2, 4])
|
.padding([2, 4])
|
||||||
.style(if flag.is_active {
|
.style(if flag.is_active {
|
||||||
@@ -756,7 +759,7 @@ pub fn view<'a>(
|
|||||||
.align_y(iced::Alignment::Center);
|
.align_y(iced::Alignment::Center);
|
||||||
for tag in semantic_suggestions {
|
for tag in semantic_suggestions {
|
||||||
chips = chips.push(
|
chips = chips.push(
|
||||||
button(text(format!("+ {tag}")).size(11))
|
keyboard::button(text(format!("+ {tag}")).size(11))
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(
|
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(
|
||||||
tag.to_string(),
|
tag.to_string(),
|
||||||
)))
|
)))
|
||||||
@@ -784,7 +787,7 @@ pub fn view<'a>(
|
|||||||
.align_y(iced::Alignment::Center);
|
.align_y(iced::Alignment::Center);
|
||||||
for tag in matching_suggestions {
|
for tag in matching_suggestions {
|
||||||
chips = chips.push(
|
chips = chips.push(
|
||||||
button(text(tag).size(11))
|
keyboard::button(text(tag).size(11))
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(
|
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(
|
||||||
tag.to_string(),
|
tag.to_string(),
|
||||||
)))
|
)))
|
||||||
@@ -795,7 +798,9 @@ pub fn view<'a>(
|
|||||||
if query_addable {
|
if query_addable {
|
||||||
let query = state.tags_input.trim().to_string();
|
let query = state.tags_input.trim().to_string();
|
||||||
chips = chips.push(
|
chips = chips.push(
|
||||||
button(text(tw(locale, "editor.createTag", &[("name", &query)])).size(11))
|
keyboard::button(
|
||||||
|
text(tw(locale, "editor.createTag", &[("name", &query)])).size(11),
|
||||||
|
)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(query)))
|
.on_press(Message::PostEditor(PostEditorMsg::AddSuggestedTag(query)))
|
||||||
.padding([4, 8])
|
.padding([4, 8])
|
||||||
.style(inputs::secondary_button),
|
.style(inputs::secondary_button),
|
||||||
@@ -860,7 +865,7 @@ pub fn view<'a>(
|
|||||||
Column::with_children(items).spacing(2).into()
|
Column::with_children(items).spacing(2).into()
|
||||||
};
|
};
|
||||||
|
|
||||||
let link_existing_button: Element<'a, Message> = button(
|
let link_existing_button: Element<'a, Message> = keyboard::button(
|
||||||
text(t(locale, "editor.linkExistingMedia"))
|
text(t(locale, "editor.linkExistingMedia"))
|
||||||
.size(11)
|
.size(11)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -916,14 +921,14 @@ pub fn view<'a>(
|
|||||||
]
|
]
|
||||||
.spacing(2)
|
.spacing(2)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "common.open"))
|
text(t(locale, "common.open"))
|
||||||
.size(11)
|
.size(11)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
)
|
)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::OpenLinkedMedia(open_id)))
|
.on_press(Message::PostEditor(PostEditorMsg::OpenLinkedMedia(open_id)))
|
||||||
.padding([4, 10]),
|
.padding([4, 10]),
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "editor.unlinkMedia"))
|
text(t(locale, "editor.unlinkMedia"))
|
||||||
.size(11)
|
.size(11)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -981,13 +986,14 @@ pub fn view<'a>(
|
|||||||
} else {
|
} else {
|
||||||
format!("\u{25B6} {}", t(locale, "editor.excerpt"))
|
format!("\u{25B6} {}", t(locale, "editor.excerpt"))
|
||||||
};
|
};
|
||||||
let excerpt_toggle = button(
|
let excerpt_toggle = keyboard::button(
|
||||||
text(excerpt_toggle_label)
|
text(excerpt_toggle_label)
|
||||||
.size(12)
|
.size(12)
|
||||||
.color(inputs::SECTION_COLOR)
|
.color(inputs::SECTION_COLOR)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
)
|
)
|
||||||
.on_press(Message::PostEditor(PostEditorMsg::ToggleExcerpt))
|
.on_press(Message::PostEditor(PostEditorMsg::ToggleExcerpt))
|
||||||
|
.hotkey('x')
|
||||||
.padding([8, 10])
|
.padding([8, 10])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.style(inputs::disclosure_button);
|
.style(inputs::disclosure_button);
|
||||||
@@ -1036,7 +1042,7 @@ pub fn view<'a>(
|
|||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
if show_content_actions {
|
if show_content_actions {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "editor.insertLink"))
|
text(t(locale, "editor.insertLink"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -1049,7 +1055,7 @@ pub fn view<'a>(
|
|||||||
Space::new(0, 0).into()
|
Space::new(0, 0).into()
|
||||||
},
|
},
|
||||||
if show_content_actions {
|
if show_content_actions {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "editor.insertMedia"))
|
text(t(locale, "editor.insertMedia"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -1062,7 +1068,7 @@ pub fn view<'a>(
|
|||||||
Space::new(0, 0).into()
|
Space::new(0, 0).into()
|
||||||
},
|
},
|
||||||
if show_content_actions {
|
if show_content_actions {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "editor.gallery"))
|
text(t(locale, "editor.gallery"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -1209,7 +1215,7 @@ fn chip_input_field<'a>(
|
|||||||
.map(|chip| {
|
.map(|chip| {
|
||||||
let label = format!("{} \u{2715}", chip);
|
let label = format!("{} \u{2715}", chip);
|
||||||
let chip_val = chip.clone();
|
let chip_val = chip.clone();
|
||||||
button(text(label).size(11).shaping(Shaping::Advanced))
|
keyboard::button(text(label).size(11).shaping(Shaping::Advanced))
|
||||||
.on_press(on_remove(chip_val))
|
.on_press(on_remove(chip_val))
|
||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
.style(chip_button_style)
|
.style(chip_button_style)
|
||||||
@@ -1266,7 +1272,7 @@ fn mode_button<'a>(
|
|||||||
"preview" => t(locale, "editor.modePreview"),
|
"preview" => t(locale, "editor.modePreview"),
|
||||||
_ => t(locale, "editor.modeMarkdown"),
|
_ => t(locale, "editor.modeMarkdown"),
|
||||||
};
|
};
|
||||||
button(text(label).size(12).shaping(Shaping::Advanced))
|
keyboard::button(text(label).size(12).shaping(Shaping::Advanced))
|
||||||
.on_press(message)
|
.on_press(message)
|
||||||
.padding([4, 10])
|
.padding([4, 10])
|
||||||
.style(if active_mode == mode {
|
.style(if active_mode == mode {
|
||||||
@@ -1284,7 +1290,7 @@ fn quick_action_item<'a>(
|
|||||||
enabled: bool,
|
enabled: bool,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let _ = locale;
|
let _ = locale;
|
||||||
button(text(label).size(12).shaping(Shaping::Advanced))
|
keyboard::button(text(label).size(12).shaping(Shaping::Advanced))
|
||||||
.on_press_maybe(enabled.then_some(Message::PostEditor(msg)))
|
.on_press_maybe(enabled.then_some(Message::PostEditor(msg)))
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(status_bar::dropdown_item)
|
.style(status_bar::dropdown_item)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Column, Space, button, container, row, svg, text};
|
use iced::widget::{Column, Space, container, row, svg, text};
|
||||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -148,7 +150,7 @@ pub fn view(
|
|||||||
};
|
};
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
button(label)
|
keyboard::button(label)
|
||||||
.on_press(Message::SwitchProject(id))
|
.on_press(Message::SwitchProject(id))
|
||||||
.padding([4, 8])
|
.padding([4, 8])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
@@ -170,7 +172,7 @@ pub fn view(
|
|||||||
|
|
||||||
// Open and create project actions
|
// Open and create project actions
|
||||||
items.push(
|
items.push(
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text("↗")
|
text("↗")
|
||||||
.size(14)
|
.size(14)
|
||||||
@@ -190,7 +192,7 @@ pub fn view(
|
|||||||
);
|
);
|
||||||
|
|
||||||
items.push(
|
items.push(
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text("+")
|
text("+")
|
||||||
.size(14)
|
.size(14)
|
||||||
@@ -256,7 +258,7 @@ pub fn trigger_button(project_name: &str) -> Element<'static, Message> {
|
|||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
.color(Color::from_rgb(0.55, 0.55, 0.60));
|
.color(Color::from_rgb(0.55, 0.55, 0.60));
|
||||||
|
|
||||||
button(
|
keyboard::button(
|
||||||
row![folder_icon, name, chevron]
|
row![folder_icon, name, chevron]
|
||||||
.spacing(4)
|
.spacing(4)
|
||||||
.align_y(iced::Alignment::Center),
|
.align_y(iced::Alignment::Center),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||||
use iced::{Color, Element, Length, Theme};
|
use iced::{Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -125,22 +126,22 @@ pub fn view<'a>(state: &'a ScriptEditorState, locale: UiLocale) -> Element<'a, M
|
|||||||
status_badge(&state.status),
|
status_badge(&state.status),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
button(text(t(locale, "common.save")).size(13))
|
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::Save))
|
.on_press(Message::ScriptEditor(ScriptEditorMsg::Save))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "editor.run")).size(13))
|
keyboard::button(text(t(locale, "editor.run")).size(13))
|
||||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::Run))
|
.on_press(Message::ScriptEditor(ScriptEditorMsg::Run))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "editor.checkSyntax")).size(13))
|
keyboard::button(text(t(locale, "editor.checkSyntax")).size(13))
|
||||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::CheckSyntax))
|
.on_press(Message::ScriptEditor(ScriptEditorMsg::CheckSyntax))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||||
.on_press(Message::ScriptEditor(ScriptEditorMsg::Delete))
|
.on_press(Message::ScriptEditor(ScriptEditorMsg::Delete))
|
||||||
.style(inputs::danger_button)
|
.style(inputs::danger_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{button, column, container, row, scrollable, text, text_editor, text_input};
|
use iced::widget::{column, container, row, scrollable, text, text_editor, text_input};
|
||||||
use iced::{Alignment, Color, Element, Length};
|
use iced::{Alignment, Color, Element, Length};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
@@ -49,6 +50,34 @@ pub enum SettingsSection {
|
|||||||
MCP,
|
MCP,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SettingsSection {
|
||||||
|
pub fn access_key(&self) -> char {
|
||||||
|
match self {
|
||||||
|
Self::Project => 'p',
|
||||||
|
Self::Editor => 'r',
|
||||||
|
Self::AI => 'a',
|
||||||
|
Self::Technology => 't',
|
||||||
|
Self::Publishing => 'u',
|
||||||
|
Self::Data => 'd',
|
||||||
|
Self::MCP => 'c',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_access_key(key: char) -> Option<Self> {
|
||||||
|
[
|
||||||
|
Self::Project,
|
||||||
|
Self::Editor,
|
||||||
|
Self::AI,
|
||||||
|
Self::Technology,
|
||||||
|
Self::Publishing,
|
||||||
|
Self::Data,
|
||||||
|
Self::MCP,
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.find(|section| section.access_key() == key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct SettingsCategoryRow {
|
pub struct SettingsCategoryRow {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@@ -261,6 +290,13 @@ impl Default for SettingsViewState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SettingsViewState {
|
impl SettingsViewState {
|
||||||
|
pub fn section_is_visible(&self, section: &SettingsSection, locale: UiLocale) -> bool {
|
||||||
|
self.search_query.is_empty()
|
||||||
|
|| t(locale, section.i18n_key())
|
||||||
|
.to_lowercase()
|
||||||
|
.contains(&self.search_query.to_lowercase())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn focus_section(&mut self, section: SettingsSection) {
|
pub fn focus_section(&mut self, section: SettingsSection) {
|
||||||
self.collapsed = SettingsSection::all()
|
self.collapsed = SettingsSection::all()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -369,12 +405,10 @@ pub fn view<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a, M
|
|||||||
.padding([8, 10])
|
.padding([8, 10])
|
||||||
.style(inputs::field_style);
|
.style(inputs::field_style);
|
||||||
|
|
||||||
let query_lower = state.search_query.to_lowercase();
|
|
||||||
|
|
||||||
let mut section_items = Vec::new();
|
let mut section_items = Vec::new();
|
||||||
for section in state.ordered_sections() {
|
for section in state.ordered_sections() {
|
||||||
let label = t(locale, section.i18n_key());
|
let label = t(locale, section.i18n_key());
|
||||||
if !query_lower.is_empty() && !label.to_lowercase().contains(&query_lower) {
|
if !state.section_is_visible(§ion, locale) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let collapsed = state.collapsed.contains(§ion);
|
let collapsed = state.collapsed.contains(§ion);
|
||||||
@@ -387,7 +421,7 @@ pub fn view<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a, M
|
|||||||
text(t(locale, "common.noResults"))
|
text(t(locale, "common.noResults"))
|
||||||
.size(14)
|
.size(14)
|
||||||
.color(Color::from_rgb(0.7, 0.72, 0.78)),
|
.color(Color::from_rgb(0.7, 0.72, 0.78)),
|
||||||
button(text(t(locale, "common.clear")).size(13))
|
keyboard::button(text(t(locale, "common.clear")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::SearchChanged(String::new())))
|
.on_press(Message::Settings(SettingsMsg::SearchChanged(String::new())))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 12]),
|
.padding([6, 12]),
|
||||||
@@ -423,7 +457,7 @@ fn render_section<'a>(
|
|||||||
locale: UiLocale,
|
locale: UiLocale,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
let toggle_char = if collapsed { "\u{25B6}" } else { "\u{25BC}" };
|
let toggle_char = if collapsed { "\u{25B6}" } else { "\u{25BC}" };
|
||||||
let header = button(
|
let header = keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(toggle_char).size(12),
|
text(toggle_char).size(12),
|
||||||
text(label.to_string()).size(14).color(Color::WHITE),
|
text(label.to_string()).size(14).color(Color::WHITE),
|
||||||
@@ -434,6 +468,7 @@ fn render_section<'a>(
|
|||||||
.on_press(Message::Settings(SettingsMsg::ToggleSection(
|
.on_press(Message::Settings(SettingsMsg::ToggleSection(
|
||||||
section.clone(),
|
section.clone(),
|
||||||
)))
|
)))
|
||||||
|
.hotkey(section.access_key())
|
||||||
.padding([6, 8])
|
.padding([6, 8])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.style(inputs::disclosure_button);
|
.style(inputs::disclosure_button);
|
||||||
@@ -478,11 +513,11 @@ fn section_project<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
inputs::labeled_input(&t(locale, "settings.dataPath"), "", &state.data_path, |s| {
|
inputs::labeled_input(&t(locale, "settings.dataPath"), "", &state.data_path, |s| {
|
||||||
Message::Settings(SettingsMsg::DataPathChanged(s))
|
Message::Settings(SettingsMsg::DataPathChanged(s))
|
||||||
},),
|
},),
|
||||||
button(text(t(locale, "settings.browse")).size(12))
|
keyboard::button(text(t(locale, "settings.browse")).size(12))
|
||||||
.on_press(Message::Settings(SettingsMsg::BrowseDataPath))
|
.on_press(Message::Settings(SettingsMsg::BrowseDataPath))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 12]),
|
.padding([6, 12]),
|
||||||
button(text(t(locale, "settings.reset")).size(12))
|
keyboard::button(text(t(locale, "settings.reset")).size(12))
|
||||||
.on_press(Message::Settings(SettingsMsg::ResetDataPath))
|
.on_press(Message::Settings(SettingsMsg::ResetDataPath))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 12]),
|
.padding([6, 12]),
|
||||||
@@ -559,11 +594,11 @@ fn section_project<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
|row| Message::Settings(SettingsMsg::BlogmarkCategoryChanged(row.name)),
|
|row| Message::Settings(SettingsMsg::BlogmarkCategoryChanged(row.name)),
|
||||||
);
|
);
|
||||||
let copy_blogmark_bookmarklet =
|
let copy_blogmark_bookmarklet =
|
||||||
button(text(t(locale, "settings.copyBlogmarkBookmarklet")).size(13))
|
keyboard::button(text(t(locale, "settings.copyBlogmarkBookmarklet")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::CopyBlogmarkBookmarklet))
|
.on_press(Message::Settings(SettingsMsg::CopyBlogmarkBookmarklet))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
let save = button(text(t(locale, "common.save")).size(13))
|
let save = keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::SaveProject))
|
.on_press(Message::Settings(SettingsMsg::SaveProject))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
@@ -619,7 +654,7 @@ fn section_editor<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element
|
|||||||
state.hide_unchanged_regions,
|
state.hide_unchanged_regions,
|
||||||
|b| Message::Settings(SettingsMsg::HideUnchangedRegionsChanged(b)),
|
|b| Message::Settings(SettingsMsg::HideUnchangedRegionsChanged(b)),
|
||||||
);
|
);
|
||||||
let save = button(text(t(locale, "common.save")).size(13))
|
let save = keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::SaveEditor))
|
.on_press(Message::Settings(SettingsMsg::SaveEditor))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
@@ -656,11 +691,11 @@ fn section_ai<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a,
|
|||||||
]
|
]
|
||||||
.spacing(4);
|
.spacing(4);
|
||||||
let btns = row![
|
let btns = row![
|
||||||
button(text(t(locale, "common.save")).size(13))
|
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::SaveAi))
|
.on_press(Message::Settings(SettingsMsg::SaveAi))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
button(text(t(locale, "settings.resetToDefault")).size(13))
|
keyboard::button(text(t(locale, "settings.resetToDefault")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::ResetSystemPrompt))
|
.on_press(Message::Settings(SettingsMsg::ResetSystemPrompt))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
@@ -727,7 +762,7 @@ fn ai_mode_block<'a>(
|
|||||||
Message::Settings(SettingsMsg::AiEndpointUrlChanged(kind, value))
|
Message::Settings(SettingsMsg::AiEndpointUrlChanged(kind, value))
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
button(text(t(locale, "settings.refreshModels")).size(13))
|
keyboard::button(text(t(locale, "settings.refreshModels")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RefreshAiModels(kind)))
|
.on_press(Message::Settings(SettingsMsg::RefreshAiModels(kind)))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
@@ -772,7 +807,7 @@ fn ai_mode_block<'a>(
|
|||||||
state.image_supports_vision,
|
state.image_supports_vision,
|
||||||
move |value| Message::Settings(SettingsMsg::AiVisionChanged(kind, value)),
|
move |value| Message::Settings(SettingsMsg::AiVisionChanged(kind, value)),
|
||||||
),
|
),
|
||||||
button(text(t(locale, "settings.testChat")).size(13))
|
keyboard::button(text(t(locale, "settings.testChat")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::TestAi(kind)))
|
.on_press(Message::Settings(SettingsMsg::TestAi(kind)))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
@@ -815,11 +850,11 @@ fn section_publishing<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Ele
|
|||||||
|s| Message::Settings(SettingsMsg::SshRemotePathChanged(s)),
|
|s| Message::Settings(SettingsMsg::SshRemotePathChanged(s)),
|
||||||
);
|
);
|
||||||
let btns = row![
|
let btns = row![
|
||||||
button(text(t(locale, "common.save")).size(13))
|
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::SavePublishing))
|
.on_press(Message::Settings(SettingsMsg::SavePublishing))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
button(text(t(locale, "settings.clear")).size(13))
|
keyboard::button(text(t(locale, "settings.clear")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::ClearPublishing))
|
.on_press(Message::Settings(SettingsMsg::ClearPublishing))
|
||||||
.style(inputs::danger_button)
|
.style(inputs::danger_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
@@ -834,37 +869,37 @@ fn section_publishing<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Ele
|
|||||||
|
|
||||||
fn section_data<'a>(locale: UiLocale) -> Element<'a, Message> {
|
fn section_data<'a>(locale: UiLocale) -> Element<'a, Message> {
|
||||||
let rebuild_btns = column![
|
let rebuild_btns = column![
|
||||||
button(text(t(locale, "settings.rebuildPosts")).size(13))
|
keyboard::button(text(t(locale, "settings.rebuildPosts")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RebuildPosts))
|
.on_press(Message::Settings(SettingsMsg::RebuildPosts))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.rebuildMedia")).size(13))
|
keyboard::button(text(t(locale, "settings.rebuildMedia")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RebuildMedia))
|
.on_press(Message::Settings(SettingsMsg::RebuildMedia))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.rebuildScripts")).size(13))
|
keyboard::button(text(t(locale, "settings.rebuildScripts")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RebuildScripts))
|
.on_press(Message::Settings(SettingsMsg::RebuildScripts))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.rebuildTemplates")).size(13))
|
keyboard::button(text(t(locale, "settings.rebuildTemplates")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RebuildTemplates))
|
.on_press(Message::Settings(SettingsMsg::RebuildTemplates))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.rebuildLinks")).size(13))
|
keyboard::button(text(t(locale, "settings.rebuildLinks")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RebuildLinks))
|
.on_press(Message::Settings(SettingsMsg::RebuildLinks))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.rebuildSearchIndex")).size(13))
|
keyboard::button(text(t(locale, "settings.rebuildSearchIndex")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RebuildSearchIndex))
|
.on_press(Message::Settings(SettingsMsg::RebuildSearchIndex))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.regenerateThumbnails")).size(13))
|
keyboard::button(text(t(locale, "settings.regenerateThumbnails")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::RegenerateThumbnails))
|
.on_press(Message::Settings(SettingsMsg::RegenerateThumbnails))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
@@ -872,12 +907,12 @@ fn section_data<'a>(locale: UiLocale) -> Element<'a, Message> {
|
|||||||
]
|
]
|
||||||
.spacing(4);
|
.spacing(4);
|
||||||
|
|
||||||
let open = button(text(t(locale, "settings.openDataFolder")).size(13))
|
let open = keyboard::button(text(t(locale, "settings.openDataFolder")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::OpenDataFolder))
|
.on_press(Message::Settings(SettingsMsg::OpenDataFolder))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
|
|
||||||
let install_cli = button(text(t(locale, "settings.installCli")).size(13))
|
let install_cli = keyboard::button(text(t(locale, "settings.installCli")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::InstallCli))
|
.on_press(Message::Settings(SettingsMsg::InstallCli))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
@@ -900,14 +935,17 @@ fn section_mcp<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a
|
|||||||
inputs::LABEL_COLOR
|
inputs::LABEL_COLOR
|
||||||
};
|
};
|
||||||
let server = column![
|
let server = column![
|
||||||
|
keyboard::focusable(
|
||||||
iced::widget::checkbox(t(locale, "settings.mcpEnable"), state.mcp_enabled)
|
iced::widget::checkbox(t(locale, "settings.mcpEnable"), state.mcp_enabled)
|
||||||
.on_toggle(|value| Message::Settings(SettingsMsg::McpEnabledChanged(value))),
|
.on_toggle(|value| Message::Settings(SettingsMsg::McpEnabledChanged(value))),
|
||||||
|
true,
|
||||||
|
),
|
||||||
row![
|
row![
|
||||||
text(status).size(13).color(status_color),
|
text(status).size(13).color(status_color),
|
||||||
text(state.mcp_endpoint.clone())
|
text(state.mcp_endpoint.clone())
|
||||||
.size(12)
|
.size(12)
|
||||||
.color(inputs::LABEL_COLOR),
|
.color(inputs::LABEL_COLOR),
|
||||||
button(text(t(locale, "settings.mcpRefresh")).size(12))
|
keyboard::button(text(t(locale, "settings.mcpRefresh")).size(12))
|
||||||
.on_press(Message::Settings(SettingsMsg::McpRefresh))
|
.on_press(Message::Settings(SettingsMsg::McpRefresh))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([5, 10]),
|
.padding([5, 10]),
|
||||||
@@ -940,13 +978,13 @@ fn section_mcp<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a
|
|||||||
]
|
]
|
||||||
.spacing(2)
|
.spacing(2)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
button(text(t(locale, "settings.mcpApprove")).size(12))
|
keyboard::button(text(t(locale, "settings.mcpApprove")).size(12))
|
||||||
.on_press(Message::Settings(SettingsMsg::McpProposalAccepted(
|
.on_press(Message::Settings(SettingsMsg::McpProposalAccepted(
|
||||||
proposal.id.clone()
|
proposal.id.clone()
|
||||||
)))
|
)))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([5, 10]),
|
.padding([5, 10]),
|
||||||
button(text(t(locale, "settings.mcpReject")).size(12))
|
keyboard::button(text(t(locale, "settings.mcpReject")).size(12))
|
||||||
.on_press(Message::Settings(SettingsMsg::McpProposalRejected(
|
.on_press(Message::Settings(SettingsMsg::McpProposalRejected(
|
||||||
proposal.id.clone()
|
proposal.id.clone()
|
||||||
)))
|
)))
|
||||||
@@ -964,10 +1002,13 @@ fn section_mcp<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Element<'a
|
|||||||
let agents = column(state.mcp_agents.iter().map(|agent| {
|
let agents = column(state.mcp_agents.iter().map(|agent| {
|
||||||
let configured = agent.configured;
|
let configured = agent.configured;
|
||||||
column![
|
column![
|
||||||
|
keyboard::focusable(
|
||||||
iced::widget::checkbox(agent.label.clone(), configured).on_toggle({
|
iced::widget::checkbox(agent.label.clone(), configured).on_toggle({
|
||||||
let agent = agent.agent;
|
let agent = agent.agent;
|
||||||
move |_| Message::Settings(SettingsMsg::McpAgentToggled(agent))
|
move |_| Message::Settings(SettingsMsg::McpAgentToggled(agent))
|
||||||
}),
|
}),
|
||||||
|
true,
|
||||||
|
),
|
||||||
text(agent.config_path.clone())
|
text(agent.config_path.clone())
|
||||||
.size(11)
|
.size(11)
|
||||||
.color(inputs::LABEL_COLOR),
|
.color(inputs::LABEL_COLOR),
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, column, container, image, row, scrollable, text, text_input};
|
use iced::widget::{Space, column, container, image, row, scrollable, text, text_input};
|
||||||
use iced::{Background, Border, Color, Element, Length, Theme};
|
use iced::{Background, Border, Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -301,10 +303,10 @@ fn row_delete_style(_theme: &Theme, status: button::Status) -> button::Style {
|
|||||||
/// Per sidebar_views.allium *ListItemEntry RowLayout: right-aligned, visible
|
/// Per sidebar_views.allium *ListItemEntry RowLayout: right-aligned, visible
|
||||||
/// only on row hover, routed to the row's delete message.
|
/// only on row hover, routed to the row's delete message.
|
||||||
fn with_row_delete(
|
fn with_row_delete(
|
||||||
open_button: iced::widget::Button<'static, Message>,
|
open_button: impl Into<Element<'static, Message>>,
|
||||||
on_delete: Message,
|
on_delete: Message,
|
||||||
) -> Element<'static, Message> {
|
) -> Element<'static, Message> {
|
||||||
let delete_button = button(
|
let delete_button = keyboard::button(
|
||||||
text("\u{2715}") // ✕
|
text("\u{2715}") // ✕
|
||||||
.size(11)
|
.size(11)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -313,7 +315,7 @@ fn with_row_delete(
|
|||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
.style(row_delete_style);
|
.style(row_delete_style);
|
||||||
iced::widget::hover(
|
iced::widget::hover(
|
||||||
open_button,
|
open_button.into(),
|
||||||
container(delete_button)
|
container(delete_button)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Fill)
|
.height(Length::Fill)
|
||||||
@@ -362,7 +364,7 @@ fn calendar_widget(
|
|||||||
} else {
|
} else {
|
||||||
calendar_style
|
calendar_style
|
||||||
};
|
};
|
||||||
let year_btn = button(text(label).size(11).shaping(Shaping::Advanced))
|
let year_btn = keyboard::button(text(label).size(11).shaping(Shaping::Advanced))
|
||||||
.on_press(if year_selected {
|
.on_press(if year_selected {
|
||||||
on_year_clone(None)
|
on_year_clone(None)
|
||||||
} else {
|
} else {
|
||||||
@@ -384,7 +386,7 @@ fn calendar_widget(
|
|||||||
} else {
|
} else {
|
||||||
calendar_style
|
calendar_style
|
||||||
};
|
};
|
||||||
let month_btn = button(text(label).size(10).shaping(Shaping::Advanced))
|
let month_btn = keyboard::button(text(label).size(10).shaping(Shaping::Advanced))
|
||||||
.on_press(if month_selected {
|
.on_press(if month_selected {
|
||||||
on_month_clone(None)
|
on_month_clone(None)
|
||||||
} else {
|
} else {
|
||||||
@@ -428,7 +430,7 @@ fn chip_selector(
|
|||||||
} else {
|
} else {
|
||||||
chip_style
|
chip_style
|
||||||
};
|
};
|
||||||
let chip = button(text(tag.clone()).size(10).shaping(Shaping::Advanced))
|
let chip = keyboard::button(text(tag.clone()).size(10).shaping(Shaping::Advanced))
|
||||||
.on_press(on_toggle_clone(tag_clone))
|
.on_press(on_toggle_clone(tag_clone))
|
||||||
.padding([2, 6])
|
.padding([2, 6])
|
||||||
.style(style_fn);
|
.style(style_fn);
|
||||||
@@ -476,7 +478,7 @@ fn single_select_chip_selector(
|
|||||||
};
|
};
|
||||||
let value = value.clone();
|
let value = value.clone();
|
||||||
let on_toggle = on_toggle.clone();
|
let on_toggle = on_toggle.clone();
|
||||||
button(text(display.clone()).size(10).shaping(Shaping::Advanced))
|
keyboard::button(text(display.clone()).size(10).shaping(Shaping::Advanced))
|
||||||
.on_press(if is_selected {
|
.on_press(if is_selected {
|
||||||
on_toggle(None)
|
on_toggle(None)
|
||||||
} else {
|
} else {
|
||||||
@@ -599,7 +601,7 @@ fn post_filter_panel(
|
|||||||
// Clear all filters button
|
// Clear all filters button
|
||||||
if filter.has_active_filters() {
|
if filter.has_active_filters() {
|
||||||
sections.push(
|
sections.push(
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "sidebar.filter.clearAll"))
|
text(t(locale, "sidebar.filter.clearAll"))
|
||||||
.size(10)
|
.size(10)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -645,7 +647,7 @@ fn media_filter_panel(filter: &MediaFilter, locale: UiLocale) -> Element<'static
|
|||||||
|
|
||||||
if filter.has_active_filters() {
|
if filter.has_active_filters() {
|
||||||
sections.push(
|
sections.push(
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "sidebar.filter.clearAll"))
|
text(t(locale, "sidebar.filter.clearAll"))
|
||||||
.size(10)
|
.size(10)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -711,7 +713,7 @@ pub fn view(
|
|||||||
row![
|
row![
|
||||||
header,
|
header,
|
||||||
Space::with_width(Length::Fill),
|
Space::with_width(Length::Fill),
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "common.add"))
|
text(t(locale, "common.add"))
|
||||||
.size(11)
|
.size(11)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
@@ -752,7 +754,8 @@ pub fn view(
|
|||||||
} else {
|
} else {
|
||||||
"\u{25BC}" // ▼ toggle icon
|
"\u{25BC}" // ▼ toggle icon
|
||||||
};
|
};
|
||||||
let filter_toggle = button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
let filter_toggle =
|
||||||
|
keyboard::button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::TogglePostFilterPanel)
|
.on_press(Message::TogglePostFilterPanel)
|
||||||
.padding([4, 6])
|
.padding([4, 6])
|
||||||
.style(toggle_style);
|
.style(toggle_style);
|
||||||
@@ -817,7 +820,7 @@ pub fn view(
|
|||||||
} else {
|
} else {
|
||||||
item_style
|
item_style
|
||||||
};
|
};
|
||||||
button(
|
keyboard::button(
|
||||||
container(column![label_text, date_text].spacing(1))
|
container(column![label_text, date_text].spacing(1))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.clip(true),
|
.clip(true),
|
||||||
@@ -901,7 +904,8 @@ pub fn view(
|
|||||||
} else {
|
} else {
|
||||||
"\u{25BC}" // ▼ toggle icon
|
"\u{25BC}" // ▼ toggle icon
|
||||||
};
|
};
|
||||||
let filter_toggle = button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
let filter_toggle =
|
||||||
|
keyboard::button(text(toggle_label).size(11).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::ToggleMediaFilterPanel)
|
.on_press(Message::ToggleMediaFilterPanel)
|
||||||
.padding([4, 6])
|
.padding([4, 6])
|
||||||
.style(toggle_style);
|
.style(toggle_style);
|
||||||
@@ -995,7 +999,7 @@ pub fn view(
|
|||||||
.spacing(8)
|
.spacing(8)
|
||||||
.align_y(iced::Alignment::Center);
|
.align_y(iced::Alignment::Center);
|
||||||
|
|
||||||
button(container(content).width(Length::Fill).clip(true))
|
keyboard::button(container(content).width(Length::Fill).clip(true))
|
||||||
.on_press(Message::OpenTab(Tab {
|
.on_press(Message::OpenTab(Tab {
|
||||||
id: m.id.clone(),
|
id: m.id.clone(),
|
||||||
tab_type: TabType::Media,
|
tab_type: TabType::Media,
|
||||||
@@ -1046,7 +1050,7 @@ pub fn view(
|
|||||||
} else {
|
} else {
|
||||||
item_style
|
item_style
|
||||||
};
|
};
|
||||||
let open_button = button(
|
let open_button = keyboard::button(
|
||||||
container(column![label_text, date_text].spacing(1))
|
container(column![label_text, date_text].spacing(1))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.clip(true),
|
.clip(true),
|
||||||
@@ -1097,7 +1101,7 @@ pub fn view(
|
|||||||
} else {
|
} else {
|
||||||
item_style
|
item_style
|
||||||
};
|
};
|
||||||
let open_button = button(
|
let open_button = keyboard::button(
|
||||||
container(column![label_text, date_text].spacing(1))
|
container(column![label_text, date_text].spacing(1))
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.clip(true),
|
.clip(true),
|
||||||
@@ -1145,7 +1149,7 @@ pub fn view(
|
|||||||
.size(10)
|
.size(10)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
.color(muted);
|
.color(muted);
|
||||||
let open_button = button(
|
let open_button = keyboard::button(
|
||||||
container(
|
container(
|
||||||
column![
|
column![
|
||||||
text(definition_name).size(12).shaping(Shaping::Advanced),
|
text(definition_name).size(12).shaping(Shaping::Advanced),
|
||||||
@@ -1197,7 +1201,7 @@ pub fn view(
|
|||||||
.size(10)
|
.size(10)
|
||||||
.shaping(Shaping::Advanced)
|
.shaping(Shaping::Advanced)
|
||||||
.color(muted);
|
.color(muted);
|
||||||
let open_button = button(
|
let open_button = keyboard::button(
|
||||||
container(
|
container(
|
||||||
column![
|
column![
|
||||||
text(conversation.title.clone())
|
text(conversation.title.clone())
|
||||||
@@ -1257,7 +1261,7 @@ pub fn view(
|
|||||||
is_dirty: false,
|
is_dirty: false,
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
button(container(label_text).width(Length::Fill))
|
keyboard::button(container(label_text).width(Length::Fill))
|
||||||
.on_press(msg)
|
.on_press(msg)
|
||||||
.padding([5, 8])
|
.padding([5, 8])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
@@ -1291,7 +1295,7 @@ pub fn view(
|
|||||||
.map(|(key, section)| {
|
.map(|(key, section)| {
|
||||||
let label = t(locale, key);
|
let label = t(locale, key);
|
||||||
let label_text = text(label).size(12).shaping(Shaping::Advanced);
|
let label_text = text(label).size(12).shaping(Shaping::Advanced);
|
||||||
button(container(label_text).width(Length::Fill))
|
keyboard::button(container(label_text).width(Length::Fill))
|
||||||
.on_press(Message::OpenTagsSection(*section))
|
.on_press(Message::OpenTagsSection(*section))
|
||||||
.padding([5, 8])
|
.padding([5, 8])
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{button, column, container, row, scrollable, text};
|
use iced::widget::{column, container, row, scrollable, text};
|
||||||
use iced::{Background, Color, Element, Length, Theme};
|
use iced::{Background, Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -21,7 +22,7 @@ pub struct SiteValidationState {
|
|||||||
|
|
||||||
pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a, Message> {
|
pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a, Message> {
|
||||||
let run_button = if state.is_running {
|
let run_button = if state.is_running {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "siteValidation.running"))
|
text(t(locale, "siteValidation.running"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -29,12 +30,13 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
|||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
} else {
|
} else {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "siteValidation.run"))
|
text(t(locale, "siteValidation.run"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
)
|
)
|
||||||
.on_press(Message::RunSiteValidation)
|
.on_press(Message::RunSiteValidation)
|
||||||
|
.hotkey('r')
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
};
|
};
|
||||||
@@ -42,7 +44,7 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
|||||||
|| !state.extra_files.is_empty()
|
|| !state.extra_files.is_empty()
|
||||||
|| !state.stale_files.is_empty();
|
|| !state.stale_files.is_empty();
|
||||||
let apply_button = if state.is_applying {
|
let apply_button = if state.is_applying {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "siteValidation.applying"))
|
text(t(locale, "siteValidation.applying"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
@@ -50,16 +52,17 @@ pub fn view<'a>(state: &'a SiteValidationState, locale: UiLocale) -> Element<'a,
|
|||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
} else if !state.is_running && state.error_message.is_none() && has_issues {
|
} else if !state.is_running && state.error_message.is_none() && has_issues {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "siteValidation.apply"))
|
text(t(locale, "siteValidation.apply"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
)
|
)
|
||||||
.on_press(Message::ApplySiteValidation)
|
.on_press(Message::ApplySiteValidation)
|
||||||
|
.hotkey('a')
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
} else {
|
} else {
|
||||||
button(
|
keyboard::button(
|
||||||
text(t(locale, "siteValidation.apply"))
|
text(t(locale, "siteValidation.apply"))
|
||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced),
|
.shaping(Shaping::Advanced),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, container, row, text};
|
use iced::widget::{Space, container, row, text};
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Length, Theme};
|
use iced::{Alignment, Background, Border, Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::engine::task::TaskStatus;
|
use bds_core::engine::task::TaskStatus;
|
||||||
@@ -235,7 +237,7 @@ pub fn view(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Airplane mode toggle — ✈ icon
|
// Airplane mode toggle — ✈ icon
|
||||||
let airplane_btn = button(text("\u{2708}").size(13).shaping(Shaping::Advanced))
|
let airplane_btn = keyboard::button(text("\u{2708}").size(13).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::SetOfflineMode(!offline_mode))
|
.on_press(Message::SetOfflineMode(!offline_mode))
|
||||||
.padding([2, 4])
|
.padding([2, 4])
|
||||||
.style(if offline_mode {
|
.style(if offline_mode {
|
||||||
@@ -249,7 +251,7 @@ pub fn view(
|
|||||||
.size(14)
|
.size(14)
|
||||||
.shaping(Shaping::Advanced);
|
.shaping(Shaping::Advanced);
|
||||||
|
|
||||||
let locale_trigger = button(trigger_flag)
|
let locale_trigger = keyboard::button(trigger_flag)
|
||||||
.on_press(Message::ToggleLocaleDropdown)
|
.on_press(Message::ToggleLocaleDropdown)
|
||||||
.padding([1, 4])
|
.padding([1, 4])
|
||||||
.style(dropdown_trigger);
|
.style(dropdown_trigger);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Column, Space, button, column, container, row, scrollable, text};
|
use iced::widget::{Column, Space, column, container, row, scrollable, text};
|
||||||
use iced::{Background, Border, Color, Element, Length, Radians, Theme, gradient};
|
use iced::{Background, Border, Color, Element, Length, Radians, Theme, gradient};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -220,7 +222,7 @@ fn swatch(background: Background, width_portion: u16) -> Element<'static, Messag
|
|||||||
fn theme_button<'a>(theme: &StyleTheme, selected_theme: &str) -> Element<'a, Message> {
|
fn theme_button<'a>(theme: &StyleTheme, selected_theme: &str) -> Element<'a, Message> {
|
||||||
let selected = theme.name == selected_theme;
|
let selected = theme.name == selected_theme;
|
||||||
let theme_name = theme.name.to_string();
|
let theme_name = theme.name.to_string();
|
||||||
button(
|
keyboard::button(
|
||||||
column![
|
column![
|
||||||
row![
|
row![
|
||||||
swatch(theme_accent_background(theme), 2),
|
swatch(theme_accent_background(theme), 2),
|
||||||
@@ -318,13 +320,13 @@ pub fn view<'a>(
|
|||||||
.size(13)
|
.size(13)
|
||||||
.shaping(Shaping::Advanced);
|
.shaping(Shaping::Advanced);
|
||||||
let apply_button: Element<'a, Message> = if state.can_apply() {
|
let apply_button: Element<'a, Message> = if state.can_apply() {
|
||||||
button(apply_label)
|
keyboard::button(apply_label)
|
||||||
.on_press(Message::Style(StyleMsg::Apply))
|
.on_press(Message::Style(StyleMsg::Apply))
|
||||||
.padding([8, 16])
|
.padding([8, 16])
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.into()
|
.into()
|
||||||
} else {
|
} else {
|
||||||
button(apply_label)
|
keyboard::button(apply_label)
|
||||||
.padding([8, 16])
|
.padding([8, 16])
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.into()
|
.into()
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::scrollable::Direction;
|
use iced::widget::scrollable::Direction;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::tooltip::Position;
|
use iced::widget::tooltip::Position;
|
||||||
use iced::widget::{Space, button, container, row, scrollable, text, tooltip};
|
use iced::widget::{Space, container, row, scrollable, text, tooltip};
|
||||||
use iced::{Background, Border, Color, Element, Font, Length, Theme};
|
use iced::{Background, Border, Color, Element, Font, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -177,9 +179,20 @@ pub fn view(tabs: &[Tab], active_tab: Option<&str>, locale: UiLocale) -> Element
|
|||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.clip(true);
|
.clip(true);
|
||||||
|
|
||||||
|
// Keep the tooltip around non-interactive title content so it does
|
||||||
|
// not hide the tab and close controls from widget operations.
|
||||||
|
let tooltip_text = build_tooltip_text(tab, locale);
|
||||||
|
let title_area = tooltip(
|
||||||
|
title_area,
|
||||||
|
text(tooltip_text).size(11).shaping(Shaping::Advanced),
|
||||||
|
Position::Bottom,
|
||||||
|
)
|
||||||
|
.gap(4)
|
||||||
|
.style(inputs::tooltip_style);
|
||||||
|
|
||||||
let label = row![
|
let label = row![
|
||||||
title_area,
|
title_area,
|
||||||
button(text("\u{2715}").size(10).shaping(Shaping::Advanced))
|
keyboard::button(text("\u{2715}").size(10).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::CloseTab(close_id))
|
.on_press(Message::CloseTab(close_id))
|
||||||
.padding(2)
|
.padding(2)
|
||||||
.style(close_style),
|
.style(close_style),
|
||||||
@@ -188,24 +201,12 @@ pub fn view(tabs: &[Tab], active_tab: Option<&str>, locale: UiLocale) -> Element
|
|||||||
.align_y(iced::Alignment::Center);
|
.align_y(iced::Alignment::Center);
|
||||||
|
|
||||||
// tabs.allium: tab_min_width=100, tab_max_width=160
|
// tabs.allium: tab_min_width=100, tab_max_width=160
|
||||||
let tab_btn = button(label)
|
keyboard::button(label)
|
||||||
.on_press(Message::SelectTab(tab_id))
|
.on_press(Message::SelectTab(tab_id))
|
||||||
.padding([6, 8])
|
.padding([6, 8])
|
||||||
.width(Length::Fixed(TAB_WIDTH))
|
.width(Length::Fixed(TAB_WIDTH))
|
||||||
.style(if is_active { tab_active } else { tab_inactive });
|
.style(if is_active { tab_active } else { tab_inactive })
|
||||||
|
.into()
|
||||||
// tabs.allium tooltip: title + "(Preview)" if transient + "* Modified" if dirty
|
|
||||||
let tooltip_text = build_tooltip_text(tab, locale);
|
|
||||||
let tip: Element<'static, Message> = tooltip(
|
|
||||||
tab_btn,
|
|
||||||
text(tooltip_text).size(11).shaping(Shaping::Advanced),
|
|
||||||
Position::Bottom,
|
|
||||||
)
|
|
||||||
.gap(4)
|
|
||||||
.style(inputs::tooltip_style)
|
|
||||||
.into();
|
|
||||||
|
|
||||||
tip
|
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
Space, button, checkbox, column, container, pick_list, row, scrollable, text, text_input,
|
Space, checkbox, column, container, pick_list, row, scrollable, text, text_input,
|
||||||
};
|
};
|
||||||
use iced::{Alignment, Background, Color, Element, Length, Theme};
|
use iced::{Alignment, Background, Color, Element, Length, Theme};
|
||||||
|
|
||||||
@@ -247,27 +249,32 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
}));
|
}));
|
||||||
let name = category.name.clone();
|
let name = category.name.clone();
|
||||||
cells.push(
|
cells.push(
|
||||||
container(
|
container(keyboard::focusable(
|
||||||
checkbox("", category.render_in_lists).on_toggle(move |value| {
|
checkbox("", category.render_in_lists).on_toggle(move |value| {
|
||||||
Message::Settings(SettingsMsg::CategoryRenderInListsChanged(
|
Message::Settings(SettingsMsg::CategoryRenderInListsChanged(
|
||||||
name.clone(),
|
name.clone(),
|
||||||
value,
|
value,
|
||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
)
|
true,
|
||||||
|
))
|
||||||
.width(Length::Fixed(TOGGLE_WIDTH))
|
.width(Length::Fixed(TOGGLE_WIDTH))
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
let name = category.name.clone();
|
let name = category.name.clone();
|
||||||
cells.push(
|
cells.push(
|
||||||
container(checkbox("", category.show_title).on_toggle(move |value| {
|
container(keyboard::focusable(
|
||||||
|
checkbox("", category.show_title).on_toggle(move |value| {
|
||||||
Message::Settings(SettingsMsg::CategoryShowTitleChanged(name.clone(), value))
|
Message::Settings(SettingsMsg::CategoryShowTitleChanged(name.clone(), value))
|
||||||
}))
|
}),
|
||||||
|
true,
|
||||||
|
))
|
||||||
.width(Length::Fixed(TOGGLE_WIDTH))
|
.width(Length::Fixed(TOGGLE_WIDTH))
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
let name = category.name.clone();
|
let name = category.name.clone();
|
||||||
cells.push(
|
cells.push(
|
||||||
|
keyboard::focusable(
|
||||||
pick_list(
|
pick_list(
|
||||||
template_options.clone(),
|
template_options.clone(),
|
||||||
Some(category.post_template_slug.clone()),
|
Some(category.post_template_slug.clone()),
|
||||||
@@ -280,11 +287,14 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
)
|
)
|
||||||
.padding([7, 9])
|
.padding([7, 9])
|
||||||
.style(inputs::select_style)
|
.style(inputs::select_style)
|
||||||
.width(Length::Fixed(TEMPLATE_WIDTH))
|
.width(Length::Fixed(TEMPLATE_WIDTH)),
|
||||||
|
true,
|
||||||
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
let name = category.name.clone();
|
let name = category.name.clone();
|
||||||
cells.push(
|
cells.push(
|
||||||
|
keyboard::focusable(
|
||||||
pick_list(
|
pick_list(
|
||||||
template_options.clone(),
|
template_options.clone(),
|
||||||
Some(category.list_template_slug.clone()),
|
Some(category.list_template_slug.clone()),
|
||||||
@@ -297,19 +307,21 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
)
|
)
|
||||||
.padding([7, 9])
|
.padding([7, 9])
|
||||||
.style(inputs::select_style)
|
.style(inputs::select_style)
|
||||||
.width(Length::Fixed(TEMPLATE_WIDTH))
|
.width(Length::Fixed(TEMPLATE_WIDTH)),
|
||||||
|
true,
|
||||||
|
)
|
||||||
.into(),
|
.into(),
|
||||||
);
|
);
|
||||||
cells.push(
|
cells.push(
|
||||||
container(
|
container(
|
||||||
row![
|
row![
|
||||||
button(text(t(locale, "common.save")).size(12))
|
keyboard::button(text(t(locale, "common.save")).size(12))
|
||||||
.on_press(Message::Settings(SettingsMsg::SaveCategory(
|
.on_press(Message::Settings(SettingsMsg::SaveCategory(
|
||||||
category.name.clone(),
|
category.name.clone(),
|
||||||
)))
|
)))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 10]),
|
.padding([6, 10]),
|
||||||
button(text(t(locale, "common.remove")).size(12))
|
keyboard::button(text(t(locale, "common.remove")).size(12))
|
||||||
.on_press_maybe((!category.is_protected).then(|| Message::Settings(
|
.on_press_maybe((!category.is_protected).then(|| Message::Settings(
|
||||||
SettingsMsg::RemoveCategory(category.name.clone()),
|
SettingsMsg::RemoveCategory(category.name.clone()),
|
||||||
)))
|
)))
|
||||||
@@ -336,11 +348,11 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
&state.new_category_name,
|
&state.new_category_name,
|
||||||
|value| Message::Settings(SettingsMsg::AddCategoryNameChanged(value)),
|
|value| Message::Settings(SettingsMsg::AddCategoryNameChanged(value)),
|
||||||
),
|
),
|
||||||
button(text(t(locale, "common.add")).size(13))
|
keyboard::button(text(t(locale, "common.add")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::AddCategory))
|
.on_press(Message::Settings(SettingsMsg::AddCategory))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 12]),
|
.padding([6, 12]),
|
||||||
button(text(t(locale, "settings.resetCategories")).size(13))
|
keyboard::button(text(t(locale, "settings.resetCategories")).size(13))
|
||||||
.on_press(Message::Settings(SettingsMsg::ResetCategoriesToDefaults))
|
.on_press(Message::Settings(SettingsMsg::ResetCategoriesToDefaults))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 12]),
|
.padding([6, 12]),
|
||||||
@@ -382,7 +394,7 @@ fn view_categories<'a>(state: &'a SettingsViewState, locale: UiLocale) -> Elemen
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn section_tab<'a>(label: &str, active: bool, section: TagsSection) -> Element<'a, Message> {
|
fn section_tab<'a>(label: &str, active: bool, section: TagsSection) -> Element<'a, Message> {
|
||||||
button(text(label.to_string()).size(13))
|
keyboard::button(text(label.to_string()).size(13))
|
||||||
.on_press(Message::Tags(TagsMsg::SetSection(section)))
|
.on_press(Message::Tags(TagsMsg::SetSection(section)))
|
||||||
.padding([6, 12])
|
.padding([6, 12])
|
||||||
.style(if active {
|
.style(if active {
|
||||||
@@ -443,7 +455,7 @@ fn view_cloud<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes
|
|||||||
.selected_tags
|
.selected_tags
|
||||||
.iter()
|
.iter()
|
||||||
.any(|selected_id| selected_id == &tag.id);
|
.any(|selected_id| selected_id == &tag.id);
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
text(&tag.name).size(font_size).color(Color::WHITE),
|
text(&tag.name).size(font_size).color(Color::WHITE),
|
||||||
text(post_count.to_string())
|
text(post_count.to_string())
|
||||||
@@ -488,7 +500,7 @@ fn view_cloud<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes
|
|||||||
))
|
))
|
||||||
.size(12)
|
.size(12)
|
||||||
.color(Color::from_rgb(0.75, 0.77, 0.82)),
|
.color(Color::from_rgb(0.75, 0.77, 0.82)),
|
||||||
button(text(t(locale, "tags.clearSelection")).size(12))
|
keyboard::button(text(t(locale, "tags.clearSelection")).size(12))
|
||||||
.on_press(Message::Tags(TagsMsg::ClearSelection))
|
.on_press(Message::Tags(TagsMsg::ClearSelection))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([4, 8]),
|
.padding([4, 8]),
|
||||||
@@ -533,7 +545,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
|||||||
|value| Message::Tags(TagsMsg::CreateColorChanged(value)),
|
|value| Message::Tags(TagsMsg::CreateColorChanged(value)),
|
||||||
),
|
),
|
||||||
color_swatches(locale, true),
|
color_swatches(locale, true),
|
||||||
button(text(t(locale, "tags.createButton")).size(13))
|
keyboard::button(text(t(locale, "tags.createButton")).size(13))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
(!state.create_name.trim().is_empty()).then_some(Message::Tags(TagsMsg::CreateTag))
|
(!state.create_name.trim().is_empty()).then_some(Message::Tags(TagsMsg::CreateTag))
|
||||||
)
|
)
|
||||||
@@ -573,7 +585,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
|||||||
.get(&tag.name.to_lowercase())
|
.get(&tag.name.to_lowercase())
|
||||||
.copied()
|
.copied()
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
button(
|
keyboard::button(
|
||||||
row![
|
row![
|
||||||
container(Space::new(12, 12)).style(move |_: &Theme| container::Style {
|
container(Space::new(12, 12)).style(move |_: &Theme| container::Style {
|
||||||
background: Some(Background::Color(color)),
|
background: Some(Background::Color(color)),
|
||||||
@@ -620,7 +632,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|option| option.slug == editing.template_slug);
|
.find(|option| option.slug == editing.template_slug);
|
||||||
let delete_button: Element<'a, Message> =
|
let delete_button: Element<'a, Message> =
|
||||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||||
.on_press(Message::Tags(TagsMsg::DeleteTag(editing.id.clone())))
|
.on_press(Message::Tags(TagsMsg::DeleteTag(editing.id.clone())))
|
||||||
.style(inputs::danger_button)
|
.style(inputs::danger_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
@@ -645,7 +657,7 @@ fn view_manage<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Me
|
|||||||
|choice| Message::Tags(TagsMsg::EditTagTemplate(choice)),
|
|choice| Message::Tags(TagsMsg::EditTagTemplate(choice)),
|
||||||
),
|
),
|
||||||
row![
|
row![
|
||||||
button(text(t(locale, "common.save")).size(13))
|
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::Tags(TagsMsg::SaveTag))
|
.on_press(Message::Tags(TagsMsg::SaveTag))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
@@ -744,7 +756,7 @@ fn view_merge<'a>(state: &'a TagsViewState, locale: UiLocale) -> Element<'a, Mes
|
|||||||
.padding([4, 0])
|
.padding([4, 0])
|
||||||
.into()
|
.into()
|
||||||
},
|
},
|
||||||
button(text(t(locale, "tags.merge")).size(13))
|
keyboard::button(text(t(locale, "tags.merge")).size(13))
|
||||||
.on_press_maybe(
|
.on_press_maybe(
|
||||||
state
|
state
|
||||||
.merge_target
|
.merge_target
|
||||||
@@ -768,7 +780,7 @@ fn view_discover<'a>(_state: &'a TagsViewState, locale: UiLocale) -> Element<'a,
|
|||||||
text(t(locale, "tags.discoverDescription"))
|
text(t(locale, "tags.discoverDescription"))
|
||||||
.size(12)
|
.size(12)
|
||||||
.color(Color::from_rgb(0.60, 0.60, 0.65)),
|
.color(Color::from_rgb(0.60, 0.60, 0.65)),
|
||||||
button(text(t(locale, "tags.discoverButton")).size(13))
|
keyboard::button(text(t(locale, "tags.discoverButton")).size(13))
|
||||||
.on_press(Message::Tags(TagsMsg::SyncTags))
|
.on_press(Message::Tags(TagsMsg::SyncTags))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]),
|
.padding([6, 16]),
|
||||||
@@ -819,7 +831,7 @@ fn color_swatches<'a>(locale: UiLocale, create_mode: bool) -> Element<'a, Messag
|
|||||||
} else {
|
} else {
|
||||||
TagsMsg::EditTagColor((*hex).to_string())
|
TagsMsg::EditTagColor((*hex).to_string())
|
||||||
};
|
};
|
||||||
button(Space::new(18, 18))
|
keyboard::button(Space::new(18, 18))
|
||||||
.on_press(Message::Tags(msg))
|
.on_press(Message::Tags(msg))
|
||||||
.padding(0)
|
.padding(0)
|
||||||
.style(move |_theme: &Theme, _status| button::Style {
|
.style(move |_theme: &Theme, _status| button::Style {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||||
use iced::{Color, Element, Length, Theme};
|
use iced::{Color, Element, Length, Theme};
|
||||||
|
|
||||||
use bds_core::i18n::UiLocale;
|
use bds_core::i18n::UiLocale;
|
||||||
@@ -114,17 +115,17 @@ pub fn view<'a>(state: &'a TemplateEditorState, locale: UiLocale) -> Element<'a,
|
|||||||
status_badge(&state.status),
|
status_badge(&state.status),
|
||||||
],
|
],
|
||||||
vec![
|
vec![
|
||||||
button(text(t(locale, "common.save")).size(13))
|
keyboard::button(text(t(locale, "common.save")).size(13))
|
||||||
.on_press(Message::TemplateEditor(TemplateEditorMsg::Save))
|
.on_press(Message::TemplateEditor(TemplateEditorMsg::Save))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "editor.validate")).size(13))
|
keyboard::button(text(t(locale, "editor.validate")).size(13))
|
||||||
.on_press(Message::TemplateEditor(TemplateEditorMsg::Validate))
|
.on_press(Message::TemplateEditor(TemplateEditorMsg::Validate))
|
||||||
.style(inputs::secondary_button)
|
.style(inputs::secondary_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
.into(),
|
.into(),
|
||||||
button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
keyboard::button(text(t(locale, "modal.confirmDelete.delete")).size(13))
|
||||||
.on_press(Message::TemplateEditor(TemplateEditorMsg::Delete))
|
.on_press(Message::TemplateEditor(TemplateEditorMsg::Delete))
|
||||||
.style(inputs::danger_button)
|
.style(inputs::danger_button)
|
||||||
.padding([6, 16])
|
.padding([6, 16])
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
|
use iced::widget::button;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, container, row, text};
|
use iced::widget::{Space, container, row, text};
|
||||||
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Theme};
|
use iced::{Alignment, Background, Border, Color, Element, Length, Padding, Theme};
|
||||||
|
|
||||||
use crate::app::Message;
|
use crate::app::Message;
|
||||||
@@ -62,7 +64,7 @@ pub fn view(toasts: &[Toast]) -> Option<Element<'static, Message>> {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|toast| {
|
.map(|toast| {
|
||||||
let level = toast.level;
|
let level = toast.level;
|
||||||
let dismiss = button(text("\u{2715}").size(11).shaping(Shaping::Advanced))
|
let dismiss = keyboard::button(text("\u{2715}").size(11).shaping(Shaping::Advanced))
|
||||||
.on_press(Message::DismissToast(toast.id))
|
.on_press(Message::DismissToast(toast.id))
|
||||||
.padding([2, 4])
|
.padding([2, 4])
|
||||||
.style(dismiss_btn);
|
.style(dismiss_btn);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, column, container, row, scrollable, text};
|
use iced::widget::{Space, column, container, row, scrollable, text};
|
||||||
use iced::{Color, Element, Length};
|
use iced::{Color, Element, Length};
|
||||||
|
|
||||||
use bds_core::engine::validate_translations::{
|
use bds_core::engine::validate_translations::{
|
||||||
@@ -19,7 +20,7 @@ pub struct TranslationValidationState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn view<'a>(state: &'a TranslationValidationState, locale: UiLocale) -> Element<'a, Message> {
|
pub fn view<'a>(state: &'a TranslationValidationState, locale: UiLocale) -> Element<'a, Message> {
|
||||||
let run = button(text(t(locale, "translationValidation.run")).size(13))
|
let run = keyboard::button(text(t(locale, "translationValidation.run")).size(13))
|
||||||
.on_press_maybe((!state.is_running).then_some(Message::ValidateTranslations))
|
.on_press_maybe((!state.is_running).then_some(Message::ValidateTranslations))
|
||||||
.style(inputs::primary_button)
|
.style(inputs::primary_button)
|
||||||
.padding([6, 16]);
|
.padding([6, 16]);
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
|
use crate::components::keyboard;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use iced::widget::text::Shaping;
|
use iced::widget::text::Shaping;
|
||||||
use iced::widget::{Space, button, column, container, mouse_area, row, stack, text};
|
use iced::widget::{Space, column, container, mouse_area, row, stack, text};
|
||||||
use iced::{Alignment, Background, Color, Element, Length, Padding, Theme};
|
use iced::{Alignment, Background, Color, Element, Length, Padding, Theme};
|
||||||
|
|
||||||
use bds_core::engine::git::GitCommit;
|
use bds_core::engine::git::GitCommit;
|
||||||
@@ -305,7 +306,7 @@ pub fn view<'a>(
|
|||||||
.map(|&l| {
|
.map(|&l| {
|
||||||
let flag_text = text(l.flag_emoji()).size(16).shaping(Shaping::Advanced);
|
let flag_text = text(l.flag_emoji()).size(16).shaping(Shaping::Advanced);
|
||||||
|
|
||||||
button(flag_text)
|
keyboard::button(flag_text)
|
||||||
.on_press(Message::SetUiLocale(l))
|
.on_press(Message::SetUiLocale(l))
|
||||||
.padding([4, 8])
|
.padding([4, 8])
|
||||||
.style(status_bar::dropdown_item)
|
.style(status_bar::dropdown_item)
|
||||||
@@ -371,15 +372,32 @@ pub fn view<'a>(
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only the top blocking layer participates in keyboard traversal/access keys.
|
||||||
|
let modal_is_active = active_modal.is_some();
|
||||||
|
let blocking_overlay = modal_is_active || overlay.is_some();
|
||||||
|
let base_layout: Element<'a, Message> = if blocking_overlay {
|
||||||
|
keyboard::suspend(base_layout).into()
|
||||||
|
} else {
|
||||||
|
base_layout
|
||||||
|
};
|
||||||
|
|
||||||
// Collect overlays: dropdowns and toasts
|
// Collect overlays: dropdowns and toasts
|
||||||
let mut overlays: Vec<Element<'a, Message>> = Vec::new();
|
let mut overlays: Vec<Element<'a, Message>> = Vec::new();
|
||||||
|
|
||||||
if let Some(toast_overlay) = toast::view(toasts) {
|
if let Some(toast_overlay) = toast::view(toasts) {
|
||||||
overlays.push(toast_overlay);
|
overlays.push(if blocking_overlay {
|
||||||
|
keyboard::suspend(toast_overlay).into()
|
||||||
|
} else {
|
||||||
|
toast_overlay
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(overlay) = overlay {
|
if let Some(overlay) = overlay {
|
||||||
overlays.push(overlay);
|
overlays.push(if modal_is_active {
|
||||||
|
keyboard::suspend(overlay).into()
|
||||||
|
} else {
|
||||||
|
overlay
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modal overlay (highest z-index)
|
// Modal overlay (highest z-index)
|
||||||
|
|||||||
@@ -342,6 +342,8 @@ surface MetadataDiffSurface {
|
|||||||
@guarantee ScanAction
|
@guarantee ScanAction
|
||||||
-- Scan/Rescan button at top.
|
-- Scan/Rescan button at top.
|
||||||
-- Progress bar + message during scan.
|
-- Progress bar + message during scan.
|
||||||
|
-- Control+Option+R (macOS) or Control+Alt+R runs the scan; repair buttons use Tab
|
||||||
|
-- and are activated with Enter or Space.
|
||||||
|
|
||||||
@guarantee EntityTabs
|
@guarantee EntityTabs
|
||||||
-- Tabs: Posts, Media, Scripts, Templates — each with badge count of diffs.
|
-- Tabs: Posts, Media, Scripts, Templates — each with badge count of diffs.
|
||||||
@@ -443,6 +445,8 @@ surface SiteValidationSurface {
|
|||||||
|
|
||||||
@guarantee ApplyAction
|
@guarantee ApplyAction
|
||||||
-- Apply button disabled when nothing to fix.
|
-- Apply button disabled when nothing to fix.
|
||||||
|
-- Control+Option (macOS) or Control+Alt, followed by R, scans and A applies;
|
||||||
|
-- both remain in Tab order.
|
||||||
-- On apply: renders missing, deletes extra, re-renders updated.
|
-- On apply: renders missing, deletes extra, re-renders updated.
|
||||||
-- Toast: "Validation applied: N rendered, N deleted".
|
-- Toast: "Validation applied: N rendered, N deleted".
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ surface PostEditorSurface {
|
|||||||
|
|
||||||
@guarantee MetadataSection
|
@guarantee MetadataSection
|
||||||
-- Collapsible section. Starts expanded when title is empty.
|
-- Collapsible section. Starts expanded when title is empty.
|
||||||
|
-- Control+Option+M (macOS) or Control+Alt+M toggles it outside focused text input.
|
||||||
-- Two-column layout.
|
-- Two-column layout.
|
||||||
-- Left column: Title, Tags, Author, Language + detect button,
|
-- Left column: Title, Tags, Author, Language + detect button,
|
||||||
-- Do Not Translate checkbox, Slug (read-only), Categories,
|
-- Do Not Translate checkbox, Slug (read-only), Categories,
|
||||||
@@ -156,6 +157,7 @@ surface PostEditorSurface {
|
|||||||
|
|
||||||
@guarantee ExcerptSection
|
@guarantee ExcerptSection
|
||||||
-- Collapsible section with textarea (4 rows).
|
-- Collapsible section with textarea (4 rows).
|
||||||
|
-- Control+Option+X (macOS) or Control+Alt+X toggles it outside focused text input.
|
||||||
|
|
||||||
@guarantee EditorBodyToolbar
|
@guarantee EditorBodyToolbar
|
||||||
-- Toolbar: "Content" label, mode toggle (Markdown/Preview),
|
-- Toolbar: "Content" label, mode toggle (Markdown/Preview),
|
||||||
|
|||||||
@@ -219,6 +219,7 @@ surface SettingsViewSurface {
|
|||||||
@guarantee CollapsibleSections
|
@guarantee CollapsibleSections
|
||||||
-- All 8 sections are collapsible.
|
-- All 8 sections are collapsible.
|
||||||
-- Section visibility respects search filter.
|
-- Section visibility respects search filter.
|
||||||
|
-- Each visible section exposes a unique Control+Option or Control+Alt access key.
|
||||||
}
|
}
|
||||||
|
|
||||||
-- ─── Settings view actions ──────────────────────────────────
|
-- ─── Settings view actions ──────────────────────────────────
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ surface WordPressImportSurface {
|
|||||||
-- taxonomy, and discovers WordPress shortcodes. The saved report shows
|
-- taxonomy, and discovers WordPress shortcodes. The saved report shows
|
||||||
-- item counts, conflicts, missing uploads, year distribution, macro
|
-- item counts, conflicts, missing uploads, year distribution, macro
|
||||||
-- usage, and importable totals before any project content is changed.
|
-- usage, and importable totals before any project content is changed.
|
||||||
|
-- Its Conflicts, Posts, Pages, Media, Taxonomy, and Macros sections
|
||||||
|
-- expose unique Control+Option or Control+Alt access keys for disclosure control.
|
||||||
|
|
||||||
@guarantee ConflictReview
|
@guarantee ConflictReview
|
||||||
-- Conflicts default to ignore. The operator can keep ignoring, overwrite
|
-- Conflicts default to ignore. The operator can keep ignoring, overwrite
|
||||||
|
|||||||
@@ -72,6 +72,26 @@ surface AppShellSurface {
|
|||||||
shell.sidebar.width
|
shell.sidebar.width
|
||||||
shell.content_area.panel.visible
|
shell.content_area.panel.visible
|
||||||
shell.assistant_sidebar.visible
|
shell.assistant_sidebar.visible
|
||||||
|
|
||||||
|
@guarantee KeyboardTraversal
|
||||||
|
-- Tab and Shift+Tab traverse every enabled button, checkbox, picker,
|
||||||
|
-- and text editor in visual order. Disabled controls are skipped.
|
||||||
|
-- Enter or Space activates the focused control.
|
||||||
|
|
||||||
|
@guarantee KeyboardHints
|
||||||
|
-- Holding Control+Option on macOS or Control+Alt on Linux/Windows reveals
|
||||||
|
-- an access-key marker on every enabled control intersecting the viewport.
|
||||||
|
-- Semantic controls use their direct one-character shortcut. Remaining
|
||||||
|
-- visible controls receive stable two- or three-letter A-Z codes in
|
||||||
|
-- widget-tree order, excluding direct letters as generic prefixes.
|
||||||
|
-- Every marker is a warm-yellow box with black text and border, placed
|
||||||
|
-- wholly inside its control without obscuring activity-bar icons.
|
||||||
|
-- Typing a prefix while holding the modifier hides non-matches and shows
|
||||||
|
-- only each candidate's remaining suffix; a complete code activates its
|
||||||
|
-- control. Escape or modifier release cancels the access-key mode.
|
||||||
|
-- Covered layers and off-screen scroll content receive no code. A focused
|
||||||
|
-- Option/Alt alone always remains available for character input, and a
|
||||||
|
-- focused text editor retains modified input instead of entering the mode.
|
||||||
}
|
}
|
||||||
|
|
||||||
value ContentArea {
|
value ContentArea {
|
||||||
@@ -162,6 +182,7 @@ value ActivityButton {
|
|||||||
label_key: String -- i18n key for tooltip
|
label_key: String -- i18n key for tooltip
|
||||||
badge: Badge? -- only git has a badge
|
badge: Badge? -- only git has a badge
|
||||||
active: Boolean -- highlighted when this view is showing
|
active: Boolean -- highlighted when this view is showing
|
||||||
|
access_key: String -- Control+Option (macOS) or Control+Alt + 1..9,0 in displayed order
|
||||||
}
|
}
|
||||||
|
|
||||||
value Badge {
|
value Badge {
|
||||||
@@ -317,3 +338,9 @@ value TokenUsage {
|
|||||||
|
|
||||||
-- Ctrl/Cmd+B: toggle sidebar
|
-- Ctrl/Cmd+B: toggle sidebar
|
||||||
-- Ctrl/Cmd+W: close active tab (see tabs.allium)
|
-- Ctrl/Cmd+W: close active tab (see tabs.allium)
|
||||||
|
-- Tab / Shift+Tab: next / previous enabled control
|
||||||
|
-- Enter / Space: activate focused control
|
||||||
|
-- Control+Option (macOS) or Control+Alt + 1..9,0: activate the corresponding activity
|
||||||
|
-- Post editor Control+Option (macOS) or Control+Alt + M/X: toggle metadata/excerpt
|
||||||
|
-- Import and Settings sections expose unique Control+Option or Control+Alt access keys
|
||||||
|
-- Site Validation uses R/A and Metadata Diff uses R with the platform modifier pair
|
||||||
|
|||||||
Reference in New Issue
Block a user