ewowi / StarBase

Try things out
GNU General Public License v3.0
9 stars 9 forks source link

UI Redesign #54

Open ewowi opened 5 months ago

ewowi commented 5 months ago

Current StarMod UI is a POC UI, meaning it implements the StarMod architecture but is not the user friendliest.

So we need to redesign it.

Step 1: Inspiration

Inspiration 1: Synology UI

image

Requirements:

netmindz commented 5 months ago

While I love the UI for Synology, I'm not sure it's really the best comparison, we aren't trying to build an entire OS gui, just controls for embedded devices so if be looking more at things like the UI for a WiFi access point, smart power plug etc

MoonModules commented 5 months ago

Yeah - agree we are not building an entire OS gui - But StarBase is a lightweight OS-like layer so maybe a bit of a OS GUI ... ;-)

A bit about the history:

I would like to keep most of these things in some way but realise it is too overwhelming / nerdy now. My next idea is instead of dragging them to another column is dragging them anywhere you like - so get rid of the column structure - see : https://www.w3schools.com/css/css_positioning.asp

I think implementing these absolute positions is not too difficult and it allows more freedom to organise it the way a user wants - this led me to Synology interface. Next to absolute position also width and height of a panel should be defined and panels scale / layout their elements within that boundary (scrolling if needed)

So long story short - would be nice to have this positioning implemented - as we are close to that - but for the rest we could make a control structure which makes it look much more straightforward as it is now.

What about Home Assistant interface? This also has panels which can be moved around?

Maybe you can share screenshots of simpler UI's we can use for inspiration?

ewowi commented 2 months ago

[{"id":"Files","type":"sysmod","ro":true,"max":255,"n":[{"id":"fileTbl","type":"table","ro":false,"n":[{"id":"flName","type":"text","ro":true,"max":32},{"id":"flSize","type":"number","ro":true},{"id":"flTime","type":"number","ro":true},{"id":"flEdit","type":"fileEdit","ro":false,"value":["F_Fiber.json","F_Panel-128x64.json","F_Panel-32x32.json","F_Panel-64x64.json","F_Panel-80x80.json","F_Panel-8x8.json","F_Panel-90x90.json","F_Rings241-9.json","balls.sc","blink.sc","helloworld1.sc","helloworld2.sc","helloworld3.sc","model.json","octo.sc","blink.sc","helloworld1.sc","helloworld2.sc","helloworld3.sc","model.json","octo.sc"]}]},{"id":"upload","type":"fileUpload","ro":false},{"id":"drsize","type":"progress","ro":true,"max":262144}]},{"id":"System","type":"sysmod","ro":true,"max":255,"s":true,"n":[{"id":"upTime","type":"text","ro":true,"max":16},{"id":"reboot","type":"button","ro":false},{"id":"loops","type":"text","ro":true,"max":16},{"id":"chip","type":"text","ro":true,"max":16},{"id":"heap","type":"progress","ro":true,"max":292},{"id":"mainStack","type":"progress","ro":true,"max":8192},{"id":"tcpStack","type":"progress","ro":true,"max":16384},{"id":"reset0","type":"select","ro":true},{"id":"reset1","type":"select","ro":true},{"id":"restartReason","type":"select","ro":true},{"id":"build","type":"text","ro":true,"max":32},{"id":"update","type":"fileUpload","ro":false},{"id":"name","type":"text","ro":false,"max":24,"value":"Lab-16","oldValue":"Star3232-16"},{"id":"now","type":"number","ro":true,"max":-1},{"id":"timeBase","type":"number","ro":true,"max":-1}],"view":"vAll"},{"id":"Pins","type":"sysmod","ro":true,"max":255,"n":[{"id":"pinTbl","type":"table","ro":true,"n":[{"id":"pinNr","type":"pin","ro":true,"max":40},{"id":"pinOwner","type":"text","ro":true,"max":32},{"id":"pinDetails","type":"text","ro":true,"max":256}]},{"id":"board","type":"canvas","ro":true,"interval":100,"loopFun":0},{"id":"pin19","type":"checkbox","ro":false,"value":1}]},{"id":"Print","type":"sysmod","ro":true,"max":255,"n":[{"id":"pOut","type":"select","ro":false,"value":1,"oldValue":2},{"id":"log","type":"textarea","ro":false}]},{"id":"Web","type":"sysmod","ro":true,"max":255,"n":[{"id":"clTbl","type":"table","ro":true,"n":[{"id":"clNr","type":"number","ro":true,"max":999},{"id":"clIp","type":"text","ro":true,"max":16},{"id":"clIsFull","type":"checkbox","ro":true},{"id":"clStatus","type":"select","ro":true},{"id":"clLength","type":"number","ro":true,"max":64}]},{"id":"maxQueue","type":"number","ro":true,"max":64},{"id":"wsSend","type":"text","ro":true,"max":16},{"id":"wsRecv","type":"text","ro":true,"max":16},{"id":"udpSend","type":"text","ro":true,"max":16},{"id":"udpRecv","type":"text","ro":true,"max":16}]},{"id":"Network","type":"sysmod","ro":true,"max":255,"s":true,"n":[{"id":"ssid","type":"text","ro":false,"max":31,"value":"ewtr","oldValue":""},{"id":"pw","type":"password","ro":false,"value":"zonledmod","oldValue":"","max":63},{"id":"connect","type":"button","ro":false},{"id":"nwstatus","type":"text","ro":true,"max":32},{"id":"rssi","type":"text","ro":true,"max":32}]},{"id":"E131","type":"usermod","ro":true,"max":255,"n":[{"id":"dun","type":"number","ro":false,"max":7,"value":1},{"id":"dch","type":"number","ro":false,"min":1,"max":512,"value":1},{"id":"e131Tbl","type":"table","ro":true,"n":[{"id":"e131Channel","type":"number","ro":true,"min":1,"max":512},{"id":"e131Name","type":"text","ro":true,"max":32},{"id":"e131Max","type":"number","ro":true},{"id":"e131Value","type":"number","ro":true,"max":255}]}]},{"id":"Model","type":"sysmod","ro":true,"max":255,"s":true,"n":[{"id":"saveModel","type":"button","ro":false},{"id":"showObsolete","type":"checkbox","ro":false,"value":0},{"id":"deleteObsolete","type":"button","ro":false}]},{"id":"UI","type":"sysmod","ro":true,"max":255,"n":[{"id":"vlTbl","type":"table","ro":true,"n":[{"id":"vlVar","type":"text","ro":true,"max":32},{"id":"vlLoopps","type":"number","ro":true,"max":999}]}]},{"id":"Instances","type":"sysmod","ro":true,"max":255,"n":[{"id":"insTbl","type":"table","ro":true,"n":[{"id":"insName","type":"text","ro":false,"max":32},{"id":"insLink","type":"number","ro":true},{"id":"insIp","type":"text","ro":true,"max":16},{"id":"insType","type":"text","ro":true,"max":16},{"id":"insVersion","type":"number","ro":true,"max":-1},{"id":"insUp","type":"number","ro":true,"max":-1},{"id":"inson","type":"checkbox","ro":false},{"id":"insdch","type":"number","ro":false,"min":1,"max":512},{"id":"insbri","type":"range","ro":false,"max":255,"log":true},{"id":"insShow","type":"url","ro":true},{"id":"insTS","type":"number","ro":true,"max":-1},{"id":"insTT","type":"number","ro":true,"max":-1},{"id":"insTM","type":"number","ro":true,"max":-1},{"id":"insNow","type":"number","ro":true,"max":-1}]}]},{"id":"Modules","type":"sysmod","ro":true,"max":255,"n":[{"id":"mdlTbl","type":"table","ro":true,"n":[{"id":"mdlName","type":"text","ro":true,"max":32},{"id":"mdlSuccess","type":"checkbox","ro":true},{"id":"mdlEnabled","type":"checkbox","ro":false,"value":[true,true,true,true,true,true,true,false,false,true,true,true,true,true]}]}]},{"id":"Motion Tracking","type":"usermod","ro":true,"max":255,"n":[{"id":"mtReady","type":"checkbox","ro":true},{"id":"gyro","type":"coord3D","ro":true},{"id":"accell","type":"coord3D","ro":true}]},{"id":"AppMod Demo","type":"appmod","ro":true,"max":255,"n":[{"id":"on","type":"checkbox","ro":false,"value":1},{"id":"bri","type":"range","ro":false,"max":255,"value":10,"log":true},{"id":"textField","type":"text","ro":false,"max":32,"value":"text"},{"id":"blinkPin","type":"pin","ro":false,"max":40},{"id":"frequency","type":"range","ro":false,"max":255,"value":44}]},{"id":"Live","type":"usermod","ro":true,"max":255,"n":[{"id":"script","type":"select","ro":false},{"id":"fps1","type":"text","ro":true,"max":10},{"id":"fps2","type":"text","ro":true,"max":10}]}]

ewoudwijma commented 2 months ago

Above is contents of model.json, which is used by JavaScript to generate all the fields in the ui

see this discussion: https://discord.com/channels/700041398778331156/1262883283809603698

ewowi commented 2 months ago

Copying discussion from discord here ...

ewowi commented 2 months ago

@aaronaverill said on July 17

I've done UI design and dev for a few decades if you want to bounce ideas around.

For what I know of your app, it seems to be primarily tailored to an admin dashboard kind of look and feel. If that's true, I would go with a vertical left rail for primary navigation, and a main area to the right of that with a vertical scrolling overflow Like this.

image

Yeah this typically collapses into a hamburger menu on the left side on mobile with a vertical list that scrolls if needed just like you did! Icons are obviously opional You can set some cute horizontal transition css property if you want it to "slide" out the other mobile approach would be a max of 4 bottom main menus, and then they have to drill into detail pages, but I dont think thats appropriate for such a complex config this has essentially become standard UI for this type of app. Of course, you could get creative if you want to have a unique and memorable UI 🙂

Do you have the concept of module collections? You could offer that as a top level navigation on the side, with the modules being indented under, if you get a lot of modules this can be more easily navigated

This is adjacent to UI design, but related to the implementation, and forgive me if I'm telling you stuff you know but you should move to implementing your javascript inside of classes with private functions prefixed with # so the minimizer will obfuscate them and you can stick to human readable method names but not worry about performance

Apologies for a newb question but are you doing the new ui development in a separate branch in the starbase repo? I've moved to doing all my mcu ui development with es6 modules for the javascript and rollup to bundle the files. Its a decent system that gives good control over how you serve your html, css, js from the controller. And everything is clean and modern and modular as it should be in 2024.

I guess its a personal preference but for example I find the 3,000 line index.js file of wled to be inscrutable for anyone except the original developer. Modern js techniques will allow much easier contribution, maintenance and quicker feature innovation Its a much more scalable approach as well if you find a set of pages is getting too large to serve from the mcu in a single request you can easily break them into separate files served only when needed hitting certain pages. Browser support for dynamic imports now does all the work of loading modules on demand only when needed. Cool stuff

ewowi commented 2 months ago

@ewowi July 18

I might do it just in the main branch as the old ui is still fully available, so the new ui is not interfering, it’s just /newUI to see it

We now zip all the files using npm into html_ui.h so it’s just a c file instead of index.js etc to be copied to the file system (like done in WLED) as far as I know index.html must explicitly import index.js, index.css etc so not sure if dynamic import works here ?

Fully agree but see the remark, but the idea of splitting into more files is for sure a good idea! I will submit the setup for the new UI most likely tomorrow, then you could play with it and tell me what you think I could use some help in css magic, to nicely move nodes around the screen 🙂

/newui is in StarBase: https://github.com/ewowi/StarBase/commit/a32ef8e080cd528891aa9999a7c4410759c8c087 GitHub Add newUI html, js and css - WIP · ewowi/StarBase@a32ef8e gitignore: add html_newui newnavbar.css: new: all css for navbar newui.htm: new: only meta data and file imports (css/js) newui.js: new: - NavBar class (navItem (level 1) and dropdownitem (level... @aaroneous you can check this link to see what I have added, probably you have some questions or remarks so happy to discuss. A few things: navbar has a seperate navbar.css file and a class NavBar in newui.js, to make it as modular as possible a few things in navbar is not working as nice as I would, so if you could help here that would be great: 1) the navBar is now the top item in the body, I would like a title bar above but then the menus of navbar are not aligned right when in hamburger mode (☰) 2) There are 2 levels navItem (module type) and dropdownItem (module.id), but alignment of both is not nice 3) when in hamburger mode (☰) a navItem is selected the nav-list is transitioning all the way to the right, would like that to go only as far as the length of the navItems 3) I will implement a theme class so no need to do any coloring, that will come from theme 4) As you can see if selecting a module, the json is shown which defines the module. I will make a setup of that in one of the next commits, then we can discuss that if you like (I expect we need subscreens there with back arrows, e.g. a table in the old ui will probably by a list of keywords and if you click a keyword you will see a row of the table) 5) after selecting a drop down item in non hamburger mode, the list of dropdowns should disappear but it stays (In hamburger mode it does disappear) This is in StarBase latest main commit /newui
ewowi commented 2 months ago

@aaronaverill on July 18

So my first recommendation is to have your top nav by sticky by using a flex layout for the body and wrap the page contents in a div that has overflow-y as auto so you get a vertical scrollbar

.. all your page content

I think the nav layout looks ok once that is fixed

ewowi commented 2 months ago

@ewowi July 18

Although my advanced html and even more css skills are very limited this sounds good to me 🙂 Did you saw the way I generate html: by using the cE function which does createElement, instead of extending innerHTML as i understand it’s then part of the dom and allows things like addEventListener on the nodes created that way. Does that sound okay for you? (Also used in WLED )

ewowi commented 2 months ago

@aaronaverill July 18

I have opinions hahaha It's definitely a preference thing, but typically you would put the markup in HTML, and then use something like mustache as a templating language. With the ESP32 I think you can get away with this, mustache comes to only 400 bytes gzipped! mustache is great, and it will be much cleaner looking code and faster to develop complex layouts You can append -OR- set innerHTML, either will be placed into the DOM and allow you to attach event listeners. So...

element.innerHTML = '<div onclick="nav.click()">Click me</div>'

Is equivalent to the same with append and add event handler, but it's maybe a lot less code on the mcu to serve back to the webpage

ewowi commented 2 months ago

@ewowi July 18:

Thanls a lot for your feedback, this helps a lot!

So what can we do? Does this makes sense: 1) I push another commit, using innerHTML instead of createElement as much as possible? 2) Add the html suggestion above 3) make a separate branch with a moustache POC

Regarding 2 and also 3: I can add it, but I think it would be nicer if you fork and pull request it, by that your work is also visible 😉 (micromoustache: not updated the last 2 years, so thats not good I guess 😉 )

ewowi commented 2 months ago

@aaronaverill on July 18

oh its also maybe worth mentioning, defining d = document and naming functions like cE doesn't really buy you any space savings since gzip is gonna squish all that down into a few bits if it's used everywhere in your code. Since vscode is doing function completion, it's pretty much just as easy to have the javascript say document.createElement ...etc... and then it's more maintainable by others that dont know the homegrown system of abbreviations.

I guess wled did this but maybe it was before there is so many build tools to squish files down for you probably the minifier and gzip are smarter than us.. they will eliminate functions you never use, etc i gotta hit work but will have a chance to check later on the weekend cheers

Sorry, I missed this earlier.

My opinion: If you're starting an entirely new project, and if you're requiring ESP32, use modern tools all the way, modern javascript languages and pull in any libraries that are useful and will work on the platform. Set up the project to be relevant for another 5-10 years.

To me that would also mean modern javascript, so just as an example. this:

let found = false; for (let module of model) { if (module.id == json.id) found = true; }

Becomes this:

const found = model.some(module => module.id == json.id) Beyond being quicker to develop and more friendly to web devs that might contribute, leveraging the full javascript api is going to make the code much smaller this is just one example but there is lots of language to leverage! ✌️ To your point. I feel like you should have an experimental UI branch thats not on main where you can really mess around with new tech and modern approaches and do some learning

I'm also happy to set up a base app framework window - body, top nav, pages, with a modern approach, if you wanna use that as a learning tool. I'm not sure if your goals are to just get this done or more a hobby where you're playing around learning new tech

ewowi commented 2 months ago

@ewowi July 18

ounds like a good plan to use the latest insights in setting it up, I am not sure what the average level of our contributors is, mostly I am asked to do UI stuff and I am good at Google and copy and paste, so I took what WLED had and build on it

For sure! I did current commit in main as it is running side by side, and not interfering and allows more people to see it , instead of them needing to compile a different branch

That sound great, my goal is somewhat in between getting it done and play around with new tech, the way we work is continuous improvement so it can always be better But if you could help setting up a framework that would be great, but I don’t have the time to do it all by myself so I hope you can fork and pull request this framework - in another branch of course So one thing to keep in mind is that the ui is fully generated based on the json file, plus the ui on itself asks the server if it needs more info, eg list of values for drop down lists, also the server periodically sends info using websockets eg uptime etc but also the preview LEDs data. This is setup in a way that the ui has for sure not a single notion of LEDs and also very little notion of the system modules (with a few pragmatic exceptions). If I understood correctly moustache could help in json to html conversions / deal with this dynamic setup

Added this in latest commit:

https://github.com/ewowi/StarBase/commit/e41595a682547c4d8d145719d7db8851653c7d6a

Did not use innerHTML but cE calls here as otherwise I did not know how to place the call to the navBar class in between

navBar = new NavBar(body); //adds the navBar to <body>

This is an improvement, thx! GitHub newui: style body and content area and place all content here · ewo... idea by aaroneous newui: style body and content area and place all content here · ewo...

if you have suggestions for point 1 to 5, I am happy to hear 🙏 I think it are mostly changes in the css file

And I am thinking, shouldn’t these style options also be moved to css ?

@aaroneous not to overhelm you with questions (the nr 1 to 5), so no hurry, but maybe start with 1. Here you can see what I mean: I added a title bar (and some more temporary stuff) above the navbar and when I click on the hamburger, the nav-list is shown above the hamburger instead of below I think it might have to do with position: absolute / top 40px but when I change it to relative it didn't work well, so any suggestions: Here is the css file for reference: https://github.com/ewowi/StarBase/blob/main/data/newnavbar.css

ewowi commented 2 months ago

@aaronaverill at July 19

definitely the absolute 40px, you need to set that px to be higher, or possibly have an empty positioning container around it that is positioned relatively, eg:

<div style="position:relative"> <!-- The drop down container -->
   <div style="position:absolute"> <!-- the actual drop down -->

The position of any absolute element is based on the nearest parent that is relative, or in the case of none, the browser window. So without the container, your menu is going to be 40px from the top of the browser screen.

You need to keep the drop down "absolute" because that style will cause it to be removed from the flow of the page layout - it won't take up space pushing things below it and will sit on top like you want

I haven't looked closely at your code, but if you have your menu in an outer container, you can then show/hide that with the toggle, and when it's shrunk to mobile use CSS media queries to change it from position:relative to position:absolute with a 100% width/height and top/left = 0, so your mobile menu fills the entire screen until they click (when it hides)

Some of this you can do with css styles, so you can do with javascript. I'd start by adding a class to the menu called "top-nav" or something Sorry haven't had a look at your code in detail yet

I will try to have two project templates for you by Sunday based on your GUI here: A standard HTML/CSS/JS that shows modern standards and you can drop into the project as-is A ES6 module version which will require some changes to your node bundling scripts, but offers better options for modularity and control over the final output sized served from the mcu

If nothing else, maybe you can learn some techniques I found have helped with my web development I'll structure these so you can also run these projects in vscode "Live Server" which gives you an option to quickly do code changes and development without any compile, upload, reboot cycle. The vscode extension runs a webserver off your file system and hot reloads the browser every time you save a file for instant feedback

oh its delightful. in vscode click Extensions search for "Live Server" install restart vscode rightclick your html file and select "Open with Live Server" Works great on your codebase now Only small issue is that you have to comment out the websockets call and mock with a json file but it supports javascript fetch and serves from the file system so you can do this:

var json = await fetch('/server-response.json').json()

And here you have all your mocked data in the file server-response.json And then... instant browser updates while you edit and save html, js, css 😄

You can easily layer mobile navigation ui on later. Or change up the nav hierarchy but its something that can give you ideas if nothing else Yay json!! Its practically done right? 😅 I gotta work on some of my own hobby projects but feel free to ask anything about how I approached the structure or js functions that I'm using. I think you can pretty much drop in your websocket code, move it into starbase and in theroy it should just build and run off the chip You've got some areas to show alerts and sample code how to write a setter and update the UI to display things. Good luck!

this is decent https://www.youtube.com/watch?v=IEf1KAcK6A8

it doesn't cover private class properties (#) or setters, but I think you can see my code and figure that out in 2 seconds lol

@ewowi Maybe one question to give a hint on: my initial screens (after on load) are build up using the model.json but after that the ui is ‘starting to live’ using websockets communicating back and forth like give me the options of a select, or hey I got a new value for you, or there is a new row in a table (or I created a new row in a table). I implemented that now playing heavily with nodes. Can you give it a thought (not necessarily now) how that will be implemented ? (Links also welcome), is that where moustache could play a role ?

I think you can just refresh the whole UI by setting innerHTML on any main pages which are active. Until you find performance is too shitty then write incremental updates of different areas more smartly

ewowi commented 2 months ago

@ewowi July 22

I am doing things step by step (to flatten the learning curve 😉 ): Added support for live server in the newui.js. See

https://github.com/ewowi/StarBase/commit/c5f0616e1e828f65779ddf58d747faf9297b4da5

Added there also a mini versie of your App class, which is invoked now if the ip is 127.0.0.1 (Live Server). Could you do a quick check there (although it is now a mix of old and new 😉 )

3 short questions: you don't use ; at the end if a line, are they not advised to use anymore? Also not in old style code? (I noticed that in case I forgot one, it was never a problem) what does # means in this.#updateUI etc? Why the # not sure about set yet, can you give a short explanation?

The live server is really cool by the way!!! so much easier now to develop

ewowi commented 2 months ago

@aaronaverill on July 22

the advantage of a setter is you can then do things like refresh the UI, but it is functionally equivalent of writing a function to set a member variable

ewowi commented 2 months ago

Added new commit https://github.com/ewowi/StarBase/commit/5e792194b46023f31da1c67565ea514508af7a4c

Implementing main navigation as proposed by @aaronaverill and made some modifications to make it fit for StarMod

https://github.com/user-attachments/assets/8b47f018-4686-45b6-9ca4-084267324833

Click on the commit changes to review what is done.

Main challenge: make it possible to use modern ui design while keeping it modular so also old style can be used to not have a big bang change which is a step to far for current devs including me ;-) So we take it step by step.

What has been done:

ewowi commented 2 months ago

New version in NewUi-dev branch: https://github.com/ewowi/StarBase/commit/864477f341b48e06551025b089bdb65fb4cf2f82