[
  {
    "path": ".gitignore",
    "content": "/target\n/release-out\n/release-build\n/tools\n/PineFlash_Installer.exe\n/pineflash*\n/libs\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"blisp\"]\n\tpath = blisp\n\turl = https://github.com/pine64/blisp\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"pineflash\"\nversion = \"0.5.5\"\nedition = \"2021\"\nbuild = \"build.rs\"\n[[bin]]\nname = \"pineflash\"\npath = \"src/main.rs\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\negui_file = \"0.17.0\"\nzip-extract = \"0.1.1\"\neframe = \"0.27.2\"\negui-notify = \"0.14.0\"\negui_extras = { version = \"0.27.2\", features = [\"all_loaders\"] }\nrfd = \"0.11.3\"\nwhoami = \"1.2.3\"\nserde = { version = \"^1.0.145\", features = [\"derive\"] }\nserde_json = { version=\"1.0.89\" }\nserialport = \"4.2.0\"\nrusb = \"0.9\"\nconfy = \"0.5.1\"\ncurl = \"0.4.44\"\nsimple-home-dir = \"0.1.2\"\nversion-compare = \"0.1.1\"\n\n[build-dependencies]\nwinresource = \"0.1.15\"\n[features]\nappimage = []\n\n[package.metadata.appimage]\nauto_link = true\nassets = [\"tools/linux\", \"tools/appimage\"]\n"
  },
  {
    "path": "LICENSE",
    "content": "The GPLv2 License (GPLv2)\n\nCopyright (c) 2022 Spagett1\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License version 2, as published by\nthe Free Software Foundation.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "Package_Windows_Release.iss",
    "content": "; Script generated by the Inno Setup Script Wizard.\n; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!\n\n#define MyAppName \"PineFlash\"\n#define MyAppVersion \"0.5.5\"\n#define MyAppPublisher \"Spagett\"\n#define MyAppURL \"https://github.com/Spagett1\"\n#define MyAppExeName \"pineflash.exe\"\n\n[Setup]\n; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.\n; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)\nAppId={{E167F410-9EB3-4AF4-9F2B-5E775FBF58ED}\nAppName={#MyAppName}\nAppVersion={#MyAppVersion}\n;AppVerName={#MyAppName} {#MyAppVersion}\nAppPublisher={#MyAppPublisher}\nAppPublisherURL={#MyAppURL}\nAppSupportURL={#MyAppURL}\nAppUpdatesURL={#MyAppURL}\nDefaultDirName=C://Program Files/{#MyAppName}\nDisableProgramGroupPage=yes\nLicenseFile=Z:\\home\\spagett\\projects\\PineFlash\\LICENSE\n; Uncomment the following line to run in non administrative install mode (install for current user only.)\n;PrivilegesRequired=lowest\nOutputDir=Z:\\home\\spagett\\projects\\PineFlash\nOutputBaseFilename=PineFlash_Installer\nSetupIconFile=Z:\\home\\spagett\\projects\\PineFlash\\assets\\pine64logo.ico\nCompression=lzma\nSolidCompression=yes\nWizardStyle=modern\n\n[Languages]\nName: \"english\"; MessagesFile: \"compiler:Default.isl\"\n\n[Tasks]\nName: \"desktopicon\"; Description: \"{cm:CreateDesktopIcon}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: unchecked\n\n[Files]\nSource: \"Z:\\home\\spagett\\projects\\PineFlash\\target\\x86_64-pc-windows-gnu\\release\\{#MyAppExeName}\"; DestDir: \"{app}\"; Flags: ignoreversion\nSource: \"Z:\\home\\spagett\\projects\\PineFlash\\tools\\win\\*\"; DestDir: \"{app}/tools/\"; Flags: ignoreversion recursesubdirs createallsubdirs\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\n\n[Icons]\nName: \"{autoprograms}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"\nName: \"{autodesktop}\\{#MyAppName}\"; Filename: \"{app}\\{#MyAppExeName}\"; Tasks: desktopicon\n\n[Run]\nFilename: \"{app}\\{#MyAppExeName}\"; Description: \"{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}\"; Flags: nowait postinstall skipifsilent\n"
  },
  {
    "path": "README.md",
    "content": "\n<a href=\"https://github.com/Spagett1/PineFlash#pineflash\"><img src=\"https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2FSpagett1%2FPineFlash&count_bg=%23187BC0&title_bg=%23555555&icon=&icon_color=%23E7E7E7&title=hits&edge_flat=true\"/></a>\n[![GitHub all downloads](https://img.shields.io/github/downloads/spagett1/pineflash/total?color=187BC0&style=flat-square)](https://github.com/Spagett1/PineFlash/releases/)\n[![GitHub release (latest by date)](https://img.shields.io/github/v/release/spagett1/pineflash?color=187BC0&style=flat-square)](https://github.com/Spagett1/PineFlash/releases/)\n\n\n# PineFlash\n\n<img src=\"https://user-images.githubusercontent.com/77225642/234575696-e6ea62ae-7189-4a9b-bcfb-7f0c36271ace.png\" align=\"right\" width=\"425\" style=\"float:left\">\n\nA GUI tool to flash IronOS to the Pinecil V1, V2 and future other pine products.  \n\nIf you need help, or want to help out with development, I have a [discord server](https://discord.gg/NHrXJRan7D) and a bridged [matrix space](https://matrix.to/#/#spagett's_cookhouse_of_spaghetti_code.:matrix.org).\n\n## Features\n* Auto-fetch the newest stable release of IronOS firmware.\n* Allows manual installs of beta versions using a browse to file feature.\n* Selectable options: pick the iron type, pick the firmware version and download it.\n* Boot logo art upload supported for V1 and V2 (use custom & folder icon)\n\n<br clear=\"both\" />\n\n## Supported Devices \n | System  |<img width=\"17\" src=\"https://cdn.simpleicons.org/Linux/187BC0\" /> Linux  | <img width=\"15\" src=\"https://cdn.simpleicons.org/Apple/187BC0\" /> MacOS|  <img width=\"15\" src=\"https://cdn.simpleicons.org/Windows11/187BC0\" /> Windows|\n | :-----: | :-----: | :-----: | :-----: |\n | Pinecil V1 |<img width=\"18\" src=\"https://cdn.simpleicons.org/cachet/187BC0\" />|<img width=\"18\" src=\"https://cdn.simpleicons.org/cachet/187BC0\" />*| <img width=\"18\" src=\"https://cdn.simpleicons.org/cachet/187BC0\" />  |\n | Pinecil V2 | <img width=\"18\" src=\"https://cdn.simpleicons.org/cachet/187BC0\" />   | <img width=\"18\" src=\"https://cdn.simpleicons.org/cachet/187BC0\" />*  |  <img width=\"18\" src=\"https://cdn.simpleicons.org/cachet/187BC0\" />  |\n* Pineflash on MacOS must be compiled from source.\n\n# :desktop_computer: Install Options\n\n\n1. Easy install: use premade binaries, currently only available for Linux x86 distros and Windows x86.\n\n2. Build from Code: recommended if you are on MacOS, an ARM device, or doing development.\n\n<details>\n  <summary>\n   \n## :clamp: Premade Binaries \n </summary>\n\n## <img width=\"18\" src=\"https://cdn.simpleicons.org/Windows11/187BC0\" /> Windows\nDownload the latest pineflash exe file from the [releases page](https://github.com/Spagett1/PineFlash/releases).\n\nThen just double click it.\n\n### <img width=\"18\" src=\"https://cdn.simpleicons.org/RedHat/\" /> RedHat distros (Fedora, Centos, Nobara, Rocky, etc.)\nDownload the latest pineflash .rpm file from the [releases page](https://github.com/Spagett1/PineFlash/releases).\n\n Then just run.\n ```\n sudo dnf install ./pineflash-*.x86_64.rpm\n ```\n \n### <img width=\"18\" src=\"https://cdn.simpleicons.org/ArchLinux/187BC0\" /> Arch based distros (Arch, Artix, Manjaro, Endeavor, etc.)\n\nDownload the latest pineflash pkg.tar.zst file from the [releases page](https://github.com/Spagett1/PineFlash/releases).\n\nThen simply run.\n```\nsudo pacman -U pineflash-*-x86_64.pkg.tar.zst\n```\n\n### <img width=\"18\" src=\"https://cdn.simpleicons.org/Debian\" /> Debian based distros (Debian, Ubuntu, PopOs, etc.)\nDownload the latest pineflash .deb file from the [releases page](https://github.com/Spagett1/PineFlash/releases).\n\nThen just run.\n```\nsudo apt install ./pineflash_*_amd64.deb\n```\n\n### <img width=\"18\" src=\"https://cdn.simpleicons.org/Linux/CC5500\" /> Other Linux x86 distro's\nDownload the latest pineflash .tar.xz file from the [releases page](https://github.com/Spagett1/PineFlash/releases).\n\nExtract the file.\n```\ntar -xf ./pineflash-*-x86_64.tar.xz\n```\n\nAnd copy the contents into your system \n```\ndoas cp -r ./usr /\n```\n> **_NOTE:_**  Make sure you install dfu-util manually and that it is at least version 0.11 as older versions can have issues. Window manager users should ensure they have a pokit agent installed and enabled.\n\n### <img width=\"18\" src=\"https://cdn.simpleicons.org/Apple/818589\" /> MacOs\nSorry we dont have built apps for you yet, head to the build from source section and use the unix install script. \n\n> **_NOTE:_** You will also need to run pineflash from the terminal, this is on the todo list to get fixed.\n\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n\n \n<details>\n  <summary>\n   \n## :building_construction: Build from code \n </summary>\n\n\nUse this build method if the premade binaries do not support your architecture or you have dev purposes.\n\n### :bookmark_tabs: Build Dependancies\n\nInstall these if you don't have them (not needed if using the PKGBUILD).\n\n\n<details>\n  <summary>\n<img width=\"17\" src=\"https://cdn.simpleicons.org/windowsterminal/F46D01\" /> General dependancy list\n</summary>\n\nThis list covers linux distros which are not named below and macos.\n\nIf your operating system has its own section, then please go there; it has package names tailored to your distro.\n\nIf you had to install more dependencies to get it to work, please open an issue to let us know the specific OS and packages you used.\n\n```\ncmake\nrust \ngit\ndfu-util - for pinecil V1 support\nblisp - for pinecil V2 support\npolkit - Linux only\ngcc         \n```\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n\n<details>\n  <summary>\n<img width=\"17\" src=\"https://cdn.simpleicons.org/debian/A81D33\" /> Dependencies for Debian\n</summary>\n\n```\ncmake\nrust-all (alternatively go to https://rustup.rs/)\ngit\ndfu-util - for pinecil V1 support\npolicykit-1\ng++\npkg-config \nlibglib2.0-dev\nbuild-essential    \nlibfontconfig-dev \nfontconfig-config  \nlibgdk3.0-cli-dev\nlibatk1.0-0   \nlibatk1.0-dev       \nlibgtk-3-dev             \n```\nThis line will install everything:\n```\nsudo apt install cmake rust-all git dfu-util policykit-1 g++ pkg-config libglib2.0-dev build-essential libfontconfig-dev fontconfig-config libgdk3.0-cli-dev libatk1.0-0 libatk1.0-dev libgtk-3-dev             \n```\n\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n<details>\n  <summary>\n<img width=\"17\" src=\"https://cdn.simpleicons.org/archlinux/187BC0\" /> Dependencies for Arch\n </summary>\n\n#### Runtime dependancies\n```\ndfu-util - for pinecil V1 support \nblisp - for pinecil V2 support, find this in the AUR\nfontconfig\nglibc\ngtk3\npolkit\n```\n#### Build dependancies\n```\nbase-devel\ncargo-ndk # To verify some integrity checksums of rust modules\ngit\noptipng\npkgconf\nrust\n```\nThis line will install everything except blisp since it's in the aur.\n```\nsudo pacman -S --needed cmake rust git dfu-util polkit gcc pkgconf glibc base-devel fontconfig gtk3\n```\n\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n<details>\n  <summary>\n  \n### :toolbox: Build option 1, handy scripts\n\n </summary>\n \nHandy scripts will compile and install PineFlash for you.\n\n### <img width=\"17\" src=\"https://cdn.simpleicons.org/Linux/187BC0\" /> Build Unix from script. (Macos and Linux)\n1. To build from source code, first install build dependencies.\n2. Download the source code with the following commands.\n```\ngit clone https://github.com/Spagett1/PineFlash/\ncd PineFlash\n```\n3. Run the `generic_unix_install.sh` file which will build and install Pineflash.\n\n### <img width=\"17\" src=\"https://cdn.simpleicons.org/archlinux/187BC0\" />  Build on Arch based distro's (Arch, Artix, Manjaro, Endeavor, Arch Arm, etc.) \n1. All dependencies will be handled by the PKGBUILD\n2. PineFlash is in the aur so you can install it just like any other aur package.\n```\ngit clone https://aur.archlinux.org/pineflash-git.git\ncd pineflash-git\nmakepkg -si\n```\n3. Alternatively just use your favourite aur helper.\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n\n<details>\n  <summary>\n   \n### :weight_lifting_man: Build option 2: manual build\n </summary>\n\nOld school style, this is recommended if you have issues with the scripts or want to help develop PineFlash.\n \n1. Install all the build dependancies listed above.\n\n2. Download the source code.\n\n```\ngit clone https://github.com/Spagett1/PineFlash/\ncd PineFlash \n```\n\n4. Download blisp which is needed for pinecil V2 support, alternatively compile it if you are not on a 64 bit computer. [Instructions](\"https://github.com/pine64/blisp\")\n\nIn the following instructions replace `platform` with your operating system (`linux` or `macos`)\n```\ncurl -L \"https://github.com/pine64/blisp/releases/download/v0.0.3/blisp-platform64-v0.0.3.zip\" -o \"blisp-platform64-v0.0.3.zip\"\nunzip \"blisp-platform-v0.0.3.zip\"\nsudo mv ./blisp /usr/local/bin/blisp\n```\n:dart: Important: Don't forget to add blisp to your path \n\n5. Then build pineflash itself\n```\ncargo build --release\n```\n6. The resulting binary will be in `target/release/pineflash`, this can be moved into your path (`/usr/bin/pineflash`) or just run as a portable executable.\n\n7. Then copy the `assets/Pineflash.desktop` file to `/usr/share/applications` and copy `assets/pine64logo.png` to `/usr/share/pixmaps` for the shortcut to show up in launchers. (This does not apply to MacOs, you will have to run pineflash from the terminal for now, sorry.)\n\n8. Just run the program by typing it into the terminal.\n```\npineflash\n```\nAlternatively just run it from your app launcher (unless your on macos).\n\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n \n \n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n\n## :runner: Run \n\n<img width=\"17\" src=\"https://cdn.simpleicons.org/Linux/CC5500\" /> Linux: Pineflash should appear in app launcher options. Alternatively, you can run the command:  \n\n    pineflash\n\n<img width=\"17\" src=\"https://cdn.simpleicons.org/Apple/818589\" /> MacOS: Sorry, no launcher icon yet, you'll need to run this command to run pineflash.\n\n    pineflash\n\n<img width=\"17\" src=\"https://cdn.simpleicons.org/Windows/187BC0\" /> Windows: Just run pineflash from the start menu.  \n\nIf there is error message like  \n```\nCannot open DFU device ... (LIBUSB_ERROR_NOT_SUPPORTED)\n```\n\nduring flash operation, then `WinUSB` drivers should be installed. See [here](https://github.com/libusb/libusb/wiki/Windows#user-content-How_to_use_libusb_on_Windows) for more details.\n\n\n## Boot Logo Art\n\n- Select your Pinecil version.\n- Click on the folder icon to browse to the art file you want to load.\n- For the v2 you must have IronOs v2.22 installed to support boot logo.\n- If you encounter errors with flashing boot logo's to the v2 ensure your blisp version is at minimum 0.0.4.\n- See [IronOS-Meta](https://github.com/Ralim/IronOS-Meta/) to get premade art or details on how to make custom art.\n\n<br>\n\n<details>\n  <summary>\n \n ## :electric_plug: Connect Pinecil to a PC\n </summary>\n\n1. To do the firmware update, connect one end of a USB cable to the PC.\n2. Then, hold down the `[-]` button **before** plugging the usb-c cable to the back of Pinecil.\n3. Keep holding the `[-]` for ~10 seconds more before releasing the button. If you correctly entered flashing mode, the screen will be black/empty. If not, do it again, flip the cable, or try another cable, different port, or a different PC.\n4. See [Pinecil Wiki](https://wiki.pine64.org/wiki/Pinecil_Firmware) firmware details if you get stuck.\n</details>\n<div style=\"clear:both;\">&nbsp;</div>\n\n## :spiral_calendar: Todo\n\n- [x] Windows support.\n- [ ] MacOS premade binary\n- [ ] Launcher icon for macos, easier method to install\n- [x] Improve UI (colors, design, workflow).\n- [ ] Improve Light Mode.\n- [x] In app instructions for connecting pinecil to pc.\n- [ ] Flatpak and/or Appimage support for linux.\n- [ ] V2 boot logo art support when blisp supports it.\n\n## :tea: Feel like supporting me?\n\nWell you can buy me a coffee, or rather tea bags since i dont drink coffee ;)\n\n<a href=\"https://www.buymeacoffee.com/spagett\" target=\"_blank\"><img src=\"https://cdn.buymeacoffee.com/buttons/default-orange.png\" alt=\"Buy Me A Coffee\" height=\"41\" width=\"174\"></a>\n\n## :book: References\n\n- [Blisp](https://github.com/pine64/blisp) - Backend for flashing Pinecil V2\n- [Dfu-util](https://dfu-util.sourceforge.net/) - Backend for flashing Pinecil V1\n- [Pinecil](https://wiki.pine64.org/wiki/Pinecil) - The Pinecil Wiki page\n- [IronOS](https://github.com/Ralim/IronOS) - The firmware running on this soldering iron\n- [PineSAM](https://github.com/builder555/PineSAM) - A cool Bluetooth app to control Pinecil V2 from any browser\n- [Egui](https://github.com/emilk/egui) - The awesome GUI toolkit used to make this program\n- [InnoSetup](https://github.com/jrsoftware/issrc) - The software i use to generate windows releases\n- [FPM](https://github.com/jordansissel/fpm) - The tool i use to generate linux releases for all sorts of distros\n \n ## :dash: Stay fluxy my friends.\n <img src=\"https://user-images.githubusercontent.com/77225642/229288128-e6993505-47a2-4437-92cf-7b2a5de10677.png\" width=\"425\">\n \n[Source](https://www.reddit.com/r/PINE64official/comments/xk9vxu/most_interesting_man_in_the_world_i_dont_always/)\n"
  },
  {
    "path": "assets/Pineflash.desktop",
    "content": "[Desktop Entry]\nName=PineFlash\nGenericName=PineFlash\nComment=Tool to flash pine64 devices\nExec=pineflash\nIcon=pine64logo\nTerminal=false\nType=Application\nCategories=System\nX-Desktop-File-Install-Version=0.26\n"
  },
  {
    "path": "build.rs",
    "content": "extern crate winresource;\n\nfn main() {\n    if std::env::var(\"CARGO_CFG_TARGET_OS\").unwrap() == \"windows\" {\n        let mut res = winresource::WindowsResource::new();\n        res.set_icon(\"./assets/pine64logo.ico\");\n        res.compile().unwrap();\n    }\n}\n"
  },
  {
    "path": "docs/LICENSE",
    "content": "The GPLv2 License (GPLv2)\n\nCopyright (c) 2022 Spagett1\n\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License version 2, as published by\nthe Free Software Foundation.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program.  If not, see <http://www.gnu.org/licenses/>.\n"
  },
  {
    "path": "docs/html/download.html",
    "content": "<!DOCTYPE html>\n<link href=\"../styles/style.css\" rel=\"stylesheet\" />\n\n<html>\n    <div class=\"topnav\">\n        <a>|</a>\n        <a href=\"../index.html\">Home</a>\n        <a>|</a>\n        <a href=\"../html/download.html\">Download</a>\n        <a>|</a>\n        <a href=\"../html/guide.html\">Usage Guide</a>\n        <a>|</a>\n        <a href=\"https://github.com/Spagett1/PineFlash\">Source Code</a>\n        <a>|</a>\n    </div> \n    <h1>Downloads</h1>\n    <h3>Latest Version: v0.5.4 Bug Begone!</h3>\n    <div class=\"row\">\n        <div class=\"col\">\n            <div class=\"inner-title\">\n                <img class=\"logo\" src=\"../images/windows.svg\" alt=\"Windows icon\">\n                <h2>Windows</h2>\n            </div>\n            x64: <a href=\"https://github.com/Spagett1/PineFlash/releases/download/0.5.4/pineflash-0.5.4-win64.exe\">pineflash-0.5.4-win64.exe</a>\n        </div>\n        <div class=\"col\">\n            <div class=\"inner-title\">\n                <img class=\"logo\" src=\"../images/tux.svg\" alt=\"Linux icon\">\n                <h2>Linux</h2>\n            </div>\n            Appimage: <a href=\"https://github.com/Spagett1/PineFlash/releases/download/0.5.4/pineflash-0.5.4-x86_64.AppImage\">pineflash-0.5.4-x86_64.AppImage</a>\n            <br>\n            Arch:     <a href=\"https://github.com/Spagett1/PineFlash/releases/download/0.5.4/pineflash-0.5.4-1-x86_64.pkg.tar.zst\">   pineflash-0.5.4-1-x86_64.pkg.tar.zst</a>\n            <br>\n            Fedora:   <a href=\"https://github.com/Spagett1/PineFlash/releases/download/0.5.4/pineflash-fedora-0.5.4-x86_64.rpm\">pineflash-fedora-0.5.4-x86_64.rpm</a>\n            <br>\n            Debian:   <a href=\"https://github.com/Spagett1/PineFlash/releases/download/0.5.4/pineflash_0.5.4_amd64.deb\">pineflash_0.5.4_amd64.deb</a>\n            <br>\n            Rhel 9:   <a href=\"https://github.com/Spagett1/PineFlash/releases/download/0.5.4/pineflash-rhel9-0.5.4-x86_64.rpm\">pineflash-rhel9-0.5.4-x86_64.rpm</a>\n        </div>\n        <div class=\"col\">\n            <div class=\"inner-title\">\n                <img class=\"logo\" src=\"../images/macos.svg\" alt=\"MacOs icon\">\n                <h2>MacOS</h2>\n            </div>\n            Not available yet.\n        </div>\n    </div>\n\n</html>\n"
  },
  {
    "path": "docs/index.html",
    "content": "<!DOCTYPE html>\n<link href=\"styles/style.css\" rel=\"stylesheet\" />\n\n<html>\n    <div class=\"topnav\">\n        <a>|</a>\n        <a href=\"./index.html\">Home</a>\n        <a>|</a>\n        <a href=\"./html/download.html\">Download</a>\n        <a>|</a>\n        <a href=\"./html/guide.html\">Usage Guide</a>\n        <a>|</a>\n        <a href=\"https://github.com/Spagett1/PineFlash\">Source Code</a>\n        <a>|</a>\n    </div> \n    <head>\n        <title>Pineflash main page</title>\n    </head>\n    <body>\n\n        <h1>Pineflash</h1>\n        <div class=\"col\">\n            <p>Pineflash is a tool for updating the firmware on the Pinecil soldering irons.</p>\n            <IMG class=\"displayed\" src=\"./images/AppPage.png\" alt=\"Pineflash screenshot\">\n            <p>Pineflash is a fully open source project, the only official download links are this site and the <a href=\"https://github.com/Spagett1/PineFlash\">github</a> page, do not download it from unofficial sources.</p>\n            \n        </div>\n\n    </body>\n</html> \n"
  },
  {
    "path": "docs/styles/style.css",
    "content": "/* {font-size:13pt;font-weight:normal;} */\n\n@media (min-width: 64em) {\n    .row {\n        display: flex;\n        flex-wrap: nowrap;\n        flex-direction: row-reverse;\n    }\n\n    .col {\n        flex-grow: 1;\n    }\n}\n\nimg.displayed {\n    display: block;\n    height: auto;\n    max-width: 90%;\n    margin-left: auto;\n    margin-right: auto\n}\n\nimg.logo {\n    float: left;\n    display: block;\n    height: 2em;\n    max-width: auto%;\n    margin-left: 0;\n    margin-right: 0.5em;\n}\n\n.grid {\n    border-collapse: collapse;\n}\n\n.col {\n    border: 6px double #fb5;\n    padding: 1em;\n}\n\n.inner-title {\n\tdisplay: flex;\n    align-items: center;\n}\n\n.user-list {\n    display: flex;\n    flex-flow: row wrap;\n    justify-content: space-evenly;\n    list-style-type: none;\n    padding: 0;\n}\n\n.user-list li {\n    overflow: hidden;\n    text-overflow: ellipsis;\n    white-space: nowrap;\n    flex: 10em;\n}\n\n.user-list::after {\n    content: \"\";\n}\n\nbody {\n    margin: auto;\n    padding: 1em;\n    max-width: 64em;\n    font-family: \"courier new\", monospace;\n    color: #fb5;\n    background: #111;\n    word-wrap: break-word;\n}\n\na {\n    text-decoration: none;\n    color: #f70;\n    font-weight: bold;\n    padding-right: 0.25em;\n}\n\na:hover {\n    color: #f20;\n}\n\nhr {\n    border-color: #fb5;\n}\n\n.text-center {\n    text-align: center\n}\n\n.advisory {\n    background: #fc4;\n    color: #222;\n    font-weight: bold;\n    padding: 1em;\n    border-radius: 0.25em;\n}\n\nh1,\nh2 {\n    text-transform: uppercase;\n    color: #fb5;\n    font-family: monospace;\n    font-size: 2em;\n}\n\n/* THEGOODS */\n#fancyboi::before {\n    content: \"$ \";\n}\n\n@media (prefers-reduced-motion: no-preference) {\n    @keyframes flash {\n        50% {\n            opacity: 0;\n        }\n    }\n\n    @keyframes reveal {\n        from {\n            width: 2em;\n        }\n\n        /* Width of ::before */\n        to {\n            width: 100%;\n        }\n    }\n\n    #fancyboi {\n        overflow: hidden;\n        white-space: nowrap;\n        animation: reveal 4s linear;\n        text-overflow: \"█\";\n    }\n\n    #fancyboi::after {\n        content: \"█\";\n        animation: flash 0.5s step-end infinite;\n    }\n}\n\n/* SDOOGEHT */\n.sourceCode {\n    color: #008000;\n}\n\ncode>span.fl {\n    color: #008000;\n}\n\n/* Style the navbar */\n#navbar {\n    overflow: hidden;\n    background-color: #fb5;\n    color: #222;\n    z-index: 99;\n    position: relative;\n    top: 0;\n    left: 0;\n    width: 100%;\n    border-radius: 0.25em;\n}\n\n#navbar a:hover {\n    background-color: #f93;\n}\n\n/* Navbar links */\n#navbar a {\n    float: left;\n    display: block;\n    color: black;\n    text-align: center;\n    padding: 14px;\n    text-decoration: none;\n}\n\n/* Page content */\n.content {\n    padding-top: 5px;\n}\n\ninput[type=\"text\"],\ntextarea {\n    background-color: #333;\n    color: darkorange;\n}\n\ndiv.alert-warning {\n    background-color: darkred;\n}\n\ndiv.alert-success {\n    background-color: darkgreen;\n}\n\nblockquote {\n    border-left: 2px solid #fb5;\n    background-color: rgba(255, 187, 85, 5%);\n    padding: 0.5em;\n}\n\n/* Mobile */\n@media (max-width: 768px) {\n    td {\n        display: block;\n    }\n}\n"
  },
  {
    "path": "generate-release.sh",
    "content": "#!/bin/sh\n#\n# dev packages needed in each container\n# Packages-redhatbased: glib2-devel gcc atk-devel openssl-devel gtk3-devel systemd-devel gem rpm-build\n# Packages-debianbased: gcc libglib2.0-dev libgtk-3-dev libssl-dev ruby libudev-dev\n# Packages-archbased: gtk3 rubygems\n#\n#\n# Remove old versions\n. \"$HOME/.cargo/env\"\nPATH=$PATH:/home/spagett/.local/share/gem/ruby/3.0.0/bin:/home/spagett/bin\nrm pineflash*{.deb,.rpm,.exe,.tar*,.AppImage} 2> /dev/null\nver=$(grep \"^version \" Cargo.toml | cut -d\\\" -f2)\n# files=target/release/pineflash=/usr/bin/pineflash assets/Pineflash.desktop=/usr/share/applications/Pineflash.desktop assets/pine64logo.png=/usr/share/pixmaps/pine64logo.png LICENSE=/usr/share/licenses/pineflash/LICENSE\narch=\"x86_64\"\ncargo clean\ncargo build --target x86_64-pc-windows-gnu --release\ncargo appimage --features=appimage\nmv target/appimage/pineflash.AppImage \"./pineflash-$ver-$arch.AppImage\"\n\n# Generate windows release\n# Update version number \nsed -i \"s/#define MyAppVersion.*/#define MyAppVersion \\\"$ver\\\"/g\" ./Package_Windows_Release.iss\nwine /home/spagett/.wine/drive_c/Program\\ Files\\ \\(x86\\)/Inno\\ Setup\\ 6/ISCC.exe ./Package_Windows_Release.iss\nmv PineFlash_Installer.exe pineflash-$ver-win64.exe\n\n# Cleans for new environment\ncargo clean\n# Generate rpm release\ndistrobox enter --name fedora-dev -- cargo build --release \ndistrobox enter --name fedora-dev -- fpm -s dir -t rpm \\\n  --name pineflash \\\n  --license gpl2 \\\n  --version $ver \\\n  --architecture $arch \\\n  --depends polkit \\\n  --depends dfu-util \\\n  -p \"pineflash-fedora-$ver-$arch.rpm\" \\\n  --description \"flashing tool for pinecil soldering irons.\" \\\n  --url \"https://github.com/spagett1/pineflash\" \\\n  --maintainer \"spagett <laar@tutanota.com>\" \\\n  target/release/pineflash=/usr/bin/pineflash assets/Pineflash.desktop=/usr/share/applications/Pineflash.desktop assets/pine64logo.png=/usr/share/pixmaps/pine64logo.png LICENSE=/usr/share/licenses/pineflash/LICENSE tools/linux/blisp=/usr/bin/blisp\n\n# Cleans for new environment\ncargo clean\n\n# Generate rpm release for rhel\n# distrobox enter --name rhel-dev -- cargo build --release\n# distrobox enter --name rhel-dev -- fpm -s dir -t rpm \\\n#   --name pineflash \\\n#   --license gpl2 \\\n#   --version $ver \\\n#   --architecture $arch \\\n#   --depends polkit \\\n#   --depends dfu-util \\\n#   -p \"pineflash-rhel9-$ver-$arch.rpm\" \\\n#   --description \"flashing tool for pinecil soldering irons.\" \\\n#   --url \"https://github.com/spagett1/pineflash\" \\\n#   --maintainer \"spagett <laar@tutanota.com>\" \\\n#   target/release/pineflash=/usr/bin/pineflash assets/Pineflash.desktop=/usr/share/applications/Pineflash.desktop assets/pine64logo.png=/usr/share/pixmaps/pine64logo.png LICENSE=/usr/share/licenses/pineflash/LICENSE tools/linux/blisp=/usr/bin/blisp\n\n\n# Cleans for new environment\n# cargo clean\n\ndistrobox enter --name debian-dev -- cargo build --release\ndistrobox enter --name debian-dev -- fpm -s dir -t deb \\\n  --name pineflash \\\n  --license gpl2 \\\n  --version $ver \\\n  --architecture $arch \\\n  --depends policykit-1 \\\n  --depends dfu-util \\\n  --depends libxkbcommon0 \\\n  --description \"Flashing tool for pinecil soldering irons.\" \\\n  --url \"https://github.com/Spagett1/PineFlash\" \\\n  --maintainer \"Spagett <laar@tutanota.com>\" \\\n  target/release/pineflash=/usr/bin/pineflash assets/Pineflash.desktop=/usr/share/applications/Pineflash.desktop assets/pine64logo.png=/usr/share/pixmaps/pine64logo.png LICENSE=/usr/share/licenses/pineflash/LICENSE tools/linux/blisp=/usr/bin/blisp\n\n# Cleans for new environment\ncargo clean\n\n# # Generate arch release\ndistrobox enter --name arch-dev -- cargo build --release\ndistrobox enter --name arch-dev -- fpm -s dir -t pacman \\\n  --name pineflash \\\n  --license gpl2 \\\n  --version $ver \\\n  --architecture $arch \\\n  --depends polkit \\\n  --depends libxkbcommon \\\n  --depends dfu-util \\\n  --description \"Flashing tool for pinecil soldering irons.\" \\\n  --url \"https://github.com/Spagett1/PineFlash\" \\\n  --maintainer \"Spagett <laar@tutanota.com>\" \\\n  target/release/pineflash=/usr/bin/pineflash assets/Pineflash.desktop=/usr/share/applications/Pineflash.desktop assets/pine64logo.png=/usr/share/pixmaps/pine64logo.png LICENSE=/usr/share/licenses/pineflash/LICENSE tools/linux/blisp=/usr/bin/blisp\n# # Generate generic release\n# fpm -s dir -t tar \\\n#   --name pineflash \\\n#   --license gpl2 \\\n#   --version $ver \\\n#   --architecture $arch \\\n#   --depends polkit \\\n#   --description \"Flashing tool for pinecil soldering irons.\" \\\n#   --url \"https://github.com/Spagett1/PineFlash\" \\\n#   --maintainer \"Spagett <laar@tutanota.com>\" \\\n#   target/release/pineflash=/usr/bin/pineflash assets/Pineflash.desktop=/usr/share/applications/Pineflash.desktop assets/pine64logo.png=/usr/share/pixmaps/pine64logo.png LICENSE=/usr/share/licenses/pineflash/LICENSE\n# mv pineflash.tar pineflash-$ver-1-x86_64.tar\n# xz pineflash-$ver-1-x86_64.tar\n#\n"
  },
  {
    "path": "generic_unix_install.sh",
    "content": "#!/bin/bash\nif command -v doas >> /dev/null; then\n  root=doas\nelse \n  root=sudo\nfi\n\n# Ensure cargo is installed before trying to proceed\nif ! command -v cargo > /dev/null 2>&1; then\n    echo \"cargo command is not installed. Cannot proceed. Please ensure cargo is installed and on the PATH\"\n    exit 1\nfi\n\ncargo build --release\n\nif [ \"$(uname)\" == \"Darwin\" ]; then\n  curl -L \"https://github.com/pine64/blisp/releases/download/v0.0.4/blisp-apple-x86_64-v0.0.4.zip\" -o \"blisp-apple-x86_64-v0.0.4.zip\"\n  unzip \"blisp-apple-x86_64-v0.0.4.zip\"\n  chmod +x ./blisp\n  $root cp ./blisp /usr/local/bin/blisp\n  $root chmod +x /usr/local/bin/blisp\n  $root cp ./target/release/pineflash /usr/local/bin/\nelif [ \"$(uname -m)\" == \"x86_64\" ]; then\n  curl -L \"https://github.com/pine64/blisp/releases/download/v0.0.4/blisp-linux-x86_64-v0.0.4.zip\" -o \"blisp-linux64-v0.0.4.zip\"\n  unzip \"blisp-linux64-v0.0.4.zip\"\n  $root cp ./blisp /usr/local/bin/blisp\n  $root chmod +x /usr/local/bin/blisp\n  $root cp ./assets/Pineflash.desktop /usr/share/applications/Pineflash.desktop\n  $root cp ./assets/pine64logo.png /usr/share/pixmaps/pine64logo.png\n  $root cp ./target/release/pineflash /usr/bin/pineflash\nelse \n  git clone --recursive \"https://github.com/pine64/blisp.git\"\n  cd blisp\n  git submodule update --init --recursive\n  mkdir build && cd build\n  cmake -DBLISP_BUILD_CLI=ON ..\n  cmake --build .\n  $root cp ./blisp/build/tools/blisp/blisp /usr/local/bin/blisp\n  $root chmod +x /usr/local/bin/blisp\n  cd ../..\n  $root cp ./assets/Pineflash.desktop /usr/share/applications/Pineflash.desktop\n  $root cp ./assets/pine64logo.png /usr/share/pixmaps/pine64logo.png\n  $root cp ./target/release/pineflash /usr/bin/pineflash\nfi\n"
  },
  {
    "path": "src/.main.rs.rustfmt",
    "content": "#![windows_subsystem = \"windows\"]\n\nuse std::{\n    collections::HashMap,\n    env,\n    fs::{self, File},\n    io::{Cursor, Read},\n    path::PathBuf,\n    time::Duration,\n};\n\nuse curl::easy::Easy;\nuse eframe::egui;\nuse eframe::{\n    emath,\n    epaint::{Color32, Rounding, Stroke},\n    CreationContext,\n};\nmod submodules;\nuse egui::Context;\nuse egui_extras::RetainedImage;\nuse egui_file::FileDialog;\nuse egui_notify::{Anchor, Toasts};\nuse serde::{Deserialize, Serialize};\nuse serde_json::Value;\n\nconst ICON: &[u8] = include_bytes!(\"../assets/pine64logo.ico\");\n\n#[derive(Serialize, Deserialize)]\nstruct Language {\n    language_code: String,\n    language_name: String,\n}\n\n#[derive(Serialize, Deserialize)]\nstruct YourValue {\n    contents: HashMap<String, Language>,\n}\n\nstruct FlasherConfig {\n    iron: String,\n    int_name: String,\n    version: String,\n    fancy_names: Vec<String>,\n    code_names: Vec<String>,\n    lang: String,\n    versions_checked: bool,\n    vers: Vec<String>,\n    download_metadata: bool,\n    blisp_version: String,\n    run_once_vers: bool,\n    download: bool,\n    picked_path: Option<String>,\n    download_versions: bool,\n    download_firm_notify: bool,\n    ready_to_flash: bool,\n    logs: String,\n    json: String,\n    iron_connected: Option<String>,\n    check_count: i32,\n    flash: bool,\n    flash_notified_count: i32,\n    v2_serial_path: Option<String>,\n    connection_guide_image: [RetainedImage; 3],\n    current_step: usize,\n    json_checked: bool,\n    metadata_path: PathBuf,\n    open_file_dialog: Option<FileDialog>,\n}\n\n#[derive(Serialize, Deserialize)]\nstruct FlashSavedConfig {\n    pub dark_mode: bool,\n}\n\nimpl Default for FlashSavedConfig {\n    fn default() -> Self {\n        Self { dark_mode: true }\n    }\n}\n\nstruct Flasher {\n    config: FlasherConfig,\n    saved_config: FlashSavedConfig,\n    toasts: Toasts,\n}\n\nimpl Default for FlasherConfig {\n    fn default() -> Self {\n        Self {\n            iron: \"Pinecil V2\".to_string(),\n            int_name: \"Pinecilv2\".to_string(),\n            version: \"Select\".to_string(),\n            fancy_names: vec![],\n            code_names: vec![],\n            lang: \"EN\".to_string(),\n            versions_checked: false,\n            vers: vec![],\n            download_metadata: false,\n            run_once_vers: true,\n            json_checked: false,\n            download: false,\n            blisp_version: \"\".to_string(),\n            download_versions: true,\n            download_firm_notify: true,\n            picked_path: None,\n            ready_to_flash: false,\n            #[cfg(feature = \"appimage\")]\n            logs: format!(\"Pineflash v{} Linux Appimage\\n\", env!(\"CARGO_PKG_VERSION\")),\n            #[cfg(not(feature = \"appimage\"))]\n            #[cfg(target_os = \"linux\")]\n            logs: format!(\"Pineflash v{} Linux Native\\n\", env!(\"CARGO_PKG_VERSION\")),\n            #[cfg(target_os = \"windows\")]\n            logs: format!(\"Pineflash v{} Windows\\n\", env!(\"CARGO_PKG_VERSION\")),\n            #[cfg(target_os = \"macos\")]\n            logs: format!(\"Pineflash v{} MacOs\\n\", env!(\"CARGO_PKG_VERSION\")),\n            json: \"\".to_string(),\n            iron_connected: None,\n            check_count: 0,\n            flash: false,\n            flash_notified_count: 0,\n            v2_serial_path: None,\n            connection_guide_image: [\n                RetainedImage::from_svg_bytes(\"Step1\", include_bytes!(\"../assets/Step1.svg\"))\n                    .unwrap(),\n                RetainedImage::from_svg_bytes(\"Step2\", include_bytes!(\"../assets/Step2.svg\"))\n                    .unwrap(),\n                RetainedImage::from_svg_bytes(\"Step3\", include_bytes!(\"../assets/Step3.svg\"))\n                    .unwrap(),\n            ],\n            current_step: 0,\n            metadata_path: [std::env::temp_dir(), \"metadata.json\".into()]\n                .iter()\n                .collect(),\n            open_file_dialog: None,\n        }\n    }\n}\n\nimpl Flasher {\n    fn new(cc: &CreationContext) -> Flasher {\n        let config: FlasherConfig = FlasherConfig::default();\n        // Flasher::configure_fonts(&cc.egui_ctx);\n        let saved_config: FlashSavedConfig = confy::load(\"PineFlash\", None).unwrap_or_default();\n        let toasts = Toasts::default()\n            .with_anchor(Anchor::TopRight)\n            .with_margin(emath::vec2(0.0, 120.0));\n\n        let mut style: egui::Style = (*cc.egui_ctx.style()).clone();\n        style.spacing.item_spacing = egui::vec2(5.0, 10.0);\n\n        cc.egui_ctx.set_style(style);\n        let new_style = egui::style::WidgetVisuals {\n            bg_fill: Color32::from_rgb(17, 17, 17),\n            weak_bg_fill: Color32::from_rgb(17, 17, 17),\n\n            ddd\n\n            rounding: Rounding {\n                nw: 4.,\n                djiwjd\n\n                ne: 4.,\n                sw: 4.,\n                se: 4.,\n            },\n\n            bg_stroke: Stroke {\n                width: 1.,\n                color: Color32::from_rgb(140, 140, 140),\n            },\n            fg_stroke: Stroke {\n                width: 1.,\n                color: Color32::from_rgb(140, 140, 140),\n            },\n\n            expansion: 2.,\n        };\n        let new_hovered_style = egui::style::WidgetVisuals {\n            bg_fill: Color32::from_rgb(17, 17, 17),\n            weak_bg_fill: Color32::from_rgb(17, 17, 17),\n\n            rounding: Rounding {\n                nw: 4.,\n                ne: 4.,\n                sw: 4.,\n                se: 4.,\n            },\n\n            bg_stroke: Stroke {\n                width: 1.5,\n                color: egui::Color32::from_rgb(56, 55, 55),\n            },\n            fg_stroke: Stroke {\n                width: 1.,\n                color: Color32::from_rgb(140, 140, 140),\n            },\n\n            expansion: 2.,\n        };\n        cc.egui_ctx.set_visuals(egui::style::Visuals {\n            widgets: egui::style::Widgets {\n                active: new_style,\n                inactive: new_style,\n                hovered: new_hovered_style,\n                noninteractive: new_style,\n                open: new_hovered_style,\n            },\n            ..Default::default()\n        });\n\n        if !saved_config.dark_mode {\n            cc.egui_ctx.set_visuals(egui::Visuals::light());\n        }\n\n        Flasher::configure_fonts(cc.egui_ctx.clone());\n        Flasher {\n            config,\n            toasts,\n            saved_config,\n        }\n    }\n}\n\nimpl eframe::App for Flasher {\n    fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {\n        // always repaint to have accurate pinecil detection\n        ctx.request_repaint();\n        ctx.set_pixels_per_point(1.8);\n\n        if self.config.check_count < 180 {\n            self.config.check_count += 1\n        } else {\n            self.config.iron_connected = Flasher::check_connections(self);\n            self.config.check_count = 0\n        }\n\n        if self.config.download_versions {\n            let _ = std::fs::remove_file(self.config.metadata_path.clone());\n            self.config.download_versions = !self.config.download_versions;\n            self.toasts\n                .info(\"Fetching versions\")\n                .set_duration(None)\n                .set_closable(false);\n\n            std::thread::spawn(|| {\n                let mut data = Vec::new();\n                let mut handle = Easy::new();\n                let mut internet = true;\n                handle\n                    .url(\"https://api.github.com/repos/Ralim/IronOS/releases\")\n                    .unwrap();\n                handle.useragent(\"PineFlash\").unwrap();\n                {\n                    let mut transfer = handle.transfer();\n                    transfer\n                        .write_function(|new_data| {\n                            data.extend_from_slice(new_data);\n                            Ok(new_data.len())\n                        })\n                        .unwrap();\n                    if transfer.perform().is_err() {\n                        internet = false;\n                    }\n                }\n                let path: PathBuf = [std::env::temp_dir(), \"metadata.json\".into()]\n                    .iter()\n                    .collect();\n\n                if internet {\n                    let json = String::from_utf8(data).unwrap();\n                    std::fs::write(path, json).unwrap();\n                } else {\n                    std::fs::write(path, \"No Internet\").unwrap();\n                }\n            });\n        } else if self.config.metadata_path.exists()\n            && !self.config.versions_checked\n            && !String::from_utf8(std::fs::read(self.config.metadata_path.clone()).unwrap())\n                .unwrap()\n                .contains(\"No Internet\")\n        {\n            self.toasts.dismiss_all_toasts();\n            self.toasts\n                .info(\"Versions Found\")\n                .set_duration(Some(Duration::from_secs(5)))\n                .set_closable(false);\n            self.config\n                .logs\n                .push_str(\"PineFlash: Versions successfully fetched.\\n\");\n            self.config.json_checked = !self.config.json_checked;\n            let string =\n                String::from_utf8(std::fs::read(self.config.metadata_path.clone()).unwrap())\n                    .unwrap();\n            let json: Result<Value, serde_json::Error> = serde_json::from_str(string.as_str());\n            if json.is_err() {\n                self.toasts.dismiss_all_toasts();\n                self.toasts\n                    .error(\"Could not access github, Online Files Will be Unavailable\")\n                    .set_duration(Some(Duration::from_secs(5)))\n                    .set_closable(false);\n                self.config\n                    .logs\n                    .push_str(\"PineFlash: Invalid json downloaded, could not fetch versions.\\n\");\n                self.config.versions_checked = true;\n            } else {\n                for i in 0..3 {\n                    let version = json.as_ref().unwrap()[i][\"tag_name\"].as_str().unwrap();\n                    self.config.vers.push(version.to_string());\n                }\n                self.config.versions_checked = true;\n                self.config.download_metadata = true;\n            }\n        } else if !self.config.versions_checked\n            && self.config.metadata_path.exists()\n            && String::from_utf8(std::fs::read(self.config.metadata_path.clone()).unwrap())\n                .unwrap()\n                .contains(\"No Internet\")\n        {\n            self.toasts.dismiss_all_toasts();\n            self.toasts\n                .error(\"No Internet, Online Files Will be Unavailable\")\n                .set_duration(Some(Duration::from_secs(5)))\n                .set_closable(false);\n            self.config\n                .logs\n                .push_str(\"PineFlash: No internet, could not fetch versions.\\n\");\n            self.config.versions_checked = true;\n        }\n\n        Flasher::render_header(self, ctx);\n        Flasher::render_main_windows(self, ctx);\n\n        if self.config.download {\n            let path: PathBuf = [\n                std::env::temp_dir(),\n                format!(\"{}-{}.zip\", self.config.version, self.config.int_name).into(),\n            ]\n            .iter()\n            .collect();\n            let pathlock: PathBuf = [std::env::temp_dir(), \"firmware.lock\".into()]\n                .iter()\n                .collect();\n            if self.config.download_firm_notify {\n                let _ = std::fs::write(pathlock, \"Locked\");\n                self.toasts\n                    .info(\"Downloading\")\n                    .set_duration(None)\n                    .set_closable(false);\n                self.config.logs.push_str(\n                    format!(\n                        \"PineFlash: Downloading Firmware {} {}.\\n\",\n                        self.config.int_name, self.config.version\n                    )\n                    .as_str(),\n                );\n                let _ = std::fs::remove_file(path);\n                let version = self.config.version.clone();\n                let int_name = self.config.int_name.clone();\n                std::thread::spawn(move || {\n                    let mut data = Vec::new();\n                    let mut handle = Easy::new();\n                    let path: PathBuf = [\n                        std::env::temp_dir(),\n                        format!(\"{}-{}.zip\", version, int_name).into(),\n                    ]\n                    .iter()\n                    .collect();\n                    let pathlock: PathBuf = [std::env::temp_dir(), \"firmware.lock\".into()]\n                        .iter()\n                        .collect();\n                    let _ = handle.url(\n                        format!(\n                            \"https://github.com/Ralim/IronOS/releases/download/{}/{}.zip\",\n                            version, int_name\n                        )\n                        .as_str(),\n                    );\n                    let _ = handle.follow_location(true);\n                    handle.useragent(\"PineFlash\").unwrap();\n                    {\n                        let mut transfer = handle.transfer();\n                        transfer\n                            .write_function(|new_data| {\n                                data.extend_from_slice(new_data);\n                                Ok(new_data.len())\n                            })\n                            .unwrap();\n                        transfer.perform().unwrap()\n                    }\n                    let zip = data.as_slice();\n                    if std::fs::write(path, zip).is_err() {\n                        println!(\"Could not write zip file\")\n                    };\n                    if std::fs::remove_file(pathlock).is_err() {\n                        println!(\"Could not remove lockfile\")\n                    };\n                });\n                self.config.download_firm_notify = false\n            } else if !self.config.download_firm_notify && !pathlock.exists() {\n                self.toasts.dismiss_all_toasts();\n                self.config.logs.push_str(\"PineFlash: Download Complete.\\n\");\n                self.toasts\n                    .info(\"Flashing.\")\n                    .set_duration(None)\n                    .set_closable(false);\n                self.config.download = false;\n                self.config.flash = true;\n            }\n        }\n        if !self.config.version.contains(\"Select\")\n            && !self.config.version.contains(\"Custom\")\n            && self.config.download_metadata\n        {\n            let path: PathBuf = [std::env::temp_dir(), \"langs.zip\".into()].iter().collect();\n            let pathlock: PathBuf = [std::env::temp_dir(), \"langs.lock\".into()].iter().collect();\n            if self.config.run_once_vers {\n                self.toasts\n                    .info(\"Downloading Language information.\")\n                    .set_duration(None)\n                    .set_closable(false);\n                let _ = std::fs::remove_file(path);\n                let _ = std::fs::write(pathlock, \"Locked\");\n                let version = self.config.version.clone();\n                std::thread::spawn(move || {\n                    let mut data = Vec::new();\n                    let mut handle = Easy::new();\n                    let path: PathBuf = [std::env::temp_dir(), \"langs.zip\".into()].iter().collect();\n                    let pathlock: PathBuf =\n                        [std::env::temp_dir(), \"langs.lock\".into()].iter().collect();\n                    let _ = std::fs::remove_file(path.clone());\n\n                    let _ = handle.follow_location(true);\n                    handle\n                        .url(\n                            format!(\n                                \"https://github.com/Ralim/IronOS/releases/download/{}/metadata.zip\",\n                                version\n                            )\n                            .as_str(),\n                        )\n                        .unwrap();\n                    handle.useragent(\"PineFlash\").unwrap();\n                    {\n                        let mut transfer = handle.transfer();\n                        transfer\n                            .write_function(|new_data| {\n                                data.extend_from_slice(new_data);\n                                Ok(new_data.len())\n                            })\n                            .unwrap();\n                        transfer.perform().unwrap();\n                    }\n                    let zip = data.as_slice();\n                    if std::fs::write(path, zip).is_err() {\n                        println!(\"Could not write zip file\")\n                    };\n                    if std::fs::remove_file(pathlock).is_err() {\n                        println!(\"Could not remove lockfile\")\n                    };\n                });\n                self.config.run_once_vers = false;\n            } else if !pathlock.exists() {\n                let path: PathBuf = [std::env::temp_dir(), \"langs.zip\".into()].iter().collect();\n                self.toasts.dismiss_all_toasts();\n                self.config\n                    .logs\n                    .push_str(\"PineFlash: Download of Language Info Complete.\\n\");\n                self.toasts\n                    .info(\"Languages Downloaded.\")\n                    .set_duration(Some(Duration::from_secs(3)))\n                    .set_closable(false);\n                let mut file = File::open(path).unwrap();\n                let mut data = Vec::new();\n                file.read_to_end(&mut data).unwrap();\n                let target_dir: PathBuf =\n                    [std::env::temp_dir(), \"metadata\".into()].iter().collect();\n                zip_extract::extract(Cursor::new(data), &target_dir, false).unwrap();\n                let json_path: PathBuf = [\n                    std::env::temp_dir(),\n                    \"metadata\".into(),\n                    format!(\"{}.json\", self.config.int_name).into(),\n                ]\n                .iter()\n                .collect();\n                self.config.json = fs::read_to_string(json_path).unwrap();\n                self.config.download_metadata = false;\n\n                let value: YourValue = serde_json::from_str(self.config.json.as_str()).unwrap();\n                for i in value.contents {\n                    if !i.0.contains(\".hex\") {\n                        let a = i.1;\n                        self.config.fancy_names.push(a.language_name);\n                        self.config.code_names.push(a.language_code);\n                    }\n                }\n                self.config.download_metadata = false;\n            }\n        }\n\n        if self.config.flash {\n            if self.config.flash_notified_count < 60 {\n                self.config.flash_notified_count += 1\n            } else {\n                Flasher::flash(self);\n            }\n        }\n    }\n}\n\nfn main() {\n    let options = eframe::NativeOptions {\n        decorated: true,\n        follow_system_theme: true,\n        icon_data: Some(eframe::IconData {\n            rgba: (ICON.to_vec()),\n            width: (32),\n            height: (32),\n        }),\n        resizable: true,\n        initial_window_size: Some(emath::Vec2 { x: 780., y: 680. }),\n        min_window_size: Some(emath::Vec2 { x: 780., y: 280. }),\n        ..Default::default()\n    };\n\n    match eframe::run_native(\n        \"PineFlash\",\n        options,\n        Box::new(|cc| Box::new(Flasher::new(cc))),\n    ) {\n        Ok(_) => (),\n        Err(_) => println!(\"A massive error occured, not sure whats goin on here.\"),\n    }\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![windows_subsystem = \"windows\"]\nuse std::{\n    collections::HashMap,\n    env,\n    fs::{self, File},\n    io::{Cursor, Read},\n    path::PathBuf,\n    time::Duration,\n};\n\nuse curl::easy::Easy;\nuse eframe::{egui::{self, ImageSource}, epaint::ColorImage};\nuse eframe::{\n    emath,\n    epaint::{Color32, Rounding, Stroke},\n    CreationContext,\n};\nmod submodules;\nuse egui::Context;\nuse egui_file::FileDialog;\nuse egui_notify::{Anchor, Toasts};\nuse serde::{Deserialize, Serialize};\nuse serde_json::Value;\n\nconst ICON: &[u8] = include_bytes!(\"../assets/pine64logo.ico\");\n\n#[derive(Serialize, Deserialize)]\nstruct Language {\n    language_code: String,\n    language_name: String,\n}\n\n#[derive(Serialize, Deserialize)]\nstruct YourValue {\n    contents: HashMap<String, Language>,\n}\n\nstruct FlasherConfig {\n    iron: String,\n    int_name: String,\n    version: String,\n    fancy_names: Vec<String>,\n    code_names: Vec<String>,\n    lang: String,\n    versions_checked: bool,\n    vers: Vec<String>, download_metadata: bool,\n    blisp_version: String,\n    run_once_vers: bool,\n    download: bool,\n    picked_path: Option<String>,\n    download_versions: bool,\n    download_firm_notify: bool,\n    ready_to_flash: bool,\n    logs: String,\n    json: String,\n    iron_connected: Option<String>,\n    check_count: i32,\n    flash: bool,\n    flash_notified_count: i32,\n    v2_serial_path: Option<String>,\n    // connection_guide_image: Vec<ColorImage>,\n    current_step: usize,\n    json_checked: bool,\n    metadata_path: PathBuf,\n    open_file_dialog: Option<FileDialog>,\n}\n\n#[derive(Serialize, Deserialize)]\nstruct FlashSavedConfig {\n    pub dark_mode: bool,\n}\n\nimpl Default for FlashSavedConfig {\n    fn default() -> Self {\n        Self { dark_mode: true }\n    }\n}\n\nstruct Flasher {\n    config: FlasherConfig,\n    saved_config: FlashSavedConfig,\n    toasts: Toasts,\n}\n\nimpl Default for FlasherConfig {\n    fn default() -> Self {\n        Self {\n            iron: \"Pinecil V2\".to_string(),\n            int_name: \"Pinecilv2\".to_string(),\n            version: \"Select\".to_string(),\n            fancy_names: vec![],\n            code_names: vec![],\n            lang: \"EN\".to_string(),\n            versions_checked: false,\n            vers: vec![],\n            download_metadata: false,\n            run_once_vers: true,\n            json_checked: false,\n            download: false,\n            blisp_version: \"\".to_string(),\n            download_versions: true,\n            download_firm_notify: true,\n            picked_path: None,\n            ready_to_flash: false,\n            #[cfg(feature = \"appimage\")]\n            logs: format!(\"Pineflash v{} Linux Appimage\\n\", env!(\"CARGO_PKG_VERSION\")),\n            #[cfg(not(feature = \"appimage\"))]\n            #[cfg(target_os = \"linux\")]\n            logs: format!(\"Pineflash v{} Linux Native\\n\", env!(\"CARGO_PKG_VERSION\")),\n            #[cfg(target_os = \"windows\")]\n            logs: format!(\"Pineflash v{} Windows\\n\", env!(\"CARGO_PKG_VERSION\")),\n            #[cfg(target_os = \"macos\")]\n            logs: format!(\"Pineflash v{} MacOs\\n\", env!(\"CARGO_PKG_VERSION\")),\n            json: \"\".to_string(),\n            iron_connected: None,\n            check_count: 0,\n            flash: false,\n            flash_notified_count: 0,\n            v2_serial_path: None,\n            // connection_guide_image: [\n                //\n                // RetainedImage::from_svg_bytes(\"Step1\", include_bytes!(\"../assets/Step1.svg\"))\n                //     .unwrap(),\n                // RetainedImage::from_svg_bytes(\"Step2\", include_bytes!(\"../assets/Step2.svg\"))\n                //     .unwrap(),\n                // RetainedImage::from_svg_bytes(\"Step3\", include_bytes!(\"../assets/Step3.svg\"))\n                //     .unwrap(),\n            // ],\n            current_step: 0,\n            metadata_path: [std::env::temp_dir(), \"metadata.json\".into()]\n                .iter()\n                .collect(),\n            open_file_dialog: None,\n        }\n    }\n}\n\nimpl Flasher {\n    fn new(cc: &CreationContext) -> Flasher {\n        let config: FlasherConfig = FlasherConfig::default();\n        // Flasher::configure_fonts(&cc.egui_ctx);\n        let saved_config: FlashSavedConfig = confy::load(\"PineFlash\", None).unwrap_or_default();\n        let toasts = Toasts::default()\n            .with_anchor(Anchor::TopRight)\n            .with_margin(emath::vec2(0.0, 120.0));\n\n        let mut style: egui::Style = (*cc.egui_ctx.style()).clone();\n        style.spacing.item_spacing = egui::vec2(5.0, 10.0);\n\n        cc.egui_ctx.set_style(style);\n        let new_style = egui::style::WidgetVisuals {\n            bg_fill: Color32::from_rgb(17, 17, 17),\n            weak_bg_fill: Color32::from_rgb(17, 17, 17),\n\n            rounding: Rounding {\n                nw: 4.,\n                ne: 4.,\n                sw: 4.,\n                se: 4.,\n            },\n\n            bg_stroke: Stroke {\n                width: 1.,\n                color: Color32::from_rgb(140, 140, 140),\n            },\n            fg_stroke: Stroke {\n                width: 1.,\n                color: Color32::from_rgb(140, 140, 140),\n            },\n\n            expansion: 2.,\n        };\n        let new_hovered_style = egui::style::WidgetVisuals {\n            bg_fill: Color32::from_rgb(17, 17, 17),\n            weak_bg_fill: Color32::from_rgb(17, 17, 17),\n\n            rounding: Rounding {\n                nw: 4.,\n                ne: 4.,\n                sw: 4.,\n                se: 4.,\n            },\n\n            bg_stroke: Stroke {\n                width: 1.5,\n                color: egui::Color32::from_rgb(56, 55, 55),\n            },\n            fg_stroke: Stroke {\n                width: 1.,\n                color: Color32::from_rgb(140, 140, 140),\n            },\n\n            expansion: 2.,\n        };\n        cc.egui_ctx.set_visuals(egui::style::Visuals {\n            widgets: egui::style::Widgets {\n                active: new_style,\n                inactive: new_style,\n                hovered: new_hovered_style,\n                noninteractive: new_style,\n                open: new_hovered_style,\n            },\n            ..Default::default()\n        });\n\n        if !saved_config.dark_mode {\n            cc.egui_ctx.set_visuals(egui::Visuals::light());\n        }\n\n        Flasher::configure_fonts(cc.egui_ctx.clone());\n        Flasher {\n            config,\n            toasts,\n            saved_config,\n        }\n    }\n}\n\nimpl eframe::App for Flasher {\n    fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {\n        // always repaint to have accurate pinecil detection\n        ctx.request_repaint();\n        ctx.set_pixels_per_point(1.8);\n\n        if self.config.check_count < 180 {\n            self.config.check_count += 1\n        } else {\n            self.config.iron_connected = Flasher::check_connections(self);\n            self.config.check_count = 0\n        }\n\n        if self.config.download_versions {\n            let _ = std::fs::remove_file(self.config.metadata_path.clone());\n            self.config.download_versions = !self.config.download_versions;\n            self.toasts\n                .info(\"Fetching versions\")\n                .set_duration(None)\n                .set_closable(false);\n\n            std::thread::spawn(|| {\n                let mut data = Vec::new();\n                let mut handle = Easy::new();\n                let mut internet = true;\n                handle\n                    .url(\"https://api.github.com/repos/Ralim/IronOS/releases\")\n                    .unwrap();\n                handle.useragent(\"PineFlash\").unwrap();\n                {\n                    let mut transfer = handle.transfer();\n                    transfer\n                        .write_function(|new_data| {\n                            data.extend_from_slice(new_data);\n                            Ok(new_data.len())\n                        })\n                        .unwrap();\n                    if transfer.perform().is_err() {\n                        internet = false;\n                    }\n                }\n                let path: PathBuf = [std::env::temp_dir(), \"metadata.json\".into()]\n                    .iter()\n                    .collect();\n\n                if internet {\n                    let json = String::from_utf8(data).unwrap();\n                    std::fs::write(path, json).unwrap();\n                } else {\n                    std::fs::write(path, \"No Internet\").unwrap();\n                }\n            });\n        } else if self.config.metadata_path.exists()\n            && !self.config.versions_checked\n            && !String::from_utf8(std::fs::read(self.config.metadata_path.clone()).unwrap())\n                .unwrap()\n                .contains(\"No Internet\")\n        {\n            self.toasts.dismiss_all_toasts();\n            self.toasts\n                .info(\"Versions Found\")\n                .set_duration(Some(Duration::from_secs(5)))\n                .set_closable(false);\n            self.config\n                .logs\n                .push_str(\"PineFlash: Versions successfully fetched.\\n\");\n            self.config.json_checked = !self.config.json_checked;\n            let string =\n                String::from_utf8(std::fs::read(self.config.metadata_path.clone()).unwrap())\n                    .unwrap();\n            let json: Result<Value, serde_json::Error> = serde_json::from_str(string.as_str());\n            if json.is_err() {\n                self.toasts.dismiss_all_toasts();\n                self.toasts\n                    .error(\"Could not access github, Online Files Will be Unavailable\")\n                    .set_duration(Some(Duration::from_secs(5)))\n                    .set_closable(false);\n                self.config\n                    .logs\n                    .push_str(\"PineFlash: Invalid json downloaded, could not fetch versions.\\n\");\n                self.config.versions_checked = true;\n            } else {\n                for i in 0..3 {\n                    let version = json.as_ref().unwrap()[i][\"tag_name\"].as_str().unwrap();\n                    self.config.vers.push(version.to_string());\n                }\n                self.config.versions_checked = true;\n                self.config.download_metadata = true;\n            }\n        } else if !self.config.versions_checked\n            && self.config.metadata_path.exists()\n            && String::from_utf8(std::fs::read(self.config.metadata_path.clone()).unwrap())\n                .unwrap()\n                .contains(\"No Internet\")\n        {\n            self.toasts.dismiss_all_toasts();\n            self.toasts\n                .error(\"No Internet, Online Files Will be Unavailable\")\n                .set_duration(Some(Duration::from_secs(5)))\n                .set_closable(false);\n            self.config\n                .logs\n                .push_str(\"PineFlash: No internet, could not fetch versions.\\n\");\n            self.config.versions_checked = true;\n        }\n\n        Flasher::render_header(self, ctx);\n        Flasher::render_main_windows(self, ctx);\n\n        if self.config.download {\n            let path: PathBuf = [\n                std::env::temp_dir(),\n                format!(\"{}-{}.zip\", self.config.version, self.config.int_name).into(),\n            ]\n            .iter()\n            .collect();\n            let pathlock: PathBuf = [std::env::temp_dir(), \"firmware.lock\".into()]\n                .iter()\n                .collect();\n            if self.config.download_firm_notify {\n                let _ = std::fs::write(pathlock, \"Locked\");\n                self.toasts\n                    .info(\"Downloading\")\n                    .set_duration(None)\n                    .set_closable(false);\n                self.config.logs.push_str(\n                    format!(\n                        \"PineFlash: Downloading Firmware {} {}.\\n\",\n                        self.config.int_name, self.config.version\n                    )\n                    .as_str(),\n                );\n                let _ = std::fs::remove_file(path);\n                let version = self.config.version.clone();\n                let int_name = self.config.int_name.clone();\n                std::thread::spawn(move || {\n                    let mut data = Vec::new();\n                    let mut handle = Easy::new();\n                    let path: PathBuf = [\n                        std::env::temp_dir(),\n                        format!(\"{}-{}.zip\", version, int_name).into(),\n                    ]\n                    .iter()\n                    .collect();\n                    let pathlock: PathBuf = [std::env::temp_dir(), \"firmware.lock\".into()]\n                        .iter()\n                        .collect();\n                    let _ = handle.url(\n                        format!(\n                            \"https://github.com/Ralim/IronOS/releases/download/{}/{}.zip\",\n                            version, int_name\n                        )\n                        .as_str(),\n                    );\n                    let _ = handle.follow_location(true);\n                    handle.useragent(\"PineFlash\").unwrap();\n                    {\n                        let mut transfer = handle.transfer();\n                        transfer\n                            .write_function(|new_data| {\n                                data.extend_from_slice(new_data);\n                                Ok(new_data.len())\n                            })\n                            .unwrap();\n                        transfer.perform().unwrap()\n                    }\n                    let zip = data.as_slice();\n                    if std::fs::write(path, zip).is_err() {\n                        println!(\"Could not write zip file\")\n                    };\n                    if std::fs::remove_file(pathlock).is_err() {\n                        println!(\"Could not remove lockfile\")\n                    };\n                });\n                self.config.download_firm_notify = false\n            } else if !self.config.download_firm_notify && !pathlock.exists() {\n                self.toasts.dismiss_all_toasts();\n                self.config.logs.push_str(\"PineFlash: Download Complete.\\n\");\n                self.toasts\n                    .info(\"Flashing.\")\n                    .set_duration(None)\n                    .set_closable(false);\n                self.config.download = false;\n                self.config.flash = true;\n            }\n        }\n        if !self.config.version.contains(\"Select\")\n            && !self.config.version.contains(\"Custom\")\n            && self.config.download_metadata\n        {\n            let path: PathBuf = [std::env::temp_dir(), \"langs.zip\".into()].iter().collect();\n            let pathlock: PathBuf = [std::env::temp_dir(), \"langs.lock\".into()].iter().collect();\n            if self.config.run_once_vers {\n                self.toasts\n                    .info(\"Downloading Language information.\")\n                    .set_duration(None)\n                    .set_closable(false);\n                let _ = std::fs::remove_file(path);\n                let _ = std::fs::write(pathlock, \"Locked\");\n                let version = self.config.version.clone();\n                std::thread::spawn(move || {\n                    let mut data = Vec::new();\n                    let mut handle = Easy::new();\n                    let path: PathBuf = [std::env::temp_dir(), \"langs.zip\".into()].iter().collect();\n                    let pathlock: PathBuf =\n                        [std::env::temp_dir(), \"langs.lock\".into()].iter().collect();\n                    let _ = std::fs::remove_file(path.clone());\n\n                    let _ = handle.follow_location(true);\n                    handle\n                        .url(\n                            format!(\n                                \"https://github.com/Ralim/IronOS/releases/download/{}/metadata.zip\",\n                                version\n                            )\n                            .as_str(),\n                        )\n                        .unwrap();\n                    handle.useragent(\"PineFlash\").unwrap();\n                    {\n                        let mut transfer = handle.transfer();\n                        transfer\n                            .write_function(|new_data| {\n                                data.extend_from_slice(new_data);\n                                Ok(new_data.len())\n                            })\n                            .unwrap();\n                        transfer.perform().unwrap();\n                    }\n                    let zip = data.as_slice();\n                    if std::fs::write(path, zip).is_err() {\n                        println!(\"Could not write zip file\")\n                    };\n                    if std::fs::remove_file(pathlock).is_err() {\n                        println!(\"Could not remove lockfile\")\n                    };\n                });\n                self.config.run_once_vers = false;\n            } else if !pathlock.exists() {\n                let path: PathBuf = [std::env::temp_dir(), \"langs.zip\".into()].iter().collect();\n                self.toasts.dismiss_all_toasts();\n                self.config\n                    .logs\n                    .push_str(\"PineFlash: Download of Language Info Complete.\\n\");\n                self.toasts\n                    .info(\"Languages Downloaded.\")\n                    .set_duration(Some(Duration::from_secs(3)))\n                    .set_closable(false);\n                let mut file = File::open(path).unwrap();\n                let mut data = Vec::new();\n                file.read_to_end(&mut data).unwrap();\n                let target_dir: PathBuf =\n                    [std::env::temp_dir(), \"metadata\".into()].iter().collect();\n                zip_extract::extract(Cursor::new(data), &target_dir, false).unwrap();\n                let json_path: PathBuf = [\n                    std::env::temp_dir(),\n                    \"metadata\".into(),\n                    format!(\"{}.json\", self.config.int_name).into(),\n                ]\n                .iter()\n                .collect();\n                self.config.json = fs::read_to_string(json_path).unwrap();\n                self.config.download_metadata = false;\n\n                let value: YourValue = serde_json::from_str(self.config.json.as_str()).unwrap();\n                for i in value.contents {\n                    if !i.0.contains(\".hex\") {\n                        let a = i.1;\n                        self.config.fancy_names.push(a.language_name);\n                        self.config.code_names.push(a.language_code);\n                    }\n                }\n                self.config.download_metadata = false;\n            }\n        }\n\n        if self.config.flash {\n            if self.config.flash_notified_count < 60 {\n                self.config.flash_notified_count += 1\n            } else {\n                Flasher::flash(self);\n            }\n        }\n    }\n}\n\nfn main() {\n    let options = eframe::NativeOptions::default();\n    // let options = eframe::NativeOptions {\n    //     decorated: true,\n    //     follow_system_theme: true,\n    //     icon_data: Some(eframe::IconData {\n    //         rgba: (ICON.to_vec()),\n    //         width: (32),\n    //         height: (32),\n    //     }),\n    //     resizable: true,\n    //     initial_window_size: Some(emath::Vec2 { x: 780., y: 680. }),\n    //     min_window_size: Some(emath::Vec2 { x: 780., y: 280. }),\n    //     ..Default::default()\n    // };\n    match eframe::run_native(\n        \"PineFlash\",\n        options,\n        // Box::new(|cc| Box::new(egui_extras::install_image_loaders(&cc.egui_ctx) Flasher::new(cc))),\n        //\n        Box::new(|cc| {\n            // This gives us image support:\n            egui_extras::install_image_loaders(&cc.egui_ctx);\n            Box::new(Flasher::new(cc))\n        }),\n        ) {\n        Ok(_) => (),\n        Err(error) => panic!(\"A massive error occured, not sure whats goin on here: \\n {}\", error),\n    }\n}\n"
  },
  {
    "path": "src/submodules/connection_poller.rs",
    "content": "use crate::Flasher;\nuse serialport::SerialPortType::UsbPort;\n\nimpl Flasher {\n    pub fn check_connections(&mut self) -> Option<String> {\n        let ports = serialport::available_ports().expect(\"No ports found!\");\n        let mut type_of_pinecil: Option<String> = None;\n        let mut v1: bool = false;\n        let mut v2: bool = false;\n\n        for device in rusb::devices().unwrap().iter() {\n            let device_info = device.device_descriptor().unwrap();\n\n            if device_info.vendor_id() == 10473 && device_info.product_id() == 393 {\n                // pinecil v1 connected\n                v1 = true;\n                type_of_pinecil = Some(\"Pinecil\".to_string())\n            }\n        }\n\n        for device in ports {\n            if let UsbPort(info) = device.port_type {\n                if let Some(serial_number) = info.serial_number {\n                    if serial_number.contains(\"000000020000\") {\n                        // pinecil v2 connected\n                        v2 = true;\n                        self.config.v2_serial_path = Some(device.port_name.clone());\n                        type_of_pinecil = Some(\"Pinecilv2\".to_string())\n                    }\n                }\n            }\n        }\n        if v1 && v2 && self.config.iron_connected.as_ref() != Some(&\"Both\".to_string()) {\n            type_of_pinecil = Some(\"Both\".to_string());\n            self.config.logs.push_str(\"Both v1 and v2 are detected\")\n        } else if self.config.iron_connected.is_none() && type_of_pinecil.is_some() {\n            self.config.logs.push_str(\n                format!(\"Pineflash: {} detected\\n\", type_of_pinecil.clone().unwrap()).as_str(),\n            );\n            if v2 {\n                self.config.logs.push_str(\n                    format!(\n                        \"PineFlash: Serial port is {} \\n\",\n                        self.config.v2_serial_path.clone().unwrap()\n                    )\n                    .as_str(),\n                );\n            }\n        } else if self.config.iron_connected.is_some() && type_of_pinecil.is_none() {\n            self.config.logs.push_str(\n                format!(\n                    \"Pineflash: {} disconnected\\n\",\n                    self.config.iron_connected.clone().unwrap()\n                )\n                .as_str(),\n            );\n        }\n        type_of_pinecil\n    }\n}\n"
  },
  {
    "path": "src/submodules/flash.rs",
    "content": "use std::fs::File;\nuse std::io::{Cursor, Read};\n#[cfg(target_os = \"windows\")]\nuse std::os::windows::process::CommandExt;\nuse std::path::PathBuf;\nuse std::process::Command;\nuse std::time::Duration;\n\nuse crate::Flasher;\n\nimpl Flasher {\n    pub fn flash(&mut self) {\n        let mut firmware_path = \"\".to_string();\n        if self.config.version != *\"Custom\" {\n            let path: PathBuf = [\n                std::env::temp_dir(),\n                format!(\"{}-{}.zip\", self.config.version, self.config.int_name)\n                    .as_str()\n                    .into(),\n            ]\n            .iter()\n            .collect();\n\n            let target: PathBuf = [\n                std::env::temp_dir(),\n                format!(\"{}-{}\", self.config.version, self.config.int_name)\n                    .as_str()\n                    .into(),\n            ]\n            .iter()\n            .collect();\n\n            let mut file = File::open(path).unwrap();\n            let mut data = Vec::new();\n            file.read_to_end(&mut data).unwrap();\n\n            zip_extract::extract(Cursor::new(data), &target, true).unwrap();\n            self.config\n                .logs\n                .push_str(\"PineFlash: File extracted successfully\\n\");\n\n            if cfg!(unix) && self.config.int_name == \"Pinecil\" {\n                firmware_path = format!(\n                    \"{}/{}_{}.dfu\",\n                    target.as_os_str().to_str().unwrap(),\n                    self.config.int_name,\n                    self.config.lang\n                );\n            } else if cfg!(unix) && self.config.int_name == \"Pinecilv2\" {\n                firmware_path = format!(\n                    \"{}/{}_{}.bin\",\n                    target.as_os_str().to_str().unwrap(),\n                    self.config.int_name,\n                    self.config.lang\n                );\n            } else if cfg!(windows) && self.config.int_name == \"Pinecil\" {\n                // Do windows functionality here.\n                firmware_path = format!(\n                    \"{}\\\\{}_{}.dfu\",\n                    target.as_os_str().to_str().unwrap(),\n                    self.config.int_name,\n                    self.config.lang\n                );\n            } else if cfg!(windows) && self.config.int_name == \"Pinecilv2\" {\n                firmware_path = format!(\n                    \"{}\\\\{}_{}.bin\",\n                    target.as_os_str().to_str().unwrap(),\n                    self.config.int_name,\n                    self.config.lang\n                );\n            }\n        } else if self.config.picked_path.is_some() {\n            firmware_path = self.config.picked_path.as_ref().unwrap().to_string();\n        } else {\n            self.toasts\n                .error(\"Please select a file or firmware version\");\n        }\n\n        self.config.logs.push_str(\n            format!(\n                \"PineFlash: Attempting to flash {} with the firmware {}\\n\",\n                self.config.int_name, firmware_path\n            )\n            .as_str(),\n        );\n        if self.config.int_name == \"Pinecil\" {\n            #[cfg(feature = \"appimage\")]\n            let path = format!(\n                \"{}/appimage/dfu-util\",\n                std::env::current_dir().unwrap().to_str().unwrap()\n            );\n            #[cfg(feature = \"appimage\")]\n            let tool_path: PathBuf = [std::env::temp_dir(), \"flash_tools\".into()]\n                .iter()\n                .collect();\n\n            #[cfg(feature = \"appimage\")]\n            let _ = std::fs::create_dir(tool_path.clone());\n            #[cfg(feature = \"appimage\")]\n            let dfupath: PathBuf = [tool_path, \"dfu-util\".into()].iter().collect();\n            #[cfg(feature = \"appimage\")]\n            std::fs::copy(path, dfupath.clone()).unwrap();\n            // println!(\"{}\", path);\n\n            #[cfg(not(feature = \"appimage\"))]\n            #[cfg(target_os = \"linux\")]\n            let dfupath = \"dfu-util\";\n\n            #[cfg(target_os = \"linux\")]\n            let command = Command::new(\"pkexec\")\n                // .env(\"PATH\", path)\n                .arg(dfupath)\n                .arg(\"-D\")\n                .arg(firmware_path)\n                .arg(\"-a 0\") \n                .output()\n                .expect(\"Could not flash soldering iron\");\n\n            #[cfg(target_os = \"macos\")]\n            let command = Command::new(\"dfu-util\")\n                .arg(\"-D\")\n                .arg(firmware_path)\n                .arg(\"-a 0\")\n                .output()\n                .expect(\"Could not flash soldering iron\");\n\n            #[cfg(target_os = \"windows\")]\n            let command: PathBuf = [\n                std::env::current_dir().unwrap(),\n                \"tools\".into(),\n                \"dfu-util.exe\".into(),\n            ]\n            .iter()\n            .collect();\n            #[cfg(target_os = \"windows\")]\n            let command = Command::new(command)\n                .creation_flags(0x00000008)\n                .arg(\"-D\")\n                .arg(firmware_path)\n                .arg(\"-a 0\")\n                .output()\n                .expect(\"Could not flash soldering iron\");\n\n            let output: String = String::from_utf8(command.stdout).unwrap();\n            let output_err: String = String::from_utf8(command.stderr).unwrap();\n            self.toasts.dismiss_all_toasts();\n            if command.status.success() {\n                self.toasts\n                    .info(\"Flashing completed\")\n                    .set_duration(Some(Duration::from_secs(5)))\n                    .set_closable(false);\n            } else {\n                self.toasts\n                    .error(\"Flashing failed, is your pinecil plugged in?\")\n                    .set_duration(Some(Duration::from_secs(5)))\n                    .set_closable(false);\n            }\n            self.config\n                .logs\n                .push_str(format!(\"Dfu-Util: {}{}\\n\", output, output_err).as_str());\n        } else if self.config.int_name == \"Pinecilv2\" {\n            #[cfg(feature = \"appimage\")]\n            let path = format!(\n                \"{}/linux/blisp\",\n                std::env::current_dir().unwrap().to_str().unwrap()\n            );\n            #[cfg(feature = \"appimage\")]\n            let tool_path: PathBuf = [std::env::temp_dir(), \"flash_tools\".into()]\n                .iter()\n                .collect();\n\n            #[cfg(feature = \"appimage\")]\n            let _ = std::fs::create_dir(tool_path.clone());\n            #[cfg(feature = \"appimage\")]\n            let blisppath: PathBuf = [tool_path, \"blisp\".into()].iter().collect();\n            #[cfg(feature = \"appimage\")]\n            std::fs::copy(path, blisppath.clone()).unwrap();\n            // println!(\"{}\", path);\n\n            #[cfg(not(feature = \"appimage\"))]\n            #[cfg(target_os = \"linux\")]\n            let blisppath = \"blisp\";\n\n            #[cfg(target_os = \"linux\")]\n            let command = Command::new(\"pkexec\")\n                .env(\"PATH\", \"/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:linux\")\n                .arg(blisppath)\n                .arg(\"write\")\n                .arg(\"-c\")\n                .arg(\"bl70x\")\n                .arg(\"-p\")\n                .arg(self.config.v2_serial_path.clone().unwrap())\n                .arg(\"--reset\")\n                .arg(firmware_path)\n                .output()\n                .expect(\"Could not flash soldering iron\");\n\n            #[cfg(target_os = \"macos\")]\n            let command = Command::new(\"blisp\")\n                .arg(\"write\")\n                .arg(\"-c\")\n                .arg(\"bl70x\")\n                .arg(\"--reset\")\n                .arg(firmware_path)\n                .output()\n                .expect(\"Could not flash soldering iron\");\n\n            #[cfg(target_family = \"windows\")]\n            let command: PathBuf = [\n                std::env::current_dir().unwrap(),\n                \"tools\".into(),\n                \"blisp.exe\".into(),\n            ]\n            .iter()\n            .collect();\n\n            #[cfg(target_os = \"windows\")]\n            let command = Command::new(command)\n                .creation_flags(0x00000008)\n                .arg(\"write\")\n                .arg(\"-c\")\n                .arg(\"bl70x\")\n                .arg(\"-p\")\n                .arg(self.config.v2_serial_path.clone().unwrap())\n                .arg(\"--reset\")\n                .arg(firmware_path)\n                .output()\n                .expect(\"Could not flash soldering iron\");\n\n            let output: String = String::from_utf8(command.stdout).unwrap();\n            let output_err: String = String::from_utf8(command.stderr).unwrap();\n            self.toasts.dismiss_all_toasts();\n            if !output_err.as_str().contains(\"Device not found\")\n                && !output_err.as_str().contains(\"Failed\")\n                && !output_err.as_str().contains(\"Error\")\n            {\n                self.toasts\n                    .info(\"Flashing completed\")\n                    .set_duration(Some(Duration::from_secs(4)))\n                    .set_closable(false);\n            } else if output_err.as_str().contains(\"Not authorized\") {\n                self.toasts\n                    .error(\"Could not run command as admin,\\n was your password wrong?\")\n                    .set_duration(Some(Duration::from_secs(4)))\n                    .set_closable(false);\n            } else {\n                self.toasts\n                    .error(\"Flashing failed,\\n your device may have been disconnected.\")\n                    .set_duration(Some(Duration::from_secs(4)))\n                    .set_closable(false);\n            }\n            self.config\n                .logs\n                .push_str(format!(\"Blisp: {}{}\\n\", output, output_err).as_str());\n        }\n        // Very ugly way of reseting the program\n        self.config.version = \"Select\".to_string();\n        self.config.fancy_names = vec![];\n        self.config.code_names = vec![];\n        // self.config.versions_checked = false;\n        // self.config.vers = vec![];\n        // self.config.promise = None;\n        // self.config.promise_2 = None;\n        // self.config.promise_3 = None;\n        // self.config.download_metadata = false;\n        self.config.download = false;\n        self.config.run_once_vers = true;\n        self.config.download_firm_notify = true;\n        self.config.picked_path = None;\n        self.config.ready_to_flash = false;\n        self.config.flash_notified_count = 0;\n        self.config.flash = false;\n    }\n}\n"
  },
  {
    "path": "src/submodules/fonts.rs",
    "content": "use eframe::{\n    egui::{self, FontData, FontDefinitions},\n    epaint::FontFamily,\n};\n\nuse crate::Flasher;\n\nimpl Flasher {\n    pub fn configure_fonts(cc: egui::Context) {\n        let mut fonts = FontDefinitions::default();\n        fonts.font_data.insert(\n            \"Chinese\".to_owned(),\n            FontData::from_static(include_bytes!(\"../../assets/XiaolaiSC-Regular.ttf\")),\n        );\n        fonts\n            .families\n            .get_mut(&FontFamily::Proportional)\n            .unwrap()\n            .push(\"Chinese\".to_owned());\n\n        // Use meslolgs for icons\n        fonts.font_data.insert(\n            \"MesloLGS\".to_owned(),\n            FontData::from_static(include_bytes!(\"../../assets/SFMono_Nerd_Font_Complete.otf\")),\n        );\n        fonts\n            .families\n            .get_mut(&FontFamily::Proportional)\n            .unwrap()\n            .push(\"MesloLGS\".to_owned());\n\n        cc.set_fonts(fonts);\n    }\n}\n"
  },
  {
    "path": "src/submodules/main_panel.rs",
    "content": "use std::time::Duration;\n#[cfg(target_family = \"windows\")]\nuse std::os::windows::process::CommandExt;\n\n#[cfg(target_family = \"windows\")]\nuse std::path::PathBuf;\n#[cfg(feature = \"appimage\")]\nuse std::path::PathBuf;\n\nuse eframe::{\n    egui::{self, Button, CentralPanel, Image, RichText, ScrollArea},\n    epaint::{Color32, Rounding},\n};\nuse egui::emath;\nuse egui::Context;\nuse egui::Vec2;\nuse egui_file::FileDialog;\nuse simple_home_dir::home_dir;\nuse std::process::Command;\nuse version_compare::Version;\n\nuse crate::Flasher;\nimpl Flasher {\n    pub fn render_main_windows(&mut self, ctx: &Context) {\n        CentralPanel::default().show(ctx, |ui|{\n            ui.horizontal(|ui| {\n                if self.config.iron_connected.is_none() {\n                    ui.colored_label(Color32::RED, RichText::new(\"\").heading());\n                    ui.label(\"Soldering Iron Disconnected\");\n                } else {\n                    ui.colored_label(Color32::GREEN, RichText::new(\"\").heading());\n                    ui.label(\"Soldering Iron Connected\");\n                }\n            });\n            ui.horizontal(|ui| {\n                // Disable strokes except for hovered\n                ui.visuals_mut().widgets.active.bg_stroke = eframe::epaint::Stroke{ width: 0., color: egui::Color32::RED};\n                ui.visuals_mut().widgets.inactive.bg_stroke = eframe::epaint::Stroke{ width: 0., color: egui::Color32::RED};\n\n                let width = ui.available_width();\n                ui.vertical(|ui| {\n                    ui.label(\"Select your Soldering Iron\");\n                        egui::ComboBox::from_label(\" \")\n                            .selected_text(self.config.iron.to_string())\n                            .show_ui(ui, |ui| {\n                                ui.selectable_value(&mut self.config.iron, \"Pinecil V1\".to_string(), \"Pinecil V1\");\n                                ui.selectable_value(&mut self.config.iron, \"Pinecil V2\".to_string(), \"Pinecil V2\");\n                            }\n                        );\n                        if self.config.iron == \"Pinecil V1\"  {\n                            self.config.int_name = \"Pinecil\".to_string();\n                        } else if self.config.iron == \"Pinecil V2\" {\n                            self.config.int_name = \"Pinecilv2\".to_string();\n                        }\n                });\n                let width_now = ui.available_width();\n\n                ui.add_space(width / 2. - ((width - width_now) * 1.3));\n\n                ui.vertical(|ui| {\n                    ui.label(\"Specify Version\");\n                        ui.horizontal(|ui| {\n\n                            ui.add_enabled_ui(self.config.versions_checked, |ui|{\n                                egui::ComboBox::from_label(\"\")\n                                    .selected_text(self.config.version.to_string())\n                                    .show_ui(ui, |ui| {\n                                        if self.config.versions_checked {\n                                            for i in &self.config.vers {\n                                                ui.selectable_value(&mut self.config.version, i.clone(), i);\n                                            }\n                                            ui.selectable_value(&mut self.config.version, \"Custom\".to_string(), \"Custom\");\n                                        }\n\n                                    }\n                                );\n                            });\n                            if ui.button(RichText::new(\" \").size(15.)).clicked() {\n                                let mut dialog = FileDialog::open_file(home_dir()).default_size(emath::Vec2 {x:264., y: 262.});\n                                dialog.open();\n                                self.config.open_file_dialog = Some(dialog);\n                            }\n                            if let Some(dialog) = &mut self.config.open_file_dialog {\n                                if dialog.show(ctx).selected() {\n                                    if let Some(file) = dialog.path() {\n                                        if self.config.int_name == \"Pinecilv2\" {\n\n                                            #[cfg(feature = \"appimage\")]\n                                            let path = format!(\n                                                \"{}/linux/blisp\",\n                                                std::env::current_dir().unwrap().to_str().unwrap()\n                                            );\n                                            #[cfg(feature = \"appimage\")]\n                                            let tool_path: PathBuf = [std::env::temp_dir(), \"flash_tools\".into()]\n                                                .iter()\n                                                .collect();\n\n                                            #[cfg(feature = \"appimage\")]\n                                            let _ = std::fs::create_dir(tool_path.clone());\n                                            #[cfg(feature = \"appimage\")]\n                                            let blisppath: PathBuf = [tool_path, \"blisp\".into()].iter().collect();\n                                            #[cfg(feature = \"appimage\")]\n                                            std::fs::copy(path, blisppath.clone()).unwrap();\n                                            #[cfg(not(feature = \"appimage\"))]\n                                            #[cfg(target_family = \"unix\")]\n                                            let blisppath = \"blisp\";\n\n                                            #[cfg(target_family = \"unix\")]\n                                            let blisp_version = String::from_utf8( Command::new(blisppath)\n                                                .env(\"PATH\", \"/usr/local/sbin:/usr/local/bin:/usr/bin:/bin\")\n                                                .arg(\"--version\").output().expect(\"Could not find blisp\").stdout).unwrap();\n                       \n                                            #[cfg(target_family = \"windows\")]\n                                            let blisp: PathBuf = [\n                                                std::env::current_dir().unwrap(),\n                                                \"tools\".into(),\n                                                \"blisp.exe\".into(),\n                                            ]\n                                            .iter()\n                                            .collect();\n\n                                            #[cfg(target_os = \"windows\")]\n                                            let blisp_version = String::from_utf8( Command::new(blisp)\n                                                .creation_flags(0x00000008)\n                                                .arg(\"--version\").output().expect(\"Could not find blisp\").stdout).unwrap();\n\n                                            let ver = blisp_version.split(\"\\n\");\n                                            for i in ver {\n                                                if i.contains(\"v\") {\n                                                    for ii in i.split(\"v\") {\n                                                        if ii.contains(\".\") {\n                                                            self.config.blisp_version = ii.to_string();\n                                                        }\n                                                    }\n                                                }\n                                            }\n                                        }\n                                        if !file.display().to_string().contains(\"dfu\") && self.config.int_name == \"Pinecil\"  || self.config.int_name == \"Pinecilv2\" && !file.display().to_string().contains(\"bin\") && !file.display().to_string().contains(\"dfu\") {\n                                            self.toasts.dismiss_all_toasts();\n                                            self.toasts.error(\"File has the incorrect format\").set_duration(Some(Duration::from_secs(4))).set_closable(false);\n                                            self.config.logs.push_str(\"PineFlash: Incorrect filetype selected.\\n\");\n                                            self.config.picked_path = None;\n                                            \n                                        } else if Version::from(self.config.blisp_version.as_str()).unwrap() < Version::from(\"0.0.4\").unwrap() && file.display().to_string().contains(\"dfu\") && self.config.int_name == \"Pinecilv2\" {\n                                            self.toasts.dismiss_all_toasts();\n                                            self.toasts.error(\"Your version of blisp can not flash boot image files.\\nPlease update it to version 0.0.4 or higher\").set_duration(Some(Duration::from_secs(4))).set_closable(false);\n                                            self.config.logs.push_str(format!(\"PineFlash: Old blisp version {}.\\n\", self.config.blisp_version).as_str());\n                                            self.config.picked_path = None;\n\n                                        }\n                                        else {\n                                            self.config.picked_path = Some(file.display().to_string());\n                                            self.config.version = \"Custom\".to_string();\n                                            self.toasts.dismiss_all_toasts();\n                                            self.toasts.info(\"Custom file selected\").set_duration(Some(Duration::from_secs(4))).set_closable(false);\n                                            self.config.logs.push_str(\"PineFlash: Custom file selected.\\n\");\n\n                                        }\n                                    }\n                                }\n                            }\n                    });\n                });\n                ui.add_space(ui.available_width() - ((width - width_now) / 1.2));\n                ui.vertical(|ui| {\n                    ui.label(\"Select Your Language.\");\n                    ui.add_enabled_ui({\n                        self.config.version != *\"Select\" && \n                        !self.config.download_metadata\n                    }, |ui|{\n                        ui.horizontal(|ui| {\n                            ui.add_space(10.);\n                            egui::ComboBox::from_label(\"  \")\n                                .selected_text(self.config.lang.to_string())\n                                .show_ui(ui, |ui| {\n                                    for i in 0..self.config.code_names.len() {\n                                        let code_name = &self.config.code_names[i];\n                                        let fancy_name = &self.config.fancy_names[i];\n                                        ui.selectable_value(&mut self.config.lang, code_name.to_string(), fancy_name);\n                                    }\n                                }\n                            );\n                        })\n                    });\n                })\n            });\n            if self.config.picked_path.is_some() &&\n                self.config.iron_connected.as_ref() == Some(&self.config.int_name) ||\n                self.config.iron_connected.as_ref() == Some(&\"Both\".to_string()) ||\n                self.config.version != *\"Custom\" &&\n                self.config.version != *\"Select\" &&\n                !self.config.download &&\n                self.config.iron_connected.as_ref() == Some(&self.config.int_name) ||\n                self.config.iron_connected.as_ref() == Some(&\"Both\".to_string())\n            {\n                self.config.ready_to_flash = true\n\n            } else {\n                self.config.ready_to_flash = false\n            }\n\n            ui.add_space(25.);\n            // Disable strokes except for hovered\n            ui.visuals_mut().widgets.active.bg_stroke = eframe::epaint::Stroke{ width: 0., color: egui::Color32::RED};\n            ui.visuals_mut().widgets.inactive.bg_stroke = eframe::epaint::Stroke{ width: 0., color: egui::Color32::RED};\n\n            if !self.config.ready_to_flash {\n\n                ui.add_enabled_ui(false, |ui| {\n                    ui.add_sized([80., 10.], egui::Button::new(\"Update!\")).on_disabled_hover_text(\n                        // Tell user why they can not flash\n                    if  self.config.iron_connected.as_ref() == Some(&self.config.int_name) ||\n                        self.config.iron_connected.as_ref() == Some(&\"Both\".to_string())\n                        { \"Select a firmware version or a custom file.\" } \n                    else if self.config.iron_connected.is_some() &&\n                        self.config.iron_connected.as_ref() != Some(&self.config.int_name) &&\n                        self.config.iron_connected.as_ref() != Some(&\"Both\".to_string())\n                        {\"The selected soldering iron does \\nnot match the one currently plugged in.\"}\n                    else if self.config.version != *\"Custom\" ||\n                        self.config.picked_path.is_some() &&\n                        self.config.version != *\"Select\"\n                        {\"Connect your soldering iron and \\nmake sure it is in flashing mode.\"} \n                    else\n                        {\"Please select a firmware version and\\nplug your soldering iron in whilst in flashing mode.\"}\n                    )\n                });\n            } else if ui.add_sized([80., 10.], Button::new(\"Update\")).clicked() {\n                if self.config.version != *\"Custom\" {\n                    self.config.download = true;\n                } else {\n                    self.toasts.dismiss_all_toasts();\n                    self.toasts.info(\"Flashing.\").set_duration(None).set_closable(false);\n                    self.config.flash = true;\n                }\n            };\n            ui.separator();\n\n            egui::CollapsingHeader::new(\"Connection Guide\")\n                .default_open(false)\n                .show_unindented(ui, |ui|\n            {\n                ui.horizontal(|ui|{\n                    ui.add_space(10.);\n                    egui::Frame::none()\n                        .fill(egui::Color32::from_rgb(17, 17, 17))\n                        .rounding(Rounding { nw: 4., ne: 4., sw: 4., se: 4. })\n                        .show(ui, |ui|\n                    {\n                        ui.vertical(|ui|{\n                            // ui.image(self.config.connection_guide_image[self.config.current_step].texture_id(ctx), Vec2 { x: ui.available_width() - 10., y: (ui.available_width() - 10.) / 3.4 });\n                            // let image = Image::from_uri(self.config.connection_guide_image[self.config.current_step]);\n                            if self.config.current_step == 0 {\n                                ui.add(Image::new(egui::include_image!(\"../../assets/Step1.svg\")).fit_to_exact_size(Vec2 { x: ui.available_width() - 10., y: (ui.available_width() - 10.) / 3.4 }));\n                            } else if self.config.current_step == 1 {\n                                ui.add(Image::new(egui::include_image!(\"../../assets/Step2.svg\")).fit_to_exact_size(Vec2 { x: ui.available_width() - 10., y: (ui.available_width() - 10.) / 3.4 }));\n\n                            } else if self.config.current_step == 2 {\n                                ui.add(Image::new(egui::include_image!(\"../../assets/Step3.svg\")).fit_to_exact_size(Vec2 { x: ui.available_width() - 10., y: (ui.available_width() - 10.) / 3.4 }));\n                            }\n                            \n                            ui.horizontal(|ui|{\n                                ui.with_layout(egui::Layout::left_to_right(eframe::emath::Align::TOP), |ui| {\n                                    ui.add_space(10.);\n                                    if self.config.current_step == 0 {\n                                        ui.add_enabled_ui(false, |ui|{\n                                            ui.add_sized([80., 10.], Button::new(\"Previous\").fill(egui::Color32::from_rgb(27, 27, 27)))\n                                        });\n                                    } else if ui.add_sized([80., 10.], Button::new(\"Previous\").fill(egui::Color32::from_rgb(27, 27, 27))).clicked() {\n                                            self.config.current_step -= 1;\n                                    }\n\n                                });\n                                ui.with_layout(egui::Layout::right_to_left(eframe::emath::Align::TOP), |ui| {\n                                    ui.add_space(10.);\n\n                                    if self.config.current_step == 2 {\n                                        ui.add_enabled_ui(false, |ui|{\n                                            ui.add_sized([80., 10.], Button::new(\"Next\").fill(egui::Color32::from_rgb(27, 27, 27)))\n                                        });\n                                    } else if ui.add_sized([80., 10.], Button::new(\"Next\").fill(egui::Color32::from_rgb(27, 27, 27))).clicked() {\n                                            self.config.current_step += 1;\n                                    }\n                                });\n                            });\n                            ui.add_space(5.);\n                        });\n                    });\n                });\n            });\n            egui::CollapsingHeader::new(\"Logs\")\n                .default_open(false)\n                .show_unindented(ui, |ui|\n            {\n                ui.horizontal(|ui|{\n                    ui.add_space(10.);\n                    egui::Frame::none()\n                        .fill(egui::Color32::from_rgb(17, 17, 17))\n                        .rounding(Rounding { nw: 4., ne: 4., sw: 4., se: 4. })\n                        .show(ui, |ui|\n                    {\n                        ui.vertical(|ui|{\n                        ui.add_space(10.);\n\n                            ui.horizontal(|ui|{\n                                ui.add_space(10.);\n                                if ui.add(Button::new(\"Copy Log\").fill(egui::Color32::from_rgb(27, 27, 27))).clicked() {\n                                    // ui.output().copied_text = self.config.logs.clone();\n                                    ui.output_mut(|i| i.copied_text = self.config.logs.clone());\n                                }\n                            });\n                            ScrollArea::vertical().auto_shrink([false, false]).stick_to_bottom(true).show(ui, |ui|{\n                                ui.horizontal(|ui|{\n                                    ui.add_space(10.);\n                                    ui.vertical(|ui|{\n                                        ui.monospace(self.config.logs.clone());\n                                    });\n                                    ui.add_space(5.);\n                                });\n                            });\n                        });\n                    });\n                })\n            });\n\n        self.toasts.show(ctx);\n        });\n    }\n}\n"
  },
  {
    "path": "src/submodules/mod.rs",
    "content": "mod connection_poller;\nmod flash;\nmod main_panel;\nmod top_panel;\n\n// mod download;\nmod fonts; // will be used for styling the final thing\n"
  },
  {
    "path": "src/submodules/top_panel.rs",
    "content": "use eframe::egui::{self, menu, Button, TopBottomPanel};\n\nuse crate::{FlashSavedConfig, Flasher};\n\nimpl Flasher {\n    pub fn render_header(&mut self, ctx: &egui::Context) {\n        TopBottomPanel::top(\"top_panel\").show(ctx, |ui| {\n            menu::bar(ui, |ui| {\n                ui.vertical_centered(|ui| ui.heading(\"PineFlash\"));\n                // ui.with_layout(Layout::align(), |ui| {\n                ui.with_layout(egui::Layout::right_to_left(egui::Align::TOP), |ui| {\n                    ui.add_space(5.);\n                    if ui\n                        .add(Button::new(if self.saved_config.dark_mode {\n                            \" \"\n                        } else {\n                            \" \"\n                        }))\n                        .clicked()\n                    {\n                        if self.saved_config.dark_mode {\n                            ctx.set_visuals(egui::Visuals::light());\n                        } else {\n                            ctx.set_visuals(egui::Visuals::dark());\n                        }\n                        self.saved_config.dark_mode = !self.saved_config.dark_mode;\n                        if let Err(..) = confy::store(\n                            \"PineFlash\",\n                            None,\n                            FlashSavedConfig {\n                                dark_mode: self.saved_config.dark_mode,\n                            },\n                        ) {\n                            self.config\n                                .logs\n                                .push_str(\"Pineflash: Failed to save the app state.\")\n                        }\n                    }\n                })\n                // })\n            });\n        });\n    }\n}\n"
  }
]