pinokiod 3.193.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.193.0",
3
+ "version": "3.194.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1163,20 +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 filterUniqueResources = (urls) => {
1166
+ const dedupeClipboardFiles = (inputs) => {
1167
1167
  const seen = new Set()
1168
- return (Array.isArray(urls) ? urls : []).filter((item) => {
1169
- if (!item || typeof item.href !== "string") {
1170
- return false
1168
+ const results = []
1169
+ inputs.forEach((file) => {
1170
+ if (!file || !(file instanceof File) || !(file.size > 0)) {
1171
+ return
1171
1172
  }
1172
- const key = item.href.trim()
1173
- if (!key || seen.has(key)) {
1174
- return false
1173
+ const signature = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1174
+ if (seen.has(signature)) {
1175
+ return
1175
1176
  }
1176
- seen.add(key)
1177
- return true
1177
+ seen.add(signature)
1178
+ results.push(file)
1178
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
+ }
1179
1269
  }
1270
+ let suppressClipboardText = false
1180
1271
  let dragDepth = 0
1181
1272
  const prevent = (event) => {
1182
1273
  event.preventDefault()
@@ -1203,7 +1294,20 @@ document.addEventListener("DOMContentLoaded", async () => {
1203
1294
  let remoteResources = []
1204
1295
  try {
1205
1296
  const extra = await this.collectFilesFromDataTransfer(event.dataTransfer)
1206
- remoteResources = filterUniqueResources(extra && extra.urls)
1297
+ if (extra && Array.isArray(extra.urls) && extra.urls.length) {
1298
+ const seenUrls = new Set()
1299
+ remoteResources = extra.urls.filter((item) => {
1300
+ if (!item || typeof item.href !== "string") {
1301
+ return false
1302
+ }
1303
+ const key = item.href.trim()
1304
+ if (!key || seenUrls.has(key)) {
1305
+ return false
1306
+ }
1307
+ seenUrls.add(key)
1308
+ return true
1309
+ })
1310
+ }
1207
1311
  } catch (error) {
1208
1312
  console.warn("Failed to collect files from drop payload", error)
1209
1313
  }
@@ -1223,23 +1327,50 @@ document.addEventListener("DOMContentLoaded", async () => {
1223
1327
  this.term.focus()
1224
1328
  })
1225
1329
  terminalContainer.addEventListener("paste", async (event) => {
1226
- const clipboard = event.clipboardData || window.clipboardData || null
1227
- if (!clipboard) {
1228
- return
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()
1229
1343
  }
1230
- const files = Array.from(clipboard.files || [])
1344
+ const allowUrlCollection = hasFileLikeType
1231
1345
  let remoteResources = []
1232
- try {
1233
- const extra = await this.collectFilesFromDataTransfer(clipboard)
1234
- remoteResources = filterUniqueResources(extra && extra.urls)
1235
- } catch (error) {
1236
- console.warn("Failed to collect clipboard resources", error)
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
+ }
1237
1366
  }
1238
- if (!files.length && (!remoteResources || remoteResources.length === 0)) {
1367
+ if (files.length === 0 && (!remoteResources || remoteResources.length === 0)) {
1368
+ suppressClipboardText = false
1239
1369
  return
1240
1370
  }
1241
1371
  event.preventDefault()
1242
1372
  event.stopPropagation()
1373
+ suppressClipboardText = true
1243
1374
  try {
1244
1375
  if (remoteResources && remoteResources.length > 0) {
1245
1376
  await this.uploadRemoteResources(remoteResources, dropOverlay)
@@ -1248,10 +1379,12 @@ document.addEventListener("DOMContentLoaded", async () => {
1248
1379
  console.warn("Clipboard remote upload failed", error)
1249
1380
  }
1250
1381
  if (files.length > 0) {
1251
- await this.uploadFiles(files, dropOverlay)
1382
+ await this.uploadFiles(files, dropOverlay).catch((error) => {
1383
+ console.warn("Clipboard upload failed", error)
1384
+ })
1252
1385
  }
1253
1386
  this.term.focus()
1254
- })
1387
+ }, true)
1255
1388
  term.attachCustomKeyEventHandler(event => {
1256
1389
  if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
1257
1390
  const selection = term.getSelection();
@@ -1261,16 +1394,19 @@ document.addEventListener("DOMContentLoaded", async () => {
1261
1394
  }
1262
1395
  }
1263
1396
  if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
1264
- navigator.clipboard.readText().then((text) => {
1265
- this.socket.run({
1266
- //key: "\x1b[200~" + text + "\x1b[201~",
1267
- key: text,
1268
- id: shell_id,
1269
- paste: true
1270
- })
1271
- 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)
1272
1406
 
1273
- })
1407
+ })
1408
+ }
1409
+ suppressClipboardText = false
1274
1410
  return false
1275
1411
  }
1276
1412
  return true;
@@ -1256,20 +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 filterUniqueResources = (urls) => {
1259
+ const dedupeClipboardFiles = (inputs) => {
1260
1260
  const seen = new Set()
1261
- return (Array.isArray(urls) ? urls : []).filter((item) => {
1262
- if (!item || typeof item.href !== "string") {
1263
- return false
1261
+ const results = []
1262
+ inputs.forEach((file) => {
1263
+ if (!file || !(file instanceof File) || !(file.size > 0)) {
1264
+ return
1264
1265
  }
1265
- const key = item.href.trim()
1266
- if (!key || seen.has(key)) {
1267
- return false
1266
+ const signature = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1267
+ if (seen.has(signature)) {
1268
+ return
1268
1269
  }
1269
- seen.add(key)
1270
- return true
1270
+ seen.add(signature)
1271
+ results.push(file)
1271
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
+ }
1272
1362
  }
1363
+ let suppressClipboardText = false
1273
1364
  let dragDepth = 0
1274
1365
  const prevent = (event) => {
1275
1366
  event.preventDefault()
@@ -1296,7 +1387,20 @@ document.addEventListener("DOMContentLoaded", async () => {
1296
1387
  let remoteResources = []
1297
1388
  try {
1298
1389
  const extra = await this.collectFilesFromDataTransfer(event.dataTransfer)
1299
- remoteResources = filterUniqueResources(extra && extra.urls)
1390
+ if (extra && Array.isArray(extra.urls) && extra.urls.length) {
1391
+ const seenUrls = new Set()
1392
+ remoteResources = extra.urls.filter((item) => {
1393
+ if (!item || typeof item.href !== "string") {
1394
+ return false
1395
+ }
1396
+ const key = item.href.trim()
1397
+ if (!key || seenUrls.has(key)) {
1398
+ return false
1399
+ }
1400
+ seenUrls.add(key)
1401
+ return true
1402
+ })
1403
+ }
1300
1404
  } catch (error) {
1301
1405
  console.warn("Failed to collect files from drop payload", error)
1302
1406
  }
@@ -1320,23 +1424,50 @@ document.addEventListener("DOMContentLoaded", async () => {
1320
1424
  this.term.focus()
1321
1425
  })
1322
1426
  terminalContainer.addEventListener("paste", async (event) => {
1323
- const clipboard = event.clipboardData || window.clipboardData || null
1324
- if (!clipboard) {
1325
- return
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()
1326
1440
  }
1327
- const files = Array.from(clipboard.files || [])
1441
+ const allowUrlCollection = hasFileLikeType
1328
1442
  let remoteResources = []
1329
- try {
1330
- const extra = await this.collectFilesFromDataTransfer(clipboard)
1331
- remoteResources = filterUniqueResources(extra && extra.urls)
1332
- } catch (error) {
1333
- console.warn("Failed to collect clipboard resources", error)
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
+ }
1334
1463
  }
1335
- if (!files.length && (!remoteResources || remoteResources.length === 0)) {
1464
+ if (files.length === 0 && (!remoteResources || remoteResources.length === 0)) {
1465
+ suppressClipboardText = false
1336
1466
  return
1337
1467
  }
1338
1468
  event.preventDefault()
1339
1469
  event.stopPropagation()
1470
+ suppressClipboardText = true
1340
1471
  try {
1341
1472
  if (remoteResources && remoteResources.length > 0) {
1342
1473
  await this.uploadRemoteResources(remoteResources, dropOverlay)
@@ -1345,10 +1476,12 @@ document.addEventListener("DOMContentLoaded", async () => {
1345
1476
  console.warn("Clipboard remote upload failed", error)
1346
1477
  }
1347
1478
  if (files.length > 0) {
1348
- await this.uploadFiles(files, dropOverlay)
1479
+ await this.uploadFiles(files, dropOverlay).catch((error) => {
1480
+ console.warn("Clipboard upload failed", error)
1481
+ })
1349
1482
  }
1350
1483
  this.term.focus()
1351
- })
1484
+ }, true)
1352
1485
  term.attachCustomKeyEventHandler(event => {
1353
1486
  if ((event.ctrlKey || event.metaKey) && event.key === 'c') {
1354
1487
  const selection = term.getSelection();
@@ -1358,14 +1491,15 @@ document.addEventListener("DOMContentLoaded", async () => {
1358
1491
  }
1359
1492
  }
1360
1493
  if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
1361
- navigator.clipboard.readText().then((text) => {
1362
- this.socket.run({
1363
- //key: "\x1b[200~" + text + "\x1b[201~",
1364
- key: text,
1365
- id: shell_id,
1366
- paste: true
1367
- })
1368
- 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)
1369
1503
 
1370
1504
 
1371
1505
  // this.socket.run({
@@ -1373,7 +1507,9 @@ document.addEventListener("DOMContentLoaded", async () => {
1373
1507
  // key: text,
1374
1508
  // id: shell_id
1375
1509
  // })
1376
- })
1510
+ })
1511
+ }
1512
+ suppressClipboardText = false
1377
1513
  return false
1378
1514
  }
1379
1515
  return true;