opencode-tui-image-clipboard-fix 1.0.8 → 1.0.11
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/README.md +2 -2
- package/lib/index.d.ts +2 -4
- package/lib/index.js +10 -45
- package/lib/storage.d.ts +1 -1
- package/lib/storage.js +10 -45
- package/lib/types.js +1 -2
- package/lib/utils.d.ts +1 -1
- package/lib/utils.js +7 -14
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# OpenCode TUI Image Clipboard Fix
|
|
2
2
|
|
|
3
|
-
修复 OpenCode TUI
|
|
3
|
+
修复 OpenCode TUI 中图片粘贴的问题:自动将剪贴板中的图片保存为本地文件,并替换 `[Image N]` 占位符为实际文件路径。
|
|
4
4
|
|
|
5
5
|
## 🚀 安装
|
|
6
6
|
|
|
@@ -32,7 +32,7 @@ npm install -g opencode-tui-image-clipboard-fix
|
|
|
32
32
|
```
|
|
33
33
|
┌─────────────────────────────────────────────────────┐
|
|
34
34
|
│ OpenCode TUI │
|
|
35
|
-
│
|
|
35
|
+
│ 用户粘贴图片 │
|
|
36
36
|
│ ↓ │
|
|
37
37
|
│ 生成 FilePart (url: "data:image/...;base64,...") │
|
|
38
38
|
│ 消息文本包含 [Image 1] 占位符 │
|
package/lib/index.d.ts
CHANGED
|
@@ -82,7 +82,5 @@ type Plugin = (input: PluginInput) => Promise<{
|
|
|
82
82
|
* 3. 在发送给模型时替换 [Image N] 占位符为实际文件路径(不影响界面显示)
|
|
83
83
|
* 4. 移除 FilePart,只保留文本(避免不支持图片的模型报错)
|
|
84
84
|
*/
|
|
85
|
-
export declare const
|
|
86
|
-
|
|
87
|
-
};
|
|
88
|
-
export default ImageStoragePlugin;
|
|
85
|
+
export declare const ImageClipboardFix: Plugin;
|
|
86
|
+
export default ImageClipboardFix;
|
package/lib/index.js
CHANGED
|
@@ -1,43 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ImageStoragePlugin = void 0;
|
|
37
|
-
const os = __importStar(require("os"));
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
|
-
const storage_1 = require("./storage");
|
|
40
|
-
const utils_1 = require("./utils");
|
|
1
|
+
import * as os from "os";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import { ImageStorageManager } from "./storage.js";
|
|
4
|
+
import { formatSize } from "./utils.js";
|
|
41
5
|
const DEFAULT_CONFIG = {
|
|
42
6
|
maxStorageMB: 2048,
|
|
43
7
|
minFreeSpaceMB: 512,
|
|
@@ -54,11 +18,12 @@ const messageImagePaths = new Map();
|
|
|
54
18
|
* 3. 在发送给模型时替换 [Image N] 占位符为实际文件路径(不影响界面显示)
|
|
55
19
|
* 4. 移除 FilePart,只保留文本(避免不支持图片的模型报错)
|
|
56
20
|
*/
|
|
57
|
-
|
|
21
|
+
// Plugin implementation with specific function name for better identification
|
|
22
|
+
export const ImageClipboardFix = async ({ client, directory }) => {
|
|
58
23
|
const config = {
|
|
59
24
|
...DEFAULT_CONFIG,
|
|
60
25
|
};
|
|
61
|
-
const storageManager = new
|
|
26
|
+
const storageManager = new ImageStorageManager(config);
|
|
62
27
|
await storageManager.initialize();
|
|
63
28
|
/**
|
|
64
29
|
* 处理消息中的图片,保存并返回路径映射
|
|
@@ -200,7 +165,7 @@ exports.ImageStoragePlugin = Object.assign(async ({ client, directory }) => {
|
|
|
200
165
|
const stats = await storageManager.getStats();
|
|
201
166
|
console.log("=== Image Storage Stats ===");
|
|
202
167
|
console.log(`Total Files: ${stats.totalFiles}`);
|
|
203
|
-
console.log(`Total Size: ${
|
|
168
|
+
console.log(`Total Size: ${formatSize(stats.totalSize)}`);
|
|
204
169
|
console.log(`Oldest File: ${stats.oldestFile || "N/A"}`);
|
|
205
170
|
console.log(`Newest File: ${stats.newestFile || "N/A"}`);
|
|
206
171
|
console.log(`Storage Dir: ${config.storageDir}`);
|
|
@@ -212,5 +177,5 @@ exports.ImageStoragePlugin = Object.assign(async ({ client, directory }) => {
|
|
|
212
177
|
},
|
|
213
178
|
},
|
|
214
179
|
};
|
|
215
|
-
}
|
|
216
|
-
|
|
180
|
+
};
|
|
181
|
+
export default ImageClipboardFix;
|
package/lib/storage.d.ts
CHANGED
package/lib/storage.js
CHANGED
|
@@ -1,42 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.ImageStorageManager = void 0;
|
|
37
|
-
const fs = __importStar(require("fs/promises"));
|
|
38
|
-
const path = __importStar(require("path"));
|
|
39
|
-
const utils_1 = require("./utils");
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { extractImageInfo, generateHash, getImageDimensions } from './utils.js';
|
|
40
4
|
/**
|
|
41
5
|
* 图片存储管理器
|
|
42
6
|
*
|
|
@@ -46,10 +10,12 @@ const utils_1 = require("./utils");
|
|
|
46
10
|
* - 自动清理(LRU 策略)
|
|
47
11
|
* - 元数据管理
|
|
48
12
|
*/
|
|
49
|
-
class ImageStorageManager {
|
|
13
|
+
export class ImageStorageManager {
|
|
14
|
+
config;
|
|
15
|
+
metadataFile;
|
|
16
|
+
metadata = new Map();
|
|
50
17
|
constructor(config) {
|
|
51
18
|
this.config = config;
|
|
52
|
-
this.metadata = new Map();
|
|
53
19
|
this.metadataFile = path.join(config.storageDir, 'metadata.json');
|
|
54
20
|
}
|
|
55
21
|
/**
|
|
@@ -86,12 +52,12 @@ class ImageStorageManager {
|
|
|
86
52
|
* @returns 图片元数据,如果图片已存在则返回现有元数据
|
|
87
53
|
*/
|
|
88
54
|
async saveImage(dataUrl) {
|
|
89
|
-
const imageInfo =
|
|
55
|
+
const imageInfo = extractImageInfo(dataUrl);
|
|
90
56
|
if (!imageInfo) {
|
|
91
57
|
// Invalid data URL format
|
|
92
58
|
return null;
|
|
93
59
|
}
|
|
94
|
-
const hash =
|
|
60
|
+
const hash = generateHash(imageInfo.base64);
|
|
95
61
|
// 检查是否已存在相同图片
|
|
96
62
|
const existingImage = Array.from(this.metadata.values()).find(m => m.hash === hash);
|
|
97
63
|
if (existingImage) {
|
|
@@ -100,7 +66,7 @@ class ImageStorageManager {
|
|
|
100
66
|
}
|
|
101
67
|
const buffer = Buffer.from(imageInfo.base64, 'base64');
|
|
102
68
|
const mimeType = `image/${imageInfo.type}`;
|
|
103
|
-
const dimensions =
|
|
69
|
+
const dimensions = getImageDimensions(buffer, mimeType);
|
|
104
70
|
const timestamp = Date.now();
|
|
105
71
|
const filename = `${timestamp}_${hash}.${imageInfo.type}`;
|
|
106
72
|
const filepath = path.join(this.config.storageDir, filename);
|
|
@@ -200,4 +166,3 @@ class ImageStorageManager {
|
|
|
200
166
|
return metadata.path;
|
|
201
167
|
}
|
|
202
168
|
}
|
|
203
|
-
exports.ImageStorageManager = ImageStorageManager;
|
package/lib/types.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
1
|
+
export {};
|
package/lib/utils.d.ts
CHANGED
package/lib/utils.js
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.formatSize = formatSize;
|
|
4
|
-
exports.extractImageInfo = extractImageInfo;
|
|
5
|
-
exports.generateHash = generateHash;
|
|
6
|
-
exports.getPNGDimensions = getPNGDimensions;
|
|
7
|
-
exports.getImageDimensions = getImageDimensions;
|
|
8
|
-
const crypto_1 = require("crypto");
|
|
1
|
+
import { createHash } from 'crypto';
|
|
9
2
|
/**
|
|
10
3
|
* 格式化字节大小为人类可读格式
|
|
11
4
|
*/
|
|
12
|
-
function formatSize(bytes) {
|
|
5
|
+
export function formatSize(bytes) {
|
|
13
6
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
14
7
|
let size = bytes;
|
|
15
8
|
let unitIndex = 0;
|
|
@@ -22,7 +15,7 @@ function formatSize(bytes) {
|
|
|
22
15
|
/**
|
|
23
16
|
* 从 data URL 提取图片信息
|
|
24
17
|
*/
|
|
25
|
-
function extractImageInfo(dataUrl) {
|
|
18
|
+
export function extractImageInfo(dataUrl) {
|
|
26
19
|
const match = dataUrl.match(/^data:image\/(png|jpeg|jpg|gif|webp);base64,(.+)$/);
|
|
27
20
|
if (!match)
|
|
28
21
|
return null;
|
|
@@ -34,13 +27,13 @@ function extractImageInfo(dataUrl) {
|
|
|
34
27
|
/**
|
|
35
28
|
* 生成 base64 数据的哈希值
|
|
36
29
|
*/
|
|
37
|
-
function generateHash(base64) {
|
|
38
|
-
return
|
|
30
|
+
export function generateHash(base64) {
|
|
31
|
+
return createHash('sha256').update(base64).digest('hex').substring(0, 16);
|
|
39
32
|
}
|
|
40
33
|
/**
|
|
41
34
|
* 获取 PNG 图片尺寸
|
|
42
35
|
*/
|
|
43
|
-
function getPNGDimensions(buffer) {
|
|
36
|
+
export function getPNGDimensions(buffer) {
|
|
44
37
|
try {
|
|
45
38
|
// PNG signature check
|
|
46
39
|
if (buffer.toString('hex', 0, 8) !== '89504e470d0a1a0a') {
|
|
@@ -58,7 +51,7 @@ function getPNGDimensions(buffer) {
|
|
|
58
51
|
/**
|
|
59
52
|
* 获取图片尺寸(目前仅支持 PNG)
|
|
60
53
|
*/
|
|
61
|
-
function getImageDimensions(buffer, mimeType) {
|
|
54
|
+
export function getImageDimensions(buffer, mimeType) {
|
|
62
55
|
if (mimeType === 'image/png') {
|
|
63
56
|
return getPNGDimensions(buffer);
|
|
64
57
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-tui-image-clipboard-fix",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"displayName": "Image Clipboard Fix",
|
|
5
|
-
"description": "OpenCode TUI plugin to fix image paste
|
|
6
|
+
"description": "OpenCode TUI plugin to fix image paste issues - saves clipboard images locally and replaces [Image N] with file paths",
|
|
6
7
|
"main": "lib/index.js",
|
|
7
8
|
"types": "lib/index.d.ts",
|
|
8
9
|
"exports": {
|