Skip to content

Commit 5e81625

Browse files
committed
✨ Add clipboard read function
1 parent 8536b0b commit 5e81625

File tree

4 files changed

+87
-0
lines changed

4 files changed

+87
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ https://github.com/sloganking/quick-assistant/assets/16965931/a0c7469a-2c64-46e5
2727
- 🗑️ **List and kill processes** by voice
2828
- 🌐 **Run internet speed tests**
2929
- 📋 **Set the clipboard** contents
30+
- 📋 **Get the clipboard** contents
3031
- 🔳 **Copy text as a QR code image** to the clipboard
3132
- ⏱️ **Timers** with alarm sounds
3233
- 🎙️ **Change voice** or speaking speed on the fly

assistant_v2/FEATURE_PROGRESS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ This document tracks which features from the original assistant have been implem
1313
| List and kill processes | Pending |
1414
| Run internet speed tests | Pending |
1515
| Set the clipboard contents | Done |
16+
| Get the clipboard contents | Done |
1617
| Timers with alarm sounds | Pending |
1718
| Change voice or speaking speed | Done |
1819
| Mute/unmute voice output | Done |

assistant_v2/src/main.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
186186
strict: None,
187187
}
188188
.into(),
189+
FunctionObject {
190+
name: "get_clipboard".into(),
191+
description: Some("Returns the current clipboard text.".into()),
192+
parameters: Some(serde_json::json!({
193+
"type": "object",
194+
"properties": {},
195+
"required": [],
196+
})),
197+
strict: None,
198+
}
199+
.into(),
189200
FunctionObject {
190201
name: "open_openai_billing".into(),
191202
description: Some(
@@ -519,6 +530,14 @@ fn get_system_info() -> String {
519530
info
520531
}
521532

533+
fn get_clipboard_string() -> Result<String, String> {
534+
let mut clipboard: ClipboardContext = ClipboardProvider::new()
535+
.map_err(|e| format!("Failed to initialize clipboard: {}", e))?;
536+
clipboard
537+
.get_contents()
538+
.map_err(|e| format!("Failed to read clipboard contents: {}", e))
539+
}
540+
522541
async fn handle_requires_action(
523542
client: Client<OpenAIConfig>,
524543
run_object: RunObject,
@@ -561,6 +580,17 @@ async fn handle_requires_action(
561580
});
562581
}
563582

583+
if tool.function.name == "get_clipboard" {
584+
let msg = match get_clipboard_string() {
585+
Ok(text) => text,
586+
Err(e) => e,
587+
};
588+
tool_outputs.push(ToolsOutputs {
589+
tool_call_id: Some(tool.id.clone()),
590+
output: Some(msg.into()),
591+
});
592+
}
593+
564594
if tool.function.name == "set_screen_brightness" {
565595
let brightness =
566596
match serde_json::from_str::<serde_json::Value>(&tool.function.arguments) {
@@ -957,4 +987,19 @@ mod tests {
957987
_ => false,
958988
}));
959989
}
990+
991+
#[test]
992+
fn get_clipboard_returns_contents() {
993+
let mut clipboard: ClipboardContext = match ClipboardProvider::new() {
994+
Ok(c) => c,
995+
Err(_) => return,
996+
};
997+
if clipboard.set_contents("clipboard_test".to_string()).is_err() {
998+
return;
999+
}
1000+
match get_clipboard_string() {
1001+
Ok(contents) => assert_eq!(contents, "clipboard_test"),
1002+
Err(_) => {}
1003+
}
1004+
}
9601005
}

src/main.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,21 @@ mod tests {
185185
assert_eq!(img.height, 128);
186186
assert_eq!(img.bytes.len(), 128 * 128 * 4);
187187
}
188+
189+
#[test]
190+
fn clipboard_roundtrip() {
191+
let mut clipboard: ClipboardContext = match ClipboardProvider::new() {
192+
Ok(c) => c,
193+
Err(_) => return, // clipboard not available
194+
};
195+
if clipboard.set_contents("test123".to_string()).is_err() {
196+
return;
197+
}
198+
match get_clipboard_string() {
199+
Ok(contents) => assert_eq!(contents, "test123"),
200+
Err(_) => {}
201+
}
202+
}
188203
}
189204

190205
/// Creates a temporary file from a byte slice and returns the path to the file.
@@ -577,6 +592,13 @@ fn call_fn(
577592
}
578593
}
579594

595+
"get_clipboard" => {
596+
match get_clipboard_string() {
597+
Ok(text) => Some(text),
598+
Err(e) => Some(e),
599+
}
600+
}
601+
580602
"qr_to_clipboard" => {
581603
let args = match serde_json::from_str::<serde_json::Value>(fn_args) {
582604
Ok(json) => json,
@@ -985,6 +1007,14 @@ fn qr_to_clipboard(text: &str) -> Result<(), String> {
9851007
.map_err(|e| format!("Failed to set clipboard image: {}", e))
9861008
}
9871009

1010+
fn get_clipboard_string() -> Result<String, String> {
1011+
let mut clipboard: ClipboardContext = ClipboardProvider::new()
1012+
.map_err(|e| format!("Failed to initialize clipboard: {}", e))?;
1013+
clipboard
1014+
.get_contents()
1015+
.map_err(|e| format!("Failed to read clipboard contents: {}", e))
1016+
}
1017+
9881018
static FAILED_TEMP_FILE: LazyLock<NamedTempFile> =
9891019
LazyLock::new(|| temp_asset!("../assets/failed.mp3"));
9901020

@@ -1591,6 +1621,16 @@ async fn main() -> Result<(), Box<dyn Error>> {
15911621
}))
15921622
.build().unwrap(),
15931623

1624+
ChatCompletionFunctionsArgs::default()
1625+
.name("get_clipboard")
1626+
.description("Returns the current clipboard text.")
1627+
.parameters(json!({
1628+
"type": "object",
1629+
"properties": {},
1630+
"required": [],
1631+
}))
1632+
.build().unwrap(),
1633+
15941634
ChatCompletionFunctionsArgs::default()
15951635
.name("qr_to_clipboard")
15961636
.description("Copies a QR code image for the given text to the clipboard.")

0 commit comments

Comments
 (0)