Open shyagamzo opened 1 year ago
Found another clue...
await drop()
is more capable than widget.onDrop()
. It detects VSCode drags and even plain text drags (e.g. Chrome address bar, selection in Word, etc.).
The difference is, instead of the drag message object, it receives VSCode drags as a long plain string:
C:\some\path\to\file1.tsC:\some\path\to\file2.ts
I'm stuck...
drop
won't let me display my own html, and the widget doesn't grab all drop types... 🥲
Need help 🙏
Found a workaround... 💪😪 While debugging the widget, I discovered how it handles the drag event. After a long trail and error I implemented my own handler and planted it in my HTML (the one I give the widget when I create it).
This creates a second handler to the browser's drag event and handles it differently:
items
array instead of the files
array, to produce more data regarding the dragged item.<script>
const mappers = {
string: {
'text/plain': 'text',
'text/html': 'html',
'text/uri-list': 'uri',
read: (item) => new Promise(item.getAsString.bind(item))
},
file: {
'image': 'image',
'': 'file',
read: (item) => Promise.resolve(fileToJSON(item.getAsFile()))
}
};
// Because we can't send a File object to the main process, we need to convert it to a JSON object.
// JSON.stringify didn't work as the properties are not enumerable.
// Had to use this trick.
function fileToJSON({ name, path, size, type, lastModified, lastModifiedDate, webkitRelativePath })
{
return { name, path, size, type, lastModified, lastModifiedDate, webkitRelativePath };
}
document.addEventListener("drop", async (event) =>
{
event.preventDefault();
let { id = "" } = event.target.closest("*[id]");
const { items } = event.dataTransfer;
// Resolve all items into a big array of { category, info, mime }
const data = await Promise.all(
Array.from(items).map(async (item) =>
{
const mapper = mappers[item.kind]; // file or string
const mime = item.type; // text/plain image/png etc
const type = Object.keys(mapper).find(key => mime.startsWith(key));
return {
category: mapper?.[type] ?? 'other',
info: await (mapper?.read ?? mappers.string.read)(item),
mime: mime === '' ? undefined : mime
};
}, [])
);
// Group by category
const dataByType = data.reduce((acc, { category, info, mime }) =>
{
(acc[category] ??= []).push({ info, mime });
return acc;
}, {});
// Send items to main process on the `dataTransfer` property, which is different to the `dataset` property
// produced by ScriptKit. This allows me to ignore Kit's drop messages quickly and use this one.
ipcRenderer.send("WIDGET_DROP", {
dataTransfer: dataByType,
targetId: id,
widgetId: window.widgetId,
});
});
</script>
Now my widget can handle all kinds of dragged items:
Trying to produce a widget which enables dropping all kinds of stuff and passing them through a certain pipeline.
When I drop from Windows Explorer or even from Total Commander, I get
input.dataset.files
filled with an array of my dragged file/folder paths:When dragging from VSCode, it doesn't pickup on the paths:
Any chance of getting this to work?