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
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