pinokiod 3.194.0 → 3.196.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.194.0",
3
+ "version": "3.196.0",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -2271,6 +2271,8 @@ class Server {
2271
2271
 
2272
2272
  if (meta) {
2273
2273
  items = running.concat(notRunning)
2274
+
2275
+ console.log("AGENT", req.agent)
2274
2276
  res.render("index", {
2275
2277
  list,
2276
2278
  current_host: this.kernel.peer.host,
@@ -2168,9 +2168,9 @@ document.addEventListener("DOMContentLoaded", () => {
2168
2168
  setTabTooltips();
2169
2169
  initTippy();
2170
2170
 
2171
- if (window !== window.top) {
2172
- document.body.removeAttribute("data-agent")
2173
- }
2171
+ // if (window !== window.top) {
2172
+ // document.body.removeAttribute("data-agent")
2173
+ // }
2174
2174
 
2175
2175
  // Listen for window resize
2176
2176
  window.addEventListener('resize', updateAllTooltips);
@@ -475,6 +475,8 @@ document.addEventListener("DOMContentLoaded", () => {
475
475
  const inspectorButton = document.querySelector('#inspector');
476
476
  const isDesktop = agent === 'electron';
477
477
 
478
+ console.log({ agent, isDesktop })
479
+
478
480
  if (inspectorButton && !isDesktop) {
479
481
  const message = 'The 1-click inspect feature is only available inside the Pinokio desktop app.';
480
482
 
@@ -1181,20 +1181,28 @@ document.addEventListener("DOMContentLoaded", async () => {
1181
1181
  }
1182
1182
  const collectClipboardFiles = (clipboardData) => {
1183
1183
  const files = []
1184
- let hasFileFlavor = false
1184
+ const seen = new Set()
1185
1185
  if (!clipboardData) {
1186
- return { files, hasFileFlavor }
1186
+ return { files, hasFileFlavor: false }
1187
1187
  }
1188
+ let hasFileFlavor = false
1188
1189
  try {
1189
- const types = clipboardData.types ? Array.from(clipboardData.types) : []
1190
+ const types = Array.from(clipboardData.types || [])
1190
1191
  hasFileFlavor = types.some((type) => type === "Files" || type === "application/x-moz-file")
1191
1192
  } catch (_) {}
1193
+ const pushIfUnique = (file) => {
1194
+ if (!file || !(file instanceof File) || !(file.size > 0)) {
1195
+ return
1196
+ }
1197
+ const key = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1198
+ if (seen.has(key)) {
1199
+ return
1200
+ }
1201
+ seen.add(key)
1202
+ files.push(file)
1203
+ }
1192
1204
  try {
1193
- Array.from(clipboardData.files || []).forEach((file) => {
1194
- if (file && file.size > 0) {
1195
- files.push(file)
1196
- }
1197
- })
1205
+ Array.from(clipboardData.files || []).forEach(pushIfUnique)
1198
1206
  } catch (_) {}
1199
1207
  try {
1200
1208
  const items = clipboardData.items ? Array.from(clipboardData.items) : []
@@ -1203,14 +1211,11 @@ document.addEventListener("DOMContentLoaded", async () => {
1203
1211
  return
1204
1212
  }
1205
1213
  try {
1206
- const file = item.getAsFile()
1207
- if (file && file.size > 0) {
1208
- files.push(file)
1209
- }
1214
+ pushIfUnique(item.getAsFile())
1210
1215
  } catch (_) {}
1211
1216
  })
1212
1217
  } catch (_) {}
1213
- return { files: dedupeClipboardFiles(files), hasFileFlavor }
1218
+ return { files, hasFileFlavor }
1214
1219
  }
1215
1220
  const readClipboardFilesFallback = async () => {
1216
1221
  if (!navigator.clipboard || typeof navigator.clipboard.read !== "function") {
@@ -1219,6 +1224,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1219
1224
  try {
1220
1225
  const clipboardItems = await navigator.clipboard.read()
1221
1226
  const collected = []
1227
+ const seen = new Set()
1222
1228
  let index = 0
1223
1229
  for (const item of clipboardItems) {
1224
1230
  if (!item || !Array.isArray(item.types)) {
@@ -1249,9 +1255,13 @@ document.addEventListener("DOMContentLoaded", async () => {
1249
1255
  type: blob.type || preferredType,
1250
1256
  lastModified: Date.now()
1251
1257
  })
1252
- if (file && file.size > 0) {
1253
- collected.push(file)
1258
+ const key = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1259
+ if (seen.has(key)) {
1260
+ index += 1
1261
+ continue
1254
1262
  }
1263
+ seen.add(key)
1264
+ collected.push(file)
1255
1265
  } catch (error) {
1256
1266
  if (error && error.name !== "NotAllowedError") {
1257
1267
  console.warn("Failed to extract clipboard blob", error)
@@ -1259,7 +1269,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1259
1269
  }
1260
1270
  index += 1
1261
1271
  }
1262
- return dedupeClipboardFiles(collected)
1272
+ return collected
1263
1273
  } catch (error) {
1264
1274
  if (error && error.name !== "NotAllowedError") {
1265
1275
  console.warn("navigator.clipboard.read() failed", error)
@@ -1267,6 +1277,101 @@ document.addEventListener("DOMContentLoaded", async () => {
1267
1277
  return []
1268
1278
  }
1269
1279
  }
1280
+ const extractUrlsFromClipboard = (clipboardData) => {
1281
+ const urls = []
1282
+ if (!clipboardData) {
1283
+ return urls
1284
+ }
1285
+ const seen = new Set()
1286
+ const pushUrl = (href, nameHint) => {
1287
+ if (!href) {
1288
+ return
1289
+ }
1290
+ let resolved
1291
+ try {
1292
+ resolved = new URL(href, window.location.href)
1293
+ } catch (_) {
1294
+ try {
1295
+ resolved = new URL(href)
1296
+ } catch (_) {
1297
+ return
1298
+ }
1299
+ }
1300
+ if (!/^https?:$/i.test(resolved.protocol)) {
1301
+ return
1302
+ }
1303
+ const key = resolved.href
1304
+ if (seen.has(key)) {
1305
+ return
1306
+ }
1307
+ seen.add(key)
1308
+ urls.push({ href: resolved.href, name: nameHint || null })
1309
+ }
1310
+ let html = ""
1311
+ let uriList = ""
1312
+ if (typeof clipboardData.getData === "function") {
1313
+ try { html = clipboardData.getData("text/html") || "" } catch (_) {}
1314
+ try { uriList = clipboardData.getData("text/uri-list") || "" } catch (_) {}
1315
+ }
1316
+ if (uriList) {
1317
+ uriList.split(/\r?\n/).forEach((line) => {
1318
+ const trimmed = line.trim()
1319
+ if (!trimmed || trimmed.startsWith("#")) {
1320
+ return
1321
+ }
1322
+ pushUrl(trimmed, null)
1323
+ })
1324
+ }
1325
+ if (html) {
1326
+ try {
1327
+ const parser = new DOMParser()
1328
+ const doc = parser.parseFromString(html, "text/html")
1329
+ doc.querySelectorAll("img[src]").forEach((img) => {
1330
+ const src = img.getAttribute("src")
1331
+ const nameHint = img.getAttribute("alt") || img.getAttribute("title") || null
1332
+ pushUrl(src, nameHint)
1333
+ })
1334
+ doc.querySelectorAll("a[href]").forEach((anchor) => {
1335
+ const href = anchor.getAttribute("href")
1336
+ const nameHint = anchor.getAttribute("download") || (anchor.textContent ? anchor.textContent.trim() : null)
1337
+ pushUrl(href, nameHint)
1338
+ })
1339
+ } catch (error) {
1340
+ console.warn("Failed to parse clipboard HTML", error)
1341
+ }
1342
+ }
1343
+ return urls
1344
+ }
1345
+ const isLikelyImagePaste = (clipboardData) => {
1346
+ if (!clipboardData) {
1347
+ return false
1348
+ }
1349
+ const types = Array.from(clipboardData.types || [])
1350
+ if (types.some((type) => type && type.toLowerCase().startsWith("image/"))) {
1351
+ return true
1352
+ }
1353
+ if (typeof clipboardData.getData === "function") {
1354
+ try {
1355
+ const html = clipboardData.getData("text/html") || ""
1356
+ if (html && /<img\b/i.test(html)) {
1357
+ return true
1358
+ }
1359
+ } catch (_) {}
1360
+ try {
1361
+ const uriList = clipboardData.getData("text/uri-list") || ""
1362
+ if (uriList && uriList.split(/\r?\n/).some((line) => {
1363
+ const trimmed = line.trim()
1364
+ if (!trimmed || trimmed.startsWith("#")) {
1365
+ return false
1366
+ }
1367
+ return /^https?:\/\//i.test(trimmed)
1368
+ })) {
1369
+ return true
1370
+ }
1371
+ } catch (_) {}
1372
+ }
1373
+ return false
1374
+ }
1270
1375
  let suppressClipboardText = false
1271
1376
  let dragDepth = 0
1272
1377
  const prevent = (event) => {
@@ -1328,43 +1433,22 @@ document.addEventListener("DOMContentLoaded", async () => {
1328
1433
  })
1329
1434
  terminalContainer.addEventListener("paste", async (event) => {
1330
1435
  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
1436
  const { files: directFiles, hasFileFlavor } = collectClipboardFiles(clipboard)
1340
1437
  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)
1438
+ let remoteUrls = []
1439
+ if (files.length === 0) {
1440
+ remoteUrls = extractUrlsFromClipboard(clipboard)
1441
+ if (remoteUrls.length === 0 && !hasFileFlavor && isLikelyImagePaste(clipboard)) {
1442
+ files = await readClipboardFilesFallback()
1365
1443
  }
1366
1444
  }
1367
- if (files.length === 0 && (!remoteResources || remoteResources.length === 0)) {
1445
+ console.log('[clipboard paste][shell]', {
1446
+ files: files.map((file) => ({ name: file.name, size: file.size, type: file.type })),
1447
+ remoteUrls,
1448
+ hasFileFlavor,
1449
+ types: clipboard ? Array.from(clipboard.types || []) : []
1450
+ })
1451
+ if (files.length === 0 && remoteUrls.length === 0) {
1368
1452
  suppressClipboardText = false
1369
1453
  return
1370
1454
  }
@@ -1372,16 +1456,14 @@ document.addEventListener("DOMContentLoaded", async () => {
1372
1456
  event.stopPropagation()
1373
1457
  suppressClipboardText = true
1374
1458
  try {
1375
- if (remoteResources && remoteResources.length > 0) {
1376
- await this.uploadRemoteResources(remoteResources, dropOverlay)
1459
+ if (remoteUrls.length > 0) {
1460
+ await this.uploadRemoteResources(remoteUrls, dropOverlay)
1461
+ }
1462
+ if (files.length > 0) {
1463
+ await this.uploadFiles(files, dropOverlay)
1377
1464
  }
1378
1465
  } 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
- })
1466
+ console.warn("Clipboard upload failed", error)
1385
1467
  }
1386
1468
  this.term.focus()
1387
1469
  }, true)
@@ -1396,6 +1478,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1396
1478
  if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
1397
1479
  if (!suppressClipboardText) {
1398
1480
  navigator.clipboard.readText().then((text) => {
1481
+ console.log('[clipboard paste][shell][text]', text)
1399
1482
  this.socket.run({
1400
1483
  //key: "\x1b[200~" + text + "\x1b[201~",
1401
1484
  key: text,
@@ -767,6 +767,7 @@ document.addEventListener("DOMContentLoaded", async () => {
767
767
  } else if (packet.type === "result") {
768
768
  if (packet.id === "terminal.upload") {
769
769
  const uploaded = Array.isArray(packet.data && packet.data.files) ? packet.data.files : []
770
+ console.log('[terminal.upload][terminal][result]', uploaded)
770
771
  if (uploaded.length > 0) {
771
772
  const mappedFiles = uploaded.map((file) => {
772
773
  const displayPath = file.displayPath || file.homeRelativePath ? `~/${(file.homeRelativePath || '').replace(/^\/+/, '')}` : (file.path || '')
@@ -1274,20 +1275,28 @@ document.addEventListener("DOMContentLoaded", async () => {
1274
1275
  }
1275
1276
  const collectClipboardFiles = (clipboardData) => {
1276
1277
  const files = []
1277
- let hasFileFlavor = false
1278
+ const seen = new Set()
1278
1279
  if (!clipboardData) {
1279
- return { files, hasFileFlavor }
1280
+ return { files, hasFileFlavor: false }
1280
1281
  }
1282
+ let hasFileFlavor = false
1281
1283
  try {
1282
- const types = clipboardData.types ? Array.from(clipboardData.types) : []
1284
+ const types = Array.from(clipboardData.types || [])
1283
1285
  hasFileFlavor = types.some((type) => type === "Files" || type === "application/x-moz-file")
1284
1286
  } catch (_) {}
1287
+ const pushIfUnique = (file) => {
1288
+ if (!file || !(file instanceof File) || !(file.size > 0)) {
1289
+ return
1290
+ }
1291
+ const key = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1292
+ if (seen.has(key)) {
1293
+ return
1294
+ }
1295
+ seen.add(key)
1296
+ files.push(file)
1297
+ }
1285
1298
  try {
1286
- Array.from(clipboardData.files || []).forEach((file) => {
1287
- if (file && file.size > 0) {
1288
- files.push(file)
1289
- }
1290
- })
1299
+ Array.from(clipboardData.files || []).forEach(pushIfUnique)
1291
1300
  } catch (_) {}
1292
1301
  try {
1293
1302
  const items = clipboardData.items ? Array.from(clipboardData.items) : []
@@ -1296,14 +1305,11 @@ document.addEventListener("DOMContentLoaded", async () => {
1296
1305
  return
1297
1306
  }
1298
1307
  try {
1299
- const file = item.getAsFile()
1300
- if (file && file.size > 0) {
1301
- files.push(file)
1302
- }
1308
+ pushIfUnique(item.getAsFile())
1303
1309
  } catch (_) {}
1304
1310
  })
1305
1311
  } catch (_) {}
1306
- return { files: dedupeClipboardFiles(files), hasFileFlavor }
1312
+ return { files, hasFileFlavor }
1307
1313
  }
1308
1314
  const readClipboardFilesFallback = async () => {
1309
1315
  if (!navigator.clipboard || typeof navigator.clipboard.read !== "function") {
@@ -1312,6 +1318,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1312
1318
  try {
1313
1319
  const clipboardItems = await navigator.clipboard.read()
1314
1320
  const collected = []
1321
+ const seen = new Set()
1315
1322
  let index = 0
1316
1323
  for (const item of clipboardItems) {
1317
1324
  if (!item || !Array.isArray(item.types)) {
@@ -1342,9 +1349,13 @@ document.addEventListener("DOMContentLoaded", async () => {
1342
1349
  type: blob.type || preferredType,
1343
1350
  lastModified: Date.now()
1344
1351
  })
1345
- if (file && file.size > 0) {
1346
- collected.push(file)
1352
+ const key = `${file.name || ''}::${file.size || 0}::${file.type || ''}`
1353
+ if (seen.has(key)) {
1354
+ index += 1
1355
+ continue
1347
1356
  }
1357
+ seen.add(key)
1358
+ collected.push(file)
1348
1359
  } catch (error) {
1349
1360
  if (error && error.name !== "NotAllowedError") {
1350
1361
  console.warn("Failed to extract clipboard blob", error)
@@ -1352,7 +1363,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1352
1363
  }
1353
1364
  index += 1
1354
1365
  }
1355
- return dedupeClipboardFiles(collected)
1366
+ return collected
1356
1367
  } catch (error) {
1357
1368
  if (error && error.name !== "NotAllowedError") {
1358
1369
  console.warn("navigator.clipboard.read() failed", error)
@@ -1360,108 +1371,120 @@ document.addEventListener("DOMContentLoaded", async () => {
1360
1371
  return []
1361
1372
  }
1362
1373
  }
1363
- let suppressClipboardText = false
1364
- let dragDepth = 0
1365
- const prevent = (event) => {
1366
- event.preventDefault()
1367
- event.stopPropagation()
1368
- }
1369
- terminalContainer.addEventListener("dragenter", (event) => {
1370
- prevent(event)
1371
- dragDepth += 1
1372
- dropOverlay.classList.add("active")
1373
- })
1374
- terminalContainer.addEventListener("dragover", prevent)
1375
- terminalContainer.addEventListener("dragleave", (event) => {
1376
- prevent(event)
1377
- dragDepth = Math.max(0, dragDepth - 1)
1378
- if (dragDepth === 0) {
1379
- dropOverlay.classList.remove("active")
1374
+ const extractUrlsFromClipboard = (clipboardData) => {
1375
+ const urls = []
1376
+ if (!clipboardData) {
1377
+ return urls
1380
1378
  }
1381
- })
1382
- terminalContainer.addEventListener("drop", async (event) => {
1383
- prevent(event)
1384
- dragDepth = 0
1385
- dropOverlay.classList.remove("active")
1386
- const files = Array.from(event.dataTransfer ? event.dataTransfer.files || [] : [])
1387
- let remoteResources = []
1388
- try {
1389
- const extra = await this.collectFilesFromDataTransfer(event.dataTransfer)
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
- })
1379
+ const seen = new Set()
1380
+ const pushUrl = (href, nameHint) => {
1381
+ if (!href) {
1382
+ return
1403
1383
  }
1404
- } catch (error) {
1405
- console.warn("Failed to collect files from drop payload", error)
1384
+ let resolved
1385
+ try {
1386
+ resolved = new URL(href, window.location.href)
1387
+ } catch (_) {
1388
+ try {
1389
+ resolved = new URL(href)
1390
+ } catch (_) {
1391
+ return
1392
+ }
1393
+ }
1394
+ if (!/^https?:$/i.test(resolved.protocol)) {
1395
+ return
1396
+ }
1397
+ const key = resolved.href
1398
+ if (seen.has(key)) {
1399
+ return
1400
+ }
1401
+ seen.add(key)
1402
+ urls.push({ href: resolved.href, name: nameHint || null })
1406
1403
  }
1407
- if (!files.length && (!remoteResources || remoteResources.length === 0)) {
1408
- n.Noty({
1409
- text: "Dropped item did not include an accessible file",
1410
- type: "error"
1404
+ let html = ""
1405
+ let uriList = ""
1406
+ if (typeof clipboardData.getData === "function") {
1407
+ try { html = clipboardData.getData("text/html") || "" } catch (_) {}
1408
+ try { uriList = clipboardData.getData("text/uri-list") || "" } catch (_) {}
1409
+ }
1410
+ if (uriList) {
1411
+ uriList.split(/\r?\n/).forEach((line) => {
1412
+ const trimmed = line.trim()
1413
+ if (!trimmed || trimmed.startsWith("#")) {
1414
+ return
1415
+ }
1416
+ pushUrl(trimmed, null)
1411
1417
  })
1412
- return
1413
1418
  }
1414
- try {
1415
- if (remoteResources && remoteResources.length > 0) {
1416
- await this.uploadRemoteResources(remoteResources, dropOverlay)
1419
+ if (html) {
1420
+ try {
1421
+ const parser = new DOMParser()
1422
+ const doc = parser.parseFromString(html, "text/html")
1423
+ doc.querySelectorAll("img[src]").forEach((img) => {
1424
+ const src = img.getAttribute("src")
1425
+ const nameHint = img.getAttribute("alt") || img.getAttribute("title") || null
1426
+ pushUrl(src, nameHint)
1427
+ })
1428
+ doc.querySelectorAll("a[href]").forEach((anchor) => {
1429
+ const href = anchor.getAttribute("href")
1430
+ const nameHint = anchor.getAttribute("download") || (anchor.textContent ? anchor.textContent.trim() : null)
1431
+ pushUrl(href, nameHint)
1432
+ })
1433
+ } catch (error) {
1434
+ console.warn("Failed to parse clipboard HTML", error)
1417
1435
  }
1418
- } catch (error) {
1419
- console.warn("Remote upload failed", error)
1420
1436
  }
1421
- if (files.length > 0) {
1422
- await this.uploadFiles(files, dropOverlay)
1437
+ return urls
1438
+ }
1439
+ const isLikelyImagePaste = (clipboardData) => {
1440
+ if (!clipboardData) {
1441
+ return false
1423
1442
  }
1424
- this.term.focus()
1425
- })
1443
+ const types = Array.from(clipboardData.types || [])
1444
+ if (types.some((type) => type && type.toLowerCase().startsWith("image/"))) {
1445
+ return true
1446
+ }
1447
+ if (typeof clipboardData.getData === "function") {
1448
+ try {
1449
+ const html = clipboardData.getData("text/html") || ""
1450
+ if (html && /<img\b/i.test(html)) {
1451
+ return true
1452
+ }
1453
+ } catch (_) {}
1454
+ try {
1455
+ const uriList = clipboardData.getData("text/uri-list") || ""
1456
+ if (uriList && uriList.split(/\r?\n/).some((line) => {
1457
+ const trimmed = line.trim()
1458
+ if (!trimmed || trimmed.startsWith("#")) {
1459
+ return false
1460
+ }
1461
+ return /^https?:\/\//i.test(trimmed)
1462
+ })) {
1463
+ return true
1464
+ }
1465
+ } catch (_) {}
1466
+ }
1467
+ return false
1468
+ }
1469
+ let suppressClipboardText = false
1426
1470
  terminalContainer.addEventListener("paste", async (event) => {
1427
1471
  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
1472
  const { files: directFiles, hasFileFlavor } = collectClipboardFiles(clipboard)
1437
1473
  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)
1474
+ let remoteUrls = []
1475
+ if (files.length === 0) {
1476
+ remoteUrls = extractUrlsFromClipboard(clipboard)
1477
+ if (remoteUrls.length === 0 && !hasFileFlavor && isLikelyImagePaste(clipboard)) {
1478
+ files = await readClipboardFilesFallback()
1462
1479
  }
1463
1480
  }
1464
- if (files.length === 0 && (!remoteResources || remoteResources.length === 0)) {
1481
+ console.log('[clipboard paste][terminal]', {
1482
+ files: files.map((file) => ({ name: file.name, size: file.size, type: file.type })),
1483
+ remoteUrls,
1484
+ hasFileFlavor,
1485
+ types: clipboard ? Array.from(clipboard.types || []) : []
1486
+ })
1487
+ if (files.length === 0 && remoteUrls.length === 0) {
1465
1488
  suppressClipboardText = false
1466
1489
  return
1467
1490
  }
@@ -1469,16 +1492,14 @@ document.addEventListener("DOMContentLoaded", async () => {
1469
1492
  event.stopPropagation()
1470
1493
  suppressClipboardText = true
1471
1494
  try {
1472
- if (remoteResources && remoteResources.length > 0) {
1473
- await this.uploadRemoteResources(remoteResources, dropOverlay)
1495
+ if (remoteUrls.length > 0) {
1496
+ await this.uploadRemoteResources(remoteUrls, dropOverlay)
1497
+ }
1498
+ if (files.length > 0) {
1499
+ await this.uploadFiles(files, dropOverlay)
1474
1500
  }
1475
1501
  } 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
- })
1502
+ console.warn("Clipboard upload failed", error)
1482
1503
  }
1483
1504
  this.term.focus()
1484
1505
  }, true)
@@ -1493,6 +1514,7 @@ document.addEventListener("DOMContentLoaded", async () => {
1493
1514
  if ((event.ctrlKey || event.metaKey) && event.key === 'v') {
1494
1515
  if (!suppressClipboardText) {
1495
1516
  navigator.clipboard.readText().then((text) => {
1517
+ console.log('[clipboard paste][terminal][text]', text)
1496
1518
  this.socket.run({
1497
1519
  //key: "\x1b[200~" + text + "\x1b[201~",
1498
1520
  key: text,