Skip to content

Conversation

@yujonglee
Copy link
Contributor

No description provided.

Add a new "hooks" tauri plugin to the workspace to provide a simple ping command and expose its API to the desktop app.

- Register tauri-plugin-hooks in the workspace Cargo.toml and desktop package.json/Cargo.toml.
- Add the plugin to desktop capabilities and initialize it in the tauri lib.rs plugin list.
- Add a new plugins/hooks crate with Cargo.toml, build.rs, Rust source (commands, ext, error, lib) and JS bindings/package.json to generate TypeScript bindings.

This change is needed to introduce the new hooks plugin (ping command), wire it into the monorepo workspace and the desktop app, and enable codegen for its TypeScript bindings.
v
v
Add HooksConfig struct and loader for hooks.json

Introduce HooksConfig and HookDefinition types to manage hook configuration
stored in hooks.json. Implement loading from the application's data directory,
with robust error handling for missing files, read failures, parse errors and
unsupported version numbers. Provide helper methods for determining the
config path and producing an empty default config.

This change was needed to implement the plan's to-dos for hook configuration
management so the application can discover and validate hook definitions at
runtime without editing the plan file itself.
Re-add hooks runner and execution logic

Restore the hooks runner that was accidentally removed and reapply hooks handling for events. The new runner loads HooksConfig, looks up hooks for an event, and spawns asynchronous tasks to execute each hook so hook execution does not block the main thread.

Also add execute_hook which splits a command string, spawns a tokio process with the configured args, forwards CLI args, and returns an error if the command fails. This ensures hooks are executed reliably and failures are reported.
@netlify
Copy link

netlify bot commented Nov 19, 2025

Deploy Preview for hyprnote ready!

Name Link
🔨 Latest commit 51e6824
🔍 Latest deploy log https://app.netlify.com/projects/hyprnote/deploys/691d6e54322c3b0008df0858
😎 Deploy Preview https://deploy-preview-1730--hyprnote.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

📝 Walkthrough

Walkthrough

Introduces a new Tauri plugin tauri-plugin-hooks, registers it in workspace and app manifests, initializes it in the desktop Tauri builder, and implements configuration, command handlers, event types, hook execution, docs extraction, web docs, and UI listing for hooks.

Changes

Cohort / File(s) Change Summary
Workspace & app manifests
\Cargo.toml`, `apps/desktop/package.json`, `apps/desktop/src-tauri/Cargo.toml``
Added tauri-plugin-hooks (workspace path) and serde_yaml dependency; added @hypr/plugin-hooks workspace dependency to desktop package.json.
Desktop app integration
\apps/desktop/src-tauri/capabilities/default.json`, `apps/desktop/src-tauri/src/lib.rs``
Added "hooks:default" capability and registered tauri_plugin_hooks::init() in the Tauri builder.
Plugin project files
\plugins/hooks/.gitignore`, `plugins/hooks/Cargo.toml`, `plugins/hooks/package.json`, `plugins/hooks/tsconfig.json`, `plugins/hooks/js/index.ts``
New plugin scaffold: manifests, packaging, TS config, gitignore, and JS re-export of generated bindings.
Plugin build & codegen
\plugins/hooks/build.rs`, `plugins/hooks/src/docs.rs``
Added build script registering CLI commands and TS AST parser to extract hook metadata and generate docs from bindings.
Plugin core implementation (Rust)
\plugins/hooks/src/lib.rs`, `plugins/hooks/src/error.rs`, `plugins/hooks/src/event.rs`, `plugins/hooks/src/config.rs`, `plugins/hooks/src/commands.rs`, `plugins/hooks/src/runner.rs`, `plugins/hooks/src/ext.rs``
Implemented plugin public API (init), error types, HookEvent and arg types, config loading/validation, tauri command run_event_hooks, extension trait for run_hooks, and async hook runner that spawns external processes.
Web docs & site integration
\apps/web/content/docs/hooks.mdx`, `apps/web/content/docs/hooks/*.mdx`, `apps/web/content-collections.ts`, `apps/web/src/components/hooks-list.tsx`, `apps/web/src/routes/_view/docs/-components.tsx``
Added documentation pages for hooks, per-hook MDX, content collection for hooks, a HooksList React component, and registered the component for MDX rendering.
Listener plugin wiring
\plugins/listener/Cargo.toml`, `apps/desktop/src/store/zustand/listener/general.ts``
Added tauri-plugin-hooks dependency to listener plugin; desktop listener store now invokes beforeListeningStarted and afterListeningStopped hooks around start/stop flows.
Misc scripts
\scripts/yabai.sh``
Added yabai helper script for focusing and repositioning an app window.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant App as Desktop app
    participant Hooks as tauri-plugin-hooks
    participant Config as hooks.json (config)
    participant Runner as Hook runner (tokio)
    participant Proc as External process

    App->>Hooks: call run_hooks(event) / emit HookEvent
    Hooks->>Config: HooksConfig::load(app)
    Config-->>Hooks: HooksConfig { version, hooks }
    Hooks->>Runner: run_hooks_for_event(event)
    alt matching hooks
        Runner->>Proc: spawn hook command with event CLI args (async)
        note right of Proc `#DDFFDD`: executes independently\nreturns exit status later
        Proc-->>Runner: exit status (error -> HookExecution)
        Runner-->>Hooks: return Ok(()) (after spawn)
    else no hooks
        Runner-->>Hooks: return Ok(())
    end
    Hooks-->>App: propagation of Result (Ok / Error)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Areas to focus during review:
    • plugins/hooks/src/docs.rs — SWC-based TypeScript parsing, JSDoc extraction, and mapping to hook docs.
    • plugins/hooks/src/runner.rs — command parsing, process spawn/await semantics, error mapping and background task behavior.
    • plugins/hooks/src/config.rs — config path resolution, JSON parsing, version validation and error mapping.
    • apps/desktop/src/store/zustand/listener/general.ts — integration points where hooks are invoked synchronously within lifecycle flows.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.81% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate relatedness to the changeset. Add a description explaining the purpose, scope, and key aspects of the hooks plugin introduction.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'introduce-hooks-plugin' directly describes the main change: introducing a new hooks plugin to the codebase, which aligns with the comprehensive additions of the tauri-plugin-hooks plugin infrastructure.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch introduce-hooks-plugin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (9)
plugins/hooks/Cargo.toml (2)

4-4: Update the placeholder author information.

The authors field contains a placeholder value that should be replaced with actual author information.

Apply this diff:

-authors = ["You"]
+authors = ["Your Name <your.email@example.com>"]

8-8: Add a description for the plugin.

The description field is empty but should contain a brief explanation of the plugin's purpose.

Apply this diff:

-description = ""
+description = "Tauri plugin for lifecycle hook execution"
plugins/hooks/src/event.rs (1)

36-43: Consider reducing code duplication.

Both AfterListeningStoppedArgs and BeforeListeningStartedArgs have identical structures and implementations. If they're expected to remain identical, consider using a macro or a shared implementation to reduce duplication.

Example approach using a macro:

macro_rules! define_session_args {
    ($name:ident, $doc:expr) => {
        #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
        #[doc = $doc]
        pub struct $name {
            /// Unique identifier for the listening session
            pub session_id: String,
        }

        impl HookArgs for $name {
            fn to_cli_args(&self) -> Vec<OsString> {
                vec![
                    OsString::from("--session-id"),
                    OsString::from(&self.session_id),
                ]
            }
        }
    };
}

define_session_args!(AfterListeningStoppedArgs, "Arguments passed to hooks triggered after listening stops");
define_session_args!(BeforeListeningStartedArgs, "Arguments passed to hooks triggered before listening starts");

Also applies to: 52-59

apps/web/content/docs/hooks.mdx (1)

1-29: Consider adding cross-platform path guidance.

The documentation is clear and well-structured. However, the configuration paths shown are macOS-specific ($HOME/Library/Application Support/hyprnote/). Consider adding a note about platform-specific paths or including examples for other supported platforms (Windows, Linux).

Example addition:

# Config

The configuration path varies by platform:
- macOS: `$HOME/Library/Application Support/hyprnote/hooks.json`
- Linux: `$HOME/.config/hyprnote/hooks.json`
- Windows: `%APPDATA%\hyprnote\hooks.json`
plugins/hooks/src/runner.rs (1)

17-30: Surface hook execution failures and consider more robust command parsing

Right now the runner spawns all hooks in the background and then drops their results:

  • execute_hook returns crate::Result<()>, but join_all(futures).await is assigned to _ and any HookExecution errors are silently ignored.
  • From the caller’s perspective, run_hooks_for_event always returns Ok(()) as long as config loading succeeds, even if every hook command fails to spawn or exits with a non‑zero status.

That makes misconfigured hooks hard to debug. At minimum, consider logging individual errors from execute_hook so failures are visible:

-    tauri::async_runtime::spawn(async move {
-        let _ = futures_util::future::join_all(futures).await;
-    });
+    tauri::async_runtime::spawn(async move {
+        for result in futures_util::future::join_all(futures).await {
+            if let Err(err) = result {
+                eprintln!("hooks: failed to execute hook: {err}");
+            }
+        }
+    });

Separately, command.split_whitespace() is quite restrictive:

  • Paths with spaces (e.g. "/Applications/My Tool/bin/hook") and quoted arguments are not supported.
  • Everything after the first whitespace is treated as fixed args, making it impossible to express richer argument structures.

If you expect more complex commands, it may be worth either:

  • Representing hooks as { program: String, args: Vec<String> } in HookDefinition, or
  • Using a shell‑like splitter (e.g. shlex‑style) to honor quoting/escaping.

Also applies to: 33-57

plugins/hooks/src/lib.rs (1)

36-71: Be aware tests have side effects on the workspace tree

The export test is effectively a docs-generation harness:

  • export_types writes ./js/bindings.gen.ts inside the crate.
  • export_docs then reads that file and writes rendered docs into ../../apps/web/content/docs/hooks.

This is convenient for tooling, but it means cargo test -p tauri-plugin-hooks will mutate files outside the crate and can create or overwrite docs in the web app. If that’s not universally desired, consider gating this behind an env var or feature flag (e.g. only running when GENERATE_HOOK_DOCS=1) so regular test runs stay side‑effect free.

plugins/hooks/src/docs.rs (2)

16-38: Escape YAML frontmatter values in doc_render

HookInfo::doc_render writes raw name, description, and arg descriptions directly into a frontmatter block:

content.push_str(&format!("name: {}\n", self.name));
…
content.push_str(&format!("description: {}\n", desc));
…
content.push_str(&format!("    description: {}\n", desc));

If any of these contain :, #, quotes, or newlines, the resulting frontmatter may be invalid YAML and could confuse whatever consumes these .mdx files.

Consider at least quoting and basic escaping, e.g. replacing newlines and wrapping values in double quotes:

-        content.push_str(&format!("name: {}\n", self.name));
+        content.push_str(&format!("name: {:?}\n", self.name));-            content.push_str(&format!("description: {}\n", desc));
+            content.push_str(&format!("description: {:?}\n", desc));-                    content.push_str(&format!("    description: {}\n", desc));
+                    content.push_str(&format!("    description: {:?}\n", desc));

You can refine this to a dedicated helper if you want prettier output, but some level of YAML-safe quoting will make the generator more robust.


118-177: Make the hook discovery AST patterns more robust to TS shape changes

command_methods and method_arg_type_name currently assume a very specific structure:

  • export const commands = { … } where each property is a Prop::Method.
  • Each method’s first parameter is a plain Pat::Ident with a simple type_ann that resolves via extract_type_name.

If tauri_specta ever changes its generated commands object to use arrow functions or key‑value properties (e.g. beforeListeningStarted: (args: Foo) => …), or uses destructuring in the first parameter, these patterns will fail and parse_hooks will silently return no hooks (beyond the assert!(!hooks.is_empty()) in tests).

To make this more future‑proof, consider:

  • Supporting Prop::KeyValue with Expr::Arrow / Expr::Fn in addition to Prop::Method.
  • Extracting the first parameter type from function/arrow expressions the same way you do for method functions.
  • Adding a couple of focused unit tests over a frozen sample of bindings.gen.ts to catch regressions if the bindings generator output changes.

That will help keep docs generation stable across updates to the TS emitter.

plugins/hooks/src/config.rs (1)

5-13: Replace placeholder doc comments on HooksConfig with meaningful descriptions

The doc comments on the config type and its fields are currently numeric placeholders:

/// 123
/// 345
    pub version: u8,
    /// 678
    #[serde(default)]
    pub hooks: HashMap<String, Vec<HookDefinition>>,

Since this struct is exported via specta::Type, these comments are likely to surface in generated TypeScript and any downstream docs. It would be better either to:

  • Replace them with concise, accurate descriptions of what version and hooks mean, or
  • Remove them entirely until you have real documentation text.
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ddefe4 and ffe8c4b.

⛔ Files ignored due to path filters (7)
  • .cursor/commands/add-plugin.md is excluded by !**/.cursor/**
  • Cargo.lock is excluded by !**/*.lock
  • plugins/hooks/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/hooks/permissions/autogenerated/commands/after_listening_stopped.toml is excluded by !plugins/**/permissions/**
  • plugins/hooks/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/hooks/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/hooks/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
📒 Files selected for processing (22)
  • Cargo.toml (1 hunks)
  • apps/desktop/package.json (1 hunks)
  • apps/desktop/src-tauri/Cargo.toml (1 hunks)
  • apps/desktop/src-tauri/capabilities/default.json (1 hunks)
  • apps/desktop/src-tauri/src/lib.rs (1 hunks)
  • apps/web/content/docs/hooks.mdx (1 hunks)
  • apps/web/content/docs/hooks/afterListeningStopped.mdx (1 hunks)
  • apps/web/content/docs/hooks/beforeListeningStarted.mdx (1 hunks)
  • plugins/hooks/.gitignore (1 hunks)
  • plugins/hooks/Cargo.toml (1 hunks)
  • plugins/hooks/build.rs (1 hunks)
  • plugins/hooks/js/index.ts (1 hunks)
  • plugins/hooks/package.json (1 hunks)
  • plugins/hooks/src/commands.rs (1 hunks)
  • plugins/hooks/src/config.rs (1 hunks)
  • plugins/hooks/src/docs.rs (1 hunks)
  • plugins/hooks/src/error.rs (1 hunks)
  • plugins/hooks/src/event.rs (1 hunks)
  • plugins/hooks/src/ext.rs (1 hunks)
  • plugins/hooks/src/lib.rs (1 hunks)
  • plugins/hooks/src/runner.rs (1 hunks)
  • plugins/hooks/tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
plugins/hooks/src/ext.rs (1)
plugins/hooks/src/runner.rs (1)
  • run_hooks_for_event (5-31)
apps/desktop/src-tauri/src/lib.rs (1)
plugins/hooks/src/lib.rs (1)
  • init (27-34)
plugins/hooks/src/runner.rs (2)
plugins/hooks/src/config.rs (1)
  • load (21-39)
plugins/hooks/src/event.rs (2)
  • condition_key (10-15)
  • cli_args (17-22)
plugins/hooks/src/lib.rs (3)
apps/desktop/src-tauri/src/lib.rs (3)
  • make_specta_builder (143-151)
  • make_specta_builder (159-159)
  • export_types (158-168)
plugins/hooks/src/docs.rs (2)
  • new (222-227)
  • parse_hooks (58-63)
plugins/hooks/src/commands.rs (2)
  • before_listening_started (8-14)
  • after_listening_stopped (18-24)
plugins/hooks/build.rs (1)
apps/desktop/src-tauri/src/lib.rs (1)
  • main (11-141)
plugins/hooks/src/docs.rs (1)
plugins/hooks/src/lib.rs (2)
  • export (41-44)
  • init (27-34)
🔇 Additional comments (14)
plugins/hooks/tsconfig.json (2)

2-2: Verify base configuration file exists.

The configuration extends ../tsconfig.base.json. Ensure this file exists at plugins/tsconfig.base.json and contains the expected TypeScript compiler options.


3-3: The original review comment is incorrect. The include pattern ./js/*.ts is appropriate for the actual file structure. The plugins/hooks/js/ directory contains only a single TypeScript file (index.ts) directly in the root with no nested subdirectories. The pattern correctly matches the existing file organization, and no changes are required.

Likely an incorrect or invalid review comment.

apps/desktop/src-tauri/capabilities/default.json (1)

78-78: LGTM! Hooks permission added correctly.

The addition of "hooks:default" follows the established pattern for plugin permissions and is required for the hooks plugin to function properly.

plugins/hooks/.gitignore (1)

1-17: LGTM! Comprehensive gitignore patterns.

The gitignore file appropriately covers IDE artifacts, build outputs, dependencies, and Tauri-specific files.

plugins/hooks/src/error.rs (1)

1-24: LGTM! Well-structured error handling.

The error type is well-designed with:

  • Clear error variants for different failure modes
  • Descriptive error messages via thiserror
  • Custom Serialize implementation for API compatibility
plugins/hooks/src/ext.rs (1)

1-15: LGTM! Clean extension trait implementation.

The extension trait follows Rust best practices:

  • Blanket implementation for all Manager<R> types
  • Appropriate trait bounds
  • Clean delegation to the runner module
apps/desktop/package.json (1)

35-35: LGTM! Dependency added correctly.

The hooks plugin dependency follows the same pattern as other workspace plugins and is properly positioned in alphabetical order.

plugins/hooks/Cargo.toml (1)

16-18: I need to verify whether these SWC dependencies are actually used in other workspace crates' Cargo.toml files, not just the lock file.

Based on the verification results and workspace dependency best practices, the original review comment's recommendation is not appropriate for this codebase.

Cargo workspace dependencies are designed to declare dependencies on a workspace level and reference them in daughter crates, making it easier to keep versions of dependencies in sync when they are used by multiple crates. However, the script output shows that swc_common, swc_ecma_ast, and swc_ecma_parser are only referenced in Cargo.lock (the lock file), not in any other workspace crate's Cargo.toml.

This means these SWC dependencies are only used in plugins/hooks and are not shared with other workspace members. Moving them to workspace dependencies would provide no cross-crate consistency benefits and would not align with the documented purpose of workspace dependencies.

The review comment's suggestion is not applicable to this codebase.

Likely an incorrect or invalid review comment.

Cargo.toml (1)

116-116: LGTM!

The workspace dependency for tauri-plugin-hooks is correctly configured and follows the established pattern for other plugins in the workspace.

plugins/hooks/js/index.ts (1)

1-1: LGTM!

The re-export pattern for generated bindings is correct and follows standard Tauri plugin conventions.

apps/desktop/src-tauri/Cargo.toml (1)

32-32: LGTM!

The dependency is correctly added using workspace semantics and is properly positioned in alphabetical order.

apps/desktop/src-tauri/src/lib.rs (1)

53-53: LGTM!

The hooks plugin is correctly initialized following the standard Tauri plugin pattern and properly positioned in the plugin chain.

plugins/hooks/package.json (1)

1-11: Version compatibility confirmed.

The configuration is correct. @tauri-apps/api@2.9.0 is the JS API that matches the Tauri 2.9.x ecosystem, and the workspace consistently uses @tauri-apps/api: ^2.9.0 with tauri: 2.9, which aligns with the requirement to keep the @tauri-apps/api NPM package major and minor versions aligned with the tauri Rust crate.

plugins/hooks/src/commands.rs (1)

6-24: Commands wiring looks solid and matches the runner API

Both commands correctly wrap the args into HookEvent and delegate to app.run_hooks, mapping the plugin error type to String for Tauri. The generic R: tauri::Runtime usage is consistent with the rest of the plugin, and the fire‑and‑forget behavior from run_hooks is preserved here.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
plugins/hooks/src/event.rs (1)

31-36: Replace placeholder documentation on hook argument structs

The "123" / "345" doc comments are placeholders; replacing them with meaningful descriptions will make the hook events self‑documenting and easier to consume.

You can apply something along these lines:

 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
-/// 123
+/// Arguments passed to hooks triggered after listening stops
 pub struct AfterListeningStoppedArgs {
-    /// 345
+    /// Unique identifier for the listening session
     pub session_id: String,
 }
 
 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, specta::Type)]
-/// 123
+/// Arguments passed to hooks triggered before listening starts
 pub struct BeforeListeningStartedArgs {
-    /// 345
+    /// Unique identifier for the listening session
     pub session_id: String,
 }

Also applies to: 47-52

🧹 Nitpick comments (4)
scripts/yabai.sh (2)

23-23: Redirect error messages to stderr.

Error messages should be sent to stderr using >&2 rather than stdout. This allows proper separation of error output from regular output and enables better error handling by calling code.

- echo "Error: --app is required"
+ echo "Error: --app is required" >&2

Apply similar changes to lines 28 and 35.

Also applies to: 28-28, 35-35


32-32: Add dependency validation for robustness.

The script depends on yabai and jq but doesn't validate their availability. Add checks at the start to provide clear error messages if dependencies are missing.

+#!/bin/bash
+
+command -v yabai &> /dev/null || { echo "Error: yabai is not installed" >&2; exit 1; }
+command -v jq &> /dev/null || { echo "Error: jq is not installed" >&2; exit 1; }
+

This ensures users get immediate feedback if dependencies are missing.

Also applies to: 39-39, 42-42

plugins/hooks/build.rs (1)

1-5: Build script correctly registers the run_event_hooks command

The command list aligns with the implemented command and integrates cleanly with tauri_plugin::Builder; just remember to extend COMMANDS if additional commands are added later.

plugins/hooks/src/commands.rs (1)

1-10: Command wrapper around run_hooks is straightforward and correct

Delegating directly to app.run_hooks(event) with error stringification keeps the command minimal; there are no obvious logic issues. If you later need richer error handling across the plugin API, you might consider returning a structured error type instead of String.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ffe8c4b and 51e6824.

⛔ Files ignored due to path filters (8)
  • Cargo.lock is excluded by !**/*.lock
  • apps/web/src/routeTree.gen.ts is excluded by !**/*.gen.ts
  • plugins/hooks/js/bindings.gen.ts is excluded by !**/*.gen.ts
  • plugins/hooks/permissions/autogenerated/commands/run_event_hooks.toml is excluded by !plugins/**/permissions/**
  • plugins/hooks/permissions/autogenerated/reference.md is excluded by !plugins/**/permissions/**
  • plugins/hooks/permissions/default.toml is excluded by !plugins/**/permissions/**
  • plugins/hooks/permissions/schemas/schema.json is excluded by !plugins/**/permissions/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (16)
  • Cargo.toml (2 hunks)
  • apps/desktop/src/store/zustand/listener/general.ts (4 hunks)
  • apps/web/content-collections.ts (2 hunks)
  • apps/web/content/docs/hooks.mdx (1 hunks)
  • apps/web/content/docs/hooks/afterListeningStopped.mdx (1 hunks)
  • apps/web/content/docs/hooks/beforeListeningStarted.mdx (1 hunks)
  • apps/web/src/components/hooks-list.tsx (1 hunks)
  • apps/web/src/routes/_view/docs/-components.tsx (2 hunks)
  • plugins/hooks/Cargo.toml (1 hunks)
  • plugins/hooks/build.rs (1 hunks)
  • plugins/hooks/src/commands.rs (1 hunks)
  • plugins/hooks/src/docs.rs (1 hunks)
  • plugins/hooks/src/event.rs (1 hunks)
  • plugins/hooks/src/lib.rs (1 hunks)
  • plugins/listener/Cargo.toml (1 hunks)
  • scripts/yabai.sh (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • apps/web/content/docs/hooks/afterListeningStopped.mdx
  • apps/web/content/docs/hooks.mdx
🚧 Files skipped from review as they are similar to previous changes (4)
  • plugins/hooks/src/docs.rs
  • plugins/hooks/src/lib.rs
  • plugins/hooks/Cargo.toml
  • apps/web/content/docs/hooks/beforeListeningStarted.mdx
🧰 Additional context used
🧬 Code graph analysis (1)
plugins/hooks/build.rs (1)
apps/desktop/src-tauri/src/lib.rs (1)
  • main (11-141)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: ci (macos, macos-14)
  • GitHub Check: fmt
🔇 Additional comments (12)
scripts/yabai.sh (2)

32-32: Verify app name matching behavior.

The current implementation uses contains() for case-insensitive substring matching. This could match unintended apps; for example, searching for "code" would match "recode" or "visual-code". Confirm whether substring matching is the desired behavior or if you need exact or prefix matching.

If exact matching is needed, consider:

- window_id=$(yabai -m query --windows | jq -r --arg app "$app" 'map(select(.app | ascii_downcase | contains($app | ascii_downcase))) | .[0].id // empty')
+ window_id=$(yabai -m query --windows | jq -r --arg app "$app" 'map(select(.app | ascii_downcase == ($app | ascii_downcase))) | .[0].id // empty')

6-20: Argument parsing and validation structure looks solid.

The bash pattern for parsing flags is clean, and the early validation checks provide good error handling flow. The default value for position="left" (line 4) is a sensible choice.

apps/web/src/components/hooks-list.tsx (1)

1-9: LGTM!

The imports are correct and the early return pattern for empty hooks is a good practice.

apps/web/src/routes/_view/docs/-components.tsx (1)

18-18: LGTM!

The HooksList import and registration follow the existing pattern for custom MDX components.

Also applies to: 118-118

apps/web/content-collections.ts (2)

130-130: LGTM!

The exclude pattern correctly prevents the docs collection from processing hooks content, which is now handled by the dedicated hooks collection.


302-302: LGTM!

The hooks collection is correctly added to the collections array.

apps/desktop/src/store/zustand/listener/general.ts (3)

5-5: LGTM!

The import statement follows the existing pattern and naming convention used for listenerCommands.


249-275: Good sessionId capture pattern, but verify fire-and-forget hook invocation against plugin contract.

The sessionId capture at line 249 correctly preserves the value before the async stop operation. However, the afterListeningStopped hook follows an intentional fire-and-forget pattern (confirmed consistent with beforeListeningStarted at line 206)—both use .catch() without await.

The hooksCommands are imported from the external "@hypr/plugin-hooks" plugin. While less critical than the "before" hook (since it runs after stop succeeds), if the plugin's hooks are meant to complete before subsequent operations, rapid start/stop/start cycles could cause issues.

Verify against the plugin's documentation:

  1. Are hooks designed as fire-and-forget or should they be awaited?
  2. Should hook failures block the flow or only be logged?
  3. Are concurrent hook executions from quick cycles handled?

205-211: Verify external package API semantics for fire-and-forget hook pattern.

The hooksCommands.runEventHooks() method is from the external @hypr/plugin-hooks package (line 5). Investigation shows that fire-and-forget is the only pattern used in this codebase (both beforeListeningStarted and afterListeningStopped hooks at lines 206 and 268 use .catch() without await). This consistency suggests intentional design, particularly since the afterListeningStopped hook name implies a notification rather than a blocking operation.

However, verify against @hypr/plugin-hooks documentation whether this fire-and-forget pattern is the intended API usage, or if hooks should be awaited to ensure completion before proceeding.

plugins/listener/Cargo.toml (1)

32-32: Listener plugin dependency on tauri-plugin-hooks looks correct

Adding the workspace dependency cleanly wires the listener plugin to the new hooks crate; no manifest issues stand out.

Cargo.toml (1)

116-116: Workspace wiring for hooks plugin and YAML support looks consistent

Registering tauri-plugin-hooks in [workspace.dependencies] and adding a YAML serialization crate at the root matches the introduction of a shared hooks plugin and hook config; Cargo structure looks sound.

Also applies to: 165-165

plugins/hooks/src/event.rs (1)

1-29: Hook event variants and CLI arg mapping are coherent

HookEvent::condition_key and cli_args stay in sync with the specta renames, and the shared HookArgs trait keeps the CLI argument generation for both events uniform; no functional issues detected here.

Also applies to: 38-45, 54-61

@yujonglee yujonglee merged commit 3594d3d into main Nov 19, 2025
18 checks passed
@yujonglee yujonglee deleted the introduce-hooks-plugin branch November 19, 2025 07:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants