core+shell: the bridge, and the first module through it
One declaration does three jobs: renders the panel, carries the defaults, names
the values that come back. A module writes no DOM and reads no DOM.
Validation refuses rather than repairs -- unknown type, a renderer its type does
not have, a label that is not text, a default outside its own bounds, a when
naming a key the module never declared. A module that is wrong does not get a
best-effort panel; it does not load, and says why once.
Ordering is by declared relation and stage, never priority numbers, and a cycle
drops only the modules in it. Compatibility is by trait, never model name.
The shell hands a module { composer, values, on, shell } and nothing else: no
node, no document, no kit internals. It cannot style anything because it is
never given anything that could.
catalogue/hide.html puts one good module and five broken ones through the real
shell and shows the result: one panel, five absent, no placeholders. The lint
fixtures make the scanner prove it catches each rule, so a clean scan means
something.
First slice: modules/system/appearance. Two settings, an optional ui.js, and
clicking Light actually turns the app light.
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""Feature modules, grouped by domain.
|
||||
|
||||
Nothing imports from here directly: core scans the folder. A module is a folder
|
||||
with an __init__.py that declares ID, TITLE, MOUNT and SETTINGS.
|
||||
"""
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Appearance: theme and grid density.
|
||||
|
||||
The first real vertical slice, and deliberately the smallest one that exercises
|
||||
the whole path -- announcement, schema, panel, values -- with no model, no
|
||||
sampler and nothing that needs a GPU to be seen working.
|
||||
"""
|
||||
|
||||
ID = "appearance"
|
||||
TITLE = "Appearance"
|
||||
MOUNT = "settings.general"
|
||||
STAGE = "load"
|
||||
STATUS = "proven"
|
||||
|
||||
SETTINGS = {
|
||||
"theme": {
|
||||
"type": "enum", "default": "auto",
|
||||
"label": "Colour scheme", "ui": "segmented",
|
||||
"options": [
|
||||
{"value": "dark", "label": "Dark"},
|
||||
{"value": "light", "label": "Light"},
|
||||
{"value": "auto", "label": "Auto"},
|
||||
],
|
||||
},
|
||||
"density": {
|
||||
"type": "int", "default": 0, "min": 0, "max": 4, "step": 1,
|
||||
"label": "Media columns",
|
||||
"hint": "0 fits as many as the panel allows.",
|
||||
"ui": "stepper",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def apply(values, context=None):
|
||||
"""Nothing to do at generation time: appearance is the browser's business.
|
||||
|
||||
Declared anyway so the module has the same shape as every other one -- a
|
||||
module core has to special-case is a module that will be forgotten.
|
||||
"""
|
||||
return context
|
||||
@@ -0,0 +1,15 @@
|
||||
// Appearance needs a little JavaScript, because its settings change the app
|
||||
// rather than the generation. Most modules have no ui.js at all.
|
||||
|
||||
export function setup({ values, on, shell }) {
|
||||
const apply = (key, value) => {
|
||||
if (key === "theme") shell.theme.set(value);
|
||||
if (key === "density") shell.density.set(value);
|
||||
};
|
||||
|
||||
// Apply what is already stored, then follow every later edit.
|
||||
const current = values.get();
|
||||
for (const key of Object.keys(current)) apply(key, current[key]);
|
||||
|
||||
return on(apply);
|
||||
}
|
||||
Reference in New Issue
Block a user