Skip to content

Commit 51cc33c

Browse files
committed
fix hooks
1 parent dde5b15 commit 51cc33c

File tree

16 files changed

+283
-109
lines changed

16 files changed

+283
-109
lines changed

Cargo.lock

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ serde_bytes = "0.11.15"
177177
serde_json = "1"
178178
serde_qs = "1.0.0-rc.3"
179179
serde_yaml = "0.9"
180+
shellexpand = "3"
180181
similar = "2.7.0"
181182
statig = "0.4"
182183
strum = "0.26"

apps/desktop/src/store/zustand/listener/general.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
import { getName } from "@tauri-apps/api/app";
1+
import { getIdentifier } from "@tauri-apps/api/app";
22
import { appDataDir } from "@tauri-apps/api/path";
33
import { Effect, Exit } from "effect";
44
import { create as mutate } from "mutative";
55
import type { StoreApi } from "zustand";
66

7+
import { commands as detectCommands } from "@hypr/plugin-detect";
78
import { commands as hooksCommands } from "@hypr/plugin-hooks";
89
import {
910
commands as listenerCommands,
@@ -201,18 +202,30 @@ export const createGeneralSlice = <
201202
}),
202203
);
203204

204-
Promise.all([appDataDir(), getName().catch(() => "com.hyprnote.app")])
205-
.then(([dataDirPath, appName]) => {
205+
Promise.all([
206+
appDataDir(),
207+
detectCommands
208+
.listMicUsingApplications()
209+
.then((r) =>
210+
r.status === "ok" ? r.data.map((app) => app.id) : null,
211+
),
212+
getIdentifier().catch(() => "com.hyprnote.stable"),
213+
])
214+
.then(([dataDirPath, micUsingApps, bundleId]) => {
206215
const sessionPath = `${dataDirPath}/hyprnote/sessions/${targetSessionId}`;
207-
return hooksCommands.runEventHooks({
208-
beforeListeningStarted: {
209-
args: {
210-
resource_dir: sessionPath,
211-
app_hyprnote: appName,
212-
app_meeting: null,
216+
const app_meeting = micUsingApps?.[0] ?? null;
217+
218+
hooksCommands
219+
.runEventHooks({
220+
beforeListeningStarted: {
221+
args: {
222+
resource_dir: sessionPath,
223+
app_hyprnote: bundleId,
224+
app_meeting,
225+
},
213226
},
214-
},
215-
});
227+
})
228+
.catch(console.error);
216229
})
217230
.catch((error) => {
218231
console.error("[hooks] BeforeListeningStarted failed:", error);
@@ -274,15 +287,15 @@ export const createGeneralSlice = <
274287
if (sessionId) {
275288
Promise.all([
276289
appDataDir(),
277-
getName().catch(() => "com.hyprnote.app"),
290+
getIdentifier().catch(() => "com.hyprnote.app"),
278291
])
279-
.then(([dataDirPath, appName]) => {
292+
.then(([dataDirPath, bundleId]) => {
280293
const sessionPath = `${dataDirPath}/hyprnote/sessions/${sessionId}`;
281294
return hooksCommands.runEventHooks({
282295
afterListeningStopped: {
283296
args: {
284297
resource_dir: sessionPath,
285-
app_hyprnote: appName,
298+
app_hyprnote: bundleId,
286299
app_meeting: null,
287300
},
288301
},

apps/web/content/docs/developers/4.hooks.mdx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ exit 0
4343
Every hook command receives additional CLI flags based on the event type. For listening-related hooks, these include:
4444

4545
- `--resource-dir`: Path to the session's resource directory
46-
- `--app-hyprnote`: Application identifier (e.g., `com.hyprnote.app`)
47-
- `--app-meeting`: Optional meeting-specific data (if available)
46+
- `--app-hyprnote`: Hyprnote application identifier (e.g., `com.hyprnote.stable`)
47+
- `--app-meeting`: Optional Meeting application identifier (if available)
4848

4949
Your scripts can parse these arguments to access session metadata, or simply ignore them if not needed.
5050

@@ -59,17 +59,14 @@ cp scripts/yabai.sh "$HOME/Library/Application Support/hyprnote/hooks/yabai.sh"
5959
chmod +x "$HOME/Library/Application Support/hyprnote/hooks/yabai.sh"
6060
```
6161

62-
Then configure your hooks to automatically tile Hyprnote when recording starts and stops:
62+
Then configure your hooks to automatically tile Hyprnote when recording starts:
6363

6464
```json
6565
{
6666
"version": 0,
6767
"hooks": {
6868
"beforeListeningStarted": [
69-
{ "command": "$HOME/Library/Application Support/hyprnote/hooks/yabai.sh --app Hyprnote --position left" }
70-
],
71-
"afterListeningStopped": [
72-
{ "command": "$HOME/Library/Application Support/hyprnote/hooks/yabai.sh --app Hyprnote --position right" }
69+
{ "command": "$HOME/dev/hyprnote/scripts/yabai.sh" }
7370
]
7471
}
7572
}

crates/detect/src/list/macos.rs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,36 +47,53 @@ pub fn list_installed_apps() -> Vec<InstalledApp> {
4747

4848
#[cfg(target_os = "macos")]
4949
pub fn list_mic_using_apps() -> Vec<InstalledApp> {
50-
let processes = ca::System::processes().ok().unwrap();
50+
let Some(processes) = ca::System::processes().ok() else {
51+
return Vec::new();
52+
};
5153

5254
let mut out = Vec::<InstalledApp>::new();
5355
for p in processes {
54-
if !p.is_running_input().unwrap_or(false) {
56+
if let Ok(false) = p.is_running_input() {
5557
continue;
5658
}
5759

58-
if let Ok(pid) = p.pid() {
59-
if let Some(running_app) = cidre::ns::RunningApp::with_pid(pid) {
60-
let bundle_id = running_app
61-
.bundle_id()
62-
.map(|s| s.to_string())
63-
.unwrap_or_default();
64-
let localized_name = running_app
65-
.localized_name()
66-
.map(|s| s.to_string())
67-
.unwrap_or_default();
68-
69-
out.push(InstalledApp {
70-
id: bundle_id,
71-
name: localized_name,
72-
});
73-
}
60+
let bundle_id = p
61+
.bundle_id()
62+
.ok()
63+
.map(|s| s.to_string())
64+
.unwrap_or_default();
65+
if bundle_id.is_empty() {
66+
continue;
7467
}
68+
69+
let (resolved_bundle_id, name) = resolve_to_parent_app(&bundle_id);
70+
71+
out.push(InstalledApp {
72+
id: resolved_bundle_id,
73+
name,
74+
});
7575
}
7676

7777
out
7878
}
7979

80+
fn resolve_to_parent_app(bundle_id: &str) -> (String, String) {
81+
let try_resolve = |id: &str| -> Option<String> {
82+
cidre::ns::RunningApp::with_bundle_id(&cidre::ns::String::with_str(id))
83+
.first()
84+
.and_then(|app| app.localized_name().map(|s| s.to_string()))
85+
};
86+
87+
if let Some((parent, _)) = bundle_id.rsplit_once('.') {
88+
if let Some(name) = try_resolve(parent) {
89+
return (parent.to_string(), name);
90+
}
91+
}
92+
93+
let name = try_resolve(bundle_id).unwrap_or_else(|| bundle_id.to_string());
94+
(bundle_id.to_string(), name)
95+
}
96+
8097
fn get_app_info(app_path: &std::path::Path) -> Option<InstalledApp> {
8198
let info_plist_path = app_path.join("Contents/Info.plist");
8299

crates/detect/src/list/mod.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,39 @@
22
mod macos;
33

44
#[cfg(target_os = "macos")]
5-
pub use macos::{list_installed_apps, list_mic_using_apps};
5+
pub use macos::list_installed_apps;
66

77
#[cfg(target_os = "linux")]
88
mod linux;
99

1010
#[cfg(target_os = "linux")]
11-
pub use linux::{list_installed_apps, list_mic_using_apps};
11+
pub use linux::list_installed_apps;
1212

1313
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
1414
pub fn list_installed_apps() -> Vec<InstalledApp> {
1515
Vec::new()
1616
}
1717

18-
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
1918
pub fn list_mic_using_apps() -> Vec<InstalledApp> {
20-
Vec::new()
19+
let apps = {
20+
#[cfg(target_os = "macos")]
21+
{
22+
macos::list_mic_using_apps()
23+
}
24+
#[cfg(target_os = "linux")]
25+
{
26+
linux::list_mic_using_apps()
27+
}
28+
29+
#[cfg(not(any(target_os = "macos", target_os = "linux")))]
30+
{
31+
Vec::new()
32+
}
33+
};
34+
35+
apps.into_iter()
36+
.filter(|app| !app.id.to_lowercase().contains("hyprnote"))
37+
.collect()
2138
}
2239

2340
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type)]

plugins/hooks/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ tauri-plugin = { workspace = true, features = ["build"] }
1212

1313
[dev-dependencies]
1414
specta-typescript = { workspace = true }
15+
tokio = { workspace = true, features = ["macros", "rt"] }
1516

1617
hypr-docs = { workspace = true }
1718

@@ -24,6 +25,8 @@ serde_json = { workspace = true }
2425
serde_yaml = { workspace = true }
2526
specta = { workspace = true }
2627

28+
dirs = { workspace = true }
2729
futures-util = { workspace = true }
30+
shellexpand = { workspace = true }
2831
thiserror = { workspace = true }
2932
tokio = { workspace = true, features = ["process"] }

plugins/hooks/js/bindings.gen.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88

99
export const commands = {
10-
async runEventHooks(event: HookEvent) : Promise<Result<null, string>> {
10+
async runEventHooks(event: HookEvent) : Promise<Result<HookResult[], string>> {
1111
try {
1212
return { status: "ok", data: await TAURI_INVOKE("plugin:hooks|run_event_hooks", { event }) };
1313
} catch (e) {
@@ -68,6 +68,7 @@ export type HookDefinition = {
6868
*/
6969
command: string }
7070
export type HookEvent = { afterListeningStopped: { args: AfterListeningStoppedArgs } } | { beforeListeningStarted: { args: BeforeListeningStartedArgs } }
71+
export type HookResult = { command: string; success: boolean; exit_code: number | null; stdout: string; stderr: string }
7172
/**
7273
* Configuration for hook execution.
7374
*/

plugins/hooks/src/commands.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use crate::{event::HookEvent, HooksPluginExt};
1+
use crate::{event::HookEvent, runner::HookResult, HooksPluginExt};
22

33
#[tauri::command]
44
#[specta::specta]
55
pub(crate) async fn run_event_hooks<R: tauri::Runtime>(
66
app: tauri::AppHandle<R>,
77
event: HookEvent,
8-
) -> Result<(), String> {
9-
app.run_hooks(event).map_err(|e| e.to_string())
8+
) -> Result<Vec<HookResult>, String> {
9+
app.run_hooks(event).await.map_err(|e| e.to_string())
1010
}

plugins/hooks/src/config.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,11 @@ impl HooksConfig {
4040
Ok(config)
4141
}
4242

43-
fn config_path<R: tauri::Runtime>(app: &impl tauri::Manager<R>) -> crate::Result<PathBuf> {
44-
let app_data_dir = app
45-
.path()
46-
.app_data_dir()
47-
.map_err(|e| crate::Error::ConfigLoad(e.to_string()))?;
43+
fn config_path<R: tauri::Runtime>(_app: &impl tauri::Manager<R>) -> crate::Result<PathBuf> {
44+
let data_dir =
45+
dirs::data_dir().ok_or_else(|| crate::Error::ConfigLoad("no data dir".to_string()))?;
4846

49-
Ok(app_data_dir.join("hyprnote").join("hooks.json"))
47+
Ok(data_dir.join("hyprnote").join("hooks.json"))
5048
}
5149

5250
fn empty() -> Self {

0 commit comments

Comments
 (0)