import path from "path";
import { shell } from "electron";
import { dialog, getCurrentWindow } from "@electron/remote";
import type { FileFilter } from "electron";
import React from "react";
import type { IDashboardComponentContext } from "eez-studio-types";
import { registerActionComponents } from "project-editor/flow/component";
import { SHOW_FILE_IN_FOLDER_ICON } from "project-editor/ui-components/icons";
////////////////////////////////////////////////////////////////////////////////
const readIcon: any = (
);
const writeIcon: any = (
);
const appendIcon: any = writeIcon;
// const fileOpenDialogIcon: any = (
//
// );
// const fileSaveDialogIcon: any = (
//
// );
const componentHeaderColor = "#f1ffc4";
registerActionComponents("File", [
{
name: "FileRead",
icon: readIcon,
componentHeaderColor,
bodyPropertyName: "filePath",
inputs: [],
outputs: [
{
name: "content",
type: "any",
isSequenceOutput: false,
isOptionalOutput: false
}
],
properties: [
{
name: "filePath",
type: "expression",
valueType: "string"
},
{
name: "encoding",
type: "expression",
valueType: "string",
formText: `"ascii", "base64", "hex", "ucs2", "ucs-2", "utf16le", "utf-16le", "utf8", "utf-8", "binary" or "latin1"`
}
],
execute: (context: IDashboardComponentContext) => {
const filePathValue = context.evalProperty("filePath");
if (typeof filePathValue != "string") {
context.throwError("filePath is not a string");
return;
}
const encodingValue = context.evalProperty("encoding");
if (typeof encodingValue != "string") {
context.throwError("encoding is not a string");
return;
}
const encodings = [
"ascii",
"base64",
"hex",
"ucs2",
"ucs-2",
"utf16le",
"utf-16le",
"utf8",
"utf-8",
"binary",
"latin1"
];
if (encodings.indexOf(encodingValue) == -1) {
context.throwError(
`Unsupported encoding value ${encodingValue}, supported: ${encodings.join(
", "
)}`
);
return;
}
context = context.startAsyncExecution();
(async function () {
try {
const fs = await import("fs");
let content = await fs.promises.readFile(
filePathValue as string,
encodingValue == "binary"
? undefined
: (encodingValue as any)
);
context.propagateValue("content", content);
context.propagateValueThroughSeqout();
} catch (err) {
context.throwError(err.toString());
} finally {
context.endAsyncExecution();
}
})();
}
},
{
name: "FileWrite",
icon: writeIcon,
componentHeaderColor,
bodyPropertyName: "filePath",
inputs: [],
outputs: [],
properties: [
{
name: "filePath",
type: "expression",
valueType: "string"
},
{
name: "content",
type: "expression",
valueType: "string"
},
{
name: "encoding",
type: "expression",
valueType: "string",
formText: `"ascii", "base64", "hex", "ucs2", "ucs-2", "utf16le", "utf-16le", "utf8", "utf-8", "utf8-bom", "binary" or "latin1"`
}
],
execute: (context: IDashboardComponentContext) => {
const filePathValue = context.evalProperty("filePath");
if (typeof filePathValue != "string") {
context.throwError("filePath is not a string");
return;
}
let encodingValue = context.evalProperty("encoding");
if (typeof encodingValue != "string") {
context.throwError("${encoding} is not a string");
return;
}
let contentValue = context.evalProperty("content");
const encodings = [
"ascii",
"base64",
"hex",
"ucs2",
"ucs-2",
"utf16le",
"utf-16le",
"utf8",
"utf-8",
"binary",
"latin1"
];
if (encodingValue == "utf8-bom") {
encodingValue = "utf8";
contentValue = "\ufeff" + contentValue;
}
if (encodings.indexOf(encodingValue) == -1) {
context.throwError(
`Unsupported encoding value ${encodingValue}, supported: ${encodings.join(
", "
)}`
);
return;
}
context = context.startAsyncExecution();
(async function () {
try {
const fs = await import("fs");
await fs.promises.writeFile(
filePathValue,
contentValue,
encodingValue as any
);
context.propagateValueThroughSeqout();
} catch (err) {
context.throwError(err.toString());
} finally {
context.endAsyncExecution();
}
})();
return undefined;
}
},
{
name: "FileAppend",
icon: appendIcon,
componentHeaderColor,
bodyPropertyName: "filePath",
inputs: [],
outputs: [],
properties: [
{
name: "filePath",
type: "expression",
valueType: "string"
},
{
name: "content",
type: "expression",
valueType: "string"
},
{
name: "encoding",
type: "expression",
valueType: "string",
formText: `"ascii", "base64", "hex", "ucs2", "ucs-2", "utf16le", "utf-16le", "utf8", "utf-8", "binary" or "latin1"`
}
],
execute: (context: IDashboardComponentContext) => {
const filePathValue = context.evalProperty("filePath");
if (typeof filePathValue != "string") {
context.throwError("filePath is not a string");
return;
}
const encodingValue = context.evalProperty("encoding");
if (typeof encodingValue != "string") {
context.throwError("${encoding} is not a string");
return;
}
const encodings = [
"ascii",
"base64",
"hex",
"ucs2",
"ucs-2",
"utf16le",
"utf-16le",
"utf8",
"utf-8",
"binary",
"latin1"
];
if (encodings.indexOf(encodingValue) == -1) {
context.throwError(
`Unsupported encoding value ${encodingValue}, supported: ${encodings.join(
", "
)}`
);
return;
}
const contentValue = context.evalProperty("content");
context = context.startAsyncExecution();
(async function () {
try {
const fs = await import("fs");
await fs.promises.appendFile(
filePathValue,
contentValue,
encodingValue as any
);
context.propagateValueThroughSeqout();
} catch (err) {
context.throwError(err.toString());
} finally {
context.endAsyncExecution();
}
})();
return undefined;
}
},
{
name: "FileOpenDialog",
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAABHhJREFUWIXdlktslVUQx39zvvvobcsFSmmxPCoEKkYwRhrwhQ1oYqML2enCxJgY48aVS9GkbDQhhkSiwTfRjbjRBGPUGARFIwVFKSAp7yoWyO3jPtrS3u/MuPgAS/q6FywLJ5nFd87M/P8z38w5B/4v0nJo9YaWjjWfl+vn/isC4nhEhMfXdjQ/UZZfOcYPH75/UTyIPRri7zDVdGg6YKKnRHS/efeuiCwzswvehm7fe2dHXykxY6UYtXa2zo1buEXxT6pZICaYCM5ATcAC5HIqIlIfSMVm4NlSYk9ZgQ0nH2rCu28VW+hNUVO8edQUVY9iY3zMzMCv37Pyl91TxZ+0B1o7W9OOxJeIWzhVoNEiIoIEb7d811gxle2kv6A6Fjzv1W90ibB9eFCqAtE6xRZjrARba3AXE1RRkCY/t/4+OLvrugi0nG6pEJVPPlv6VddENg8cal4SiHtFRJ4evW5mhtnH3X/1/TgZOBOxL0daDjU/Iy74YBT4r4q+8MPKAz+V4l/SFEwm5oJ1EiFnVOSl71e0v4egpfpPSKBQKDwGmKq2p9PpzER2YrrK4E1vl17eu7K02b/Gf7zFrq6uVE1NTVZE4tFIcRrYZ2btqtqezWYPLlq0aAhgaefS5ImmE8Oj/a1zaTIsxB4Usbrx4nsA08PJu4//fg0BM5sJLM7lcvO9918457ii2cIQvblBatIpKpOJopl2iMg+73070L5169ZjbW1tGh5sesojr4vIuOBXscAQfU76PmycFU+kNgm2PLF+e1NQv6bxMhnCMMR7j/eenv4ClRVxujM5+nKDJOIOVWNGVQVz0pUk4kGuIr/9aDL3/hqRUpvbjsRckNimqtEFEqu8uiUixONxMtlB+vKDBCK4GMyfV8uyxqh1VJXBoWF6swNIf0e6sfjRPWDY2MNxIgJdMTVtFosIS6p+jEmmP8/s6jjg6DrfS8PcWfzc0c2Culn0ZAeYnU4hWmC5vobY8Bj/SeGNXU699nmveHNIqhaAgaKy7XCOY71RwONnurjQ20/d7GoaatPUpFMc6TxCKhlDiudZopuQYhfR+VO6hqq7Yqo+A4KrvAWR6Go4P6QcPXeAVclhan2BofAiM/xsZlKN5VNUFrtpqOimQfdQZ1/jNF9W5gAGvVU7T/4WU68ZQ3Cpf5t2Qf4NNte9BYXou6EKCIGeSG9zwJwoStTO5YuZ7ZY2NOa99iAgqXnRxsg5uPgOZXTSdYnALoBYqGFGcEhlRCC8uB2zkWkFBwjFIgIWkjGnuKp5mM/jMzuwac4eozu9+uwfEQEJM1hAUN1AmNmBhbnpBY8YXH0jxAKVjHcQVNXjL7yKXVdL3QCBEdWewDkIO9DhP28COARaHFUBCTK4GJbbeVOyF7NTqXsvnLny7QrZTE+QwnxhfzR606w2qvwAbkUbI8k6zZd7jF6vjoxcSyAGEK+RfrD0dJe/GNqloWH3zRgC/QXZkoiz0YnMmS5wM8uPFOXFW1v/7hm9fu2L6FOC3Udu/KU8nqxrwzPOtfEPmju7fm01yQcAAAAASUVORK5CYII=",
componentHeaderColor,
properties: [
{
name: "filters",
type: "expression",
valueType: "array:string",
optional: () => true
}
],
inputs: [],
outputs: [
{
name: "file_path",
type: "string",
isSequenceOutput: false,
isOptionalOutput: false
}
],
execute: (context: IDashboardComponentContext) => {
context = context.startAsyncExecution();
(async () => {
const filtersValue = context.evalProperty("filters");
let filters: FileFilter[];
if (filtersValue != undefined) {
if (!Array.isArray(filtersValue)) {
context.endAsyncExecution();
context.throwError("filters is not an array");
return;
}
if (filtersValue.length == 0) {
context.endAsyncExecution();
context.throwError("filters empty");
return;
}
filters = [];
for (let i = 0; i < filtersValue.length; i++) {
if (typeof filtersValue[i] != "string") {
context.endAsyncExecution();
context.throwError(
"filters is not an array of strings"
);
return;
}
const parts = filtersValue[i].split("|");
if (parts.length < 2) {
context.endAsyncExecution();
context.throwError(
`invalid filter at position ${i + 1}`
);
return;
}
filters.push({
name: parts[0],
extensions: parts.slice(1)
});
}
console.log(filters);
} else {
filters = [{ name: "All Files", extensions: ["*"] }];
}
const result = await dialog.showOpenDialog(getCurrentWindow(), {
properties: ["openFile"],
filters
});
// workaround: for some reason electron returs "\\" as path separator on windows, probably bug
const filePath =
result.filePaths && result.filePaths[0]
? result.filePaths[0].replace(/\\/g, "/")
: undefined;
if (!result.canceled) {
context.propagateValue("file_path", filePath);
}
context.propagateValueThroughSeqout();
context.endAsyncExecution();
})();
}
},
{
name: "FileSaveDialog",
icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAOxAAADsQBlSsOGwAABRBJREFUWIWtll+I3NUVxz/nzszOJlnzZ3A3m40ma8x/rIipAVEqFFIoaUQMixoKhbagD74U6oP0wYdCqaIPakvtQwgtISlLC7Wk+tSHQmmLwcQU6ibZ9U80upttdnc25s/M/XNOH34zszO76+7M6oELv/ndM/d8ft9z7rlXqNmRP17YVnTyi4nJzzduv72H1w7vY6X2wtv/4Z8fXWWgd2NZyf1lZObG8Xef/mZYzDdffxCz4/mce9A0EdOc76E3/sZ1HwE4fN9Wnv3W7mUBUkrEGOjuctwM8tieUs+ze/409t3jh7dPzvd19QfVtEc1EYInhjkAHwLee7z3pBjbUiDGmPknRTWhmu5PsXrqwO/fW7MEgKKm+OgJTYFC8PhQxYcqKaU2AQI+VGvBFVXFVB8oFfInh4aHc4sCWOZE8J4YmxXwBJ+NmNpUIEWC96SUUNPGMEuHJOz41ZcokNGGGFoAYvD42kjangIpRnzwja9vGcYzQ8fPHKn7NopQrSaVGWbWWOyJ/buohCzwA4Mblw0+dv0UA4Nnebhnlv7eSZJJY67qK1z67ABRCz8DTrQC1AgBRq+UGZsss71vPT/5Tmfb8V+zr9O/tUr/1oVzTopMzw4wM3vv3oNH39n21x/t/7ABkFVsBnArRB57/c+sKXZ1FBzgqcMXyeUW3fKZWUBVyZncAXzYpEDCR9jcV2Ls0wnU4IuK7xhADcSWmrfsQwWBeSmoeKWv1Mem3r6WOujEBIc15X0BgNZ6Q239piJMiAkT09dXFLhuOxUcywEoiMwD0FRX5SuZmiBLKWBWU4BWAFPFvg4AlSU/RLVeA64VINsBXxOALA+gposALPHHTgCWWmcu+DwFkuqS5O1aUkdTh18EwFqaXosCIrC3fwPf+8ad5N3KYE7fdCRdLgUJyKqw4fngS2/OiLD+5A+/zZZST1vBonpeOvMQlXRtRbCYjbduQxHWdGWvpsvXmZ5dvifsyH2f96pvrDC+/XReCuakq3hP+dryAHcVDjK1+hwfffHvjoIL/OHFR86faDmOxYR6Ax7oKzHQV2prsZ3VX/LK6ce5Ecpt+Vf9WkZGH/0tPD+vCIF6cYxdmuCDTybaWhBg860jjLjlU2EII6OHmJq+22BBJ5yzgb4NrF+7um2AfWzjrU/HOT1+akm/icl9TE0PAtmR3XIfaG6E3cUCXV15OrHHdz3Hhatnma6MLzrf3zNIvutJLugVTBccRjWAmgzvj13mv6OfdAQAcFfuKf6XXsNqrbZuOZfnB/f+nF9fmUJVSSw4jiM0FeE9O7dwz84tHQPAw5RGyrw1+ruWtwd3/JjB9Xsx/XvWiOZ3QrOshV87+xsK3a30ndojGjmrOcZddpm9Q/M8NDNF+Z2X8VO3AcWG71yShY+B+94+d5E1rvOr2Hy7ze3m8qbzYELPxC7e/Ow8AJdndmPWm8TZxwAyPDzclVK68x9X/IFzs+mFaLIWxAnmJLveZf3JTBpV+uUHvmU5NBCx20sXMXN2dXq7GaJgCuY3d8uJRze5V8vl8iU5evRob6FQeEJE9ovIALDBzHqAooh0mVkeyIlI/ZgTq136pNY6rekCKSJa+60iEoFoZgGoADeAMvA5cAY4KYAcO3asGGNcvW7dulUhhNUisirGuMrMup1zdZCCcy6vqnkzcyIiZpZ3zmFm0TlnMUbL5/NBVaOIBDPzqloVkQpQMbNbzrmbxWLxJnBraGio8n+BRf2XIiOGEwAAAABJRU5ErkJggg==",
componentHeaderColor,
inputs: [],
outputs: [
{
name: "file_path",
type: "string",
isSequenceOutput: false,
isOptionalOutput: false
}
],
properties: [
{
name: "fileName",
type: "expression",
valueType: "string"
},
{
name: "filters",
type: "expression",
valueType: "array:string",
optional: () => true
}
],
execute: (context: IDashboardComponentContext) => {
const fileNameValue = context.evalProperty("fileName");
if (fileNameValue && typeof fileNameValue != "string") {
context.throwError("fileName is not a string");
return;
}
context = context.startAsyncExecution();
(async () => {
const filtersValue = context.evalProperty("filters");
let filters: FileFilter[] | undefined;
if (filtersValue != undefined) {
if (!Array.isArray(filtersValue)) {
context.endAsyncExecution();
context.throwError("filters is not an array");
return;
}
if (filtersValue.length == 0) {
context.endAsyncExecution();
context.throwError("filters empty");
return;
}
filters = [];
for (let i = 0; i < filtersValue.length; i++) {
if (typeof filtersValue[i] != "string") {
context.endAsyncExecution();
context.throwError(
"filters is not an array of strings"
);
return;
}
const parts = filtersValue[i].split("|");
if (parts.length < 2) {
context.endAsyncExecution();
context.throwError(
`invalid filter at position ${i + 1}`
);
return;
}
filters.push({
name: parts[0],
extensions: parts.slice(1)
});
}
console.log(filters);
} else {
filters = undefined;
}
const result = await dialog.showSaveDialog(getCurrentWindow(), {
defaultPath: fileNameValue,
filters
});
// workaround: for some reason electron returs "\\" as path separator on windows, probably bug
const filePath = result.filePath
? result.filePath.replace(/\\/g, "/")
: undefined;
if (!result.canceled) {
context.propagateValue("file_path", filePath);
}
context.propagateValueThroughSeqout();
context.endAsyncExecution();
})();
}
},
{
name: "ShowFileInFolder",
icon: SHOW_FILE_IN_FOLDER_ICON,
componentHeaderColor,
inputs: [],
outputs: [],
properties: [
{
name: "filePath",
type: "expression",
valueType: "string"
}
],
execute: (context: IDashboardComponentContext) => {
const filePathValue = context.evalProperty("filePath");
if (typeof filePathValue != "string") {
context.throwError("filePathValue is not a string");
return;
}
// workaround: on windows, showItemInFolder will not work if path.sep is /
const filePath = filePathValue.replace(/\//g, path.sep);
shell.showItemInFolder(filePath);
context.propagateValueThroughSeqout();
}
}
]);