@lynker-desktop/electron-sdk 0.0.9-alpha.62 → 0.0.9-alpha.63
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/esm/main/clipboard.d.ts.map +1 -1
- package/esm/main/clipboard.js +245 -44
- package/esm/main/clipboard.js.map +1 -1
- package/esm/main/downloader.d.ts +15 -1
- package/esm/main/downloader.d.ts.map +1 -1
- package/esm/main/downloader.js +132 -40
- package/esm/main/downloader.js.map +1 -1
- package/esm/main/index.d.ts.map +1 -1
- package/esm/main/index.js +2 -0
- package/esm/main/index.js.map +1 -1
- package/esm/main/shortcut.d.ts +14 -0
- package/esm/main/shortcut.d.ts.map +1 -0
- package/esm/main/shortcut.js +173 -0
- package/esm/main/shortcut.js.map +1 -0
- package/main/clipboard.d.ts.map +1 -1
- package/main/clipboard.js +245 -44
- package/main/clipboard.js.map +1 -1
- package/main/downloader.d.ts +15 -1
- package/main/downloader.d.ts.map +1 -1
- package/main/downloader.js +132 -40
- package/main/downloader.js.map +1 -1
- package/main/index.d.ts.map +1 -1
- package/main/index.js +2 -0
- package/main/index.js.map +1 -1
- package/main/shortcut.d.ts +14 -0
- package/main/shortcut.d.ts.map +1 -0
- package/main/shortcut.js +173 -0
- package/main/shortcut.js.map +1 -0
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/main/clipboard.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/main/clipboard.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,iBAAiB,CAAC;IAC1B,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,KAAK,CAAC,EAAE,iBAAiB,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,OAAO,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG,iBAAiB,EAAE,GAAG,WAAW,CAAC;IACvE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACtE,OAAO,CAAC,EAAE;QACR,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AA2qCD;;GAEG;AACH,eAAO,MAAM,mBAAmB,YA0F/B,CAAA"}
|
package/esm/main/clipboard.js
CHANGED
|
@@ -11,6 +11,10 @@ const FILE_URI_PREFIX = 'file://';
|
|
|
11
11
|
const PLATFORM = process.platform;
|
|
12
12
|
const DOWNLOAD_TIMEOUT = 30000; // 30秒下载超时
|
|
13
13
|
const UNIX_FILE_FORMATS = ['public.file-url', 'text/uri-list', 'x-special/gnome-copied-files'];
|
|
14
|
+
// 图片相关的剪贴板格式
|
|
15
|
+
const IMAGE_FORMATS = ['image/png', 'image/jpeg', 'image/jpg', 'image/gif', 'image/bmp', 'image/webp', 'image/tiff', 'image/x-png', 'image/x-icon'];
|
|
16
|
+
// Windows 文件格式
|
|
17
|
+
const WINDOWS_FILE_FORMAT = 'FileNameW';
|
|
14
18
|
// MIME 类型映射表
|
|
15
19
|
const MIME_TYPES = {
|
|
16
20
|
// 文本文件
|
|
@@ -212,12 +216,133 @@ function normalizeFilePath(filePath) {
|
|
|
212
216
|
return path.isAbsolute(normalized) ? normalized : path.resolve(normalized);
|
|
213
217
|
}
|
|
214
218
|
/**
|
|
215
|
-
* 判断字符串是否为 URL
|
|
219
|
+
* 判断字符串是否为 URL(更严格的判断)
|
|
216
220
|
*/
|
|
217
221
|
function isUrl(str) {
|
|
222
|
+
if (!str || typeof str !== 'string') {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
const trimmed = str.trim();
|
|
226
|
+
if (trimmed.length === 0) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
const url = new URL(trimmed);
|
|
231
|
+
// 必须是 http 或 https 协议
|
|
232
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
// 必须有 hostname
|
|
236
|
+
if (!url.hostname || url.hostname.length === 0) {
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
// 避免将本地文件路径误判为 URL(如 C:\path\to\file)
|
|
240
|
+
if (url.hostname.includes('\\') || url.hostname.includes(':')) {
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 检查剪贴板是否有图片格式
|
|
251
|
+
*/
|
|
252
|
+
function hasImageFormat() {
|
|
218
253
|
try {
|
|
219
|
-
const
|
|
220
|
-
|
|
254
|
+
const formats = clipboard.availableFormats();
|
|
255
|
+
// 检查是否有图片格式
|
|
256
|
+
for (const format of formats) {
|
|
257
|
+
if (!format)
|
|
258
|
+
continue;
|
|
259
|
+
const lowerFormat = format.toLowerCase();
|
|
260
|
+
// 检查是否匹配图片格式
|
|
261
|
+
if (IMAGE_FORMATS.some(imgFormat => lowerFormat.includes(imgFormat.toLowerCase()))) {
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
// 检查常见的图片格式标识
|
|
265
|
+
if (lowerFormat.startsWith('image/')) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Electron 的 readImage 可能在没有明确格式时也能读取
|
|
270
|
+
// 尝试读取一次来验证(但不实际读取数据,只检查是否为空)
|
|
271
|
+
try {
|
|
272
|
+
const image = clipboard.readImage();
|
|
273
|
+
if (!image.isEmpty()) {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// 读取失败,说明不是图片
|
|
279
|
+
}
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* 检查剪贴板是否有文件格式
|
|
288
|
+
*/
|
|
289
|
+
function hasFileFormat() {
|
|
290
|
+
try {
|
|
291
|
+
const formats = clipboard.availableFormats();
|
|
292
|
+
if (PLATFORM === 'win32') {
|
|
293
|
+
// Windows: 检查 FileNameW 格式
|
|
294
|
+
return formats.includes(WINDOWS_FILE_FORMAT);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
// macOS/Linux: 检查文件 URI 格式
|
|
298
|
+
for (const format of UNIX_FILE_FORMATS) {
|
|
299
|
+
if (formats.includes(format)) {
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// 也检查文本中是否包含 file:// URI
|
|
304
|
+
try {
|
|
305
|
+
const text = clipboard.readText();
|
|
306
|
+
if (text && text.includes(FILE_URI_PREFIX)) {
|
|
307
|
+
// 进一步验证:检查是否真的是文件路径
|
|
308
|
+
const lines = text.split('\n').filter(Boolean);
|
|
309
|
+
for (const line of lines) {
|
|
310
|
+
if (line.trim().startsWith(FILE_URI_PREFIX)) {
|
|
311
|
+
const filePath = parseFileUri(line.trim());
|
|
312
|
+
const normalizedPath = normalizeFilePath(filePath);
|
|
313
|
+
if (fs.existsSync(normalizedPath)) {
|
|
314
|
+
const stat = fs.statSync(normalizedPath);
|
|
315
|
+
if (stat.isFile()) {
|
|
316
|
+
return true;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
324
|
+
// 忽略错误
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return false;
|
|
328
|
+
}
|
|
329
|
+
catch {
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* 检查剪贴板内容是否为 URL(基于格式判断,不读取内容)
|
|
335
|
+
*/
|
|
336
|
+
function hasUrlFormat() {
|
|
337
|
+
try {
|
|
338
|
+
const formats = clipboard.availableFormats();
|
|
339
|
+
// 检查是否有 URL 相关的格式
|
|
340
|
+
if (formats.includes('text/uri-list') || formats.includes('text/x-moz-url')) {
|
|
341
|
+
return true;
|
|
342
|
+
}
|
|
343
|
+
// 注意:这里不读取文本内容来判断,因为读取会消耗剪贴板
|
|
344
|
+
// URL 检测将在 handleGetUrl 中进行
|
|
345
|
+
return false;
|
|
221
346
|
}
|
|
222
347
|
catch {
|
|
223
348
|
return false;
|
|
@@ -599,6 +724,17 @@ function createImageFileDataFromClipboard(options) {
|
|
|
599
724
|
* 读取图片类型
|
|
600
725
|
*/
|
|
601
726
|
function handleGetImage(options) {
|
|
727
|
+
// 先检查是否有图片格式,避免误判
|
|
728
|
+
if (!hasImageFormat()) {
|
|
729
|
+
// 即使没有明确的图片格式,也尝试读取(Electron 可能能读取)
|
|
730
|
+
// 但如果读取失败,返回空结果
|
|
731
|
+
const imageData = createImageFileDataFromClipboard(options);
|
|
732
|
+
if (imageData) {
|
|
733
|
+
return { type: 'image', image: imageData };
|
|
734
|
+
}
|
|
735
|
+
return { type: 'image' };
|
|
736
|
+
}
|
|
737
|
+
// 有图片格式,尝试读取
|
|
602
738
|
const imageData = createImageFileDataFromClipboard(options);
|
|
603
739
|
if (imageData) {
|
|
604
740
|
return { type: 'image', image: imageData };
|
|
@@ -610,22 +746,49 @@ function handleGetImage(options) {
|
|
|
610
746
|
*/
|
|
611
747
|
function handleGetUrl() {
|
|
612
748
|
try {
|
|
613
|
-
//
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
return { type: 'url', url: text };
|
|
749
|
+
// 先检查是否有 URL 格式
|
|
750
|
+
if (!hasUrlFormat()) {
|
|
751
|
+
return { type: 'url' };
|
|
617
752
|
}
|
|
618
|
-
//
|
|
753
|
+
// 检查是否有 URL 相关的格式
|
|
619
754
|
const formats = clipboard.availableFormats();
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
const
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
755
|
+
// 优先检查 text/uri-list 或 text/x-moz-url 格式
|
|
756
|
+
if (formats.includes('text/uri-list') || formats.includes('text/x-moz-url')) {
|
|
757
|
+
try {
|
|
758
|
+
const uriList = clipboard.readText();
|
|
759
|
+
if (uriList) {
|
|
760
|
+
const uris = uriList.split('\n').filter(Boolean);
|
|
761
|
+
// 查找第一个有效的 HTTP/HTTPS URL
|
|
762
|
+
for (const uri of uris) {
|
|
763
|
+
const trimmed = uri.trim();
|
|
764
|
+
if (isUrl(trimmed)) {
|
|
765
|
+
return { type: 'url', url: trimmed };
|
|
766
|
+
}
|
|
767
|
+
}
|
|
627
768
|
}
|
|
628
769
|
}
|
|
770
|
+
catch {
|
|
771
|
+
// 忽略错误,继续尝试其他方法
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
// 尝试从普通文本中读取 URL
|
|
775
|
+
try {
|
|
776
|
+
const text = clipboard.readText();
|
|
777
|
+
if (text) {
|
|
778
|
+
const trimmed = text.trim();
|
|
779
|
+
// 检查整段文本是否为 URL
|
|
780
|
+
if (isUrl(trimmed)) {
|
|
781
|
+
return { type: 'url', url: trimmed };
|
|
782
|
+
}
|
|
783
|
+
// 尝试从文本中提取 URL(简单模式:查找 http:// 或 https://)
|
|
784
|
+
const urlMatch = trimmed.match(/https?:\/\/[^\s]+/);
|
|
785
|
+
if (urlMatch && urlMatch[0] && isUrl(urlMatch[0])) {
|
|
786
|
+
return { type: 'url', url: urlMatch[0] };
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
catch {
|
|
791
|
+
// 忽略错误
|
|
629
792
|
}
|
|
630
793
|
return { type: 'url' };
|
|
631
794
|
}
|
|
@@ -655,6 +818,10 @@ function handleGetBuffer() {
|
|
|
655
818
|
* 读取文件类型(单个)
|
|
656
819
|
*/
|
|
657
820
|
function handleGetFile(options) {
|
|
821
|
+
// 先检查是否有文件格式,避免误判
|
|
822
|
+
if (!hasFileFormat()) {
|
|
823
|
+
return { type: 'file' };
|
|
824
|
+
}
|
|
658
825
|
const filePaths = readClipboardFiles();
|
|
659
826
|
if (filePaths.length === 0 || !filePaths[0]) {
|
|
660
827
|
return { type: 'file' };
|
|
@@ -666,44 +833,78 @@ function handleGetFile(options) {
|
|
|
666
833
|
return { type: 'file', file: fileData };
|
|
667
834
|
}
|
|
668
835
|
/**
|
|
669
|
-
*
|
|
836
|
+
* 自动检测类型(基于格式检测,优先文件,然后是图片、URL,最后是文本)
|
|
670
837
|
*/
|
|
671
838
|
function handleGetAuto(options) {
|
|
672
|
-
//
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
839
|
+
// 先获取剪贴板格式,避免重复调用
|
|
840
|
+
let formats = [];
|
|
841
|
+
try {
|
|
842
|
+
formats = clipboard.availableFormats();
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
// 如果获取格式失败,降级到文本
|
|
846
|
+
const text = handleGetText();
|
|
847
|
+
return { type: 'text', text: text || '' };
|
|
848
|
+
}
|
|
849
|
+
// 1. 优先检测文件(基于格式检测)
|
|
850
|
+
if (hasFileFormat()) {
|
|
851
|
+
const filePaths = readClipboardFiles();
|
|
852
|
+
if (filePaths.length > 0) {
|
|
853
|
+
if (filePaths.length === 1) {
|
|
854
|
+
const firstPath = filePaths[0];
|
|
855
|
+
if (firstPath) {
|
|
856
|
+
const fileData = readFileData(firstPath, options);
|
|
857
|
+
if (fileData) {
|
|
858
|
+
return { type: 'file', file: fileData };
|
|
859
|
+
}
|
|
681
860
|
}
|
|
682
861
|
}
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
.
|
|
689
|
-
|
|
690
|
-
|
|
862
|
+
else {
|
|
863
|
+
// 多个文件
|
|
864
|
+
const files = filePaths
|
|
865
|
+
.map(filePath => readFileData(filePath, options))
|
|
866
|
+
.filter((file) => file !== null);
|
|
867
|
+
if (files.length > 0) {
|
|
868
|
+
return { type: 'files', files };
|
|
869
|
+
}
|
|
691
870
|
}
|
|
692
871
|
}
|
|
693
872
|
}
|
|
694
|
-
// 2.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
873
|
+
// 2. 检测图片(基于格式检测)
|
|
874
|
+
if (hasImageFormat()) {
|
|
875
|
+
const imageData = createImageFileDataFromClipboard(options);
|
|
876
|
+
if (imageData) {
|
|
877
|
+
return { type: 'image', image: imageData };
|
|
878
|
+
}
|
|
698
879
|
}
|
|
699
|
-
// 3. 检测
|
|
700
|
-
|
|
701
|
-
if (
|
|
702
|
-
|
|
880
|
+
// 3. 检测 URL(基于格式检测,或文本内容判断)
|
|
881
|
+
// 优先检查是否有 URL 格式
|
|
882
|
+
if (hasUrlFormat()) {
|
|
883
|
+
const urlResult = handleGetUrl();
|
|
884
|
+
if (urlResult.url) {
|
|
885
|
+
return urlResult;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
// 4. 检测 HTML(检查格式)
|
|
889
|
+
if (formats.includes('text/html') || formats.some(f => f && f.toLowerCase().includes('html'))) {
|
|
890
|
+
const htmlData = safeClipboardOperation(() => clipboard.readHTML(), '', '读取 HTML 失败:');
|
|
891
|
+
if (htmlData && htmlData.trim().length > 0) {
|
|
892
|
+
return { type: 'html', html: htmlData };
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
// 5. 最后读取文本,并检查是否为 URL
|
|
896
|
+
const text = safeClipboardOperation(() => clipboard.readText(), '', '读取文本失败:');
|
|
897
|
+
if (text && text.trim().length > 0) {
|
|
898
|
+
const trimmed = text.trim();
|
|
899
|
+
// 如果文本是纯 URL(单行且是有效的 URL),且之前没有检测到 URL 格式
|
|
900
|
+
// 则判断为 URL 类型
|
|
901
|
+
if (!trimmed.includes('\n') && isUrl(trimmed)) {
|
|
902
|
+
return { type: 'url', url: trimmed };
|
|
903
|
+
}
|
|
904
|
+
// 否则返回文本类型
|
|
905
|
+
return { type: 'text', text: trimmed };
|
|
703
906
|
}
|
|
704
|
-
|
|
705
|
-
const text = handleGetText();
|
|
706
|
-
return { type: 'text', text: text || '' };
|
|
907
|
+
return { type: 'text', text: '' };
|
|
707
908
|
}
|
|
708
909
|
/**
|
|
709
910
|
* 判断字符串是否为文件路径或 URL
|