A composition of elements reads like a sentence: nesting plain HTML custom elements is enough to describe what a widget does, while how it does it stays private to each element.
Like any HTML element, a TCE is nested to be meaningful. A window showing an application is one element slotted into another:
<tce-window data-title="Notepad">
<tce-notepad slot="display" init-file-path="/documents/todo.txt"></tce-notepad>
</tce-window>
Each element is agnostic of the others' internals: tce-window does not know what
tce-notepad does once it is slotted, and tce-notepad does not know how its window draws
itself. They agree only on the slot name and on the request events of
Interfacing.
A slotted child is a more complex attribute of its parent, keyed by its slot name. The
desktop's system tray is such a slot:
<tce-desktop>
<tce-clock slot="task-bar-icons"></tce-clock>
</tce-desktop>
Effects are slots too. A window has a decoration slot — a 48px strip resting on its top
border, full window width, with pointer events passing through — and the desktop has an
effects slot, an overlay above every window and the taskbar that also lets pointer events
through (an effect opts its own parts back in):
<tce-window data-title="Notepad">
<tce-flames slot="decoration"></tce-flames>
<tce-notepad slot="display"></tce-notepad>
</tce-window>
<tce-desktop data-window-decoration="tce-flames">
<tce-klabautermann slot="effects"></tce-klabautermann>
</tce-desktop>
The desktop's data-window-decoration names packages it composes into every window's
decoration slot itself, exactly as the first snippet does by hand; data-effects names
packages for its own effects slot and data-tray for task-bar-icons. All three are live,
comma-separated lists, and the same three slots are offered as settings and in the properties
dialog (tce-inspector, "Desktop Properties…" on the desktop menu), so a user switches each
package on or off at runtime and keeps the choice across reloads — together with the attributes
each composed element is created with. An effect learns where windows and icons are through
the layout query (Interfacing), never by
looking.
Every composition point of an element is a named slot — display and decoration on
tce-window, task-bar-icons and effects on tce-desktop — documented in the element's class
comment.
Elements take attributes as input. Rules:
init- (init-file-path, init-count), so other elements know not to rely on live updates
to it. It is read in Logic.start() behind a once-flag, never in the constructor.observedAttributes and reacts in
attributeChangedCallback.data- prefix, matching the
native dataset API (data-title, data-mode, data-docs-base).read_attribute(element, name, schema, fallback) (Coding Style).An element declares the attributes it accepts in its entry script's bind_self() meta, together
with the desktop slots it is made for, so a desktop or a properties dialog can offer them
without loading the element (Package Model):
TceRoot.bind_self(package_identifier, tce_flames_package, {
kind: 'element',
description: '…',
slots: ['decoration'],
attributes: [
{ name: 'data-intensity', label: 'Intensity', type: 'number', default: '1', min: 0, max: 1, step: 0.1 },
{ name: 'data-color', label: 'Colour', type: 'select', default: 'fire', options: ['fire', 'gas', 'ember', 'ghost'] },
],
});
name is the attribute as written in HTML; type is one of text, number, color,
boolean (values true/false), select (with options); default is what the element
does without the attribute. Values are always strings. The element still reads every attribute
through a schema — the declaration is for the dialog, the schema is the truth. Declare the list
once (a *_attributes constant next to the element's Settings) and pass it to bind_self.
The desktop keeps the attributes chosen for an icon ("Properties…" on the icon) and applies
them when the icon is activated, exactly as a shortcut's attributes would be; the attributes
chosen for a composed package are set on its element whenever the desktop creates it.
An element must not mutate its own attributes to change how its parent treats it. A child that needs its owner to resize, retitle, maximize or close it asks through a bubbling, composed request event, and the owner decides whether to honor it:
import { request_window_action } from '@tce-v3/desktop-protocol';
request_window_action(this.host, { action: 'resize', width: 640, height: 480 });
The full set of request events is in Interfacing. The child never gets an acknowledgment and never assumes the request succeeded.
Reflecting an element's own state into a data- attribute (a window marking itself
data-minimized so it can be styled and inspected) is fine; the owner may read it, but it is
never the channel through which the child asks for a change.
Convenience methods (the desktop's add_icon, the desktop opening a window for a
tce.desktop.open-request, the desktop slotting the decoration and effects its attributes or
settings name) build the same slotted markup a page could have written by hand.
An element exposes every composition point as a named slot, even if only one caller uses it;
no element offers an imperative-only way to compose something expressible as a slot.
slot.init- and read them once, behind a flag.init- attribute and react live.data-* for anything that is not a real DOM/ARIA attribute.