@valni/cli-darwin-x64 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/LICENSE +1 -2
  2. package/package.json +1 -1
  3. package/runtime/LICENSE +1 -2
  4. package/runtime/examples/extensions/auto-commit-on-exit.ts +6 -6
  5. package/runtime/examples/extensions/bash-spawn-hook.ts +3 -3
  6. package/runtime/examples/extensions/bookmark.ts +5 -5
  7. package/runtime/examples/extensions/border-status-editor.ts +7 -7
  8. package/runtime/examples/extensions/built-in-tool-renderer.ts +6 -6
  9. package/runtime/examples/extensions/claude-rules.ts +4 -4
  10. package/runtime/examples/extensions/commands.ts +5 -5
  11. package/runtime/examples/extensions/confirm-destructive.ts +3 -3
  12. package/runtime/examples/extensions/custom-footer.ts +2 -2
  13. package/runtime/examples/extensions/custom-header.ts +5 -5
  14. package/runtime/examples/extensions/custom-provider-anthropic/index.ts +4 -4
  15. package/runtime/examples/extensions/custom-provider-anthropic/package-lock.json +4 -4
  16. package/runtime/examples/extensions/custom-provider-anthropic/package.json +3 -3
  17. package/runtime/examples/extensions/dirty-repo-guard.ts +7 -7
  18. package/runtime/examples/extensions/doom-overlay/doom/build.sh +4 -4
  19. package/runtime/examples/extensions/doom-overlay/doom/{doomgeneric_pi.c → doomgeneric_valni.c} +1 -1
  20. package/runtime/examples/extensions/doom-overlay/index.ts +3 -3
  21. package/runtime/examples/extensions/dynamic-resources/dynamic.json +0 -1
  22. package/runtime/examples/extensions/dynamic-resources/index.ts +2 -4
  23. package/runtime/examples/extensions/dynamic-tools.ts +4 -4
  24. package/runtime/examples/extensions/entry-renderer.ts +6 -6
  25. package/runtime/examples/extensions/event-bus.ts +8 -8
  26. package/runtime/examples/extensions/file-trigger.ts +3 -3
  27. package/runtime/examples/extensions/git-checkpoint.ts +7 -7
  28. package/runtime/examples/extensions/git-merge-and-resolve.ts +14 -14
  29. package/runtime/examples/extensions/github-issue-autocomplete.ts +6 -6
  30. package/runtime/examples/extensions/hello.ts +2 -2
  31. package/runtime/examples/extensions/hidden-thinking-label.ts +4 -4
  32. package/runtime/examples/extensions/inline-bash.ts +5 -5
  33. package/runtime/examples/extensions/input-transform-streaming.ts +5 -5
  34. package/runtime/examples/extensions/input-transform.ts +5 -5
  35. package/runtime/examples/extensions/interactive-shell.ts +3 -3
  36. package/runtime/examples/extensions/mac-system-theme.ts +5 -5
  37. package/runtime/examples/extensions/message-renderer.ts +4 -4
  38. package/runtime/examples/extensions/minimal-mode.ts +9 -9
  39. package/runtime/examples/extensions/modal-editor.ts +3 -3
  40. package/runtime/examples/extensions/model-status.ts +3 -3
  41. package/runtime/examples/extensions/notify.ts +5 -5
  42. package/runtime/examples/extensions/overlay-qa-tests.ts +20 -20
  43. package/runtime/examples/extensions/overlay-test.ts +3 -3
  44. package/runtime/examples/extensions/permission-gate.ts +2 -2
  45. package/runtime/examples/extensions/pirate.ts +4 -4
  46. package/runtime/examples/extensions/plan-mode/index.ts +21 -21
  47. package/runtime/examples/extensions/preset.ts +26 -26
  48. package/runtime/examples/extensions/project-trust.ts +7 -7
  49. package/runtime/examples/extensions/prompt-customizer.ts +3 -3
  50. package/runtime/examples/extensions/protected-paths.ts +2 -2
  51. package/runtime/examples/extensions/provider-payload.ts +3 -3
  52. package/runtime/examples/extensions/question.ts +2 -2
  53. package/runtime/examples/extensions/questionnaire.ts +2 -2
  54. package/runtime/examples/extensions/rainbow-editor.ts +3 -3
  55. package/runtime/examples/extensions/reload-runtime.ts +4 -4
  56. package/runtime/examples/extensions/rpc-demo.ts +10 -10
  57. package/runtime/examples/extensions/sandbox/index.ts +15 -15
  58. package/runtime/examples/extensions/sandbox/package-lock.json +4 -4
  59. package/runtime/examples/extensions/sandbox/package.json +3 -3
  60. package/runtime/examples/extensions/send-user-message.ts +13 -13
  61. package/runtime/examples/extensions/session-name.ts +4 -4
  62. package/runtime/examples/extensions/shutdown-command.ts +10 -10
  63. package/runtime/examples/extensions/snake.ts +3 -3
  64. package/runtime/examples/extensions/space-invaders.ts +3 -3
  65. package/runtime/examples/extensions/ssh.ts +12 -12
  66. package/runtime/examples/extensions/status-line.ts +4 -4
  67. package/runtime/examples/extensions/structured-output.ts +2 -2
  68. package/runtime/examples/extensions/subagent/index.ts +5 -5
  69. package/runtime/examples/extensions/system-prompt-header.ts +3 -3
  70. package/runtime/examples/extensions/tic-tac-toe.ts +15 -15
  71. package/runtime/examples/extensions/timed-confirm.ts +4 -4
  72. package/runtime/examples/extensions/titlebar-spinner.ts +11 -11
  73. package/runtime/examples/extensions/todo.ts +5 -5
  74. package/runtime/examples/extensions/tool-override.ts +5 -5
  75. package/runtime/examples/extensions/tools.ts +10 -10
  76. package/runtime/examples/extensions/trigger-compact.ts +3 -3
  77. package/runtime/examples/extensions/truncated-tool.ts +3 -3
  78. package/runtime/examples/extensions/widget-placement.ts +2 -2
  79. package/runtime/examples/extensions/with-deps/index.ts +2 -2
  80. package/runtime/examples/extensions/with-deps/package-lock.json +4 -4
  81. package/runtime/examples/extensions/with-deps/package.json +3 -3
  82. package/runtime/examples/extensions/working-indicator.ts +7 -7
  83. package/runtime/examples/extensions/working-message-test.ts +3 -3
  84. package/runtime/examples/sdk/01-minimal.ts +1 -1
  85. package/runtime/examples/sdk/03-custom-prompt.ts +1 -1
  86. package/runtime/examples/sdk/04-skills.ts +1 -1
  87. package/runtime/examples/sdk/05-tools.ts +1 -1
  88. package/runtime/examples/sdk/06-extensions.ts +11 -11
  89. package/runtime/examples/sdk/08-prompt-templates.ts +1 -1
  90. package/runtime/export-html/template.js +3 -3
  91. package/runtime/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  92. package/runtime/package.json +8 -14
  93. package/runtime/theme/dark.json +0 -1
  94. package/runtime/theme/light.json +0 -1
  95. package/runtime/theme/theme-schema.json +2 -2
  96. package/runtime/valni +2 -2
  97. package/runtime/README.md +0 -674
  98. package/runtime/docs/extensions.md +0 -2910
  99. package/runtime/docs/json.md +0 -82
  100. package/runtime/docs/models.md +0 -540
  101. package/runtime/docs/packages.md +0 -228
  102. package/runtime/docs/rpc.md +0 -1480
  103. package/runtime/docs/sdk.md +0 -1183
  104. package/runtime/docs/session-format.md +0 -422
  105. package/runtime/docs/settings.md +0 -315
  106. package/runtime/docs/shell-aliases.md +0 -13
  107. package/runtime/docs/terminal-setup.md +0 -142
  108. package/runtime/docs/termux.md +0 -127
  109. package/runtime/docs/tmux.md +0 -63
  110. package/runtime/docs/tui.md +0 -927
  111. package/runtime/docs/windows.md +0 -17
  112. package/runtime/examples/README.md +0 -25
  113. package/runtime/examples/extensions/README.md +0 -212
  114. package/runtime/examples/extensions/doom-overlay/README.md +0 -46
  115. package/runtime/examples/extensions/dynamic-resources/SKILL.md +0 -8
  116. package/runtime/examples/extensions/dynamic-resources/dynamic.md +0 -5
  117. package/runtime/examples/extensions/gondolin/index.ts +0 -531
  118. package/runtime/examples/extensions/gondolin/package-lock.json +0 -185
  119. package/runtime/examples/extensions/gondolin/package.json +0 -19
  120. package/runtime/examples/extensions/plan-mode/README.md +0 -66
  121. package/runtime/examples/extensions/subagent/README.md +0 -175
  122. package/runtime/examples/extensions/subagent/agents/planner.md +0 -37
  123. package/runtime/examples/extensions/subagent/agents/reviewer.md +0 -35
  124. package/runtime/examples/extensions/subagent/agents/scout.md +0 -50
  125. package/runtime/examples/extensions/subagent/agents/worker.md +0 -24
  126. package/runtime/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
  127. package/runtime/examples/extensions/subagent/prompts/implement.md +0 -10
  128. package/runtime/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
  129. package/runtime/examples/sdk/README.md +0 -140
  130. package/runtime/node_modules/@mariozechner/clipboard/.yarnrc.yml +0 -1
  131. package/runtime/node_modules/@mariozechner/clipboard/Cargo.toml +0 -45
  132. package/runtime/node_modules/@mariozechner/clipboard/README.md +0 -60
  133. package/runtime/node_modules/@mariozechner/clipboard/build.rs +0 -5
  134. package/runtime/node_modules/@mariozechner/clipboard/clipboard.darwin-x64.node +0 -0
  135. package/runtime/node_modules/@mariozechner/clipboard/exp.ts +0 -12
  136. package/runtime/node_modules/@mariozechner/clipboard/index.js +0 -606
  137. package/runtime/node_modules/@mariozechner/clipboard/package.json +0 -56
  138. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/common.rs +0 -307
  139. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/mod.rs +0 -93
  140. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/ios.rs +0 -313
  141. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/macos.rs +0 -471
  142. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/mod.rs +0 -252
  143. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/wayland.rs +0 -431
  144. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/win.rs +0 -712
  145. package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/x11.rs +0 -916
  146. package/runtime/node_modules/@mariozechner/clipboard/src/lib.rs +0 -194
  147. package/runtime/node_modules/@mariozechner/clipboard-darwin-x64/README.md +0 -3
  148. package/runtime/node_modules/@mariozechner/clipboard-darwin-x64/clipboard.darwin-x64.node +0 -0
  149. package/runtime/node_modules/@mariozechner/clipboard-darwin-x64/package.json +0 -22
@@ -1,252 +0,0 @@
1
- #[cfg(target_os = "ios")]
2
- mod ios;
3
- #[cfg(target_os = "ios")]
4
- pub use ios::{ClipboardContext, ClipboardWatcherContext, WatcherShutdown};
5
- #[cfg(target_os = "macos")]
6
- mod macos;
7
- #[cfg(target_os = "macos")]
8
- pub use macos::{ClipboardContext, ClipboardWatcherContext, WatcherShutdown};
9
- #[cfg(target_os = "windows")]
10
- mod win;
11
- #[cfg(target_os = "windows")]
12
- pub use win::{ClipboardContext, ClipboardWatcherContext, WatcherShutdown};
13
-
14
- // Linux: runtime detection between Wayland and X11
15
- #[cfg(all(
16
- unix,
17
- not(any(
18
- target_os = "macos",
19
- target_os = "ios",
20
- target_os = "android",
21
- target_os = "emscripten"
22
- ))
23
- ))]
24
- mod x11;
25
-
26
- #[cfg(all(
27
- unix,
28
- not(any(
29
- target_os = "macos",
30
- target_os = "ios",
31
- target_os = "android",
32
- target_os = "emscripten"
33
- )),
34
- feature = "wayland"
35
- ))]
36
- mod wayland;
37
-
38
- #[cfg(all(
39
- unix,
40
- not(any(
41
- target_os = "macos",
42
- target_os = "ios",
43
- target_os = "android",
44
- target_os = "emscripten"
45
- ))
46
- ))]
47
- pub use x11::ClipboardContextX11Options;
48
-
49
- #[cfg(all(
50
- unix,
51
- not(any(
52
- target_os = "macos",
53
- target_os = "ios",
54
- target_os = "android",
55
- target_os = "emscripten"
56
- ))
57
- ))]
58
- mod linux_clipboard {
59
- use crate::clipboard_rs::{
60
- common::Result, Clipboard, ClipboardContent, ClipboardHandler, ContentFormat,
61
- };
62
- #[cfg(feature = "image")]
63
- use crate::RustImageData;
64
-
65
- pub enum ClipboardContext {
66
- X11(super::x11::ClipboardContext),
67
- #[cfg(feature = "wayland")]
68
- Wayland(super::wayland::ClipboardContext),
69
- }
70
-
71
- impl ClipboardContext {
72
- pub fn new() -> Result<Self> {
73
- #[cfg(feature = "wayland")]
74
- {
75
- if std::env::var_os("WAYLAND_DISPLAY").is_some() {
76
- match super::wayland::ClipboardContext::new() {
77
- Ok(ctx) => return Ok(Self::Wayland(ctx)),
78
- Err(e) => {
79
- eprintln!("Wayland clipboard init failed, falling back to X11: {}", e);
80
- }
81
- }
82
- }
83
- }
84
- Ok(Self::X11(super::x11::ClipboardContext::new()?))
85
- }
86
- }
87
-
88
- macro_rules! dispatch {
89
- ($self:expr, $method:ident $(, $arg:expr)*) => {
90
- match $self {
91
- ClipboardContext::X11(ctx) => ctx.$method($($arg),*),
92
- #[cfg(feature = "wayland")]
93
- ClipboardContext::Wayland(ctx) => ctx.$method($($arg),*),
94
- }
95
- };
96
- }
97
-
98
- impl Clipboard for ClipboardContext {
99
- fn available_formats(&self) -> Result<Vec<String>> {
100
- dispatch!(self, available_formats)
101
- }
102
-
103
- fn has(&self, format: ContentFormat) -> bool {
104
- dispatch!(self, has, format)
105
- }
106
-
107
- fn clear(&self) -> Result<()> {
108
- dispatch!(self, clear)
109
- }
110
-
111
- fn get_buffer(&self, format: &str) -> Result<Vec<u8>> {
112
- dispatch!(self, get_buffer, format)
113
- }
114
-
115
- fn get_text(&self) -> Result<String> {
116
- dispatch!(self, get_text)
117
- }
118
-
119
- fn get_rich_text(&self) -> Result<String> {
120
- dispatch!(self, get_rich_text)
121
- }
122
-
123
- fn get_html(&self) -> Result<String> {
124
- dispatch!(self, get_html)
125
- }
126
-
127
- #[cfg(feature = "image")]
128
- fn get_image(&self) -> Result<RustImageData> {
129
- dispatch!(self, get_image)
130
- }
131
-
132
- fn get_files(&self) -> Result<Vec<String>> {
133
- dispatch!(self, get_files)
134
- }
135
-
136
- fn get(&self, formats: &[ContentFormat]) -> Result<Vec<ClipboardContent>> {
137
- dispatch!(self, get, formats)
138
- }
139
-
140
- fn set_buffer(&self, format: &str, buffer: Vec<u8>) -> Result<()> {
141
- dispatch!(self, set_buffer, format, buffer)
142
- }
143
-
144
- fn set_text(&self, text: String) -> Result<()> {
145
- dispatch!(self, set_text, text)
146
- }
147
-
148
- fn set_rich_text(&self, text: String) -> Result<()> {
149
- dispatch!(self, set_rich_text, text)
150
- }
151
-
152
- fn set_html(&self, html: String) -> Result<()> {
153
- dispatch!(self, set_html, html)
154
- }
155
-
156
- #[cfg(feature = "image")]
157
- fn set_image(&self, image: RustImageData) -> Result<()> {
158
- dispatch!(self, set_image, image)
159
- }
160
-
161
- fn set_files(&self, files: Vec<String>) -> Result<()> {
162
- dispatch!(self, set_files, files)
163
- }
164
-
165
- fn set(&self, contents: Vec<ClipboardContent>) -> Result<()> {
166
- dispatch!(self, set, contents)
167
- }
168
- }
169
-
170
- unsafe impl Send for ClipboardContext {}
171
-
172
- pub enum ClipboardWatcherContext<T: ClipboardHandler> {
173
- X11(super::x11::ClipboardWatcherContext<T>),
174
- #[cfg(feature = "wayland")]
175
- Wayland(super::wayland::ClipboardWatcherContext<T>),
176
- }
177
-
178
- impl<T: ClipboardHandler> ClipboardWatcherContext<T> {
179
- pub fn new() -> Result<Self> {
180
- #[cfg(feature = "wayland")]
181
- {
182
- if std::env::var_os("WAYLAND_DISPLAY").is_some() {
183
- match super::wayland::ClipboardWatcherContext::new() {
184
- Ok(ctx) => return Ok(Self::Wayland(ctx)),
185
- Err(e) => {
186
- eprintln!("Wayland watcher init failed, falling back to X11: {}", e);
187
- }
188
- }
189
- }
190
- }
191
- Ok(Self::X11(super::x11::ClipboardWatcherContext::new()?))
192
- }
193
- }
194
-
195
- impl<T: ClipboardHandler + Send> crate::ClipboardWatcher<T> for ClipboardWatcherContext<T> {
196
- fn add_handler(&mut self, f: T) -> &mut Self {
197
- match self {
198
- Self::X11(ctx) => {
199
- ctx.handlers.push(f);
200
- }
201
- #[cfg(feature = "wayland")]
202
- Self::Wayland(ctx) => {
203
- ctx.handlers.push(f);
204
- }
205
- }
206
- self
207
- }
208
-
209
- fn start_watch(&mut self) {
210
- match self {
211
- Self::X11(ctx) => ctx.start_watch_inner(),
212
- #[cfg(feature = "wayland")]
213
- Self::Wayland(ctx) => ctx.start_watch_inner(),
214
- }
215
- }
216
-
217
- fn get_shutdown_channel(&self) -> WatcherShutdown {
218
- match self {
219
- Self::X11(ctx) => WatcherShutdown {
220
- _shutdown: Box::new(ctx.get_shutdown_channel()),
221
- },
222
- #[cfg(feature = "wayland")]
223
- Self::Wayland(ctx) => WatcherShutdown {
224
- _shutdown: Box::new(ctx.get_shutdown_channel()),
225
- },
226
- }
227
- }
228
- }
229
-
230
- unsafe impl<T: ClipboardHandler + Send> Send for ClipboardWatcherContext<T> {}
231
-
232
- pub struct WatcherShutdown {
233
- _shutdown: Box<dyn std::any::Any + Send>,
234
- }
235
-
236
- impl WatcherShutdown {
237
- pub fn stop(self) {
238
- drop(self);
239
- }
240
- }
241
- }
242
-
243
- #[cfg(all(
244
- unix,
245
- not(any(
246
- target_os = "macos",
247
- target_os = "ios",
248
- target_os = "android",
249
- target_os = "emscripten"
250
- ))
251
- ))]
252
- pub use linux_clipboard::{ClipboardContext, ClipboardWatcherContext, WatcherShutdown};
@@ -1,431 +0,0 @@
1
- use crate::clipboard_rs::{
2
- common::Result, Clipboard, ClipboardContent, ClipboardHandler, ContentFormat,
3
- };
4
-
5
- #[cfg(feature = "image")]
6
- use crate::clipboard_rs::{common::RustImage, RustImageData};
7
-
8
- use std::io::Read;
9
- use std::sync::mpsc::{self, Receiver, Sender};
10
- use std::time::Duration;
11
- use wl_clipboard_rs::{
12
- copy::{self, MimeSource, MimeType, Options, Source},
13
- paste::{self, get_contents, get_mime_types, ClipboardType, Seat},
14
- utils::is_primary_selection_supported,
15
- };
16
-
17
- const MIME_HTML: &str = "text/html";
18
- const MIME_RTF: &str = "text/rtf";
19
- #[cfg(feature = "image")]
20
- const MIME_PNG: &str = "image/png";
21
- const MIME_URI_LIST: &str = "text/uri-list";
22
- const FILE_PATH_PREFIX: &str = "file://";
23
-
24
- fn read_wayland_clipboard(mime: paste::MimeType) -> Result<Vec<u8>> {
25
- let result = get_contents(ClipboardType::Regular, Seat::Unspecified, mime);
26
- match result {
27
- Ok((mut pipe, _)) => {
28
- let mut buffer = vec![];
29
- pipe
30
- .read_to_end(&mut buffer)
31
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })?;
32
- Ok(buffer)
33
- }
34
- Err(paste::Error::ClipboardEmpty) | Err(paste::Error::NoMimeType) => {
35
- Err("Clipboard is empty or content type not available".into())
36
- }
37
- Err(e) => Err(e.to_string().into()),
38
- }
39
- }
40
-
41
- fn write_wayland_clipboard(sources: Vec<MimeSource>) -> Result<()> {
42
- let mut opts = Options::new();
43
- opts.foreground(false);
44
- opts.clipboard(copy::ClipboardType::Regular);
45
- opts
46
- .copy_multi(sources)
47
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })?;
48
- Ok(())
49
- }
50
-
51
- fn get_offered_mime_types() -> Result<std::collections::HashSet<String>> {
52
- get_mime_types(ClipboardType::Regular, Seat::Unspecified)
53
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })
54
- }
55
-
56
- pub struct ClipboardContext;
57
-
58
- impl ClipboardContext {
59
- pub fn new() -> Result<Self> {
60
- match is_primary_selection_supported() {
61
- Ok(_) => Ok(Self),
62
- Err(e) => Err(e.to_string().into()),
63
- }
64
- }
65
- }
66
-
67
- impl Clipboard for ClipboardContext {
68
- fn available_formats(&self) -> Result<Vec<String>> {
69
- let mime_types = get_offered_mime_types()?;
70
- Ok(mime_types.into_iter().collect())
71
- }
72
-
73
- fn has(&self, format: ContentFormat) -> bool {
74
- #[allow(unreachable_patterns)]
75
- let mime_to_check = match format {
76
- ContentFormat::Text => "text/plain",
77
- ContentFormat::Html => MIME_HTML,
78
- ContentFormat::Rtf => MIME_RTF,
79
- #[cfg(feature = "image")]
80
- ContentFormat::Image => MIME_PNG,
81
- ContentFormat::Files => MIME_URI_LIST,
82
- ContentFormat::Other(ref s) => s.as_str(),
83
- #[cfg(not(feature = "image"))]
84
- _ => return false,
85
- };
86
- if let Ok(mime_types) = get_offered_mime_types() {
87
- // For text, also check text/plain;charset=utf-8
88
- if mime_to_check == "text/plain" {
89
- mime_types.iter().any(|m| m.starts_with("text/plain"))
90
- } else {
91
- mime_types.contains(mime_to_check)
92
- }
93
- } else {
94
- false
95
- }
96
- }
97
-
98
- fn clear(&self) -> Result<()> {
99
- copy::clear(copy::ClipboardType::Regular, copy::Seat::All)
100
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })?;
101
- Ok(())
102
- }
103
-
104
- fn get_buffer(&self, format: &str) -> Result<Vec<u8>> {
105
- read_wayland_clipboard(paste::MimeType::Specific(format))
106
- }
107
-
108
- fn get_text(&self) -> Result<String> {
109
- let bytes = read_wayland_clipboard(paste::MimeType::Text)?;
110
- String::from_utf8(bytes)
111
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })
112
- }
113
-
114
- fn get_rich_text(&self) -> Result<String> {
115
- let bytes = read_wayland_clipboard(paste::MimeType::Specific(MIME_RTF))?;
116
- String::from_utf8(bytes)
117
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })
118
- }
119
-
120
- fn get_html(&self) -> Result<String> {
121
- let bytes = read_wayland_clipboard(paste::MimeType::Specific(MIME_HTML))?;
122
- String::from_utf8(bytes)
123
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })
124
- }
125
-
126
- #[cfg(feature = "image")]
127
- fn get_image(&self) -> Result<RustImageData> {
128
- let bytes = read_wayland_clipboard(paste::MimeType::Specific(MIME_PNG))?;
129
- RustImageData::from_bytes(&bytes)
130
- }
131
-
132
- fn get_files(&self) -> Result<Vec<String>> {
133
- let bytes = read_wayland_clipboard(paste::MimeType::Specific(MIME_URI_LIST))?;
134
- let text = String::from_utf8(bytes)
135
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })?;
136
- Ok(
137
- text
138
- .lines()
139
- .map(|line| line.trim_end_matches('\r'))
140
- .filter(|line| {
141
- !line.starts_with('#') && !line.is_empty() && line.starts_with(FILE_PATH_PREFIX)
142
- })
143
- .map(|line| line.to_string())
144
- .collect(),
145
- )
146
- }
147
-
148
- fn get(&self, formats: &[ContentFormat]) -> Result<Vec<ClipboardContent>> {
149
- let mut contents = Vec::new();
150
- for format in formats {
151
- #[allow(unreachable_patterns)]
152
- match format {
153
- ContentFormat::Text => {
154
- if let Ok(text) = self.get_text() {
155
- contents.push(ClipboardContent::Text(text));
156
- }
157
- }
158
- ContentFormat::Html => {
159
- if let Ok(html) = self.get_html() {
160
- contents.push(ClipboardContent::Html(html));
161
- }
162
- }
163
- ContentFormat::Rtf => {
164
- if let Ok(rtf) = self.get_rich_text() {
165
- contents.push(ClipboardContent::Rtf(rtf));
166
- }
167
- }
168
- #[cfg(feature = "image")]
169
- ContentFormat::Image => {
170
- if let Ok(image) = self.get_image() {
171
- contents.push(ClipboardContent::Image(image));
172
- }
173
- }
174
- ContentFormat::Files => {
175
- if let Ok(files) = self.get_files() {
176
- contents.push(ClipboardContent::Files(files));
177
- }
178
- }
179
- ContentFormat::Other(mime) => {
180
- if let Ok(data) = self.get_buffer(mime) {
181
- contents.push(ClipboardContent::Other(mime.clone(), data));
182
- }
183
- }
184
- #[cfg(not(feature = "image"))]
185
- _ => {}
186
- }
187
- }
188
- Ok(contents)
189
- }
190
-
191
- fn set_buffer(&self, format: &str, buffer: Vec<u8>) -> Result<()> {
192
- write_wayland_clipboard(vec![MimeSource {
193
- source: Source::Bytes(buffer.into_boxed_slice()),
194
- mime_type: MimeType::Specific(format.to_string()),
195
- }])
196
- }
197
-
198
- fn set_text(&self, text: String) -> Result<()> {
199
- write_wayland_clipboard(vec![MimeSource {
200
- source: Source::Bytes(text.into_bytes().into_boxed_slice()),
201
- mime_type: MimeType::Text,
202
- }])
203
- }
204
-
205
- fn set_rich_text(&self, text: String) -> Result<()> {
206
- write_wayland_clipboard(vec![MimeSource {
207
- source: Source::Bytes(text.into_bytes().into_boxed_slice()),
208
- mime_type: MimeType::Specific(MIME_RTF.to_string()),
209
- }])
210
- }
211
-
212
- fn set_html(&self, html: String) -> Result<()> {
213
- write_wayland_clipboard(vec![MimeSource {
214
- source: Source::Bytes(html.into_bytes().into_boxed_slice()),
215
- mime_type: MimeType::Specific(MIME_HTML.to_string()),
216
- }])
217
- }
218
-
219
- #[cfg(feature = "image")]
220
- fn set_image(&self, image: RustImageData) -> Result<()> {
221
- let bytes = image.to_png()?;
222
- write_wayland_clipboard(vec![MimeSource {
223
- source: Source::Bytes(bytes.get_bytes().into()),
224
- mime_type: MimeType::Specific(MIME_PNG.to_string()),
225
- }])
226
- }
227
-
228
- fn set_files(&self, files: Vec<String>) -> Result<()> {
229
- // Normalize paths to file:// URIs and use CRLF for text/uri-list
230
- let uri_list: Vec<String> = files
231
- .into_iter()
232
- .map(|f| {
233
- if f.starts_with(FILE_PATH_PREFIX) {
234
- f
235
- } else {
236
- format!("{FILE_PATH_PREFIX}{f}")
237
- }
238
- })
239
- .collect();
240
- let data = uri_list.join("\r\n");
241
- write_wayland_clipboard(vec![MimeSource {
242
- source: Source::Bytes(data.into_bytes().into_boxed_slice()),
243
- mime_type: MimeType::Specific(MIME_URI_LIST.to_string()),
244
- }])
245
- }
246
-
247
- fn set(&self, contents: Vec<ClipboardContent>) -> Result<()> {
248
- let mut sources = Vec::new();
249
- for content in contents {
250
- #[allow(unreachable_patterns)]
251
- match content {
252
- ClipboardContent::Text(text) => {
253
- sources.push(MimeSource {
254
- source: Source::Bytes(text.into_bytes().into_boxed_slice()),
255
- mime_type: MimeType::Text,
256
- });
257
- }
258
- ClipboardContent::Html(html) => {
259
- sources.push(MimeSource {
260
- source: Source::Bytes(html.into_bytes().into_boxed_slice()),
261
- mime_type: MimeType::Specific(MIME_HTML.to_string()),
262
- });
263
- }
264
- ClipboardContent::Rtf(rtf) => {
265
- sources.push(MimeSource {
266
- source: Source::Bytes(rtf.into_bytes().into_boxed_slice()),
267
- mime_type: MimeType::Specific(MIME_RTF.to_string()),
268
- });
269
- }
270
- #[cfg(feature = "image")]
271
- ClipboardContent::Image(image) => {
272
- let bytes = image.to_png()?;
273
- sources.push(MimeSource {
274
- source: Source::Bytes(bytes.get_bytes().into()),
275
- mime_type: MimeType::Specific(MIME_PNG.to_string()),
276
- });
277
- }
278
- ClipboardContent::Files(files) => {
279
- let uri_list: Vec<String> = files
280
- .into_iter()
281
- .map(|f| {
282
- if f.starts_with(FILE_PATH_PREFIX) {
283
- f
284
- } else {
285
- format!("{FILE_PATH_PREFIX}{f}")
286
- }
287
- })
288
- .collect();
289
- let data = uri_list.join("\r\n");
290
- sources.push(MimeSource {
291
- source: Source::Bytes(data.into_bytes().into_boxed_slice()),
292
- mime_type: MimeType::Specific(MIME_URI_LIST.to_string()),
293
- });
294
- }
295
- ClipboardContent::Other(mime, data) => {
296
- sources.push(MimeSource {
297
- source: Source::Bytes(data.into_boxed_slice()),
298
- mime_type: MimeType::Specific(mime),
299
- });
300
- }
301
- #[cfg(not(feature = "image"))]
302
- _ => {}
303
- }
304
- }
305
- write_wayland_clipboard(sources)
306
- }
307
- }
308
-
309
- unsafe impl Send for ClipboardContext {}
310
-
311
- // Polling-based clipboard watcher for Wayland
312
- pub struct ClipboardWatcherContext<T: ClipboardHandler> {
313
- pub(crate) handlers: Vec<T>,
314
- pub(crate) stop_signal: Sender<()>,
315
- stop_receiver: Receiver<()>,
316
- }
317
-
318
- unsafe impl<T: ClipboardHandler + Send> Send for ClipboardWatcherContext<T> {}
319
-
320
- impl<T: ClipboardHandler> ClipboardWatcherContext<T> {
321
- pub fn new() -> Result<Self> {
322
- // Verify data-control protocol is available (same check as ClipboardContext)
323
- is_primary_selection_supported()
324
- .map_err(|e| -> Box<dyn std::error::Error + Send + Sync> { e.to_string().into() })?;
325
- let (tx, rx) = mpsc::channel();
326
- Ok(Self {
327
- handlers: Vec::new(),
328
- stop_signal: tx,
329
- stop_receiver: rx,
330
- })
331
- }
332
- }
333
-
334
- impl<T: ClipboardHandler> ClipboardWatcherContext<T> {
335
- pub(crate) fn get_shutdown_channel(&self) -> WatcherShutdown {
336
- WatcherShutdown {
337
- sender: self.stop_signal.clone(),
338
- }
339
- }
340
-
341
- pub(crate) fn start_watch_inner(&mut self) {
342
- let mut last_mime_types: Vec<String> = Vec::new();
343
- let mut last_text = String::new();
344
-
345
- // Get initial clipboard state
346
- if let Ok(types) = get_offered_mime_types() {
347
- last_mime_types = {
348
- let mut v: Vec<String> = types.into_iter().collect();
349
- v.sort();
350
- v
351
- };
352
- }
353
- if let Ok(text) = read_wayland_clipboard(paste::MimeType::Text) {
354
- if let Ok(s) = String::from_utf8(text) {
355
- last_text = s;
356
- }
357
- }
358
-
359
- loop {
360
- if self
361
- .stop_receiver
362
- .recv_timeout(Duration::from_millis(500))
363
- .is_ok()
364
- {
365
- break;
366
- }
367
-
368
- let changed = if let Ok(types) = get_offered_mime_types() {
369
- let mut current_types: Vec<String> = types.into_iter().collect();
370
- current_types.sort();
371
-
372
- if current_types != last_mime_types {
373
- // MIME types changed, clipboard definitely changed
374
- last_mime_types = current_types;
375
- // Update text cache too
376
- if let Ok(bytes) = read_wayland_clipboard(paste::MimeType::Text) {
377
- if let Ok(s) = String::from_utf8(bytes) {
378
- last_text = s;
379
- }
380
- } else {
381
- last_text.clear();
382
- }
383
- true
384
- } else if let Ok(bytes) = read_wayland_clipboard(paste::MimeType::Text) {
385
- // Same MIME types, check if text content changed
386
- if let Ok(current) = String::from_utf8(bytes) {
387
- if current != last_text {
388
- last_text = current;
389
- true
390
- } else {
391
- false
392
- }
393
- } else {
394
- false
395
- }
396
- } else if !last_text.is_empty() {
397
- last_text.clear();
398
- true
399
- } else {
400
- false
401
- }
402
- } else {
403
- // No clipboard content available
404
- if !last_mime_types.is_empty() {
405
- last_mime_types.clear();
406
- last_text.clear();
407
- true
408
- } else {
409
- false
410
- }
411
- };
412
-
413
- if changed {
414
- self
415
- .handlers
416
- .iter_mut()
417
- .for_each(|handler| handler.on_clipboard_change());
418
- }
419
- }
420
- }
421
- }
422
-
423
- pub struct WatcherShutdown {
424
- pub(crate) sender: Sender<()>,
425
- }
426
-
427
- impl Drop for WatcherShutdown {
428
- fn drop(&mut self) {
429
- let _ = self.sender.send(());
430
- }
431
- }