pinokiod 3.192.0 → 3.194.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.192.0",
3
+ "version": "3.194.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1163,6 +1163,111 @@ document.addEventListener("DOMContentLoaded", async () => {
1163
1163
  dropOverlay.className = "terminal-drop-overlay"
1164
1164
  dropOverlay.textContent = "Drop files to upload"
1165
1165
  terminalContainer.appendChild(dropOverlay)
1166
+ const dedupeClipboardFiles = (inputs) => {
1167
+ const seen = new Set()
1168
+ const results = []
1169
+ inputs.forEach((file) => {
1170
+ if (!file || !(file instanceof File) || !(file.size > 0)) {
1171
+ return
1172
+ }
1173
+ const signature = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1174
+ if (seen.has(signature)) {
1175
+ return
1176
+ }
1177
+ seen.add(signature)
1178
+ results.push(file)
1179
+ })
1180
+ return results
1181
+ }
1182
+ const collectClipboardFiles = (clipboardData) => {
1183
+ const files = []
1184
+ let hasFileFlavor = false
1185
+ if (!clipboardData) {
1186
+ return { files, hasFileFlavor }
1187
+ }
1188
+ try {
1189
+ const types = clipboardData.types ? Array.from(clipboardData.types) : []
1190
+ hasFileFlavor = types.some((type) => type === "Files" || type === "application/x-moz-file")
1191
+ } catch (_) {}
1192
+ try {
1193
+ Array.from(clipboardData.files || []).forEach((file) => {
1194
+ if (file && file.size > 0) {
1195
+ files.push(file)
1196
+ }
1197
+ })
1198
+ } catch (_) {}
1199
+ try {
1200
+ const items = clipboardData.items ? Array.from(clipboardData.items) : []
1201
+ items.forEach((item) => {
1202
+ if (!item || item.kind !== "file" || typeof item.getAsFile !== "function") {
1203
+ return
1204
+ }
1205
+ try {
1206
+ const file = item.getAsFile()
1207
+ if (file && file.size > 0) {
1208
+ files.push(file)
1209
+ }
1210
+ } catch (_) {}
1211
+ })
1212
+ } catch (_) {}
1213
+ return { files: dedupeClipboardFiles(files), hasFileFlavor }
1214
+ }
1215
+ const readClipboardFilesFallback = async () => {
1216
+ if (!navigator.clipboard || typeof navigator.clipboard.read !== "function") {
1217
+ return []
1218
+ }
1219
+ try {
1220
+ const clipboardItems = await navigator.clipboard.read()
1221
+ const collected = []
1222
+ let index = 0
1223
+ for (const item of clipboardItems) {
1224
+ if (!item || !Array.isArray(item.types)) {
1225
+ index += 1
1226
+ continue
1227
+ }
1228
+ const types = item.types
1229
+ const preferredType = types.find((type) => type && !type.startsWith("text/"))
1230
+ || types.find((type) => /^image\//i.test(type))
1231
+ || types[0]
1232
+ if (!preferredType) {
1233
+ index += 1
1234
+ continue
1235
+ }
1236
+ try {
1237
+ const blob = await item.getType(preferredType)
1238
+ if (!blob) {
1239
+ index += 1
1240
+ continue
1241
+ }
1242
+ let name = typeof item.name === "string" && item.name ? item.name : ""
1243
+ if (!name) {
1244
+ const ext = preferredType && preferredType.includes("/") ? preferredType.split("/").pop() : ""
1245
+ const safeExt = ext ? ext.replace(/[^a-z0-9]/gi, "").toLowerCase() : ""
1246
+ name = `clipboard-${Date.now()}-${index}${safeExt ? `.${safeExt}` : ""}`
1247
+ }
1248
+ const file = blob instanceof File ? blob : new File([blob], name, {
1249
+ type: blob.type || preferredType,
1250
+ lastModified: Date.now()
1251
+ })
1252
+ if (file && file.size > 0) {
1253
+ collected.push(file)
1254
+ }
1255
+ } catch (error) {
1256
+ if (error && error.name !== "NotAllowedError") {
1257
+ console.warn("Failed to extract clipboard blob", error)
1258
+ }
1259
+ }
1260
+ index += 1
1261
+ }
1262
+ return dedupeClipboardFiles(collected)
1263
+ } catch (error) {
1264
+ if (error && error.name !== "NotAllowedError") {
1265
+ console.warn("navigator.clipboard.read() failed", error)
1266
+ }
1267
+ return []
1268
+ }
1269
+ }
1270
+ let suppressClipboardText = false
1166
1271
  let dragDepth = 0
1167
1272
  const prevent = (event) => {
1168
1273
  event.preventDefault()
@@ -1221,6 +1326,65 @@ document.addEventListener("DOMContentLoaded", async () => {
1221
1326
  }
1222
1327
  this.term.focus()
1223
1328
  })
1329
+ terminalContainer.addEventListener("paste", async (event) => {
1330
+ const clipboard = event && event.clipboardData ? event.clipboardData : null
1331
+ const clipboardTypes = clipboard ? Array.from(clipboard.types || []) : []
1332
+ const hasFileLikeType = clipboardTypes.some((type) => {
1333
+ if (!type) {
1334
+ return false
1335
+ }
1336
+ const lower = type.toLowerCase()
1337
+ return lower === "text/uri-list" || lower === "text/html" || lower === "downloadurl" || lower === "text/x-moz-url" || lower.startsWith("image/")
1338
+ })
1339
+ const { files: directFiles, hasFileFlavor } = collectClipboardFiles(clipboard)
1340
+ let files = directFiles
1341
+ if (files.length === 0 && !hasFileFlavor && hasFileLikeType) {
1342
+ files = await readClipboardFilesFallback()
1343
+ }
1344
+ const allowUrlCollection = hasFileLikeType
1345
+ let remoteResources = []
1346
+ if (files.length === 0 && allowUrlCollection) {
1347
+ try {
1348
+ const extra = await this.collectFilesFromDataTransfer(clipboard)
1349
+ if (extra && Array.isArray(extra.urls) && extra.urls.length) {
1350
+ const seen = new Set()
1351
+ remoteResources = extra.urls.filter((item) => {
1352
+ if (!item || typeof item.href !== "string") {
1353
+ return false
1354
+ }
1355
+ const key = item.href.trim()
1356
+ if (!key || seen.has(key)) {
1357
+ return false
1358
+ }
1359
+ seen.add(key)
1360
+ return true
1361
+ })
1362
+ }
1363
+ } catch (error) {
1364
+ console.warn("Failed to collect clipboard resources", error)
1365
+ }
1366
+ }
1367
+ if (files.length === 0 && (!remoteResources || remoteResources.length === 0)) {
1368
+ suppressClipboardText = false
1369
+ return
1370
+ }
1371
+ event.preventDefault()
1372
+ event.stopPropagation()
1373
+ suppressClipboardText = true
1374
+ try {
1375
+ if (remoteResources && remoteResources.length > 0) {
1376
+ await this.uploadRemoteResources(remoteResources, dropOverlay)
1377
+ }
1378
+ } catch (error) {
1379
+ console.warn("Clipboard remote upload failed", error)
1380
+ }
1381
+ if (files.length > 0) {
1382
+ await this.uploadFiles(files, dropOverlay).catch((error) => {
1383
+ console.warn("Clipboard upload failed", error)
1384
+ })
1385
+ }
1386
+ this.term.focus()
1387
+ }, true)
1224
1388
  term.attachCustomKeyEventHandler(event => {
1225
1389
  if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
1226
1390
  const selection = term.getSelection();
@@ -1230,16 +1394,19 @@ document.addEventListener("DOMContentLoaded", async () => {
1230
1394
  }
1231
1395
  }
1232
1396
  if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
1233
- navigator.clipboard.readText().then((text) => {
1234
- this.socket.run({
1235
- //key: "\x1b[200~" + text + "\x1b[201~",
1236
- key: text,
1237
- id: shell_id,
1238
- paste: true
1239
- })
1240
- this.captureTextInput(text)
1397
+ if (!suppressClipboardText) {
1398
+ navigator.clipboard.readText().then((text) => {
1399
+ this.socket.run({
1400
+ //key: "\x1b[200~" + text + "\x1b[201~",
1401
+ key: text,
1402
+ id: shell_id,
1403
+ paste: true
1404
+ })
1405
+ this.captureTextInput(text)
1241
1406
 
1242
- })
1407
+ })
1408
+ }
1409
+ suppressClipboardText = false
1243
1410
  return false
1244
1411
  }
1245
1412
  return true;
@@ -1256,6 +1256,111 @@ document.addEventListener("DOMContentLoaded", async () => {
1256
1256
  dropOverlay.className = "terminal-drop-overlay"
1257
1257
  dropOverlay.textContent = "Drop files to upload"
1258
1258
  terminalContainer.appendChild(dropOverlay)
1259
+ const dedupeClipboardFiles = (inputs) => {
1260
+ const seen = new Set()
1261
+ const results = []
1262
+ inputs.forEach((file) => {
1263
+ if (!file || !(file instanceof File) || !(file.size > 0)) {
1264
+ return
1265
+ }
1266
+ const signature = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1267
+ if (seen.has(signature)) {
1268
+ return
1269
+ }
1270
+ seen.add(signature)
1271
+ results.push(file)
1272
+ })
1273
+ return results
1274
+ }
1275
+ const collectClipboardFiles = (clipboardData) => {
1276
+ const files = []
1277
+ let hasFileFlavor = false
1278
+ if (!clipboardData) {
1279
+ return { files, hasFileFlavor }
1280
+ }
1281
+ try {
1282
+ const types = clipboardData.types ? Array.from(clipboardData.types) : []
1283
+ hasFileFlavor = types.some((type) => type === "Files" || type === "application/x-moz-file")
1284
+ } catch (_) {}
1285
+ try {
1286
+ Array.from(clipboardData.files || []).forEach((file) => {
1287
+ if (file && file.size > 0) {
1288
+ files.push(file)
1289
+ }
1290
+ })
1291
+ } catch (_) {}
1292
+ try {
1293
+ const items = clipboardData.items ? Array.from(clipboardData.items) : []
1294
+ items.forEach((item) => {
1295
+ if (!item || item.kind !== "file" || typeof item.getAsFile !== "function") {
1296
+ return
1297
+ }
1298
+ try {
1299
+ const file = item.getAsFile()
1300
+ if (file && file.size > 0) {
1301
+ files.push(file)
1302
+ }
1303
+ } catch (_) {}
1304
+ })
1305
+ } catch (_) {}
1306
+ return { files: dedupeClipboardFiles(files), hasFileFlavor }
1307
+ }
1308
+ const readClipboardFilesFallback = async () => {
1309
+ if (!navigator.clipboard || typeof navigator.clipboard.read !== "function") {
1310
+ return []
1311
+ }
1312
+ try {
1313
+ const clipboardItems = await navigator.clipboard.read()
1314
+ const collected = []
1315
+ let index = 0
1316
+ for (const item of clipboardItems) {
1317
+ if (!item || !Array.isArray(item.types)) {
1318
+ index += 1
1319
+ continue
1320
+ }
1321
+ const types = item.types
1322
+ const preferredType = types.find((type) => type && !type.startsWith("text/"))
1323
+ || types.find((type) => /^image\//i.test(type))
1324
+ || types[0]
1325
+ if (!preferredType) {
1326
+ index += 1
1327
+ continue
1328
+ }
1329
+ try {
1330
+ const blob = await item.getType(preferredType)
1331
+ if (!blob) {
1332
+ index += 1
1333
+ continue
1334
+ }
1335
+ let name = typeof item.name === "string" && item.name ? item.name : ""
1336
+ if (!name) {
1337
+ const ext = preferredType && preferredType.includes("/") ? preferredType.split("/").pop() : ""
1338
+ const safeExt = ext ? ext.replace(/[^a-z0-9]/gi, "").toLowerCase() : ""
1339
+ name = `clipboard-${Date.now()}-${index}${safeExt ? `.${safeExt}` : ""}`
1340
+ }
1341
+ const file = blob instanceof File ? blob : new File([blob], name, {
1342
+ type: blob.type || preferredType,
1343
+ lastModified: Date.now()
1344
+ })
1345
+ if (file && file.size > 0) {
1346
+ collected.push(file)
1347
+ }
1348
+ } catch (error) {
1349
+ if (error && error.name !== "NotAllowedError") {
1350
+ console.warn("Failed to extract clipboard blob", error)
1351
+ }
1352
+ }
1353
+ index += 1
1354
+ }
1355
+ return dedupeClipboardFiles(collected)
1356
+ } catch (error) {
1357
+ if (error && error.name !== "NotAllowedError") {
1358
+ console.warn("navigator.clipboard.read() failed", error)
1359
+ }
1360
+ return []
1361
+ }
1362
+ }
1363
+ let suppressClipboardText = false
1259
1364
  let dragDepth = 0
1260
1365
  const prevent = (event) => {
1261
1366
  event.preventDefault()
@@ -1318,6 +1423,65 @@ document.addEventListener("DOMContentLoaded", async () => {
1318
1423
  }
1319
1424
  this.term.focus()
1320
1425
  })
1426
+ terminalContainer.addEventListener("paste", async (event) => {
1427
+ const clipboard = event && event.clipboardData ? event.clipboardData : null
1428
+ const clipboardTypes = clipboard ? Array.from(clipboard.types || []) : []
1429
+ const hasFileLikeType = clipboardTypes.some((type) => {
1430
+ if (!type) {
1431
+ return false
1432
+ }
1433
+ const lower = type.toLowerCase()
1434
+ return lower === "text/uri-list" || lower === "text/html" || lower === "downloadurl" || lower === "text/x-moz-url" || lower.startsWith("image/")
1435
+ })
1436
+ const { files: directFiles, hasFileFlavor } = collectClipboardFiles(clipboard)
1437
+ let files = directFiles
1438
+ if (files.length === 0 && !hasFileFlavor && hasFileLikeType) {
1439
+ files = await readClipboardFilesFallback()
1440
+ }
1441
+ const allowUrlCollection = hasFileLikeType
1442
+ let remoteResources = []
1443
+ if (files.length === 0 && allowUrlCollection) {
1444
+ try {
1445
+ const extra = await this.collectFilesFromDataTransfer(clipboard)
1446
+ if (extra && Array.isArray(extra.urls) && extra.urls.length) {
1447
+ const seen = new Set()
1448
+ remoteResources = extra.urls.filter((item) => {
1449
+ if (!item || typeof item.href !== "string") {
1450
+ return false
1451
+ }
1452
+ const key = item.href.trim()
1453
+ if (!key || seen.has(key)) {
1454
+ return false
1455
+ }
1456
+ seen.add(key)
1457
+ return true
1458
+ })
1459
+ }
1460
+ } catch (error) {
1461
+ console.warn("Failed to collect clipboard resources", error)
1462
+ }
1463
+ }
1464
+ if (files.length === 0 && (!remoteResources || remoteResources.length === 0)) {
1465
+ suppressClipboardText = false
1466
+ return
1467
+ }
1468
+ event.preventDefault()
1469
+ event.stopPropagation()
1470
+ suppressClipboardText = true
1471
+ try {
1472
+ if (remoteResources && remoteResources.length > 0) {
1473
+ await this.uploadRemoteResources(remoteResources, dropOverlay)
1474
+ }
1475
+ } catch (error) {
1476
+ console.warn("Clipboard remote upload failed", error)
1477
+ }
1478
+ if (files.length > 0) {
1479
+ await this.uploadFiles(files, dropOverlay).catch((error) => {
1480
+ console.warn("Clipboard upload failed", error)
1481
+ })
1482
+ }
1483
+ this.term.focus()
1484
+ }, true)
1321
1485
  term.attachCustomKeyEventHandler(event => {
1322
1486
  if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
1323
1487
  const selection = term.getSelection();
@@ -1327,14 +1491,15 @@ document.addEventListener("DOMContentLoaded", async () => {
1327
1491
  }
1328
1492
  }
1329
1493
  if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
1330
- navigator.clipboard.readText().then((text) => {
1331
- this.socket.run({
1332
- //key: "\x1b[200~" + text + "\x1b[201~",
1333
- key: text,
1334
- id: shell_id,
1335
- paste: true
1336
- })
1337
- this.captureTextInput(text)
1494
+ if (!suppressClipboardText) {
1495
+ navigator.clipboard.readText().then((text) => {
1496
+ this.socket.run({
1497
+ //key: "\x1b[200~" + text + "\x1b[201~",
1498
+ key: text,
1499
+ id: shell_id,
1500
+ paste: true
1501
+ })
1502
+ this.captureTextInput(text)
1338
1503
 
1339
1504
 
1340
1505
  // this.socket.run({
@@ -1342,7 +1507,9 @@ document.addEventListener("DOMContentLoaded", async () => {
1342
1507
  // key: text,
1343
1508
  // id: shell_id
1344
1509
  // })
1345
- })
1510
+ })
1511
+ }
1512
+ suppressClipboardText = false
1346
1513
  return false
1347
1514
  }
1348
1515
  return true;