{"id":14995809,"url":"https://github.com/rd4com/mojo-ui-html","last_synced_at":"2025-09-25T15:31:30.583Z","repository":{"id":214657366,"uuid":"737030686","full_name":"rd4com/mojo-ui-html","owner":"rd4com","description":"Immediate mode GUI, HTML, CSS, Work in progress, Mojo language","archived":false,"fork":false,"pushed_at":"2025-05-11T18:57:44.000Z","size":936,"stargazers_count":24,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-05-11T19:39:44.056Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Mojo","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rd4com.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-12-29T15:41:53.000Z","updated_at":"2025-05-11T18:57:47.000Z","dependencies_parsed_at":"2024-09-24T16:22:55.464Z","dependency_job_id":"f12bb0c6-4e8a-44ce-87b5-250986a5e00a","html_url":"https://github.com/rd4com/mojo-ui-html","commit_stats":null,"previous_names":["rd4com/mojo-ui-html"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rd4com/mojo-ui-html","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rd4com%2Fmojo-ui-html","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rd4com%2Fmojo-ui-html/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rd4com%2Fmojo-ui-html/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rd4com%2Fmojo-ui-html/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rd4com","download_url":"https://codeload.github.com/rd4com/mojo-ui-html/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rd4com%2Fmojo-ui-html/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276940143,"owners_count":25732250,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-25T02:00:09.612Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T16:19:49.737Z","updated_at":"2025-09-25T15:31:30.577Z","avatar_url":"https://github.com/rd4com.png","language":"Mojo","readme":"#### 🆕 \n\n- `mojoproject.toml` for `magic` !\n- 🔁 Updated for `25.3.0` 🔥!\n\n\u0026nbsp;\n\n#### ➕ Smaller changes/new features\n- UI remember scroll position between events and refreshes\n- Any window can be minimized/expanded into/from a titlebar with the `➖` button\n- Move some parameters from `Server` to `param_get_env`\n  - `mojo run -D mojo_ui_html_theme=\"theme_neutral.css\"`\n  - `exit_if_request_not_from_localhost` (default: `True`)\n- Add custom html easily \n  - `GUI.RawHtml(\"\u003ch1\u003ePage\u003c/h1\u003e\")`\n- `GUI.CustomEvent('MyEvent')`\n\n  see [demo_custom_events.mojo](./demo_custom_events.mojo)\n\n  (Just a start, not ready/user-friendly yet!) \n\n- `GUI.Event()` ➡️ `GUI.NeedNewRendition()`\n\n  Because events are handled once,\n  \n  but multiple renditions are usually done!\n\n- Faster rendering using more types\n  \n  TODO: `initial_capacity` and `clear()`\n\n- Add `with` ➡️`GUI.HorizontalGrow`, ⬇️`GUI.VerticalGrow` \n  \n  Theses are flex `div`, the content added grows it. \n  \n  see [demo_horizontal_vertical](./demo_horizontal_vertical_containers.mojo)\n\n\n\u0026nbsp;\n\n#### 🥾 Quick start\n```bash\ngit clone https://github.com/rd4com/mojo-ui-html\ncd mojo-ui-html\nmagic shell\nmojo run demo_principal.mojo\n```\n\n\u0026nbsp;\n\n#### ❤️‍🔥 Mojo is a programming language\n\nThis gui is built using mojo!\n\nUser-friendly, fast, active community, friends, python family, love it!\n\n\u003e **MAX and Mojo usage and distribution are licensed under the [MAX \u0026 Mojo Community License](https://www.modular.com/legal/max-mojo-license)**\n\n\n\u0026nbsp;\n\n#### 🎮 Good start to `learn mojo` by `creating` a small `videogame` !\nThis new feature: `GUI.keyboard_handler` is cool,\n\nit can work even when there are unfocussed UI widgets in a window👍\n```mojo\nfrom ui import *\n\ndef main():\n    var GUI = Server()\n    GUI.request_interval_second = 0 #no Time.sleep between events\n    GUI.keyboard_handler = True\n    var pos = SIMD[DType.int32, 2](0)   #[x, y] \n    while GUI.NeedNewRendition(): \n        k = GUI.KeyDown()\n        if not k.isa[NoneType]():\n            # if k.isa[Int]():\n            #     print(k[Int])# example: ord('a'), ..\n            if k.isa[String]():\n                var k_tmp = k[String] \n                if k_tmp == \"ArrowUp\": pos[1] -= 10\n                elif k_tmp == \"ArrowDown\": pos[1] += 10\n                elif k_tmp == \"ArrowLeft\": pos[0] -= 10\n                elif k_tmp == \"ArrowRight\": pos[0] += 10\n        GUI.RawHtml(String(\n            \"\u003cdiv style='position:absolute;\",\n            \"left:\",pos[0],\";\",\n            \"top:\", pos[1],\";\"\n            \"'\u003e🚙\u003c/div\u003e\"\n        ))\n```\n\n⬅️ ⬆️ ⬇️ ➡️ to move the 🚙 on the `html` page!\n\n\u0026nbsp;\n\n#### 📋 Example todo app\n```mojo\nfrom ui import *\ndef main():\n    GUI = Server()\n    var txt:String = \"test\"\n    var todos= List[String]()  \n    var time:String = \"15:00\"\n    var date:String = \"2024-01-01\"\n    var pos = Position(256,128,2.0)\n    while GUI.NeedNewRendition():\n        with GUI.Window(\"Todo app\",pos):\n            GUI.Text(str(len(todos)))\n            with GUI.ScrollableArea(128):\n                for i in range(len(todos)): \n                    GUI.Text(todos[i])\n            GUI.NewLine()\n\n            GUI.TimeSelector(time)\n            GUI.DateSelector(date)\n            GUI.TextInput(\"textinput\",txt)\n            \n            if GUI.Button(\"Add\"): \n                todos.append(GUI.Circle.Blue+\" \"+time+\" \" + date + \" \" + txt)\n            if GUI.Button(\"pop\"):\n                if len(todos):todos.pop()\n```\n\n\u0026nbsp;\n\n\u0026nbsp;\n\n\n#### 🔺 Next todo:\nCreate a tutorial that adds different widgets to the page (one by one), \n\nprogressively learning how to add to the ui.\n\n\u0026nbsp;\n\n\u0026nbsp;\n\n# mojo-ui-html\n\n\n- ### 👷👷‍♀️ Under construction, make sure to wear a helmet !\n\n- ### 🤕 Bugs and unexpected behaviours are to be expected\n\n- ### ⏳ not beginner-friendly yet (will be in the future❤️‍🔥) \n\n- ### Not ready for use yet, feedbacks, ideas and contributions welcome!\n\n\u0026nbsp;\n\n## ⚠️ \n- Server on ```127.0.0.1:8000```\n  - not meant to be a web-framework, but a simple ui\n  - Exit loop if request from other than \"127.0.0.1\" by default \n    - As an additional safeguard (not been tested)\n- Dom generated from the content of values\n  \n  - ```example: \"\u003cinput value='\" + value + \"'/\u003e\"```\n  - ```UNSAFE because value content can generate/modify html or javascript.```\n\n- If the widget id is the address of a value, two input widgets of the same value will trigger twice (need more thinking for solution)\n\n\n\n- Blocking loop by default\n\n- ❤️‍🔥 [How 'widgets' attempts(⚠️) to maintain the rendering up-to-date ?](#how-widgets-attempts%EF%B8%8F-to-maintain-the-rendering-up-to-date-)\n  - Will spin the loop multiple times and send only last rendition as response\n  - events are handled once, but multiple renditions are done\n  - ```should_re_render()``` for user initiated triggering!\n  - once response is sent, need to wait for next request (blocking socket)\n\n\n- Probably more\n\n\u0026nbsp;\n\n\n\n\n# Principal demo: ```theme.css```(default)\n\u003cimg src=\"./example.png\"\u003e\n\n\n\u0026nbsp;\n\n# Simple simd counters: ```theme_neutral.css``` \n\u003cimg src='./example2.png'/\u003e\n\n\n```python\nfrom ui import *\nfrom math import iota, sqrt\nfrom sys import simdwidthof\ndef main():\n    GUI = Server()\n    var counter = 0\n    while GUI.NeedNewRendition(): \n        #Not necessary to create a window if not needed\n        if GUI.Button(\"increment\"): counter+=1\n        if GUI.Button(\"decrement\"): counter-=1\n        GUI.Slider(\"Counter\",counter)\n        var tmp = iota[DType.float16, simdwidthof[DType.float16]()](counter)\n        GUI.Text(repr(tmp))\n        GUI.Text(repr(sqrt(tmp)))\n```\n\n\u0026nbsp;\n# Principal demo code:\n```python\nfrom ui import *\n\ndef main():\n    #⚠️ see readme.md in order to be aware about challenges and limitations!\n    val = 50\n    txt = String(\"Naïve UTF8 🥳\")\n    boolval = True\n    multichoicevalue = String(\"First\")\n    colorvalue = String(\"#1C71D8\")\n    datevalue = String(\"2024-01-01\")\n\n    GUI = Server()\n\n    POS = Position(1,1)\n    POS2 = Position(1,350)\n    POS3 = Position(32,512)\n    POS4 = Position(512,16)\n\n    combovalues = List[String]()\n    for i in range(5): combovalues.append(\"Value \"+str(i))\n    selection = 1\n\n    while GUI.NeedNewRendition():\n        with GUI.Window(\"Debug window\",POS):\n            GUI.Text(\"Hello world 🔥\")\n            if GUI.Button(\"Button\"): val = 50 \n            if GUI.Slider(\"Slider\",val): \n                print(\"Changed\")\n            GUI.TextInput(\"Input\",txt) #⚠️ ```maxlength='32'``` attribute by default.\n            GUI.ComboBox(\"ComboBox\",combovalues,selection)\n            GUI.Toggle(boolval,\"Checkbox\")\n\n        with GUI.Window(\"Fun features\",POS3):\n            GUI.Text(GUI.Circle.Green + \" Green circle\")\n            GUI.Text(GUI.Square.Blue + \" Blue square\")\n            GUI.Text(GUI.Accessibility.Info + \" Some icons\")\n            GUI.Text(GUI.Bold(\"Bold() \")+GUI.Highlight(\"Highlight()\"))\n            GUI.Text(GUI.Small(\"small\") + \" text\")\n\n            with GUI.Collapsible(\"Collapsible()\"):\n                GUI.Text(\"Content\")\n\n        with GUI.Window(\"More widgets\",POS4):\n            GUI.TextChoice(\"Multi Choice\",multichoicevalue,\"First\",\"Second\")\n            GUI.Ticker(\"⬅️♾️ cycling left in a 128 pixels area\",width=128)\n\n            with GUI.Table():\n                for r in range(3):\n                    with GUI.Row():\n                        for c in range(3): \n                            with GUI.Cell():\n                                GUI.Text(str(r) + \",\" + str(c))\n    \n            with GUI.ScrollableArea(123):\n                GUI.Text(GUI.Bold(\"ScrollableArea()\"))\n                GUI.ColorSelector(colorvalue)\n                GUI.NewLine()\n                GUI.DateSelector(datevalue) #⚠️ format is unclear (see readme.md)\n                for i in range(10): GUI.Text(str(i))\n        \n\n        with GUI.Window(\"Values\",POS2,CSSTitle=\"background-color:\"+colorvalue): \n            GUI.Text(txt)\n            if selection \u003c len(combovalues):      #manual bound check for now\n                GUI.Text(combovalues[selection])\n            with GUI.Tag(\"div\",\"background-color:\"+colorvalue):\n                GUI.Text(colorvalue)\n            GUI.Text(datevalue)\n            with GUI.Tag(\"div\",\"padding:0px;margin:0px;font-size:100\"):\n                GUI.Text(\"❤️‍🔥\")\n            GUI.Button(\"ok\",CSS=\"font-size:32;background-color:\"+colorvalue)\n```\n\n\u0026nbsp;\n\n## Features\n- 🎨 Themed with `CSS`\n  - Default theme colors are kept familiar (🎁)🔥\n  - `theme.css` where widgets have corresponding entries (class)\n  - Customize individual widgets instances styles with keyword arguments\n    - dom element style attribute\n  - [The current styling system](#-the-current-styling-system)\n  - helper function with variadic keyword arguments\n    ```python\n    #Example\n    MyStyleOne = CSS(color=\"blue\",`font-size`=32)\n    var MyStyleTwo = CSS(\n      `text-shadow` = \"1px 1px 1px yellow\",\n      `font-size` = \"32px\",\n      background = \"linear-gradient(#ffff00, #f90)\"\n    )\n    ```\n\n- 🆕 Keyboard event handler\n  - Send events only if the user is not already interacting with a dom element.\n    - That way, is is still possible to interact with an Input element independently.\n  - ```Server.keyboard_handler``` is False by default, as additional safeguard\n    ```python\n    k = GUI.KeyDown()\n    if k.isa[String]():   #special keys (Backspace,ArrowLeft,..)\n    if k.isa[Int]():      #Normal characters: chr(k.take[Int]())\n    if k.isa[NoneType](): #No sent events or keyboard_handler is False\n    ```\n  - see [demo_keyboard_and_css.mojo](./demo_keyboard_and_css.mojo)\n- Button\n  - return True when clicked\n  - ```CSS``` keyword argument, for the style attribute of the dom element (default: \"\")\n  - Naïve UTF8 support 🥳\n  - ⚠️ Label \n    - can't be too long: behaviour unexpected ([Challenges for utf8 support](#challenges-for-utf8-support))\n    - two buttons with the same label lead to wrong event\n      - usually, the first one will receive the event *(even if the second was clicked)*\n\n\n    \n\n- TextInput\n  - return ```True``` when UI interaction occured\n  - mutate the argument (passed as inout) automatically\n  - Naïve UTF8 support 🥳\n    - ⚠️ need more work, see challenges sections\n    - Additional untested safeguard:\n      -  DOM element is limited with the ```maxlength='32'``` attribute by default.\n  - ```CSSBox``` keyword argument (default: \"\")\n    - style attribute for the widget container (contains both label and input element)\n    - todo: keyword arguments for label and input element\n- Text\n- Slider\n  - return ```True``` when UI interaction occured\n  - ```label:String``` not optional\n  - mutate ```inout val:Int``` argument\n  - ```min:Int=0, max:Int=100``` keyword arguments\n  - ```CSSLabel``` keyword argument, style attribute of label (default:  \"\")\n  - ```CSSBox``` keyword argument, style attribute of widget container (default: \"\")\n- Windowing system (optional)\n  - ```Minimized``` by clicking the ```-``` icon on the title bar\n    (stored in Position struct)\n    ```python\n    #example, var MyWindow: Position, var GUI:Server\n    with GUI.Window(\"The Title\",MyWindow):\n      if MyWindow.opened: GUI.Text(\"opened\")\n    ```\n  - ```Moved``` by dragging the title bar! 🥳\n  - 🔥🔥🔥 ```Individually scaled/zoomed``` with mousewheel \"scroll\" on the titlebar !    \n  - Positions and scales on the mojo side in user defined values (```Position(0,0)```)\n    - ```Position(0,0).opened==False``` if window is minimized\n  - ```CSSTitle``` keyword argument (default to empty)\n    - Provides Additional css for the style attribute of the title box\n    - Usefull for changing the title bar background-color, for example\n\n- Toggle\n  - return ```True``` when UI interaction occured\n  - Mutate a bool passed as argument (inout)\n  - Similar to a checkbox\n- ComboBox\n  - return ```True``` when UI interaction occured\n  - ID is the inout address of the selection value\n  - The selection value is the index of the selected value in the DynamicVector of selections\n  - VariadicList support ! 🔥\n    - ```ComboBox(\"Simple combobox\",selection,\"one\",\"two\",\"three\")```\n\n\n- Collapsible\n  - Implemented as a with block\n  - ```CSS``` keyword argument, to define the style attribute of the title part.\n\n- TextChoice\n  - Inout string to store the selected value \n  - Available choices as a variadic list\n  - ```TextChoice(\"Label\", selected, \"First\", \"Second\")```\n\n- Ticker \n  - Cycle left (⬅️♾️) in an area of a specifig width (200 pixels by default).\n  - ```Ticker(\"Emojis are supported\",width=64)```\n\n- Table \n  - Simple but it is a start! \n  - Example:\n    ```python\n    with GUI.Table():\n      for r in range(3):\n          with GUI.Row():\n              for c in range(3): \n                  with GUI.Cell():\n                      GUI.Text(str(r) + \",\" + str(c))   \n    ```\n\n- ScrollableArea 🔥\n  - ```height:Int = 128``` (pixels)\n  - Example:\n    ```python\n    with GUI.ScrollableArea(50):\n      for i in range(10): GUI.Text(str(i))\n    ```\n\n- NewLine\n\n- 🎨 ColorSelector\n  - inout string argument (example: ```var c:String = \"#FF0000\"```)\n  - return ```True``` when UI interaction occured\n- ⌚ TimeSelector\n  - inout string argument (example: ```var t:String = \"23:59\"```)\n  - return ```True``` when UI interaction occured\n- 🗓️ DateSelector\n  - inout string argument (example: ```var d:String = \"2024-01-01\"```)\n  - return ```True``` when UI interaction occured\n  - ⚠️ date format:\n    - Not same for every machine?\n    - Todo: unix timestamp\n\n- Tag\n  - With block implementation, example: ```with GUI.Tag(\"div\"):```\n  - ```style``` keyword argument (example: ```\"background-color:orange;\"```)\n\n  - ```_additional_attributes``` keyword argument\n    - specify attributes on the html DOM element (example: ```\"class='otherclass'\"```)\n\n- ❤️‍🔥 should_re_render\n  - mark the rendition created by the current iteration as \"out-of-date\"\n  - allows for re-rendering before sending a response\n    - maximum is +- ```10``` by default \n    - widgets also triggers re-renders (if event handled)\n    - more testing is required ⚠️\n  - re-rendering means another iteration of the loop\n  - could trigger more than one (until two successive ones are equals, need testing)\n  - could not be done if already reached maximum (+-)\n\n\n\n- Add html manually:\n   - `GUI.RawHtml(\"\u003ch1\u003ePage\u003c/h1\u003e\")`\n\n- ⬇️VerticalGrow and ➡️HorizontalGrow\n\n  Flex divs implemented `with` context managers:\n  ```mojo\n  from ui import *\n  def main():\n      GUI = Server()\n      while GUI.NeedNewRendition(): \n          with GUI.HorizontalGrow():\n              with GUI.VerticalGrow():\n                  GUI.Text(\"First column\")\n                  for i in range(3):\n                      GUI.Button(GUI.Digitize(i))\n              with GUI.VerticalGrow():\n                  GUI.Text(\"Second column\")\n                  for i in range(3):\n                      GUI.Button(repr(i))\n  ```\n  Result:\n  ```\n  First column Second column\n  0️⃣           0\n  1️⃣           1\n  2️⃣           2\n  ```\n\n- Expressivity:\n  - Bold(\"Hello\") -\u003e **Hello**\n  - Highlight(\"Hello\")\n  - Small(\"Hello\")\n  - Digitize(153) -\u003e 1️⃣5️⃣3️⃣\n  - Square.Green 🟩 and Circle.Yellow 🟡 (Blue, Red, Black, Purple, Brown, Orange, Green, Yellow, White)\n  - Accessibility.Info (Info ℹ️, Warning ⚠️, Success ✅)\n  - Arrow.Right (Up ⬆️, Down ⬇️, Right ➡️, Left ⬅️)\n\n\u0026nbsp;\n\n## Mechanism\nThe address of a value passed as an ```inout argument``` is used as a dom element id to check for events.\n\nFor example, ```GUI.Slider(\"Slider\",val)``` will generate an html input element with the id ```address of val```.\n\nThe generated html is sent, and the page listen for any event on ```\u003cbody\u003e```.\n\nIf an event occur on the page, it first check if the target element is marked with data-attribute (example: data-change).\n\nIf it is the case, an url is generated, according to:\n-  the e.target dom element id\n- the target value (depending on wich widget it represent)\n\nIn this example: ```/slider_address_of_val/new_dom_element_value```.\n\nThe page is then redirected to that url in order to \"send\" the event.\n\nOn the mojo side, an event is \"received\" and\nthe loop runs again. \n\nThis time, the inout argument address will correspond to the current event url and the new value is assigned.\n\nAnything can be used to generate an id, require more thinking ! \n\n\n\u0026nbsp;\n# How 'widgets' attempts(⚠️) to maintain the rendering up-to-date ?\n\n\u003e Please make sure to read this section up to the end!\n \nWhen widgets handles an event, \n\nthey also mark the current response as potentially based on out-of-date values.\n\nSo the loop will runs again (one more time, if not reached limit), \n\nin order to reduce the probability of a page partially containing out-of-date values.\n\n\u0026nbsp;\n\nAnd more, \nit should run as many times as needed until two successive renditions are equals!\n\nIt is also possible to call *\"should re-render anyway\"* anywhere, \n\nmaking very interesting logic possible: ```GUI.should_re_render()```.\n\nThere is an additional safeguard on the maximum number of renditions: ```+/- 10 by default```.\n\n*(in attempt to avoid infinite loops)*\n\nThe idea is also to try to reduce the probabilities of interacting with out-of-date values:\n\n**Only the last rendition will be sent for interaction(🔥)!** \n\n\n\n\u0026nbsp;\n\n(TODO: parametrize this)\n\nOn the top right corner, \n\nthe number of iterations done (more or less) to generate the page is shown for debugging.\n\nNote that two successive frames might not be enough, in somes cases,\n\n but it is a start and feedbacks are welcomed!\n\nExample illustrating the new features: [Simple todo list](demo_todos.mojo)\n\n\u0026nbsp;\n\n#### Another example:\n```python\nfrom ui import *\ndef main():\n    GUI = Server()\n    var color:String = \"#3584E4\"\n    var favorite_color:String = \"#33D17A\"\n    var pos = Position(128,128,2.0)\n    while GUI.NeedNewRendition(): \n        with GUI.Window(\"Test\",pos,\"background-color:\"+color):\n            if color == favorite_color:\n                color = \"#FFFFAA\"\n                GUI.should_re_render()\n            GUI.Text(color)\n            GUI.Text(favorite_color)\n            if GUI.Button(\" ✅ Set as favorite color \"):\n                favorite_color = color \n            GUI.ColorSelector(color)\n```\nNote that if the favorite color is clicked two times in a row,\n\nThe additional safeguard did prevent the infinite loop! *(see top right corner)*\n\n\n\n\u0026nbsp;\n\n## Characteristics:\n### 🏜️ Less dependencies\n- Use a socket as PythonObject for now\n- To make it platform agnostic and ready to runs anywhere with little changes.\n\n### 🛞 Non blocking event loop (default mode: blocking)\n- Usecase: if no request/event, custom user defined calculations on multiple workers.\n- Additionally, slowed down by a call to the time.sleep()\n\n### 🏕️ Immediate mode vs retained\n- Works inside an explicitely user-defined loop\n  - the user choose what should happen when there are no events. (not implemented yet)\n- The full dom is re-generated after each event \n### 🎨 CSS and HTML\n- Interesting features:\n  - audio/video playing\n  - drag and drop\n  - modals\n  - more\n- To implement custom widgets\n  - Both are user friendly and easy to learn\n\n\n\n\n\n\n\u0026nbsp;\n## Challenges for UTF8 support:\nThe new value of an TextInput() is passed to mojo trough the URL (GET request).\n\nAs a temporary solution, the new value is converted to UTF8 by javascript.\n\nOn the mojo side, part the url is splitted by \"-\" and atol() is used with chr().\n\nExample: ```/change_140732054756824/104-101-108-108-111```\n\n⚠️ \n- There is an unknown maximum size for new values! ( because URLs are size limited)\n- Currenly, the socket will only read 1024 bytes from the request. (can be changed)\n\nFor theses reasons, an additional safeguard is provided (untested):\n  - For the TextInput widget:\n    - The input DOM element is limited with the ```maxlength='32'``` attribute by default.\n\nNeed more thinking! any ideas ?\n\n\u0026nbsp;\n\n# 🎨 The current styling system\nThe idea is to provide choices of default CSS to act as a base and include theses inside the ```\u003cstyle\u003e``` tag.\n\nThe default css of the widgets is 'defined' with the class attribute.\n\nIt is possible to 'patch' it on the fly with a style attribute next to it (on the right)!\n\nThis is how individual widgets instances can be customized on top of a base style (example, another font-size for that button).\n\nThe customization part could become a new abstraction on top of css. (optional user-friendly helper functions, for example)\n\nEventually, abstraction and pure css should be both be avaiblable.\n\n\n### Example\nHere is the base style for the Button widget *(theme.css)*:\n```css\n/* ... */\n.Button_ {\n    border-width: 4px;border-color: black;border-style: solid;\n    color: blue; background-color: yellow; max-width: fit-content;\n}\n/* ... */\n```\nButton can take additional CSS as a keyword argument for the ```style``` attribute of the DOM element:\n```python\nwith GUI.Tag(\"div\",\"padding:0px;margin:0px;font-size:100\"):\n    GUI.Text(\"❤️‍🔥\")\n    #Additional CSS:\n    GUI.Button(\"ok\",CSS=\"font-size:32;background-color:\"+colorvalue)\n```\n\n### Different base themes (`.CSS`)\nBy default, the type will use \"theme.css\" as a base style.\n\nThe theme can specified on the command-line:\n\n*(Thanks to [Carl Caulkett](https://github.com/carlca/) for the suggestion 🔥)*\n\n```\nmojo run -D mojo_ui_html_theme=\"theme_neutral.css\" demo_principal.mojo\n```\n\n\u0026nbsp;\n\n# For the future:\n- Toast messages (```notifications```)\n- A ```node system``` (plug, drag-drop)\n- Widget to form a number using the scrollwheel (modify individual hovered digits)\n- ```XHR Post``` instead of ```get /widget_id/newvalue  ```\n  - should fix ```%20``` problem\n  - play ```audio``` in an independent DOM element\n- ```Drag and drop``` capabilities (example: list to list)\n\n- ✏️\n\n\u0026nbsp;\n\n## Current implementation challenges:\n- The dom could be sent as json and rendered safer-ly in a JS loop.\n\n- ```onchange``` is used instead of ```oninput``` (to not keep track of dom element focus, temporarely)\n  - solved by generating serialized dom as nested nodes, and \"morphing\" it\n- More\n","funding_links":[],"categories":["GUI"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frd4com%2Fmojo-ui-html","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frd4com%2Fmojo-ui-html","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frd4com%2Fmojo-ui-html/lists"}