pinokiod 3.183.0 → 3.184.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 +1 -1
- package/server/views/shell.ejs +105 -3
- package/server/views/terminal.ejs +105 -3
package/package.json
CHANGED
package/server/views/shell.ejs
CHANGED
|
@@ -893,6 +893,93 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
893
893
|
}
|
|
894
894
|
}
|
|
895
895
|
}
|
|
896
|
+
async collectFilesFromDataTransfer(dataTransfer) {
|
|
897
|
+
if (!dataTransfer) {
|
|
898
|
+
return []
|
|
899
|
+
}
|
|
900
|
+
const items = dataTransfer.items ? Array.from(dataTransfer.items) : []
|
|
901
|
+
const stringItems = items.filter((item) => item && item.kind === "string" && typeof item.getAsString === "function")
|
|
902
|
+
const collectedStrings = []
|
|
903
|
+
if (stringItems.length > 0) {
|
|
904
|
+
const stringValues = await Promise.all(stringItems.map((item) => new Promise((resolve) => {
|
|
905
|
+
try {
|
|
906
|
+
item.getAsString((value) => resolve({ type: item.type || "text/plain", value }))
|
|
907
|
+
} catch (_) {
|
|
908
|
+
resolve({ type: item.type || "text/plain", value: "" })
|
|
909
|
+
}
|
|
910
|
+
})))
|
|
911
|
+
for (const entry of stringValues) {
|
|
912
|
+
if (entry && typeof entry.value === "string" && entry.value.trim()) {
|
|
913
|
+
collectedStrings.push(entry)
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
} else if (typeof dataTransfer.getData === "function") {
|
|
917
|
+
const uriList = dataTransfer.getData("text/uri-list")
|
|
918
|
+
if (uriList && uriList.trim()) {
|
|
919
|
+
collectedStrings.push({ type: "text/uri-list", value: uriList })
|
|
920
|
+
}
|
|
921
|
+
const plain = dataTransfer.getData("text/plain")
|
|
922
|
+
if (plain && plain.trim()) {
|
|
923
|
+
collectedStrings.push({ type: "text/plain", value: plain })
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
if (!collectedStrings.length) {
|
|
927
|
+
return []
|
|
928
|
+
}
|
|
929
|
+
const origin = window.location.origin
|
|
930
|
+
const urls = new Set()
|
|
931
|
+
for (const { type, value } of collectedStrings) {
|
|
932
|
+
if (!value) {
|
|
933
|
+
continue
|
|
934
|
+
}
|
|
935
|
+
const trimmed = value.trim()
|
|
936
|
+
if (!trimmed) {
|
|
937
|
+
continue
|
|
938
|
+
}
|
|
939
|
+
const candidates = (type === "text/uri-list" ? trimmed.split(/\r?\n/) : [trimmed]).filter((line) => line && !line.startsWith("#"))
|
|
940
|
+
for (const candidate of candidates) {
|
|
941
|
+
let resolved
|
|
942
|
+
try {
|
|
943
|
+
resolved = new URL(candidate, origin)
|
|
944
|
+
} catch (_) {
|
|
945
|
+
continue
|
|
946
|
+
}
|
|
947
|
+
if (resolved.origin !== origin) {
|
|
948
|
+
continue
|
|
949
|
+
}
|
|
950
|
+
urls.add(resolved.href)
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (!urls.size) {
|
|
954
|
+
return []
|
|
955
|
+
}
|
|
956
|
+
const FileCtor = typeof window !== "undefined" ? window.File : null
|
|
957
|
+
if (typeof FileCtor !== "function") {
|
|
958
|
+
return []
|
|
959
|
+
}
|
|
960
|
+
const files = []
|
|
961
|
+
for (const href of urls) {
|
|
962
|
+
try {
|
|
963
|
+
const response = await fetch(href, { credentials: "include" })
|
|
964
|
+
if (!response || !response.ok) {
|
|
965
|
+
continue
|
|
966
|
+
}
|
|
967
|
+
const blob = await response.blob()
|
|
968
|
+
const nameSegment = href.split("/").pop() || "download"
|
|
969
|
+
let filename = nameSegment || "download"
|
|
970
|
+
try {
|
|
971
|
+
filename = decodeURIComponent(filename)
|
|
972
|
+
} catch (_) {}
|
|
973
|
+
if (!filename) {
|
|
974
|
+
filename = "download"
|
|
975
|
+
}
|
|
976
|
+
files.push(new FileCtor([blob], filename, { type: blob.type || "application/octet-stream", lastModified: Date.now() }))
|
|
977
|
+
} catch (error) {
|
|
978
|
+
console.warn("Failed to resolve dropped resource", href, error)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return files
|
|
982
|
+
}
|
|
896
983
|
async createTerm (_theme) {
|
|
897
984
|
console.log(xtermTheme)
|
|
898
985
|
if (!this.term) {
|
|
@@ -964,11 +1051,26 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
964
1051
|
prevent(event)
|
|
965
1052
|
dragDepth = 0
|
|
966
1053
|
dropOverlay.classList.remove("active")
|
|
967
|
-
const
|
|
968
|
-
|
|
1054
|
+
const files = Array.from(event.dataTransfer ? event.dataTransfer.files || [] : [])
|
|
1055
|
+
try {
|
|
1056
|
+
const extra = await this.collectFilesFromDataTransfer(event.dataTransfer)
|
|
1057
|
+
if (Array.isArray(extra) && extra.length) {
|
|
1058
|
+
const seen = new Set(files.map((file) => `${file.name}-${file.size}`))
|
|
1059
|
+
for (const file of extra) {
|
|
1060
|
+
const key = `${file.name}-${file.size}`
|
|
1061
|
+
if (!seen.has(key)) {
|
|
1062
|
+
seen.add(key)
|
|
1063
|
+
files.push(file)
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
console.warn("Failed to collect files from drop payload", error)
|
|
1069
|
+
}
|
|
1070
|
+
if (!files.length) {
|
|
969
1071
|
return
|
|
970
1072
|
}
|
|
971
|
-
await this.uploadFiles(
|
|
1073
|
+
await this.uploadFiles(files, dropOverlay)
|
|
972
1074
|
this.term.focus()
|
|
973
1075
|
})
|
|
974
1076
|
term.attachCustomKeyEventHandler(event => {
|
|
@@ -980,6 +980,93 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
980
980
|
}
|
|
981
981
|
}
|
|
982
982
|
}
|
|
983
|
+
async collectFilesFromDataTransfer(dataTransfer) {
|
|
984
|
+
if (!dataTransfer) {
|
|
985
|
+
return []
|
|
986
|
+
}
|
|
987
|
+
const items = dataTransfer.items ? Array.from(dataTransfer.items) : []
|
|
988
|
+
const stringItems = items.filter((item) => item && item.kind === "string" && typeof item.getAsString === "function")
|
|
989
|
+
const collectedStrings = []
|
|
990
|
+
if (stringItems.length > 0) {
|
|
991
|
+
const stringValues = await Promise.all(stringItems.map((item) => new Promise((resolve) => {
|
|
992
|
+
try {
|
|
993
|
+
item.getAsString((value) => resolve({ type: item.type || "text/plain", value }))
|
|
994
|
+
} catch (_) {
|
|
995
|
+
resolve({ type: item.type || "text/plain", value: "" })
|
|
996
|
+
}
|
|
997
|
+
})))
|
|
998
|
+
for (const entry of stringValues) {
|
|
999
|
+
if (entry && typeof entry.value === "string" && entry.value.trim()) {
|
|
1000
|
+
collectedStrings.push(entry)
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
} else if (typeof dataTransfer.getData === "function") {
|
|
1004
|
+
const uriList = dataTransfer.getData("text/uri-list")
|
|
1005
|
+
if (uriList && uriList.trim()) {
|
|
1006
|
+
collectedStrings.push({ type: "text/uri-list", value: uriList })
|
|
1007
|
+
}
|
|
1008
|
+
const plain = dataTransfer.getData("text/plain")
|
|
1009
|
+
if (plain && plain.trim()) {
|
|
1010
|
+
collectedStrings.push({ type: "text/plain", value: plain })
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
if (!collectedStrings.length) {
|
|
1014
|
+
return []
|
|
1015
|
+
}
|
|
1016
|
+
const origin = window.location.origin
|
|
1017
|
+
const urls = new Set()
|
|
1018
|
+
for (const { type, value } of collectedStrings) {
|
|
1019
|
+
if (!value) {
|
|
1020
|
+
continue
|
|
1021
|
+
}
|
|
1022
|
+
const trimmed = value.trim()
|
|
1023
|
+
if (!trimmed) {
|
|
1024
|
+
continue
|
|
1025
|
+
}
|
|
1026
|
+
const candidates = (type === "text/uri-list" ? trimmed.split(/\r?\n/) : [trimmed]).filter((line) => line && !line.startsWith("#"))
|
|
1027
|
+
for (const candidate of candidates) {
|
|
1028
|
+
let resolved
|
|
1029
|
+
try {
|
|
1030
|
+
resolved = new URL(candidate, origin)
|
|
1031
|
+
} catch (_) {
|
|
1032
|
+
continue
|
|
1033
|
+
}
|
|
1034
|
+
if (resolved.origin !== origin) {
|
|
1035
|
+
continue
|
|
1036
|
+
}
|
|
1037
|
+
urls.add(resolved.href)
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
if (!urls.size) {
|
|
1041
|
+
return []
|
|
1042
|
+
}
|
|
1043
|
+
const FileCtor = typeof window !== "undefined" ? window.File : null
|
|
1044
|
+
if (typeof FileCtor !== "function") {
|
|
1045
|
+
return []
|
|
1046
|
+
}
|
|
1047
|
+
const files = []
|
|
1048
|
+
for (const href of urls) {
|
|
1049
|
+
try {
|
|
1050
|
+
const response = await fetch(href, { credentials: "include" })
|
|
1051
|
+
if (!response || !response.ok) {
|
|
1052
|
+
continue
|
|
1053
|
+
}
|
|
1054
|
+
const blob = await response.blob()
|
|
1055
|
+
const nameSegment = href.split("/").pop() || "download"
|
|
1056
|
+
let filename = nameSegment || "download"
|
|
1057
|
+
try {
|
|
1058
|
+
filename = decodeURIComponent(filename)
|
|
1059
|
+
} catch (_) {}
|
|
1060
|
+
if (!filename) {
|
|
1061
|
+
filename = "download"
|
|
1062
|
+
}
|
|
1063
|
+
files.push(new FileCtor([blob], filename, { type: blob.type || "application/octet-stream", lastModified: Date.now() }))
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
console.warn("Failed to resolve dropped resource", href, error)
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
return files
|
|
1069
|
+
}
|
|
983
1070
|
async createTerm (_theme) {
|
|
984
1071
|
if (!this.term) {
|
|
985
1072
|
const theme = Object.assign({ }, _theme, {
|
|
@@ -1048,11 +1135,26 @@ document.addEventListener("DOMContentLoaded", async () => {
|
|
|
1048
1135
|
prevent(event)
|
|
1049
1136
|
dragDepth = 0
|
|
1050
1137
|
dropOverlay.classList.remove("active")
|
|
1051
|
-
const
|
|
1052
|
-
|
|
1138
|
+
const files = Array.from(event.dataTransfer ? event.dataTransfer.files || [] : [])
|
|
1139
|
+
try {
|
|
1140
|
+
const extra = await this.collectFilesFromDataTransfer(event.dataTransfer)
|
|
1141
|
+
if (Array.isArray(extra) && extra.length) {
|
|
1142
|
+
const seen = new Set(files.map((file) => `${file.name}-${file.size}`))
|
|
1143
|
+
for (const file of extra) {
|
|
1144
|
+
const key = `${file.name}-${file.size}`
|
|
1145
|
+
if (!seen.has(key)) {
|
|
1146
|
+
seen.add(key)
|
|
1147
|
+
files.push(file)
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
} catch (error) {
|
|
1152
|
+
console.warn("Failed to collect files from drop payload", error)
|
|
1153
|
+
}
|
|
1154
|
+
if (!files.length) {
|
|
1053
1155
|
return
|
|
1054
1156
|
}
|
|
1055
|
-
await this.uploadFiles(
|
|
1157
|
+
await this.uploadFiles(files, dropOverlay)
|
|
1056
1158
|
this.term.focus()
|
|
1057
1159
|
})
|
|
1058
1160
|
term.attachCustomKeyEventHandler(event => {
|