In the web app a backend module is used as an app: it opens from the home screen, shows its sections as tabs, and keeps running in the background when the user leaves it. This page explains the app catalogue entries the client reads, how webapp/js/module-app.js builds a module app, how running apps keep their screens, and how webapp/js/app-scope.js publishes which modules are installed. The home screen itself is on home screen, dock, and launcher.
In short¶
- The client knows its apps only from
GET /app_store/registry, which returns each app's manifest and its entitlement state for the signed-in user (webapp/js/home.js:409-419,src/cluster_erp/modules/app_store/routes.py:18-40). - The catalogue file holds 28 app manifests; their
screenvalues aremodule(20),list(2), and one each ofsettings,library,store,assistant,mail, andappmgr(counted by parsingsrc/cluster_erp/modules/app_store/catalog.json). screenRenderer(a)maps thescreenvalue to a render function;modulebecomesmakeModuleScreen(webapp/js/home.js:2415-2441).makeModuleScreenturns each sidebar section into a tab of a bottom tab bar (webapp/js/module-app.js:69-235).- Leaving an app keeps its live screen in memory; only the switcher's close resets it (
webapp/js/home.js:2252-2326,webapp/js/home.js:2535-2557).
How it works¶
App manifests¶
Each registry entry carries a manifest from the catalogue. The client reads these manifest fields:
| Field | Use in the client | Source |
|---|---|---|
id, label, glyph, color, bg |
Tile, dock icon, and title | webapp/js/home.js:520, webapp/js/home.js:1063-1070 |
screen |
Which renderer opens the app | webapp/js/home.js:2415-2441 |
module, modules |
The backend module the app shows, and every module it covers | webapp/js/home.js:794-800, webapp/js/home.js:2429 |
entity, endpoint |
The entity of a list app |
webapp/js/home.js:2431-2437 |
tableList |
Full-column tables in the app's lists | webapp/js/home.js:2429-2437 |
menu |
The app opens as a transient menu, not a running app | webapp/js/home.js:2452-2465, webapp/js/home.js:2469-2473 |
permanent |
The tile cannot be removed from the home screen | webapp/js/home.js:742-744 |
dock_fixed |
The app is pinned at the start of the dock and cannot be unpinned | webapp/js/home.js:990-997 |
folder |
The app joins a system folder when added to the home screen | webapp/js/home.js:762-780 |
gated_entry |
The app's custom code ships as a gated bundle | webapp/js/home.js:452-457 |
The state map from the registry hides an app on every surface unless it is installed, licensed, and seat-assigned to one of the user's roles (webapp/js/home.js:425-430). The layers behind these flags are described on app store, licensing, and entitlements.
Building a module app¶
makeModuleScreen(moduleName, {table, accent}) returns a render function that waits for metadata and for the module's gated bundle, then reads the module's sidebar (webapp/js/module-app.js:69-101):
- Sections with
hidden_from_barare skipped. They stay reachable from search (webapp/js/module-app.js:103-105). - Sections with
required_permissionare skipped for a user without that code (webapp/js/module-app.js:106-108). - Each remaining section becomes one tab, or one tab per entity for
tabbed_list(webapp/js/module-app.js:109-137). See metadata rendering for the engine perview_type. - A module with one tab shows its content without a tab bar (
webapp/js/module-app.js:148-154). - The tab bar is a
tablistwith roving focus; the left and right arrow keys move between tabs (webapp/js/module-app.js:156-212). - Each tab paints into a fresh pane, so a late answer from a previous tab cannot overwrite the current one (
webapp/js/module-app.js:183-192). - A
cluster:module-tabevent with a module and a section key switches tabs; parameters re-render the target tab even when it is already active (webapp/js/module-app.js:213-231).
Tab icons are looked up by the section's icon name in TAB_ICONS, with a square as fallback (webapp/js/module-app.js:21-66).
Running apps and keep-alive¶
The home screen keeps a list of running apps, each with its own view stack (webapp/js/home.js:2223-2234):
openApp(a)moves the current app's screen aside and either restores the target app's screen or renders its root (webapp/js/home.js:2469-2500).stashActive()moves the live DOM nodes, scroll offsets, title, and back label into a document fragment and records the data version seen (webapp/js/home.js:2252-2291).unstashInto(entry)puts the nodes back. If data changed meanwhile, or a paint was dropped while the screen was away, the screen is redrawn, except when a form is open (webapp/js/home.js:2293-2326). See data refresh and change signals.- After a language change, stashed screens are marked stale and redrawn on return (
webapp/js/home.js:2334-2342,webapp/js/home.js:2501-2511). closeRunningApp(id)discards the stashed screen and the module's form drafts (webapp/js/home.js:2533-2557).- On logout
closeAllApps()empties the app content area so no screen of the old session remains (webapp/js/home.js:2559-2578).
pushView(title, render) adds a sub-screen to the active stack and popView() removes it; popping the root returns to the home screen with the app still running (webapp/js/home.js:2400-2413). Feature code calls these through nav.js, which home.js fills at load, so engines never import the shell (webapp/js/nav.js:5-30, webapp/js/home.js:2848-2849).
Opening a section from elsewhere¶
A widget tap, a record link, and a search result open an app section through the same path. openModuleSection(module, section, params) opens the app that covers the module, returns to its root screen, and dispatches cluster:module-tab after the tab bar exists (webapp/js/home.js:221-240). Generic code that cannot import the shell raises cluster:open-section instead (webapp/js/home.js:512-519, webapp/js/gen-form.js:2371-2385).
App scope¶
syncAppScope() collects the modules of every installed app and passes them to setInstalledModules (webapp/js/home.js:792-800). app-scope.js stores the sorted list and dispatches cluster:apps-changed when it changes (webapp/js/app-scope.js:7-18). The assistant sends this list with every message and cancels an open task card whose module was removed (webapp/js/assistant.js:201-230). See assistant and chat screens.
Why it is built this way¶
- The project's written engineering rules state that a module is an application and its pages are that app's tabs, and that the CRM folder is an approved exception.
- The comment in
webapp/js/home.js:2252-2258states that keep-alive keeps typed input, sub-pages, and scroll positions without serialising anything, so the state dies with the session. - The comment in
webapp/js/module-app.js:183-187states that per-tab panes stop a delayed answer from a previous tab from overwriting the new tab.
Rules and boundaries¶
crmhas no module app. Its entities appear as twolistapps (customer-parts,rfqs) and in themodulesof thepartiesapp (src/cluster_erp/modules/app_store/catalog.json:1002-1150).- A menu app (
menu: true, the Apps library) is never added to the running apps and never appears in the switcher (webapp/js/home.js:2448-2465). - Browser tests:
web-client/_e2e_switcher.mjs,web-client/_e2e_keepalive_paint.mjs,web-client/_e2e_lang_background.mjs,web-client/_e2e_mobile_app.mjs.
Interaction with other parts¶
- Custom sections are rendered by widgets from gated bundles; see gated app bundles.
- The store, app manager, and licence screens are built by
renderStore,renderAppManager, andrenderLicenseinwebapp/js/home.js:3588-4319. Their server side is on app store module.
Limits and open points¶
view_type: "detail"is accepted bySidebarSectionMetabutmakeModuleScreenhas no branch for it (webapp/js/module-app.js:109-137). No module declares it.- A reload restores running apps at their root screens only (
webapp/js/home.js:2579-2598).
Terms introduced¶
| Term | Code identifier | Definition | Turkish term | Source |
|---|---|---|---|---|
| module app | makeModuleScreen |
A backend module presented as an app whose sidebar sections are tabs. | modül uygulaması | webapp/js/module-app.js:69 |
| app manifest | registry.apps[].manifest |
The catalogue entry that tells the client how to show and open an app. | uygulama manifestosu | webapp/js/home.js:447 |
| keep-alive | stashActive |
Keeping a left app's live screen in memory until it is closed. | arka planda canlı tutma | webapp/js/home.js:2259 |
| app scope | setInstalledModules |
The list of modules covered by the installed apps. | uygulama kapsamı | webapp/js/app-scope.js:9 |
Related pages¶
- Web app platform
- Metadata rendering
- Home screen, dock, and launcher
- Gated app bundles
- Module lifecycle
Verified against commit 92f8051 on 2026-11-13.