some opt + load directly from a store
This commit is contained in:
parent
cbd6a49181
commit
d36ddfd61c
13 changed files with 157 additions and 1409 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
[build]
|
||||
rustc-wrapper = "sccache"
|
||||
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
|
|
@ -22,7 +22,8 @@
|
|||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"tailwindcss": "^4.0.2",
|
||||
"tauri-plugin-macos-permissions-api": "^2.0.4"
|
||||
"tauri-plugin-macos-permissions-api": "^2.0.4",
|
||||
"zod": "^3.24.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.3.1",
|
||||
|
|
|
|||
1370
src-tauri/Cargo.lock
generated
1370
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -5,6 +5,9 @@ description = "A Tauri App"
|
|||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
[profile.dev]
|
||||
incremental = true # Compile your binary in smaller steps.
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
|
|
@ -29,14 +32,10 @@ cpal = "0.15.3"
|
|||
whisper-rs = { version = "0.13.2", features = ["whisper-cpp-log"] }
|
||||
anyhow = "1.0.95"
|
||||
rubato = "0.16.1"
|
||||
samplerate = "0.2.4"
|
||||
rig-core = "0.8.0"
|
||||
env_logger = "0.11.6"
|
||||
log = "0.4.25"
|
||||
tokio = "1.43.0"
|
||||
vad-rs = "0.1.5"
|
||||
tauri-plugin-upload = "2"
|
||||
tauri-plugin-stronghold = "2"
|
||||
tauri-plugin-store = "2"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default",
|
||||
"store:default",
|
||||
"global-shortcut:allow-is-registered",
|
||||
"global-shortcut:allow-register",
|
||||
"global-shortcut:allow-unregister",
|
||||
|
|
|
|||
|
|
@ -1,25 +1,18 @@
|
|||
mod managers;
|
||||
mod shortcut;
|
||||
|
||||
use log::info;
|
||||
use managers::audio::AudioRecordingManager;
|
||||
use managers::transcription::TranscriptionManager;
|
||||
use rdev::{simulate, EventType, Key, SimulateError};
|
||||
use std::sync::Arc;
|
||||
use std::{thread, time};
|
||||
use tauri::tray::TrayIconBuilder;
|
||||
use tauri::{AppHandle, Manager};
|
||||
use tauri::Manager;
|
||||
use tauri_plugin_autostart::MacosLauncher;
|
||||
use tauri_plugin_global_shortcut::{Code, Modifiers, Shortcut, ShortcutEvent, ShortcutState};
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
env_logger::init();
|
||||
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_stronghold::Builder::new(|_pass| todo!()).build())
|
||||
.plugin(tauri_plugin_upload::init())
|
||||
// .plugin(tauri_plugin_global_shortcut::Builder::new().build())
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_autostart::init(
|
||||
MacosLauncher::LaunchAgent,
|
||||
|
|
@ -27,6 +20,7 @@ pub fn run() {
|
|||
))
|
||||
.plugin(tauri_plugin_macos_permissions::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_store::Builder::default().build())
|
||||
.setup(move |app| {
|
||||
let _tray = TrayIconBuilder::new().build(app)?;
|
||||
|
||||
|
|
@ -57,7 +51,7 @@ pub fn run() {
|
|||
app.manage(recording_manager.clone());
|
||||
app.manage(transcription_manager.clone());
|
||||
|
||||
shortcut::enable_shortcut(app);
|
||||
shortcut::init_shortcuts(app);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
|
|
|
|||
0
src-tauri/src/settings.rs
Normal file
0
src-tauri/src/settings.rs
Normal file
|
|
@ -3,6 +3,8 @@ use std::thread;
|
|||
use std::time;
|
||||
|
||||
use rdev::{simulate, EventType, Key, SimulateError};
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use tauri::App;
|
||||
use tauri::AppHandle;
|
||||
use tauri::Manager;
|
||||
|
|
@ -14,6 +16,41 @@ use tauri_plugin_store::{JsonValue, StoreExt};
|
|||
use crate::managers::audio::AudioRecordingManager;
|
||||
use crate::managers::transcription::TranscriptionManager;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)] // Clone is useful
|
||||
pub struct ShortcutBinding {
|
||||
id: String,
|
||||
name: String,
|
||||
description: String,
|
||||
default_binding: String,
|
||||
current_binding: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct AppSettings {
|
||||
bindings: Vec<ShortcutBinding>,
|
||||
}
|
||||
|
||||
fn get_default_settings() -> AppSettings {
|
||||
AppSettings {
|
||||
bindings: vec![
|
||||
ShortcutBinding {
|
||||
id: "transcribe".to_string(),
|
||||
name: "Transcribe".to_string(),
|
||||
description: "Converts your speech into text.".to_string(),
|
||||
default_binding: "alt+space".to_string(),
|
||||
current_binding: "alt+space".to_string(),
|
||||
},
|
||||
ShortcutBinding {
|
||||
id: "test".to_string(),
|
||||
name: "Test".to_string(),
|
||||
description: "This is a test binding.".to_string(),
|
||||
default_binding: "ctrl+d".to_string(),
|
||||
current_binding: "ctrl+d".to_string(),
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
fn try_send_event(event: &EventType) {
|
||||
if let Err(SimulateError) = simulate(event) {
|
||||
println!("We could not send {:?}", event);
|
||||
|
|
@ -82,7 +119,25 @@ fn transcribe_released(app: &AppHandle) {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn enable_shortcut(app: &App) {
|
||||
pub fn init_shortcuts(app: &App) {
|
||||
// init store
|
||||
let kb_store = app
|
||||
.store("settings_store.json")
|
||||
.expect("Failed to initialize store");
|
||||
|
||||
if let Some(bindings) = kb_store.get("settings") {
|
||||
// print the bindings that exist
|
||||
println!("Bindings: {:?}", bindings);
|
||||
} else {
|
||||
kb_store.set(
|
||||
"settings",
|
||||
serde_json::to_value(&get_default_settings()).unwrap(),
|
||||
);
|
||||
// create the default bindings
|
||||
}
|
||||
|
||||
// load state from store
|
||||
|
||||
_register_shortcut_upon_start(
|
||||
app,
|
||||
"alt+space"
|
||||
|
|
|
|||
0
src-tauri/src/utils.rs
Normal file
0
src-tauri/src/utils.rs
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,44 +1,42 @@
|
|||
import React from "react";
|
||||
|
||||
interface Shortcut {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
keys: string[];
|
||||
}
|
||||
import React, { useEffect } from "react";
|
||||
import { load } from "@tauri-apps/plugin-store";
|
||||
import { SettingsSchema, ShortcutBinding } from "../../lib/types";
|
||||
|
||||
export const KeyboardShortcuts: React.FC = () => {
|
||||
// These would normally come from the backend configuration
|
||||
const shortcuts: Shortcut[] = [
|
||||
{
|
||||
id: "transcribe",
|
||||
name: "Transcribe",
|
||||
description: "Convert speech to text",
|
||||
keys: ["⌃", "Space"],
|
||||
},
|
||||
];
|
||||
const [bindings, setBindings] = React.useState<ShortcutBinding[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
load("settings_store.json", { autoSave: false }).then((r) => {
|
||||
console.log("loaded store", r);
|
||||
|
||||
r.get("settings").then((s) => {
|
||||
const settings = SettingsSchema.parse(s);
|
||||
setBindings(settings.bindings);
|
||||
});
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{shortcuts.map((shortcut) => (
|
||||
{bindings.map((binding) => (
|
||||
<div
|
||||
key={shortcut.id}
|
||||
key={binding.id}
|
||||
className="flex items-center justify-between p-4 rounded-lg border border-gray-200 hover:bg-gray-50"
|
||||
>
|
||||
<div>
|
||||
<h3 className="text-sm font-medium text-gray-900">
|
||||
{shortcut.name}
|
||||
{binding.name}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-500">{shortcut.description}</p>
|
||||
<p className="text-sm text-gray-500">{binding.description}</p>
|
||||
</div>
|
||||
<div className="flex items-center space-x-1">
|
||||
{shortcut.keys.map((key, index) => (
|
||||
<React.Fragment key={index}>
|
||||
<kbd className="px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded">
|
||||
{key}
|
||||
</kbd>
|
||||
</React.Fragment>
|
||||
))}
|
||||
<React.Fragment>
|
||||
{/* <kbd className="px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded"> */}
|
||||
<div className="px-2 py-1 text-sm font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded">
|
||||
{binding.current_binding}
|
||||
</div>
|
||||
{/* </kbd> */}
|
||||
</React.Fragment>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
|||
16
src/lib/types.ts
Normal file
16
src/lib/types.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const ShortcutBindingSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
description: z.string(),
|
||||
default_binding: z.string(),
|
||||
current_binding: z.string(),
|
||||
});
|
||||
|
||||
export const SettingsSchema = z.object({
|
||||
bindings: z.array(ShortcutBindingSchema),
|
||||
});
|
||||
|
||||
export type ShortcutBinding = z.infer<typeof ShortcutBindingSchema>;
|
||||
export type Settings = z.infer<typeof SettingsSchema>;
|
||||
Loading…
Reference in a new issue