HTML Based Mini App Source section: `https://appsudo.com/doc#html-mini-app` Core idea - An HTML mini app renders its UI through a single `WebView`. - HTML, CSS, and JavaScript are shipped as local files inside the app. - No web server is required. Typical folder shape ```text myhtmlapp/ myhtmlapp.py metadata.json logo.png view/ webview.asxml static/ index.html page_two.html style.css script.js ``` Critical rule: secured URLs - Raw `file://` paths are not loaded directly by `WebView`. - Always wrap local file URLs with `webview.get_secured_url(...)`. - Use `self.application_path` when building paths. - Compute every page's secured URL in Python before the HTML needs it. Standard wrapper pattern from the docs ```python from appsudo import App from components.view import ViewInflater class MyHtmlApp(App): def __init__(self, application_path): App.__init__(self, "MyHtmlApp", application_path) self.inflater = ViewInflater(self) self.ui_view = None self.setup() def setup(self): self.ui_view = self.inflater.inflate("webview", None) def post_setup(self): webview = self.ui_view.find_element_by_id("webview") index_url = webview.get_secured_url( f"file://{self.application_path}/static/index.html" ) webview.set_url(index_url) def run(self): App.run(self) self.render(self.ui_view.render()) self.post_setup() ``` View file from the docs ```xml ``` Multi-page pattern - Precompute one secured URL per HTML file. - Store those URLs in `window.localStorage` using `webview.eval(...)`. - Have pages navigate by reading the known localStorage keys. Representative multi-page wrapper: ```python class RecipeBookApp(App): def post_setup(self): webview = self.ui_view.find_element_by_id("webview") home = webview.get_secured_url(f"file://{self.application_path}/static/index.html") about = webview.get_secured_url(f"file://{self.application_path}/static/about.html") contact = webview.get_secured_url(f"file://{self.application_path}/static/contact.html") webview.set_url(home) webview.eval(f""" window.localStorage.setItem('home', '{home}'); window.localStorage.setItem('about', '{about}'); window.localStorage.setItem('contact', '{contact}'); """) ``` Representative HTML example from the docs ```html Tip Calculator

Tip Calculator

Total: $47.20
``` Controller mapping for HTML games - If `metadata.json` declares `"type": "Game"`, an on-screen controller appears. - HTML receives controller interaction as normal `keydown`/`keyup` events. - Important key codes include: `19/20/21/22` for arrows, `96` for A, `97` for B, `108` for Start, `109` for Select. Code Snippets From Docs - The blocks below were extracted from the corresponding section of `appsudo.com/doc`. Snippet 1 ``` myhtmlapp/ myhtmlapp.py # App subclass, entry point metadata.json # icon, type, tags, author, displayName, scope logo.png # app icon view/ webview.asxml # one WebView, fill x fill static/ index.html # first page loaded by the WebView page_two.html # any extra pages the app ships style.css # optional shared assets script.js ``` Snippet 2 ``` from appsudo import App from components.view import ViewInflater class MyHtmlApp(App): def __init__(self, application_path): App.__init__(self, "MyHtmlApp", application_path) self.inflater = ViewInflater(self) self.ui_view = None self.setup() def setup(self): self.ui_view = self.inflater.inflate("webview", None) def post_setup(self): webview = self.ui_view.find_element_by_id("webview") index_url = webview.get_secured_url( f"file://{self.application_path}/static/index.html" ) webview.set_url(index_url) def run(self): App.run(self) self.render(self.ui_view.render()) self.post_setup() ``` Snippet 3 ``` ``` Snippet 4 ``` def post_setup(self): webview = self.ui_view.find_element_by_id("webview") home = webview.get_secured_url(f"file://{self.application_path}/static/index.html") about = webview.get_secured_url(f"file://{self.application_path}/static/about.html") contact = webview.get_secured_url(f"file://{self.application_path}/static/contact.html") webview.set_url(home) webview.eval(f""" window.localStorage.setItem('home', '{home}'); window.localStorage.setItem('about', '{about}'); window.localStorage.setItem('contact', '{contact}'); """) ``` Snippet 5 ``` About ``` Snippet 6 ``` from appsudo import App from components.view import ViewInflater class ArcadeDotApp(App): def __init__(self, application_path): App.__init__(self, "ArcadeDot", application_path) self.inflater = ViewInflater(self) self.ui_view = None self.setup() def setup(self): self.ui_view = self.inflater.inflate("webview", None) def post_setup(self): webview = self.ui_view.find_element_by_id("webview") index_url = webview.get_secured_url( f"file://{self.application_path}/static/index.html" ) webview.set_url(index_url) def run(self): App.run(self) self.render(self.ui_view.render()) self.post_setup() ``` Snippet 7 ``` Arcade Dot ``` Snippet 8 ``` { "icon": "logo.png", "type": "Game", "tags": "Arcade", "author": "You", "displayName": "Arcade Dot", "scope": "thirdparty" } ``` Snippet 9 ``` from appsudo import App from components.view import ViewInflater class MiniArcadeApp(App): def __init__(self, application_path): App.__init__(self, "MiniArcade", application_path) self.inflater = ViewInflater(self) self.ui_view = None self.setup() def setup(self): self.ui_view = self.inflater.inflate("webview", None) def post_setup(self): webview = self.ui_view.find_element_by_id("webview") menu = webview.get_secured_url(f"file://{self.application_path}/static/menu.html") tap = webview.get_secured_url(f"file://{self.application_path}/static/coin_tap.html") dodge = webview.get_secured_url(f"file://{self.application_path}/static/dodge.html") webview.set_url(menu) webview.eval(f""" window.localStorage.setItem('menu', '{menu}'); window.localStorage.setItem('tap', '{tap}'); window.localStorage.setItem('dodge', '{dodge}'); """) def run(self): App.run(self) self.render(self.ui_view.render()) self.post_setup() ``` Snippet 10 ``` Mini Arcade

Mini Arcade

``` Snippet 11 ``` Coin Tap

Press A to collect a coin. SELECT to go back.

0

``` Snippet 12 ``` Dodge It ``` Snippet 13 ``` from appsudo import App from components.view import ViewInflater class TipCalcApp(App): def __init__(self, application_path): App.__init__(self, "TipCalc", application_path) self.inflater = ViewInflater(self) self.ui_view = None self.setup() def setup(self): self.ui_view = self.inflater.inflate("webview", None) def post_setup(self): webview = self.ui_view.find_element_by_id("webview") index_url = webview.get_secured_url( f"file://{self.application_path}/static/index.html" ) webview.set_url(index_url) def run(self): App.run(self) self.render(self.ui_view.render()) self.post_setup() ``` Snippet 14 ``` Tip Calculator

Tip Calculator

$0$40$200
0%18%30%
Total: $47.20
``` Snippet 15 ``` { "icon": "logo.png", "type": "App", "tags": "Utility", "author": "You", "displayName": "Tip Calculator", "scope": "thirdparty" } ``` Snippet 16 ``` from appsudo import App from components.view import ViewInflater class RecipeBookApp(App): def __init__(self, application_path): App.__init__(self, "RecipeBook", application_path) self.inflater = ViewInflater(self) self.ui_view = None self.setup() def setup(self): self.ui_view = self.inflater.inflate("webview", None) def post_setup(self): webview = self.ui_view.find_element_by_id("webview") home = webview.get_secured_url(f"file://{self.application_path}/static/index.html") about = webview.get_secured_url(f"file://{self.application_path}/static/about.html") contact = webview.get_secured_url(f"file://{self.application_path}/static/contact.html") webview.set_url(home) webview.eval(f""" window.localStorage.setItem('home', '{home}'); window.localStorage.setItem('about', '{about}'); window.localStorage.setItem('contact', '{contact}'); """) def run(self): App.run(self) self.render(self.ui_view.render()) self.post_setup() ``` Snippet 17 ``` Recipe Book

Recipe Book

Welcome. Pick a recipe from the menu or read about this app.

``` Snippet 18 ``` { "icon": "logo.png", "type": "App", "tags": "Reference", "author": "You", "displayName": "Recipe Book", "scope": "thirdparty" } ``` Snippet 19 ``` window.addEventListener('keydown', function (e) { if (e.keyCode === 96) { /* A pressed */ } if (e.key === 'ArrowUp') { /* Up pressed */ } }); ``` Detailed Coverage Map - The entries below were derived from the section structure in `appsudo.com/doc`. Articles - `html-mini-app-overview`: 1. Overview - `html-mini-app-folder-layout`: 2. Folder layout - `html-mini-app-secured-url`: 3. Working with URLs and get_secured_url - `html-mini-app-webview-view`: 4. The view file - `html-mini-app-python-wrapper`: 5. The Python wrapper Methods/attributes: __init__, setup, post_setup, run - `html-mini-app-multi-page`: 6. Multi page apps without a webserver - `html-mini-app-game-single`: 7. Game example, single HTML file - `html-mini-app-game-multi`: 8. Game example, multi HTML files - `html-mini-app-regular-single`: 9. Regular HTML app example, single HTML file - `html-mini-app-regular-multi`: 10. Regular HTML app example, multi HTML files - `html-mini-app-key-mapping`: 11. Key mapping table for the game controller