Development

This commit is contained in:
Matthew Grotke 2026-05-29 22:26:30 -04:00
parent aff93abf5f
commit 8f377b1839
2 changed files with 31 additions and 3 deletions

View file

@ -19,13 +19,14 @@ app/ Python source (baked into image)
<pagename>/ <pagename>/
content.json Declarative page layout content.json Declarative page layout
action.py Flask Blueprint for POST actions on this page action.py Flask Blueprint for POST actions on this page
page.js? Optional page-specific client-side JS (auto-included if present)
sanitize.py Input sanitization (strips dangerous characters) sanitize.py Input sanitization (strips dangerous characters)
config_utils.py Config I/O, snapshot system, command queue config_utils.py Config I/O, snapshot system, command queue
authorized_accounts.json Web UI user accounts (separate from Routlin users) authorized_accounts.json Web UI user accounts (separate from Routlin users)
data/ Live-mounted at runtime (./data:/data) data/ Live-mounted at runtime (./data:/data)
styles.css Application stylesheet styles.css Application stylesheet
common.js Client-side interaction logic common.js Shared client-side interaction logic (all pages)
validation.js Client-side field validation validation.js Client-side field validation
# host directory mounted into container: $HOME/routlin -> /routlin_location # host directory mounted into container: $HOME/routlin -> /routlin_location
@ -198,6 +199,10 @@ bp = Blueprint(_PAGE, __name__)
In `app/main.py` (or wherever blueprints are registered), add the new action blueprint alongside the existing ones. In `app/main.py` (or wherever blueprints are registered), add the new action blueprint alongside the existing ones.
### 5. Add page.js (optional)
If the page needs client-side behavior not shared with other pages, create `app/pages/<pagename>/page.js`. It is automatically included - no registration needed. See the "Page-Specific JavaScript" section for guidance on what belongs here vs `common.js`.
--- ---
## Token System ## Token System
@ -375,6 +380,27 @@ For data that must be passed to a JS validator (such as existing VLAN IDs for un
--- ---
## Page-Specific JavaScript
If a page needs client-side behavior that is not shared with any other page, put it in `app/pages/<pagename>/page.js`. `view_page.py` automatically appends this file to the inline script bundle when it exists.
```
app/pages/ddns/page.js -- credential provider toggle
app/pages/physicalinterfaces/page.js -- interface config card wiring
app/pages/actions/page.js -- history revert button state
app/pages/networklayout/page.js -- is_vpn -> mdns_reflection sync
```
`page.js` runs after `common.js` and `validation.js`, so all shared utilities (`htmlEsc`, `showCard`, `tablePickerCloseAll`, etc.) are available.
Rules for what belongs in `page.js` vs `common.js`:
- **`page.js`**: behavior that only activates on one page (specific field names, CSS classes, or form actions that appear nowhere else).
- **`common.js`**: shared infrastructure used by multiple pages, or global event handlers (click-to-close, UUID hover highlight, stat card edit mode).
- **Inline `<script>` from factory**: per-element wiring emitted by factory at render time (form validation, table-picker row wiring). Factory uses `document.currentScript.previousElementSibling` to scope the script to the element it follows.
---
## JS Table Workers ## JS Table Workers
Non-standard `input_type` values in `inline_edit` row actions require a table worker. `factory.py` detects any `input_type` not in `STANDARD_INPUT_TYPES = {'text', 'password', 'number', 'checkbox', 'select', 'textarea'}` and emits a `<script>` block via `build_table_worker_script()` that registers the worker using `registerTableWorker(id, impl)`. Non-standard `input_type` values in `inline_edit` row actions require a table worker. `factory.py` detects any `input_type` not in `STANDARD_INPUT_TYPES = {'text', 'password', 'number', 'checkbox', 'select', 'textarea'}` and emits a `<script>` block via `build_table_worker_script()` that registers the worker using `registerTableWorker(id, impl)`.

View file

@ -456,7 +456,8 @@ JS-driven table where the user can add, edit, and remove rows. Submits as a JSON
{ {
"type": "readonly_select", "type": "readonly_select",
"label?": "Gateway", "label?": "Gateway",
"name?": "gateway" "name?": "gateway",
"hint?": "Helper text shown below the select."
} }
``` ```
Disabled `<select>` placeholder shown while dependent data is loading. JS replaces it with real options once identities are available. Disabled `<select>` placeholder shown while dependent data is loading. JS replaces it with real options once identities are available.
@ -470,7 +471,8 @@ Disabled `<select>` placeholder shown while dependent data is loading. JS replac
"label": "Custom Rules", "label": "Custom Rules",
"name": "custom_rules", "name": "custom_rules",
"override_name?": "custom_rules_override", "override_name?": "custom_rules_override",
"validate?": "ip_in_subnet" "validate?": "ip_in_subnet",
"hint?": "Helper text shown below the textarea."
} }
``` ```
Read-only textarea with an "Override" checkbox. Checking the box enables editing. `override_name` is the checkbox's `name` attribute and defaults to `{name}_override`. Read-only textarea with an "Override" checkbox. Checking the box enables editing. `override_name` is the checkbox's `name` attribute and defaults to `{name}_override`.