From 4a706c64a87630d6d326865f3058295f8a1d958b Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Mon, 20 Mar 2023 17:36:01 +0100
Subject: [PATCH 1/9] WIP FTP ui with React and Typescript

---
 .gitignore                                    |    3 +-
 index.js                                      |   14 +-
 ui/{arduino => editor}/about.css              |    0
 ui/{arduino => editor}/about.html             |    0
 ui/{arduino => editor}/app.js                 |    0
 ui/{arduino => editor}/assets/about_image.png |  Bin
 ui/{arduino => editor}/components/editor.js   |    0
 ui/{arduino => editor}/components/filename.js |    0
 ui/{arduino => editor}/components/icon.js     |    0
 ui/{arduino => editor}/components/message.js  |    0
 .../components/newfiledialog.js               |    0
 ui/{arduino => editor}/components/panel.js    |    0
 .../components/panel_files.js                 |    0
 .../components/panel_terminal.js              |    0
 .../components/portdialog.js                  |    0
 ui/{arduino => editor}/components/toolbar.js  |    0
 .../components/toolbar_button.js              |    0
 ui/{arduino => editor}/icons/Connect.svg      |    0
 ui/{arduino => editor}/icons/Connect@3x.png   |  Bin
 ui/{arduino => editor}/icons/Copy-Left.svg    |    0
 ui/{arduino => editor}/icons/Copy-Left@3x.png |  Bin
 ui/{arduino => editor}/icons/Copy-Right.svg   |    0
 .../icons/Copy-Right@3x.png                   |  Bin
 ui/{arduino => editor}/icons/Copy.png         |  Bin
 ui/{arduino => editor}/icons/Copy.svg         |    0
 ui/{arduino => editor}/icons/Delete.svg       |    0
 ui/{arduino => editor}/icons/Delete@3x.png    |  Bin
 ui/{arduino => editor}/icons/Disconnect.svg   |    0
 .../icons/Disconnect@3x.png                   |  Bin
 .../icons/File-Explorer.svg                   |    0
 .../icons/File-Explorer@3x.png                |  Bin
 ui/{arduino => editor}/icons/New.svg          |    0
 ui/{arduino => editor}/icons/New@3x.png       |  Bin
 ui/{arduino => editor}/icons/Open.svg         |    0
 ui/{arduino => editor}/icons/Open@3x.png      |  Bin
 ui/{arduino => editor}/icons/Output.svg       |    0
 ui/{arduino => editor}/icons/Output@3x.png    |  Bin
 ui/{arduino => editor}/icons/Paste.png        |  Bin
 ui/{arduino => editor}/icons/Paste.svg        |    0
 ui/{arduino => editor}/icons/Reboot.svg       |    0
 ui/{arduino => editor}/icons/Reboot@3x.png    |  Bin
 ui/{arduino => editor}/icons/Run.svg          |    0
 ui/{arduino => editor}/icons/Run@3x.png       |  Bin
 ui/{arduino => editor}/icons/Save.svg         |    0
 ui/{arduino => editor}/icons/Save@3x.png      |  Bin
 ui/{arduino => editor}/icons/Stop.svg         |    0
 ui/{arduino => editor}/icons/Stop@3x.png      |  Bin
 ui/{arduino => editor}/index.html             |    0
 ui/{arduino => editor}/libs/FileSaver.js      |    0
 ui/{arduino => editor}/libs/ace/ace.js        |    0
 .../libs/ace/ext-beautify.js                  |    0
 .../libs/ace/ext-code_lens.js                 |    0
 .../libs/ace/ext-elastic_tabstops_lite.js     |    0
 ui/{arduino => editor}/libs/ace/ext-emmet.js  |    0
 .../libs/ace/ext-error_marker.js              |    0
 .../libs/ace/ext-keybinding_menu.js           |    0
 .../libs/ace/ext-language_tools.js            |    0
 .../libs/ace/ext-linking.js                   |    0
 .../libs/ace/ext-modelist.js                  |    0
 .../libs/ace/ext-options.js                   |    0
 ui/{arduino => editor}/libs/ace/ext-prompt.js |    0
 ui/{arduino => editor}/libs/ace/ext-rtl.js    |    0
 .../libs/ace/ext-searchbox.js                 |    0
 .../libs/ace/ext-settings_menu.js             |    0
 .../libs/ace/ext-spellcheck.js                |    0
 ui/{arduino => editor}/libs/ace/ext-split.js  |    0
 .../libs/ace/ext-static_highlight.js          |    0
 .../libs/ace/ext-statusbar.js                 |    0
 .../libs/ace/ext-textarea.js                  |    0
 .../libs/ace/ext-themelist.js                 |    0
 .../libs/ace/ext-whitespace.js                |    0
 .../libs/ace/keybinding-emacs.js              |    0
 .../libs/ace/keybinding-sublime.js            |    0
 .../libs/ace/keybinding-vim.js                |    0
 .../libs/ace/keybinding-vscode.js             |    0
 ui/{arduino => editor}/libs/ace/mode-abap.js  |    0
 ui/{arduino => editor}/libs/ace/mode-abc.js   |    0
 .../libs/ace/mode-actionscript.js             |    0
 ui/{arduino => editor}/libs/ace/mode-ada.js   |    0
 ui/{arduino => editor}/libs/ace/mode-alda.js  |    0
 .../libs/ace/mode-apache_conf.js              |    0
 ui/{arduino => editor}/libs/ace/mode-apex.js  |    0
 .../libs/ace/mode-applescript.js              |    0
 ui/{arduino => editor}/libs/ace/mode-aql.js   |    0
 .../libs/ace/mode-asciidoc.js                 |    0
 ui/{arduino => editor}/libs/ace/mode-asl.js   |    0
 .../libs/ace/mode-assembly_x86.js             |    0
 .../libs/ace/mode-autohotkey.js               |    0
 .../libs/ace/mode-batchfile.js                |    0
 .../libs/ace/mode-c9search.js                 |    0
 ui/{arduino => editor}/libs/ace/mode-c_cpp.js |    0
 ui/{arduino => editor}/libs/ace/mode-cirru.js |    0
 .../libs/ace/mode-clojure.js                  |    0
 ui/{arduino => editor}/libs/ace/mode-cobol.js |    0
 .../libs/ace/mode-coffee.js                   |    0
 .../libs/ace/mode-coldfusion.js               |    0
 .../libs/ace/mode-crystal.js                  |    0
 .../libs/ace/mode-csharp.js                   |    0
 .../libs/ace/mode-csound_document.js          |    0
 .../libs/ace/mode-csound_orchestra.js         |    0
 .../libs/ace/mode-csound_score.js             |    0
 ui/{arduino => editor}/libs/ace/mode-csp.js   |    0
 ui/{arduino => editor}/libs/ace/mode-css.js   |    0
 ui/{arduino => editor}/libs/ace/mode-curly.js |    0
 ui/{arduino => editor}/libs/ace/mode-d.js     |    0
 ui/{arduino => editor}/libs/ace/mode-dart.js  |    0
 ui/{arduino => editor}/libs/ace/mode-diff.js  |    0
 .../libs/ace/mode-django.js                   |    0
 .../libs/ace/mode-dockerfile.js               |    0
 ui/{arduino => editor}/libs/ace/mode-dot.js   |    0
 .../libs/ace/mode-drools.js                   |    0
 .../libs/ace/mode-edifact.js                  |    0
 .../libs/ace/mode-eiffel.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-ejs.js   |    0
 .../libs/ace/mode-elixir.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-elm.js   |    0
 .../libs/ace/mode-erlang.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-forth.js |    0
 .../libs/ace/mode-fortran.js                  |    0
 .../libs/ace/mode-fsharp.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-fsl.js   |    0
 ui/{arduino => editor}/libs/ace/mode-ftl.js   |    0
 ui/{arduino => editor}/libs/ace/mode-gcode.js |    0
 .../libs/ace/mode-gherkin.js                  |    0
 .../libs/ace/mode-gitignore.js                |    0
 ui/{arduino => editor}/libs/ace/mode-glsl.js  |    0
 .../libs/ace/mode-gobstones.js                |    0
 .../libs/ace/mode-golang.js                   |    0
 .../libs/ace/mode-graphqlschema.js            |    0
 .../libs/ace/mode-groovy.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-haml.js  |    0
 .../libs/ace/mode-handlebars.js               |    0
 .../libs/ace/mode-haskell.js                  |    0
 .../libs/ace/mode-haskell_cabal.js            |    0
 ui/{arduino => editor}/libs/ace/mode-haxe.js  |    0
 ui/{arduino => editor}/libs/ace/mode-hjson.js |    0
 ui/{arduino => editor}/libs/ace/mode-html.js  |    0
 .../libs/ace/mode-html_elixir.js              |    0
 .../libs/ace/mode-html_ruby.js                |    0
 ui/{arduino => editor}/libs/ace/mode-ini.js   |    0
 ui/{arduino => editor}/libs/ace/mode-io.js    |    0
 ui/{arduino => editor}/libs/ace/mode-jack.js  |    0
 ui/{arduino => editor}/libs/ace/mode-jade.js  |    0
 ui/{arduino => editor}/libs/ace/mode-java.js  |    0
 .../libs/ace/mode-javascript.js               |    0
 ui/{arduino => editor}/libs/ace/mode-json.js  |    0
 ui/{arduino => editor}/libs/ace/mode-json5.js |    0
 .../libs/ace/mode-jsoniq.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-jsp.js   |    0
 ui/{arduino => editor}/libs/ace/mode-jssm.js  |    0
 ui/{arduino => editor}/libs/ace/mode-jsx.js   |    0
 ui/{arduino => editor}/libs/ace/mode-julia.js |    0
 .../libs/ace/mode-kotlin.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-latex.js |    0
 ui/{arduino => editor}/libs/ace/mode-less.js  |    0
 .../libs/ace/mode-liquid.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-lisp.js  |    0
 .../libs/ace/mode-livescript.js               |    0
 .../libs/ace/mode-logiql.js                   |    0
 .../libs/ace/mode-logtalk.js                  |    0
 ui/{arduino => editor}/libs/ace/mode-lsl.js   |    0
 ui/{arduino => editor}/libs/ace/mode-lua.js   |    0
 .../libs/ace/mode-luapage.js                  |    0
 .../libs/ace/mode-lucene.js                   |    0
 .../libs/ace/mode-makefile.js                 |    0
 .../libs/ace/mode-markdown.js                 |    0
 ui/{arduino => editor}/libs/ace/mode-mask.js  |    0
 .../libs/ace/mode-matlab.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-maze.js  |    0
 .../libs/ace/mode-mediawiki.js                |    0
 ui/{arduino => editor}/libs/ace/mode-mel.js   |    0
 ui/{arduino => editor}/libs/ace/mode-mixal.js |    0
 .../libs/ace/mode-mushcode.js                 |    0
 ui/{arduino => editor}/libs/ace/mode-mysql.js |    0
 ui/{arduino => editor}/libs/ace/mode-nginx.js |    0
 ui/{arduino => editor}/libs/ace/mode-nim.js   |    0
 ui/{arduino => editor}/libs/ace/mode-nix.js   |    0
 ui/{arduino => editor}/libs/ace/mode-nsis.js  |    0
 .../libs/ace/mode-nunjucks.js                 |    0
 .../libs/ace/mode-objectivec.js               |    0
 ui/{arduino => editor}/libs/ace/mode-ocaml.js |    0
 .../libs/ace/mode-pascal.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-perl.js  |    0
 ui/{arduino => editor}/libs/ace/mode-perl6.js |    0
 ui/{arduino => editor}/libs/ace/mode-pgsql.js |    0
 ui/{arduino => editor}/libs/ace/mode-php.js   |    0
 .../libs/ace/mode-php_laravel_blade.js        |    0
 ui/{arduino => editor}/libs/ace/mode-pig.js   |    0
 .../libs/ace/mode-plain_text.js               |    0
 .../libs/ace/mode-powershell.js               |    0
 ui/{arduino => editor}/libs/ace/mode-praat.js |    0
 .../libs/ace/mode-prisma.js                   |    0
 .../libs/ace/mode-prolog.js                   |    0
 .../libs/ace/mode-properties.js               |    0
 .../libs/ace/mode-protobuf.js                 |    0
 .../libs/ace/mode-puppet.js                   |    0
 .../libs/ace/mode-python.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-qml.js   |    0
 ui/{arduino => editor}/libs/ace/mode-r.js     |    0
 ui/{arduino => editor}/libs/ace/mode-razor.js |    0
 ui/{arduino => editor}/libs/ace/mode-rdoc.js  |    0
 ui/{arduino => editor}/libs/ace/mode-red.js   |    0
 .../libs/ace/mode-redshift.js                 |    0
 ui/{arduino => editor}/libs/ace/mode-rhtml.js |    0
 ui/{arduino => editor}/libs/ace/mode-rst.js   |    0
 ui/{arduino => editor}/libs/ace/mode-ruby.js  |    0
 ui/{arduino => editor}/libs/ace/mode-rust.js  |    0
 ui/{arduino => editor}/libs/ace/mode-sass.js  |    0
 ui/{arduino => editor}/libs/ace/mode-scad.js  |    0
 ui/{arduino => editor}/libs/ace/mode-scala.js |    0
 .../libs/ace/mode-scheme.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-scss.js  |    0
 ui/{arduino => editor}/libs/ace/mode-sh.js    |    0
 ui/{arduino => editor}/libs/ace/mode-sjs.js   |    0
 ui/{arduino => editor}/libs/ace/mode-slim.js  |    0
 .../libs/ace/mode-smarty.js                   |    0
 .../libs/ace/mode-snippets.js                 |    0
 .../libs/ace/mode-soy_template.js             |    0
 ui/{arduino => editor}/libs/ace/mode-space.js |    0
 .../libs/ace/mode-sparql.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-sql.js   |    0
 .../libs/ace/mode-sqlserver.js                |    0
 .../libs/ace/mode-stylus.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-svg.js   |    0
 ui/{arduino => editor}/libs/ace/mode-swift.js |    0
 ui/{arduino => editor}/libs/ace/mode-tcl.js   |    0
 .../libs/ace/mode-terraform.js                |    0
 ui/{arduino => editor}/libs/ace/mode-tex.js   |    0
 ui/{arduino => editor}/libs/ace/mode-text.js  |    0
 .../libs/ace/mode-textile.js                  |    0
 ui/{arduino => editor}/libs/ace/mode-toml.js  |    0
 ui/{arduino => editor}/libs/ace/mode-tsx.js   |    0
 .../libs/ace/mode-turtle.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-twig.js  |    0
 .../libs/ace/mode-typescript.js               |    0
 ui/{arduino => editor}/libs/ace/mode-vala.js  |    0
 .../libs/ace/mode-vbscript.js                 |    0
 .../libs/ace/mode-velocity.js                 |    0
 .../libs/ace/mode-verilog.js                  |    0
 ui/{arduino => editor}/libs/ace/mode-vhdl.js  |    0
 .../libs/ace/mode-visualforce.js              |    0
 .../libs/ace/mode-wollok.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-xml.js   |    0
 .../libs/ace/mode-xquery.js                   |    0
 ui/{arduino => editor}/libs/ace/mode-yaml.js  |    0
 ui/{arduino => editor}/libs/ace/mode-zeek.js  |    0
 .../libs/ace/snippets/abap.js                 |    0
 .../libs/ace/snippets/abc.js                  |    0
 .../libs/ace/snippets/actionscript.js         |    0
 .../libs/ace/snippets/ada.js                  |    0
 .../libs/ace/snippets/alda.js                 |    0
 .../libs/ace/snippets/apache_conf.js          |    0
 .../libs/ace/snippets/apex.js                 |    0
 .../libs/ace/snippets/applescript.js          |    0
 .../libs/ace/snippets/aql.js                  |    0
 .../libs/ace/snippets/asciidoc.js             |    0
 .../libs/ace/snippets/asl.js                  |    0
 .../libs/ace/snippets/assembly_x86.js         |    0
 .../libs/ace/snippets/autohotkey.js           |    0
 .../libs/ace/snippets/batchfile.js            |    0
 .../libs/ace/snippets/c9search.js             |    0
 .../libs/ace/snippets/c_cpp.js                |    0
 .../libs/ace/snippets/cirru.js                |    0
 .../libs/ace/snippets/clojure.js              |    0
 .../libs/ace/snippets/cobol.js                |    0
 .../libs/ace/snippets/coffee.js               |    0
 .../libs/ace/snippets/coldfusion.js           |    0
 .../libs/ace/snippets/crystal.js              |    0
 .../libs/ace/snippets/csharp.js               |    0
 .../libs/ace/snippets/csound_document.js      |    0
 .../libs/ace/snippets/csound_orchestra.js     |    0
 .../libs/ace/snippets/csound_score.js         |    0
 .../libs/ace/snippets/csp.js                  |    0
 .../libs/ace/snippets/css.js                  |    0
 .../libs/ace/snippets/curly.js                |    0
 ui/{arduino => editor}/libs/ace/snippets/d.js |    0
 .../libs/ace/snippets/dart.js                 |    0
 .../libs/ace/snippets/diff.js                 |    0
 .../libs/ace/snippets/django.js               |    0
 .../libs/ace/snippets/dockerfile.js           |    0
 .../libs/ace/snippets/dot.js                  |    0
 .../libs/ace/snippets/drools.js               |    0
 .../libs/ace/snippets/edifact.js              |    0
 .../libs/ace/snippets/eiffel.js               |    0
 .../libs/ace/snippets/ejs.js                  |    0
 .../libs/ace/snippets/elixir.js               |    0
 .../libs/ace/snippets/elm.js                  |    0
 .../libs/ace/snippets/erlang.js               |    0
 .../libs/ace/snippets/forth.js                |    0
 .../libs/ace/snippets/fortran.js              |    0
 .../libs/ace/snippets/fsharp.js               |    0
 .../libs/ace/snippets/fsl.js                  |    0
 .../libs/ace/snippets/ftl.js                  |    0
 .../libs/ace/snippets/gcode.js                |    0
 .../libs/ace/snippets/gherkin.js              |    0
 .../libs/ace/snippets/gitignore.js            |    0
 .../libs/ace/snippets/glsl.js                 |    0
 .../libs/ace/snippets/gobstones.js            |    0
 .../libs/ace/snippets/golang.js               |    0
 .../libs/ace/snippets/graphqlschema.js        |    0
 .../libs/ace/snippets/groovy.js               |    0
 .../libs/ace/snippets/haml.js                 |    0
 .../libs/ace/snippets/handlebars.js           |    0
 .../libs/ace/snippets/haskell.js              |    0
 .../libs/ace/snippets/haskell_cabal.js        |    0
 .../libs/ace/snippets/haxe.js                 |    0
 .../libs/ace/snippets/hjson.js                |    0
 .../libs/ace/snippets/html.js                 |    0
 .../libs/ace/snippets/html_elixir.js          |    0
 .../libs/ace/snippets/html_ruby.js            |    0
 .../libs/ace/snippets/ini.js                  |    0
 .../libs/ace/snippets/io.js                   |    0
 .../libs/ace/snippets/jack.js                 |    0
 .../libs/ace/snippets/jade.js                 |    0
 .../libs/ace/snippets/java.js                 |    0
 .../libs/ace/snippets/javascript.js           |    0
 .../libs/ace/snippets/json.js                 |    0
 .../libs/ace/snippets/json5.js                |    0
 .../libs/ace/snippets/jsoniq.js               |    0
 .../libs/ace/snippets/jsp.js                  |    0
 .../libs/ace/snippets/jssm.js                 |    0
 .../libs/ace/snippets/jsx.js                  |    0
 .../libs/ace/snippets/julia.js                |    0
 .../libs/ace/snippets/kotlin.js               |    0
 .../libs/ace/snippets/latex.js                |    0
 .../libs/ace/snippets/less.js                 |    0
 .../libs/ace/snippets/liquid.js               |    0
 .../libs/ace/snippets/lisp.js                 |    0
 .../libs/ace/snippets/livescript.js           |    0
 .../libs/ace/snippets/logiql.js               |    0
 .../libs/ace/snippets/logtalk.js              |    0
 .../libs/ace/snippets/lsl.js                  |    0
 .../libs/ace/snippets/lua.js                  |    0
 .../libs/ace/snippets/luapage.js              |    0
 .../libs/ace/snippets/lucene.js               |    0
 .../libs/ace/snippets/makefile.js             |    0
 .../libs/ace/snippets/markdown.js             |    0
 .../libs/ace/snippets/mask.js                 |    0
 .../libs/ace/snippets/matlab.js               |    0
 .../libs/ace/snippets/maze.js                 |    0
 .../libs/ace/snippets/mediawiki.js            |    0
 .../libs/ace/snippets/mel.js                  |    0
 .../libs/ace/snippets/mixal.js                |    0
 .../libs/ace/snippets/mushcode.js             |    0
 .../libs/ace/snippets/mysql.js                |    0
 .../libs/ace/snippets/nginx.js                |    0
 .../libs/ace/snippets/nim.js                  |    0
 .../libs/ace/snippets/nix.js                  |    0
 .../libs/ace/snippets/nsis.js                 |    0
 .../libs/ace/snippets/nunjucks.js             |    0
 .../libs/ace/snippets/objectivec.js           |    0
 .../libs/ace/snippets/ocaml.js                |    0
 .../libs/ace/snippets/pascal.js               |    0
 .../libs/ace/snippets/perl.js                 |    0
 .../libs/ace/snippets/perl6.js                |    0
 .../libs/ace/snippets/pgsql.js                |    0
 .../libs/ace/snippets/php.js                  |    0
 .../libs/ace/snippets/php_laravel_blade.js    |    0
 .../libs/ace/snippets/pig.js                  |    0
 .../libs/ace/snippets/plain_text.js           |    0
 .../libs/ace/snippets/powershell.js           |    0
 .../libs/ace/snippets/praat.js                |    0
 .../libs/ace/snippets/prisma.js               |    0
 .../libs/ace/snippets/prolog.js               |    0
 .../libs/ace/snippets/properties.js           |    0
 .../libs/ace/snippets/protobuf.js             |    0
 .../libs/ace/snippets/puppet.js               |    0
 .../libs/ace/snippets/python.js               |    0
 .../libs/ace/snippets/qml.js                  |    0
 ui/{arduino => editor}/libs/ace/snippets/r.js |    0
 .../libs/ace/snippets/razor.js                |    0
 .../libs/ace/snippets/rdoc.js                 |    0
 .../libs/ace/snippets/red.js                  |    0
 .../libs/ace/snippets/redshift.js             |    0
 .../libs/ace/snippets/rhtml.js                |    0
 .../libs/ace/snippets/rst.js                  |    0
 .../libs/ace/snippets/ruby.js                 |    0
 .../libs/ace/snippets/rust.js                 |    0
 .../libs/ace/snippets/sass.js                 |    0
 .../libs/ace/snippets/scad.js                 |    0
 .../libs/ace/snippets/scala.js                |    0
 .../libs/ace/snippets/scheme.js               |    0
 .../libs/ace/snippets/scss.js                 |    0
 .../libs/ace/snippets/sh.js                   |    0
 .../libs/ace/snippets/sjs.js                  |    0
 .../libs/ace/snippets/slim.js                 |    0
 .../libs/ace/snippets/smarty.js               |    0
 .../libs/ace/snippets/snippets.js             |    0
 .../libs/ace/snippets/soy_template.js         |    0
 .../libs/ace/snippets/space.js                |    0
 .../libs/ace/snippets/sparql.js               |    0
 .../libs/ace/snippets/sql.js                  |    0
 .../libs/ace/snippets/sqlserver.js            |    0
 .../libs/ace/snippets/stylus.js               |    0
 .../libs/ace/snippets/svg.js                  |    0
 .../libs/ace/snippets/swift.js                |    0
 .../libs/ace/snippets/tcl.js                  |    0
 .../libs/ace/snippets/terraform.js            |    0
 .../libs/ace/snippets/tex.js                  |    0
 .../libs/ace/snippets/text.js                 |    0
 .../libs/ace/snippets/textile.js              |    0
 .../libs/ace/snippets/toml.js                 |    0
 .../libs/ace/snippets/tsx.js                  |    0
 .../libs/ace/snippets/turtle.js               |    0
 .../libs/ace/snippets/twig.js                 |    0
 .../libs/ace/snippets/typescript.js           |    0
 .../libs/ace/snippets/vala.js                 |    0
 .../libs/ace/snippets/vbscript.js             |    0
 .../libs/ace/snippets/velocity.js             |    0
 .../libs/ace/snippets/verilog.js              |    0
 .../libs/ace/snippets/vhdl.js                 |    0
 .../libs/ace/snippets/visualforce.js          |    0
 .../libs/ace/snippets/wollok.js               |    0
 .../libs/ace/snippets/xml.js                  |    0
 .../libs/ace/snippets/xquery.js               |    0
 .../libs/ace/snippets/yaml.js                 |    0
 .../libs/ace/snippets/zeek.js                 |    0
 .../libs/ace/theme-ambiance.js                |    0
 .../libs/ace/theme-chaos.js                   |    0
 .../libs/ace/theme-chrome.js                  |    0
 .../libs/ace/theme-clouds.js                  |    0
 .../libs/ace/theme-clouds_midnight.js         |    0
 .../libs/ace/theme-cobalt.js                  |    0
 .../libs/ace/theme-crimson_editor.js          |    0
 ui/{arduino => editor}/libs/ace/theme-dawn.js |    0
 .../libs/ace/theme-dracula.js                 |    0
 .../libs/ace/theme-dreamweaver.js             |    0
 .../libs/ace/theme-eclipse.js                 |    0
 .../libs/ace/theme-github.js                  |    0
 ui/{arduino => editor}/libs/ace/theme-gob.js  |    0
 .../libs/ace/theme-gruvbox.js                 |    0
 .../libs/ace/theme-idle_fingers.js            |    0
 .../libs/ace/theme-iplastic.js                |    0
 .../libs/ace/theme-katzenmilch.js             |    0
 .../libs/ace/theme-kr_theme.js                |    0
 .../libs/ace/theme-kuroir.js                  |    0
 .../libs/ace/theme-merbivore.js               |    0
 .../libs/ace/theme-merbivore_soft.js          |    0
 .../libs/ace/theme-mono_industrial.js         |    0
 .../libs/ace/theme-monokai.js                 |    0
 .../libs/ace/theme-nord_dark.js               |    0
 .../libs/ace/theme-pastel_on_dark.js          |    0
 .../libs/ace/theme-solarized_dark.js          |    0
 .../libs/ace/theme-solarized_light.js         |    0
 .../libs/ace/theme-sqlserver.js               |    0
 .../libs/ace/theme-terminal.js                |    0
 .../libs/ace/theme-textmate.js                |    0
 .../libs/ace/theme-tomorrow.js                |    0
 .../libs/ace/theme-tomorrow_night.js          |    0
 .../libs/ace/theme-tomorrow_night_blue.js     |    0
 .../libs/ace/theme-tomorrow_night_bright.js   |    0
 .../libs/ace/theme-tomorrow_night_eighties.js |    0
 .../libs/ace/theme-twilight.js                |    0
 .../libs/ace/theme-vibrant_ink.js             |    0
 .../libs/ace/theme-xcode.js                   |    0
 .../libs/ace/worker-base.js                   |    0
 .../libs/ace/worker-coffee.js                 |    0
 ui/{arduino => editor}/libs/ace/worker-css.js |    0
 .../libs/ace/worker-html.js                   |    0
 .../libs/ace/worker-javascript.js             |    0
 .../libs/ace/worker-json.js                   |    0
 ui/{arduino => editor}/libs/ace/worker-lua.js |    0
 ui/{arduino => editor}/libs/ace/worker-php.js |    0
 ui/{arduino => editor}/libs/ace/worker-xml.js |    0
 .../libs/ace/worker-xquery.js                 |    0
 ui/{arduino => editor}/libs/build_choo.js     |    0
 ui/{arduino => editor}/libs/choo.js           |    0
 .../libs/xterm-addon-fit.js                   |    0
 ui/{arduino => editor}/libs/xterm.css         |    0
 ui/{arduino => editor}/libs/xterm.js          |    0
 ui/{arduino => editor}/package-lock.json      |    0
 ui/{arduino => editor}/package.json           |    0
 ui/{arduino => editor}/store.js               |    0
 ui/{arduino => editor}/theme.css              |    0
 ui/ftp/components/files/diskFiles.tsx         |   16 +
 ui/ftp/components/files/diskFiles.type.ts     |    8 +
 ui/ftp/components/files/management.type.ts    |    7 +
 ui/ftp/components/files/serialFiles.tsx       |   16 +
 ui/ftp/components/files/serialFiles.type.ts   |    8 +
 .../components/navigation/diskNavigation.tsx  |    9 +
 .../navigation/diskNavigation.type.ts         |    6 +
 .../navigation/serialNavigation.tsx           |    9 +
 .../navigation/serialNavigation.type.ts       |    6 +
 .../components/shared/breadcrumbs.module.scss |    9 +
 ui/ftp/components/shared/breadcrumbs.tsx      |   21 +
 ui/ftp/components/shared/button.module.scss   |   12 +
 ui/ftp/components/shared/button.tsx           |    8 +
 ui/ftp/components/toolbar/toolbar.module.scss |    8 +
 ui/ftp/components/toolbar/toolbar.tsx         |   42 +
 ui/ftp/components/toolbar/toolbar.type.ts     |   10 +
 ui/ftp/index.html                             |   14 +
 ui/ftp/main.logic.ts                          |  164 +
 ui/ftp/main.scss                              |    9 +
 ui/ftp/main.tsx                               |   46 +
 ui/ftp/main.type.ts                           |    6 +
 ui/ftp/package-lock.json                      | 2975 +++++++++++++++++
 ui/ftp/package.json                           |   20 +
 497 files changed, 3439 insertions(+), 7 deletions(-)
 rename ui/{arduino => editor}/about.css (100%)
 rename ui/{arduino => editor}/about.html (100%)
 rename ui/{arduino => editor}/app.js (100%)
 rename ui/{arduino => editor}/assets/about_image.png (100%)
 rename ui/{arduino => editor}/components/editor.js (100%)
 rename ui/{arduino => editor}/components/filename.js (100%)
 rename ui/{arduino => editor}/components/icon.js (100%)
 rename ui/{arduino => editor}/components/message.js (100%)
 rename ui/{arduino => editor}/components/newfiledialog.js (100%)
 rename ui/{arduino => editor}/components/panel.js (100%)
 rename ui/{arduino => editor}/components/panel_files.js (100%)
 rename ui/{arduino => editor}/components/panel_terminal.js (100%)
 rename ui/{arduino => editor}/components/portdialog.js (100%)
 rename ui/{arduino => editor}/components/toolbar.js (100%)
 rename ui/{arduino => editor}/components/toolbar_button.js (100%)
 rename ui/{arduino => editor}/icons/Connect.svg (100%)
 rename ui/{arduino => editor}/icons/Connect@3x.png (100%)
 rename ui/{arduino => editor}/icons/Copy-Left.svg (100%)
 rename ui/{arduino => editor}/icons/Copy-Left@3x.png (100%)
 rename ui/{arduino => editor}/icons/Copy-Right.svg (100%)
 rename ui/{arduino => editor}/icons/Copy-Right@3x.png (100%)
 rename ui/{arduino => editor}/icons/Copy.png (100%)
 rename ui/{arduino => editor}/icons/Copy.svg (100%)
 rename ui/{arduino => editor}/icons/Delete.svg (100%)
 rename ui/{arduino => editor}/icons/Delete@3x.png (100%)
 rename ui/{arduino => editor}/icons/Disconnect.svg (100%)
 rename ui/{arduino => editor}/icons/Disconnect@3x.png (100%)
 rename ui/{arduino => editor}/icons/File-Explorer.svg (100%)
 rename ui/{arduino => editor}/icons/File-Explorer@3x.png (100%)
 rename ui/{arduino => editor}/icons/New.svg (100%)
 rename ui/{arduino => editor}/icons/New@3x.png (100%)
 rename ui/{arduino => editor}/icons/Open.svg (100%)
 rename ui/{arduino => editor}/icons/Open@3x.png (100%)
 rename ui/{arduino => editor}/icons/Output.svg (100%)
 rename ui/{arduino => editor}/icons/Output@3x.png (100%)
 rename ui/{arduino => editor}/icons/Paste.png (100%)
 rename ui/{arduino => editor}/icons/Paste.svg (100%)
 rename ui/{arduino => editor}/icons/Reboot.svg (100%)
 rename ui/{arduino => editor}/icons/Reboot@3x.png (100%)
 rename ui/{arduino => editor}/icons/Run.svg (100%)
 rename ui/{arduino => editor}/icons/Run@3x.png (100%)
 rename ui/{arduino => editor}/icons/Save.svg (100%)
 rename ui/{arduino => editor}/icons/Save@3x.png (100%)
 rename ui/{arduino => editor}/icons/Stop.svg (100%)
 rename ui/{arduino => editor}/icons/Stop@3x.png (100%)
 rename ui/{arduino => editor}/index.html (100%)
 rename ui/{arduino => editor}/libs/FileSaver.js (100%)
 rename ui/{arduino => editor}/libs/ace/ace.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-beautify.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-code_lens.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-elastic_tabstops_lite.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-emmet.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-error_marker.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-keybinding_menu.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-language_tools.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-linking.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-modelist.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-options.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-prompt.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-rtl.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-searchbox.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-settings_menu.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-spellcheck.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-split.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-static_highlight.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-statusbar.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-textarea.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-themelist.js (100%)
 rename ui/{arduino => editor}/libs/ace/ext-whitespace.js (100%)
 rename ui/{arduino => editor}/libs/ace/keybinding-emacs.js (100%)
 rename ui/{arduino => editor}/libs/ace/keybinding-sublime.js (100%)
 rename ui/{arduino => editor}/libs/ace/keybinding-vim.js (100%)
 rename ui/{arduino => editor}/libs/ace/keybinding-vscode.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-abap.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-abc.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-actionscript.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-ada.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-alda.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-apache_conf.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-apex.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-applescript.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-aql.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-asciidoc.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-asl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-assembly_x86.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-autohotkey.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-batchfile.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-c9search.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-c_cpp.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-cirru.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-clojure.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-cobol.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-coffee.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-coldfusion.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-crystal.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-csharp.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-csound_document.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-csound_orchestra.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-csound_score.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-csp.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-css.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-curly.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-d.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-dart.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-diff.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-django.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-dockerfile.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-dot.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-drools.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-edifact.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-eiffel.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-ejs.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-elixir.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-elm.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-erlang.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-forth.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-fortran.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-fsharp.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-fsl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-ftl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-gcode.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-gherkin.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-gitignore.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-glsl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-gobstones.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-golang.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-graphqlschema.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-groovy.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-haml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-handlebars.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-haskell.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-haskell_cabal.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-haxe.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-hjson.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-html.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-html_elixir.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-html_ruby.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-ini.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-io.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-jack.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-jade.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-java.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-javascript.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-json.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-json5.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-jsoniq.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-jsp.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-jssm.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-jsx.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-julia.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-kotlin.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-latex.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-less.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-liquid.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-lisp.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-livescript.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-logiql.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-logtalk.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-lsl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-lua.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-luapage.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-lucene.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-makefile.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-markdown.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-mask.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-matlab.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-maze.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-mediawiki.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-mel.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-mixal.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-mushcode.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-mysql.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-nginx.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-nim.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-nix.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-nsis.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-nunjucks.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-objectivec.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-ocaml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-pascal.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-perl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-perl6.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-pgsql.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-php.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-php_laravel_blade.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-pig.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-plain_text.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-powershell.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-praat.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-prisma.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-prolog.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-properties.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-protobuf.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-puppet.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-python.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-qml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-r.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-razor.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-rdoc.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-red.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-redshift.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-rhtml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-rst.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-ruby.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-rust.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-sass.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-scad.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-scala.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-scheme.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-scss.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-sh.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-sjs.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-slim.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-smarty.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-snippets.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-soy_template.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-space.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-sparql.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-sql.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-sqlserver.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-stylus.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-svg.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-swift.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-tcl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-terraform.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-tex.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-text.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-textile.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-toml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-tsx.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-turtle.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-twig.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-typescript.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-vala.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-vbscript.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-velocity.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-verilog.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-vhdl.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-visualforce.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-wollok.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-xml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-xquery.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-yaml.js (100%)
 rename ui/{arduino => editor}/libs/ace/mode-zeek.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/abap.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/abc.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/actionscript.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/ada.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/alda.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/apache_conf.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/apex.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/applescript.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/aql.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/asciidoc.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/asl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/assembly_x86.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/autohotkey.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/batchfile.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/c9search.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/c_cpp.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/cirru.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/clojure.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/cobol.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/coffee.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/coldfusion.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/crystal.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/csharp.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/csound_document.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/csound_orchestra.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/csound_score.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/csp.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/css.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/curly.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/d.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/dart.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/diff.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/django.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/dockerfile.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/dot.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/drools.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/edifact.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/eiffel.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/ejs.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/elixir.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/elm.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/erlang.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/forth.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/fortran.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/fsharp.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/fsl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/ftl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/gcode.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/gherkin.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/gitignore.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/glsl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/gobstones.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/golang.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/graphqlschema.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/groovy.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/haml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/handlebars.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/haskell.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/haskell_cabal.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/haxe.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/hjson.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/html.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/html_elixir.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/html_ruby.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/ini.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/io.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/jack.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/jade.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/java.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/javascript.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/json.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/json5.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/jsoniq.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/jsp.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/jssm.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/jsx.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/julia.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/kotlin.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/latex.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/less.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/liquid.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/lisp.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/livescript.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/logiql.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/logtalk.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/lsl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/lua.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/luapage.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/lucene.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/makefile.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/markdown.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/mask.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/matlab.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/maze.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/mediawiki.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/mel.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/mixal.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/mushcode.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/mysql.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/nginx.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/nim.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/nix.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/nsis.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/nunjucks.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/objectivec.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/ocaml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/pascal.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/perl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/perl6.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/pgsql.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/php.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/php_laravel_blade.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/pig.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/plain_text.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/powershell.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/praat.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/prisma.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/prolog.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/properties.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/protobuf.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/puppet.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/python.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/qml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/r.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/razor.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/rdoc.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/red.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/redshift.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/rhtml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/rst.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/ruby.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/rust.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/sass.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/scad.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/scala.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/scheme.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/scss.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/sh.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/sjs.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/slim.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/smarty.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/snippets.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/soy_template.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/space.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/sparql.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/sql.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/sqlserver.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/stylus.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/svg.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/swift.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/tcl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/terraform.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/tex.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/text.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/textile.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/toml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/tsx.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/turtle.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/twig.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/typescript.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/vala.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/vbscript.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/velocity.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/verilog.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/vhdl.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/visualforce.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/wollok.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/xml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/xquery.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/yaml.js (100%)
 rename ui/{arduino => editor}/libs/ace/snippets/zeek.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-ambiance.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-chaos.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-chrome.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-clouds.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-clouds_midnight.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-cobalt.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-crimson_editor.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-dawn.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-dracula.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-dreamweaver.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-eclipse.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-github.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-gob.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-gruvbox.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-idle_fingers.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-iplastic.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-katzenmilch.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-kr_theme.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-kuroir.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-merbivore.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-merbivore_soft.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-mono_industrial.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-monokai.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-nord_dark.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-pastel_on_dark.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-solarized_dark.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-solarized_light.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-sqlserver.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-terminal.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-textmate.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-tomorrow.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-tomorrow_night.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-tomorrow_night_blue.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-tomorrow_night_bright.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-tomorrow_night_eighties.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-twilight.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-vibrant_ink.js (100%)
 rename ui/{arduino => editor}/libs/ace/theme-xcode.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-base.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-coffee.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-css.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-html.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-javascript.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-json.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-lua.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-php.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-xml.js (100%)
 rename ui/{arduino => editor}/libs/ace/worker-xquery.js (100%)
 rename ui/{arduino => editor}/libs/build_choo.js (100%)
 rename ui/{arduino => editor}/libs/choo.js (100%)
 rename ui/{arduino => editor}/libs/xterm-addon-fit.js (100%)
 rename ui/{arduino => editor}/libs/xterm.css (100%)
 rename ui/{arduino => editor}/libs/xterm.js (100%)
 rename ui/{arduino => editor}/package-lock.json (100%)
 rename ui/{arduino => editor}/package.json (100%)
 rename ui/{arduino => editor}/store.js (100%)
 rename ui/{arduino => editor}/theme.css (100%)
 create mode 100644 ui/ftp/components/files/diskFiles.tsx
 create mode 100644 ui/ftp/components/files/diskFiles.type.ts
 create mode 100644 ui/ftp/components/files/management.type.ts
 create mode 100644 ui/ftp/components/files/serialFiles.tsx
 create mode 100644 ui/ftp/components/files/serialFiles.type.ts
 create mode 100644 ui/ftp/components/navigation/diskNavigation.tsx
 create mode 100644 ui/ftp/components/navigation/diskNavigation.type.ts
 create mode 100644 ui/ftp/components/navigation/serialNavigation.tsx
 create mode 100644 ui/ftp/components/navigation/serialNavigation.type.ts
 create mode 100644 ui/ftp/components/shared/breadcrumbs.module.scss
 create mode 100644 ui/ftp/components/shared/breadcrumbs.tsx
 create mode 100644 ui/ftp/components/shared/button.module.scss
 create mode 100644 ui/ftp/components/shared/button.tsx
 create mode 100644 ui/ftp/components/toolbar/toolbar.module.scss
 create mode 100644 ui/ftp/components/toolbar/toolbar.tsx
 create mode 100644 ui/ftp/components/toolbar/toolbar.type.ts
 create mode 100644 ui/ftp/index.html
 create mode 100644 ui/ftp/main.logic.ts
 create mode 100644 ui/ftp/main.scss
 create mode 100644 ui/ftp/main.tsx
 create mode 100644 ui/ftp/main.type.ts
 create mode 100644 ui/ftp/package-lock.json
 create mode 100644 ui/ftp/package.json

diff --git a/.gitignore b/.gitignore
index c0320fa..a2220b2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ dist
 dist/
 .vscode
 Resources
-temp
\ No newline at end of file
+temp
+.parcel-cache
\ No newline at end of file
diff --git a/index.js b/index.js
index bbf1df4..d49cd27 100644
--- a/index.js
+++ b/index.js
@@ -12,13 +12,15 @@ async function openFolderDialog() {
   return dir.filePaths[0] || null
 }
 
-function listFolder(folder) {
+function listFolder(folder, filesOnly) {
   files = fs.readdirSync(path.resolve(folder))
   // Filter out directories
-  files = files.filter(f => {
-    let filePath = path.resolve(folder, f)
-    return !fs.lstatSync(filePath).isDirectory()
-  })
+  if (filesOnly) {
+    files = files.filter(f => {
+      let filePath = path.resolve(folder, f)
+      return !fs.lstatSync(filePath).isDirectory()
+    })
+  }
   // Filter out dot files
   files = files.filter(f => f.indexOf('.') !== 0)
   return files
@@ -105,7 +107,7 @@ function createWindow () {
     }
   })
   // and load the index.html of the app.
-  win.loadFile('ui/arduino/index.html')
+  win.loadFile('ui/ftp/dist/index.html')
 }
 
 // TODO: Loading splash screen
diff --git a/ui/arduino/about.css b/ui/editor/about.css
similarity index 100%
rename from ui/arduino/about.css
rename to ui/editor/about.css
diff --git a/ui/arduino/about.html b/ui/editor/about.html
similarity index 100%
rename from ui/arduino/about.html
rename to ui/editor/about.html
diff --git a/ui/arduino/app.js b/ui/editor/app.js
similarity index 100%
rename from ui/arduino/app.js
rename to ui/editor/app.js
diff --git a/ui/arduino/assets/about_image.png b/ui/editor/assets/about_image.png
similarity index 100%
rename from ui/arduino/assets/about_image.png
rename to ui/editor/assets/about_image.png
diff --git a/ui/arduino/components/editor.js b/ui/editor/components/editor.js
similarity index 100%
rename from ui/arduino/components/editor.js
rename to ui/editor/components/editor.js
diff --git a/ui/arduino/components/filename.js b/ui/editor/components/filename.js
similarity index 100%
rename from ui/arduino/components/filename.js
rename to ui/editor/components/filename.js
diff --git a/ui/arduino/components/icon.js b/ui/editor/components/icon.js
similarity index 100%
rename from ui/arduino/components/icon.js
rename to ui/editor/components/icon.js
diff --git a/ui/arduino/components/message.js b/ui/editor/components/message.js
similarity index 100%
rename from ui/arduino/components/message.js
rename to ui/editor/components/message.js
diff --git a/ui/arduino/components/newfiledialog.js b/ui/editor/components/newfiledialog.js
similarity index 100%
rename from ui/arduino/components/newfiledialog.js
rename to ui/editor/components/newfiledialog.js
diff --git a/ui/arduino/components/panel.js b/ui/editor/components/panel.js
similarity index 100%
rename from ui/arduino/components/panel.js
rename to ui/editor/components/panel.js
diff --git a/ui/arduino/components/panel_files.js b/ui/editor/components/panel_files.js
similarity index 100%
rename from ui/arduino/components/panel_files.js
rename to ui/editor/components/panel_files.js
diff --git a/ui/arduino/components/panel_terminal.js b/ui/editor/components/panel_terminal.js
similarity index 100%
rename from ui/arduino/components/panel_terminal.js
rename to ui/editor/components/panel_terminal.js
diff --git a/ui/arduino/components/portdialog.js b/ui/editor/components/portdialog.js
similarity index 100%
rename from ui/arduino/components/portdialog.js
rename to ui/editor/components/portdialog.js
diff --git a/ui/arduino/components/toolbar.js b/ui/editor/components/toolbar.js
similarity index 100%
rename from ui/arduino/components/toolbar.js
rename to ui/editor/components/toolbar.js
diff --git a/ui/arduino/components/toolbar_button.js b/ui/editor/components/toolbar_button.js
similarity index 100%
rename from ui/arduino/components/toolbar_button.js
rename to ui/editor/components/toolbar_button.js
diff --git a/ui/arduino/icons/Connect.svg b/ui/editor/icons/Connect.svg
similarity index 100%
rename from ui/arduino/icons/Connect.svg
rename to ui/editor/icons/Connect.svg
diff --git a/ui/arduino/icons/Connect@3x.png b/ui/editor/icons/Connect@3x.png
similarity index 100%
rename from ui/arduino/icons/Connect@3x.png
rename to ui/editor/icons/Connect@3x.png
diff --git a/ui/arduino/icons/Copy-Left.svg b/ui/editor/icons/Copy-Left.svg
similarity index 100%
rename from ui/arduino/icons/Copy-Left.svg
rename to ui/editor/icons/Copy-Left.svg
diff --git a/ui/arduino/icons/Copy-Left@3x.png b/ui/editor/icons/Copy-Left@3x.png
similarity index 100%
rename from ui/arduino/icons/Copy-Left@3x.png
rename to ui/editor/icons/Copy-Left@3x.png
diff --git a/ui/arduino/icons/Copy-Right.svg b/ui/editor/icons/Copy-Right.svg
similarity index 100%
rename from ui/arduino/icons/Copy-Right.svg
rename to ui/editor/icons/Copy-Right.svg
diff --git a/ui/arduino/icons/Copy-Right@3x.png b/ui/editor/icons/Copy-Right@3x.png
similarity index 100%
rename from ui/arduino/icons/Copy-Right@3x.png
rename to ui/editor/icons/Copy-Right@3x.png
diff --git a/ui/arduino/icons/Copy.png b/ui/editor/icons/Copy.png
similarity index 100%
rename from ui/arduino/icons/Copy.png
rename to ui/editor/icons/Copy.png
diff --git a/ui/arduino/icons/Copy.svg b/ui/editor/icons/Copy.svg
similarity index 100%
rename from ui/arduino/icons/Copy.svg
rename to ui/editor/icons/Copy.svg
diff --git a/ui/arduino/icons/Delete.svg b/ui/editor/icons/Delete.svg
similarity index 100%
rename from ui/arduino/icons/Delete.svg
rename to ui/editor/icons/Delete.svg
diff --git a/ui/arduino/icons/Delete@3x.png b/ui/editor/icons/Delete@3x.png
similarity index 100%
rename from ui/arduino/icons/Delete@3x.png
rename to ui/editor/icons/Delete@3x.png
diff --git a/ui/arduino/icons/Disconnect.svg b/ui/editor/icons/Disconnect.svg
similarity index 100%
rename from ui/arduino/icons/Disconnect.svg
rename to ui/editor/icons/Disconnect.svg
diff --git a/ui/arduino/icons/Disconnect@3x.png b/ui/editor/icons/Disconnect@3x.png
similarity index 100%
rename from ui/arduino/icons/Disconnect@3x.png
rename to ui/editor/icons/Disconnect@3x.png
diff --git a/ui/arduino/icons/File-Explorer.svg b/ui/editor/icons/File-Explorer.svg
similarity index 100%
rename from ui/arduino/icons/File-Explorer.svg
rename to ui/editor/icons/File-Explorer.svg
diff --git a/ui/arduino/icons/File-Explorer@3x.png b/ui/editor/icons/File-Explorer@3x.png
similarity index 100%
rename from ui/arduino/icons/File-Explorer@3x.png
rename to ui/editor/icons/File-Explorer@3x.png
diff --git a/ui/arduino/icons/New.svg b/ui/editor/icons/New.svg
similarity index 100%
rename from ui/arduino/icons/New.svg
rename to ui/editor/icons/New.svg
diff --git a/ui/arduino/icons/New@3x.png b/ui/editor/icons/New@3x.png
similarity index 100%
rename from ui/arduino/icons/New@3x.png
rename to ui/editor/icons/New@3x.png
diff --git a/ui/arduino/icons/Open.svg b/ui/editor/icons/Open.svg
similarity index 100%
rename from ui/arduino/icons/Open.svg
rename to ui/editor/icons/Open.svg
diff --git a/ui/arduino/icons/Open@3x.png b/ui/editor/icons/Open@3x.png
similarity index 100%
rename from ui/arduino/icons/Open@3x.png
rename to ui/editor/icons/Open@3x.png
diff --git a/ui/arduino/icons/Output.svg b/ui/editor/icons/Output.svg
similarity index 100%
rename from ui/arduino/icons/Output.svg
rename to ui/editor/icons/Output.svg
diff --git a/ui/arduino/icons/Output@3x.png b/ui/editor/icons/Output@3x.png
similarity index 100%
rename from ui/arduino/icons/Output@3x.png
rename to ui/editor/icons/Output@3x.png
diff --git a/ui/arduino/icons/Paste.png b/ui/editor/icons/Paste.png
similarity index 100%
rename from ui/arduino/icons/Paste.png
rename to ui/editor/icons/Paste.png
diff --git a/ui/arduino/icons/Paste.svg b/ui/editor/icons/Paste.svg
similarity index 100%
rename from ui/arduino/icons/Paste.svg
rename to ui/editor/icons/Paste.svg
diff --git a/ui/arduino/icons/Reboot.svg b/ui/editor/icons/Reboot.svg
similarity index 100%
rename from ui/arduino/icons/Reboot.svg
rename to ui/editor/icons/Reboot.svg
diff --git a/ui/arduino/icons/Reboot@3x.png b/ui/editor/icons/Reboot@3x.png
similarity index 100%
rename from ui/arduino/icons/Reboot@3x.png
rename to ui/editor/icons/Reboot@3x.png
diff --git a/ui/arduino/icons/Run.svg b/ui/editor/icons/Run.svg
similarity index 100%
rename from ui/arduino/icons/Run.svg
rename to ui/editor/icons/Run.svg
diff --git a/ui/arduino/icons/Run@3x.png b/ui/editor/icons/Run@3x.png
similarity index 100%
rename from ui/arduino/icons/Run@3x.png
rename to ui/editor/icons/Run@3x.png
diff --git a/ui/arduino/icons/Save.svg b/ui/editor/icons/Save.svg
similarity index 100%
rename from ui/arduino/icons/Save.svg
rename to ui/editor/icons/Save.svg
diff --git a/ui/arduino/icons/Save@3x.png b/ui/editor/icons/Save@3x.png
similarity index 100%
rename from ui/arduino/icons/Save@3x.png
rename to ui/editor/icons/Save@3x.png
diff --git a/ui/arduino/icons/Stop.svg b/ui/editor/icons/Stop.svg
similarity index 100%
rename from ui/arduino/icons/Stop.svg
rename to ui/editor/icons/Stop.svg
diff --git a/ui/arduino/icons/Stop@3x.png b/ui/editor/icons/Stop@3x.png
similarity index 100%
rename from ui/arduino/icons/Stop@3x.png
rename to ui/editor/icons/Stop@3x.png
diff --git a/ui/arduino/index.html b/ui/editor/index.html
similarity index 100%
rename from ui/arduino/index.html
rename to ui/editor/index.html
diff --git a/ui/arduino/libs/FileSaver.js b/ui/editor/libs/FileSaver.js
similarity index 100%
rename from ui/arduino/libs/FileSaver.js
rename to ui/editor/libs/FileSaver.js
diff --git a/ui/arduino/libs/ace/ace.js b/ui/editor/libs/ace/ace.js
similarity index 100%
rename from ui/arduino/libs/ace/ace.js
rename to ui/editor/libs/ace/ace.js
diff --git a/ui/arduino/libs/ace/ext-beautify.js b/ui/editor/libs/ace/ext-beautify.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-beautify.js
rename to ui/editor/libs/ace/ext-beautify.js
diff --git a/ui/arduino/libs/ace/ext-code_lens.js b/ui/editor/libs/ace/ext-code_lens.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-code_lens.js
rename to ui/editor/libs/ace/ext-code_lens.js
diff --git a/ui/arduino/libs/ace/ext-elastic_tabstops_lite.js b/ui/editor/libs/ace/ext-elastic_tabstops_lite.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-elastic_tabstops_lite.js
rename to ui/editor/libs/ace/ext-elastic_tabstops_lite.js
diff --git a/ui/arduino/libs/ace/ext-emmet.js b/ui/editor/libs/ace/ext-emmet.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-emmet.js
rename to ui/editor/libs/ace/ext-emmet.js
diff --git a/ui/arduino/libs/ace/ext-error_marker.js b/ui/editor/libs/ace/ext-error_marker.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-error_marker.js
rename to ui/editor/libs/ace/ext-error_marker.js
diff --git a/ui/arduino/libs/ace/ext-keybinding_menu.js b/ui/editor/libs/ace/ext-keybinding_menu.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-keybinding_menu.js
rename to ui/editor/libs/ace/ext-keybinding_menu.js
diff --git a/ui/arduino/libs/ace/ext-language_tools.js b/ui/editor/libs/ace/ext-language_tools.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-language_tools.js
rename to ui/editor/libs/ace/ext-language_tools.js
diff --git a/ui/arduino/libs/ace/ext-linking.js b/ui/editor/libs/ace/ext-linking.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-linking.js
rename to ui/editor/libs/ace/ext-linking.js
diff --git a/ui/arduino/libs/ace/ext-modelist.js b/ui/editor/libs/ace/ext-modelist.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-modelist.js
rename to ui/editor/libs/ace/ext-modelist.js
diff --git a/ui/arduino/libs/ace/ext-options.js b/ui/editor/libs/ace/ext-options.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-options.js
rename to ui/editor/libs/ace/ext-options.js
diff --git a/ui/arduino/libs/ace/ext-prompt.js b/ui/editor/libs/ace/ext-prompt.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-prompt.js
rename to ui/editor/libs/ace/ext-prompt.js
diff --git a/ui/arduino/libs/ace/ext-rtl.js b/ui/editor/libs/ace/ext-rtl.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-rtl.js
rename to ui/editor/libs/ace/ext-rtl.js
diff --git a/ui/arduino/libs/ace/ext-searchbox.js b/ui/editor/libs/ace/ext-searchbox.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-searchbox.js
rename to ui/editor/libs/ace/ext-searchbox.js
diff --git a/ui/arduino/libs/ace/ext-settings_menu.js b/ui/editor/libs/ace/ext-settings_menu.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-settings_menu.js
rename to ui/editor/libs/ace/ext-settings_menu.js
diff --git a/ui/arduino/libs/ace/ext-spellcheck.js b/ui/editor/libs/ace/ext-spellcheck.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-spellcheck.js
rename to ui/editor/libs/ace/ext-spellcheck.js
diff --git a/ui/arduino/libs/ace/ext-split.js b/ui/editor/libs/ace/ext-split.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-split.js
rename to ui/editor/libs/ace/ext-split.js
diff --git a/ui/arduino/libs/ace/ext-static_highlight.js b/ui/editor/libs/ace/ext-static_highlight.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-static_highlight.js
rename to ui/editor/libs/ace/ext-static_highlight.js
diff --git a/ui/arduino/libs/ace/ext-statusbar.js b/ui/editor/libs/ace/ext-statusbar.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-statusbar.js
rename to ui/editor/libs/ace/ext-statusbar.js
diff --git a/ui/arduino/libs/ace/ext-textarea.js b/ui/editor/libs/ace/ext-textarea.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-textarea.js
rename to ui/editor/libs/ace/ext-textarea.js
diff --git a/ui/arduino/libs/ace/ext-themelist.js b/ui/editor/libs/ace/ext-themelist.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-themelist.js
rename to ui/editor/libs/ace/ext-themelist.js
diff --git a/ui/arduino/libs/ace/ext-whitespace.js b/ui/editor/libs/ace/ext-whitespace.js
similarity index 100%
rename from ui/arduino/libs/ace/ext-whitespace.js
rename to ui/editor/libs/ace/ext-whitespace.js
diff --git a/ui/arduino/libs/ace/keybinding-emacs.js b/ui/editor/libs/ace/keybinding-emacs.js
similarity index 100%
rename from ui/arduino/libs/ace/keybinding-emacs.js
rename to ui/editor/libs/ace/keybinding-emacs.js
diff --git a/ui/arduino/libs/ace/keybinding-sublime.js b/ui/editor/libs/ace/keybinding-sublime.js
similarity index 100%
rename from ui/arduino/libs/ace/keybinding-sublime.js
rename to ui/editor/libs/ace/keybinding-sublime.js
diff --git a/ui/arduino/libs/ace/keybinding-vim.js b/ui/editor/libs/ace/keybinding-vim.js
similarity index 100%
rename from ui/arduino/libs/ace/keybinding-vim.js
rename to ui/editor/libs/ace/keybinding-vim.js
diff --git a/ui/arduino/libs/ace/keybinding-vscode.js b/ui/editor/libs/ace/keybinding-vscode.js
similarity index 100%
rename from ui/arduino/libs/ace/keybinding-vscode.js
rename to ui/editor/libs/ace/keybinding-vscode.js
diff --git a/ui/arduino/libs/ace/mode-abap.js b/ui/editor/libs/ace/mode-abap.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-abap.js
rename to ui/editor/libs/ace/mode-abap.js
diff --git a/ui/arduino/libs/ace/mode-abc.js b/ui/editor/libs/ace/mode-abc.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-abc.js
rename to ui/editor/libs/ace/mode-abc.js
diff --git a/ui/arduino/libs/ace/mode-actionscript.js b/ui/editor/libs/ace/mode-actionscript.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-actionscript.js
rename to ui/editor/libs/ace/mode-actionscript.js
diff --git a/ui/arduino/libs/ace/mode-ada.js b/ui/editor/libs/ace/mode-ada.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-ada.js
rename to ui/editor/libs/ace/mode-ada.js
diff --git a/ui/arduino/libs/ace/mode-alda.js b/ui/editor/libs/ace/mode-alda.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-alda.js
rename to ui/editor/libs/ace/mode-alda.js
diff --git a/ui/arduino/libs/ace/mode-apache_conf.js b/ui/editor/libs/ace/mode-apache_conf.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-apache_conf.js
rename to ui/editor/libs/ace/mode-apache_conf.js
diff --git a/ui/arduino/libs/ace/mode-apex.js b/ui/editor/libs/ace/mode-apex.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-apex.js
rename to ui/editor/libs/ace/mode-apex.js
diff --git a/ui/arduino/libs/ace/mode-applescript.js b/ui/editor/libs/ace/mode-applescript.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-applescript.js
rename to ui/editor/libs/ace/mode-applescript.js
diff --git a/ui/arduino/libs/ace/mode-aql.js b/ui/editor/libs/ace/mode-aql.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-aql.js
rename to ui/editor/libs/ace/mode-aql.js
diff --git a/ui/arduino/libs/ace/mode-asciidoc.js b/ui/editor/libs/ace/mode-asciidoc.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-asciidoc.js
rename to ui/editor/libs/ace/mode-asciidoc.js
diff --git a/ui/arduino/libs/ace/mode-asl.js b/ui/editor/libs/ace/mode-asl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-asl.js
rename to ui/editor/libs/ace/mode-asl.js
diff --git a/ui/arduino/libs/ace/mode-assembly_x86.js b/ui/editor/libs/ace/mode-assembly_x86.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-assembly_x86.js
rename to ui/editor/libs/ace/mode-assembly_x86.js
diff --git a/ui/arduino/libs/ace/mode-autohotkey.js b/ui/editor/libs/ace/mode-autohotkey.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-autohotkey.js
rename to ui/editor/libs/ace/mode-autohotkey.js
diff --git a/ui/arduino/libs/ace/mode-batchfile.js b/ui/editor/libs/ace/mode-batchfile.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-batchfile.js
rename to ui/editor/libs/ace/mode-batchfile.js
diff --git a/ui/arduino/libs/ace/mode-c9search.js b/ui/editor/libs/ace/mode-c9search.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-c9search.js
rename to ui/editor/libs/ace/mode-c9search.js
diff --git a/ui/arduino/libs/ace/mode-c_cpp.js b/ui/editor/libs/ace/mode-c_cpp.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-c_cpp.js
rename to ui/editor/libs/ace/mode-c_cpp.js
diff --git a/ui/arduino/libs/ace/mode-cirru.js b/ui/editor/libs/ace/mode-cirru.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-cirru.js
rename to ui/editor/libs/ace/mode-cirru.js
diff --git a/ui/arduino/libs/ace/mode-clojure.js b/ui/editor/libs/ace/mode-clojure.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-clojure.js
rename to ui/editor/libs/ace/mode-clojure.js
diff --git a/ui/arduino/libs/ace/mode-cobol.js b/ui/editor/libs/ace/mode-cobol.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-cobol.js
rename to ui/editor/libs/ace/mode-cobol.js
diff --git a/ui/arduino/libs/ace/mode-coffee.js b/ui/editor/libs/ace/mode-coffee.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-coffee.js
rename to ui/editor/libs/ace/mode-coffee.js
diff --git a/ui/arduino/libs/ace/mode-coldfusion.js b/ui/editor/libs/ace/mode-coldfusion.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-coldfusion.js
rename to ui/editor/libs/ace/mode-coldfusion.js
diff --git a/ui/arduino/libs/ace/mode-crystal.js b/ui/editor/libs/ace/mode-crystal.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-crystal.js
rename to ui/editor/libs/ace/mode-crystal.js
diff --git a/ui/arduino/libs/ace/mode-csharp.js b/ui/editor/libs/ace/mode-csharp.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-csharp.js
rename to ui/editor/libs/ace/mode-csharp.js
diff --git a/ui/arduino/libs/ace/mode-csound_document.js b/ui/editor/libs/ace/mode-csound_document.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-csound_document.js
rename to ui/editor/libs/ace/mode-csound_document.js
diff --git a/ui/arduino/libs/ace/mode-csound_orchestra.js b/ui/editor/libs/ace/mode-csound_orchestra.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-csound_orchestra.js
rename to ui/editor/libs/ace/mode-csound_orchestra.js
diff --git a/ui/arduino/libs/ace/mode-csound_score.js b/ui/editor/libs/ace/mode-csound_score.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-csound_score.js
rename to ui/editor/libs/ace/mode-csound_score.js
diff --git a/ui/arduino/libs/ace/mode-csp.js b/ui/editor/libs/ace/mode-csp.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-csp.js
rename to ui/editor/libs/ace/mode-csp.js
diff --git a/ui/arduino/libs/ace/mode-css.js b/ui/editor/libs/ace/mode-css.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-css.js
rename to ui/editor/libs/ace/mode-css.js
diff --git a/ui/arduino/libs/ace/mode-curly.js b/ui/editor/libs/ace/mode-curly.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-curly.js
rename to ui/editor/libs/ace/mode-curly.js
diff --git a/ui/arduino/libs/ace/mode-d.js b/ui/editor/libs/ace/mode-d.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-d.js
rename to ui/editor/libs/ace/mode-d.js
diff --git a/ui/arduino/libs/ace/mode-dart.js b/ui/editor/libs/ace/mode-dart.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-dart.js
rename to ui/editor/libs/ace/mode-dart.js
diff --git a/ui/arduino/libs/ace/mode-diff.js b/ui/editor/libs/ace/mode-diff.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-diff.js
rename to ui/editor/libs/ace/mode-diff.js
diff --git a/ui/arduino/libs/ace/mode-django.js b/ui/editor/libs/ace/mode-django.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-django.js
rename to ui/editor/libs/ace/mode-django.js
diff --git a/ui/arduino/libs/ace/mode-dockerfile.js b/ui/editor/libs/ace/mode-dockerfile.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-dockerfile.js
rename to ui/editor/libs/ace/mode-dockerfile.js
diff --git a/ui/arduino/libs/ace/mode-dot.js b/ui/editor/libs/ace/mode-dot.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-dot.js
rename to ui/editor/libs/ace/mode-dot.js
diff --git a/ui/arduino/libs/ace/mode-drools.js b/ui/editor/libs/ace/mode-drools.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-drools.js
rename to ui/editor/libs/ace/mode-drools.js
diff --git a/ui/arduino/libs/ace/mode-edifact.js b/ui/editor/libs/ace/mode-edifact.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-edifact.js
rename to ui/editor/libs/ace/mode-edifact.js
diff --git a/ui/arduino/libs/ace/mode-eiffel.js b/ui/editor/libs/ace/mode-eiffel.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-eiffel.js
rename to ui/editor/libs/ace/mode-eiffel.js
diff --git a/ui/arduino/libs/ace/mode-ejs.js b/ui/editor/libs/ace/mode-ejs.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-ejs.js
rename to ui/editor/libs/ace/mode-ejs.js
diff --git a/ui/arduino/libs/ace/mode-elixir.js b/ui/editor/libs/ace/mode-elixir.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-elixir.js
rename to ui/editor/libs/ace/mode-elixir.js
diff --git a/ui/arduino/libs/ace/mode-elm.js b/ui/editor/libs/ace/mode-elm.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-elm.js
rename to ui/editor/libs/ace/mode-elm.js
diff --git a/ui/arduino/libs/ace/mode-erlang.js b/ui/editor/libs/ace/mode-erlang.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-erlang.js
rename to ui/editor/libs/ace/mode-erlang.js
diff --git a/ui/arduino/libs/ace/mode-forth.js b/ui/editor/libs/ace/mode-forth.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-forth.js
rename to ui/editor/libs/ace/mode-forth.js
diff --git a/ui/arduino/libs/ace/mode-fortran.js b/ui/editor/libs/ace/mode-fortran.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-fortran.js
rename to ui/editor/libs/ace/mode-fortran.js
diff --git a/ui/arduino/libs/ace/mode-fsharp.js b/ui/editor/libs/ace/mode-fsharp.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-fsharp.js
rename to ui/editor/libs/ace/mode-fsharp.js
diff --git a/ui/arduino/libs/ace/mode-fsl.js b/ui/editor/libs/ace/mode-fsl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-fsl.js
rename to ui/editor/libs/ace/mode-fsl.js
diff --git a/ui/arduino/libs/ace/mode-ftl.js b/ui/editor/libs/ace/mode-ftl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-ftl.js
rename to ui/editor/libs/ace/mode-ftl.js
diff --git a/ui/arduino/libs/ace/mode-gcode.js b/ui/editor/libs/ace/mode-gcode.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-gcode.js
rename to ui/editor/libs/ace/mode-gcode.js
diff --git a/ui/arduino/libs/ace/mode-gherkin.js b/ui/editor/libs/ace/mode-gherkin.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-gherkin.js
rename to ui/editor/libs/ace/mode-gherkin.js
diff --git a/ui/arduino/libs/ace/mode-gitignore.js b/ui/editor/libs/ace/mode-gitignore.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-gitignore.js
rename to ui/editor/libs/ace/mode-gitignore.js
diff --git a/ui/arduino/libs/ace/mode-glsl.js b/ui/editor/libs/ace/mode-glsl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-glsl.js
rename to ui/editor/libs/ace/mode-glsl.js
diff --git a/ui/arduino/libs/ace/mode-gobstones.js b/ui/editor/libs/ace/mode-gobstones.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-gobstones.js
rename to ui/editor/libs/ace/mode-gobstones.js
diff --git a/ui/arduino/libs/ace/mode-golang.js b/ui/editor/libs/ace/mode-golang.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-golang.js
rename to ui/editor/libs/ace/mode-golang.js
diff --git a/ui/arduino/libs/ace/mode-graphqlschema.js b/ui/editor/libs/ace/mode-graphqlschema.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-graphqlschema.js
rename to ui/editor/libs/ace/mode-graphqlschema.js
diff --git a/ui/arduino/libs/ace/mode-groovy.js b/ui/editor/libs/ace/mode-groovy.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-groovy.js
rename to ui/editor/libs/ace/mode-groovy.js
diff --git a/ui/arduino/libs/ace/mode-haml.js b/ui/editor/libs/ace/mode-haml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-haml.js
rename to ui/editor/libs/ace/mode-haml.js
diff --git a/ui/arduino/libs/ace/mode-handlebars.js b/ui/editor/libs/ace/mode-handlebars.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-handlebars.js
rename to ui/editor/libs/ace/mode-handlebars.js
diff --git a/ui/arduino/libs/ace/mode-haskell.js b/ui/editor/libs/ace/mode-haskell.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-haskell.js
rename to ui/editor/libs/ace/mode-haskell.js
diff --git a/ui/arduino/libs/ace/mode-haskell_cabal.js b/ui/editor/libs/ace/mode-haskell_cabal.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-haskell_cabal.js
rename to ui/editor/libs/ace/mode-haskell_cabal.js
diff --git a/ui/arduino/libs/ace/mode-haxe.js b/ui/editor/libs/ace/mode-haxe.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-haxe.js
rename to ui/editor/libs/ace/mode-haxe.js
diff --git a/ui/arduino/libs/ace/mode-hjson.js b/ui/editor/libs/ace/mode-hjson.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-hjson.js
rename to ui/editor/libs/ace/mode-hjson.js
diff --git a/ui/arduino/libs/ace/mode-html.js b/ui/editor/libs/ace/mode-html.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-html.js
rename to ui/editor/libs/ace/mode-html.js
diff --git a/ui/arduino/libs/ace/mode-html_elixir.js b/ui/editor/libs/ace/mode-html_elixir.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-html_elixir.js
rename to ui/editor/libs/ace/mode-html_elixir.js
diff --git a/ui/arduino/libs/ace/mode-html_ruby.js b/ui/editor/libs/ace/mode-html_ruby.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-html_ruby.js
rename to ui/editor/libs/ace/mode-html_ruby.js
diff --git a/ui/arduino/libs/ace/mode-ini.js b/ui/editor/libs/ace/mode-ini.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-ini.js
rename to ui/editor/libs/ace/mode-ini.js
diff --git a/ui/arduino/libs/ace/mode-io.js b/ui/editor/libs/ace/mode-io.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-io.js
rename to ui/editor/libs/ace/mode-io.js
diff --git a/ui/arduino/libs/ace/mode-jack.js b/ui/editor/libs/ace/mode-jack.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-jack.js
rename to ui/editor/libs/ace/mode-jack.js
diff --git a/ui/arduino/libs/ace/mode-jade.js b/ui/editor/libs/ace/mode-jade.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-jade.js
rename to ui/editor/libs/ace/mode-jade.js
diff --git a/ui/arduino/libs/ace/mode-java.js b/ui/editor/libs/ace/mode-java.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-java.js
rename to ui/editor/libs/ace/mode-java.js
diff --git a/ui/arduino/libs/ace/mode-javascript.js b/ui/editor/libs/ace/mode-javascript.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-javascript.js
rename to ui/editor/libs/ace/mode-javascript.js
diff --git a/ui/arduino/libs/ace/mode-json.js b/ui/editor/libs/ace/mode-json.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-json.js
rename to ui/editor/libs/ace/mode-json.js
diff --git a/ui/arduino/libs/ace/mode-json5.js b/ui/editor/libs/ace/mode-json5.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-json5.js
rename to ui/editor/libs/ace/mode-json5.js
diff --git a/ui/arduino/libs/ace/mode-jsoniq.js b/ui/editor/libs/ace/mode-jsoniq.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-jsoniq.js
rename to ui/editor/libs/ace/mode-jsoniq.js
diff --git a/ui/arduino/libs/ace/mode-jsp.js b/ui/editor/libs/ace/mode-jsp.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-jsp.js
rename to ui/editor/libs/ace/mode-jsp.js
diff --git a/ui/arduino/libs/ace/mode-jssm.js b/ui/editor/libs/ace/mode-jssm.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-jssm.js
rename to ui/editor/libs/ace/mode-jssm.js
diff --git a/ui/arduino/libs/ace/mode-jsx.js b/ui/editor/libs/ace/mode-jsx.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-jsx.js
rename to ui/editor/libs/ace/mode-jsx.js
diff --git a/ui/arduino/libs/ace/mode-julia.js b/ui/editor/libs/ace/mode-julia.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-julia.js
rename to ui/editor/libs/ace/mode-julia.js
diff --git a/ui/arduino/libs/ace/mode-kotlin.js b/ui/editor/libs/ace/mode-kotlin.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-kotlin.js
rename to ui/editor/libs/ace/mode-kotlin.js
diff --git a/ui/arduino/libs/ace/mode-latex.js b/ui/editor/libs/ace/mode-latex.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-latex.js
rename to ui/editor/libs/ace/mode-latex.js
diff --git a/ui/arduino/libs/ace/mode-less.js b/ui/editor/libs/ace/mode-less.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-less.js
rename to ui/editor/libs/ace/mode-less.js
diff --git a/ui/arduino/libs/ace/mode-liquid.js b/ui/editor/libs/ace/mode-liquid.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-liquid.js
rename to ui/editor/libs/ace/mode-liquid.js
diff --git a/ui/arduino/libs/ace/mode-lisp.js b/ui/editor/libs/ace/mode-lisp.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-lisp.js
rename to ui/editor/libs/ace/mode-lisp.js
diff --git a/ui/arduino/libs/ace/mode-livescript.js b/ui/editor/libs/ace/mode-livescript.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-livescript.js
rename to ui/editor/libs/ace/mode-livescript.js
diff --git a/ui/arduino/libs/ace/mode-logiql.js b/ui/editor/libs/ace/mode-logiql.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-logiql.js
rename to ui/editor/libs/ace/mode-logiql.js
diff --git a/ui/arduino/libs/ace/mode-logtalk.js b/ui/editor/libs/ace/mode-logtalk.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-logtalk.js
rename to ui/editor/libs/ace/mode-logtalk.js
diff --git a/ui/arduino/libs/ace/mode-lsl.js b/ui/editor/libs/ace/mode-lsl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-lsl.js
rename to ui/editor/libs/ace/mode-lsl.js
diff --git a/ui/arduino/libs/ace/mode-lua.js b/ui/editor/libs/ace/mode-lua.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-lua.js
rename to ui/editor/libs/ace/mode-lua.js
diff --git a/ui/arduino/libs/ace/mode-luapage.js b/ui/editor/libs/ace/mode-luapage.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-luapage.js
rename to ui/editor/libs/ace/mode-luapage.js
diff --git a/ui/arduino/libs/ace/mode-lucene.js b/ui/editor/libs/ace/mode-lucene.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-lucene.js
rename to ui/editor/libs/ace/mode-lucene.js
diff --git a/ui/arduino/libs/ace/mode-makefile.js b/ui/editor/libs/ace/mode-makefile.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-makefile.js
rename to ui/editor/libs/ace/mode-makefile.js
diff --git a/ui/arduino/libs/ace/mode-markdown.js b/ui/editor/libs/ace/mode-markdown.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-markdown.js
rename to ui/editor/libs/ace/mode-markdown.js
diff --git a/ui/arduino/libs/ace/mode-mask.js b/ui/editor/libs/ace/mode-mask.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-mask.js
rename to ui/editor/libs/ace/mode-mask.js
diff --git a/ui/arduino/libs/ace/mode-matlab.js b/ui/editor/libs/ace/mode-matlab.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-matlab.js
rename to ui/editor/libs/ace/mode-matlab.js
diff --git a/ui/arduino/libs/ace/mode-maze.js b/ui/editor/libs/ace/mode-maze.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-maze.js
rename to ui/editor/libs/ace/mode-maze.js
diff --git a/ui/arduino/libs/ace/mode-mediawiki.js b/ui/editor/libs/ace/mode-mediawiki.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-mediawiki.js
rename to ui/editor/libs/ace/mode-mediawiki.js
diff --git a/ui/arduino/libs/ace/mode-mel.js b/ui/editor/libs/ace/mode-mel.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-mel.js
rename to ui/editor/libs/ace/mode-mel.js
diff --git a/ui/arduino/libs/ace/mode-mixal.js b/ui/editor/libs/ace/mode-mixal.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-mixal.js
rename to ui/editor/libs/ace/mode-mixal.js
diff --git a/ui/arduino/libs/ace/mode-mushcode.js b/ui/editor/libs/ace/mode-mushcode.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-mushcode.js
rename to ui/editor/libs/ace/mode-mushcode.js
diff --git a/ui/arduino/libs/ace/mode-mysql.js b/ui/editor/libs/ace/mode-mysql.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-mysql.js
rename to ui/editor/libs/ace/mode-mysql.js
diff --git a/ui/arduino/libs/ace/mode-nginx.js b/ui/editor/libs/ace/mode-nginx.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-nginx.js
rename to ui/editor/libs/ace/mode-nginx.js
diff --git a/ui/arduino/libs/ace/mode-nim.js b/ui/editor/libs/ace/mode-nim.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-nim.js
rename to ui/editor/libs/ace/mode-nim.js
diff --git a/ui/arduino/libs/ace/mode-nix.js b/ui/editor/libs/ace/mode-nix.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-nix.js
rename to ui/editor/libs/ace/mode-nix.js
diff --git a/ui/arduino/libs/ace/mode-nsis.js b/ui/editor/libs/ace/mode-nsis.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-nsis.js
rename to ui/editor/libs/ace/mode-nsis.js
diff --git a/ui/arduino/libs/ace/mode-nunjucks.js b/ui/editor/libs/ace/mode-nunjucks.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-nunjucks.js
rename to ui/editor/libs/ace/mode-nunjucks.js
diff --git a/ui/arduino/libs/ace/mode-objectivec.js b/ui/editor/libs/ace/mode-objectivec.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-objectivec.js
rename to ui/editor/libs/ace/mode-objectivec.js
diff --git a/ui/arduino/libs/ace/mode-ocaml.js b/ui/editor/libs/ace/mode-ocaml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-ocaml.js
rename to ui/editor/libs/ace/mode-ocaml.js
diff --git a/ui/arduino/libs/ace/mode-pascal.js b/ui/editor/libs/ace/mode-pascal.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-pascal.js
rename to ui/editor/libs/ace/mode-pascal.js
diff --git a/ui/arduino/libs/ace/mode-perl.js b/ui/editor/libs/ace/mode-perl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-perl.js
rename to ui/editor/libs/ace/mode-perl.js
diff --git a/ui/arduino/libs/ace/mode-perl6.js b/ui/editor/libs/ace/mode-perl6.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-perl6.js
rename to ui/editor/libs/ace/mode-perl6.js
diff --git a/ui/arduino/libs/ace/mode-pgsql.js b/ui/editor/libs/ace/mode-pgsql.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-pgsql.js
rename to ui/editor/libs/ace/mode-pgsql.js
diff --git a/ui/arduino/libs/ace/mode-php.js b/ui/editor/libs/ace/mode-php.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-php.js
rename to ui/editor/libs/ace/mode-php.js
diff --git a/ui/arduino/libs/ace/mode-php_laravel_blade.js b/ui/editor/libs/ace/mode-php_laravel_blade.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-php_laravel_blade.js
rename to ui/editor/libs/ace/mode-php_laravel_blade.js
diff --git a/ui/arduino/libs/ace/mode-pig.js b/ui/editor/libs/ace/mode-pig.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-pig.js
rename to ui/editor/libs/ace/mode-pig.js
diff --git a/ui/arduino/libs/ace/mode-plain_text.js b/ui/editor/libs/ace/mode-plain_text.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-plain_text.js
rename to ui/editor/libs/ace/mode-plain_text.js
diff --git a/ui/arduino/libs/ace/mode-powershell.js b/ui/editor/libs/ace/mode-powershell.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-powershell.js
rename to ui/editor/libs/ace/mode-powershell.js
diff --git a/ui/arduino/libs/ace/mode-praat.js b/ui/editor/libs/ace/mode-praat.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-praat.js
rename to ui/editor/libs/ace/mode-praat.js
diff --git a/ui/arduino/libs/ace/mode-prisma.js b/ui/editor/libs/ace/mode-prisma.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-prisma.js
rename to ui/editor/libs/ace/mode-prisma.js
diff --git a/ui/arduino/libs/ace/mode-prolog.js b/ui/editor/libs/ace/mode-prolog.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-prolog.js
rename to ui/editor/libs/ace/mode-prolog.js
diff --git a/ui/arduino/libs/ace/mode-properties.js b/ui/editor/libs/ace/mode-properties.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-properties.js
rename to ui/editor/libs/ace/mode-properties.js
diff --git a/ui/arduino/libs/ace/mode-protobuf.js b/ui/editor/libs/ace/mode-protobuf.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-protobuf.js
rename to ui/editor/libs/ace/mode-protobuf.js
diff --git a/ui/arduino/libs/ace/mode-puppet.js b/ui/editor/libs/ace/mode-puppet.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-puppet.js
rename to ui/editor/libs/ace/mode-puppet.js
diff --git a/ui/arduino/libs/ace/mode-python.js b/ui/editor/libs/ace/mode-python.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-python.js
rename to ui/editor/libs/ace/mode-python.js
diff --git a/ui/arduino/libs/ace/mode-qml.js b/ui/editor/libs/ace/mode-qml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-qml.js
rename to ui/editor/libs/ace/mode-qml.js
diff --git a/ui/arduino/libs/ace/mode-r.js b/ui/editor/libs/ace/mode-r.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-r.js
rename to ui/editor/libs/ace/mode-r.js
diff --git a/ui/arduino/libs/ace/mode-razor.js b/ui/editor/libs/ace/mode-razor.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-razor.js
rename to ui/editor/libs/ace/mode-razor.js
diff --git a/ui/arduino/libs/ace/mode-rdoc.js b/ui/editor/libs/ace/mode-rdoc.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-rdoc.js
rename to ui/editor/libs/ace/mode-rdoc.js
diff --git a/ui/arduino/libs/ace/mode-red.js b/ui/editor/libs/ace/mode-red.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-red.js
rename to ui/editor/libs/ace/mode-red.js
diff --git a/ui/arduino/libs/ace/mode-redshift.js b/ui/editor/libs/ace/mode-redshift.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-redshift.js
rename to ui/editor/libs/ace/mode-redshift.js
diff --git a/ui/arduino/libs/ace/mode-rhtml.js b/ui/editor/libs/ace/mode-rhtml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-rhtml.js
rename to ui/editor/libs/ace/mode-rhtml.js
diff --git a/ui/arduino/libs/ace/mode-rst.js b/ui/editor/libs/ace/mode-rst.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-rst.js
rename to ui/editor/libs/ace/mode-rst.js
diff --git a/ui/arduino/libs/ace/mode-ruby.js b/ui/editor/libs/ace/mode-ruby.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-ruby.js
rename to ui/editor/libs/ace/mode-ruby.js
diff --git a/ui/arduino/libs/ace/mode-rust.js b/ui/editor/libs/ace/mode-rust.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-rust.js
rename to ui/editor/libs/ace/mode-rust.js
diff --git a/ui/arduino/libs/ace/mode-sass.js b/ui/editor/libs/ace/mode-sass.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-sass.js
rename to ui/editor/libs/ace/mode-sass.js
diff --git a/ui/arduino/libs/ace/mode-scad.js b/ui/editor/libs/ace/mode-scad.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-scad.js
rename to ui/editor/libs/ace/mode-scad.js
diff --git a/ui/arduino/libs/ace/mode-scala.js b/ui/editor/libs/ace/mode-scala.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-scala.js
rename to ui/editor/libs/ace/mode-scala.js
diff --git a/ui/arduino/libs/ace/mode-scheme.js b/ui/editor/libs/ace/mode-scheme.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-scheme.js
rename to ui/editor/libs/ace/mode-scheme.js
diff --git a/ui/arduino/libs/ace/mode-scss.js b/ui/editor/libs/ace/mode-scss.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-scss.js
rename to ui/editor/libs/ace/mode-scss.js
diff --git a/ui/arduino/libs/ace/mode-sh.js b/ui/editor/libs/ace/mode-sh.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-sh.js
rename to ui/editor/libs/ace/mode-sh.js
diff --git a/ui/arduino/libs/ace/mode-sjs.js b/ui/editor/libs/ace/mode-sjs.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-sjs.js
rename to ui/editor/libs/ace/mode-sjs.js
diff --git a/ui/arduino/libs/ace/mode-slim.js b/ui/editor/libs/ace/mode-slim.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-slim.js
rename to ui/editor/libs/ace/mode-slim.js
diff --git a/ui/arduino/libs/ace/mode-smarty.js b/ui/editor/libs/ace/mode-smarty.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-smarty.js
rename to ui/editor/libs/ace/mode-smarty.js
diff --git a/ui/arduino/libs/ace/mode-snippets.js b/ui/editor/libs/ace/mode-snippets.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-snippets.js
rename to ui/editor/libs/ace/mode-snippets.js
diff --git a/ui/arduino/libs/ace/mode-soy_template.js b/ui/editor/libs/ace/mode-soy_template.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-soy_template.js
rename to ui/editor/libs/ace/mode-soy_template.js
diff --git a/ui/arduino/libs/ace/mode-space.js b/ui/editor/libs/ace/mode-space.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-space.js
rename to ui/editor/libs/ace/mode-space.js
diff --git a/ui/arduino/libs/ace/mode-sparql.js b/ui/editor/libs/ace/mode-sparql.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-sparql.js
rename to ui/editor/libs/ace/mode-sparql.js
diff --git a/ui/arduino/libs/ace/mode-sql.js b/ui/editor/libs/ace/mode-sql.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-sql.js
rename to ui/editor/libs/ace/mode-sql.js
diff --git a/ui/arduino/libs/ace/mode-sqlserver.js b/ui/editor/libs/ace/mode-sqlserver.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-sqlserver.js
rename to ui/editor/libs/ace/mode-sqlserver.js
diff --git a/ui/arduino/libs/ace/mode-stylus.js b/ui/editor/libs/ace/mode-stylus.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-stylus.js
rename to ui/editor/libs/ace/mode-stylus.js
diff --git a/ui/arduino/libs/ace/mode-svg.js b/ui/editor/libs/ace/mode-svg.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-svg.js
rename to ui/editor/libs/ace/mode-svg.js
diff --git a/ui/arduino/libs/ace/mode-swift.js b/ui/editor/libs/ace/mode-swift.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-swift.js
rename to ui/editor/libs/ace/mode-swift.js
diff --git a/ui/arduino/libs/ace/mode-tcl.js b/ui/editor/libs/ace/mode-tcl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-tcl.js
rename to ui/editor/libs/ace/mode-tcl.js
diff --git a/ui/arduino/libs/ace/mode-terraform.js b/ui/editor/libs/ace/mode-terraform.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-terraform.js
rename to ui/editor/libs/ace/mode-terraform.js
diff --git a/ui/arduino/libs/ace/mode-tex.js b/ui/editor/libs/ace/mode-tex.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-tex.js
rename to ui/editor/libs/ace/mode-tex.js
diff --git a/ui/arduino/libs/ace/mode-text.js b/ui/editor/libs/ace/mode-text.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-text.js
rename to ui/editor/libs/ace/mode-text.js
diff --git a/ui/arduino/libs/ace/mode-textile.js b/ui/editor/libs/ace/mode-textile.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-textile.js
rename to ui/editor/libs/ace/mode-textile.js
diff --git a/ui/arduino/libs/ace/mode-toml.js b/ui/editor/libs/ace/mode-toml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-toml.js
rename to ui/editor/libs/ace/mode-toml.js
diff --git a/ui/arduino/libs/ace/mode-tsx.js b/ui/editor/libs/ace/mode-tsx.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-tsx.js
rename to ui/editor/libs/ace/mode-tsx.js
diff --git a/ui/arduino/libs/ace/mode-turtle.js b/ui/editor/libs/ace/mode-turtle.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-turtle.js
rename to ui/editor/libs/ace/mode-turtle.js
diff --git a/ui/arduino/libs/ace/mode-twig.js b/ui/editor/libs/ace/mode-twig.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-twig.js
rename to ui/editor/libs/ace/mode-twig.js
diff --git a/ui/arduino/libs/ace/mode-typescript.js b/ui/editor/libs/ace/mode-typescript.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-typescript.js
rename to ui/editor/libs/ace/mode-typescript.js
diff --git a/ui/arduino/libs/ace/mode-vala.js b/ui/editor/libs/ace/mode-vala.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-vala.js
rename to ui/editor/libs/ace/mode-vala.js
diff --git a/ui/arduino/libs/ace/mode-vbscript.js b/ui/editor/libs/ace/mode-vbscript.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-vbscript.js
rename to ui/editor/libs/ace/mode-vbscript.js
diff --git a/ui/arduino/libs/ace/mode-velocity.js b/ui/editor/libs/ace/mode-velocity.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-velocity.js
rename to ui/editor/libs/ace/mode-velocity.js
diff --git a/ui/arduino/libs/ace/mode-verilog.js b/ui/editor/libs/ace/mode-verilog.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-verilog.js
rename to ui/editor/libs/ace/mode-verilog.js
diff --git a/ui/arduino/libs/ace/mode-vhdl.js b/ui/editor/libs/ace/mode-vhdl.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-vhdl.js
rename to ui/editor/libs/ace/mode-vhdl.js
diff --git a/ui/arduino/libs/ace/mode-visualforce.js b/ui/editor/libs/ace/mode-visualforce.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-visualforce.js
rename to ui/editor/libs/ace/mode-visualforce.js
diff --git a/ui/arduino/libs/ace/mode-wollok.js b/ui/editor/libs/ace/mode-wollok.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-wollok.js
rename to ui/editor/libs/ace/mode-wollok.js
diff --git a/ui/arduino/libs/ace/mode-xml.js b/ui/editor/libs/ace/mode-xml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-xml.js
rename to ui/editor/libs/ace/mode-xml.js
diff --git a/ui/arduino/libs/ace/mode-xquery.js b/ui/editor/libs/ace/mode-xquery.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-xquery.js
rename to ui/editor/libs/ace/mode-xquery.js
diff --git a/ui/arduino/libs/ace/mode-yaml.js b/ui/editor/libs/ace/mode-yaml.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-yaml.js
rename to ui/editor/libs/ace/mode-yaml.js
diff --git a/ui/arduino/libs/ace/mode-zeek.js b/ui/editor/libs/ace/mode-zeek.js
similarity index 100%
rename from ui/arduino/libs/ace/mode-zeek.js
rename to ui/editor/libs/ace/mode-zeek.js
diff --git a/ui/arduino/libs/ace/snippets/abap.js b/ui/editor/libs/ace/snippets/abap.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/abap.js
rename to ui/editor/libs/ace/snippets/abap.js
diff --git a/ui/arduino/libs/ace/snippets/abc.js b/ui/editor/libs/ace/snippets/abc.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/abc.js
rename to ui/editor/libs/ace/snippets/abc.js
diff --git a/ui/arduino/libs/ace/snippets/actionscript.js b/ui/editor/libs/ace/snippets/actionscript.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/actionscript.js
rename to ui/editor/libs/ace/snippets/actionscript.js
diff --git a/ui/arduino/libs/ace/snippets/ada.js b/ui/editor/libs/ace/snippets/ada.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/ada.js
rename to ui/editor/libs/ace/snippets/ada.js
diff --git a/ui/arduino/libs/ace/snippets/alda.js b/ui/editor/libs/ace/snippets/alda.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/alda.js
rename to ui/editor/libs/ace/snippets/alda.js
diff --git a/ui/arduino/libs/ace/snippets/apache_conf.js b/ui/editor/libs/ace/snippets/apache_conf.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/apache_conf.js
rename to ui/editor/libs/ace/snippets/apache_conf.js
diff --git a/ui/arduino/libs/ace/snippets/apex.js b/ui/editor/libs/ace/snippets/apex.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/apex.js
rename to ui/editor/libs/ace/snippets/apex.js
diff --git a/ui/arduino/libs/ace/snippets/applescript.js b/ui/editor/libs/ace/snippets/applescript.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/applescript.js
rename to ui/editor/libs/ace/snippets/applescript.js
diff --git a/ui/arduino/libs/ace/snippets/aql.js b/ui/editor/libs/ace/snippets/aql.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/aql.js
rename to ui/editor/libs/ace/snippets/aql.js
diff --git a/ui/arduino/libs/ace/snippets/asciidoc.js b/ui/editor/libs/ace/snippets/asciidoc.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/asciidoc.js
rename to ui/editor/libs/ace/snippets/asciidoc.js
diff --git a/ui/arduino/libs/ace/snippets/asl.js b/ui/editor/libs/ace/snippets/asl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/asl.js
rename to ui/editor/libs/ace/snippets/asl.js
diff --git a/ui/arduino/libs/ace/snippets/assembly_x86.js b/ui/editor/libs/ace/snippets/assembly_x86.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/assembly_x86.js
rename to ui/editor/libs/ace/snippets/assembly_x86.js
diff --git a/ui/arduino/libs/ace/snippets/autohotkey.js b/ui/editor/libs/ace/snippets/autohotkey.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/autohotkey.js
rename to ui/editor/libs/ace/snippets/autohotkey.js
diff --git a/ui/arduino/libs/ace/snippets/batchfile.js b/ui/editor/libs/ace/snippets/batchfile.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/batchfile.js
rename to ui/editor/libs/ace/snippets/batchfile.js
diff --git a/ui/arduino/libs/ace/snippets/c9search.js b/ui/editor/libs/ace/snippets/c9search.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/c9search.js
rename to ui/editor/libs/ace/snippets/c9search.js
diff --git a/ui/arduino/libs/ace/snippets/c_cpp.js b/ui/editor/libs/ace/snippets/c_cpp.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/c_cpp.js
rename to ui/editor/libs/ace/snippets/c_cpp.js
diff --git a/ui/arduino/libs/ace/snippets/cirru.js b/ui/editor/libs/ace/snippets/cirru.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/cirru.js
rename to ui/editor/libs/ace/snippets/cirru.js
diff --git a/ui/arduino/libs/ace/snippets/clojure.js b/ui/editor/libs/ace/snippets/clojure.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/clojure.js
rename to ui/editor/libs/ace/snippets/clojure.js
diff --git a/ui/arduino/libs/ace/snippets/cobol.js b/ui/editor/libs/ace/snippets/cobol.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/cobol.js
rename to ui/editor/libs/ace/snippets/cobol.js
diff --git a/ui/arduino/libs/ace/snippets/coffee.js b/ui/editor/libs/ace/snippets/coffee.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/coffee.js
rename to ui/editor/libs/ace/snippets/coffee.js
diff --git a/ui/arduino/libs/ace/snippets/coldfusion.js b/ui/editor/libs/ace/snippets/coldfusion.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/coldfusion.js
rename to ui/editor/libs/ace/snippets/coldfusion.js
diff --git a/ui/arduino/libs/ace/snippets/crystal.js b/ui/editor/libs/ace/snippets/crystal.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/crystal.js
rename to ui/editor/libs/ace/snippets/crystal.js
diff --git a/ui/arduino/libs/ace/snippets/csharp.js b/ui/editor/libs/ace/snippets/csharp.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/csharp.js
rename to ui/editor/libs/ace/snippets/csharp.js
diff --git a/ui/arduino/libs/ace/snippets/csound_document.js b/ui/editor/libs/ace/snippets/csound_document.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/csound_document.js
rename to ui/editor/libs/ace/snippets/csound_document.js
diff --git a/ui/arduino/libs/ace/snippets/csound_orchestra.js b/ui/editor/libs/ace/snippets/csound_orchestra.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/csound_orchestra.js
rename to ui/editor/libs/ace/snippets/csound_orchestra.js
diff --git a/ui/arduino/libs/ace/snippets/csound_score.js b/ui/editor/libs/ace/snippets/csound_score.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/csound_score.js
rename to ui/editor/libs/ace/snippets/csound_score.js
diff --git a/ui/arduino/libs/ace/snippets/csp.js b/ui/editor/libs/ace/snippets/csp.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/csp.js
rename to ui/editor/libs/ace/snippets/csp.js
diff --git a/ui/arduino/libs/ace/snippets/css.js b/ui/editor/libs/ace/snippets/css.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/css.js
rename to ui/editor/libs/ace/snippets/css.js
diff --git a/ui/arduino/libs/ace/snippets/curly.js b/ui/editor/libs/ace/snippets/curly.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/curly.js
rename to ui/editor/libs/ace/snippets/curly.js
diff --git a/ui/arduino/libs/ace/snippets/d.js b/ui/editor/libs/ace/snippets/d.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/d.js
rename to ui/editor/libs/ace/snippets/d.js
diff --git a/ui/arduino/libs/ace/snippets/dart.js b/ui/editor/libs/ace/snippets/dart.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/dart.js
rename to ui/editor/libs/ace/snippets/dart.js
diff --git a/ui/arduino/libs/ace/snippets/diff.js b/ui/editor/libs/ace/snippets/diff.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/diff.js
rename to ui/editor/libs/ace/snippets/diff.js
diff --git a/ui/arduino/libs/ace/snippets/django.js b/ui/editor/libs/ace/snippets/django.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/django.js
rename to ui/editor/libs/ace/snippets/django.js
diff --git a/ui/arduino/libs/ace/snippets/dockerfile.js b/ui/editor/libs/ace/snippets/dockerfile.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/dockerfile.js
rename to ui/editor/libs/ace/snippets/dockerfile.js
diff --git a/ui/arduino/libs/ace/snippets/dot.js b/ui/editor/libs/ace/snippets/dot.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/dot.js
rename to ui/editor/libs/ace/snippets/dot.js
diff --git a/ui/arduino/libs/ace/snippets/drools.js b/ui/editor/libs/ace/snippets/drools.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/drools.js
rename to ui/editor/libs/ace/snippets/drools.js
diff --git a/ui/arduino/libs/ace/snippets/edifact.js b/ui/editor/libs/ace/snippets/edifact.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/edifact.js
rename to ui/editor/libs/ace/snippets/edifact.js
diff --git a/ui/arduino/libs/ace/snippets/eiffel.js b/ui/editor/libs/ace/snippets/eiffel.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/eiffel.js
rename to ui/editor/libs/ace/snippets/eiffel.js
diff --git a/ui/arduino/libs/ace/snippets/ejs.js b/ui/editor/libs/ace/snippets/ejs.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/ejs.js
rename to ui/editor/libs/ace/snippets/ejs.js
diff --git a/ui/arduino/libs/ace/snippets/elixir.js b/ui/editor/libs/ace/snippets/elixir.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/elixir.js
rename to ui/editor/libs/ace/snippets/elixir.js
diff --git a/ui/arduino/libs/ace/snippets/elm.js b/ui/editor/libs/ace/snippets/elm.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/elm.js
rename to ui/editor/libs/ace/snippets/elm.js
diff --git a/ui/arduino/libs/ace/snippets/erlang.js b/ui/editor/libs/ace/snippets/erlang.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/erlang.js
rename to ui/editor/libs/ace/snippets/erlang.js
diff --git a/ui/arduino/libs/ace/snippets/forth.js b/ui/editor/libs/ace/snippets/forth.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/forth.js
rename to ui/editor/libs/ace/snippets/forth.js
diff --git a/ui/arduino/libs/ace/snippets/fortran.js b/ui/editor/libs/ace/snippets/fortran.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/fortran.js
rename to ui/editor/libs/ace/snippets/fortran.js
diff --git a/ui/arduino/libs/ace/snippets/fsharp.js b/ui/editor/libs/ace/snippets/fsharp.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/fsharp.js
rename to ui/editor/libs/ace/snippets/fsharp.js
diff --git a/ui/arduino/libs/ace/snippets/fsl.js b/ui/editor/libs/ace/snippets/fsl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/fsl.js
rename to ui/editor/libs/ace/snippets/fsl.js
diff --git a/ui/arduino/libs/ace/snippets/ftl.js b/ui/editor/libs/ace/snippets/ftl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/ftl.js
rename to ui/editor/libs/ace/snippets/ftl.js
diff --git a/ui/arduino/libs/ace/snippets/gcode.js b/ui/editor/libs/ace/snippets/gcode.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/gcode.js
rename to ui/editor/libs/ace/snippets/gcode.js
diff --git a/ui/arduino/libs/ace/snippets/gherkin.js b/ui/editor/libs/ace/snippets/gherkin.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/gherkin.js
rename to ui/editor/libs/ace/snippets/gherkin.js
diff --git a/ui/arduino/libs/ace/snippets/gitignore.js b/ui/editor/libs/ace/snippets/gitignore.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/gitignore.js
rename to ui/editor/libs/ace/snippets/gitignore.js
diff --git a/ui/arduino/libs/ace/snippets/glsl.js b/ui/editor/libs/ace/snippets/glsl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/glsl.js
rename to ui/editor/libs/ace/snippets/glsl.js
diff --git a/ui/arduino/libs/ace/snippets/gobstones.js b/ui/editor/libs/ace/snippets/gobstones.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/gobstones.js
rename to ui/editor/libs/ace/snippets/gobstones.js
diff --git a/ui/arduino/libs/ace/snippets/golang.js b/ui/editor/libs/ace/snippets/golang.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/golang.js
rename to ui/editor/libs/ace/snippets/golang.js
diff --git a/ui/arduino/libs/ace/snippets/graphqlschema.js b/ui/editor/libs/ace/snippets/graphqlschema.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/graphqlschema.js
rename to ui/editor/libs/ace/snippets/graphqlschema.js
diff --git a/ui/arduino/libs/ace/snippets/groovy.js b/ui/editor/libs/ace/snippets/groovy.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/groovy.js
rename to ui/editor/libs/ace/snippets/groovy.js
diff --git a/ui/arduino/libs/ace/snippets/haml.js b/ui/editor/libs/ace/snippets/haml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/haml.js
rename to ui/editor/libs/ace/snippets/haml.js
diff --git a/ui/arduino/libs/ace/snippets/handlebars.js b/ui/editor/libs/ace/snippets/handlebars.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/handlebars.js
rename to ui/editor/libs/ace/snippets/handlebars.js
diff --git a/ui/arduino/libs/ace/snippets/haskell.js b/ui/editor/libs/ace/snippets/haskell.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/haskell.js
rename to ui/editor/libs/ace/snippets/haskell.js
diff --git a/ui/arduino/libs/ace/snippets/haskell_cabal.js b/ui/editor/libs/ace/snippets/haskell_cabal.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/haskell_cabal.js
rename to ui/editor/libs/ace/snippets/haskell_cabal.js
diff --git a/ui/arduino/libs/ace/snippets/haxe.js b/ui/editor/libs/ace/snippets/haxe.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/haxe.js
rename to ui/editor/libs/ace/snippets/haxe.js
diff --git a/ui/arduino/libs/ace/snippets/hjson.js b/ui/editor/libs/ace/snippets/hjson.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/hjson.js
rename to ui/editor/libs/ace/snippets/hjson.js
diff --git a/ui/arduino/libs/ace/snippets/html.js b/ui/editor/libs/ace/snippets/html.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/html.js
rename to ui/editor/libs/ace/snippets/html.js
diff --git a/ui/arduino/libs/ace/snippets/html_elixir.js b/ui/editor/libs/ace/snippets/html_elixir.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/html_elixir.js
rename to ui/editor/libs/ace/snippets/html_elixir.js
diff --git a/ui/arduino/libs/ace/snippets/html_ruby.js b/ui/editor/libs/ace/snippets/html_ruby.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/html_ruby.js
rename to ui/editor/libs/ace/snippets/html_ruby.js
diff --git a/ui/arduino/libs/ace/snippets/ini.js b/ui/editor/libs/ace/snippets/ini.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/ini.js
rename to ui/editor/libs/ace/snippets/ini.js
diff --git a/ui/arduino/libs/ace/snippets/io.js b/ui/editor/libs/ace/snippets/io.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/io.js
rename to ui/editor/libs/ace/snippets/io.js
diff --git a/ui/arduino/libs/ace/snippets/jack.js b/ui/editor/libs/ace/snippets/jack.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/jack.js
rename to ui/editor/libs/ace/snippets/jack.js
diff --git a/ui/arduino/libs/ace/snippets/jade.js b/ui/editor/libs/ace/snippets/jade.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/jade.js
rename to ui/editor/libs/ace/snippets/jade.js
diff --git a/ui/arduino/libs/ace/snippets/java.js b/ui/editor/libs/ace/snippets/java.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/java.js
rename to ui/editor/libs/ace/snippets/java.js
diff --git a/ui/arduino/libs/ace/snippets/javascript.js b/ui/editor/libs/ace/snippets/javascript.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/javascript.js
rename to ui/editor/libs/ace/snippets/javascript.js
diff --git a/ui/arduino/libs/ace/snippets/json.js b/ui/editor/libs/ace/snippets/json.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/json.js
rename to ui/editor/libs/ace/snippets/json.js
diff --git a/ui/arduino/libs/ace/snippets/json5.js b/ui/editor/libs/ace/snippets/json5.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/json5.js
rename to ui/editor/libs/ace/snippets/json5.js
diff --git a/ui/arduino/libs/ace/snippets/jsoniq.js b/ui/editor/libs/ace/snippets/jsoniq.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/jsoniq.js
rename to ui/editor/libs/ace/snippets/jsoniq.js
diff --git a/ui/arduino/libs/ace/snippets/jsp.js b/ui/editor/libs/ace/snippets/jsp.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/jsp.js
rename to ui/editor/libs/ace/snippets/jsp.js
diff --git a/ui/arduino/libs/ace/snippets/jssm.js b/ui/editor/libs/ace/snippets/jssm.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/jssm.js
rename to ui/editor/libs/ace/snippets/jssm.js
diff --git a/ui/arduino/libs/ace/snippets/jsx.js b/ui/editor/libs/ace/snippets/jsx.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/jsx.js
rename to ui/editor/libs/ace/snippets/jsx.js
diff --git a/ui/arduino/libs/ace/snippets/julia.js b/ui/editor/libs/ace/snippets/julia.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/julia.js
rename to ui/editor/libs/ace/snippets/julia.js
diff --git a/ui/arduino/libs/ace/snippets/kotlin.js b/ui/editor/libs/ace/snippets/kotlin.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/kotlin.js
rename to ui/editor/libs/ace/snippets/kotlin.js
diff --git a/ui/arduino/libs/ace/snippets/latex.js b/ui/editor/libs/ace/snippets/latex.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/latex.js
rename to ui/editor/libs/ace/snippets/latex.js
diff --git a/ui/arduino/libs/ace/snippets/less.js b/ui/editor/libs/ace/snippets/less.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/less.js
rename to ui/editor/libs/ace/snippets/less.js
diff --git a/ui/arduino/libs/ace/snippets/liquid.js b/ui/editor/libs/ace/snippets/liquid.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/liquid.js
rename to ui/editor/libs/ace/snippets/liquid.js
diff --git a/ui/arduino/libs/ace/snippets/lisp.js b/ui/editor/libs/ace/snippets/lisp.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/lisp.js
rename to ui/editor/libs/ace/snippets/lisp.js
diff --git a/ui/arduino/libs/ace/snippets/livescript.js b/ui/editor/libs/ace/snippets/livescript.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/livescript.js
rename to ui/editor/libs/ace/snippets/livescript.js
diff --git a/ui/arduino/libs/ace/snippets/logiql.js b/ui/editor/libs/ace/snippets/logiql.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/logiql.js
rename to ui/editor/libs/ace/snippets/logiql.js
diff --git a/ui/arduino/libs/ace/snippets/logtalk.js b/ui/editor/libs/ace/snippets/logtalk.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/logtalk.js
rename to ui/editor/libs/ace/snippets/logtalk.js
diff --git a/ui/arduino/libs/ace/snippets/lsl.js b/ui/editor/libs/ace/snippets/lsl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/lsl.js
rename to ui/editor/libs/ace/snippets/lsl.js
diff --git a/ui/arduino/libs/ace/snippets/lua.js b/ui/editor/libs/ace/snippets/lua.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/lua.js
rename to ui/editor/libs/ace/snippets/lua.js
diff --git a/ui/arduino/libs/ace/snippets/luapage.js b/ui/editor/libs/ace/snippets/luapage.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/luapage.js
rename to ui/editor/libs/ace/snippets/luapage.js
diff --git a/ui/arduino/libs/ace/snippets/lucene.js b/ui/editor/libs/ace/snippets/lucene.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/lucene.js
rename to ui/editor/libs/ace/snippets/lucene.js
diff --git a/ui/arduino/libs/ace/snippets/makefile.js b/ui/editor/libs/ace/snippets/makefile.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/makefile.js
rename to ui/editor/libs/ace/snippets/makefile.js
diff --git a/ui/arduino/libs/ace/snippets/markdown.js b/ui/editor/libs/ace/snippets/markdown.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/markdown.js
rename to ui/editor/libs/ace/snippets/markdown.js
diff --git a/ui/arduino/libs/ace/snippets/mask.js b/ui/editor/libs/ace/snippets/mask.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/mask.js
rename to ui/editor/libs/ace/snippets/mask.js
diff --git a/ui/arduino/libs/ace/snippets/matlab.js b/ui/editor/libs/ace/snippets/matlab.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/matlab.js
rename to ui/editor/libs/ace/snippets/matlab.js
diff --git a/ui/arduino/libs/ace/snippets/maze.js b/ui/editor/libs/ace/snippets/maze.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/maze.js
rename to ui/editor/libs/ace/snippets/maze.js
diff --git a/ui/arduino/libs/ace/snippets/mediawiki.js b/ui/editor/libs/ace/snippets/mediawiki.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/mediawiki.js
rename to ui/editor/libs/ace/snippets/mediawiki.js
diff --git a/ui/arduino/libs/ace/snippets/mel.js b/ui/editor/libs/ace/snippets/mel.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/mel.js
rename to ui/editor/libs/ace/snippets/mel.js
diff --git a/ui/arduino/libs/ace/snippets/mixal.js b/ui/editor/libs/ace/snippets/mixal.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/mixal.js
rename to ui/editor/libs/ace/snippets/mixal.js
diff --git a/ui/arduino/libs/ace/snippets/mushcode.js b/ui/editor/libs/ace/snippets/mushcode.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/mushcode.js
rename to ui/editor/libs/ace/snippets/mushcode.js
diff --git a/ui/arduino/libs/ace/snippets/mysql.js b/ui/editor/libs/ace/snippets/mysql.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/mysql.js
rename to ui/editor/libs/ace/snippets/mysql.js
diff --git a/ui/arduino/libs/ace/snippets/nginx.js b/ui/editor/libs/ace/snippets/nginx.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/nginx.js
rename to ui/editor/libs/ace/snippets/nginx.js
diff --git a/ui/arduino/libs/ace/snippets/nim.js b/ui/editor/libs/ace/snippets/nim.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/nim.js
rename to ui/editor/libs/ace/snippets/nim.js
diff --git a/ui/arduino/libs/ace/snippets/nix.js b/ui/editor/libs/ace/snippets/nix.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/nix.js
rename to ui/editor/libs/ace/snippets/nix.js
diff --git a/ui/arduino/libs/ace/snippets/nsis.js b/ui/editor/libs/ace/snippets/nsis.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/nsis.js
rename to ui/editor/libs/ace/snippets/nsis.js
diff --git a/ui/arduino/libs/ace/snippets/nunjucks.js b/ui/editor/libs/ace/snippets/nunjucks.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/nunjucks.js
rename to ui/editor/libs/ace/snippets/nunjucks.js
diff --git a/ui/arduino/libs/ace/snippets/objectivec.js b/ui/editor/libs/ace/snippets/objectivec.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/objectivec.js
rename to ui/editor/libs/ace/snippets/objectivec.js
diff --git a/ui/arduino/libs/ace/snippets/ocaml.js b/ui/editor/libs/ace/snippets/ocaml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/ocaml.js
rename to ui/editor/libs/ace/snippets/ocaml.js
diff --git a/ui/arduino/libs/ace/snippets/pascal.js b/ui/editor/libs/ace/snippets/pascal.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/pascal.js
rename to ui/editor/libs/ace/snippets/pascal.js
diff --git a/ui/arduino/libs/ace/snippets/perl.js b/ui/editor/libs/ace/snippets/perl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/perl.js
rename to ui/editor/libs/ace/snippets/perl.js
diff --git a/ui/arduino/libs/ace/snippets/perl6.js b/ui/editor/libs/ace/snippets/perl6.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/perl6.js
rename to ui/editor/libs/ace/snippets/perl6.js
diff --git a/ui/arduino/libs/ace/snippets/pgsql.js b/ui/editor/libs/ace/snippets/pgsql.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/pgsql.js
rename to ui/editor/libs/ace/snippets/pgsql.js
diff --git a/ui/arduino/libs/ace/snippets/php.js b/ui/editor/libs/ace/snippets/php.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/php.js
rename to ui/editor/libs/ace/snippets/php.js
diff --git a/ui/arduino/libs/ace/snippets/php_laravel_blade.js b/ui/editor/libs/ace/snippets/php_laravel_blade.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/php_laravel_blade.js
rename to ui/editor/libs/ace/snippets/php_laravel_blade.js
diff --git a/ui/arduino/libs/ace/snippets/pig.js b/ui/editor/libs/ace/snippets/pig.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/pig.js
rename to ui/editor/libs/ace/snippets/pig.js
diff --git a/ui/arduino/libs/ace/snippets/plain_text.js b/ui/editor/libs/ace/snippets/plain_text.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/plain_text.js
rename to ui/editor/libs/ace/snippets/plain_text.js
diff --git a/ui/arduino/libs/ace/snippets/powershell.js b/ui/editor/libs/ace/snippets/powershell.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/powershell.js
rename to ui/editor/libs/ace/snippets/powershell.js
diff --git a/ui/arduino/libs/ace/snippets/praat.js b/ui/editor/libs/ace/snippets/praat.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/praat.js
rename to ui/editor/libs/ace/snippets/praat.js
diff --git a/ui/arduino/libs/ace/snippets/prisma.js b/ui/editor/libs/ace/snippets/prisma.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/prisma.js
rename to ui/editor/libs/ace/snippets/prisma.js
diff --git a/ui/arduino/libs/ace/snippets/prolog.js b/ui/editor/libs/ace/snippets/prolog.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/prolog.js
rename to ui/editor/libs/ace/snippets/prolog.js
diff --git a/ui/arduino/libs/ace/snippets/properties.js b/ui/editor/libs/ace/snippets/properties.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/properties.js
rename to ui/editor/libs/ace/snippets/properties.js
diff --git a/ui/arduino/libs/ace/snippets/protobuf.js b/ui/editor/libs/ace/snippets/protobuf.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/protobuf.js
rename to ui/editor/libs/ace/snippets/protobuf.js
diff --git a/ui/arduino/libs/ace/snippets/puppet.js b/ui/editor/libs/ace/snippets/puppet.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/puppet.js
rename to ui/editor/libs/ace/snippets/puppet.js
diff --git a/ui/arduino/libs/ace/snippets/python.js b/ui/editor/libs/ace/snippets/python.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/python.js
rename to ui/editor/libs/ace/snippets/python.js
diff --git a/ui/arduino/libs/ace/snippets/qml.js b/ui/editor/libs/ace/snippets/qml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/qml.js
rename to ui/editor/libs/ace/snippets/qml.js
diff --git a/ui/arduino/libs/ace/snippets/r.js b/ui/editor/libs/ace/snippets/r.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/r.js
rename to ui/editor/libs/ace/snippets/r.js
diff --git a/ui/arduino/libs/ace/snippets/razor.js b/ui/editor/libs/ace/snippets/razor.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/razor.js
rename to ui/editor/libs/ace/snippets/razor.js
diff --git a/ui/arduino/libs/ace/snippets/rdoc.js b/ui/editor/libs/ace/snippets/rdoc.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/rdoc.js
rename to ui/editor/libs/ace/snippets/rdoc.js
diff --git a/ui/arduino/libs/ace/snippets/red.js b/ui/editor/libs/ace/snippets/red.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/red.js
rename to ui/editor/libs/ace/snippets/red.js
diff --git a/ui/arduino/libs/ace/snippets/redshift.js b/ui/editor/libs/ace/snippets/redshift.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/redshift.js
rename to ui/editor/libs/ace/snippets/redshift.js
diff --git a/ui/arduino/libs/ace/snippets/rhtml.js b/ui/editor/libs/ace/snippets/rhtml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/rhtml.js
rename to ui/editor/libs/ace/snippets/rhtml.js
diff --git a/ui/arduino/libs/ace/snippets/rst.js b/ui/editor/libs/ace/snippets/rst.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/rst.js
rename to ui/editor/libs/ace/snippets/rst.js
diff --git a/ui/arduino/libs/ace/snippets/ruby.js b/ui/editor/libs/ace/snippets/ruby.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/ruby.js
rename to ui/editor/libs/ace/snippets/ruby.js
diff --git a/ui/arduino/libs/ace/snippets/rust.js b/ui/editor/libs/ace/snippets/rust.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/rust.js
rename to ui/editor/libs/ace/snippets/rust.js
diff --git a/ui/arduino/libs/ace/snippets/sass.js b/ui/editor/libs/ace/snippets/sass.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/sass.js
rename to ui/editor/libs/ace/snippets/sass.js
diff --git a/ui/arduino/libs/ace/snippets/scad.js b/ui/editor/libs/ace/snippets/scad.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/scad.js
rename to ui/editor/libs/ace/snippets/scad.js
diff --git a/ui/arduino/libs/ace/snippets/scala.js b/ui/editor/libs/ace/snippets/scala.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/scala.js
rename to ui/editor/libs/ace/snippets/scala.js
diff --git a/ui/arduino/libs/ace/snippets/scheme.js b/ui/editor/libs/ace/snippets/scheme.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/scheme.js
rename to ui/editor/libs/ace/snippets/scheme.js
diff --git a/ui/arduino/libs/ace/snippets/scss.js b/ui/editor/libs/ace/snippets/scss.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/scss.js
rename to ui/editor/libs/ace/snippets/scss.js
diff --git a/ui/arduino/libs/ace/snippets/sh.js b/ui/editor/libs/ace/snippets/sh.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/sh.js
rename to ui/editor/libs/ace/snippets/sh.js
diff --git a/ui/arduino/libs/ace/snippets/sjs.js b/ui/editor/libs/ace/snippets/sjs.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/sjs.js
rename to ui/editor/libs/ace/snippets/sjs.js
diff --git a/ui/arduino/libs/ace/snippets/slim.js b/ui/editor/libs/ace/snippets/slim.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/slim.js
rename to ui/editor/libs/ace/snippets/slim.js
diff --git a/ui/arduino/libs/ace/snippets/smarty.js b/ui/editor/libs/ace/snippets/smarty.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/smarty.js
rename to ui/editor/libs/ace/snippets/smarty.js
diff --git a/ui/arduino/libs/ace/snippets/snippets.js b/ui/editor/libs/ace/snippets/snippets.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/snippets.js
rename to ui/editor/libs/ace/snippets/snippets.js
diff --git a/ui/arduino/libs/ace/snippets/soy_template.js b/ui/editor/libs/ace/snippets/soy_template.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/soy_template.js
rename to ui/editor/libs/ace/snippets/soy_template.js
diff --git a/ui/arduino/libs/ace/snippets/space.js b/ui/editor/libs/ace/snippets/space.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/space.js
rename to ui/editor/libs/ace/snippets/space.js
diff --git a/ui/arduino/libs/ace/snippets/sparql.js b/ui/editor/libs/ace/snippets/sparql.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/sparql.js
rename to ui/editor/libs/ace/snippets/sparql.js
diff --git a/ui/arduino/libs/ace/snippets/sql.js b/ui/editor/libs/ace/snippets/sql.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/sql.js
rename to ui/editor/libs/ace/snippets/sql.js
diff --git a/ui/arduino/libs/ace/snippets/sqlserver.js b/ui/editor/libs/ace/snippets/sqlserver.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/sqlserver.js
rename to ui/editor/libs/ace/snippets/sqlserver.js
diff --git a/ui/arduino/libs/ace/snippets/stylus.js b/ui/editor/libs/ace/snippets/stylus.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/stylus.js
rename to ui/editor/libs/ace/snippets/stylus.js
diff --git a/ui/arduino/libs/ace/snippets/svg.js b/ui/editor/libs/ace/snippets/svg.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/svg.js
rename to ui/editor/libs/ace/snippets/svg.js
diff --git a/ui/arduino/libs/ace/snippets/swift.js b/ui/editor/libs/ace/snippets/swift.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/swift.js
rename to ui/editor/libs/ace/snippets/swift.js
diff --git a/ui/arduino/libs/ace/snippets/tcl.js b/ui/editor/libs/ace/snippets/tcl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/tcl.js
rename to ui/editor/libs/ace/snippets/tcl.js
diff --git a/ui/arduino/libs/ace/snippets/terraform.js b/ui/editor/libs/ace/snippets/terraform.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/terraform.js
rename to ui/editor/libs/ace/snippets/terraform.js
diff --git a/ui/arduino/libs/ace/snippets/tex.js b/ui/editor/libs/ace/snippets/tex.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/tex.js
rename to ui/editor/libs/ace/snippets/tex.js
diff --git a/ui/arduino/libs/ace/snippets/text.js b/ui/editor/libs/ace/snippets/text.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/text.js
rename to ui/editor/libs/ace/snippets/text.js
diff --git a/ui/arduino/libs/ace/snippets/textile.js b/ui/editor/libs/ace/snippets/textile.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/textile.js
rename to ui/editor/libs/ace/snippets/textile.js
diff --git a/ui/arduino/libs/ace/snippets/toml.js b/ui/editor/libs/ace/snippets/toml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/toml.js
rename to ui/editor/libs/ace/snippets/toml.js
diff --git a/ui/arduino/libs/ace/snippets/tsx.js b/ui/editor/libs/ace/snippets/tsx.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/tsx.js
rename to ui/editor/libs/ace/snippets/tsx.js
diff --git a/ui/arduino/libs/ace/snippets/turtle.js b/ui/editor/libs/ace/snippets/turtle.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/turtle.js
rename to ui/editor/libs/ace/snippets/turtle.js
diff --git a/ui/arduino/libs/ace/snippets/twig.js b/ui/editor/libs/ace/snippets/twig.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/twig.js
rename to ui/editor/libs/ace/snippets/twig.js
diff --git a/ui/arduino/libs/ace/snippets/typescript.js b/ui/editor/libs/ace/snippets/typescript.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/typescript.js
rename to ui/editor/libs/ace/snippets/typescript.js
diff --git a/ui/arduino/libs/ace/snippets/vala.js b/ui/editor/libs/ace/snippets/vala.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/vala.js
rename to ui/editor/libs/ace/snippets/vala.js
diff --git a/ui/arduino/libs/ace/snippets/vbscript.js b/ui/editor/libs/ace/snippets/vbscript.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/vbscript.js
rename to ui/editor/libs/ace/snippets/vbscript.js
diff --git a/ui/arduino/libs/ace/snippets/velocity.js b/ui/editor/libs/ace/snippets/velocity.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/velocity.js
rename to ui/editor/libs/ace/snippets/velocity.js
diff --git a/ui/arduino/libs/ace/snippets/verilog.js b/ui/editor/libs/ace/snippets/verilog.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/verilog.js
rename to ui/editor/libs/ace/snippets/verilog.js
diff --git a/ui/arduino/libs/ace/snippets/vhdl.js b/ui/editor/libs/ace/snippets/vhdl.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/vhdl.js
rename to ui/editor/libs/ace/snippets/vhdl.js
diff --git a/ui/arduino/libs/ace/snippets/visualforce.js b/ui/editor/libs/ace/snippets/visualforce.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/visualforce.js
rename to ui/editor/libs/ace/snippets/visualforce.js
diff --git a/ui/arduino/libs/ace/snippets/wollok.js b/ui/editor/libs/ace/snippets/wollok.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/wollok.js
rename to ui/editor/libs/ace/snippets/wollok.js
diff --git a/ui/arduino/libs/ace/snippets/xml.js b/ui/editor/libs/ace/snippets/xml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/xml.js
rename to ui/editor/libs/ace/snippets/xml.js
diff --git a/ui/arduino/libs/ace/snippets/xquery.js b/ui/editor/libs/ace/snippets/xquery.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/xquery.js
rename to ui/editor/libs/ace/snippets/xquery.js
diff --git a/ui/arduino/libs/ace/snippets/yaml.js b/ui/editor/libs/ace/snippets/yaml.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/yaml.js
rename to ui/editor/libs/ace/snippets/yaml.js
diff --git a/ui/arduino/libs/ace/snippets/zeek.js b/ui/editor/libs/ace/snippets/zeek.js
similarity index 100%
rename from ui/arduino/libs/ace/snippets/zeek.js
rename to ui/editor/libs/ace/snippets/zeek.js
diff --git a/ui/arduino/libs/ace/theme-ambiance.js b/ui/editor/libs/ace/theme-ambiance.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-ambiance.js
rename to ui/editor/libs/ace/theme-ambiance.js
diff --git a/ui/arduino/libs/ace/theme-chaos.js b/ui/editor/libs/ace/theme-chaos.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-chaos.js
rename to ui/editor/libs/ace/theme-chaos.js
diff --git a/ui/arduino/libs/ace/theme-chrome.js b/ui/editor/libs/ace/theme-chrome.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-chrome.js
rename to ui/editor/libs/ace/theme-chrome.js
diff --git a/ui/arduino/libs/ace/theme-clouds.js b/ui/editor/libs/ace/theme-clouds.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-clouds.js
rename to ui/editor/libs/ace/theme-clouds.js
diff --git a/ui/arduino/libs/ace/theme-clouds_midnight.js b/ui/editor/libs/ace/theme-clouds_midnight.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-clouds_midnight.js
rename to ui/editor/libs/ace/theme-clouds_midnight.js
diff --git a/ui/arduino/libs/ace/theme-cobalt.js b/ui/editor/libs/ace/theme-cobalt.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-cobalt.js
rename to ui/editor/libs/ace/theme-cobalt.js
diff --git a/ui/arduino/libs/ace/theme-crimson_editor.js b/ui/editor/libs/ace/theme-crimson_editor.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-crimson_editor.js
rename to ui/editor/libs/ace/theme-crimson_editor.js
diff --git a/ui/arduino/libs/ace/theme-dawn.js b/ui/editor/libs/ace/theme-dawn.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-dawn.js
rename to ui/editor/libs/ace/theme-dawn.js
diff --git a/ui/arduino/libs/ace/theme-dracula.js b/ui/editor/libs/ace/theme-dracula.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-dracula.js
rename to ui/editor/libs/ace/theme-dracula.js
diff --git a/ui/arduino/libs/ace/theme-dreamweaver.js b/ui/editor/libs/ace/theme-dreamweaver.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-dreamweaver.js
rename to ui/editor/libs/ace/theme-dreamweaver.js
diff --git a/ui/arduino/libs/ace/theme-eclipse.js b/ui/editor/libs/ace/theme-eclipse.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-eclipse.js
rename to ui/editor/libs/ace/theme-eclipse.js
diff --git a/ui/arduino/libs/ace/theme-github.js b/ui/editor/libs/ace/theme-github.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-github.js
rename to ui/editor/libs/ace/theme-github.js
diff --git a/ui/arduino/libs/ace/theme-gob.js b/ui/editor/libs/ace/theme-gob.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-gob.js
rename to ui/editor/libs/ace/theme-gob.js
diff --git a/ui/arduino/libs/ace/theme-gruvbox.js b/ui/editor/libs/ace/theme-gruvbox.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-gruvbox.js
rename to ui/editor/libs/ace/theme-gruvbox.js
diff --git a/ui/arduino/libs/ace/theme-idle_fingers.js b/ui/editor/libs/ace/theme-idle_fingers.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-idle_fingers.js
rename to ui/editor/libs/ace/theme-idle_fingers.js
diff --git a/ui/arduino/libs/ace/theme-iplastic.js b/ui/editor/libs/ace/theme-iplastic.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-iplastic.js
rename to ui/editor/libs/ace/theme-iplastic.js
diff --git a/ui/arduino/libs/ace/theme-katzenmilch.js b/ui/editor/libs/ace/theme-katzenmilch.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-katzenmilch.js
rename to ui/editor/libs/ace/theme-katzenmilch.js
diff --git a/ui/arduino/libs/ace/theme-kr_theme.js b/ui/editor/libs/ace/theme-kr_theme.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-kr_theme.js
rename to ui/editor/libs/ace/theme-kr_theme.js
diff --git a/ui/arduino/libs/ace/theme-kuroir.js b/ui/editor/libs/ace/theme-kuroir.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-kuroir.js
rename to ui/editor/libs/ace/theme-kuroir.js
diff --git a/ui/arduino/libs/ace/theme-merbivore.js b/ui/editor/libs/ace/theme-merbivore.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-merbivore.js
rename to ui/editor/libs/ace/theme-merbivore.js
diff --git a/ui/arduino/libs/ace/theme-merbivore_soft.js b/ui/editor/libs/ace/theme-merbivore_soft.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-merbivore_soft.js
rename to ui/editor/libs/ace/theme-merbivore_soft.js
diff --git a/ui/arduino/libs/ace/theme-mono_industrial.js b/ui/editor/libs/ace/theme-mono_industrial.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-mono_industrial.js
rename to ui/editor/libs/ace/theme-mono_industrial.js
diff --git a/ui/arduino/libs/ace/theme-monokai.js b/ui/editor/libs/ace/theme-monokai.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-monokai.js
rename to ui/editor/libs/ace/theme-monokai.js
diff --git a/ui/arduino/libs/ace/theme-nord_dark.js b/ui/editor/libs/ace/theme-nord_dark.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-nord_dark.js
rename to ui/editor/libs/ace/theme-nord_dark.js
diff --git a/ui/arduino/libs/ace/theme-pastel_on_dark.js b/ui/editor/libs/ace/theme-pastel_on_dark.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-pastel_on_dark.js
rename to ui/editor/libs/ace/theme-pastel_on_dark.js
diff --git a/ui/arduino/libs/ace/theme-solarized_dark.js b/ui/editor/libs/ace/theme-solarized_dark.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-solarized_dark.js
rename to ui/editor/libs/ace/theme-solarized_dark.js
diff --git a/ui/arduino/libs/ace/theme-solarized_light.js b/ui/editor/libs/ace/theme-solarized_light.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-solarized_light.js
rename to ui/editor/libs/ace/theme-solarized_light.js
diff --git a/ui/arduino/libs/ace/theme-sqlserver.js b/ui/editor/libs/ace/theme-sqlserver.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-sqlserver.js
rename to ui/editor/libs/ace/theme-sqlserver.js
diff --git a/ui/arduino/libs/ace/theme-terminal.js b/ui/editor/libs/ace/theme-terminal.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-terminal.js
rename to ui/editor/libs/ace/theme-terminal.js
diff --git a/ui/arduino/libs/ace/theme-textmate.js b/ui/editor/libs/ace/theme-textmate.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-textmate.js
rename to ui/editor/libs/ace/theme-textmate.js
diff --git a/ui/arduino/libs/ace/theme-tomorrow.js b/ui/editor/libs/ace/theme-tomorrow.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-tomorrow.js
rename to ui/editor/libs/ace/theme-tomorrow.js
diff --git a/ui/arduino/libs/ace/theme-tomorrow_night.js b/ui/editor/libs/ace/theme-tomorrow_night.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-tomorrow_night.js
rename to ui/editor/libs/ace/theme-tomorrow_night.js
diff --git a/ui/arduino/libs/ace/theme-tomorrow_night_blue.js b/ui/editor/libs/ace/theme-tomorrow_night_blue.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-tomorrow_night_blue.js
rename to ui/editor/libs/ace/theme-tomorrow_night_blue.js
diff --git a/ui/arduino/libs/ace/theme-tomorrow_night_bright.js b/ui/editor/libs/ace/theme-tomorrow_night_bright.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-tomorrow_night_bright.js
rename to ui/editor/libs/ace/theme-tomorrow_night_bright.js
diff --git a/ui/arduino/libs/ace/theme-tomorrow_night_eighties.js b/ui/editor/libs/ace/theme-tomorrow_night_eighties.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-tomorrow_night_eighties.js
rename to ui/editor/libs/ace/theme-tomorrow_night_eighties.js
diff --git a/ui/arduino/libs/ace/theme-twilight.js b/ui/editor/libs/ace/theme-twilight.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-twilight.js
rename to ui/editor/libs/ace/theme-twilight.js
diff --git a/ui/arduino/libs/ace/theme-vibrant_ink.js b/ui/editor/libs/ace/theme-vibrant_ink.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-vibrant_ink.js
rename to ui/editor/libs/ace/theme-vibrant_ink.js
diff --git a/ui/arduino/libs/ace/theme-xcode.js b/ui/editor/libs/ace/theme-xcode.js
similarity index 100%
rename from ui/arduino/libs/ace/theme-xcode.js
rename to ui/editor/libs/ace/theme-xcode.js
diff --git a/ui/arduino/libs/ace/worker-base.js b/ui/editor/libs/ace/worker-base.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-base.js
rename to ui/editor/libs/ace/worker-base.js
diff --git a/ui/arduino/libs/ace/worker-coffee.js b/ui/editor/libs/ace/worker-coffee.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-coffee.js
rename to ui/editor/libs/ace/worker-coffee.js
diff --git a/ui/arduino/libs/ace/worker-css.js b/ui/editor/libs/ace/worker-css.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-css.js
rename to ui/editor/libs/ace/worker-css.js
diff --git a/ui/arduino/libs/ace/worker-html.js b/ui/editor/libs/ace/worker-html.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-html.js
rename to ui/editor/libs/ace/worker-html.js
diff --git a/ui/arduino/libs/ace/worker-javascript.js b/ui/editor/libs/ace/worker-javascript.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-javascript.js
rename to ui/editor/libs/ace/worker-javascript.js
diff --git a/ui/arduino/libs/ace/worker-json.js b/ui/editor/libs/ace/worker-json.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-json.js
rename to ui/editor/libs/ace/worker-json.js
diff --git a/ui/arduino/libs/ace/worker-lua.js b/ui/editor/libs/ace/worker-lua.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-lua.js
rename to ui/editor/libs/ace/worker-lua.js
diff --git a/ui/arduino/libs/ace/worker-php.js b/ui/editor/libs/ace/worker-php.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-php.js
rename to ui/editor/libs/ace/worker-php.js
diff --git a/ui/arduino/libs/ace/worker-xml.js b/ui/editor/libs/ace/worker-xml.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-xml.js
rename to ui/editor/libs/ace/worker-xml.js
diff --git a/ui/arduino/libs/ace/worker-xquery.js b/ui/editor/libs/ace/worker-xquery.js
similarity index 100%
rename from ui/arduino/libs/ace/worker-xquery.js
rename to ui/editor/libs/ace/worker-xquery.js
diff --git a/ui/arduino/libs/build_choo.js b/ui/editor/libs/build_choo.js
similarity index 100%
rename from ui/arduino/libs/build_choo.js
rename to ui/editor/libs/build_choo.js
diff --git a/ui/arduino/libs/choo.js b/ui/editor/libs/choo.js
similarity index 100%
rename from ui/arduino/libs/choo.js
rename to ui/editor/libs/choo.js
diff --git a/ui/arduino/libs/xterm-addon-fit.js b/ui/editor/libs/xterm-addon-fit.js
similarity index 100%
rename from ui/arduino/libs/xterm-addon-fit.js
rename to ui/editor/libs/xterm-addon-fit.js
diff --git a/ui/arduino/libs/xterm.css b/ui/editor/libs/xterm.css
similarity index 100%
rename from ui/arduino/libs/xterm.css
rename to ui/editor/libs/xterm.css
diff --git a/ui/arduino/libs/xterm.js b/ui/editor/libs/xterm.js
similarity index 100%
rename from ui/arduino/libs/xterm.js
rename to ui/editor/libs/xterm.js
diff --git a/ui/arduino/package-lock.json b/ui/editor/package-lock.json
similarity index 100%
rename from ui/arduino/package-lock.json
rename to ui/editor/package-lock.json
diff --git a/ui/arduino/package.json b/ui/editor/package.json
similarity index 100%
rename from ui/arduino/package.json
rename to ui/editor/package.json
diff --git a/ui/arduino/store.js b/ui/editor/store.js
similarity index 100%
rename from ui/arduino/store.js
rename to ui/editor/store.js
diff --git a/ui/arduino/theme.css b/ui/editor/theme.css
similarity index 100%
rename from ui/arduino/theme.css
rename to ui/editor/theme.css
diff --git a/ui/ftp/components/files/diskFiles.tsx b/ui/ftp/components/files/diskFiles.tsx
new file mode 100644
index 0000000..682840d
--- /dev/null
+++ b/ui/ftp/components/files/diskFiles.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+import DiskFilesType from './diskFiles.type.ts'
+import Button from '../shared/button.tsx'
+
+const DiskFiles: React.FC = ({ diskFilesLogic }) => {    
+    const { diskFiles = [], navigate }: DiskFilesType = diskFilesLogic()
+    const onClick = (folder) => () => {
+        navigate(folder)
+    }
+    const files = diskFiles.map((folder, i) => {
+        return <li key={i}><Button onClick={onClick(folder)}>{folder}</Button></li>
+    })
+    return <ul>{files}</ul>
+}
+
+export default DiskFiles
\ No newline at end of file
diff --git a/ui/ftp/components/files/diskFiles.type.ts b/ui/ftp/components/files/diskFiles.type.ts
new file mode 100644
index 0000000..cf38a69
--- /dev/null
+++ b/ui/ftp/components/files/diskFiles.type.ts
@@ -0,0 +1,8 @@
+type DiskFilesType = () => {
+    diskFiles: String[]
+    // selectedFile: File
+    navigate: (folder : String) => void
+    selectFile: (path: String) => void
+}
+
+export default DiskFilesType
\ No newline at end of file
diff --git a/ui/ftp/components/files/management.type.ts b/ui/ftp/components/files/management.type.ts
new file mode 100644
index 0000000..5dd88ff
--- /dev/null
+++ b/ui/ftp/components/files/management.type.ts
@@ -0,0 +1,7 @@
+type FileManagementType = () => {
+    upload: (diskPath: String, serialPath: String) => void
+    download: (serialPath: String, diskPath: String) => void
+    remove: () => void
+}
+
+export default FileManagementType
\ No newline at end of file
diff --git a/ui/ftp/components/files/serialFiles.tsx b/ui/ftp/components/files/serialFiles.tsx
new file mode 100644
index 0000000..23c9724
--- /dev/null
+++ b/ui/ftp/components/files/serialFiles.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+import SerialFilesType from './serialFiles.type.ts'
+import Button from '../shared/button.tsx'
+
+const SerialFiles: React.FC = ({ serialFilesLogic }) => {    
+    const { serialFiles = [], navigate }: SerialFilesType = serialFilesLogic()
+    const onClick = (folder) => () => {
+        navigate(folder)
+    }
+    const files = serialFiles.map((folder, i) => {
+        return <li key={i}><Button onClick={onClick(folder)}>{folder}</Button></li>
+    })
+    return <ul>{files}</ul>
+}
+
+export default SerialFiles
\ No newline at end of file
diff --git a/ui/ftp/components/files/serialFiles.type.ts b/ui/ftp/components/files/serialFiles.type.ts
new file mode 100644
index 0000000..93bc68b
--- /dev/null
+++ b/ui/ftp/components/files/serialFiles.type.ts
@@ -0,0 +1,8 @@
+type SerialFilesType = () => {
+    serialFiles: String[]
+    selectedFile: File
+    navigate: (folder : String) => void
+    selectFile: (path: String) => void
+}
+
+export default SerialFilesType
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/diskNavigation.tsx b/ui/ftp/components/navigation/diskNavigation.tsx
new file mode 100644
index 0000000..beab208
--- /dev/null
+++ b/ui/ftp/components/navigation/diskNavigation.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import BreadCrumbs from '../shared/breadcrumbs'
+
+const DiskNavigation: React.FC = ({ navigationLogic }) => {    
+    const { diskPath = '', navigate } = navigationLogic()
+    return <BreadCrumbs path={diskPath} navigate={navigate}></BreadCrumbs>
+}
+
+export default DiskNavigation
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/diskNavigation.type.ts b/ui/ftp/components/navigation/diskNavigation.type.ts
new file mode 100644
index 0000000..fb8364b
--- /dev/null
+++ b/ui/ftp/components/navigation/diskNavigation.type.ts
@@ -0,0 +1,6 @@
+type DiskNavigationType = () => {
+    diskPath: String
+    navigate: (folder : String) => void
+}
+
+export default DiskNavigationType
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/serialNavigation.tsx b/ui/ftp/components/navigation/serialNavigation.tsx
new file mode 100644
index 0000000..98b8314
--- /dev/null
+++ b/ui/ftp/components/navigation/serialNavigation.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+import BreadCrumbs from '../shared/breadcrumbs'
+
+const SerialNavigation: React.FC = ({ navigationLogic }) => {    
+    const { serialPath = '', navigate } = navigationLogic()
+    return <BreadCrumbs path={serialPath} navigate={navigate}></BreadCrumbs>
+}
+
+export default SerialNavigation
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/serialNavigation.type.ts b/ui/ftp/components/navigation/serialNavigation.type.ts
new file mode 100644
index 0000000..1d057c0
--- /dev/null
+++ b/ui/ftp/components/navigation/serialNavigation.type.ts
@@ -0,0 +1,6 @@
+type SerialNavigationType = () => {
+    serialPath: String
+    navigate: (folder: String) => void
+}
+
+export default SerialNavigationType
\ No newline at end of file
diff --git a/ui/ftp/components/shared/breadcrumbs.module.scss b/ui/ftp/components/shared/breadcrumbs.module.scss
new file mode 100644
index 0000000..822400d
--- /dev/null
+++ b/ui/ftp/components/shared/breadcrumbs.module.scss
@@ -0,0 +1,9 @@
+.body {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    padding: 1rem;
+    > * {
+        margin: 0 0.5rem 0 0;
+    }
+}
\ No newline at end of file
diff --git a/ui/ftp/components/shared/breadcrumbs.tsx b/ui/ftp/components/shared/breadcrumbs.tsx
new file mode 100644
index 0000000..f42d966
--- /dev/null
+++ b/ui/ftp/components/shared/breadcrumbs.tsx
@@ -0,0 +1,21 @@
+import React from 'react'
+import Button from './button.tsx'
+import styles from './breadcrumbs.module.scss'
+
+const BreadCrumbs: React.FC = ({ path, navigate }) => {
+    if (!path) return <></>
+    const pathArray = path.split('/')
+    const onClick = (folder) => () => {
+        navigate(folder)
+    }
+    const items = pathArray.map(
+        (folder, i) => (
+            <span key={i}>
+                <Button onClick={onClick(folder)}>{folder}</Button>/
+            </span>
+        )
+    )
+    return <div className={styles.body}>{items}</div>
+}
+
+export default BreadCrumbs
\ No newline at end of file
diff --git a/ui/ftp/components/shared/button.module.scss b/ui/ftp/components/shared/button.module.scss
new file mode 100644
index 0000000..ce0e3f6
--- /dev/null
+++ b/ui/ftp/components/shared/button.module.scss
@@ -0,0 +1,12 @@
+.button {
+    padding: 1rem;
+    border: none;
+    cursor: pointer;
+    background: #ddd;
+    &:hover {
+        opacity: 0.8;
+    }
+    &:active {
+        opacity: 1.0;
+    }
+}
\ No newline at end of file
diff --git a/ui/ftp/components/shared/button.tsx b/ui/ftp/components/shared/button.tsx
new file mode 100644
index 0000000..47e5c29
--- /dev/null
+++ b/ui/ftp/components/shared/button.tsx
@@ -0,0 +1,8 @@
+import React from 'react'
+import styles from './button.module.scss'
+
+const Button: React.FC = (attr) => {
+  return <button {...attr} className={styles.button}></button>
+}
+
+export default Button
\ No newline at end of file
diff --git a/ui/ftp/components/toolbar/toolbar.module.scss b/ui/ftp/components/toolbar/toolbar.module.scss
new file mode 100644
index 0000000..6d41740
--- /dev/null
+++ b/ui/ftp/components/toolbar/toolbar.module.scss
@@ -0,0 +1,8 @@
+.body {
+    display: flex;
+    flex-direction: row;
+    padding: 1rem;
+    > * {
+        margin: 0 0.5rem 0 0;
+    }
+}
diff --git a/ui/ftp/components/toolbar/toolbar.tsx b/ui/ftp/components/toolbar/toolbar.tsx
new file mode 100644
index 0000000..46bc943
--- /dev/null
+++ b/ui/ftp/components/toolbar/toolbar.tsx
@@ -0,0 +1,42 @@
+import React, { useState } from 'react'
+import styles from './toolbar.module.scss'
+import Button from '../shared/button.tsx'
+
+
+const Toolbar: React.FC = ({ toolbarLogic }) => {    
+    const { 
+        openFolder, 
+        refresh,
+        connect,
+        disconnect,
+        connectedDevice,
+        availableDevices = [] 
+    } = toolbarLogic()
+    const [ selectedDevice, setSelectedDevice ] = useState<String | null>()
+    const onChange = (e) => setSelectedDevice(e.target.value)
+    const onConnect = () => connect(selectedDevice)
+
+    const deviceSelector = (
+        <select onChange={onChange} value={selectedDevice}>
+            <option value="none">Select a device...</option>
+            {availableDevices.map((d, i) => <option key={i} value={d.path}>{d.path}</option>)}
+        </select>
+    )
+    const deviceDisplay = (
+        <span>{connectedDevice}</span>
+    )
+
+    return (
+        <div className={styles.body}>
+            {connectedDevice ? deviceDisplay : deviceSelector}
+            <Button onClick={refresh}>Refresh</Button>
+            <Button onClick={connectedDevice ? disconnect : onConnect}>
+                {connectedDevice ? 'Disconnect' : 'Connect'}
+            </Button>
+            <Button onClick={openFolder}>Select Folder</Button>
+            {connectedDevice ? <span>Connected</span> : <span>Disconnected</span>}
+        </div>
+    )
+}
+
+export default Toolbar
\ No newline at end of file
diff --git a/ui/ftp/components/toolbar/toolbar.type.ts b/ui/ftp/components/toolbar/toolbar.type.ts
new file mode 100644
index 0000000..01a653b
--- /dev/null
+++ b/ui/ftp/components/toolbar/toolbar.type.ts
@@ -0,0 +1,10 @@
+type ToolbarType = () => {
+    availableDevices: String[]
+    connectedDevice: String | null
+    connect: (devicePath: String) => void
+    disconnect: () => void
+    openFolder: () => void
+    refresh: () => void
+}
+
+export default ToolbarType
\ No newline at end of file
diff --git a/ui/ftp/index.html b/ui/ftp/index.html
new file mode 100644
index 0000000..4a7d0ad
--- /dev/null
+++ b/ui/ftp/index.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Document</title>
+    <link rel="stylesheet" href="main.scss">
+    <script type="module" src="main.tsx"></script>
+</head>
+<body>
+    <main></main>
+</body>
+</html>
\ No newline at end of file
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
new file mode 100644
index 0000000..5c511d7
--- /dev/null
+++ b/ui/ftp/main.logic.ts
@@ -0,0 +1,164 @@
+import { useState } from 'react';
+
+import ToolbarType from './components/toolbar/toolbar.type'
+import DiskNavigationType from './components/navigation/diskNavigation.type'
+import DiskFilesType from './components/files/diskFiles.type'
+import SerialNavigationType from './components/navigation/serialNavigation.type'
+import SerialFilesType from './components/files/serialFiles.type'
+import FileManagementType from './components/files/management.type'
+
+import { Device, File } from './main.type'
+
+export type UseMainLogic = () => {
+    toolbarLogic: ToolbarType
+    diskNavigationLogic: DiskNavigationType
+    diskFilesLogic: DiskFilesType
+    serialNavigationLogic: SerialNavigationType
+    serialFilesLogic: SerialFilesType
+    fileManagementLogic: FileManagementType
+}
+
+export const useMainLogic : UseMainLogic = function() : ReturnType<UseMainLogic> {
+    const { BridgeSerial, BridgeDisk } = window
+    // List and connect to serial devices
+    const [ availableDevices, setAvailableDevices ] = useState<String[]>()
+    const [ connectedDevice, setConnectedDevice ] = useState<String | null>()
+    // Navigation paths
+    const [ serialPath, setSerialPath ] = useState<String | null>()
+    const [ diskPath, setDiskPath ] = useState<String | null>()
+    // Available files (listed)
+    const [ diskFiles, setDiskFiles ] = useState<String[]>()
+    const [ serialFiles, setSerialFiles ] = useState<String[]>()
+    // Selected file
+    const [ selectedFile, setSelectedFile ] = useState<File | null>()
+    // Progress
+    const [ waiting, setWaiting ] = useState<Boolean>()
+
+    // HELPERS
+    const navigateDisk = async (folder) => {
+        const pathArray = diskPath.split('/')
+        const folderIndex = pathArray.indexOf(folder)
+        let newPathArray = []
+        if (folderIndex === -1) {
+            newPathArray = pathArray.concat(folder)
+        } else {
+            newPathArray = pathArray.slice(0, folderIndex+1)
+        }
+        const newPath = newPathArray.join('/')
+        const files = await BridgeDisk.listFiles(newPath)
+        setDiskPath(newPath)
+        setDiskFiles(files)
+    }
+
+    const navigateSerial = async (folder) => {
+        const pathArray = serialPath.split('/')
+        const folderIndex = pathArray.indexOf(folder)
+        let newPathArray = []
+        if (folderIndex === -1) {
+            newPathArray = pathArray.concat(folder)
+        } else {
+            newPathArray = pathArray.slice(0, folderIndex+1)
+        }
+        const newPath = newPathArray.join('/')
+        const files = await BridgeSerial.listFiles(newPath)
+        setSerialPath(newPath)
+        setSerialFiles(files)
+    }
+
+    // LOGIC
+    const toolbarLogic = () : ReturnType<ToolbarType> => {
+        return {
+            availableDevices: availableDevices,
+            connectedDevice: connectedDevice,
+            connect: async (devicePath: String) => {
+                await BridgeSerial.connect(devicePath)
+                setConnectedDevice(devicePath)
+                setSerialPath('/')
+            },
+            disconnect: () => {
+                setConnectedDevice(null)
+                setSerialPath(null)
+            },
+            openFolder: async () => {
+                const { folder, files } = await BridgeDisk.openFolder()
+                setDiskPath(folder)
+                setDiskFiles(files)
+            },
+            refresh: async () => {
+                // list available devices
+                const devices = await BridgeSerial.loadPorts()
+                setAvailableDevices(devices)
+                // list serial files
+                if (connectedDevice) {
+                    const files = await BridgeSerial.listFiles(serialPath)
+                    setSerialFiles(files)
+                }
+                // list disk files
+                if (diskPath) {
+                    const files = await BridgeDisk.listFiles(diskPath)
+                    setDiskFiles(files)
+                }
+            }
+        }
+    }
+
+    const diskNavigationLogic = () : ReturnType<DiskNavigationType> => {
+        return {
+            diskPath: diskPath,
+            navigate: navigateDisk
+        }
+    }
+
+    const diskFilesLogic = () : ReturnType<DiskFilesType> => {
+        return {
+            diskFiles: diskFiles,
+            // selectedFile: selectedFile,
+            navigate: navigateDisk,
+            selectFile: (path) => {
+                setSelectedFile({
+                    path: path, 
+                    device: Device.disk
+                })
+            }
+        }
+    }
+
+    const serialNavigationLogic = () : ReturnType<SerialNavigationType> => {
+        return {
+            serialPath: serialPath,
+            navigate: navigateSerial
+        }
+    }
+
+    const serialFilesLogic = () : ReturnType<SerialFilesType> => {
+        return {
+            serialFiles: serialFiles,
+            selectedFile: selectedFile,
+            navigate: navigateSerial,
+            selectFile: (path) => {
+                setSelectedFile({
+                    path: path,
+                    device: Device.serial
+                })
+            }
+        }
+    }
+
+    const fileManagementLogic = () : ReturnType<FileManagementType> => {
+        return {
+            upload: (diskPath, serialPath) => {},
+            download: (serialPath, diskPath) => {},
+            remove: () => {}
+        }
+    }
+
+    return {
+        toolbarLogic,
+        diskNavigationLogic,
+        diskFilesLogic,
+        serialNavigationLogic,
+        serialFilesLogic,
+        fileManagementLogic
+    }
+}
+
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
new file mode 100644
index 0000000..dee2604
--- /dev/null
+++ b/ui/ftp/main.scss
@@ -0,0 +1,9 @@
+* { 
+    box-sizing: border-box; 
+}
+html,
+body {
+    margin: 0;
+    width: 100%;
+    height: 100%;
+}
\ No newline at end of file
diff --git a/ui/ftp/main.tsx b/ui/ftp/main.tsx
new file mode 100644
index 0000000..be3b1de
--- /dev/null
+++ b/ui/ftp/main.tsx
@@ -0,0 +1,46 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+
+import Toolbar from './components/toolbar/toolbar'
+import DiskNavigation from './components/navigation/diskNavigation'
+import DiskFiles from './components/files/diskFiles'
+import SerialNavigation from './components/navigation/serialNavigation'
+import SerialFiles from './components/files/serialFiles'
+
+import { useMainLogic } from './main.logic.ts'
+
+const App: React.FC = () => {
+  const { 
+    toolbarLogic,
+    diskNavigationLogic,
+    diskFilesLogic,
+    serialNavigationLogic,
+    serialFilesLogic
+  } = useMainLogic()
+  return (
+    <>
+      <Toolbar toolbarLogic={toolbarLogic}></Toolbar>
+      <div>
+        <div>
+          <DiskNavigation navigationLogic={diskNavigationLogic}></DiskNavigation>
+          <DiskFiles diskFilesLogic={diskFilesLogic}></DiskFiles>
+        </div>
+        <div>
+          <SerialNavigation navigationLogic={serialNavigationLogic}></SerialNavigation>
+          <SerialFiles serialFilesLogic={serialFilesLogic}></SerialFiles>
+        </div>
+      </div>
+    </>
+  )
+}
+
+window.addEventListener('load', () => {
+  window.BridgeWindow.setWindowSize(900, 600)
+  const container : HTMLElement | null = document.querySelector('main')
+  const root = ReactDOM.createRoot(container)
+  root.render(
+    <React.StrictMode>
+      <App />
+    </React.StrictMode>
+  )
+})
diff --git a/ui/ftp/main.type.ts b/ui/ftp/main.type.ts
new file mode 100644
index 0000000..3516891
--- /dev/null
+++ b/ui/ftp/main.type.ts
@@ -0,0 +1,6 @@
+export enum Device { serial, disk }
+
+export type File = {
+    path: String
+    device: Device
+}
\ No newline at end of file
diff --git a/ui/ftp/package-lock.json b/ui/ftp/package-lock.json
new file mode 100644
index 0000000..f66a792
--- /dev/null
+++ b/ui/ftp/package-lock.json
@@ -0,0 +1,2975 @@
+{
+  "name": "ftp",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "ftp",
+      "version": "1.0.0",
+      "dependencies": {
+        "react": "^18.2.0",
+        "react-dom": "^18.2.0"
+      },
+      "devDependencies": {
+        "@parcel/transformer-sass": "^2.8.3",
+        "parcel": "latest",
+        "process": "^0.11.10"
+      }
+    },
+    "node_modules/@babel/code-frame": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
+      "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==",
+      "dev": true,
+      "dependencies": {
+        "@babel/highlight": "^7.18.6"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/helper-validator-identifier": {
+      "version": "7.19.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight": {
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
+      "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+      "dev": true,
+      "dependencies": {
+        "@babel/helper-validator-identifier": "^7.18.6",
+        "chalk": "^2.0.0",
+        "js-tokens": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/chalk": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+      "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
+      "dev": true
+    },
+    "node_modules/@babel/highlight/node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@babel/highlight/node_modules/supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/@jridgewell/gen-mapping": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+      "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/set-array": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@jridgewell/source-map": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+      "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/gen-mapping": "^0.3.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      }
+    },
+    "node_modules/@jridgewell/sourcemap-codec": {
+      "version": "1.4.14",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
+      "dev": true
+    },
+    "node_modules/@jridgewell/trace-mapping": {
+      "version": "0.3.17",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+      "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/resolve-uri": "3.1.0",
+        "@jridgewell/sourcemap-codec": "1.4.14"
+      }
+    },
+    "node_modules/@lezer/common": {
+      "version": "0.15.12",
+      "resolved": "https://registry.npmjs.org/@lezer/common/-/common-0.15.12.tgz",
+      "integrity": "sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==",
+      "dev": true
+    },
+    "node_modules/@lezer/lr": {
+      "version": "0.15.8",
+      "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-0.15.8.tgz",
+      "integrity": "sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==",
+      "dev": true,
+      "dependencies": {
+        "@lezer/common": "^0.15.0"
+      }
+    },
+    "node_modules/@lmdb/lmdb-darwin-arm64": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.5.2.tgz",
+      "integrity": "sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-darwin-x64": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.5.2.tgz",
+      "integrity": "sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.5.2.tgz",
+      "integrity": "sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-arm64": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.5.2.tgz",
+      "integrity": "sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-linux-x64": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.5.2.tgz",
+      "integrity": "sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@lmdb/lmdb-win32-x64": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.5.2.tgz",
+      "integrity": "sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@mischnic/json-sourcemap": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.0.tgz",
+      "integrity": "sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==",
+      "dev": true,
+      "dependencies": {
+        "@lezer/common": "^0.15.7",
+        "@lezer/lr": "^0.15.4",
+        "json5": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.2.tgz",
+      "integrity": "sha512-9bfjwDxIDWmmOKusUcqdS4Rw+SETlp9Dy39Xui9BEGEk19dDwH0jhipwFzEff/pFg95NKymc6TOTbRKcWeRqyQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.2.tgz",
+      "integrity": "sha512-lwriRAHm1Yg4iDf23Oxm9n/t5Zpw1lVnxYU3HnJPTi2lJRkKTrps1KVgvL6m7WvmhYVt/FIsssWay+k45QHeuw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.2.tgz",
+      "integrity": "sha512-MOI9Dlfrpi2Cuc7i5dXdxPbFIgbDBGgKR5F2yWEa6FVEtSWncfVNKW5AKjImAQ6CZlBK9tympdsZJ2xThBiWWA==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.2.tgz",
+      "integrity": "sha512-FU20Bo66/f7He9Fp9sP2zaJ1Q8L9uLPZQDub/WlUip78JlPeMbVL8546HbZfcW9LNciEXc8d+tThSJjSC+tmsg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.2.tgz",
+      "integrity": "sha512-gsWNDCklNy7Ajk0vBBf9jEx04RUxuDQfBse918Ww+Qb9HCPoGzS+XJTLe96iN3BVK7grnLiYghP/M4L8VsaHeA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ]
+    },
+    "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.2.tgz",
+      "integrity": "sha512-O+6Gs8UeDbyFpbSh2CPEz/UOrrdWPTBYNblZK5CxxLisYt4kGX3Sc+czffFonyjiGSq3jWLwJS/CCJc7tBr4sQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ]
+    },
+    "node_modules/@parcel/bundler-default": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.8.3.tgz",
+      "integrity": "sha512-yJvRsNWWu5fVydsWk3O2L4yIy3UZiKWO2cPDukGOIWMgp/Vbpp+2Ct5IygVRtE22bnseW/E/oe0PV3d2IkEJGg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/graph": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/cache": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.8.3.tgz",
+      "integrity": "sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/fs": "2.8.3",
+        "@parcel/logger": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "lmdb": "2.5.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@parcel/codeframe": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.8.3.tgz",
+      "integrity": "sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/compressor-raw": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.8.3.tgz",
+      "integrity": "sha512-bVDsqleBUxRdKMakWSlWC9ZjOcqDKE60BE+Gh3JSN6WJrycJ02P5wxjTVF4CStNP/G7X17U+nkENxSlMG77ySg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/config-default": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.8.3.tgz",
+      "integrity": "sha512-o/A/mbrO6X/BfGS65Sib8d6SSG45NYrNooNBkH/o7zbOBSRQxwyTlysleK1/3Wa35YpvFyLOwgfakqCtbGy4fw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/bundler-default": "2.8.3",
+        "@parcel/compressor-raw": "2.8.3",
+        "@parcel/namer-default": "2.8.3",
+        "@parcel/optimizer-css": "2.8.3",
+        "@parcel/optimizer-htmlnano": "2.8.3",
+        "@parcel/optimizer-image": "2.8.3",
+        "@parcel/optimizer-svgo": "2.8.3",
+        "@parcel/optimizer-terser": "2.8.3",
+        "@parcel/packager-css": "2.8.3",
+        "@parcel/packager-html": "2.8.3",
+        "@parcel/packager-js": "2.8.3",
+        "@parcel/packager-raw": "2.8.3",
+        "@parcel/packager-svg": "2.8.3",
+        "@parcel/reporter-dev-server": "2.8.3",
+        "@parcel/resolver-default": "2.8.3",
+        "@parcel/runtime-browser-hmr": "2.8.3",
+        "@parcel/runtime-js": "2.8.3",
+        "@parcel/runtime-react-refresh": "2.8.3",
+        "@parcel/runtime-service-worker": "2.8.3",
+        "@parcel/transformer-babel": "2.8.3",
+        "@parcel/transformer-css": "2.8.3",
+        "@parcel/transformer-html": "2.8.3",
+        "@parcel/transformer-image": "2.8.3",
+        "@parcel/transformer-js": "2.8.3",
+        "@parcel/transformer-json": "2.8.3",
+        "@parcel/transformer-postcss": "2.8.3",
+        "@parcel/transformer-posthtml": "2.8.3",
+        "@parcel/transformer-raw": "2.8.3",
+        "@parcel/transformer-react-refresh-wrap": "2.8.3",
+        "@parcel/transformer-svg": "2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@parcel/core": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.8.3.tgz",
+      "integrity": "sha512-Euf/un4ZAiClnlUXqPB9phQlKbveU+2CotZv7m7i+qkgvFn5nAGnrV4h1OzQU42j9dpgOxWi7AttUDMrvkbhCQ==",
+      "dev": true,
+      "dependencies": {
+        "@mischnic/json-sourcemap": "^0.1.0",
+        "@parcel/cache": "2.8.3",
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/events": "2.8.3",
+        "@parcel/fs": "2.8.3",
+        "@parcel/graph": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/logger": "2.8.3",
+        "@parcel/package-manager": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "@parcel/workers": "2.8.3",
+        "abortcontroller-polyfill": "^1.1.9",
+        "base-x": "^3.0.8",
+        "browserslist": "^4.6.6",
+        "clone": "^2.1.1",
+        "dotenv": "^7.0.0",
+        "dotenv-expand": "^5.1.0",
+        "json5": "^2.2.0",
+        "msgpackr": "^1.5.4",
+        "nullthrows": "^1.1.1",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/diagnostic": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.8.3.tgz",
+      "integrity": "sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ==",
+      "dev": true,
+      "dependencies": {
+        "@mischnic/json-sourcemap": "^0.1.0",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/events": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.8.3.tgz",
+      "integrity": "sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/fs": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.8.3.tgz",
+      "integrity": "sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/fs-search": "2.8.3",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "@parcel/watcher": "^2.0.7",
+        "@parcel/workers": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@parcel/fs-search": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/fs-search/-/fs-search-2.8.3.tgz",
+      "integrity": "sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ==",
+      "dev": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/graph": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-2.8.3.tgz",
+      "integrity": "sha512-26GL8fYZPdsRhSXCZ0ZWliloK6DHlMJPWh6Z+3VVZ5mnDSbYg/rRKWmrkhnr99ZWmL9rJsv4G74ZwvDEXTMPBg==",
+      "dev": true,
+      "dependencies": {
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/hash": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/hash/-/hash-2.8.3.tgz",
+      "integrity": "sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw==",
+      "dev": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3",
+        "xxhash-wasm": "^0.4.2"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/logger": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.8.3.tgz",
+      "integrity": "sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/events": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/markdown-ansi": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.8.3.tgz",
+      "integrity": "sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/namer-default": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.8.3.tgz",
+      "integrity": "sha512-tJ7JehZviS5QwnxbARd8Uh63rkikZdZs1QOyivUhEvhN+DddSAVEdQLHGPzkl3YRk0tjFhbqo+Jci7TpezuAMw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/node-resolver-core": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-2.8.3.tgz",
+      "integrity": "sha512-12YryWcA5Iw2WNoEVr/t2HDjYR1iEzbjEcxfh1vaVDdZ020PiGw67g5hyIE/tsnG7SRJ0xdRx1fQ2hDgED+0Ww==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-css": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.8.3.tgz",
+      "integrity": "sha512-JotGAWo8JhuXsQDK0UkzeQB0UR5hDAKvAviXrjqB4KM9wZNLhLleeEAW4Hk8R9smCeQFP6Xg/N/NkLDpqMwT3g==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "browserslist": "^4.6.6",
+        "lightningcss": "^1.16.1",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-htmlnano": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.8.3.tgz",
+      "integrity": "sha512-L8/fHbEy8Id2a2E0fwR5eKGlv9VYDjrH9PwdJE9Za9v1O/vEsfl/0T/79/x129l5O0yB6EFQkFa20MiK3b+vOg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "htmlnano": "^2.0.0",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "svgo": "^2.4.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-image": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.8.3.tgz",
+      "integrity": "sha512-SD71sSH27SkCDNUNx9A3jizqB/WIJr3dsfp+JZGZC42tpD/Siim6Rqy9M4To/BpMMQIIiEXa5ofwS+DgTEiEHQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "@parcel/workers": "2.8.3",
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-svgo": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.8.3.tgz",
+      "integrity": "sha512-9KQed99NZnQw3/W4qBYVQ7212rzA9EqrQG019TIWJzkA9tjGBMIm2c/nXpK1tc3hQ3e7KkXkFCQ3C+ibVUnHNA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "svgo": "^2.4.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/optimizer-terser": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/optimizer-terser/-/optimizer-terser-2.8.3.tgz",
+      "integrity": "sha512-9EeQlN6zIeUWwzrzu6Q2pQSaYsYGah8MtiQ/hog9KEPlYTP60hBv/+utDyYEHSQhL7y5ym08tPX5GzBvwAD/dA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1",
+        "terser": "^5.2.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/package-manager": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.8.3.tgz",
+      "integrity": "sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/fs": "2.8.3",
+        "@parcel/logger": "2.8.3",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "@parcel/workers": "2.8.3",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@parcel/packager-css": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.8.3.tgz",
+      "integrity": "sha512-WyvkMmsurlHG8d8oUVm7S+D+cC/T3qGeqogb7sTI52gB6uiywU7lRCizLNqGFyFGIxcVTVHWnSHqItBcLN76lA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-html": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.8.3.tgz",
+      "integrity": "sha512-OhPu1Hx1RRKJodpiu86ZqL8el2Aa4uhBHF6RAL1Pcrh2EhRRlPf70Sk0tC22zUpYL7es+iNKZ/n0Rl+OWSHWEw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-js": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.8.3.tgz",
+      "integrity": "sha512-0pGKC3Ax5vFuxuZCRB+nBucRfFRz4ioie19BbDxYnvBxrd4M3FIu45njf6zbBYsI9eXqaDnL1b3DcZJfYqtIzw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "globals": "^13.2.0",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-raw": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.8.3.tgz",
+      "integrity": "sha512-BA6enNQo1RCnco9MhkxGrjOk59O71IZ9DPKu3lCtqqYEVd823tXff2clDKHK25i6cChmeHu6oB1Rb73hlPqhUA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/packager-svg": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.8.3.tgz",
+      "integrity": "sha512-mvIoHpmv5yzl36OjrklTDFShLUfPFTwrmp1eIwiszGdEBuQaX7JVI3Oo2jbVQgcN4W7J6SENzGQ3Q5hPTW3pMw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "posthtml": "^0.16.4"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/plugin": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.8.3.tgz",
+      "integrity": "sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/types": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/reporter-cli": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.8.3.tgz",
+      "integrity": "sha512-3sJkS6tFFzgIOz3u3IpD/RsmRxvOKKiQHOTkiiqRt1l44mMDGKS7zANRnJYsQzdCsgwc9SOP30XFgJwtoVlMbw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "chalk": "^4.1.0",
+        "term-size": "^2.2.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/reporter-dev-server": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.8.3.tgz",
+      "integrity": "sha512-Y8C8hzgzTd13IoWTj+COYXEyCkXfmVJs3//GDBsH22pbtSFMuzAZd+8J9qsCo0EWpiDow7V9f1LischvEh3FbQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/resolver-default": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.8.3.tgz",
+      "integrity": "sha512-k0B5M/PJ+3rFbNj4xZSBr6d6HVIe6DH/P3dClLcgBYSXAvElNDfXgtIimbjCyItFkW9/BfcgOVKEEIZOeySH/A==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/node-resolver-core": "2.8.3",
+        "@parcel/plugin": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-browser-hmr": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.8.3.tgz",
+      "integrity": "sha512-2O1PYi2j/Q0lTyGNV3JdBYwg4rKo6TEVFlYGdd5wCYU9ZIN9RRuoCnWWH2qCPj3pjIVtBeppYxzfVjPEHINWVg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-js": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.8.3.tgz",
+      "integrity": "sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-react-refresh": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.8.3.tgz",
+      "integrity": "sha512-2v/qFKp00MfG0234OdOgQNAo6TLENpFYZMbVbAsPMY9ITiqG73MrEsrGXVoGbYiGTMB/Toer/lSWlJxtacOCuA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "react-error-overlay": "6.0.9",
+        "react-refresh": "^0.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/runtime-service-worker": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.8.3.tgz",
+      "integrity": "sha512-/Skkw+EeRiwzOJso5fQtK8c9b452uWLNhQH1ISTodbmlcyB4YalAiSsyHCtMYD0c3/t5Sx4ZS7vxBAtQd0RvOw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/source-map": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz",
+      "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==",
+      "dev": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": "^12.18.3 || >=14"
+      }
+    },
+    "node_modules/@parcel/transformer-babel": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.8.3.tgz",
+      "integrity": "sha512-L6lExfpvvC7T/g3pxf3CIJRouQl+sgrSzuWQ0fD4PemUDHvHchSP4SNUVnd6gOytF3Y1KpnEZIunQGi5xVqQCQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "browserslist": "^4.6.6",
+        "json5": "^2.2.0",
+        "nullthrows": "^1.1.1",
+        "semver": "^5.7.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-css": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.8.3.tgz",
+      "integrity": "sha512-xTqFwlSXtnaYen9ivAgz+xPW7yRl/u4QxtnDyDpz5dr8gSeOpQYRcjkd4RsYzKsWzZcGtB5EofEk8ayUbWKEUg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "browserslist": "^4.6.6",
+        "lightningcss": "^1.16.1",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-html": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.8.3.tgz",
+      "integrity": "sha512-kIZO3qsMYTbSnSpl9cnZog+SwL517ffWH54JeB410OSAYF1ouf4n5v9qBnALZbuCCmPwJRGs4jUtE452hxwN4g==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "posthtml-parser": "^0.10.1",
+        "posthtml-render": "^3.0.0",
+        "semver": "^5.7.1",
+        "srcset": "4"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-image": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.8.3.tgz",
+      "integrity": "sha512-cO4uptcCGTi5H6bvTrAWEFUsTNhA4kCo8BSvRSCHA2sf/4C5tGQPHt3JhdO0GQLPwZRCh/R41EkJs5HZ8A8DAg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "@parcel/workers": "2.8.3",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@parcel/transformer-js": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.8.3.tgz",
+      "integrity": "sha512-9Qd6bib+sWRcpovvzvxwy/PdFrLUXGfmSW9XcVVG8pvgXsZPFaNjnNT8stzGQj1pQiougCoxMY4aTM5p1lGHEQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/utils": "2.8.3",
+        "@parcel/workers": "2.8.3",
+        "@swc/helpers": "^0.4.12",
+        "browserslist": "^4.6.6",
+        "detect-libc": "^1.0.3",
+        "nullthrows": "^1.1.1",
+        "regenerator-runtime": "^0.13.7",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@parcel/transformer-json": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.8.3.tgz",
+      "integrity": "sha512-B7LmVq5Q7bZO4ERb6NHtRuUKWGysEeaj9H4zelnyBv+wLgpo4f5FCxSE1/rTNmP9u1qHvQ3scGdK6EdSSokGPg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "json5": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-postcss": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.8.3.tgz",
+      "integrity": "sha512-e8luB/poIlz6jBsD1Izms+6ElbyzuoFVa4lFVLZnTAChI3UxPdt9p/uTsIO46HyBps/Bk8ocvt3J4YF84jzmvg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "clone": "^2.1.1",
+        "nullthrows": "^1.1.1",
+        "postcss-value-parser": "^4.2.0",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-posthtml": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.8.3.tgz",
+      "integrity": "sha512-pkzf9Smyeaw4uaRLsT41RGrPLT5Aip8ZPcntawAfIo+KivBQUV0erY1IvHYjyfFzq1ld/Fo2Ith9He6mxpPifA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "posthtml-parser": "^0.10.1",
+        "posthtml-render": "^3.0.0",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-raw": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.8.3.tgz",
+      "integrity": "sha512-G+5cXnd2/1O3nV/pgRxVKZY/HcGSseuhAe71gQdSQftb8uJEURyUHoQ9Eh0JUD3MgWh9V+nIKoyFEZdf9T0sUQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-react-refresh-wrap": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.8.3.tgz",
+      "integrity": "sha512-q8AAoEvBnCf/nPvgOwFwKZfEl/thwq7c2duxXkhl+tTLDRN2vGmyz4355IxCkavSX+pLWSQ5MexklSEeMkgthg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "react-refresh": "^0.9.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-sass": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-sass/-/transformer-sass-2.8.3.tgz",
+      "integrity": "sha512-ak196rjvXdsBOGi5aTkBEKv6i4LKQgOkHuaKEjeT8g2a3CU6Z36J+j2GbZzsznfws/hH+CRTf8bAsbkxtKlkjQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/plugin": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "sass": "^1.38.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/transformer-svg": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.8.3.tgz",
+      "integrity": "sha512-3Zr/gBzxi1ZH1fftH/+KsZU7w5GqkmxlB0ZM8ovS5E/Pl1lq1t0xvGJue9m2VuQqP8Mxfpl5qLFmsKlhaZdMIQ==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/plugin": "2.8.3",
+        "nullthrows": "^1.1.1",
+        "posthtml": "^0.16.5",
+        "posthtml-parser": "^0.10.1",
+        "posthtml-render": "^3.0.0",
+        "semver": "^5.7.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0",
+        "parcel": "^2.8.3"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/types": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.8.3.tgz",
+      "integrity": "sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/cache": "2.8.3",
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/fs": "2.8.3",
+        "@parcel/package-manager": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "@parcel/workers": "2.8.3",
+        "utility-types": "^3.10.0"
+      }
+    },
+    "node_modules/@parcel/utils": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.8.3.tgz",
+      "integrity": "sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/codeframe": "2.8.3",
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/hash": "2.8.3",
+        "@parcel/logger": "2.8.3",
+        "@parcel/markdown-ansi": "2.8.3",
+        "@parcel/source-map": "^2.1.1",
+        "chalk": "^4.1.0"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/watcher": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.1.0.tgz",
+      "integrity": "sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==",
+      "dev": true,
+      "hasInstallScript": true,
+      "dependencies": {
+        "is-glob": "^4.0.3",
+        "micromatch": "^4.0.5",
+        "node-addon-api": "^3.2.1",
+        "node-gyp-build": "^4.3.0"
+      },
+      "engines": {
+        "node": ">= 10.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/@parcel/workers": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.8.3.tgz",
+      "integrity": "sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/logger": "2.8.3",
+        "@parcel/types": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "chrome-trace-event": "^1.0.2",
+        "nullthrows": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "peerDependencies": {
+        "@parcel/core": "^2.8.3"
+      }
+    },
+    "node_modules/@swc/helpers": {
+      "version": "0.4.14",
+      "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.4.14.tgz",
+      "integrity": "sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==",
+      "dev": true,
+      "dependencies": {
+        "tslib": "^2.4.0"
+      }
+    },
+    "node_modules/@trysound/sax": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
+      "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/@types/parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+      "dev": true
+    },
+    "node_modules/abortcontroller-polyfill": {
+      "version": "1.7.5",
+      "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz",
+      "integrity": "sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==",
+      "dev": true
+    },
+    "node_modules/acorn": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
+      "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==",
+      "dev": true,
+      "bin": {
+        "acorn": "bin/acorn"
+      },
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/base-x": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz",
+      "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==",
+      "dev": true,
+      "dependencies": {
+        "safe-buffer": "^5.0.1"
+      }
+    },
+    "node_modules/binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+      "dev": true
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/browserslist": {
+      "version": "4.21.5",
+      "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
+      "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        }
+      ],
+      "dependencies": {
+        "caniuse-lite": "^1.0.30001449",
+        "electron-to-chromium": "^1.4.284",
+        "node-releases": "^2.0.8",
+        "update-browserslist-db": "^1.0.10"
+      },
+      "bin": {
+        "browserslist": "cli.js"
+      },
+      "engines": {
+        "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+      }
+    },
+    "node_modules/buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+      "dev": true
+    },
+    "node_modules/callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/caniuse-lite": {
+      "version": "1.0.30001460",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001460.tgz",
+      "integrity": "sha512-Bud7abqjvEjipUkpLs4D7gR0l8hBYBHoa+tGtKJHvT2AYzLp1z7EmVkUT4ERpVUfca8S2HGIVs883D8pUH1ZzQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+        }
+      ]
+    },
+    "node_modules/chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://paulmillr.com/funding/"
+        }
+      ],
+      "dependencies": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/chrome-trace-event": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz",
+      "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6.0"
+      }
+    },
+    "node_modules/clone": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+      "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "~1.1.4"
+      },
+      "engines": {
+        "node": ">=7.0.0"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "node_modules/commander": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+      "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/cosmiconfig": {
+      "version": "7.1.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
+      "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
+      "dev": true,
+      "dependencies": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.2.1",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.10.0"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/css-select": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz",
+      "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.0.1",
+        "domhandler": "^4.3.1",
+        "domutils": "^2.8.0",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-tree": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
+      "integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
+      "dev": true,
+      "dependencies": {
+        "mdn-data": "2.0.14",
+        "source-map": "^0.6.1"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/csso": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/csso/-/csso-4.2.0.tgz",
+      "integrity": "sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==",
+      "dev": true,
+      "dependencies": {
+        "css-tree": "^1.1.2"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/detect-libc": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+      "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+      "dev": true,
+      "bin": {
+        "detect-libc": "bin/detect-libc.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+      "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.0",
+        "entities": "^2.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/dom-serializer/node_modules/entities": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+      "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+      "dev": true,
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
+    "node_modules/domhandler": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+      "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+      "dev": true,
+      "dependencies": {
+        "domelementtype": "^2.2.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+      "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+      "dev": true,
+      "dependencies": {
+        "dom-serializer": "^1.0.1",
+        "domelementtype": "^2.2.0",
+        "domhandler": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/dotenv": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-7.0.0.tgz",
+      "integrity": "sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/dotenv-expand": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+      "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
+      "dev": true
+    },
+    "node_modules/electron-to-chromium": {
+      "version": "1.4.320",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.320.tgz",
+      "integrity": "sha512-h70iRscrNluMZPVICXYl5SSB+rBKo22XfuIS1ER0OQxQZpKTnFpuS6coj7wY9M/3trv7OR88rRMOlKmRvDty7Q==",
+      "dev": true
+    },
+    "node_modules/entities": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+      "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/escalade": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+      "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/escape-string-regexp": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+      "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/get-port": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz",
+      "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/globals": {
+      "version": "13.20.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+      "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+      "dev": true,
+      "dependencies": {
+        "type-fest": "^0.20.2"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/htmlnano": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.0.3.tgz",
+      "integrity": "sha512-S4PGGj9RbdgW8LhbILNK7W9JhmYP8zmDY7KDV/8eCiJBQJlbmltp5I0gv8c5ntLljfdxxfmJ+UJVSqyH4mb41A==",
+      "dev": true,
+      "dependencies": {
+        "cosmiconfig": "^7.0.1",
+        "posthtml": "^0.16.5",
+        "timsort": "^0.3.0"
+      },
+      "peerDependencies": {
+        "cssnano": "^5.0.11",
+        "postcss": "^8.3.11",
+        "purgecss": "^5.0.0",
+        "relateurl": "^0.2.7",
+        "srcset": "4.0.0",
+        "svgo": "^2.8.0",
+        "terser": "^5.10.0",
+        "uncss": "^0.17.3"
+      },
+      "peerDependenciesMeta": {
+        "cssnano": {
+          "optional": true
+        },
+        "postcss": {
+          "optional": true
+        },
+        "purgecss": {
+          "optional": true
+        },
+        "relateurl": {
+          "optional": true
+        },
+        "srcset": {
+          "optional": true
+        },
+        "svgo": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        },
+        "uncss": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz",
+      "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==",
+      "dev": true,
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "dependencies": {
+        "domelementtype": "^2.0.1",
+        "domhandler": "^4.2.2",
+        "domutils": "^2.8.0",
+        "entities": "^3.0.1"
+      }
+    },
+    "node_modules/immutable": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.2.4.tgz",
+      "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w==",
+      "dev": true
+    },
+    "node_modules/import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "dependencies": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
+      "dev": true
+    },
+    "node_modules/is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/is-json": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz",
+      "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==",
+      "dev": true
+    },
+    "node_modules/is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/js-tokens": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+      "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
+    },
+    "node_modules/json-parse-even-better-errors": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
+      "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
+      "dev": true
+    },
+    "node_modules/json5": {
+      "version": "2.2.3",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+      "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+      "dev": true,
+      "bin": {
+        "json5": "lib/cli.js"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/lightningcss": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.19.0.tgz",
+      "integrity": "sha512-yV5UR7og+Og7lQC+70DA7a8ta1uiOPnWPJfxa0wnxylev5qfo4P+4iMpzWAdYWOca4jdNQZii+bDL/l+4hUXIA==",
+      "dev": true,
+      "dependencies": {
+        "detect-libc": "^1.0.3"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      },
+      "optionalDependencies": {
+        "lightningcss-darwin-arm64": "1.19.0",
+        "lightningcss-darwin-x64": "1.19.0",
+        "lightningcss-linux-arm-gnueabihf": "1.19.0",
+        "lightningcss-linux-arm64-gnu": "1.19.0",
+        "lightningcss-linux-arm64-musl": "1.19.0",
+        "lightningcss-linux-x64-gnu": "1.19.0",
+        "lightningcss-linux-x64-musl": "1.19.0",
+        "lightningcss-win32-x64-msvc": "1.19.0"
+      }
+    },
+    "node_modules/lightningcss-darwin-arm64": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.19.0.tgz",
+      "integrity": "sha512-wIJmFtYX0rXHsXHSr4+sC5clwblEMji7HHQ4Ub1/CznVRxtCFha6JIt5JZaNf8vQrfdZnBxLLC6R8pC818jXqg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-darwin-x64": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.19.0.tgz",
+      "integrity": "sha512-Lif1wD6P4poaw9c/4Uh2z+gmrWhw/HtXFoeZ3bEsv6Ia4tt8rOJBdkfVaUJ6VXmpKHALve+iTyP2+50xY1wKPw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm-gnueabihf": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.19.0.tgz",
+      "integrity": "sha512-P15VXY5682mTXaiDtbnLYQflc8BYb774j2R84FgDLJTN6Qp0ZjWEFyN1SPqyfTj2B2TFjRHRUvQSSZ7qN4Weig==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-gnu": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.19.0.tgz",
+      "integrity": "sha512-zwXRjWqpev8wqO0sv0M1aM1PpjHz6RVIsBcxKszIG83Befuh4yNysjgHVplF9RTU7eozGe3Ts7r6we1+Qkqsww==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-arm64-musl": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.19.0.tgz",
+      "integrity": "sha512-vSCKO7SDnZaFN9zEloKSZM5/kC5gbzUjoJQ43BvUpyTFUX7ACs/mDfl2Eq6fdz2+uWhUh7vf92c4EaaP4udEtA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-gnu": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.19.0.tgz",
+      "integrity": "sha512-0AFQKvVzXf9byrXUq9z0anMGLdZJS+XSDqidyijI5njIwj6MdbvX2UZK/c4FfNmeRa2N/8ngTffoIuOUit5eIQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-linux-x64-musl": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.19.0.tgz",
+      "integrity": "sha512-SJoM8CLPt6ECCgSuWe+g0qo8dqQYVcPiW2s19dxkmSI5+Uu1GIRzyKA0b7QqmEXolA+oSJhQqCmJpzjY4CuZAg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lightningcss-win32-x64-msvc": {
+      "version": "1.19.0",
+      "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.19.0.tgz",
+      "integrity": "sha512-C+VuUTeSUOAaBZZOPT7Etn/agx/MatzJzGRkeV+zEABmPuntv1zihncsi+AyGmjkkzq3wVedEy7h0/4S84mUtg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/lines-and-columns": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+      "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+      "dev": true
+    },
+    "node_modules/lmdb": {
+      "version": "2.5.2",
+      "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.5.2.tgz",
+      "integrity": "sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "dependencies": {
+        "msgpackr": "^1.5.4",
+        "node-addon-api": "^4.3.0",
+        "node-gyp-build-optional-packages": "5.0.3",
+        "ordered-binary": "^1.2.4",
+        "weak-lru-cache": "^1.2.2"
+      },
+      "optionalDependencies": {
+        "@lmdb/lmdb-darwin-arm64": "2.5.2",
+        "@lmdb/lmdb-darwin-x64": "2.5.2",
+        "@lmdb/lmdb-linux-arm": "2.5.2",
+        "@lmdb/lmdb-linux-arm64": "2.5.2",
+        "@lmdb/lmdb-linux-x64": "2.5.2",
+        "@lmdb/lmdb-win32-x64": "2.5.2"
+      }
+    },
+    "node_modules/lmdb/node_modules/node-addon-api": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz",
+      "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==",
+      "dev": true
+    },
+    "node_modules/loose-envify": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+      "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+      "dependencies": {
+        "js-tokens": "^3.0.0 || ^4.0.0"
+      },
+      "bin": {
+        "loose-envify": "cli.js"
+      }
+    },
+    "node_modules/mdn-data": {
+      "version": "2.0.14",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
+      "integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
+      "dev": true
+    },
+    "node_modules/micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "dependencies": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      },
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/msgpackr": {
+      "version": "1.8.4",
+      "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.8.4.tgz",
+      "integrity": "sha512-BE3hD3PqV7jsNaV022uq0jMW+ZVc32wSYyQmwAoJUc+vPtCeyro2MOtAW61Fd9ZKNySM6y913E9fBY0mG+hKXg==",
+      "dev": true,
+      "optionalDependencies": {
+        "msgpackr-extract": "^3.0.1"
+      }
+    },
+    "node_modules/msgpackr-extract": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.2.tgz",
+      "integrity": "sha512-SdzXp4kD/Qf8agZ9+iTu6eql0m3kWm1A2y1hkpTeVNENutaB0BwHlSvAIaMxwntmRUAUjon2V4L8Z/njd0Ct8A==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "dependencies": {
+        "node-gyp-build-optional-packages": "5.0.7"
+      },
+      "bin": {
+        "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
+      },
+      "optionalDependencies": {
+        "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.2",
+        "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.2"
+      }
+    },
+    "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.7.tgz",
+      "integrity": "sha512-YlCCc6Wffkx0kHkmam79GKvDQ6x+QZkMjFGrIMxgFNILFvGSbCp2fCBC55pGTT9gVaz8Na5CLmxt/urtzRv36w==",
+      "dev": true,
+      "optional": true,
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/node-addon-api": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz",
+      "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==",
+      "dev": true
+    },
+    "node_modules/node-gyp-build": {
+      "version": "4.6.0",
+      "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.0.tgz",
+      "integrity": "sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ==",
+      "dev": true,
+      "bin": {
+        "node-gyp-build": "bin.js",
+        "node-gyp-build-optional": "optional.js",
+        "node-gyp-build-test": "build-test.js"
+      }
+    },
+    "node_modules/node-gyp-build-optional-packages": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.0.3.tgz",
+      "integrity": "sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==",
+      "dev": true,
+      "bin": {
+        "node-gyp-build-optional-packages": "bin.js",
+        "node-gyp-build-optional-packages-optional": "optional.js",
+        "node-gyp-build-optional-packages-test": "build-test.js"
+      }
+    },
+    "node_modules/node-releases": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
+      "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
+      "dev": true
+    },
+    "node_modules/normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dev": true,
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/nullthrows": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz",
+      "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==",
+      "dev": true
+    },
+    "node_modules/ordered-binary": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.4.0.tgz",
+      "integrity": "sha512-EHQ/jk4/a9hLupIKxTfUsQRej1Yd/0QLQs3vGvIqg5ZtCYSzNhkzHoZc7Zf4e4kUlDaC3Uw8Q/1opOLNN2OKRQ==",
+      "dev": true
+    },
+    "node_modules/parcel": {
+      "version": "2.8.3",
+      "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.8.3.tgz",
+      "integrity": "sha512-5rMBpbNE72g6jZvkdR5gS2nyhwIXaJy8i65osOqs/+5b7zgf3eMKgjSsDrv6bhz3gzifsba6MBJiZdBckl+vnA==",
+      "dev": true,
+      "dependencies": {
+        "@parcel/config-default": "2.8.3",
+        "@parcel/core": "2.8.3",
+        "@parcel/diagnostic": "2.8.3",
+        "@parcel/events": "2.8.3",
+        "@parcel/fs": "2.8.3",
+        "@parcel/logger": "2.8.3",
+        "@parcel/package-manager": "2.8.3",
+        "@parcel/reporter-cli": "2.8.3",
+        "@parcel/reporter-dev-server": "2.8.3",
+        "@parcel/utils": "2.8.3",
+        "chalk": "^4.1.0",
+        "commander": "^7.0.0",
+        "get-port": "^4.2.0",
+        "v8-compile-cache": "^2.0.0"
+      },
+      "bin": {
+        "parcel": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">= 12.0.0"
+      },
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/parcel"
+      }
+    },
+    "node_modules/parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "dependencies": {
+        "callsites": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/parse-json": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
+      "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
+      "dev": true,
+      "dependencies": {
+        "@babel/code-frame": "^7.0.0",
+        "error-ex": "^1.3.1",
+        "json-parse-even-better-errors": "^2.3.0",
+        "lines-and-columns": "^1.1.6"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
+      "dev": true
+    },
+    "node_modules/picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/jonschlinkert"
+      }
+    },
+    "node_modules/postcss-value-parser": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+      "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+      "dev": true
+    },
+    "node_modules/posthtml": {
+      "version": "0.16.6",
+      "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz",
+      "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==",
+      "dev": true,
+      "dependencies": {
+        "posthtml-parser": "^0.11.0",
+        "posthtml-render": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/posthtml-parser": {
+      "version": "0.10.2",
+      "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.10.2.tgz",
+      "integrity": "sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==",
+      "dev": true,
+      "dependencies": {
+        "htmlparser2": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/posthtml-render": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz",
+      "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==",
+      "dev": true,
+      "dependencies": {
+        "is-json": "^2.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/posthtml/node_modules/posthtml-parser": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz",
+      "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==",
+      "dev": true,
+      "dependencies": {
+        "htmlparser2": "^7.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/process": {
+      "version": "0.11.10",
+      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+      "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
+    "node_modules/react": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
+      "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/react-dom": {
+      "version": "18.2.0",
+      "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
+      "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
+      "dependencies": {
+        "loose-envify": "^1.1.0",
+        "scheduler": "^0.23.0"
+      },
+      "peerDependencies": {
+        "react": "^18.2.0"
+      }
+    },
+    "node_modules/react-error-overlay": {
+      "version": "6.0.9",
+      "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
+      "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==",
+      "dev": true
+    },
+    "node_modules/react-refresh": {
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.9.0.tgz",
+      "integrity": "sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/regenerator-runtime": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
+      "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
+      "dev": true
+    },
+    "node_modules/resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/feross"
+        },
+        {
+          "type": "patreon",
+          "url": "https://www.patreon.com/feross"
+        },
+        {
+          "type": "consulting",
+          "url": "https://feross.org/support"
+        }
+      ]
+    },
+    "node_modules/sass": {
+      "version": "1.58.3",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.58.3.tgz",
+      "integrity": "sha512-Q7RaEtYf6BflYrQ+buPudKR26/lH+10EmO9bBqbmPh/KeLqv8bjpTNqxe71ocONqXq+jYiCbpPUmQMS+JJPk4A==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/scheduler": {
+      "version": "0.23.0",
+      "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
+      "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
+      "dependencies": {
+        "loose-envify": "^1.1.0"
+      }
+    },
+    "node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "dev": true,
+      "dependencies": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "node_modules/srcset": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz",
+      "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/stable": {
+      "version": "0.1.8",
+      "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
+      "integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
+      "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
+      "dev": true
+    },
+    "node_modules/supports-color": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+      "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+      "dev": true,
+      "dependencies": {
+        "has-flag": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/svgo": {
+      "version": "2.8.0",
+      "resolved": "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz",
+      "integrity": "sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==",
+      "dev": true,
+      "dependencies": {
+        "@trysound/sax": "0.2.0",
+        "commander": "^7.2.0",
+        "css-select": "^4.1.3",
+        "css-tree": "^1.1.3",
+        "csso": "^4.2.0",
+        "picocolors": "^1.0.0",
+        "stable": "^0.1.8"
+      },
+      "bin": {
+        "svgo": "bin/svgo"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      }
+    },
+    "node_modules/term-size": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
+      "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/terser": {
+      "version": "5.16.5",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.5.tgz",
+      "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/source-map": "^0.3.2",
+        "acorn": "^8.5.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      },
+      "bin": {
+        "terser": "bin/terser"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/terser/node_modules/commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+      "dev": true
+    },
+    "node_modules/timsort": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
+      "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==",
+      "dev": true
+    },
+    "node_modules/to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/tslib": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz",
+      "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==",
+      "dev": true
+    },
+    "node_modules/type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/update-browserslist-db": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+      "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/browserslist"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/browserslist"
+        }
+      ],
+      "dependencies": {
+        "escalade": "^3.1.1",
+        "picocolors": "^1.0.0"
+      },
+      "bin": {
+        "browserslist-lint": "cli.js"
+      },
+      "peerDependencies": {
+        "browserslist": ">= 4.21.0"
+      }
+    },
+    "node_modules/utility-types": {
+      "version": "3.10.0",
+      "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.10.0.tgz",
+      "integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/v8-compile-cache": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
+      "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
+      "dev": true
+    },
+    "node_modules/weak-lru-cache": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
+      "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==",
+      "dev": true
+    },
+    "node_modules/xxhash-wasm": {
+      "version": "0.4.2",
+      "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-0.4.2.tgz",
+      "integrity": "sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==",
+      "dev": true
+    },
+    "node_modules/yaml": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
+      "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 6"
+      }
+    }
+  }
+}
diff --git a/ui/ftp/package.json b/ui/ftp/package.json
new file mode 100644
index 0000000..cb1692f
--- /dev/null
+++ b/ui/ftp/package.json
@@ -0,0 +1,20 @@
+{
+  "name": "ftp",
+  "version": "1.0.0",
+  "description": "FTP Frontend for MPYLAB",
+  "source": "index.html",
+  "scripts": {
+    "start": "parcel",
+    "dev": "parcel watch --public-url ./",
+    "build": "parcel build --public-url ./"
+  },
+  "devDependencies": {
+    "@parcel/transformer-sass": "^2.8.3",
+    "parcel": "latest",
+    "process": "^0.11.10"
+  },
+  "dependencies": {
+    "react": "^18.2.0",
+    "react-dom": "^18.2.0"
+  }
+}

From 8dc5027380c93842aeda066a81ebc8bb64040d9d Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Tue, 4 Apr 2023 00:21:21 +0200
Subject: [PATCH 2/9] Navigating, selecting multiple files and file management

---
 preload.js                                    |  17 +-
 ui/ftp/components/files/diskFiles.tsx         |  28 +-
 ui/ftp/components/files/diskFiles.type.ts     |  11 +-
 ui/ftp/components/files/fileManagement.tsx    |  16 +
 .../components/files/fileManagement.type.ts   |   7 +
 ui/ftp/components/files/management.type.ts    |   7 -
 ui/ftp/components/files/serialFiles.tsx       |  34 +-
 ui/ftp/components/files/serialFiles.type.ts   |   4 +-
 ui/ftp/components/shared/breadcrumbs.tsx      |  34 +-
 ui/ftp/main.logic.ts                          | 311 ++++++++++--------
 ui/ftp/main.scss                              |  43 ++-
 ui/ftp/main.tsx                               |  36 +-
 12 files changed, 353 insertions(+), 195 deletions(-)
 create mode 100644 ui/ftp/components/files/fileManagement.tsx
 create mode 100644 ui/ftp/components/files/fileManagement.type.ts
 delete mode 100644 ui/ftp/components/files/management.type.ts

diff --git a/preload.js b/preload.js
index b1ef2fd..1e413f5 100644
--- a/preload.js
+++ b/preload.js
@@ -47,9 +47,8 @@ const Serial = {
   onData: (fn) => {
     board.serial.on('data', fn)
   },
-  listFiles: async () => {
-    const output = await board.fs_ls()
-    return output
+  listFiles: async (folder) => {
+    return await board.fs_ls(folder)
   },
   loadFile: async (file) => {
     const output = await board.fs_cat(file)
@@ -61,14 +60,14 @@ const Serial = {
   saveFileContent: async (filename, content) => {
     return board.fs_save(content || ' ', filename)
   },
-  uploadFile: async (folder, filename) => {
-    let src = `${folder}/${filename}`
-    let dest = filename
+  uploadFile: async (diskPath, serialPath, filename) => {
+    let src = `${diskPath}/${filename}`
+    let dest = `${serialPath}/${filename}`
     return board.fs_put(src, dest)
   },
-  downloadFile: async (folder, filename) => {
-    let contents = await Serial.loadFile(filename)
-    return ipcRenderer.invoke('save-file', folder, filename, contents)
+  downloadFile: async (serialPath, diskPath, filename) => {
+    let contents = await Serial.loadFile(serialPath + '/' + filename)
+    return ipcRenderer.invoke('save-file', diskPath, filename, contents)
   },
   renameFile: async (oldName, newName) => {
     return board.fs_rename(oldName, newName)
diff --git a/ui/ftp/components/files/diskFiles.tsx b/ui/ftp/components/files/diskFiles.tsx
index 682840d..0a43d32 100644
--- a/ui/ftp/components/files/diskFiles.tsx
+++ b/ui/ftp/components/files/diskFiles.tsx
@@ -1,16 +1,30 @@
 import React from 'react'
 import DiskFilesType from './diskFiles.type.ts'
 import Button from '../shared/button.tsx'
+import { Device } from '../../main.type'
 
-const DiskFiles: React.FC = ({ diskFilesLogic }) => {    
-    const { diskFiles = [], navigate }: DiskFilesType = diskFilesLogic()
-    const onClick = (folder) => () => {
-        navigate(folder)
-    }
+const DiskFiles: React.FC = ({ diskFilesLogic }) => {
+    const {
+      diskPath,
+      selectedFiles = [],
+      diskFiles = [],
+      navigate,
+      selectFile
+    } : DiskFilesType = diskFilesLogic()
+    const onNavigate = (folder) => () => navigate([diskPath,folder].join('/'))
+    const onSelect = (folder) => () => selectFile([diskPath,folder].join('/'))
     const files = diskFiles.map((folder, i) => {
-        return <li key={i}><Button onClick={onClick(folder)}>{folder}</Button></li>
+        const checked = selectedFiles
+                        .filter(f => f.device === Device.disk)
+                        .find(f => f.path === [diskPath,folder].join('/'))
+        return (
+          <li key={i}>
+            <Button onClick={onSelect(folder)}>[{checked?`x`:' '}]</Button>
+            <Button onClick={onNavigate(folder)}>{folder}</Button>
+          </li>
+        )
     })
     return <ul>{files}</ul>
 }
 
-export default DiskFiles
\ No newline at end of file
+export default DiskFiles
diff --git a/ui/ftp/components/files/diskFiles.type.ts b/ui/ftp/components/files/diskFiles.type.ts
index cf38a69..2f09c16 100644
--- a/ui/ftp/components/files/diskFiles.type.ts
+++ b/ui/ftp/components/files/diskFiles.type.ts
@@ -1,8 +1,9 @@
 type DiskFilesType = () => {
-    diskFiles: String[]
-    // selectedFile: File
-    navigate: (folder : String) => void
-    selectFile: (path: String) => void
+  diskPath: string,
+  diskFiles: string[]
+  selectedFiles: File[]
+  navigate: (folder : string) => void
+  selectFile: (path: string) => void
 }
 
-export default DiskFilesType
\ No newline at end of file
+export default DiskFilesType
diff --git a/ui/ftp/components/files/fileManagement.tsx b/ui/ftp/components/files/fileManagement.tsx
new file mode 100644
index 0000000..e80d1cc
--- /dev/null
+++ b/ui/ftp/components/files/fileManagement.tsx
@@ -0,0 +1,16 @@
+import React from 'react'
+import FileManagementType from './fileManagement.type'
+import Button from '../shared/button.tsx'
+
+const FileManagement: React.FC = ({ fileManagementLogic }) => {
+    const { upload, download, remove }: FileManagementType = fileManagementLogic()
+    return (
+      <ul>
+          <li><Button onClick={upload}>{`<-`}</Button></li>
+          <li><Button onClick={download}>{`->`}</Button></li>
+          <li><Button onClick={remove}>{`x`}</Button></li>
+      </ul>
+    )
+}
+
+export default FileManagement
diff --git a/ui/ftp/components/files/fileManagement.type.ts b/ui/ftp/components/files/fileManagement.type.ts
new file mode 100644
index 0000000..f1200d0
--- /dev/null
+++ b/ui/ftp/components/files/fileManagement.type.ts
@@ -0,0 +1,7 @@
+type FileManagementType = () => {
+    upload: () => void
+    download: () => void
+    remove: () => void
+}
+
+export default FileManagementType
diff --git a/ui/ftp/components/files/management.type.ts b/ui/ftp/components/files/management.type.ts
deleted file mode 100644
index 5dd88ff..0000000
--- a/ui/ftp/components/files/management.type.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-type FileManagementType = () => {
-    upload: (diskPath: String, serialPath: String) => void
-    download: (serialPath: String, diskPath: String) => void
-    remove: () => void
-}
-
-export default FileManagementType
\ No newline at end of file
diff --git a/ui/ftp/components/files/serialFiles.tsx b/ui/ftp/components/files/serialFiles.tsx
index 23c9724..5ff8d9f 100644
--- a/ui/ftp/components/files/serialFiles.tsx
+++ b/ui/ftp/components/files/serialFiles.tsx
@@ -1,16 +1,30 @@
 import React from 'react'
 import SerialFilesType from './serialFiles.type.ts'
 import Button from '../shared/button.tsx'
+import { Device } from '../../main.type'
 
-const SerialFiles: React.FC = ({ serialFilesLogic }) => {    
-    const { serialFiles = [], navigate }: SerialFilesType = serialFilesLogic()
-    const onClick = (folder) => () => {
-        navigate(folder)
-    }
-    const files = serialFiles.map((folder, i) => {
-        return <li key={i}><Button onClick={onClick(folder)}>{folder}</Button></li>
-    })
-    return <ul>{files}</ul>
+const SerialFiles: React.FC = ({ serialFilesLogic }) => {
+  const {
+    serialPath,
+    selectedFiles = [],
+    serialFiles = [],
+    navigate,
+    selectFile
+  } : SerialFilesType = serialFilesLogic()
+  const onNavigate = (folder) => () => navigate([serialPath,folder].join('/'))
+  const onSelect = (folder) => () => selectFile([serialPath,folder].join('/'))
+  const files = serialFiles.map((folder, i) => {
+      const checked = selectedFiles
+                      .filter(f => f.device === Device.serial)
+                      .find(f => f.path === [serialPath,folder].join('/'))
+      return (
+        <li key={i}>
+          <Button onClick={onSelect(folder)}>[{checked?`x`:' '}]</Button>
+          <Button onClick={onNavigate(folder)}>{folder}</Button>
+        </li>
+      )
+  })
+  return <ul>{files}</ul>
 }
 
-export default SerialFiles
\ No newline at end of file
+export default SerialFiles
diff --git a/ui/ftp/components/files/serialFiles.type.ts b/ui/ftp/components/files/serialFiles.type.ts
index 93bc68b..74809c0 100644
--- a/ui/ftp/components/files/serialFiles.type.ts
+++ b/ui/ftp/components/files/serialFiles.type.ts
@@ -1,8 +1,8 @@
 type SerialFilesType = () => {
     serialFiles: String[]
-    selectedFile: File
+    selectedFiles: File[]
     navigate: (folder : String) => void
     selectFile: (path: String) => void
 }
 
-export default SerialFilesType
\ No newline at end of file
+export default SerialFilesType
diff --git a/ui/ftp/components/shared/breadcrumbs.tsx b/ui/ftp/components/shared/breadcrumbs.tsx
index f42d966..1ae240d 100644
--- a/ui/ftp/components/shared/breadcrumbs.tsx
+++ b/ui/ftp/components/shared/breadcrumbs.tsx
@@ -3,19 +3,27 @@ import Button from './button.tsx'
 import styles from './breadcrumbs.module.scss'
 
 const BreadCrumbs: React.FC = ({ path, navigate }) => {
-    if (!path) return <></>
-    const pathArray = path.split('/')
-    const onClick = (folder) => () => {
-        navigate(folder)
-    }
-    const items = pathArray.map(
-        (folder, i) => (
-            <span key={i}>
-                <Button onClick={onClick(folder)}>{folder}</Button>/
-            </span>
-        )
+  if (!path) return <></>
+  const pathArray = path.split('/')
+  const crumbs = pathArray.filter(crumb => crumb != '')
+
+  const onClick = (i) => () => {
+    const newPath = '/' + crumbs.slice(0, i+1).join('/')
+    navigate(newPath)
+  }
+  const items = crumbs.map(
+    (folder, i) => (
+      <span key={i}>
+        <Button onClick={onClick(i)}>{folder||'/'}</Button>
+      </span>
     )
-    return <div className={styles.body}>{items}</div>
+  )
+  return (
+    <div className={styles.body}>
+      <span><Button onClick={() => navigate('/')}>/</Button></span>
+      {items}
+    </div>
+  )
 }
 
-export default BreadCrumbs
\ No newline at end of file
+export default BreadCrumbs
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
index 5c511d7..b54962f 100644
--- a/ui/ftp/main.logic.ts
+++ b/ui/ftp/main.logic.ts
@@ -5,160 +5,211 @@ import DiskNavigationType from './components/navigation/diskNavigation.type'
 import DiskFilesType from './components/files/diskFiles.type'
 import SerialNavigationType from './components/navigation/serialNavigation.type'
 import SerialFilesType from './components/files/serialFiles.type'
-import FileManagementType from './components/files/management.type'
+import FileManagementType from './components/files/fileManagement.type'
 
 import { Device, File } from './main.type'
 
 export type UseMainLogic = () => {
-    toolbarLogic: ToolbarType
-    diskNavigationLogic: DiskNavigationType
-    diskFilesLogic: DiskFilesType
-    serialNavigationLogic: SerialNavigationType
-    serialFilesLogic: SerialFilesType
-    fileManagementLogic: FileManagementType
+  toolbarLogic: ToolbarType
+  diskNavigationLogic: DiskNavigationType
+  diskFilesLogic: DiskFilesType
+  serialNavigationLogic: SerialNavigationType
+  serialFilesLogic: SerialFilesType
+  fileManagementLogic: FileManagementType
 }
 
 export const useMainLogic : UseMainLogic = function() : ReturnType<UseMainLogic> {
-    const { BridgeSerial, BridgeDisk } = window
-    // List and connect to serial devices
-    const [ availableDevices, setAvailableDevices ] = useState<String[]>()
-    const [ connectedDevice, setConnectedDevice ] = useState<String | null>()
-    // Navigation paths
-    const [ serialPath, setSerialPath ] = useState<String | null>()
-    const [ diskPath, setDiskPath ] = useState<String | null>()
-    // Available files (listed)
-    const [ diskFiles, setDiskFiles ] = useState<String[]>()
-    const [ serialFiles, setSerialFiles ] = useState<String[]>()
-    // Selected file
-    const [ selectedFile, setSelectedFile ] = useState<File | null>()
-    // Progress
-    const [ waiting, setWaiting ] = useState<Boolean>()
+  const { BridgeSerial, BridgeDisk } = window
+  // List and connect to serial devices
+  const [ availableDevices, setAvailableDevices ] = useState<String[]>()
+  const [ connectedDevice, setConnectedDevice ] = useState<String | null>()
+  // Navigation paths
+  const [ serialPath, setSerialPath ] = useState<String | null>()
+  const [ diskPath, setDiskPath ] = useState<String | null>()
+  // Available files (listed)
+  const [ diskFiles, setDiskFiles ] = useState<String[]>()
+  const [ serialFiles, setSerialFiles ] = useState<String[]>()
+  // Selected files
+  const [ selectedFiles = [], setSelectedFiles ] = useState<File[]>()
+  // Progress
+  const [ waiting, setWaiting ] = useState<Boolean>()
 
-    // HELPERS
-    const navigateDisk = async (folder) => {
-        const pathArray = diskPath.split('/')
-        const folderIndex = pathArray.indexOf(folder)
-        let newPathArray = []
-        if (folderIndex === -1) {
-            newPathArray = pathArray.concat(folder)
-        } else {
-            newPathArray = pathArray.slice(0, folderIndex+1)
-        }
-        const newPath = newPathArray.join('/')
-        const files = await BridgeDisk.listFiles(newPath)
-        setDiskPath(newPath)
-        setDiskFiles(files)
-    }
+  // HELPERS
+  const navigateDisk = async (newPath) => {
+    const files = await BridgeDisk.listFiles(newPath)
+    setDiskPath(newPath)
+    setDiskFiles(files)
+    const newSelection = selectedFiles.filter(f => f.device !== Device.disk)
+    setSelectedFiles(newSelection)
+  }
 
-    const navigateSerial = async (folder) => {
-        const pathArray = serialPath.split('/')
-        const folderIndex = pathArray.indexOf(folder)
-        let newPathArray = []
-        if (folderIndex === -1) {
-            newPathArray = pathArray.concat(folder)
-        } else {
-            newPathArray = pathArray.slice(0, folderIndex+1)
-        }
-        const newPath = newPathArray.join('/')
-        const files = await BridgeSerial.listFiles(newPath)
-        setSerialPath(newPath)
+  const navigateSerial = async (newPath) => {
+    const files = await BridgeSerial.listFiles(newPath)
+    setSerialPath(newPath)
+    setSerialFiles(files)
+    const newSelection = selectedFiles.filter(f => f.device !== Device.serial)
+    setSelectedFiles(newSelection)
+  }
+
+  // LOGIC
+  const refresh = async () => {
+    // list available devices
+    const devices = await BridgeSerial.loadPorts()
+    setAvailableDevices(devices)
+    // list serial files
+    if (connectedDevice) {
+      const files = await BridgeSerial.listFiles(serialPath)
+      setSerialFiles(files)
+    }
+    // list disk files
+    if (diskPath) {
+      const files = await BridgeDisk.listFiles(diskPath)
+      setDiskFiles(files)
+    }
+  }
+  const toolbarLogic = () : ReturnType<ToolbarType> => {
+    return {
+      availableDevices: availableDevices,
+      connectedDevice: connectedDevice,
+      connect: async (devicePath: String) => {
+        await BridgeSerial.connect(devicePath)
+        setConnectedDevice(devicePath)
+        setSerialPath('/')
+        const files = await BridgeSerial.listFiles('/')
         setSerialFiles(files)
+      },
+      disconnect: () => {
+        setConnectedDevice(null)
+        setSerialPath(null)
+      },
+      openFolder: async () => {
+        const { folder, files } = await BridgeDisk.openFolder()
+        setDiskPath(folder)
+        setDiskFiles(files)
+      },
+      refresh: refresh
     }
+  }
 
-    // LOGIC
-    const toolbarLogic = () : ReturnType<ToolbarType> => {
-        return {
-            availableDevices: availableDevices,
-            connectedDevice: connectedDevice,
-            connect: async (devicePath: String) => {
-                await BridgeSerial.connect(devicePath)
-                setConnectedDevice(devicePath)
-                setSerialPath('/')
-            },
-            disconnect: () => {
-                setConnectedDevice(null)
-                setSerialPath(null)
-            },
-            openFolder: async () => {
-                const { folder, files } = await BridgeDisk.openFolder()
-                setDiskPath(folder)
-                setDiskFiles(files)
-            },
-            refresh: async () => {
-                // list available devices
-                const devices = await BridgeSerial.loadPorts()
-                setAvailableDevices(devices)
-                // list serial files
-                if (connectedDevice) {
-                    const files = await BridgeSerial.listFiles(serialPath)
-                    setSerialFiles(files)
-                }
-                // list disk files
-                if (diskPath) {
-                    const files = await BridgeDisk.listFiles(diskPath)
-                    setDiskFiles(files)
-                }
-            }
-        }
+  const diskNavigationLogic = () : ReturnType<DiskNavigationType> => {
+    return {
+      diskPath: diskPath,
+      navigate: navigateDisk
     }
+  }
 
-    const diskNavigationLogic = () : ReturnType<DiskNavigationType> => {
-        return {
-            diskPath: diskPath,
-            navigate: navigateDisk
+  const diskFilesLogic = () : ReturnType<DiskFilesType> => {
+    return {
+      diskPath: diskPath,
+      diskFiles: diskFiles,
+      selectedFiles: selectedFiles,
+      navigate: navigateDisk,
+      selectFile: (path) => {
+        const diskFilesOnly = selectedFiles.filter(f => f.device === Device.disk)
+        const selected = diskFilesOnly.find(f => f.path === path)
+        if (selected) {
+          let newSelection = diskFilesOnly.filter(f => f.path !== path)
+          setSelectedFiles(newSelection)
+        } else {
+          let file = {
+            path: path,
+            device: Device.disk
+          }
+          diskFilesOnly.push(file)
+          setSelectedFiles(diskFilesOnly.slice())
         }
+      }
     }
+  }
 
-    const diskFilesLogic = () : ReturnType<DiskFilesType> => {
-        return {
-            diskFiles: diskFiles,
-            // selectedFile: selectedFile,
-            navigate: navigateDisk,
-            selectFile: (path) => {
-                setSelectedFile({
-                    path: path, 
-                    device: Device.disk
-                })
-            }
-        }
+  const serialNavigationLogic = () : ReturnType<SerialNavigationType> => {
+    return {
+      serialPath: serialPath,
+      navigate: navigateSerial
     }
+  }
 
-    const serialNavigationLogic = () : ReturnType<SerialNavigationType> => {
-        return {
-            serialPath: serialPath,
-            navigate: navigateSerial
+  const serialFilesLogic = () : ReturnType<SerialFilesType> => {
+    return {
+      serialPath: serialPath,
+      serialFiles: serialFiles,
+      selectedFiles: selectedFiles,
+      navigate: navigateSerial,
+      selectFile: (path) => {
+        const serialFilesOnly = selectedFiles.filter(f => f.device === Device.serial)
+        const selected = serialFilesOnly.find(f => f.path === path)
+        if (selected) {
+          let newSelection = serialFilesOnly.filter(f => f.path !== path)
+          setSelectedFiles(newSelection)
+        } else {
+          let file = {
+            path: path,
+            device: Device.serial
+          }
+          serialFilesOnly.push(file)
+          setSelectedFiles(serialFilesOnly.slice())
         }
+      }
     }
+  }
 
-    const serialFilesLogic = () : ReturnType<SerialFilesType> => {
-        return {
-            serialFiles: serialFiles,
-            selectedFile: selectedFile,
-            navigate: navigateSerial,
-            selectFile: (path) => {
-                setSelectedFile({
-                    path: path,
-                    device: Device.serial
-                })
-            }
+  const fileManagementLogic = () : ReturnType<FileManagementType> => {
+    return {
+      upload: async () => {
+        setWaiting(true)
+        try {
+          for (let i = 0; i < selectedFiles.length; i++) {
+            const filename = selectedFiles[i].path.split('/').pop()
+            await BridgeSerial.uploadFile(diskPath, serialPath, filename)
+          }
+          refresh()
+        } catch (e) {
+          console.log('error', e)
         }
-    }
-
-    const fileManagementLogic = () : ReturnType<FileManagementType> => {
-        return {
-            upload: (diskPath, serialPath) => {},
-            download: (serialPath, diskPath) => {},
-            remove: () => {}
+        setWaiting(false)
+      },
+      download: async () => {
+        setWaiting(true)
+        try {
+          for (let i = 0; i < selectedFiles.length; i++) {
+            const filename = selectedFiles[i].path.split('/').pop()
+            await BridgeSerial.downloadFile(serialPath, diskPath, filename)
+          }
+          refresh()
+        } catch (e) {
+          console.log('error', e)
+        }
+        setWaiting(false)
+      },
+      remove: async () => {
+        setWaiting(true)
+        try {
+          for (let i = 0; i < selectedFiles.length; i++) {
+            const f = selectedFiles[i]
+            const filename = f.path.split('/').pop()
+            if (f.device === Device.disk) {
+              await BridgeDisk.removeFile(diskPath, filename)
+            }
+            if (f.device === Device.serial) {
+              await BridgeSerial.removeFile(f.path)
+            }
+          }
+          refresh()
+        } catch (e) {
+          console.log('error', e)
         }
+        setWaiting(false)
+      }
     }
+  }
 
-    return {
-        toolbarLogic,
-        diskNavigationLogic,
-        diskFilesLogic,
-        serialNavigationLogic,
-        serialFilesLogic,
-        fileManagementLogic
-    }
+  return {
+    toolbarLogic,
+    diskNavigationLogic,
+    diskFilesLogic,
+    serialNavigationLogic,
+    serialFilesLogic,
+    fileManagementLogic,
+    waiting
+  }
 }
-
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index dee2604..3791c39 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -1,9 +1,46 @@
-* { 
-    box-sizing: border-box; 
+* {
+    box-sizing: border-box;
 }
 html,
 body {
     margin: 0;
     width: 100%;
     height: 100%;
-}
\ No newline at end of file
+}
+
+.row {
+  display: flex;
+  flex-direction: row;
+  height: 100%;
+}
+
+.column {
+  display: flex;
+  flex-direction: column;
+  width: 100%;
+}
+
+.justify-center {
+  justify-content: center;
+}
+
+.align-center {
+  align-items: center;
+}
+
+ul {
+    list-style: none;
+    padding: 0;
+}
+
+.file-panel {
+  width: 100%;
+  overflow: scroll;
+  height: 70vh;
+}
+
+.file-management {
+  width: 10%;
+  justify-content: center;
+  align-items: center;
+}
diff --git a/ui/ftp/main.tsx b/ui/ftp/main.tsx
index be3b1de..82109da 100644
--- a/ui/ftp/main.tsx
+++ b/ui/ftp/main.tsx
@@ -6,28 +6,46 @@ import DiskNavigation from './components/navigation/diskNavigation'
 import DiskFiles from './components/files/diskFiles'
 import SerialNavigation from './components/navigation/serialNavigation'
 import SerialFiles from './components/files/serialFiles'
+import FileManagement from './components/files/fileManagement'
 
 import { useMainLogic } from './main.logic.ts'
 
 const App: React.FC = () => {
-  const { 
+  const {
+    waiting,
     toolbarLogic,
     diskNavigationLogic,
     diskFilesLogic,
     serialNavigationLogic,
-    serialFilesLogic
+    serialFilesLogic,
+    fileManagementLogic
   } = useMainLogic()
+  if (waiting) {
+    return (
+      <>
+        <Toolbar toolbarLogic={toolbarLogic}></Toolbar>
+        WAIT!
+      </>
+    )
+  }
   return (
     <>
       <Toolbar toolbarLogic={toolbarLogic}></Toolbar>
-      <div>
-        <div>
-          <DiskNavigation navigationLogic={diskNavigationLogic}></DiskNavigation>
-          <DiskFiles diskFilesLogic={diskFilesLogic}></DiskFiles>
-        </div>
-        <div>
+      <div className="row">
+        <div className="column">
           <SerialNavigation navigationLogic={serialNavigationLogic}></SerialNavigation>
-          <SerialFiles serialFilesLogic={serialFilesLogic}></SerialFiles>
+          <div className="file-panel">
+            <SerialFiles serialFilesLogic={serialFilesLogic}></SerialFiles>
+          </div>
+        </div>
+        <div className="column file-management">
+          <FileManagement fileManagementLogic={fileManagementLogic} />
+        </div>
+        <div className="column">
+          <DiskNavigation navigationLogic={diskNavigationLogic}></DiskNavigation>
+          <div className="file-panel">
+            <DiskFiles diskFilesLogic={diskFilesLogic}></DiskFiles>
+          </div>
         </div>
       </div>
     </>

From 88b35a4557a7a5336ca9c134e64f2efd21ec71c6 Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Tue, 18 Apr 2023 16:49:31 +0200
Subject: [PATCH 3/9] WIP FTP ui bindings

---
 ui/ftp/components/diskView.tsx           |  84 +++++++
 ui/ftp/components/fileManagementView.tsx |  22 ++
 ui/ftp/components/files/diskFiles.tsx    |   2 +-
 ui/ftp/components/files/serialFiles.tsx  |   2 +-
 ui/ftp/components/loadinbView.tsx        |   9 +
 ui/ftp/components/serialView.tsx         |  74 ++++++
 ui/ftp/components/toolbar/toolbar.tsx    |  70 +++---
 ui/ftp/main.logic.ts                     | 277 ++++++++++-------------
 ui/ftp/main.scss                         | 113 ++++++++-
 ui/ftp/main.tsx                          |  59 ++---
 ui/ftp/main.type.ts                      |  14 +-
 11 files changed, 490 insertions(+), 236 deletions(-)
 create mode 100644 ui/ftp/components/diskView.tsx
 create mode 100644 ui/ftp/components/fileManagementView.tsx
 create mode 100644 ui/ftp/components/loadinbView.tsx
 create mode 100644 ui/ftp/components/serialView.tsx

diff --git a/ui/ftp/components/diskView.tsx b/ui/ftp/components/diskView.tsx
new file mode 100644
index 0000000..5a2c3f5
--- /dev/null
+++ b/ui/ftp/components/diskView.tsx
@@ -0,0 +1,84 @@
+import React from 'react'
+
+const DiskView: React.FC = ({ logic }) => {
+    const { waiting } = logic()
+    return (
+      <div className="column file-panel">
+        <div className="toolbar row full-width">
+            <button>Select folder</button>
+        </div>
+        <div className="row full-width navigation">
+          <button>/</button>
+          <button>home</button>
+          <button>projects</button>
+          <button>arduino</button>
+          <button>micropython</button>
+          <button>example</button>
+        </div>
+        <div className="column full-width full-height list">
+          <div className="list-item">
+            <div className="checkbox">📁</div>
+            lib
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>boot.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>main.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>turing_machine.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>README.md</span>
+          </div>
+          <div className="list-item">
+            <div className="checkbox">📁</div>
+            lib
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>boot.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>main.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>turing_machine.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>README.md</span>
+          </div>
+          <div className="list-item">
+            <div className="checkbox">📁</div>
+            lib
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>boot.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>main.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>turing_machine.py</span>
+          </div>
+          <div className="list-item">
+            <input type="checkbox" />
+            <span>README.md</span>
+          </div>
+        </div>
+      </div>
+    )
+}
+
+export default DiskView
diff --git a/ui/ftp/components/fileManagementView.tsx b/ui/ftp/components/fileManagementView.tsx
new file mode 100644
index 0000000..9251e94
--- /dev/null
+++ b/ui/ftp/components/fileManagementView.tsx
@@ -0,0 +1,22 @@
+import React from 'react'
+
+type FileManagementParams = () => {
+  upload: () => void
+  download: () => void
+  remove: () => void
+  refresh: () => void
+}
+
+const FileManagementView: React.FC = ({ logic }) => {
+    const { refresh } : FileManagementParams = logic()
+    return (
+      <div className="file-management column align-center justify-center">
+        <button onClick={refresh}>↺</button>
+        <button>←</button>
+        <button>→</button>
+        <button>×</button>
+      </div>
+    )
+}
+
+export default FileManagementView
diff --git a/ui/ftp/components/files/diskFiles.tsx b/ui/ftp/components/files/diskFiles.tsx
index 0a43d32..40b6b6e 100644
--- a/ui/ftp/components/files/diskFiles.tsx
+++ b/ui/ftp/components/files/diskFiles.tsx
@@ -15,7 +15,7 @@ const DiskFiles: React.FC = ({ diskFilesLogic }) => {
     const onSelect = (folder) => () => selectFile([diskPath,folder].join('/'))
     const files = diskFiles.map((folder, i) => {
         const checked = selectedFiles
-                        .filter(f => f.device === Device.disk)
+                        .filter(f => f.device === DeviceType.disk)
                         .find(f => f.path === [diskPath,folder].join('/'))
         return (
           <li key={i}>
diff --git a/ui/ftp/components/files/serialFiles.tsx b/ui/ftp/components/files/serialFiles.tsx
index 5ff8d9f..ff047cc 100644
--- a/ui/ftp/components/files/serialFiles.tsx
+++ b/ui/ftp/components/files/serialFiles.tsx
@@ -15,7 +15,7 @@ const SerialFiles: React.FC = ({ serialFilesLogic }) => {
   const onSelect = (folder) => () => selectFile([serialPath,folder].join('/'))
   const files = serialFiles.map((folder, i) => {
       const checked = selectedFiles
-                      .filter(f => f.device === Device.serial)
+                      .filter(f => f.device === DeviceType.serial)
                       .find(f => f.path === [serialPath,folder].join('/'))
       return (
         <li key={i}>
diff --git a/ui/ftp/components/loadinbView.tsx b/ui/ftp/components/loadinbView.tsx
new file mode 100644
index 0000000..8abeef4
--- /dev/null
+++ b/ui/ftp/components/loadinbView.tsx
@@ -0,0 +1,9 @@
+import React from 'react'
+
+const LoadingView: React.FC = ({ logic }) => {
+    const { waiting } = logic()
+    if (waiting) return <div id="loading">Wait</div>
+    return <></>
+}
+
+export default LoadingView
diff --git a/ui/ftp/components/serialView.tsx b/ui/ftp/components/serialView.tsx
new file mode 100644
index 0000000..1bd406c
--- /dev/null
+++ b/ui/ftp/components/serialView.tsx
@@ -0,0 +1,74 @@
+import { React } from 'react'
+
+import {
+  File,
+  AvailableDevice
+} from '../main.type.ts'
+
+type SerialParams = () => {
+  availableDevices: AvailableDevices[]
+  connectedDevice: string
+  serialPath: string
+  serialFiles: String[]
+  selectedFiles: File[]
+  connect: (path: string) => void
+  disconnect: () => void
+  selectFile: (path: string) => void
+  refresh: () => void
+  navigate: (path: string) => void
+}
+
+const SerialView: React.FC = ({ logic }) => {
+  const {
+    availableDevices = [],
+    serialFiles = [],
+    serialPath = '',
+    connect,
+    disconnect
+  } : SerialParams = logic()
+
+  const onSelectDevice = (e) => {
+    const value = e.target.value
+    if (value === "null") {
+      disconnect()
+    } else {
+      connect(value)
+    }
+  }
+
+  const ListItem = (file: File, i: number) => (
+    <div className="list-item" key={i}>
+      <input className="checkbox" type="checkbox" />
+      <span>{file}</span>
+    </div>
+  )
+
+  const NavigationItem = (name: string, i:number) => (
+    <button key={i}>{name}</button>
+  )
+  let serialPathArray = []
+  if (serialPath) {
+    serialPathArray = ['/'].concat(
+      serialPath.split('/').filter(s => s !== '')
+    )
+  }
+
+  return (
+    <div className="column file-panel">
+      <div className="toolbar row">
+        <select onChange={onSelectDevice}>
+          <option value="null">Select a device...</option>
+          {availableDevices.map((d, i) => <option key={i}>{d.path}</option>)}
+        </select>
+      </div>
+      <div className="row full-width navigation">
+        {serialPathArray.map(NavigationItem)}
+      </div>
+      <div className="column full-width list full-height">
+        {serialFiles.map(ListItem)}
+      </div>
+    </div>
+  )
+}
+
+export default SerialView
diff --git a/ui/ftp/components/toolbar/toolbar.tsx b/ui/ftp/components/toolbar/toolbar.tsx
index 46bc943..5c6fcce 100644
--- a/ui/ftp/components/toolbar/toolbar.tsx
+++ b/ui/ftp/components/toolbar/toolbar.tsx
@@ -3,40 +3,44 @@ import styles from './toolbar.module.scss'
 import Button from '../shared/button.tsx'
 
 
-const Toolbar: React.FC = ({ toolbarLogic }) => {    
-    const { 
-        openFolder, 
-        refresh,
-        connect,
-        disconnect,
-        connectedDevice,
-        availableDevices = [] 
-    } = toolbarLogic()
-    const [ selectedDevice, setSelectedDevice ] = useState<String | null>()
-    const onChange = (e) => setSelectedDevice(e.target.value)
-    const onConnect = () => connect(selectedDevice)
+const Toolbar: React.FC = ({ toolbarLogic }) => {
+  const {
+    openFolder,
+    refresh,
+    connect,
+    disconnect,
+    connectedDevice,
+    availableDevices = []
+  } = toolbarLogic()
+  const [ selectedDevice, setSelectedDevice ] = useState<String | null>()
+  const onChange = (e) => setSelectedDevice(e.target.value)
+  const onConnect = () => {
+    if (connectedDevice) {
+      disconnect()
+    } else {
+      connect(selectedDevice)
+    }
+  }
 
-    const deviceSelector = (
-        <select onChange={onChange} value={selectedDevice}>
-            <option value="none">Select a device...</option>
-            {availableDevices.map((d, i) => <option key={i} value={d.path}>{d.path}</option>)}
-        </select>
-    )
-    const deviceDisplay = (
-        <span>{connectedDevice}</span>
-    )
+  const deviceSelector = (
+    <select onChange={onChange} value={selectedDevice}>
+      <option value="none">Select a device...</option>
+      {availableDevices.map((d, i) => <option key={i} value={d.path}>{d.path}</option>)}
+    </select>
+  )
+  const deviceDisplay = <span>{connectedDevice}</span>
 
-    return (
-        <div className={styles.body}>
-            {connectedDevice ? deviceDisplay : deviceSelector}
-            <Button onClick={refresh}>Refresh</Button>
-            <Button onClick={connectedDevice ? disconnect : onConnect}>
-                {connectedDevice ? 'Disconnect' : 'Connect'}
-            </Button>
-            <Button onClick={openFolder}>Select Folder</Button>
-            {connectedDevice ? <span>Connected</span> : <span>Disconnected</span>}
-        </div>
-    )
+  return (
+    <div className={styles.body}>
+    <Button onClick={refresh}>Refresh</Button>
+    {connectedDevice ? deviceDisplay : deviceSelector}
+    {connectedDevice ? <span>✅</span> : <span>⛔</span>}
+    <Button onClick={onConnect}>
+    {connectedDevice ? 'Disconnect' : 'Connect'}
+    </Button>
+    <Button onClick={openFolder}>Select Folder</Button>
+    </div>
+  )
 }
 
-export default Toolbar
\ No newline at end of file
+export default Toolbar
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
index b54962f..8e93668 100644
--- a/ui/ftp/main.logic.ts
+++ b/ui/ftp/main.logic.ts
@@ -1,27 +1,11 @@
 import { useState } from 'react';
 
-import ToolbarType from './components/toolbar/toolbar.type'
-import DiskNavigationType from './components/navigation/diskNavigation.type'
-import DiskFilesType from './components/files/diskFiles.type'
-import SerialNavigationType from './components/navigation/serialNavigation.type'
-import SerialFilesType from './components/files/serialFiles.type'
-import FileManagementType from './components/files/fileManagement.type'
+import { Device, File, AvailableDevices } from './main.type'
 
-import { Device, File } from './main.type'
-
-export type UseMainLogic = () => {
-  toolbarLogic: ToolbarType
-  diskNavigationLogic: DiskNavigationType
-  diskFilesLogic: DiskFilesType
-  serialNavigationLogic: SerialNavigationType
-  serialFilesLogic: SerialFilesType
-  fileManagementLogic: FileManagementType
-}
-
-export const useMainLogic : UseMainLogic = function() : ReturnType<UseMainLogic> {
+export const useMainLogic = function() {
   const { BridgeSerial, BridgeDisk } = window
   // List and connect to serial devices
-  const [ availableDevices, setAvailableDevices ] = useState<String[]>()
+  const [ availableDevices, setAvailableDevices ] = useState<AvailableDevices[]>()
   const [ connectedDevice, setConnectedDevice ] = useState<String | null>()
   // Navigation paths
   const [ serialPath, setSerialPath ] = useState<String | null>()
@@ -39,7 +23,7 @@ export const useMainLogic : UseMainLogic = function() : ReturnType<UseMainLogic>
     const files = await BridgeDisk.listFiles(newPath)
     setDiskPath(newPath)
     setDiskFiles(files)
-    const newSelection = selectedFiles.filter(f => f.device !== Device.disk)
+    const newSelection = selectedFiles.filter(f => f.device !== DeviceType.disk)
     setSelectedFiles(newSelection)
   }
 
@@ -47,12 +31,12 @@ export const useMainLogic : UseMainLogic = function() : ReturnType<UseMainLogic>
     const files = await BridgeSerial.listFiles(newPath)
     setSerialPath(newPath)
     setSerialFiles(files)
-    const newSelection = selectedFiles.filter(f => f.device !== Device.serial)
+    const newSelection = selectedFiles.filter(f => f.device !== DeviceType.serial)
     setSelectedFiles(newSelection)
   }
 
-  // LOGIC
   const refresh = async () => {
+    setWaiting(true)
     // list available devices
     const devices = await BridgeSerial.loadPorts()
     setAvailableDevices(devices)
@@ -60,156 +44,143 @@ export const useMainLogic : UseMainLogic = function() : ReturnType<UseMainLogic>
     if (connectedDevice) {
       const files = await BridgeSerial.listFiles(serialPath)
       setSerialFiles(files)
+    } else {
+      setSerialFiles([])
     }
     // list disk files
     if (diskPath) {
       const files = await BridgeDisk.listFiles(diskPath)
       setDiskFiles(files)
+    } else {
+      setDiskFiles([])
     }
-  }
-  const toolbarLogic = () : ReturnType<ToolbarType> => {
-    return {
-      availableDevices: availableDevices,
-      connectedDevice: connectedDevice,
-      connect: async (devicePath: String) => {
-        await BridgeSerial.connect(devicePath)
-        setConnectedDevice(devicePath)
-        setSerialPath('/')
-        const files = await BridgeSerial.listFiles('/')
-        setSerialFiles(files)
-      },
-      disconnect: () => {
-        setConnectedDevice(null)
-        setSerialPath(null)
-      },
-      openFolder: async () => {
-        const { folder, files } = await BridgeDisk.openFolder()
-        setDiskPath(folder)
-        setDiskFiles(files)
-      },
-      refresh: refresh
-    }
-  }
-
-  const diskNavigationLogic = () : ReturnType<DiskNavigationType> => {
-    return {
-      diskPath: diskPath,
-      navigate: navigateDisk
-    }
+    setWaiting(false)
   }
 
-  const diskFilesLogic = () : ReturnType<DiskFilesType> => {
-    return {
-      diskPath: diskPath,
-      diskFiles: diskFiles,
-      selectedFiles: selectedFiles,
-      navigate: navigateDisk,
-      selectFile: (path) => {
-        const diskFilesOnly = selectedFiles.filter(f => f.device === Device.disk)
-        const selected = diskFilesOnly.find(f => f.path === path)
-        if (selected) {
-          let newSelection = diskFilesOnly.filter(f => f.path !== path)
-          setSelectedFiles(newSelection)
-        } else {
-          let file = {
-            path: path,
-            device: Device.disk
-          }
-          diskFilesOnly.push(file)
-          setSelectedFiles(diskFilesOnly.slice())
+  const serialLogic = () => ({
+    availableDevices: availableDevices,
+    connectedDevice: connectedDevice,
+    serialPath: serialPath,
+    serialFiles: serialFiles,
+    selectedFiles: selectedFiles,
+    connect: async (devicePath: String) => {
+      setWaiting(true)
+      await BridgeSerial.connect(devicePath)
+      setConnectedDevice(devicePath)
+      setSerialPath('/')
+      const files = await BridgeSerial.listFiles('/')
+      setSerialFiles(files)
+      setWaiting(false)
+    },
+    disconnect: async () => {
+      setWaiting(true)
+      await BridgeSerial.disconnect()
+      setConnectedDevice(null)
+      setSerialPath(null)
+      setSerialFiles([])
+      setWaiting(false)
+    },
+    selectFile: (path) => {
+      const serialFilesOnly = selectedFiles.filter(f => f.device === DeviceType.serial)
+      const selected = serialFilesOnly.find(f => f.path === path)
+      if (selected) {
+        let newSelection = serialFilesOnly.filter(f => f.path !== path)
+        setSelectedFiles(newSelection)
+      } else {
+        let file = {
+          path: path,
+          device: DeviceType.serial
         }
+        serialFilesOnly.push(file)
+        setSelectedFiles(serialFilesOnly.slice())
       }
-    }
-  }
-
-  const serialNavigationLogic = () : ReturnType<SerialNavigationType> => {
-    return {
-      serialPath: serialPath,
-      navigate: navigateSerial
-    }
-  }
-
-  const serialFilesLogic = () : ReturnType<SerialFilesType> => {
-    return {
-      serialPath: serialPath,
-      serialFiles: serialFiles,
-      selectedFiles: selectedFiles,
-      navigate: navigateSerial,
-      selectFile: (path) => {
-        const serialFilesOnly = selectedFiles.filter(f => f.device === Device.serial)
-        const selected = serialFilesOnly.find(f => f.path === path)
-        if (selected) {
-          let newSelection = serialFilesOnly.filter(f => f.path !== path)
-          setSelectedFiles(newSelection)
-        } else {
-          let file = {
-            path: path,
-            device: Device.serial
-          }
-          serialFilesOnly.push(file)
-          setSelectedFiles(serialFilesOnly.slice())
+    },
+    refresh: refresh,
+    navigate: navigateSerial,
+  })
+  const diskLogic = () => ({
+    diskPath: diskPath,
+    diskFiles: diskFiles,
+    selectedFiles: selectedFiles,
+    openFolder: async () => {
+      const { folder, files } = await BridgeDisk.openFolder()
+      setDiskPath(folder)
+      setDiskFiles(files)
+    },
+    selectFile: (path) => {
+      const diskFilesOnly = selectedFiles.filter(f => f.device === DeviceType.disk)
+      const selected = diskFilesOnly.find(f => f.path === path)
+      if (selected) {
+        let newSelection = diskFilesOnly.filter(f => f.path !== path)
+        setSelectedFiles(newSelection)
+      } else {
+        let file = {
+          path: path,
+          device: DeviceType.disk
         }
+        diskFilesOnly.push(file)
+        setSelectedFiles(diskFilesOnly.slice())
       }
-    }
-  }
-
-  const fileManagementLogic = () : ReturnType<FileManagementType> => {
-    return {
-      upload: async () => {
-        setWaiting(true)
-        try {
-          for (let i = 0; i < selectedFiles.length; i++) {
-            const filename = selectedFiles[i].path.split('/').pop()
-            await BridgeSerial.uploadFile(diskPath, serialPath, filename)
-          }
-          refresh()
-        } catch (e) {
-          console.log('error', e)
+    },
+    navigate: navigateDisk
+  })
+  const fileManagementLogic = () => ({
+    upload: async () => {
+      setWaiting(true)
+      try {
+        for (let i = 0; i < selectedFiles.length; i++) {
+          const filename = selectedFiles[i].path.split('/').pop()
+          await BridgeSerial.uploadFile(diskPath, serialPath, filename)
         }
-        setWaiting(false)
-      },
-      download: async () => {
-        setWaiting(true)
-        try {
-          for (let i = 0; i < selectedFiles.length; i++) {
-            const filename = selectedFiles[i].path.split('/').pop()
-            await BridgeSerial.downloadFile(serialPath, diskPath, filename)
-          }
-          refresh()
-        } catch (e) {
-          console.log('error', e)
+        refresh()
+      } catch (e) {
+        console.log('error', e)
+      }
+      setWaiting(false)
+    },
+    download: async () => {
+      setWaiting(true)
+      try {
+        for (let i = 0; i < selectedFiles.length; i++) {
+          const filename = selectedFiles[i].path.split('/').pop()
+          await BridgeSerial.downloadFile(serialPath, diskPath, filename)
         }
-        setWaiting(false)
-      },
-      remove: async () => {
-        setWaiting(true)
-        try {
-          for (let i = 0; i < selectedFiles.length; i++) {
-            const f = selectedFiles[i]
-            const filename = f.path.split('/').pop()
-            if (f.device === Device.disk) {
-              await BridgeDisk.removeFile(diskPath, filename)
-            }
-            if (f.device === Device.serial) {
-              await BridgeSerial.removeFile(f.path)
-            }
+        refresh()
+      } catch (e) {
+        console.log('error', e)
+      }
+      setWaiting(false)
+    },
+    remove: async () => {
+      setWaiting(true)
+      try {
+        for (let i = 0; i < selectedFiles.length; i++) {
+          const f = selectedFiles[i]
+          const filename = f.path.split('/').pop()
+          if (f.device === DeviceType.disk) {
+            await BridgeDisk.removeFile(diskPath, filename)
+          }
+          if (f.device === DeviceType.serial) {
+            await BridgeSerial.removeFile(f.path)
           }
-          refresh()
-        } catch (e) {
-          console.log('error', e)
         }
-        setWaiting(false)
+        refresh()
+      } catch (e) {
+        console.log('error', e)
       }
-    }
-  }
+      setWaiting(false)
+    },
+    refresh: refresh
+  })
+  const loadingLogic = async () => ({
+    waiting
+  })
 
   return {
-    toolbarLogic,
-    diskNavigationLogic,
-    diskFilesLogic,
-    serialNavigationLogic,
-    serialFilesLogic,
+    waiting,
+    serialLogic,
+    diskLogic,
     fileManagementLogic,
-    waiting
+    loadingLogic
   }
 }
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index 3791c39..c30528e 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -1,11 +1,32 @@
 * {
     box-sizing: border-box;
+    font-family: sans-serif;
+    font-size: var(--base-size);
 }
 html,
 body {
     margin: 0;
     width: 100%;
     height: 100%;
+
+    --base-size: 18px;
+    --brand-color: #008184;
+    --light-color: #FFFFFF;
+    --dark-color: #005C5F;
+    --dark-tone: #DAE3E3;
+    --light-tone: #ECF1F1;
+    --highlight-color: #F26727;
+
+    background: var(--light-tone);
+}
+
+main {
+  width: 100vw;
+  height: 100vh;
+}
+.window-wrapper {
+  width: 100%;
+  height: 100%;
 }
 
 .row {
@@ -28,19 +49,95 @@ body {
   align-items: center;
 }
 
-ul {
-    list-style: none;
-    padding: 0;
+.full-width {
+  width: 100%;
+}
+.full-height {
+  height: 100%;
+}
+
+.toolbar {
+  height: 100px;
+  padding-bottom: var(--base-size);
+
+  select {
+    width: 100%;
+  }
+
+  button {
+    width: 100%;
+  }
 }
 
 .file-panel {
-  width: 100%;
-  overflow: scroll;
-  height: 70vh;
+  padding: 40px;
+  width: calc(50% - 25px);
+}
+
+.list {
+  background: var(--light-color);
+  overflow: overlay;
+}
+
+.list-item {
+  padding: var(--base-size) 5px;
+  cursor: pointer;
+  .checkbox, input[type="checkbox"] {
+    display: inline-block;
+    width: 50px;
+    cursor: pointer;
+    text-align: center;
+  }
+  &:hover {
+    background: var(--dark-tone);
+  }
+}
+
+.navigation {
+  height: 100px;
+  background: var(--dark-tone);
+  padding: var(--base-size);
+  padding-bottom: var(--base-size);
+  overflow: overlay;
+  & > * {
+    margin-right: 10px;
+  }
 }
 
 .file-management {
-  width: 10%;
+  width: 50px;
+  button {
+    padding: 0;
+    width: 50px;
+    height: 50px;
+    margin: 5px 0;
+  }
+}
+
+
+select {
+  padding: 5px 30px;
+  border: none;
+  cursor: pointer;
+  background: var(--light-color);
+}
+
+button {
+  background: var(--brand-color);
+  color: var(--light-color);
+  border: none;
+  padding: 5px 30px;
+  cursor: pointer;
+}
+
+.window-overlay {
+  width: 100vw;
+  height: 100vh;
+  top: 0;
+  left: 0;
+  position: fixed;
+  z-index: 9999;
+  background: rgba(255, 255, 255, 0.5);
+  display: flex;
   justify-content: center;
-  align-items: center;
 }
diff --git a/ui/ftp/main.tsx b/ui/ftp/main.tsx
index 82109da..389494d 100644
--- a/ui/ftp/main.tsx
+++ b/ui/ftp/main.tsx
@@ -1,53 +1,35 @@
 import React from 'react'
 import ReactDOM from 'react-dom/client'
 
-import Toolbar from './components/toolbar/toolbar'
-import DiskNavigation from './components/navigation/diskNavigation'
-import DiskFiles from './components/files/diskFiles'
-import SerialNavigation from './components/navigation/serialNavigation'
-import SerialFiles from './components/files/serialFiles'
-import FileManagement from './components/files/fileManagement'
+import SerialView from './components/serialView.tsx'
+import FileManagementView from './components/fileManagementView.tsx'
+import DiskView from './components/diskView.tsx'
+import LoadingView from './components/loadingView.tsx'
 
 import { useMainLogic } from './main.logic.ts'
 
 const App: React.FC = () => {
   const {
     waiting,
-    toolbarLogic,
-    diskNavigationLogic,
-    diskFilesLogic,
-    serialNavigationLogic,
-    serialFilesLogic,
-    fileManagementLogic
+    serialLogic,
+    diskLogic,
+    fileManagementLogic,
+    loadingLogic
   } = useMainLogic()
-  if (waiting) {
-    return (
-      <>
-        <Toolbar toolbarLogic={toolbarLogic}></Toolbar>
-        WAIT!
-      </>
-    )
-  }
+
+  const overlay = (
+    <div className="window-overlay">
+      <div>Waiting</div>
+    </div>
+  )
   return (
     <>
-      <Toolbar toolbarLogic={toolbarLogic}></Toolbar>
-      <div className="row">
-        <div className="column">
-          <SerialNavigation navigationLogic={serialNavigationLogic}></SerialNavigation>
-          <div className="file-panel">
-            <SerialFiles serialFilesLogic={serialFilesLogic}></SerialFiles>
-          </div>
-        </div>
-        <div className="column file-management">
-          <FileManagement fileManagementLogic={fileManagementLogic} />
-        </div>
-        <div className="column">
-          <DiskNavigation navigationLogic={diskNavigationLogic}></DiskNavigation>
-          <div className="file-panel">
-            <DiskFiles diskFilesLogic={diskFilesLogic}></DiskFiles>
-          </div>
-        </div>
-      </div>
+    <div className="window-wrapper row">
+      <SerialView logic={serialLogic} />
+      <FileManagementView logic={fileManagementLogic} />
+      <DiskView logic={diskLogic} />
+    </div>
+    {waiting ? overlay : null}
     </>
   )
 }
@@ -56,6 +38,7 @@ window.addEventListener('load', () => {
   window.BridgeWindow.setWindowSize(900, 600)
   const container : HTMLElement | null = document.querySelector('main')
   const root = ReactDOM.createRoot(container)
+
   root.render(
     <React.StrictMode>
       <App />
diff --git a/ui/ftp/main.type.ts b/ui/ftp/main.type.ts
index 3516891..4468173 100644
--- a/ui/ftp/main.type.ts
+++ b/ui/ftp/main.type.ts
@@ -1,6 +1,16 @@
-export enum Device { serial, disk }
+export enum DeviceType { serial, disk }
 
 export type File = {
     path: String
     device: Device
-}
\ No newline at end of file
+}
+
+export type AvailableDevice = {
+  locationId: undefined | string
+  manufacturer: string
+  path: string
+  pnpId: string
+  productId: string
+  serialNumber: string
+  vendorId: string
+}

From 63cc09949ec8c2ceae6e3c098083e603367839cb Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Wed, 19 Apr 2023 15:47:03 +0200
Subject: [PATCH 4/9] Serial view plugged

---
 index.js                         |  1 +
 package-lock.json                |  9 +++--
 package.json                     |  5 ++-
 preload.js                       |  3 ++
 ui/ftp/components/serialView.tsx | 42 ++++++++++++++++------
 ui/ftp/main.logic.ts             | 62 ++++++++++++++++++--------------
 ui/ftp/main.scss                 |  5 +++
 ui/ftp/main.type.ts              |  3 ++
 8 files changed, 85 insertions(+), 45 deletions(-)

diff --git a/index.js b/index.js
index d49cd27..69befd9 100644
--- a/index.js
+++ b/index.js
@@ -107,6 +107,7 @@ function createWindow () {
     }
   })
   // and load the index.html of the app.
+  // win.loadFile('ui/editor/index.html')
   win.loadFile('ui/ftp/dist/index.html')
 }
 
diff --git a/package-lock.json b/package-lock.json
index 5cb6cdb..fdc6798 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,8 @@
 			"license": "MIT",
 			"dependencies": {
 				"about-window": "^1.15.2",
-				"micropython.js": "github:murilopolese/micropython.js#v1.2.2",
-				"mkdirp": "^1.0.3",
-				"serialport": "^10.4.0"
+				"micropython.js": "github:arduino/micropython.js#1.3.0",
+				"mkdirp": "^1.0.3"
 			},
 			"devDependencies": {
 				"electron": "^19.0.10",
@@ -3143,8 +3142,8 @@
 			}
 		},
 		"node_modules/micropython.js": {
-			"version": "1.2.2",
-			"resolved": "git+ssh://git@github.com/murilopolese/micropython.js.git#1f630d21f6ee18a901dc5a1ebe4ce5b92f05943a",
+			"version": "1.3.0",
+			"resolved": "git+ssh://git@github.com/arduino/micropython.js.git#418c81147d547a5cac9f51cba9700800811803f7",
 			"dependencies": {
 				"serialport": "^10.4.0"
 			},
diff --git a/package.json b/package.json
index 670bcc5..de76002 100644
--- a/package.json
+++ b/package.json
@@ -38,9 +38,8 @@
 	"license": "MIT",
 	"dependencies": {
 		"about-window": "^1.15.2",
-		"micropython.js": "github:murilopolese/micropython.js#v1.2.2",
-		"mkdirp": "^1.0.3",
-		"serialport": "^10.4.0"
+		"micropython.js": "github:arduino/micropython.js#1.3.0",
+		"mkdirp": "^1.0.3"
 	},
 	"engines": {
 		"node": "18"
diff --git a/preload.js b/preload.js
index 1e413f5..34e616c 100644
--- a/preload.js
+++ b/preload.js
@@ -50,6 +50,9 @@ const Serial = {
   listFiles: async (folder) => {
     return await board.fs_ls(folder)
   },
+  ilistFiles: async (folder) => {
+    return await board.fs_ils(folder)
+  },
   loadFile: async (file) => {
     const output = await board.fs_cat(file)
     return output || ''
diff --git a/ui/ftp/components/serialView.tsx b/ui/ftp/components/serialView.tsx
index 1bd406c..ed0af6a 100644
--- a/ui/ftp/components/serialView.tsx
+++ b/ui/ftp/components/serialView.tsx
@@ -2,6 +2,7 @@ import { React } from 'react'
 
 import {
   File,
+  DeviceType,
   AvailableDevice
 } from '../main.type.ts'
 
@@ -22,9 +23,12 @@ const SerialView: React.FC = ({ logic }) => {
   const {
     availableDevices = [],
     serialFiles = [],
+    selectedFiles = [],
     serialPath = '',
     connect,
-    disconnect
+    disconnect,
+    navigate,
+    selectFile
   } : SerialParams = logic()
 
   const onSelectDevice = (e) => {
@@ -36,16 +40,34 @@ const SerialView: React.FC = ({ logic }) => {
     }
   }
 
-  const ListItem = (file: File, i: number) => (
-    <div className="list-item" key={i}>
-      <input className="checkbox" type="checkbox" />
-      <span>{file}</span>
-    </div>
-  )
+  const ListItem = (file: File, i: number) => {
+    const onClick = () => {
+      if (file.type === 'file') {
+        selectFile(file)
+      } else {
+        navigate(serialPath + '/' + file.path)
+      }
+    }
+    const checked = selectedFiles
+      .filter(f => f.device === DeviceType.serial)
+      .find(f => f.path === file.path)
+    const icon = file.type === 'file'
+      ? <div className="checkbox">📄</div>
+      : <div className="checkbox">📁</div>
+    return (
+      <div className={`list-item ${checked?'checked':''}`} key={i} onClick={onClick}>
+        {icon}<span>{file.path}</span>
+      </div>
+    )
+  }
 
-  const NavigationItem = (name: string, i:number) => (
-    <button key={i}>{name}</button>
-  )
+  const NavigationItem = (name: string, i:number) => {
+    const crumbs = serialPath.split('/').filter(c => c !== '')
+    const path = '/' + crumbs.slice(0, i).join('/')
+    return (
+      <button key={i} onClick={() => navigate(path)}>{name}</button>
+    )
+  }
   let serialPathArray = []
   if (serialPath) {
     serialPathArray = ['/'].concat(
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
index 8e93668..06b7fb4 100644
--- a/ui/ftp/main.logic.ts
+++ b/ui/ftp/main.logic.ts
@@ -1,6 +1,6 @@
 import { useState } from 'react';
 
-import { Device, File, AvailableDevices } from './main.type'
+import { DeviceType, File, FileType, AvailableDevices } from './main.type'
 
 export const useMainLogic = function() {
   const { BridgeSerial, BridgeDisk } = window
@@ -28,11 +28,31 @@ export const useMainLogic = function() {
   }
 
   const navigateSerial = async (newPath) => {
-    const files = await BridgeSerial.listFiles(newPath)
+    setWaiting(true)
     setSerialPath(newPath)
-    setSerialFiles(files)
+    await refreshSerialFiles(newPath)
     const newSelection = selectedFiles.filter(f => f.device !== DeviceType.serial)
     setSelectedFiles(newSelection)
+    setWaiting(false)
+  }
+
+  const refreshSerialFiles = async (path) => {
+    const detailedFiles = await BridgeSerial.ilistFiles(path)
+    const folders = detailedFiles.filter(f => f[1] === FileType.folder) || []
+    const files = detailedFiles.filter(f => f[1] === FileType.file) || []
+    const sortedFiles = folders.concat(files)
+      .map(f => ({
+        path: f[0],
+        type: (f[1] === FileType.folder) ? 'folder' : 'file',
+        size: f[3],
+        device: DeviceType.serial
+      })) || []
+    setSerialFiles(sortedFiles)
+  }
+
+  const refreshDiskFiles = async () => {
+    const files = await BridgeDisk.listFiles(diskPath)
+    setDiskFiles(files)
   }
 
   const refresh = async () => {
@@ -41,19 +61,12 @@ export const useMainLogic = function() {
     const devices = await BridgeSerial.loadPorts()
     setAvailableDevices(devices)
     // list serial files
-    if (connectedDevice) {
-      const files = await BridgeSerial.listFiles(serialPath)
-      setSerialFiles(files)
-    } else {
-      setSerialFiles([])
-    }
+    if (connectedDevice) await refreshSerialFiles(serialPath)
+    else setSerialFiles([])
     // list disk files
-    if (diskPath) {
-      const files = await BridgeDisk.listFiles(diskPath)
-      setDiskFiles(files)
-    } else {
-      setDiskFiles([])
-    }
+    if (diskPath) await refreshDiskFiles()
+    else setDiskFiles([])
+
     setWaiting(false)
   }
 
@@ -63,13 +76,12 @@ export const useMainLogic = function() {
     serialPath: serialPath,
     serialFiles: serialFiles,
     selectedFiles: selectedFiles,
-    connect: async (devicePath: String) => {
+    connect: async (port: String) => {
       setWaiting(true)
-      await BridgeSerial.connect(devicePath)
-      setConnectedDevice(devicePath)
+      await BridgeSerial.connect(port)
+      setConnectedDevice(port)
       setSerialPath('/')
-      const files = await BridgeSerial.listFiles('/')
-      setSerialFiles(files)
+      refreshSerialFiles(serialPath)
       setWaiting(false)
     },
     disconnect: async () => {
@@ -80,17 +92,13 @@ export const useMainLogic = function() {
       setSerialFiles([])
       setWaiting(false)
     },
-    selectFile: (path) => {
+    selectFile: (file: File) => {
       const serialFilesOnly = selectedFiles.filter(f => f.device === DeviceType.serial)
-      const selected = serialFilesOnly.find(f => f.path === path)
+      const selected = serialFilesOnly.find(f => f.path === file.path)
       if (selected) {
-        let newSelection = serialFilesOnly.filter(f => f.path !== path)
+        let newSelection = serialFilesOnly.filter(f => f.path !== file.path)
         setSelectedFiles(newSelection)
       } else {
-        let file = {
-          path: path,
-          device: DeviceType.serial
-        }
         serialFilesOnly.push(file)
         setSelectedFiles(serialFilesOnly.slice())
       }
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index c30528e..ad1766a 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -91,6 +91,11 @@ main {
   &:hover {
     background: var(--dark-tone);
   }
+  &.checked {
+    background: var(--highlight-color);
+    color: var(--light-color);
+    font-weight: bold;
+  }
 }
 
 .navigation {
diff --git a/ui/ftp/main.type.ts b/ui/ftp/main.type.ts
index 4468173..7712173 100644
--- a/ui/ftp/main.type.ts
+++ b/ui/ftp/main.type.ts
@@ -1,8 +1,11 @@
 export enum DeviceType { serial, disk }
+export enum FileType { file = 0x8000, folder = 0x4000 }
 
 export type File = {
     path: String
+    type: FileType
     device: Device
+    size: Number
 }
 
 export type AvailableDevice = {

From ce9c5dc723ea693b1d48a94e958a31f374e79027 Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Wed, 19 Apr 2023 16:29:40 +0200
Subject: [PATCH 5/9] Disk view plugged

---
 index.js                         |  26 ++++++-
 preload.js                       |   3 +
 ui/ftp/components/diskView.tsx   | 130 +++++++++++++++----------------
 ui/ftp/components/serialView.tsx |   1 -
 ui/ftp/main.logic.ts             |  43 ++++++----
 ui/ftp/main.scss                 |   5 ++
 ui/ftp/main.type.ts              |   2 +-
 7 files changed, 123 insertions(+), 87 deletions(-)

diff --git a/index.js b/index.js
index 69befd9..341c3ea 100644
--- a/index.js
+++ b/index.js
@@ -26,13 +26,31 @@ function listFolder(folder, filesOnly) {
   return files
 }
 
+function ilistFolder(folder, filesOnly) {
+  let files = fs.readdirSync(path.resolve(folder))
+  files = files.map(f => {
+    let filePath = path.resolve(folder, f)
+    return {
+      path: f,
+      type: fs.lstatSync(filePath).isDirectory() ? 'folder' : 'file'
+    }
+  })
+  // Filter out directories
+  if (filesOnly) {
+    files = files.filter(f => f.type === 'file')
+  }
+  // Filter out dot files
+  files = files.filter(f => f.path.indexOf('.') !== 0)
+  return files
+}
+
 // LOCAL FILE SYSTEM ACCESS
 ipcMain.handle('open-folder', async (event) => {
   console.log('ipcMain', 'open-folder')
   const folder = await openFolderDialog()
   let files = []
   if (folder) {
-    files = listFolder(folder)
+    files = ilistFolder(folder)
   }
   return { folder, files }
 })
@@ -43,6 +61,12 @@ ipcMain.handle('list-files', async (event, folder) => {
   return listFolder(folder)
 })
 
+ipcMain.handle('ilist-files', async (event, folder) => {
+  console.log('ipcMain', 'ilist-files', folder)
+  if (!folder) return []
+  return ilistFolder(folder)
+})
+
 ipcMain.handle('load-file', (event, folder, filename) => {
   console.log('ipcMain', 'load-file', folder, filename )
   let filePath = path.resolve(folder, filename)
diff --git a/preload.js b/preload.js
index 34e616c..0b5db21 100644
--- a/preload.js
+++ b/preload.js
@@ -87,6 +87,9 @@ const Disk = {
   listFiles: async (folder) => {
     return ipcRenderer.invoke('list-files', folder)
   },
+  ilistFiles: async (folder) => {
+    return ipcRenderer.invoke('ilist-files', folder)
+  },
   loadFile: async (folder, file) => {
     let content = await ipcRenderer.invoke('load-file', folder, file)
     return new TextDecoder().decode(content)
diff --git a/ui/ftp/components/diskView.tsx b/ui/ftp/components/diskView.tsx
index 5a2c3f5..334d324 100644
--- a/ui/ftp/components/diskView.tsx
+++ b/ui/ftp/components/diskView.tsx
@@ -1,81 +1,75 @@
 import React from 'react'
 
+import {
+  File,
+  DeviceType
+} from '../main.type.ts'
+
+type DiskViewParams = {
+  waiting: Boolean,
+  diskPath: String
+  diskFiles: File[]
+  selectedFiles: File[]
+  openFolder: () => void
+  selectFile: (file: File) => void
+  navigate: (path: String) => void
+}
+
 const DiskView: React.FC = ({ logic }) => {
-    const { waiting } = logic()
+    const {
+      waiting = true,
+      diskPath,
+      diskFiles = [],
+      selectedFiles = [],
+      openFolder,
+      selectFile,
+      navigate
+    } : DiskViewParams = logic()
+
+    const ListItem = (file: File, i: number) => {
+      const onClick = () => {
+        if (file.type === 'file') {
+          selectFile(file)
+        } else {
+          navigate(diskPath + '/' + file.path)
+        }
+      }
+      const checked = selectedFiles
+        .filter(f => f.device === DeviceType.disk)
+        .find(f => f.path === file.path)
+      const icon = file.type === 'file'
+        ? <div className="checkbox">📄</div>
+        : <div className="checkbox">📁</div>
+      return (
+        <div className={`list-item ${checked?'checked':''}`} key={i} onClick={onClick}>
+          {icon}<span>{file.path}</span>
+        </div>
+      )
+    }
+
+    const NavigationItem = (name: string, i:number) => {
+      const crumbs = diskPath.split('/').filter(c => c !== '')
+      const path = '/' + crumbs.slice(0, i).join('/')
+      return (
+        <button key={i} onClick={() => navigate(path)}>{name}</button>
+      )
+    }
+    let diskPathArray = []
+    if (diskPath) {
+      diskPathArray = ['/'].concat(
+        diskPath.split('/').filter(s => s !== '')
+      )
+    }
     return (
       <div className="column file-panel">
         <div className="toolbar row full-width">
-            <button>Select folder</button>
+            <button onClick={openFolder}>Select folder</button>
         </div>
         <div className="row full-width navigation">
-          <button>/</button>
-          <button>home</button>
-          <button>projects</button>
-          <button>arduino</button>
-          <button>micropython</button>
-          <button>example</button>
+          {diskPathArray.map(NavigationItem)}
         </div>
         <div className="column full-width full-height list">
-          <div className="list-item">
-            <div className="checkbox">📁</div>
-            lib
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>boot.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>main.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>turing_machine.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>README.md</span>
-          </div>
-          <div className="list-item">
-            <div className="checkbox">📁</div>
-            lib
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>boot.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>main.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>turing_machine.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>README.md</span>
-          </div>
-          <div className="list-item">
-            <div className="checkbox">📁</div>
-            lib
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>boot.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>main.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>turing_machine.py</span>
-          </div>
-          <div className="list-item">
-            <input type="checkbox" />
-            <span>README.md</span>
-          </div>
+          {diskFiles.map(ListItem)}
         </div>
       </div>
     )
diff --git a/ui/ftp/components/serialView.tsx b/ui/ftp/components/serialView.tsx
index ed0af6a..5e8646f 100644
--- a/ui/ftp/components/serialView.tsx
+++ b/ui/ftp/components/serialView.tsx
@@ -15,7 +15,6 @@ type SerialParams = () => {
   connect: (path: string) => void
   disconnect: () => void
   selectFile: (path: string) => void
-  refresh: () => void
   navigate: (path: string) => void
 }
 
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
index 06b7fb4..29c27fa 100644
--- a/ui/ftp/main.logic.ts
+++ b/ui/ftp/main.logic.ts
@@ -20,11 +20,12 @@ export const useMainLogic = function() {
 
   // HELPERS
   const navigateDisk = async (newPath) => {
-    const files = await BridgeDisk.listFiles(newPath)
+    setWaiting(true)
     setDiskPath(newPath)
-    setDiskFiles(files)
+    await refreshDiskFiles(newPath)
     const newSelection = selectedFiles.filter(f => f.device !== DeviceType.disk)
     setSelectedFiles(newSelection)
+    setWaiting(false)
   }
 
   const navigateSerial = async (newPath) => {
@@ -36,11 +37,12 @@ export const useMainLogic = function() {
     setWaiting(false)
   }
 
-  const refreshSerialFiles = async (path) => {
+  const refreshSerialFiles = async (path: String) => {
     const detailedFiles = await BridgeSerial.ilistFiles(path)
     const folders = detailedFiles.filter(f => f[1] === FileType.folder) || []
     const files = detailedFiles.filter(f => f[1] === FileType.file) || []
-    const sortedFiles = folders.concat(files)
+    const sortedFiles = folders
+      .concat(files)
       .map(f => ({
         path: f[0],
         type: (f[1] === FileType.folder) ? 'folder' : 'file',
@@ -50,9 +52,18 @@ export const useMainLogic = function() {
     setSerialFiles(sortedFiles)
   }
 
-  const refreshDiskFiles = async () => {
-    const files = await BridgeDisk.listFiles(diskPath)
-    setDiskFiles(files)
+  const refreshDiskFiles = async (path: String) => {
+    const detailedFiles = await BridgeDisk.ilistFiles(path)
+    const folders = detailedFiles.filter(f => f.type === 'folder') || []
+    const files = detailedFiles.filter(f => f.type === 'file') || []
+    const sortedFiles = folders
+      .concat(files)
+      .map(f => ({
+        path: f.path,
+        type: f.type,
+        device: DeviceType.disk
+      }))
+    setDiskFiles(sortedFiles)
   }
 
   const refresh = async () => {
@@ -64,7 +75,7 @@ export const useMainLogic = function() {
     if (connectedDevice) await refreshSerialFiles(serialPath)
     else setSerialFiles([])
     // list disk files
-    if (diskPath) await refreshDiskFiles()
+    if (diskPath) await refreshDiskFiles(diskPath)
     else setDiskFiles([])
 
     setWaiting(false)
@@ -103,7 +114,6 @@ export const useMainLogic = function() {
         setSelectedFiles(serialFilesOnly.slice())
       }
     },
-    refresh: refresh,
     navigate: navigateSerial,
   })
   const diskLogic = () => ({
@@ -113,20 +123,21 @@ export const useMainLogic = function() {
     openFolder: async () => {
       const { folder, files } = await BridgeDisk.openFolder()
       setDiskPath(folder)
-      setDiskFiles(files)
+      await refreshDiskFiles(folder)
     },
-    selectFile: (path) => {
+    selectFile: (file: File) => {
       const diskFilesOnly = selectedFiles.filter(f => f.device === DeviceType.disk)
-      const selected = diskFilesOnly.find(f => f.path === path)
+      const selected = diskFilesOnly.find(f => f.path === file.path)
       if (selected) {
-        let newSelection = diskFilesOnly.filter(f => f.path !== path)
+        let newSelection = diskFilesOnly.filter(f => f.path !== file.path)
         setSelectedFiles(newSelection)
       } else {
-        let file = {
-          path: path,
+        let f = {
+          path: file.path,
+          type: file.type,
           device: DeviceType.disk
         }
-        diskFilesOnly.push(file)
+        diskFilesOnly.push(f)
         setSelectedFiles(diskFilesOnly.slice())
       }
     },
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index ad1766a..1552c0d 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -82,6 +82,7 @@ main {
 .list-item {
   padding: var(--base-size) 5px;
   cursor: pointer;
+  display: flex;
   .checkbox, input[type="checkbox"] {
     display: inline-block;
     width: 50px;
@@ -95,6 +96,10 @@ main {
     background: var(--highlight-color);
     color: var(--light-color);
     font-weight: bold;
+
+    &:hover {
+      opacity: 0.85;
+    }
   }
 }
 
diff --git a/ui/ftp/main.type.ts b/ui/ftp/main.type.ts
index 7712173..7bbcc4e 100644
--- a/ui/ftp/main.type.ts
+++ b/ui/ftp/main.type.ts
@@ -5,7 +5,7 @@ export type File = {
     path: String
     type: FileType
     device: Device
-    size: Number
+    size?: Number
 }
 
 export type AvailableDevice = {

From f05c5bbe5c268e2e5316c57262079a8966f0cd76 Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Wed, 19 Apr 2023 18:13:37 +0200
Subject: [PATCH 6/9] File management plugged

---
 ui/ftp/components/fileManagementView.tsx | 21 +++++++++++++++++----
 ui/ftp/main.logic.ts                     |  4 +++-
 ui/ftp/main.scss                         |  4 ++++
 3 files changed, 24 insertions(+), 5 deletions(-)

diff --git a/ui/ftp/components/fileManagementView.tsx b/ui/ftp/components/fileManagementView.tsx
index 9251e94..35e883d 100644
--- a/ui/ftp/components/fileManagementView.tsx
+++ b/ui/ftp/components/fileManagementView.tsx
@@ -1,20 +1,33 @@
 import React from 'react'
 
+import { File, DeviceType } from '../main.type.ts'
+
 type FileManagementParams = () => {
   upload: () => void
   download: () => void
   remove: () => void
   refresh: () => void
+  canDownload: Boolean,
+  canUpload: Boolean,
+  selectedFiles: File[]
 }
 
 const FileManagementView: React.FC = ({ logic }) => {
-    const { refresh } : FileManagementParams = logic()
+    const {
+      refresh,
+      download,
+      upload,
+      remove,
+      canUpload,
+      canDownload,
+      selectedFiles = []
+    } : FileManagementParams = logic()
     return (
       <div className="file-management column align-center justify-center">
         <button onClick={refresh}>↺</button>
-        <button>←</button>
-        <button>→</button>
-        <button>×</button>
+        <button disabled={!canUpload} onClick={upload}>←</button>
+        <button disabled={!canDownload} onClick={download}>→</button>
+        <button onClick={remove}>×</button>
       </div>
     )
 }
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
index 29c27fa..7dc3ab4 100644
--- a/ui/ftp/main.logic.ts
+++ b/ui/ftp/main.logic.ts
@@ -144,6 +144,8 @@ export const useMainLogic = function() {
     navigate: navigateDisk
   })
   const fileManagementLogic = () => ({
+    canUpload: selectedFiles.find(f => f.device === DeviceType.disk) && serialPath,
+    canDownload: selectedFiles.find(f => f.device === DeviceType.serial) && diskPath,
     upload: async () => {
       setWaiting(true)
       try {
@@ -180,7 +182,7 @@ export const useMainLogic = function() {
             await BridgeDisk.removeFile(diskPath, filename)
           }
           if (f.device === DeviceType.serial) {
-            await BridgeSerial.removeFile(f.path)
+            await BridgeSerial.removeFile(serialPath + '/' + f.path)
           }
         }
         refresh()
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index 1552c0d..38ae44d 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -139,6 +139,10 @@ button {
   padding: 5px 30px;
   cursor: pointer;
 }
+button[disabled] {
+  opacity: 0.5;
+  cursor: not-allowed;
+}
 
 .window-overlay {
   width: 100vw;

From bf98398afa5df9910a8767b238b7d21c3f06a2bc Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Fri, 28 Apr 2023 15:14:30 +0200
Subject: [PATCH 7/9] Cleanup and wip breadcrumb

---
 ui/ftp/components/diskView.tsx                | 17 ++-----
 ui/ftp/components/files/diskFiles.tsx         | 30 ------------
 ui/ftp/components/files/diskFiles.type.ts     |  9 ----
 ui/ftp/components/files/fileManagement.tsx    | 16 -------
 .../components/files/fileManagement.type.ts   |  7 ---
 ui/ftp/components/files/serialFiles.tsx       | 30 ------------
 ui/ftp/components/files/serialFiles.type.ts   |  8 ----
 .../components/navigation/diskNavigation.tsx  |  9 ----
 .../navigation/diskNavigation.type.ts         |  6 ---
 .../navigation/serialNavigation.tsx           |  9 ----
 .../navigation/serialNavigation.type.ts       |  6 ---
 ui/ftp/components/serialView.tsx              | 18 ++------
 .../components/shared/breadcrumbs.module.scss |  9 ----
 ui/ftp/components/shared/breadcrumbs.tsx      | 29 ------------
 ui/ftp/components/shared/button.module.scss   | 12 -----
 ui/ftp/components/shared/button.tsx           |  8 ----
 ui/ftp/components/toolbar/toolbar.module.scss |  8 ----
 ui/ftp/components/toolbar/toolbar.tsx         | 46 -------------------
 ui/ftp/components/toolbar/toolbar.type.ts     | 10 ----
 ui/ftp/components/ui/BreadCrumb.tsx           | 21 +++++++++
 ui/ftp/main.logic.ts                          |  3 +-
 ui/ftp/main.scss                              | 13 +++++-
 ui/ftp/main.tsx                               |  8 +++-
 ui/ftp/main.type.ts                           |  1 +
 24 files changed, 48 insertions(+), 285 deletions(-)
 delete mode 100644 ui/ftp/components/files/diskFiles.tsx
 delete mode 100644 ui/ftp/components/files/diskFiles.type.ts
 delete mode 100644 ui/ftp/components/files/fileManagement.tsx
 delete mode 100644 ui/ftp/components/files/fileManagement.type.ts
 delete mode 100644 ui/ftp/components/files/serialFiles.tsx
 delete mode 100644 ui/ftp/components/files/serialFiles.type.ts
 delete mode 100644 ui/ftp/components/navigation/diskNavigation.tsx
 delete mode 100644 ui/ftp/components/navigation/diskNavigation.type.ts
 delete mode 100644 ui/ftp/components/navigation/serialNavigation.tsx
 delete mode 100644 ui/ftp/components/navigation/serialNavigation.type.ts
 delete mode 100644 ui/ftp/components/shared/breadcrumbs.module.scss
 delete mode 100644 ui/ftp/components/shared/breadcrumbs.tsx
 delete mode 100644 ui/ftp/components/shared/button.module.scss
 delete mode 100644 ui/ftp/components/shared/button.tsx
 delete mode 100644 ui/ftp/components/toolbar/toolbar.module.scss
 delete mode 100644 ui/ftp/components/toolbar/toolbar.tsx
 delete mode 100644 ui/ftp/components/toolbar/toolbar.type.ts
 create mode 100644 ui/ftp/components/ui/BreadCrumb.tsx

diff --git a/ui/ftp/components/diskView.tsx b/ui/ftp/components/diskView.tsx
index 334d324..57ac8f6 100644
--- a/ui/ftp/components/diskView.tsx
+++ b/ui/ftp/components/diskView.tsx
@@ -5,6 +5,8 @@ import {
   DeviceType
 } from '../main.type.ts'
 
+import BreadCrumb from './ui/BreadCrumb'
+
 type DiskViewParams = {
   waiting: Boolean,
   diskPath: String
@@ -47,26 +49,13 @@ const DiskView: React.FC = ({ logic }) => {
       )
     }
 
-    const NavigationItem = (name: string, i:number) => {
-      const crumbs = diskPath.split('/').filter(c => c !== '')
-      const path = '/' + crumbs.slice(0, i).join('/')
-      return (
-        <button key={i} onClick={() => navigate(path)}>{name}</button>
-      )
-    }
-    let diskPathArray = []
-    if (diskPath) {
-      diskPathArray = ['/'].concat(
-        diskPath.split('/').filter(s => s !== '')
-      )
-    }
     return (
       <div className="column file-panel">
         <div className="toolbar row full-width">
             <button onClick={openFolder}>Select folder</button>
         </div>
         <div className="row full-width navigation">
-          {diskPathArray.map(NavigationItem)}
+          {BreadCrumb(diskPath, navigate)}
         </div>
         <div className="column full-width full-height list">
           {diskFiles.map(ListItem)}
diff --git a/ui/ftp/components/files/diskFiles.tsx b/ui/ftp/components/files/diskFiles.tsx
deleted file mode 100644
index 40b6b6e..0000000
--- a/ui/ftp/components/files/diskFiles.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react'
-import DiskFilesType from './diskFiles.type.ts'
-import Button from '../shared/button.tsx'
-import { Device } from '../../main.type'
-
-const DiskFiles: React.FC = ({ diskFilesLogic }) => {
-    const {
-      diskPath,
-      selectedFiles = [],
-      diskFiles = [],
-      navigate,
-      selectFile
-    } : DiskFilesType = diskFilesLogic()
-    const onNavigate = (folder) => () => navigate([diskPath,folder].join('/'))
-    const onSelect = (folder) => () => selectFile([diskPath,folder].join('/'))
-    const files = diskFiles.map((folder, i) => {
-        const checked = selectedFiles
-                        .filter(f => f.device === DeviceType.disk)
-                        .find(f => f.path === [diskPath,folder].join('/'))
-        return (
-          <li key={i}>
-            <Button onClick={onSelect(folder)}>[{checked?`x`:' '}]</Button>
-            <Button onClick={onNavigate(folder)}>{folder}</Button>
-          </li>
-        )
-    })
-    return <ul>{files}</ul>
-}
-
-export default DiskFiles
diff --git a/ui/ftp/components/files/diskFiles.type.ts b/ui/ftp/components/files/diskFiles.type.ts
deleted file mode 100644
index 2f09c16..0000000
--- a/ui/ftp/components/files/diskFiles.type.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-type DiskFilesType = () => {
-  diskPath: string,
-  diskFiles: string[]
-  selectedFiles: File[]
-  navigate: (folder : string) => void
-  selectFile: (path: string) => void
-}
-
-export default DiskFilesType
diff --git a/ui/ftp/components/files/fileManagement.tsx b/ui/ftp/components/files/fileManagement.tsx
deleted file mode 100644
index e80d1cc..0000000
--- a/ui/ftp/components/files/fileManagement.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react'
-import FileManagementType from './fileManagement.type'
-import Button from '../shared/button.tsx'
-
-const FileManagement: React.FC = ({ fileManagementLogic }) => {
-    const { upload, download, remove }: FileManagementType = fileManagementLogic()
-    return (
-      <ul>
-          <li><Button onClick={upload}>{`<-`}</Button></li>
-          <li><Button onClick={download}>{`->`}</Button></li>
-          <li><Button onClick={remove}>{`x`}</Button></li>
-      </ul>
-    )
-}
-
-export default FileManagement
diff --git a/ui/ftp/components/files/fileManagement.type.ts b/ui/ftp/components/files/fileManagement.type.ts
deleted file mode 100644
index f1200d0..0000000
--- a/ui/ftp/components/files/fileManagement.type.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-type FileManagementType = () => {
-    upload: () => void
-    download: () => void
-    remove: () => void
-}
-
-export default FileManagementType
diff --git a/ui/ftp/components/files/serialFiles.tsx b/ui/ftp/components/files/serialFiles.tsx
deleted file mode 100644
index ff047cc..0000000
--- a/ui/ftp/components/files/serialFiles.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from 'react'
-import SerialFilesType from './serialFiles.type.ts'
-import Button from '../shared/button.tsx'
-import { Device } from '../../main.type'
-
-const SerialFiles: React.FC = ({ serialFilesLogic }) => {
-  const {
-    serialPath,
-    selectedFiles = [],
-    serialFiles = [],
-    navigate,
-    selectFile
-  } : SerialFilesType = serialFilesLogic()
-  const onNavigate = (folder) => () => navigate([serialPath,folder].join('/'))
-  const onSelect = (folder) => () => selectFile([serialPath,folder].join('/'))
-  const files = serialFiles.map((folder, i) => {
-      const checked = selectedFiles
-                      .filter(f => f.device === DeviceType.serial)
-                      .find(f => f.path === [serialPath,folder].join('/'))
-      return (
-        <li key={i}>
-          <Button onClick={onSelect(folder)}>[{checked?`x`:' '}]</Button>
-          <Button onClick={onNavigate(folder)}>{folder}</Button>
-        </li>
-      )
-  })
-  return <ul>{files}</ul>
-}
-
-export default SerialFiles
diff --git a/ui/ftp/components/files/serialFiles.type.ts b/ui/ftp/components/files/serialFiles.type.ts
deleted file mode 100644
index 74809c0..0000000
--- a/ui/ftp/components/files/serialFiles.type.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-type SerialFilesType = () => {
-    serialFiles: String[]
-    selectedFiles: File[]
-    navigate: (folder : String) => void
-    selectFile: (path: String) => void
-}
-
-export default SerialFilesType
diff --git a/ui/ftp/components/navigation/diskNavigation.tsx b/ui/ftp/components/navigation/diskNavigation.tsx
deleted file mode 100644
index beab208..0000000
--- a/ui/ftp/components/navigation/diskNavigation.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-import BreadCrumbs from '../shared/breadcrumbs'
-
-const DiskNavigation: React.FC = ({ navigationLogic }) => {    
-    const { diskPath = '', navigate } = navigationLogic()
-    return <BreadCrumbs path={diskPath} navigate={navigate}></BreadCrumbs>
-}
-
-export default DiskNavigation
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/diskNavigation.type.ts b/ui/ftp/components/navigation/diskNavigation.type.ts
deleted file mode 100644
index fb8364b..0000000
--- a/ui/ftp/components/navigation/diskNavigation.type.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-type DiskNavigationType = () => {
-    diskPath: String
-    navigate: (folder : String) => void
-}
-
-export default DiskNavigationType
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/serialNavigation.tsx b/ui/ftp/components/navigation/serialNavigation.tsx
deleted file mode 100644
index 98b8314..0000000
--- a/ui/ftp/components/navigation/serialNavigation.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react'
-import BreadCrumbs from '../shared/breadcrumbs'
-
-const SerialNavigation: React.FC = ({ navigationLogic }) => {    
-    const { serialPath = '', navigate } = navigationLogic()
-    return <BreadCrumbs path={serialPath} navigate={navigate}></BreadCrumbs>
-}
-
-export default SerialNavigation
\ No newline at end of file
diff --git a/ui/ftp/components/navigation/serialNavigation.type.ts b/ui/ftp/components/navigation/serialNavigation.type.ts
deleted file mode 100644
index 1d057c0..0000000
--- a/ui/ftp/components/navigation/serialNavigation.type.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-type SerialNavigationType = () => {
-    serialPath: String
-    navigate: (folder: String) => void
-}
-
-export default SerialNavigationType
\ No newline at end of file
diff --git a/ui/ftp/components/serialView.tsx b/ui/ftp/components/serialView.tsx
index 5e8646f..52ee8e4 100644
--- a/ui/ftp/components/serialView.tsx
+++ b/ui/ftp/components/serialView.tsx
@@ -6,6 +6,8 @@ import {
   AvailableDevice
 } from '../main.type.ts'
 
+import BreadCrumb from './ui/BreadCrumb'
+
 type SerialParams = () => {
   availableDevices: AvailableDevices[]
   connectedDevice: string
@@ -60,20 +62,6 @@ const SerialView: React.FC = ({ logic }) => {
     )
   }
 
-  const NavigationItem = (name: string, i:number) => {
-    const crumbs = serialPath.split('/').filter(c => c !== '')
-    const path = '/' + crumbs.slice(0, i).join('/')
-    return (
-      <button key={i} onClick={() => navigate(path)}>{name}</button>
-    )
-  }
-  let serialPathArray = []
-  if (serialPath) {
-    serialPathArray = ['/'].concat(
-      serialPath.split('/').filter(s => s !== '')
-    )
-  }
-
   return (
     <div className="column file-panel">
       <div className="toolbar row">
@@ -83,7 +71,7 @@ const SerialView: React.FC = ({ logic }) => {
         </select>
       </div>
       <div className="row full-width navigation">
-        {serialPathArray.map(NavigationItem)}
+        {BreadCrumb(serialPath, navigate)}
       </div>
       <div className="column full-width list full-height">
         {serialFiles.map(ListItem)}
diff --git a/ui/ftp/components/shared/breadcrumbs.module.scss b/ui/ftp/components/shared/breadcrumbs.module.scss
deleted file mode 100644
index 822400d..0000000
--- a/ui/ftp/components/shared/breadcrumbs.module.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-.body {
-    display: flex;
-    flex-direction: row;
-    align-items: center;
-    padding: 1rem;
-    > * {
-        margin: 0 0.5rem 0 0;
-    }
-}
\ No newline at end of file
diff --git a/ui/ftp/components/shared/breadcrumbs.tsx b/ui/ftp/components/shared/breadcrumbs.tsx
deleted file mode 100644
index 1ae240d..0000000
--- a/ui/ftp/components/shared/breadcrumbs.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react'
-import Button from './button.tsx'
-import styles from './breadcrumbs.module.scss'
-
-const BreadCrumbs: React.FC = ({ path, navigate }) => {
-  if (!path) return <></>
-  const pathArray = path.split('/')
-  const crumbs = pathArray.filter(crumb => crumb != '')
-
-  const onClick = (i) => () => {
-    const newPath = '/' + crumbs.slice(0, i+1).join('/')
-    navigate(newPath)
-  }
-  const items = crumbs.map(
-    (folder, i) => (
-      <span key={i}>
-        <Button onClick={onClick(i)}>{folder||'/'}</Button>
-      </span>
-    )
-  )
-  return (
-    <div className={styles.body}>
-      <span><Button onClick={() => navigate('/')}>/</Button></span>
-      {items}
-    </div>
-  )
-}
-
-export default BreadCrumbs
diff --git a/ui/ftp/components/shared/button.module.scss b/ui/ftp/components/shared/button.module.scss
deleted file mode 100644
index ce0e3f6..0000000
--- a/ui/ftp/components/shared/button.module.scss
+++ /dev/null
@@ -1,12 +0,0 @@
-.button {
-    padding: 1rem;
-    border: none;
-    cursor: pointer;
-    background: #ddd;
-    &:hover {
-        opacity: 0.8;
-    }
-    &:active {
-        opacity: 1.0;
-    }
-}
\ No newline at end of file
diff --git a/ui/ftp/components/shared/button.tsx b/ui/ftp/components/shared/button.tsx
deleted file mode 100644
index 47e5c29..0000000
--- a/ui/ftp/components/shared/button.tsx
+++ /dev/null
@@ -1,8 +0,0 @@
-import React from 'react'
-import styles from './button.module.scss'
-
-const Button: React.FC = (attr) => {
-  return <button {...attr} className={styles.button}></button>
-}
-
-export default Button
\ No newline at end of file
diff --git a/ui/ftp/components/toolbar/toolbar.module.scss b/ui/ftp/components/toolbar/toolbar.module.scss
deleted file mode 100644
index 6d41740..0000000
--- a/ui/ftp/components/toolbar/toolbar.module.scss
+++ /dev/null
@@ -1,8 +0,0 @@
-.body {
-    display: flex;
-    flex-direction: row;
-    padding: 1rem;
-    > * {
-        margin: 0 0.5rem 0 0;
-    }
-}
diff --git a/ui/ftp/components/toolbar/toolbar.tsx b/ui/ftp/components/toolbar/toolbar.tsx
deleted file mode 100644
index 5c6fcce..0000000
--- a/ui/ftp/components/toolbar/toolbar.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import React, { useState } from 'react'
-import styles from './toolbar.module.scss'
-import Button from '../shared/button.tsx'
-
-
-const Toolbar: React.FC = ({ toolbarLogic }) => {
-  const {
-    openFolder,
-    refresh,
-    connect,
-    disconnect,
-    connectedDevice,
-    availableDevices = []
-  } = toolbarLogic()
-  const [ selectedDevice, setSelectedDevice ] = useState<String | null>()
-  const onChange = (e) => setSelectedDevice(e.target.value)
-  const onConnect = () => {
-    if (connectedDevice) {
-      disconnect()
-    } else {
-      connect(selectedDevice)
-    }
-  }
-
-  const deviceSelector = (
-    <select onChange={onChange} value={selectedDevice}>
-      <option value="none">Select a device...</option>
-      {availableDevices.map((d, i) => <option key={i} value={d.path}>{d.path}</option>)}
-    </select>
-  )
-  const deviceDisplay = <span>{connectedDevice}</span>
-
-  return (
-    <div className={styles.body}>
-    <Button onClick={refresh}>Refresh</Button>
-    {connectedDevice ? deviceDisplay : deviceSelector}
-    {connectedDevice ? <span>✅</span> : <span>⛔</span>}
-    <Button onClick={onConnect}>
-    {connectedDevice ? 'Disconnect' : 'Connect'}
-    </Button>
-    <Button onClick={openFolder}>Select Folder</Button>
-    </div>
-  )
-}
-
-export default Toolbar
diff --git a/ui/ftp/components/toolbar/toolbar.type.ts b/ui/ftp/components/toolbar/toolbar.type.ts
deleted file mode 100644
index 01a653b..0000000
--- a/ui/ftp/components/toolbar/toolbar.type.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-type ToolbarType = () => {
-    availableDevices: String[]
-    connectedDevice: String | null
-    connect: (devicePath: String) => void
-    disconnect: () => void
-    openFolder: () => void
-    refresh: () => void
-}
-
-export default ToolbarType
\ No newline at end of file
diff --git a/ui/ftp/components/ui/BreadCrumb.tsx b/ui/ftp/components/ui/BreadCrumb.tsx
new file mode 100644
index 0000000..6e66016
--- /dev/null
+++ b/ui/ftp/components/ui/BreadCrumb.tsx
@@ -0,0 +1,21 @@
+
+const BreadCrumb = (path: string, navigate: (p: string) => void) => {
+  let pathArray = []
+  if (path) {
+    pathArray = ['/'].concat(
+      path.split('/').filter(s => s !== '')
+    )
+  }
+  if (pathArray.length > 2) {
+    pathArray = pathArray.slice(-3)
+  }
+  return pathArray.map((name, i) => {
+    const crumbs = path.split('/').filter(c => c !== '')
+    const p = '/' + crumbs.slice(0, i).join('/')
+    return (
+      <a className="breadcrumb" key={i} onClick={() => navigate(p)}>{name}</a>
+    )
+  })
+}
+
+export default BreadCrumb
diff --git a/ui/ftp/main.logic.ts b/ui/ftp/main.logic.ts
index 7dc3ab4..0174f99 100644
--- a/ui/ftp/main.logic.ts
+++ b/ui/ftp/main.logic.ts
@@ -202,6 +202,7 @@ export const useMainLogic = function() {
     serialLogic,
     diskLogic,
     fileManagementLogic,
-    loadingLogic
+    loadingLogic,
+    refresh
   }
 }
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index 38ae44d..e3ad1fe 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -104,16 +104,25 @@ main {
 }
 
 .navigation {
-  height: 100px;
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+  height: calc(var(--base-size) * 5);
   background: var(--dark-tone);
   padding: var(--base-size);
   padding-bottom: var(--base-size);
-  overflow: overlay;
+  overflow: hidden;
   & > * {
     margin-right: 10px;
   }
 }
 
+.breadcrumb {
+  padding: 0.5em;
+  background: var(--light-tone);
+  cursor: pointer;
+}
+
 .file-management {
   width: 50px;
   button {
diff --git a/ui/ftp/main.tsx b/ui/ftp/main.tsx
index 389494d..8247b83 100644
--- a/ui/ftp/main.tsx
+++ b/ui/ftp/main.tsx
@@ -1,4 +1,5 @@
 import React from 'react'
+import { useEffect } from 'react'
 import ReactDOM from 'react-dom/client'
 
 import SerialView from './components/serialView.tsx'
@@ -14,9 +15,14 @@ const App: React.FC = () => {
     serialLogic,
     diskLogic,
     fileManagementLogic,
-    loadingLogic
+    loadingLogic,
+    refresh
   } = useMainLogic()
 
+  useEffect(() => {
+    refresh()
+  }, [])
+
   const overlay = (
     <div className="window-overlay">
       <div>Waiting</div>
diff --git a/ui/ftp/main.type.ts b/ui/ftp/main.type.ts
index 7bbcc4e..e1f3c73 100644
--- a/ui/ftp/main.type.ts
+++ b/ui/ftp/main.type.ts
@@ -1,4 +1,5 @@
 export enum DeviceType { serial, disk }
+
 export enum FileType { file = 0x8000, folder = 0x4000 }
 
 export type File = {

From b1dde2e38522810d6e44f774a33e3705e0942548 Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Tue, 2 May 2023 15:19:17 +0200
Subject: [PATCH 8/9] WIP

---
 index.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/index.js b/index.js
index 341c3ea..db37855 100644
--- a/index.js
+++ b/index.js
@@ -131,8 +131,8 @@ function createWindow () {
     }
   })
   // and load the index.html of the app.
-  // win.loadFile('ui/editor/index.html')
-  win.loadFile('ui/ftp/dist/index.html')
+  win.loadFile('ui/editor/index.html')
+  // win.loadFile('ui/ftp/dist/index.html')
 }
 
 // TODO: Loading splash screen

From 90aa58f7bdc072cc4a1738ad5db849e9fb3ad005 Mon Sep 17 00:00:00 2001
From: Murilo Polese <murilopolese@gmail.com>
Date: Tue, 2 May 2023 20:06:47 +0200
Subject: [PATCH 9/9] Use back button instead of breadcrumb for navigation

---
 index.js                            |  4 ++--
 ui/ftp/components/diskView.tsx      |  5 ++---
 ui/ftp/components/serialView.tsx    |  6 +++---
 ui/ftp/components/ui/BreadCrumb.tsx |  3 ---
 ui/ftp/components/ui/NavigateUp.tsx | 16 ++++++++++++++++
 ui/ftp/main.scss                    | 15 ++++++++++++++-
 6 files changed, 37 insertions(+), 12 deletions(-)
 create mode 100644 ui/ftp/components/ui/NavigateUp.tsx

diff --git a/index.js b/index.js
index db37855..341c3ea 100644
--- a/index.js
+++ b/index.js
@@ -131,8 +131,8 @@ function createWindow () {
     }
   })
   // and load the index.html of the app.
-  win.loadFile('ui/editor/index.html')
-  // win.loadFile('ui/ftp/dist/index.html')
+  // win.loadFile('ui/editor/index.html')
+  win.loadFile('ui/ftp/dist/index.html')
 }
 
 // TODO: Loading splash screen
diff --git a/ui/ftp/components/diskView.tsx b/ui/ftp/components/diskView.tsx
index 57ac8f6..fe7262f 100644
--- a/ui/ftp/components/diskView.tsx
+++ b/ui/ftp/components/diskView.tsx
@@ -6,6 +6,7 @@ import {
 } from '../main.type.ts'
 
 import BreadCrumb from './ui/BreadCrumb'
+import NavigateUp from './ui/NavigateUp'
 
 type DiskViewParams = {
   waiting: Boolean,
@@ -54,10 +55,8 @@ const DiskView: React.FC = ({ logic }) => {
         <div className="toolbar row full-width">
             <button onClick={openFolder}>Select folder</button>
         </div>
-        <div className="row full-width navigation">
-          {BreadCrumb(diskPath, navigate)}
-        </div>
         <div className="column full-width full-height list">
+          {NavigateUp(diskPath, navigate)}
           {diskFiles.map(ListItem)}
         </div>
       </div>
diff --git a/ui/ftp/components/serialView.tsx b/ui/ftp/components/serialView.tsx
index 52ee8e4..f9648fe 100644
--- a/ui/ftp/components/serialView.tsx
+++ b/ui/ftp/components/serialView.tsx
@@ -7,6 +7,7 @@ import {
 } from '../main.type.ts'
 
 import BreadCrumb from './ui/BreadCrumb'
+import NavigateUp from './ui/NavigateUp'
 
 type SerialParams = () => {
   availableDevices: AvailableDevices[]
@@ -62,6 +63,7 @@ const SerialView: React.FC = ({ logic }) => {
     )
   }
 
+
   return (
     <div className="column file-panel">
       <div className="toolbar row">
@@ -70,10 +72,8 @@ const SerialView: React.FC = ({ logic }) => {
           {availableDevices.map((d, i) => <option key={i}>{d.path}</option>)}
         </select>
       </div>
-      <div className="row full-width navigation">
-        {BreadCrumb(serialPath, navigate)}
-      </div>
       <div className="column full-width list full-height">
+        {NavigateUp(serialPath, navigate)}
         {serialFiles.map(ListItem)}
       </div>
     </div>
diff --git a/ui/ftp/components/ui/BreadCrumb.tsx b/ui/ftp/components/ui/BreadCrumb.tsx
index 6e66016..f2380d2 100644
--- a/ui/ftp/components/ui/BreadCrumb.tsx
+++ b/ui/ftp/components/ui/BreadCrumb.tsx
@@ -6,9 +6,6 @@ const BreadCrumb = (path: string, navigate: (p: string) => void) => {
       path.split('/').filter(s => s !== '')
     )
   }
-  if (pathArray.length > 2) {
-    pathArray = pathArray.slice(-3)
-  }
   return pathArray.map((name, i) => {
     const crumbs = path.split('/').filter(c => c !== '')
     const p = '/' + crumbs.slice(0, i).join('/')
diff --git a/ui/ftp/components/ui/NavigateUp.tsx b/ui/ftp/components/ui/NavigateUp.tsx
new file mode 100644
index 0000000..a533aa1
--- /dev/null
+++ b/ui/ftp/components/ui/NavigateUp.tsx
@@ -0,0 +1,16 @@
+
+const NavigateUp = (path: string, navigate: (p: string) => void) => {
+  if (!path || path === '/') return null
+  const navigateUp = () => {
+    const pathArray = path.split('/').filter(p => p)
+    pathArray.pop()
+    navigate('/'+pathArray.join('/'))
+  }
+  return (
+    <div className={`list-item`} onClick={navigateUp}>
+      <div className="checkbox">←</div><span>Back</span>
+    </div>
+  )
+}
+
+export default NavigateUp
diff --git a/ui/ftp/main.scss b/ui/ftp/main.scss
index e3ad1fe..8d873e0 100644
--- a/ui/ftp/main.scss
+++ b/ui/ftp/main.scss
@@ -111,16 +111,29 @@ main {
   background: var(--dark-tone);
   padding: var(--base-size);
   padding-bottom: var(--base-size);
-  overflow: hidden;
+  overflow: overlay;
   & > * {
     margin-right: 10px;
   }
 }
 
 .breadcrumb {
+  position: relative;
   padding: 0.5em;
+  padding-left: 2.5rem;
   background: var(--light-tone);
   cursor: pointer;
+  overflow: hidden;
+  height: 2rem;
+  text-overflow: ellipsis;
+}
+
+.breadcrumb::after {
+  content: '📁';
+  position: absolute;
+  left: 0.5rem;
+  top: 50%;
+  transform: translateY(-50%);
 }
 
 .file-management {
