1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
//! Disable default browser shortcuts in your Tauri app, e.g. `F3` or `Ctrl+J`.

#![cfg(not(any(target_os = "android", target_os = "ios")))]

use bitflags::bitflags;
use tauri::plugin::TauriPlugin;
use tauri::Runtime;

bitflags! {
  #[derive(Clone, Copy, Debug)]
  pub struct Flags: u32 {
      /// Find (`Ctrl+F`, `Ctrl+G`, `Ctrl+Shift+G`, `F3`)
      const FIND          = 1 << 0;
      /// Caret browsing (`F7`)
      const CARET_BROWSING  = 1 << 1;
      /// Developer tools (`Ctrl+Shift+I`)
      const DEV_TOOLS       = 1 << 2;
      /// Downloads (`Ctrl+J`)
      const DOWNLOADS       = 1 << 3;
      /// Focus move (`Shift+Tab`)
      const FOCUS_MOVE      = 1 << 4;
      /// Reload (`F5`, `Ctrl+F5`, `Shift+F5`, `Ctrl+R`, `Ctrl+Shift+R`)
      const RELOAD          = 1 << 5;
      /// Source (`Ctrl+U`)
      const SOURCE          = 1 << 7;
      /// Open (`Ctrl+O`)
      const OPEN            = 1 << 8;
      /// Print document (`Ctrl+P`, `Ctrl+Shift+P`)
      const PRINT           = 1 << 9;
      /// Context menu (mouse right click)
      const CONTEXT_MENU    = 1 << 10;
  }
}

impl Default for Flags {
  fn default() -> Self {
    Self::all()
  }
}

#[derive(Default)]
pub struct Builder {
  flags: Flags,
}

impl Builder {
  pub fn new() -> Self {
    Self::default()
  }

  /// Set flags to control which shortcuts the plugin should disable.
  pub fn with_flags(mut self, flags: Flags) -> Self {
    self.flags = flags;
    self
  }

  pub fn build<R: Runtime>(self) -> TauriPlugin<R> {
    let mut js = String::new();

    if self.flags.contains(Flags::FIND) {
      js.push_str("onKey('F3');");
      js.push_str("onKey(['f', 'F', 'g', 'G'], { ctrlKey: true });");
      js.push_str("onKey(['g', 'G'], { ctrlKey: true, shiftKey: true });");
    }

    if self.flags.contains(Flags::CARET_BROWSING) {
      js.push_str("onKey('F7');");
    }

    if self.flags.contains(Flags::DEV_TOOLS) {
      js.push_str("onKey(['i', 'I'], { ctrlKey: true, shiftKey: true });");
    }

    if self.flags.contains(Flags::DOWNLOADS) {
      js.push_str("onKey(['j', 'J'], { ctrlKey: true });");
    }

    if self.flags.contains(Flags::FOCUS_MOVE) {
      js.push_str("onKey('Tab', { shiftKey: true });");
    }

    if self.flags.contains(Flags::RELOAD) {
      js.push_str("onKey('F5');");
      js.push_str("onKey('F5', { ctrlKey: true });");
      js.push_str("onKey('F5', { shiftKey: true });");
      js.push_str("onKey(['r', 'R'], { ctrlKey: true });");
      js.push_str("onKey(['r', 'R'], { ctrlKey: true, shiftKey: true });");
    }

    if self.flags.contains(Flags::SOURCE) {
      js.push_str("onKey(['u', 'U'], { ctrlKey: true });");
    }

    if self.flags.contains(Flags::OPEN) {
      js.push_str("onKey(['o', 'O'], { ctrlKey: true });");
    }

    if self.flags.contains(Flags::PRINT) {
      js.push_str("onKey(['p', 'P'], { ctrlKey: true });");
      js.push_str("onKey(['p', 'P'], { ctrlKey: true, shiftKey: true });");
    }

    if self.flags.contains(Flags::CONTEXT_MENU) {
      js.push_str(
        "globalThis.addEventListener('contextmenu', (e) => {
          e.preventDefault();
        });",
      );
    }

    let script = include_str!("script.js")
      .trim()
      .replace("//REPLACE_ME", &js);

    tauri::plugin::Builder::new("prevent-default")
      .js_init_script(script)
      .build()
  }
}

/// Initialize the plugin with default values.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
  Builder::default().build()
}