+
Skip to content

Next #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 19 commits into from
May 25, 2025
Merged

Next #59

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
424 changes: 208 additions & 216 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ The standard view will show `ssh` and `drun`, for other modes the following pref
* `ssh` (optional)
* `?` web search
* `/`, `$` or `~` for files
* `emoji` for emojis

<img src="images/demo.gif">

Expand Down Expand Up @@ -66,6 +65,9 @@ Styling names and classes are inspired by wofi, so most of the documentation and
| `row` | Row containing the entry, used to control hover effects. |
| `custom-key-label-text` | The label for custom keys |
| `custom-key-label-box` | Box containing the label, can be used for borders etc. |
| `custom-key-hint-text` | The label for custom keys hint |
| `custom-key-hint-box` | Box containing the hint, can be used for borders etc. |


Checkout more showcases in the [styles directory of this repo](styles).

Expand Down Expand Up @@ -97,6 +99,7 @@ This library is not available publicly yet as the interface is not stable enough
* Color files are not supported
* `line_wrap` is now called `line-wrap`
* Wofi has a C-API, that is not and won't be supported, but Worf can be used as a rust library.
* Most boolean options now need `true` or `false` as argument, as Worf is using the same struct for config and command line arguments and this is the only way to merge both data sources

### Dropped arguments / config values
* `mode`, use show
Expand Down
184 changes: 116 additions & 68 deletions examples/worf-warden/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use std::env;
use std::process::Command;
use std::thread::sleep;
use std::time::Duration;
use worf_lib::config::Config;
use worf_lib::config::{Config, CustomKeyHintLocation};
use worf_lib::desktop::{copy_to_clipboard, spawn_fork};
use worf_lib::gui::{ItemProvider, Key, KeyBinding, MenuItem, Modifier};
use worf_lib::gui::{CustomKeyHint, CustomKeys, ItemProvider, Key, KeyBinding, MenuItem, Modifier};
use worf_lib::{config, gui};

#[derive(Clone)]
Expand All @@ -24,51 +24,34 @@ fn split_at_tab(input: &str) -> Option<(&str, &str)> {
}

impl PasswordProvider {
fn new(config: &Config) -> Self {
let output = rbw("list", Some(vec!["--fields", "id,name"]));
let items = match output {
Ok(output) => {
let mut items = output
.lines()
.filter_map(|s| split_at_tab(s))
.fold(
HashMap::new(),
|mut acc: HashMap<String, Vec<String>>, (id, name)| {
acc.entry(name.to_owned()).or_default().push(id.to_owned());
acc
},
)
.iter()
.map(|(key, value)| {
MenuItem::new(
key.clone(),
None,
None,
vec![],
None,
0.0,
Some(MenuItemMetaData { ids: value.clone() }),
)
})
.collect::<Vec<_>>();
gui::apply_sort(&mut items, &config.sort_order());
items
}
Err(error) => {
let item = MenuItem::new(
format!("Error from rbw: {error}"),
fn new(config: &Config) -> Result<Self, String> {
let output = rbw("list", Some(vec!["--fields", "id,name"]))?;
let mut items = output
.lines()
.filter_map(|s| split_at_tab(s))
.fold(
HashMap::new(),
|mut acc: HashMap<String, Vec<String>>, (id, name)| {
acc.entry(name.to_owned()).or_default().push(id.to_owned());
acc
},
)
.iter()
.map(|(key, value)| {
MenuItem::new(
key.clone(),
None,
None,
vec![],
vec![].into_iter().collect(),
None,
0.0,
None,
);
vec![item]
}
};
Some(MenuItemMetaData { ids: value.clone() }),
)
})
.collect::<Vec<_>>();
gui::apply_sort(&mut items, &config.sort_order());

Self { items }
Ok(Self { items })
}

fn sub_provider(ids: Vec<String>) -> Result<Self, String> {
Expand All @@ -79,11 +62,11 @@ impl PasswordProvider {
rbw_get_user(id, false)?,
None,
None,
vec![],
vec![].into_iter().collect(),
None,
0.0,
Some(MenuItemMetaData {
ids: vec![id.clone()],
ids: vec![id.clone()].into_iter().collect(),
}),
))
})
Expand Down Expand Up @@ -125,7 +108,16 @@ fn keyboard_type(text: &str) {

fn keyboard_tab() {
Command::new("ydotool")
.arg("TAB")
.arg("type")
.arg("\t")
.output()
.expect("Failed to execute ydotool");
}

fn keyboard_return() {
Command::new("ydotool")
.arg("type")
.arg("\n")
.output()
.expect("Failed to execute ydotool");
}
Expand Down Expand Up @@ -178,57 +170,100 @@ fn rbw_get_totp(id: &str, copy: bool) -> Result<String, String> {
fn key_type_all() -> KeyBinding {
KeyBinding {
key: Key::Num1,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+1</b> Type All".to_string(),
visible: true,
}
}

fn key_type_all_and_enter() -> KeyBinding {
KeyBinding {
key: Key::Exclamation,
modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
label: String::new(),
visible: false,
}
}

fn key_type_user() -> KeyBinding {
KeyBinding {
key: Key::Num2,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+2</b> Type User".to_string(),
visible: true,
}
}

fn key_type_user_and_enter() -> KeyBinding {
KeyBinding {
key: Key::At,
modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
label: String::new(),
visible: false,
}
}

fn key_type_password() -> KeyBinding {
KeyBinding {
key: Key::Num3,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+3</b> Type Password".to_string(),
visible: true,
}
}

fn key_type_password_and_enter() -> KeyBinding {
KeyBinding {
key: Key::Hash,
modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
label: String::new(),
visible: false,
}
}

fn key_type_totp() -> KeyBinding {
KeyBinding {
key: Key::Num4,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+4</b> Type Totp".to_string(),
visible: true,
}
}

fn key_type_totp_and_enter() -> KeyBinding {
KeyBinding {
key: Key::Dollar,
modifiers: vec![Modifier::Alt, Modifier::Shift].into_iter().collect(),
label: String::new(),
visible: false,
}
}

fn key_sync() -> KeyBinding {
KeyBinding {
key: Key::R,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+r</b> Sync".to_string(),
visible: true,
}
}

/// copies totp to clipboard
fn key_totp() -> KeyBinding {
KeyBinding {
key: Key::T,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+t</b> Totp".to_string(),
visible: true,
}
}

fn key_lock() -> KeyBinding {
KeyBinding {
key: Key::L,
modifiers: Modifier::Alt,
modifiers: vec![Modifier::Alt].into_iter().collect(),
label: "<b>Alt+l</b> Lock".to_string(),
visible: true,
}
}

Expand All @@ -238,15 +273,25 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
provider,
false,
None,
Some(vec![
key_type_all(),
key_type_user(),
key_type_password(),
key_type_totp(),
key_sync(),
key_totp(),
key_lock(),
]),
Some(CustomKeys {
bindings: vec![
key_type_all(),
key_type_all_and_enter(),
key_type_user(),
key_type_user_and_enter(),
key_type_password(),
key_type_password_and_enter(),
key_type_totp(),
key_type_totp_and_enter(),
key_sync(),
key_totp(),
key_lock(),
],
hint: Some(CustomKeyHint {
label: "Use Shift as additional modifier to send enter".to_string(),
location: CustomKeyHintLocation::Top,
}),
}),
) {
Ok(selection) => {
if let Some(meta) = selection.menu.data {
Expand All @@ -256,17 +301,17 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {

let id = meta.ids.first().unwrap_or(&selection.menu.label);

sleep(Duration::from_millis(250));
sleep(Duration::from_millis(500));
if let Some(key) = selection.custom_key {
if key == key_type_all() {
if key == key_type_all() || key == key_type_all_and_enter() {
keyboard_type(&rbw_get_user(id, false)?);
keyboard_tab();
keyboard_type(&rbw_get_password(id, false)?);
} else if key == key_type_user() {
} else if key == key_type_user() || key == key_type_user_and_enter() {
keyboard_type(&rbw_get_user(id, false)?);
} else if key == key_type_password() {
} else if key == key_type_password() || key == key_type_password_and_enter() {
keyboard_type(&rbw_get_password(id, false)?);
} else if key == key_type_totp() {
} else if key == key_type_totp() || key == key_type_totp_and_enter() {
keyboard_type(&rbw_get_totp(id, false)?);
} else if key == key_lock() {
rbw("lock", None)?;
Expand All @@ -275,6 +320,10 @@ fn show(config: Config, provider: PasswordProvider) -> Result<(), String> {
} else if key == key_totp() {
rbw_get_totp(id, true)?;
}

if key.modifiers.contains(&Modifier::Shift) {
keyboard_return();
}
} else {
let pw = rbw_get_password(id, true)?;
if let Err(e) = copy_to_clipboard(pw, None) {
Expand Down Expand Up @@ -310,7 +359,6 @@ fn main() -> Result<(), String> {
spawn_fork("ydotoold", None).expect("failed to spawn ydotoold");

// todo eventually use a propper rust client for this, for now rbw is good enough
let provider = PasswordProvider::new(&config);

let provider = PasswordProvider::new(&config)?;
show(config, provider)
}
9 changes: 8 additions & 1 deletion styles/compact/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,15 @@ font-family: DejaVu;
margin-bottom: 0.25em;
border-right: 1px solid rgba(214, 174, 0, 1);
border-left: 1px solid rgba(214, 174, 0, 1);
border-bottom: 1px solid rgba(214, 174, 0, 1);
padding-left: 1em;
}

#custom-key-hint-box {
margin-top: 0.25em;
margin-bottom: 0.25em;
padding-left: 1em;
}

#custom-key-label-text {
}
}
4 changes: 2 additions & 2 deletions styles/dmenu/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ image_size=0
columns=999
allow_images=false
orientation="Horizontal"
row_bow_orientation="Horizontal"
row_box_orientation="Horizontal"
content_halign="Start"
height="0"
width="100%"
hide_scroll=true
location=["Top"]
lines=1
9 changes: 9 additions & 0 deletions styles/emoji/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
image_size=64
columns=6
orientation="Vertical"
row_box_orientation="Vertical"
content_halign="Center"
height="70%"
width="60%"
valign="Start"
emoji_hide_label=true
Loading
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载