@valni/cli-darwin-x64 0.1.2 → 0.1.3
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.
- package/LICENSE +1 -2
- package/package.json +1 -1
- package/runtime/LICENSE +1 -2
- package/runtime/examples/extensions/auto-commit-on-exit.ts +6 -6
- package/runtime/examples/extensions/bash-spawn-hook.ts +3 -3
- package/runtime/examples/extensions/bookmark.ts +5 -5
- package/runtime/examples/extensions/border-status-editor.ts +7 -7
- package/runtime/examples/extensions/built-in-tool-renderer.ts +6 -6
- package/runtime/examples/extensions/claude-rules.ts +4 -4
- package/runtime/examples/extensions/commands.ts +5 -5
- package/runtime/examples/extensions/confirm-destructive.ts +3 -3
- package/runtime/examples/extensions/custom-footer.ts +2 -2
- package/runtime/examples/extensions/custom-header.ts +5 -5
- package/runtime/examples/extensions/custom-provider-anthropic/index.ts +4 -4
- package/runtime/examples/extensions/custom-provider-anthropic/package-lock.json +4 -4
- package/runtime/examples/extensions/custom-provider-anthropic/package.json +3 -3
- package/runtime/examples/extensions/dirty-repo-guard.ts +7 -7
- package/runtime/examples/extensions/doom-overlay/doom/build.sh +4 -4
- package/runtime/examples/extensions/doom-overlay/doom/{doomgeneric_pi.c → doomgeneric_valni.c} +1 -1
- package/runtime/examples/extensions/doom-overlay/index.ts +3 -3
- package/runtime/examples/extensions/dynamic-resources/dynamic.json +0 -1
- package/runtime/examples/extensions/dynamic-resources/index.ts +2 -4
- package/runtime/examples/extensions/dynamic-tools.ts +4 -4
- package/runtime/examples/extensions/entry-renderer.ts +6 -6
- package/runtime/examples/extensions/event-bus.ts +8 -8
- package/runtime/examples/extensions/file-trigger.ts +3 -3
- package/runtime/examples/extensions/git-checkpoint.ts +7 -7
- package/runtime/examples/extensions/git-merge-and-resolve.ts +14 -14
- package/runtime/examples/extensions/github-issue-autocomplete.ts +6 -6
- package/runtime/examples/extensions/hello.ts +2 -2
- package/runtime/examples/extensions/hidden-thinking-label.ts +4 -4
- package/runtime/examples/extensions/inline-bash.ts +5 -5
- package/runtime/examples/extensions/input-transform-streaming.ts +5 -5
- package/runtime/examples/extensions/input-transform.ts +5 -5
- package/runtime/examples/extensions/interactive-shell.ts +3 -3
- package/runtime/examples/extensions/mac-system-theme.ts +5 -5
- package/runtime/examples/extensions/message-renderer.ts +4 -4
- package/runtime/examples/extensions/minimal-mode.ts +9 -9
- package/runtime/examples/extensions/modal-editor.ts +3 -3
- package/runtime/examples/extensions/model-status.ts +3 -3
- package/runtime/examples/extensions/notify.ts +5 -5
- package/runtime/examples/extensions/overlay-qa-tests.ts +20 -20
- package/runtime/examples/extensions/overlay-test.ts +3 -3
- package/runtime/examples/extensions/permission-gate.ts +2 -2
- package/runtime/examples/extensions/pirate.ts +4 -4
- package/runtime/examples/extensions/plan-mode/index.ts +21 -21
- package/runtime/examples/extensions/preset.ts +26 -26
- package/runtime/examples/extensions/project-trust.ts +7 -7
- package/runtime/examples/extensions/prompt-customizer.ts +3 -3
- package/runtime/examples/extensions/protected-paths.ts +2 -2
- package/runtime/examples/extensions/provider-payload.ts +3 -3
- package/runtime/examples/extensions/question.ts +2 -2
- package/runtime/examples/extensions/questionnaire.ts +2 -2
- package/runtime/examples/extensions/rainbow-editor.ts +3 -3
- package/runtime/examples/extensions/reload-runtime.ts +4 -4
- package/runtime/examples/extensions/rpc-demo.ts +10 -10
- package/runtime/examples/extensions/sandbox/index.ts +15 -15
- package/runtime/examples/extensions/sandbox/package-lock.json +4 -4
- package/runtime/examples/extensions/sandbox/package.json +3 -3
- package/runtime/examples/extensions/send-user-message.ts +13 -13
- package/runtime/examples/extensions/session-name.ts +4 -4
- package/runtime/examples/extensions/shutdown-command.ts +10 -10
- package/runtime/examples/extensions/snake.ts +3 -3
- package/runtime/examples/extensions/space-invaders.ts +3 -3
- package/runtime/examples/extensions/ssh.ts +12 -12
- package/runtime/examples/extensions/status-line.ts +4 -4
- package/runtime/examples/extensions/structured-output.ts +2 -2
- package/runtime/examples/extensions/subagent/index.ts +5 -5
- package/runtime/examples/extensions/system-prompt-header.ts +3 -3
- package/runtime/examples/extensions/tic-tac-toe.ts +15 -15
- package/runtime/examples/extensions/timed-confirm.ts +4 -4
- package/runtime/examples/extensions/titlebar-spinner.ts +11 -11
- package/runtime/examples/extensions/todo.ts +5 -5
- package/runtime/examples/extensions/tool-override.ts +5 -5
- package/runtime/examples/extensions/tools.ts +10 -10
- package/runtime/examples/extensions/trigger-compact.ts +3 -3
- package/runtime/examples/extensions/truncated-tool.ts +3 -3
- package/runtime/examples/extensions/widget-placement.ts +2 -2
- package/runtime/examples/extensions/with-deps/index.ts +2 -2
- package/runtime/examples/extensions/with-deps/package-lock.json +4 -4
- package/runtime/examples/extensions/with-deps/package.json +3 -3
- package/runtime/examples/extensions/working-indicator.ts +7 -7
- package/runtime/examples/extensions/working-message-test.ts +3 -3
- package/runtime/examples/sdk/01-minimal.ts +1 -1
- package/runtime/examples/sdk/03-custom-prompt.ts +1 -1
- package/runtime/examples/sdk/04-skills.ts +1 -1
- package/runtime/examples/sdk/05-tools.ts +1 -1
- package/runtime/examples/sdk/06-extensions.ts +11 -11
- package/runtime/examples/sdk/08-prompt-templates.ts +1 -1
- package/runtime/export-html/template.js +3 -3
- package/runtime/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
- package/runtime/package.json +8 -14
- package/runtime/theme/dark.json +0 -1
- package/runtime/theme/light.json +0 -1
- package/runtime/theme/theme-schema.json +2 -2
- package/runtime/valni +2 -2
- package/runtime/README.md +0 -674
- package/runtime/docs/extensions.md +0 -2910
- package/runtime/docs/json.md +0 -82
- package/runtime/docs/models.md +0 -540
- package/runtime/docs/packages.md +0 -228
- package/runtime/docs/rpc.md +0 -1480
- package/runtime/docs/sdk.md +0 -1183
- package/runtime/docs/session-format.md +0 -422
- package/runtime/docs/settings.md +0 -315
- package/runtime/docs/shell-aliases.md +0 -13
- package/runtime/docs/terminal-setup.md +0 -142
- package/runtime/docs/termux.md +0 -127
- package/runtime/docs/tmux.md +0 -63
- package/runtime/docs/tui.md +0 -927
- package/runtime/docs/windows.md +0 -17
- package/runtime/examples/README.md +0 -25
- package/runtime/examples/extensions/README.md +0 -212
- package/runtime/examples/extensions/doom-overlay/README.md +0 -46
- package/runtime/examples/extensions/dynamic-resources/SKILL.md +0 -8
- package/runtime/examples/extensions/dynamic-resources/dynamic.md +0 -5
- package/runtime/examples/extensions/gondolin/index.ts +0 -531
- package/runtime/examples/extensions/gondolin/package-lock.json +0 -185
- package/runtime/examples/extensions/gondolin/package.json +0 -19
- package/runtime/examples/extensions/plan-mode/README.md +0 -66
- package/runtime/examples/extensions/subagent/README.md +0 -175
- package/runtime/examples/extensions/subagent/agents/planner.md +0 -37
- package/runtime/examples/extensions/subagent/agents/reviewer.md +0 -35
- package/runtime/examples/extensions/subagent/agents/scout.md +0 -50
- package/runtime/examples/extensions/subagent/agents/worker.md +0 -24
- package/runtime/examples/extensions/subagent/prompts/implement-and-review.md +0 -10
- package/runtime/examples/extensions/subagent/prompts/implement.md +0 -10
- package/runtime/examples/extensions/subagent/prompts/scout-and-plan.md +0 -9
- package/runtime/examples/sdk/README.md +0 -140
- package/runtime/node_modules/@mariozechner/clipboard/.yarnrc.yml +0 -1
- package/runtime/node_modules/@mariozechner/clipboard/Cargo.toml +0 -45
- package/runtime/node_modules/@mariozechner/clipboard/README.md +0 -60
- package/runtime/node_modules/@mariozechner/clipboard/build.rs +0 -5
- package/runtime/node_modules/@mariozechner/clipboard/clipboard.darwin-x64.node +0 -0
- package/runtime/node_modules/@mariozechner/clipboard/exp.ts +0 -12
- package/runtime/node_modules/@mariozechner/clipboard/index.js +0 -606
- package/runtime/node_modules/@mariozechner/clipboard/package.json +0 -56
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/common.rs +0 -307
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/mod.rs +0 -93
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/ios.rs +0 -313
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/macos.rs +0 -471
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/mod.rs +0 -252
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/wayland.rs +0 -431
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/win.rs +0 -712
- package/runtime/node_modules/@mariozechner/clipboard/src/clipboard_rs/platform/x11.rs +0 -916
- package/runtime/node_modules/@mariozechner/clipboard/src/lib.rs +0 -194
- package/runtime/node_modules/@mariozechner/clipboard-darwin-x64/README.md +0 -3
- package/runtime/node_modules/@mariozechner/clipboard-darwin-x64/clipboard.darwin-x64.node +0 -0
- package/runtime/node_modules/@mariozechner/clipboard-darwin-x64/package.json +0 -22
|
@@ -1,307 +0,0 @@
|
|
|
1
|
-
#[cfg(feature = "image")]
|
|
2
|
-
use image::imageops::FilterType;
|
|
3
|
-
#[cfg(feature = "image")]
|
|
4
|
-
use image::{ColorType, DynamicImage, GenericImageView, ImageFormat, RgbaImage};
|
|
5
|
-
use std::error::Error;
|
|
6
|
-
use std::io::Cursor;
|
|
7
|
-
pub type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync + 'static>>;
|
|
8
|
-
|
|
9
|
-
pub trait ContentData {
|
|
10
|
-
fn get_format(&self) -> ContentFormat;
|
|
11
|
-
|
|
12
|
-
fn as_bytes(&self) -> &[u8];
|
|
13
|
-
|
|
14
|
-
fn as_str(&self) -> Result<&str>;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
pub trait ClipboardHandler {
|
|
18
|
-
fn on_clipboard_change(&mut self);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
pub enum ClipboardContent {
|
|
22
|
-
Text(String),
|
|
23
|
-
Rtf(String),
|
|
24
|
-
Html(String),
|
|
25
|
-
#[cfg(feature = "image")]
|
|
26
|
-
Image(RustImageData),
|
|
27
|
-
Files(Vec<String>),
|
|
28
|
-
Other(String, Vec<u8>),
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
impl ContentData for ClipboardContent {
|
|
32
|
-
fn get_format(&self) -> ContentFormat {
|
|
33
|
-
match self {
|
|
34
|
-
ClipboardContent::Text(_) => ContentFormat::Text,
|
|
35
|
-
ClipboardContent::Rtf(_) => ContentFormat::Rtf,
|
|
36
|
-
ClipboardContent::Html(_) => ContentFormat::Html,
|
|
37
|
-
#[cfg(feature = "image")]
|
|
38
|
-
ClipboardContent::Image(_) => ContentFormat::Image,
|
|
39
|
-
ClipboardContent::Files(_) => ContentFormat::Files,
|
|
40
|
-
ClipboardContent::Other(format, _) => ContentFormat::Other(format.clone()),
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
fn as_bytes(&self) -> &[u8] {
|
|
45
|
-
match self {
|
|
46
|
-
ClipboardContent::Text(data) => data.as_bytes(),
|
|
47
|
-
ClipboardContent::Rtf(data) => data.as_bytes(),
|
|
48
|
-
ClipboardContent::Html(data) => data.as_bytes(),
|
|
49
|
-
// dynamic image is not supported to as bytes
|
|
50
|
-
#[cfg(feature = "image")]
|
|
51
|
-
ClipboardContent::Image(_) => &[],
|
|
52
|
-
ClipboardContent::Files(data) => {
|
|
53
|
-
// use first file path as data
|
|
54
|
-
if let Some(path) = data.first() {
|
|
55
|
-
path.as_bytes()
|
|
56
|
-
} else {
|
|
57
|
-
&[]
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
ClipboardContent::Other(_, data) => data.as_slice(),
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
fn as_str(&self) -> Result<&str> {
|
|
65
|
-
match self {
|
|
66
|
-
ClipboardContent::Text(data) => Ok(data),
|
|
67
|
-
ClipboardContent::Rtf(data) => Ok(data),
|
|
68
|
-
ClipboardContent::Html(data) => Ok(data),
|
|
69
|
-
#[cfg(feature = "image")]
|
|
70
|
-
ClipboardContent::Image(_) => Err("can't convert image to string".into()),
|
|
71
|
-
ClipboardContent::Files(data) => {
|
|
72
|
-
// use first file path as data
|
|
73
|
-
if let Some(path) = data.first() {
|
|
74
|
-
Ok(path)
|
|
75
|
-
} else {
|
|
76
|
-
Err("content is empty".into())
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
ClipboardContent::Other(_, data) => std::str::from_utf8(data).map_err(|e| e.into()),
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
#[derive(Clone)]
|
|
85
|
-
pub enum ContentFormat {
|
|
86
|
-
Text,
|
|
87
|
-
Rtf,
|
|
88
|
-
Html,
|
|
89
|
-
#[cfg(feature = "image")]
|
|
90
|
-
Image,
|
|
91
|
-
Files,
|
|
92
|
-
Other(String),
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
#[cfg(feature = "image")]
|
|
96
|
-
pub struct RustImageData {
|
|
97
|
-
width: u32,
|
|
98
|
-
height: u32,
|
|
99
|
-
data: Option<DynamicImage>,
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/// 此处的 `RustImageBuffer` 已经是带有图片格式的字节流,例如 png,jpeg;
|
|
103
|
-
#[cfg(feature = "image")]
|
|
104
|
-
pub struct RustImageBuffer(Vec<u8>);
|
|
105
|
-
|
|
106
|
-
#[cfg(feature = "image")]
|
|
107
|
-
pub trait RustImage: Sized {
|
|
108
|
-
/// create an empty image
|
|
109
|
-
fn empty() -> Self;
|
|
110
|
-
|
|
111
|
-
fn is_empty(&self) -> bool;
|
|
112
|
-
|
|
113
|
-
/// Read image from file path
|
|
114
|
-
fn from_path(path: &str) -> Result<Self>;
|
|
115
|
-
|
|
116
|
-
/// Create a new image from a byte slice
|
|
117
|
-
fn from_bytes(bytes: &[u8]) -> Result<Self>;
|
|
118
|
-
|
|
119
|
-
fn from_dynamic_image(image: DynamicImage) -> Self;
|
|
120
|
-
|
|
121
|
-
/// width and height
|
|
122
|
-
fn get_size(&self) -> (u32, u32);
|
|
123
|
-
|
|
124
|
-
/// Scale this image down to fit within a specific size.
|
|
125
|
-
/// Returns a new image. The image's aspect ratio is preserved.
|
|
126
|
-
/// The image is scaled to the maximum possible size that fits
|
|
127
|
-
/// within the bounds specified by `nwidth` and `nheight`.
|
|
128
|
-
///
|
|
129
|
-
/// This method uses a fast integer algorithm where each source
|
|
130
|
-
/// pixel contributes to exactly one target pixel.
|
|
131
|
-
/// May give aliasing artifacts if new size is close to old size.
|
|
132
|
-
fn thumbnail(&self, width: u32, height: u32) -> Result<Self>;
|
|
133
|
-
|
|
134
|
-
/// en: Adjust the size of the image without retaining the aspect ratio
|
|
135
|
-
/// zh: 调整图片大小,不保留长宽比
|
|
136
|
-
fn resize(&self, width: u32, height: u32, filter: FilterType) -> Result<Self>;
|
|
137
|
-
|
|
138
|
-
fn encode_image(
|
|
139
|
-
&self,
|
|
140
|
-
target_color_type: ColorType,
|
|
141
|
-
format: ImageFormat,
|
|
142
|
-
) -> Result<RustImageBuffer>;
|
|
143
|
-
|
|
144
|
-
fn to_jpeg(&self) -> Result<RustImageBuffer>;
|
|
145
|
-
|
|
146
|
-
/// en: Convert to png format, the returned image is a new image, and the data itself will not be modified
|
|
147
|
-
/// zh: 转为 png 格式,返回的为新的图片,本身数据不会修改
|
|
148
|
-
fn to_png(&self) -> Result<RustImageBuffer>;
|
|
149
|
-
|
|
150
|
-
#[cfg(target_os = "windows")]
|
|
151
|
-
fn to_bitmap(&self) -> Result<RustImageBuffer>;
|
|
152
|
-
|
|
153
|
-
fn save_to_path(&self, path: &str) -> Result<()>;
|
|
154
|
-
|
|
155
|
-
fn get_dynamic_image(&self) -> Result<DynamicImage>;
|
|
156
|
-
|
|
157
|
-
fn to_rgba8(&self) -> Result<RgbaImage>;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
#[cfg(feature = "image")]
|
|
161
|
-
impl RustImage for RustImageData {
|
|
162
|
-
fn empty() -> Self {
|
|
163
|
-
RustImageData {
|
|
164
|
-
width: 0,
|
|
165
|
-
height: 0,
|
|
166
|
-
data: None,
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
fn is_empty(&self) -> bool {
|
|
171
|
-
self.data.is_none()
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
fn from_path(path: &str) -> Result<Self> {
|
|
175
|
-
let image = image::open(path)?;
|
|
176
|
-
let (width, height) = image.dimensions();
|
|
177
|
-
Ok(RustImageData {
|
|
178
|
-
width,
|
|
179
|
-
height,
|
|
180
|
-
data: Some(image),
|
|
181
|
-
})
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
fn from_bytes(bytes: &[u8]) -> Result<Self> {
|
|
185
|
-
let image = image::load_from_memory(bytes)?;
|
|
186
|
-
let (width, height) = image.dimensions();
|
|
187
|
-
Ok(RustImageData {
|
|
188
|
-
width,
|
|
189
|
-
height,
|
|
190
|
-
data: Some(image),
|
|
191
|
-
})
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
fn from_dynamic_image(image: DynamicImage) -> Self {
|
|
195
|
-
let (width, height) = image.dimensions();
|
|
196
|
-
RustImageData {
|
|
197
|
-
width,
|
|
198
|
-
height,
|
|
199
|
-
data: Some(image),
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
fn get_size(&self) -> (u32, u32) {
|
|
204
|
-
(self.width, self.height)
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
fn thumbnail(&self, width: u32, height: u32) -> Result<Self> {
|
|
208
|
-
match &self.data {
|
|
209
|
-
Some(image) => {
|
|
210
|
-
let resized = image.thumbnail(width, height);
|
|
211
|
-
Ok(RustImageData {
|
|
212
|
-
width: resized.width(),
|
|
213
|
-
height: resized.height(),
|
|
214
|
-
data: Some(resized),
|
|
215
|
-
})
|
|
216
|
-
}
|
|
217
|
-
None => Err("image is empty".into()),
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
fn resize(&self, width: u32, height: u32, filter: FilterType) -> Result<Self> {
|
|
222
|
-
match &self.data {
|
|
223
|
-
Some(image) => {
|
|
224
|
-
let resized = image.resize_exact(width, height, filter);
|
|
225
|
-
Ok(RustImageData {
|
|
226
|
-
width: resized.width(),
|
|
227
|
-
height: resized.height(),
|
|
228
|
-
data: Some(resized),
|
|
229
|
-
})
|
|
230
|
-
}
|
|
231
|
-
None => Err("image is empty".into()),
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
fn save_to_path(&self, path: &str) -> Result<()> {
|
|
236
|
-
match &self.data {
|
|
237
|
-
Some(image) => {
|
|
238
|
-
image.save(path)?;
|
|
239
|
-
Ok(())
|
|
240
|
-
}
|
|
241
|
-
None => Err("image is empty".into()),
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
fn get_dynamic_image(&self) -> Result<DynamicImage> {
|
|
246
|
-
match &self.data {
|
|
247
|
-
Some(image) => Ok(image.clone()),
|
|
248
|
-
None => Err("image is empty".into()),
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
fn to_rgba8(&self) -> Result<RgbaImage> {
|
|
253
|
-
match &self.data {
|
|
254
|
-
Some(image) => Ok(image.to_rgba8()),
|
|
255
|
-
None => Err("image is empty".into()),
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// 私有辅助函数,处理图像格式转换和编码
|
|
260
|
-
fn encode_image(
|
|
261
|
-
&self,
|
|
262
|
-
target_color_type: ColorType,
|
|
263
|
-
format: ImageFormat,
|
|
264
|
-
) -> Result<RustImageBuffer> {
|
|
265
|
-
let image = self.data.as_ref().ok_or("image is empty")?;
|
|
266
|
-
|
|
267
|
-
let mut bytes = Vec::new();
|
|
268
|
-
match (image.color(), target_color_type) {
|
|
269
|
-
(ColorType::Rgba8, ColorType::Rgb8) => image
|
|
270
|
-
.to_rgb8()
|
|
271
|
-
.write_to(&mut Cursor::new(&mut bytes), format)?,
|
|
272
|
-
(_, ColorType::Rgba8) => image
|
|
273
|
-
.to_rgba8()
|
|
274
|
-
.write_to(&mut Cursor::new(&mut bytes), format)?,
|
|
275
|
-
_ => image.write_to(&mut Cursor::new(&mut bytes), format)?,
|
|
276
|
-
};
|
|
277
|
-
Ok(RustImageBuffer(bytes))
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
fn to_jpeg(&self) -> Result<RustImageBuffer> {
|
|
281
|
-
// JPEG 需要 RGB 格式(不支持 alpha 通道)
|
|
282
|
-
self.encode_image(ColorType::Rgb8, ImageFormat::Jpeg)
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
fn to_png(&self) -> Result<RustImageBuffer> {
|
|
286
|
-
// PNG 使用 RGBA 格式以支持透明度
|
|
287
|
-
self.encode_image(ColorType::Rgba8, ImageFormat::Png)
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
#[cfg(target_os = "windows")]
|
|
291
|
-
fn to_bitmap(&self) -> Result<RustImageBuffer> {
|
|
292
|
-
// BMP 使用 RGBA 格式
|
|
293
|
-
self.encode_image(ColorType::Rgba8, ImageFormat::Bmp)
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
#[cfg(feature = "image")]
|
|
298
|
-
impl RustImageBuffer {
|
|
299
|
-
pub fn get_bytes(&self) -> &[u8] {
|
|
300
|
-
&self.0
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
pub fn save_to_path(&self, path: &str) -> Result<()> {
|
|
304
|
-
std::fs::write(path, &self.0)?;
|
|
305
|
-
Ok(())
|
|
306
|
-
}
|
|
307
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
pub mod common;
|
|
2
|
-
mod platform;
|
|
3
|
-
#[cfg(feature = "image")]
|
|
4
|
-
pub use common::RustImageData;
|
|
5
|
-
pub use common::{ClipboardContent, ClipboardHandler, ContentFormat, Result};
|
|
6
|
-
#[cfg(feature = "image")]
|
|
7
|
-
pub use image::imageops::FilterType;
|
|
8
|
-
#[cfg(target_os = "linux")]
|
|
9
|
-
pub use platform::ClipboardContextX11Options;
|
|
10
|
-
pub use platform::{ClipboardContext, ClipboardWatcherContext, WatcherShutdown};
|
|
11
|
-
|
|
12
|
-
pub trait Clipboard: Send {
|
|
13
|
-
/// zh: 获得剪切板当前内容的所有格式
|
|
14
|
-
/// en: Get all formats of the current content in the clipboard
|
|
15
|
-
fn available_formats(&self) -> Result<Vec<String>>;
|
|
16
|
-
|
|
17
|
-
fn has(&self, format: ContentFormat) -> bool;
|
|
18
|
-
|
|
19
|
-
/// zh: 清空剪切板
|
|
20
|
-
/// en: clear clipboard
|
|
21
|
-
fn clear(&self) -> Result<()>;
|
|
22
|
-
|
|
23
|
-
/// zh: 获得指定格式的数据,以字节数组形式返回
|
|
24
|
-
/// en: Get the data in the specified format in the clipboard as a byte array
|
|
25
|
-
fn get_buffer(&self, format: &str) -> Result<Vec<u8>>;
|
|
26
|
-
|
|
27
|
-
/// zh: 仅获得无格式纯文本,以字符串形式返回
|
|
28
|
-
/// en: Get plain text content in the clipboard as string
|
|
29
|
-
fn get_text(&self) -> Result<String>;
|
|
30
|
-
|
|
31
|
-
/// zh: 获得剪贴板中的富文本内容,以字符串形式返回
|
|
32
|
-
/// en: Get the rich text content in the clipboard as string
|
|
33
|
-
fn get_rich_text(&self) -> Result<String>;
|
|
34
|
-
|
|
35
|
-
/// zh: 获得剪贴板中的html内容,以字符串形式返回
|
|
36
|
-
/// en: Get the html format content in the clipboard as string
|
|
37
|
-
fn get_html(&self) -> Result<String>;
|
|
38
|
-
|
|
39
|
-
#[cfg(feature = "image")]
|
|
40
|
-
fn get_image(&self) -> Result<RustImageData>;
|
|
41
|
-
|
|
42
|
-
fn get_files(&self) -> Result<Vec<String>>;
|
|
43
|
-
|
|
44
|
-
fn get(&self, formats: &[ContentFormat]) -> Result<Vec<ClipboardContent>>;
|
|
45
|
-
|
|
46
|
-
fn set_buffer(&self, format: &str, buffer: Vec<u8>) -> Result<()>;
|
|
47
|
-
|
|
48
|
-
fn set_text(&self, text: String) -> Result<()>;
|
|
49
|
-
|
|
50
|
-
fn set_rich_text(&self, text: String) -> Result<()>;
|
|
51
|
-
|
|
52
|
-
fn set_html(&self, html: String) -> Result<()>;
|
|
53
|
-
|
|
54
|
-
#[cfg(feature = "image")]
|
|
55
|
-
fn set_image(&self, image: RustImageData) -> Result<()>;
|
|
56
|
-
|
|
57
|
-
fn set_files(&self, files: Vec<String>) -> Result<()>;
|
|
58
|
-
|
|
59
|
-
/// set image will clear clipboard
|
|
60
|
-
fn set(&self, contents: Vec<ClipboardContent>) -> Result<()>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
pub trait ClipboardWatcher<T: ClipboardHandler>: Send {
|
|
64
|
-
/// zh: 添加一个剪切板变化处理器,可以添加多个处理器,处理器需要实现 [`ClipboardHandler`] 这个trait
|
|
65
|
-
/// en: Add a clipboard change handler, you can add multiple handlers, the handler needs to implement the trait [`ClipboardHandler`]
|
|
66
|
-
fn add_handler(&mut self, handler: T) -> &mut Self;
|
|
67
|
-
|
|
68
|
-
/// zh: 开始监视剪切板变化,这是一个阻塞方法,直到监视结束,或者调用了stop方法,所以建议在单独的线程中调用
|
|
69
|
-
/// en: Start monitoring clipboard changes, this is a blocking method, until the monitoring ends, or the stop method is called, so it is recommended to call it in a separate thread
|
|
70
|
-
fn start_watch(&mut self);
|
|
71
|
-
|
|
72
|
-
/// zh: 获得停止监视的通道,可以通过这个通道停止监视
|
|
73
|
-
/// en: Get the channel to stop monitoring, you can stop monitoring through this channel
|
|
74
|
-
fn get_shutdown_channel(&self) -> WatcherShutdown;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
#[cfg(not(all(
|
|
78
|
-
unix,
|
|
79
|
-
not(any(
|
|
80
|
-
target_os = "macos",
|
|
81
|
-
target_os = "ios",
|
|
82
|
-
target_os = "android",
|
|
83
|
-
target_os = "emscripten"
|
|
84
|
-
))
|
|
85
|
-
)))]
|
|
86
|
-
impl WatcherShutdown {
|
|
87
|
-
/// zh: 停止监视
|
|
88
|
-
///
|
|
89
|
-
/// en: stop watching
|
|
90
|
-
pub fn stop(self) {
|
|
91
|
-
drop(self);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
@@ -1,313 +0,0 @@
|
|
|
1
|
-
use crate::clipboard_rs::{
|
|
2
|
-
common::Result, Clipboard, ClipboardContent, ClipboardHandler, ClipboardWatcher, ContentFormat,
|
|
3
|
-
};
|
|
4
|
-
#[cfg(feature = "image")]
|
|
5
|
-
use crate::clipboard_rs::{common::RustImage, RustImageData};
|
|
6
|
-
use objc2::{rc::Retained, runtime::ProtocolObject};
|
|
7
|
-
use objc2_foundation::{ns_string, NSArray, NSData, NSDictionary, NSString};
|
|
8
|
-
use objc2_ui_kit::UIPasteboard;
|
|
9
|
-
#[cfg(feature = "image")]
|
|
10
|
-
use objc2_ui_kit::{UIImage, UIImagePNGRepresentation};
|
|
11
|
-
use std::{
|
|
12
|
-
sync::mpsc::{self, Receiver, Sender},
|
|
13
|
-
time::Duration,
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
pub struct ClipboardContext {
|
|
17
|
-
clipboard: Retained<UIPasteboard>,
|
|
18
|
-
}
|
|
19
|
-
pub struct ClipboardWatcherContext<T: ClipboardHandler> {
|
|
20
|
-
clipboard: Retained<UIPasteboard>,
|
|
21
|
-
handlers: Vec<T>,
|
|
22
|
-
running: bool,
|
|
23
|
-
stop_signal: Sender<()>,
|
|
24
|
-
stop_receiver: Receiver<()>,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
impl<T: ClipboardHandler> ClipboardWatcherContext<T> {
|
|
28
|
-
pub fn new() -> Result<Self> {
|
|
29
|
-
let clipboard = unsafe { UIPasteboard::generalPasteboard() };
|
|
30
|
-
let (tx, rx) = mpsc::channel();
|
|
31
|
-
Ok(Self {
|
|
32
|
-
clipboard,
|
|
33
|
-
handlers: Vec::new(),
|
|
34
|
-
running: false,
|
|
35
|
-
stop_signal: tx,
|
|
36
|
-
stop_receiver: rx,
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
unsafe impl<T: ClipboardHandler> Send for ClipboardWatcherContext<T> {}
|
|
42
|
-
|
|
43
|
-
impl<T: ClipboardHandler> ClipboardWatcher<T> for ClipboardWatcherContext<T> {
|
|
44
|
-
fn add_handler(&mut self, handler: T) -> &mut Self {
|
|
45
|
-
self.handlers.push(handler);
|
|
46
|
-
self
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
fn start_watch(&mut self) {
|
|
50
|
-
if self.running {
|
|
51
|
-
println!("already start watch!");
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
if self.handlers.is_empty() {
|
|
55
|
-
println!("no handler, no need to start watch!");
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
self.running = true;
|
|
59
|
-
let mut last_change_count = unsafe { self.clipboard.changeCount() };
|
|
60
|
-
loop {
|
|
61
|
-
// if receive stop signal, break loop
|
|
62
|
-
if self
|
|
63
|
-
.stop_receiver
|
|
64
|
-
.recv_timeout(Duration::from_millis(500))
|
|
65
|
-
.is_ok()
|
|
66
|
-
{
|
|
67
|
-
break;
|
|
68
|
-
}
|
|
69
|
-
let change_count = unsafe { self.clipboard.changeCount() };
|
|
70
|
-
if last_change_count == 0 {
|
|
71
|
-
last_change_count = change_count;
|
|
72
|
-
} else if change_count != last_change_count {
|
|
73
|
-
self
|
|
74
|
-
.handlers
|
|
75
|
-
.iter_mut()
|
|
76
|
-
.for_each(|handler| handler.on_clipboard_change());
|
|
77
|
-
last_change_count = change_count;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
self.running = false;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
fn get_shutdown_channel(&self) -> WatcherShutdown {
|
|
84
|
-
WatcherShutdown {
|
|
85
|
-
stop_signal: self.stop_signal.clone(),
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
pub struct WatcherShutdown {
|
|
91
|
-
stop_signal: Sender<()>,
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
impl Drop for WatcherShutdown {
|
|
95
|
-
fn drop(&mut self) {
|
|
96
|
-
let _ = self.stop_signal.send(());
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
impl ClipboardContext {
|
|
101
|
-
pub fn new() -> Result<Self> {
|
|
102
|
-
let clipboard = unsafe { UIPasteboard::generalPasteboard() };
|
|
103
|
-
Ok(Self { clipboard })
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
fn write_to_clipboard(&self, data: &[ClipboardContent]) -> Result<()> {
|
|
107
|
-
let items = data
|
|
108
|
-
.iter()
|
|
109
|
-
.map(|content| match content {
|
|
110
|
-
ClipboardContent::Text(text) => {
|
|
111
|
-
let ns_text = NSString::from_str(text);
|
|
112
|
-
let pair = unsafe {
|
|
113
|
-
NSDictionary::dictionaryWithObject_forKey(
|
|
114
|
-
ns_text.as_ref(),
|
|
115
|
-
ProtocolObject::from_ref(ns_string!("public.utf8-plain-text")),
|
|
116
|
-
)
|
|
117
|
-
};
|
|
118
|
-
Some(pair)
|
|
119
|
-
}
|
|
120
|
-
ClipboardContent::Rtf(rtf) => {
|
|
121
|
-
let ns_rtf = NSString::from_str(rtf);
|
|
122
|
-
let pair = unsafe {
|
|
123
|
-
NSDictionary::dictionaryWithObject_forKey(
|
|
124
|
-
ns_rtf.as_ref(),
|
|
125
|
-
ProtocolObject::from_ref(ns_string!("public.rtf")),
|
|
126
|
-
)
|
|
127
|
-
};
|
|
128
|
-
Some(pair)
|
|
129
|
-
}
|
|
130
|
-
ClipboardContent::Html(html) => {
|
|
131
|
-
let ns_html = NSString::from_str(html);
|
|
132
|
-
let pair = unsafe {
|
|
133
|
-
NSDictionary::dictionaryWithObject_forKey(
|
|
134
|
-
ns_html.as_ref(),
|
|
135
|
-
ProtocolObject::from_ref(ns_string!("public.html")),
|
|
136
|
-
)
|
|
137
|
-
};
|
|
138
|
-
Some(pair)
|
|
139
|
-
}
|
|
140
|
-
#[cfg(feature = "image")]
|
|
141
|
-
ClipboardContent::Image(image) => {
|
|
142
|
-
let png = image.to_png().unwrap();
|
|
143
|
-
let ns_data = NSData::with_bytes(png.get_bytes());
|
|
144
|
-
let image = unsafe { UIImage::imageWithData(&ns_data) };
|
|
145
|
-
if let Some(image) = image {
|
|
146
|
-
let pair = unsafe {
|
|
147
|
-
NSDictionary::dictionaryWithObject_forKey(
|
|
148
|
-
image.as_ref(),
|
|
149
|
-
ProtocolObject::from_ref(ns_string!("public.png")),
|
|
150
|
-
)
|
|
151
|
-
};
|
|
152
|
-
Some(pair)
|
|
153
|
-
} else {
|
|
154
|
-
None
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
_ => None,
|
|
158
|
-
})
|
|
159
|
-
.filter_map(|item| item)
|
|
160
|
-
.collect::<Vec<_>>();
|
|
161
|
-
unsafe {
|
|
162
|
-
self
|
|
163
|
-
.clipboard
|
|
164
|
-
.setItems(&NSArray::from_retained_slice(&items))
|
|
165
|
-
};
|
|
166
|
-
Ok(())
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
impl Clipboard for ClipboardContext {
|
|
171
|
-
fn available_formats(&self) -> Result<Vec<String>> {
|
|
172
|
-
let formats = unsafe { self.clipboard.pasteboardTypes() };
|
|
173
|
-
Ok(formats.iter().map(|f| f.to_string()).collect())
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
fn has(&self, format: ContentFormat) -> bool {
|
|
177
|
-
match format {
|
|
178
|
-
ContentFormat::Text => unsafe { self.clipboard.hasStrings() },
|
|
179
|
-
#[cfg(feature = "image")]
|
|
180
|
-
ContentFormat::Image => unsafe { self.clipboard.hasImages() },
|
|
181
|
-
ContentFormat::Rtf => unsafe {
|
|
182
|
-
self
|
|
183
|
-
.clipboard
|
|
184
|
-
.containsPasteboardTypes(&NSArray::from_slice(&[ns_string!("public.rtf")]))
|
|
185
|
-
},
|
|
186
|
-
ContentFormat::Html => unsafe {
|
|
187
|
-
self
|
|
188
|
-
.clipboard
|
|
189
|
-
.containsPasteboardTypes(&NSArray::from_slice(&[ns_string!("public.html")]))
|
|
190
|
-
},
|
|
191
|
-
ContentFormat::Files => false,
|
|
192
|
-
ContentFormat::Other(format) => unsafe {
|
|
193
|
-
self
|
|
194
|
-
.clipboard
|
|
195
|
-
.containsPasteboardTypes(&NSArray::from_retained_slice(&[NSString::from_str(
|
|
196
|
-
&format,
|
|
197
|
-
)]))
|
|
198
|
-
},
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
fn clear(&self) -> Result<()> {
|
|
203
|
-
unsafe { self.clipboard.setItems(&NSArray::from_slice(&[])) };
|
|
204
|
-
Ok(())
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
fn get_buffer(&self, format: &str) -> Result<Vec<u8>> {
|
|
208
|
-
let ns_format = NSString::from_str(format);
|
|
209
|
-
let data = unsafe { self.clipboard.dataForPasteboardType(&ns_format) };
|
|
210
|
-
if let Some(data) = data {
|
|
211
|
-
Ok(data.to_vec())
|
|
212
|
-
} else {
|
|
213
|
-
Err("No data found".into())
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
fn get_text(&self) -> Result<String> {
|
|
218
|
-
let text = unsafe { self.clipboard.string() };
|
|
219
|
-
if let Some(text) = text {
|
|
220
|
-
Ok(text.to_string())
|
|
221
|
-
} else {
|
|
222
|
-
Err("No text found".into())
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
fn get_rich_text(&self) -> Result<String> {
|
|
227
|
-
let buffer = self.get_buffer("public.rtf")?;
|
|
228
|
-
Ok(String::from_utf8_lossy(&buffer).to_string())
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
fn get_html(&self) -> Result<String> {
|
|
232
|
-
let buffer = self.get_buffer("public.html")?;
|
|
233
|
-
Ok(String::from_utf8_lossy(&buffer).to_string())
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
#[cfg(feature = "image")]
|
|
237
|
-
fn get_image(&self) -> Result<RustImageData> {
|
|
238
|
-
let image = unsafe { self.clipboard.image() };
|
|
239
|
-
if let Some(image) = image {
|
|
240
|
-
let data = unsafe { UIImagePNGRepresentation(&image) };
|
|
241
|
-
if let Some(data) = data {
|
|
242
|
-
let bytes = unsafe { data.as_bytes_unchecked() };
|
|
243
|
-
Ok(RustImageData::from_bytes(bytes)?)
|
|
244
|
-
} else {
|
|
245
|
-
Err("No image data found".into())
|
|
246
|
-
}
|
|
247
|
-
} else {
|
|
248
|
-
Err("No image found".into())
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
fn get_files(&self) -> Result<Vec<String>> {
|
|
253
|
-
Err("Not supported".into())
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
fn get(&self, _formats: &[ContentFormat]) -> Result<Vec<ClipboardContent>> {
|
|
257
|
-
Err("Not supported".into())
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
fn set_buffer(&self, format: &str, buffer: Vec<u8>) -> Result<()> {
|
|
261
|
-
let ns_format = NSString::from_str(format);
|
|
262
|
-
let ns_data = NSData::with_bytes(&buffer);
|
|
263
|
-
unsafe {
|
|
264
|
-
self
|
|
265
|
-
.clipboard
|
|
266
|
-
.setData_forPasteboardType(&ns_data, &ns_format)
|
|
267
|
-
}
|
|
268
|
-
Ok(())
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
fn set_text(&self, text: String) -> Result<()> {
|
|
272
|
-
unsafe {
|
|
273
|
-
self
|
|
274
|
-
.clipboard
|
|
275
|
-
.setString(Some(&NSString::from_str(text.as_str())))
|
|
276
|
-
};
|
|
277
|
-
Ok(())
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
fn set_rich_text(&self, _text: String) -> Result<()> {
|
|
281
|
-
Err("Not supported".into())
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
fn set_html(&self, _html: String) -> Result<()> {
|
|
285
|
-
Err("Not supported".into())
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
#[cfg(feature = "image")]
|
|
289
|
-
fn set_image(&self, image: RustImageData) -> Result<()> {
|
|
290
|
-
if image.is_empty() {
|
|
291
|
-
Err("Image is empty".into())
|
|
292
|
-
} else {
|
|
293
|
-
let png = image.to_png()?;
|
|
294
|
-
let ns_data = NSData::with_bytes(png.get_bytes());
|
|
295
|
-
let image = unsafe { UIImage::imageWithData(&ns_data) };
|
|
296
|
-
if let Some(image) = image {
|
|
297
|
-
unsafe { self.clipboard.setImage(Some(&image)) };
|
|
298
|
-
Ok(())
|
|
299
|
-
} else {
|
|
300
|
-
Err("Failed to create image".into())
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
fn set_files(&self, _files: Vec<String>) -> Result<()> {
|
|
306
|
-
Err("Not supported".into())
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
fn set(&self, contents: Vec<ClipboardContent>) -> Result<()> {
|
|
310
|
-
self.write_to_clipboard(&contents)?;
|
|
311
|
-
Ok(())
|
|
312
|
-
}
|
|
313
|
-
}
|