@aglyn/aglyn 1.0.0-beta.258 → 1.0.0-beta.259

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Aglyn LLC
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ import { type AnimationEase, type AnimationPreset } from './element-animation';
18
+ /**
19
+ * Entrance motion for a whole page (AGL-3838): the pass that writes the
20
+ * existing animation props (`element-animation.ts`) onto a page built by
21
+ * code — a starter, a new site's home, a page the AI composed — so it moves
22
+ * the way an author would have set it up by hand in the attributes panel.
23
+ *
24
+ * Nothing new is rendered or shipped: the pass only writes the reserved
25
+ * `aglynAnimation*` props the renderer and the tenant's stylesheet already
26
+ * read, with values from their own vocabulary. A page that moves pays the
27
+ * stylesheet and the scroll observer it always paid; a page this pass leaves
28
+ * alone pays nothing.
29
+ *
30
+ * ## The rules
31
+ *
32
+ * - The FIRST section's secondary content — its eyebrow, its lede, its
33
+ * buttons, a small picture — plays on `load`, as a short ladder: each
34
+ * block starts {@link ElementMotionCharacter.heroStepMs} after the one
35
+ * before. Its main heading and its largest picture, the candidates for the
36
+ * page's Largest Contentful Paint, never move: they are painted where
37
+ * they stand from the first frame. Its band — a background color or a
38
+ * cover photo — never moves either.
39
+ * - Every later section's content slides in on `scroll`.
40
+ * - A group of like items — a grid of cards, a list, the questions of an
41
+ * FAQ, a dataset repeated over one card — STAGGERS its items, one after
42
+ * another, rather than arriving as one block.
43
+ * - A picture, and a group whose items each lead with one, takes the
44
+ * character's media preset.
45
+ * - Never touched: forms and their fields, navigation and the site's frame,
46
+ * anything out of the flow (`position: fixed | sticky | absolute` — a
47
+ * cover photo's backdrop is its band's), anything hidden, and
48
+ * anything that already carries an animation prop — an author's choice,
49
+ * `none` included, is never overwritten. A block that HOLDS one of those
50
+ * is not animated as a whole either; its other children are animated one
51
+ * by one instead, so a form never slides in with its section.
52
+ * - Never nested: once a block animates, nothing inside it does. Two
53
+ * entrances on the same pixels multiply their fades and add their
54
+ * offsets, which reads as a stutter, not as motion.
55
+ *
56
+ * Pure and idempotent: a page it already moved comes back as the same
57
+ * object, which is what lets a caller compare by identity to skip a write.
58
+ *
59
+ * Leaf imports only, the same as `starter-template-nodes.ts`: the starter
60
+ * kit reads this from the console's client gallery and its seed route alike.
61
+ */
62
+ /** A page's motion: the presets, curve and timing every entrance on it shares. */
63
+ export interface ElementMotionCharacter {
64
+ /** Words and blocks: a section's content, a group of cards. */
65
+ preset: AnimationPreset;
66
+ /** A picture, or a group whose every item leads with one. */
67
+ mediaPreset: AnimationPreset;
68
+ ease: AnimationEase;
69
+ durationMs: number;
70
+ /** Gap between the items of a staggered group. */
71
+ staggerStepMs: number;
72
+ /** Gap between the rungs of the first section's load ladder. */
73
+ heroStepMs: number;
74
+ }
75
+ /** The motion a page gets when nothing says otherwise: a soft rise, as the attributes panel defaults to. */
76
+ export declare const ELEMENT_MOTION_DEFAULT: ElementMotionCharacter;
77
+ /** The most rungs the first section's ladder climbs; later blocks share the last. */
78
+ export declare const ELEMENT_MOTION_HERO_RUNGS = 6;
79
+ /**
80
+ * Elements that are never animated, nor animated as part of a block that
81
+ * holds them: forms and every field of one, and the site's navigation and
82
+ * frame. A field that slides in under a visitor's cursor is a field they
83
+ * miss, and a header that moves is a site that looks broken.
84
+ */
85
+ export declare const ELEMENT_MOTION_NEVER: ReadonlySet<string>;
86
+ export interface ElementMotionOptions {
87
+ /** The page's root; the canvas root when absent. */
88
+ rootId?: string;
89
+ /** The page's sections in order; the root's own children when absent. */
90
+ sectionIds?: readonly string[];
91
+ character?: ElementMotionCharacter;
92
+ }
93
+ /**
94
+ * Writes entrance motion onto a page's nodes by the rules above, and returns
95
+ * the new node map — or `nodes` itself, unchanged, when there was nothing to
96
+ * move.
97
+ */
98
+ export declare function applyElementMotion<T extends Record<string, any>>(nodes: T, options?: ElementMotionOptions): T;
@@ -0,0 +1,341 @@
1
+ import { _ as _extends } from "@swc/helpers/_/_extends";
2
+ /**
3
+ * @license
4
+ * Copyright 2026 Aglyn LLC
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */ import { CANVAS_ROOT_ELEMENT_ID } from "../foundation/constants/canvas.js";
18
+ import { ANIMATION_MAX_DELAY_MS, ANIMATION_MAX_DURATION_MS, ANIMATION_MAX_STAGGER_STEP_MS, NODE_ANIMATION_DELAY_PROP, NODE_ANIMATION_DURATION_PROP, NODE_ANIMATION_EASE_PROP, NODE_ANIMATION_PROPS, NODE_ANIMATION_PROP, NODE_ANIMATION_STAGGER_PROP, NODE_ANIMATION_STAGGER_STEP_PROP, NODE_ANIMATION_TRIGGER_PROP } from "./element-animation.js";
19
+ /** The motion a page gets when nothing says otherwise: a soft rise, as the attributes panel defaults to. */ export const ELEMENT_MOTION_DEFAULT = {
20
+ preset: 'slide-up',
21
+ mediaPreset: 'fade',
22
+ ease: 'smooth',
23
+ durationMs: 600,
24
+ staggerStepMs: 90,
25
+ heroStepMs: 120
26
+ };
27
+ /** The most rungs the first section's ladder climbs; later blocks share the last. */ export const ELEMENT_MOTION_HERO_RUNGS = 6;
28
+ /**
29
+ * Elements that are never animated, nor animated as part of a block that
30
+ * holds them: forms and every field of one, and the site's navigation and
31
+ * frame. A field that slides in under a visitor's cursor is a field they
32
+ * miss, and a header that moves is a site that looks broken.
33
+ */ export const ELEMENT_MOTION_NEVER = new Set([
34
+ // Forms and their fields.
35
+ 'form',
36
+ 'formField',
37
+ 'muiFormControl',
38
+ 'muiFormLabel',
39
+ 'muiFormControlLabel',
40
+ 'muiFormHelperText',
41
+ 'muiInput',
42
+ 'muiSelect',
43
+ 'muiSelectOption',
44
+ 'muiRadioGroup',
45
+ 'muiRadio',
46
+ 'muiCheckbox',
47
+ 'muiRating',
48
+ 'muiDatePicker',
49
+ 'muiTimePicker',
50
+ 'newsletter-signup',
51
+ 'member-signin',
52
+ 'member-signup',
53
+ 'member-recovery',
54
+ 'booking',
55
+ 'reservation-widget',
56
+ 'customer-account',
57
+ 'return-request',
58
+ 'order-status',
59
+ 'searchBox',
60
+ 'collectionSearch',
61
+ // Navigation and the site's frame.
62
+ 'layoutSlot',
63
+ 'muiAppBar',
64
+ 'muiToolbar',
65
+ 'muiDrawer',
66
+ 'muiDrawerToggle',
67
+ 'muiNavMenu',
68
+ 'muiMegaMenu',
69
+ 'muiBreadcrumbs',
70
+ 'muiTabs',
71
+ 'languageSwitcher',
72
+ 'themeModeSwitcher',
73
+ 'cart'
74
+ ]);
75
+ /** Pictures: what the media preset is for. */ const MEDIA = new Set([
76
+ 'image',
77
+ 'video',
78
+ 'videoEmbed',
79
+ 'gated-video'
80
+ ]);
81
+ /** Components that list like items whatever their children are. */ const LISTS = new Set([
82
+ 'muiList',
83
+ 'muiImageList',
84
+ 'collectionEntries'
85
+ ]);
86
+ /**
87
+ * What a group's items are made of when they are alike: cards, cells,
88
+ * panels, pictures. Words and buttons never count, however alike — a row of
89
+ * two buttons is a call to action, not a group of cards.
90
+ */ const ITEMS = new Set([
91
+ 'muiCard',
92
+ 'muiPaper',
93
+ 'muiGrid',
94
+ 'muiStack',
95
+ 'muiBox',
96
+ 'muiLinkBox',
97
+ 'muiListItem',
98
+ 'muiImageListItem',
99
+ 'muiAccordion',
100
+ 'reusableInstance',
101
+ 'div',
102
+ 'image',
103
+ 'video'
104
+ ]);
105
+ const clamp = (value, max)=>Math.min(Math.max(Math.round(value), 0), max);
106
+ /**
107
+ * Whether a style value — plain or one per breakpoint — takes the element out
108
+ * of the flow: pinned to the screen, or laid over its band, as a cover
109
+ * photo's backdrop and its scrim are. Either is part of the band, not of what
110
+ * the band holds.
111
+ */ function pins(value) {
112
+ if (value === 'fixed' || value === 'sticky' || value === 'absolute') return true;
113
+ return typeof value === 'object' && value !== null && Object.values(value).some(pins);
114
+ }
115
+ /**
116
+ * Writes entrance motion onto a page's nodes by the rules above, and returns
117
+ * the new node map — or `nodes` itself, unchanged, when there was nothing to
118
+ * move.
119
+ */ export function applyElementMotion(nodes, options = {}) {
120
+ var _options_character, _options_rootId, _options_sectionIds;
121
+ if (!nodes) return nodes;
122
+ const character = (_options_character = options.character) != null ? _options_character : ELEMENT_MOTION_DEFAULT;
123
+ const rootId = (_options_rootId = options.rootId) != null ? _options_rootId : CANVAS_ROOT_ELEMENT_ID;
124
+ const node = (id)=>nodes[id];
125
+ const children = (id)=>{
126
+ var _ref;
127
+ var _node;
128
+ return ((_ref = (_node = node(id)) == null ? void 0 : _node.nodes) != null ? _ref : []).filter((child)=>typeof child === 'string' && !!node(child));
129
+ };
130
+ const patches = new Map();
131
+ /** The first section's Largest Contentful Paint candidates, painted where they stand. */ const still = new Set();
132
+ /** Left alone, and so is everything inside it. */ const closed = (id)=>{
133
+ var _at_componentId;
134
+ var _at_sx;
135
+ const at = node(id);
136
+ if (!at) return true;
137
+ if (at.hidden || still.has(id)) return true;
138
+ if (ELEMENT_MOTION_NEVER.has(String((_at_componentId = at.componentId) != null ? _at_componentId : ''))) return true;
139
+ if (at.props && NODE_ANIMATION_PROPS.some((prop)=>{
140
+ var _at_props;
141
+ return ((_at_props = at.props) == null ? void 0 : _at_props[prop]) !== undefined;
142
+ })) return true;
143
+ return pins((_at_sx = at.sx) == null ? void 0 : _at_sx['position']);
144
+ };
145
+ const holdsClosed = new Map();
146
+ /** The element, or one inside it, is left alone: it cannot move as one block. */ const holds = (id)=>{
147
+ const known = holdsClosed.get(id);
148
+ if (known !== undefined) return known;
149
+ holdsClosed.set(id, false);
150
+ const result = closed(id) || children(id).some(holds);
151
+ holdsClosed.set(id, result);
152
+ return result;
153
+ };
154
+ const groupMemo = new Map();
155
+ const belowMemo = new Map();
156
+ /**
157
+ * A group of like items, whose items stagger. A grid or a row of panels
158
+ * that holds a group of its own is the LAYOUT around it — a heading beside
159
+ * its cards — so it is opened up and the group inside staggers instead.
160
+ */ const isGroup = (id)=>{
161
+ var _at_componentId;
162
+ var _at_props;
163
+ const known = groupMemo.get(id);
164
+ if (known !== undefined) return known;
165
+ const at = node(id);
166
+ const componentId = String((_at_componentId = at.componentId) != null ? _at_componentId : '');
167
+ const kids = children(id);
168
+ let result;
169
+ // A dataset repeated over one card renders one child per record.
170
+ if ((((_at_props = at.props) == null ? void 0 : _at_props['repeatDataset']) || LISTS.has(componentId)) && kids.length) result = true;
171
+ else if (kids.length < 2) result = false;
172
+ else {
173
+ var _ref;
174
+ var _node, _at_props1;
175
+ const first = String((_ref = (_node = node(kids[0])) == null ? void 0 : _node.componentId) != null ? _ref : '');
176
+ const alike = componentId === 'muiGrid' && !!((_at_props1 = at.props) == null ? void 0 : _at_props1['container']) || ITEMS.has(first) && kids.every((child)=>{
177
+ var _node;
178
+ return ((_node = node(child)) == null ? void 0 : _node.componentId) === first;
179
+ });
180
+ result = alike && !holdsGroup(id);
181
+ }
182
+ groupMemo.set(id, result);
183
+ return result;
184
+ };
185
+ /** A group sits somewhere inside the element. */ const holdsGroup = (id)=>{
186
+ const known = belowMemo.get(id);
187
+ if (known !== undefined) return known;
188
+ const result = children(id).some((child)=>isGroup(child) || holdsGroup(child));
189
+ belowMemo.set(id, result);
190
+ return result;
191
+ };
192
+ /** Leads with a picture: its first leaf, down its first children, is one. */ const leadsWithMedia = (id)=>{
193
+ let at = id;
194
+ for(let depth = 0; depth < 12; depth += 1){
195
+ var _ref;
196
+ var _node;
197
+ if (MEDIA.has(String((_ref = (_node = node(at)) == null ? void 0 : _node.componentId) != null ? _ref : ''))) return true;
198
+ const kids = children(at);
199
+ if (!kids.length) return false;
200
+ at = kids[0];
201
+ }
202
+ return false;
203
+ };
204
+ const presetOf = (id, group)=>{
205
+ const media = group ? children(id).every(leadsWithMedia) : leadsWithMedia(id);
206
+ return media ? character.mediaPreset : character.preset;
207
+ };
208
+ const animate = (id, trigger, delayMs)=>{
209
+ const group = isGroup(id);
210
+ patches.set(id, _extends({
211
+ [NODE_ANIMATION_PROP]: presetOf(id, group),
212
+ [NODE_ANIMATION_TRIGGER_PROP]: trigger,
213
+ [NODE_ANIMATION_DURATION_PROP]: clamp(character.durationMs, ANIMATION_MAX_DURATION_MS)
214
+ }, delayMs > 0 ? {
215
+ [NODE_ANIMATION_DELAY_PROP]: clamp(delayMs, ANIMATION_MAX_DELAY_MS)
216
+ } : {}, {
217
+ [NODE_ANIMATION_EASE_PROP]: character.ease
218
+ }, group ? {
219
+ [NODE_ANIMATION_STAGGER_PROP]: true,
220
+ [NODE_ANIMATION_STAGGER_STEP_PROP]: clamp(character.staggerStepMs, ANIMATION_MAX_STAGGER_STEP_MS)
221
+ } : {}));
222
+ };
223
+ /** Down through wrappers of one child, to where a section's content starts. */ const contentOf = (id)=>{
224
+ let at = id;
225
+ while(!closed(at) && !isGroup(at)){
226
+ const kids = children(at);
227
+ if (kids.length !== 1) break;
228
+ at = kids[0];
229
+ }
230
+ return at;
231
+ };
232
+ /** A later section's content: on scroll, groups staggered, nothing nested. */ const place = (id)=>{
233
+ if (closed(id)) return;
234
+ if (holds(id) || !isGroup(id) && holdsGroup(id)) {
235
+ for (const child of children(id))place(child);
236
+ return;
237
+ }
238
+ animate(id, 'scroll', 0);
239
+ };
240
+ /** Down through wrappers of one child that are free to move. */ const descend = (id)=>{
241
+ let at = id;
242
+ for(let kids = children(at); kids.length === 1 && !closed(kids[0]); kids = children(at))at = kids[0];
243
+ return at;
244
+ };
245
+ /**
246
+ * The first section's content: on load, one rung per block, around the
247
+ * blocks that stay still. A rung that holds one is opened up, down to the
248
+ * parts beside it, so the eyebrow, the lede and the buttons still climb.
249
+ */ const ladder = (id)=>{
250
+ if (closed(id)) return;
251
+ // A repeat's one child is the card every record is drawn with: the
252
+ // repeat staggers its records rather than each record playing at once.
253
+ if (isGroup(id) && children(id).length < 2) return animate(id, 'load', 0);
254
+ const around = (at)=>children(at).flatMap((child)=>closed(child) ? [] : holds(child) ? around(child) : [
255
+ child
256
+ ]);
257
+ const base = children(id).length ? around(id) : [
258
+ id
259
+ ];
260
+ // A column of words beside a picture is opened up, so its lines climb one
261
+ // after another before the picture lands.
262
+ const rungs = [];
263
+ base.forEach((rung, index)=>{
264
+ const inner = descend(rung);
265
+ const kids = children(inner);
266
+ const opens = kids.length >= 2 && !isGroup(inner) && !leadsWithMedia(inner) && rungs.length + kids.length + (base.length - index - 1) <= ELEMENT_MOTION_HERO_RUNGS;
267
+ rungs.push(...opens ? kids : [
268
+ rung
269
+ ]);
270
+ });
271
+ // A rung left alone takes no step of the ladder: the first block that moves starts at once.
272
+ rungs.filter((rung)=>!closed(rung)).forEach((rung, index)=>animate(rung, 'load', Math.min(index, ELEMENT_MOTION_HERO_RUNGS - 1) * character.heroStepMs));
273
+ };
274
+ /**
275
+ * What the first section paints first, and so what Lighthouse times as its
276
+ * Largest Contentful Paint (AGL-3581): its main heading — the h1, else its
277
+ * first heading — and its largest picture. Neither ever starts at opacity 0,
278
+ * which would hold the paint back by the length of the entrance.
279
+ */ const markStill = (sectionId)=>{
280
+ var _order_find;
281
+ const order = [];
282
+ const walk = (id)=>{
283
+ order.push(id);
284
+ for (const child of children(id))walk(child);
285
+ };
286
+ walk(sectionId);
287
+ const typeOf = (id)=>{
288
+ var _ref;
289
+ var _at_props;
290
+ const at = node(id);
291
+ return (at == null ? void 0 : at.componentId) === 'muiTypography' ? String((_ref = (_at_props = at.props) == null ? void 0 : _at_props['variant']) != null ? _ref : '') : null;
292
+ };
293
+ const main = (id)=>{
294
+ var _typeOf;
295
+ var _node_props, _node;
296
+ return /^(h1|display)/i.test((_typeOf = typeOf(id)) != null ? _typeOf : '') || typeOf(id) !== null && ((_node = node(id)) == null ? void 0 : (_node_props = _node.props) == null ? void 0 : _node_props['component']) === 'h1';
297
+ };
298
+ const heading = (_order_find = order.find(main)) != null ? _order_find : order.find((id)=>{
299
+ var _typeOf;
300
+ return /^(h[1-6]|display)/i.test((_typeOf = typeOf(id)) != null ? _typeOf : '');
301
+ });
302
+ if (heading) still.add(heading);
303
+ const size = (id)=>{
304
+ var _at_props, _at_props1, _at_sx, _at_sx1;
305
+ const at = node(id);
306
+ const read = (value)=>typeof value === 'number' ? value : parseFloat(String(value != null ? value : '')) || 0;
307
+ return Math.max(read(at == null ? void 0 : (_at_props = at.props) == null ? void 0 : _at_props['height']), read(at == null ? void 0 : (_at_props1 = at.props) == null ? void 0 : _at_props1['width']), read(at == null ? void 0 : (_at_sx = at.sx) == null ? void 0 : _at_sx['height']), read(at == null ? void 0 : (_at_sx1 = at.sx) == null ? void 0 : _at_sx1['width']));
308
+ };
309
+ let picture;
310
+ for (const id of order){
311
+ var _ref;
312
+ var _node;
313
+ if (MEDIA.has(String((_ref = (_node = node(id)) == null ? void 0 : _node.componentId) != null ? _ref : '')) && (picture === undefined || size(id) > size(picture))) picture = id;
314
+ }
315
+ if (picture) still.add(picture);
316
+ };
317
+ const sections = ((_options_sectionIds = options.sectionIds) != null ? _options_sectionIds : children(rootId)).filter((id)=>!!node(id));
318
+ if (sections[0]) markStill(sections[0]);
319
+ sections.forEach((id, index)=>{
320
+ if (closed(id)) return;
321
+ const content = contentOf(id);
322
+ // The band itself never moves — only what it holds — so a section that
323
+ // is nothing but its band has nothing to move.
324
+ if (content === id && !children(id).length) return;
325
+ if (index === 0) ladder(content);
326
+ else if (content === id && !isGroup(id)) for (const child of children(id))place(child);
327
+ else place(content);
328
+ });
329
+ if (!patches.size) return nodes;
330
+ const next = _extends({}, nodes);
331
+ for (const [id, props] of patches){
332
+ var _ref;
333
+ var _nodes_id;
334
+ next[id] = _extends({}, nodes[id], {
335
+ props: _extends({}, (_ref = (_nodes_id = nodes[id]) == null ? void 0 : _nodes_id.props) != null ? _ref : {}, props)
336
+ });
337
+ }
338
+ return next;
339
+ }
340
+
341
+ //# sourceMappingURL=element-motion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/element-motion.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { CANVAS_ROOT_ELEMENT_ID } from '../foundation/constants/canvas'\nimport {\n ANIMATION_MAX_DELAY_MS,\n ANIMATION_MAX_DURATION_MS,\n ANIMATION_MAX_STAGGER_STEP_MS,\n NODE_ANIMATION_DELAY_PROP,\n NODE_ANIMATION_DURATION_PROP,\n NODE_ANIMATION_EASE_PROP,\n NODE_ANIMATION_PROPS,\n NODE_ANIMATION_PROP,\n NODE_ANIMATION_STAGGER_PROP,\n NODE_ANIMATION_STAGGER_STEP_PROP,\n NODE_ANIMATION_TRIGGER_PROP,\n type AnimationEase,\n type AnimationPreset,\n type AnimationTrigger,\n} from './element-animation'\n\n/**\n * Entrance motion for a whole page (AGL-3838): the pass that writes the\n * existing animation props (`element-animation.ts`) onto a page built by\n * code — a starter, a new site's home, a page the AI composed — so it moves\n * the way an author would have set it up by hand in the attributes panel.\n *\n * Nothing new is rendered or shipped: the pass only writes the reserved\n * `aglynAnimation*` props the renderer and the tenant's stylesheet already\n * read, with values from their own vocabulary. A page that moves pays the\n * stylesheet and the scroll observer it always paid; a page this pass leaves\n * alone pays nothing.\n *\n * ## The rules\n *\n * - The FIRST section's secondary content — its eyebrow, its lede, its\n * buttons, a small picture — plays on `load`, as a short ladder: each\n * block starts {@link ElementMotionCharacter.heroStepMs} after the one\n * before. Its main heading and its largest picture, the candidates for the\n * page's Largest Contentful Paint, never move: they are painted where\n * they stand from the first frame. Its band — a background color or a\n * cover photo — never moves either.\n * - Every later section's content slides in on `scroll`.\n * - A group of like items — a grid of cards, a list, the questions of an\n * FAQ, a dataset repeated over one card — STAGGERS its items, one after\n * another, rather than arriving as one block.\n * - A picture, and a group whose items each lead with one, takes the\n * character's media preset.\n * - Never touched: forms and their fields, navigation and the site's frame,\n * anything out of the flow (`position: fixed | sticky | absolute` — a\n * cover photo's backdrop is its band's), anything hidden, and\n * anything that already carries an animation prop — an author's choice,\n * `none` included, is never overwritten. A block that HOLDS one of those\n * is not animated as a whole either; its other children are animated one\n * by one instead, so a form never slides in with its section.\n * - Never nested: once a block animates, nothing inside it does. Two\n * entrances on the same pixels multiply their fades and add their\n * offsets, which reads as a stutter, not as motion.\n *\n * Pure and idempotent: a page it already moved comes back as the same\n * object, which is what lets a caller compare by identity to skip a write.\n *\n * Leaf imports only, the same as `starter-template-nodes.ts`: the starter\n * kit reads this from the console's client gallery and its seed route alike.\n */\n\n/** A page's motion: the presets, curve and timing every entrance on it shares. */\nexport interface ElementMotionCharacter {\n /** Words and blocks: a section's content, a group of cards. */\n preset: AnimationPreset\n /** A picture, or a group whose every item leads with one. */\n mediaPreset: AnimationPreset\n ease: AnimationEase\n durationMs: number\n /** Gap between the items of a staggered group. */\n staggerStepMs: number\n /** Gap between the rungs of the first section's load ladder. */\n heroStepMs: number\n}\n\n/** The motion a page gets when nothing says otherwise: a soft rise, as the attributes panel defaults to. */\nexport const ELEMENT_MOTION_DEFAULT: ElementMotionCharacter = {\n preset: 'slide-up',\n mediaPreset: 'fade',\n ease: 'smooth',\n durationMs: 600,\n staggerStepMs: 90,\n heroStepMs: 120,\n}\n\n/** The most rungs the first section's ladder climbs; later blocks share the last. */\nexport const ELEMENT_MOTION_HERO_RUNGS = 6\n\n/**\n * Elements that are never animated, nor animated as part of a block that\n * holds them: forms and every field of one, and the site's navigation and\n * frame. A field that slides in under a visitor's cursor is a field they\n * miss, and a header that moves is a site that looks broken.\n */\nexport const ELEMENT_MOTION_NEVER: ReadonlySet<string> = new Set([\n // Forms and their fields.\n 'form',\n 'formField',\n 'muiFormControl',\n 'muiFormLabel',\n 'muiFormControlLabel',\n 'muiFormHelperText',\n 'muiInput',\n 'muiSelect',\n 'muiSelectOption',\n 'muiRadioGroup',\n 'muiRadio',\n 'muiCheckbox',\n 'muiRating',\n 'muiDatePicker',\n 'muiTimePicker',\n 'newsletter-signup',\n 'member-signin',\n 'member-signup',\n 'member-recovery',\n 'booking',\n 'reservation-widget',\n 'customer-account',\n 'return-request',\n 'order-status',\n 'searchBox',\n 'collectionSearch',\n // Navigation and the site's frame.\n 'layoutSlot',\n 'muiAppBar',\n 'muiToolbar',\n 'muiDrawer',\n 'muiDrawerToggle',\n 'muiNavMenu',\n 'muiMegaMenu',\n 'muiBreadcrumbs',\n 'muiTabs',\n 'languageSwitcher',\n 'themeModeSwitcher',\n 'cart',\n])\n\n/** Pictures: what the media preset is for. */\nconst MEDIA: ReadonlySet<string> = new Set(['image', 'video', 'videoEmbed', 'gated-video'])\n\n/** Components that list like items whatever their children are. */\nconst LISTS: ReadonlySet<string> = new Set(['muiList', 'muiImageList', 'collectionEntries'])\n\n/**\n * What a group's items are made of when they are alike: cards, cells,\n * panels, pictures. Words and buttons never count, however alike — a row of\n * two buttons is a call to action, not a group of cards.\n */\nconst ITEMS: ReadonlySet<string> = new Set([\n 'muiCard',\n 'muiPaper',\n 'muiGrid',\n 'muiStack',\n 'muiBox',\n 'muiLinkBox',\n 'muiListItem',\n 'muiImageListItem',\n 'muiAccordion',\n 'reusableInstance',\n 'div',\n 'image',\n 'video',\n])\n\ninterface MotionNode {\n componentId?: string\n props?: Record<string, unknown>\n sx?: Record<string, unknown>\n nodes?: string[]\n hidden?: boolean\n}\n\nexport interface ElementMotionOptions {\n /** The page's root; the canvas root when absent. */\n rootId?: string\n /** The page's sections in order; the root's own children when absent. */\n sectionIds?: readonly string[]\n character?: ElementMotionCharacter\n}\n\nconst clamp = (value: number, max: number): number => Math.min(Math.max(Math.round(value), 0), max)\n\n/**\n * Whether a style value — plain or one per breakpoint — takes the element out\n * of the flow: pinned to the screen, or laid over its band, as a cover\n * photo's backdrop and its scrim are. Either is part of the band, not of what\n * the band holds.\n */\nfunction pins(value: unknown): boolean {\n if (value === 'fixed' || value === 'sticky' || value === 'absolute') return true\n return typeof value === 'object' && value !== null && Object.values(value).some(pins)\n}\n\n/**\n * Writes entrance motion onto a page's nodes by the rules above, and returns\n * the new node map — or `nodes` itself, unchanged, when there was nothing to\n * move.\n */\nexport function applyElementMotion<T extends Record<string, any>>(nodes: T, options: ElementMotionOptions = {}): T {\n if (!nodes) return nodes\n const character = options.character ?? ELEMENT_MOTION_DEFAULT\n const rootId = options.rootId ?? CANVAS_ROOT_ELEMENT_ID\n const node = (id: string): MotionNode | undefined => nodes[id] as MotionNode | undefined\n const children = (id: string): string[] => (node(id)?.nodes ?? []).filter((child) => typeof child === 'string' && !!node(child))\n const patches = new Map<string, Record<string, unknown>>()\n\n /** The first section's Largest Contentful Paint candidates, painted where they stand. */\n const still = new Set<string>()\n /** Left alone, and so is everything inside it. */\n const closed = (id: string): boolean => {\n const at = node(id)\n if (!at) return true\n if (at.hidden || still.has(id)) return true\n if (ELEMENT_MOTION_NEVER.has(String(at.componentId ?? ''))) return true\n if (at.props && NODE_ANIMATION_PROPS.some((prop) => at.props?.[prop] !== undefined)) return true\n return pins(at.sx?.['position'])\n }\n const holdsClosed = new Map<string, boolean>()\n /** The element, or one inside it, is left alone: it cannot move as one block. */\n const holds = (id: string): boolean => {\n const known = holdsClosed.get(id)\n if (known !== undefined) return known\n holdsClosed.set(id, false)\n const result = closed(id) || children(id).some(holds)\n holdsClosed.set(id, result)\n return result\n }\n const groupMemo = new Map<string, boolean>()\n const belowMemo = new Map<string, boolean>()\n /**\n * A group of like items, whose items stagger. A grid or a row of panels\n * that holds a group of its own is the LAYOUT around it — a heading beside\n * its cards — so it is opened up and the group inside staggers instead.\n */\n const isGroup = (id: string): boolean => {\n const known = groupMemo.get(id)\n if (known !== undefined) return known\n const at = node(id) as MotionNode\n const componentId = String(at.componentId ?? '')\n const kids = children(id)\n let result: boolean\n // A dataset repeated over one card renders one child per record.\n if ((at.props?.['repeatDataset'] || LISTS.has(componentId)) && kids.length) result = true\n else if (kids.length < 2) result = false\n else {\n const first = String(node(kids[0])?.componentId ?? '')\n const alike =\n (componentId === 'muiGrid' && !!at.props?.['container']) ||\n (ITEMS.has(first) && kids.every((child) => node(child)?.componentId === first))\n result = alike && !holdsGroup(id)\n }\n groupMemo.set(id, result)\n return result\n }\n /** A group sits somewhere inside the element. */\n const holdsGroup = (id: string): boolean => {\n const known = belowMemo.get(id)\n if (known !== undefined) return known\n const result = children(id).some((child) => isGroup(child) || holdsGroup(child))\n belowMemo.set(id, result)\n return result\n }\n /** Leads with a picture: its first leaf, down its first children, is one. */\n const leadsWithMedia = (id: string): boolean => {\n let at = id\n for (let depth = 0; depth < 12; depth += 1) {\n if (MEDIA.has(String(node(at)?.componentId ?? ''))) return true\n const kids = children(at)\n if (!kids.length) return false\n at = kids[0]\n }\n return false\n }\n const presetOf = (id: string, group: boolean): AnimationPreset => {\n const media = group ? children(id).every(leadsWithMedia) : leadsWithMedia(id)\n return media ? character.mediaPreset : character.preset\n }\n\n const animate = (id: string, trigger: AnimationTrigger, delayMs: number): void => {\n const group = isGroup(id)\n patches.set(id, {\n [NODE_ANIMATION_PROP]: presetOf(id, group),\n [NODE_ANIMATION_TRIGGER_PROP]: trigger,\n [NODE_ANIMATION_DURATION_PROP]: clamp(character.durationMs, ANIMATION_MAX_DURATION_MS),\n ...(delayMs > 0 ? { [NODE_ANIMATION_DELAY_PROP]: clamp(delayMs, ANIMATION_MAX_DELAY_MS) } : {}),\n [NODE_ANIMATION_EASE_PROP]: character.ease,\n ...(group\n ? {\n [NODE_ANIMATION_STAGGER_PROP]: true,\n [NODE_ANIMATION_STAGGER_STEP_PROP]: clamp(character.staggerStepMs, ANIMATION_MAX_STAGGER_STEP_MS),\n }\n : {}),\n })\n }\n\n /** Down through wrappers of one child, to where a section's content starts. */\n const contentOf = (id: string): string => {\n let at = id\n while (!closed(at) && !isGroup(at)) {\n const kids = children(at)\n if (kids.length !== 1) break\n at = kids[0]\n }\n return at\n }\n\n /** A later section's content: on scroll, groups staggered, nothing nested. */\n const place = (id: string): void => {\n if (closed(id)) return\n if (holds(id) || (!isGroup(id) && holdsGroup(id))) {\n for (const child of children(id)) place(child)\n return\n }\n animate(id, 'scroll', 0)\n }\n\n /** Down through wrappers of one child that are free to move. */\n const descend = (id: string): string => {\n let at = id\n for (let kids = children(at); kids.length === 1 && !closed(kids[0]); kids = children(at)) at = kids[0]\n return at\n }\n\n /**\n * The first section's content: on load, one rung per block, around the\n * blocks that stay still. A rung that holds one is opened up, down to the\n * parts beside it, so the eyebrow, the lede and the buttons still climb.\n */\n const ladder = (id: string): void => {\n if (closed(id)) return\n // A repeat's one child is the card every record is drawn with: the\n // repeat staggers its records rather than each record playing at once.\n if (isGroup(id) && children(id).length < 2) return animate(id, 'load', 0)\n const around = (at: string): string[] =>\n children(at).flatMap((child) => (closed(child) ? [] : holds(child) ? around(child) : [child]))\n const base = children(id).length ? around(id) : [id]\n // A column of words beside a picture is opened up, so its lines climb one\n // after another before the picture lands.\n const rungs: string[] = []\n base.forEach((rung, index) => {\n const inner = descend(rung)\n const kids = children(inner)\n const opens =\n kids.length >= 2 &&\n !isGroup(inner) &&\n !leadsWithMedia(inner) &&\n rungs.length + kids.length + (base.length - index - 1) <= ELEMENT_MOTION_HERO_RUNGS\n rungs.push(...(opens ? kids : [rung]))\n })\n // A rung left alone takes no step of the ladder: the first block that moves starts at once.\n rungs\n .filter((rung) => !closed(rung))\n .forEach((rung, index) => animate(rung, 'load', Math.min(index, ELEMENT_MOTION_HERO_RUNGS - 1) * character.heroStepMs))\n }\n\n /**\n * What the first section paints first, and so what Lighthouse times as its\n * Largest Contentful Paint (AGL-3581): its main heading — the h1, else its\n * first heading — and its largest picture. Neither ever starts at opacity 0,\n * which would hold the paint back by the length of the entrance.\n */\n const markStill = (sectionId: string): void => {\n const order: string[] = []\n const walk = (id: string): void => {\n order.push(id)\n for (const child of children(id)) walk(child)\n }\n walk(sectionId)\n const typeOf = (id: string) => {\n const at = node(id)\n return at?.componentId === 'muiTypography' ? String(at.props?.['variant'] ?? '') : null\n }\n const main = (id: string) => /^(h1|display)/i.test(typeOf(id) ?? '') || (typeOf(id) !== null && node(id)?.props?.['component'] === 'h1')\n const heading = order.find(main) ?? order.find((id) => /^(h[1-6]|display)/i.test(typeOf(id) ?? ''))\n if (heading) still.add(heading)\n const size = (id: string): number => {\n const at = node(id)\n const read = (value: unknown) => (typeof value === 'number' ? value : parseFloat(String(value ?? '')) || 0)\n return Math.max(read(at?.props?.['height']), read(at?.props?.['width']), read(at?.sx?.['height']), read(at?.sx?.['width']))\n }\n let picture: string | undefined\n for (const id of order) {\n if (MEDIA.has(String(node(id)?.componentId ?? '')) && (picture === undefined || size(id) > size(picture))) picture = id\n }\n if (picture) still.add(picture)\n }\n\n const sections = (options.sectionIds ?? children(rootId)).filter((id) => !!node(id))\n if (sections[0]) markStill(sections[0])\n sections.forEach((id, index) => {\n if (closed(id)) return\n const content = contentOf(id)\n // The band itself never moves — only what it holds — so a section that\n // is nothing but its band has nothing to move.\n if (content === id && !children(id).length) return\n if (index === 0) ladder(content)\n else if (content === id && !isGroup(id)) for (const child of children(id)) place(child)\n else place(content)\n })\n\n if (!patches.size) return nodes\n const next: Record<string, any> = { ...nodes }\n for (const [id, props] of patches) {\n next[id] = { ...nodes[id], props: { ...(nodes[id]?.props ?? {}), ...props } }\n }\n return next as T\n}\n"],"names":["CANVAS_ROOT_ELEMENT_ID","ANIMATION_MAX_DELAY_MS","ANIMATION_MAX_DURATION_MS","ANIMATION_MAX_STAGGER_STEP_MS","NODE_ANIMATION_DELAY_PROP","NODE_ANIMATION_DURATION_PROP","NODE_ANIMATION_EASE_PROP","NODE_ANIMATION_PROPS","NODE_ANIMATION_PROP","NODE_ANIMATION_STAGGER_PROP","NODE_ANIMATION_STAGGER_STEP_PROP","NODE_ANIMATION_TRIGGER_PROP","ELEMENT_MOTION_DEFAULT","preset","mediaPreset","ease","durationMs","staggerStepMs","heroStepMs","ELEMENT_MOTION_HERO_RUNGS","ELEMENT_MOTION_NEVER","Set","MEDIA","LISTS","ITEMS","clamp","value","max","Math","min","round","pins","Object","values","some","applyElementMotion","nodes","options","character","rootId","node","id","children","filter","child","patches","Map","still","closed","at","hidden","has","String","componentId","props","prop","undefined","sx","holdsClosed","holds","known","get","set","result","groupMemo","belowMemo","isGroup","kids","length","first","alike","every","holdsGroup","leadsWithMedia","depth","presetOf","group","media","animate","trigger","delayMs","contentOf","place","descend","ladder","around","flatMap","base","rungs","forEach","rung","index","inner","opens","push","markStill","sectionId","order","walk","typeOf","main","test","heading","find","add","size","read","parseFloat","picture","sections","sectionIds","content","next"],"mappings":"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"}
@@ -20,10 +20,12 @@ import { _ as _extends } from "@swc/helpers/_/_extends";
20
20
  // shows them to a model — and neither barrel works in both: the full
21
21
  // `@aglyn/aglyn` barrel breaks the RSC/server-route graph, while the server
22
22
  // entry drags `node:fs` into the client bundle. The starter kit imports only
23
- // the canvas constants, a leaf with no imports of their own; the compiled
23
+ // the canvas constants, a leaf with no imports of their own, and the
24
+ // entrance-motion pass, which adds only the animation vocabulary; the compiled
24
25
  // plugin starters are data behind a type-only import; and `screen-route`
25
26
  // imports only a TYPE from foundation — so all three are safe on either side.
26
27
  import { PLUGIN_STARTER_TEMPLATES } from "./plugin-starter-templates.generated.js";
28
+ import { applyElementMotion } from "./element-motion.js";
27
29
  import { normalizeScreenSlug } from "./screen-route.js";
28
30
  import { buildStarterNodes as buildNodes, starterHeroSection as heroSection, starterSection as section, starterText as text, starterTextField } from "./starter-template-nodes.js";
29
31
  export { buildStarterNodes, starterHeroSection, starterSection, starterText, starterTextField } from "./starter-template-nodes.js";
@@ -67,7 +69,11 @@ export { buildStarterNodes, starterHeroSection, starterSection, starterText, sta
67
69
  } : {}, screen.seo ? {
68
70
  seo: screen.seo
69
71
  } : {}, {
70
- nodes: screen.nodes,
72
+ // A starter's page moves the way the attributes panel would set it
73
+ // up (AGL-3838). Written here, as it is seeded, and not into the
74
+ // starter itself: the starters are also the page examples a model is
75
+ // shown, and those carry no motion — the AI's is decided in code.
76
+ nodes: applyElementMotion(screen.nodes),
71
77
  source: {
72
78
  type: 'starter',
73
79
  starterId: starter.id,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/starter-templates.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Leaf imports only, deliberately (AGL-687). This module is imported from\n// BOTH graphs — the console's client gallery renders virtual starters from\n// it, the console's seed route materializes them, and the AI plugin's server\n// shows them to a model — and neither barrel works in both: the full\n// `@aglyn/aglyn` barrel breaks the RSC/server-route graph, while the server\n// entry drags `node:fs` into the client bundle. The starter kit imports only\n// the canvas constants, a leaf with no imports of their own; the compiled\n// plugin starters are data behind a type-only import; and `screen-route`\n// imports only a TYPE from foundation — so all three are safe on either side.\nimport { PLUGIN_STARTER_TEMPLATES } from './plugin-starter-templates.generated'\nimport { normalizeScreenSlug } from './screen-route'\nimport {\n buildStarterNodes as buildNodes,\n starterHeroSection as heroSection,\n starterSection as section,\n starterText as text,\n starterTextField,\n type StarterNodeSpec as NodeSpec,\n type StarterTemplate,\n} from './starter-template-nodes'\n\nexport type {\n StarterNodeSpec,\n StarterSectionWidth,\n StarterTemplate,\n StarterTemplateScreen,\n} from './starter-template-nodes'\nexport {\n buildStarterNodes,\n starterHeroSection,\n starterSection,\n starterText,\n starterTextField,\n} from './starter-template-nodes'\n\n/**\n * First-party starter definitions (AGL-78/79), SEED INPUT (AGL-687).\n *\n * These used to be rendered straight into the gallery and instantiated from\n * code, which made a starter a second kind of template: no version history,\n * no placeholders, no editor, changeable only by shipping a release. Now\n * they are seeded into each host's own template library as ordinary\n * `hosts/{hostId}/templates` documents (see the console's\n * `utils/server/seed-starter-templates.ts` for why copy-on-use rather than a\n * global read-only collection), and nothing reads this file at render time.\n *\n * They are also the page examples the AI plugin's template generator shows a\n * model (AGL-2909), which is why they live in the core package rather than\n * beside the console gallery that renders them.\n *\n * Node ids stay template-local (unique within each screen's version doc);\n * `createPageFromTemplate` re-keys screen and version ids at instantiation.\n *\n * PERSISTED IDENTIFIERS: `StarterTemplate.id`, `StarterTemplateScreen.key`\n * and every node id below appear in stored documents (seeded template doc\n * ids are derived from the first two). They must never be renamed.\n */\nexport interface StarterTemplateDoc {\n id: string\n /** Which starter this page belongs to — lets one starter be selected. */\n starterId: string\n data: Record<string, unknown>\n}\n\n/**\n * Deterministic document id for a seeded starter screen (AGL-687).\n *\n * Derived from the starter id and the screen key — never randomly generated\n * — so re-running the seed addresses the same documents instead of stacking\n * duplicate copies of every starter on every run.\n */\nexport function starterTemplateDocId(\n starterId: string,\n screenKey: string,\n): string {\n return `starter-${starterId}-${screenKey}`\n}\n\n/**\n * Expands one starter into the template documents it seeds as.\n *\n * One page template per screen, matching what a marketplace install of a\n * multi-screen site template already produces (AGL-669) — the alternative,\n * a bespoke multi-page template shape, would be a third lifecycle on top of\n * the two this issue exists to merge. The bundle's name/description/order\n * ride `source` so the gallery can still present the starter as one card.\n */\nexport function buildStarterTemplateDocs(\n starter: StarterTemplate,\n): StarterTemplateDoc[] {\n return starter.screens.map((screen, index) => {\n // Normalized on the way into the document so the persisted address is in\n // the routing-map format the tenant matches against — in particular the\n // root, which the previous `screen.slug ? …` guard dropped entirely when\n // home was spelled `''` (AGL-1575). A slug that sanitizes away carries no\n // field, and the apply path derives one from the display name.\n const slug = normalizeScreenSlug(screen.slug)\n return {\n id: starterTemplateDocId(starter.id, screen.key),\n starterId: starter.id,\n data: {\n kind: 'page',\n displayName: screen.displayName,\n // Only the screen's own description, never the starter blurb: a\n // template's `description` is carried onto the page it creates, and a\n // screen description is the live site's meta-description fallback.\n // The bundle blurb lives on `source.starterDescription` instead.\n ...(screen.description ? { description: screen.description } : {}),\n category: starter.category,\n ...(slug ? { slug } : {}),\n ...(screen.seo ? { seo: screen.seo } : {}),\n nodes: screen.nodes,\n source: {\n type: 'starter',\n starterId: starter.id,\n starterName: starter.displayName,\n starterDescription: starter.description,\n starterOrder: index,\n },\n },\n }\n })\n}\n\n/** Every document a starter COULD materialize as, in bundle order. */\nexport function buildAllStarterTemplateDocs(): StarterTemplateDoc[] {\n return STARTER_TEMPLATES.flatMap(buildStarterTemplateDocs)\n}\n\nconst featureColumn = (id: string, title: string, body: string): NodeSpec => ({\n id,\n componentId: 'muiStack',\n props: { spacing: 1 },\n // `flex: 1` is a column's share of its row, not a width cap — it survives\n // the sweep deliberately. Only `maxWidth` in raw pixels was the violation.\n sx: {\n flex: 1,\n paddingTop: 2,\n paddingRight: 2,\n paddingBottom: 2,\n paddingLeft: 2,\n },\n children: [text(`${id}T`, 'h5', title), text(`${id}B`, 'body1', body)],\n})\n\nconst contactForm = (prefix: string): NodeSpec => ({\n id: `${prefix}form`,\n componentId: 'form',\n props: {\n formName: 'Contact',\n submitLabel: 'Send message',\n successMessage: 'Thanks — we will get back to you soon.',\n },\n children: [\n starterTextField(`${prefix}fName`, { name: 'name', label: 'Name', required: true }),\n starterTextField(`${prefix}fEmail`, {\n name: 'email',\n label: 'Email',\n type: 'email',\n required: true,\n }),\n starterTextField(`${prefix}fMessage`, {\n name: 'message',\n label: 'Message',\n multiline: true,\n required: true,\n }),\n ],\n})\n\n\n/**\n * The home page every new site is born with now lives with the rest of the\n * site it is born into — layout, theme and SEO — in `default-site.ts`\n * (AGL-3497). Re-exported so the name keeps its home here.\n */\nexport { buildDefaultHomeScreen } from './default-site'\n\n/** The platform's own starters, which name no plugin's elements. */\nconst PLATFORM_STARTER_TEMPLATES: StarterTemplate[] = [\n {\n id: 'landing',\n displayName: 'Landing Page',\n description:\n 'One-page launch site: hero, three selling points, and a contact form.',\n category: 'Marketing',\n screens: [\n {\n key: 'landing',\n displayName: 'Landing',\n slug: 'landing',\n seo: {\n title: 'Welcome',\n description: 'Everything you need to know, on one page.',\n },\n nodes: buildNodes([\n heroSection(\n 'l_',\n 'A headline that sells your idea',\n 'One clear sentence about the value you deliver.',\n ),\n section('l_featuresSection', 'xl', 6, [\n {\n id: 'l_features',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn(\n 'l_f1',\n 'Fast',\n 'Explain the first reason customers pick you.',\n ),\n featureColumn(\n 'l_f2',\n 'Simple',\n 'Explain the second reason customers pick you.',\n ),\n featureColumn(\n 'l_f3',\n 'Reliable',\n 'Explain the third reason customers pick you.',\n ),\n ],\n },\n ]),\n // Was `maxWidth: 560`. A band, not an inner measure: it sat at the\n // top level carrying the section's own gutters and rhythm, so the\n // pixel value was capping the BAND. MD is the stock width for a\n // narrow, form-led column.\n section('l_contactSection', 'md', 6, [\n {\n id: 'l_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('l_contactTitle', 'h4', 'Get in touch'),\n contactForm('l_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'business',\n displayName: 'Business',\n description:\n 'Company site: home with services, an about page, and a contact page.',\n category: 'Business',\n screens: [\n {\n key: 'home',\n displayName: 'Business Home',\n slug: 'home',\n seo: { title: 'Home' },\n nodes: buildNodes([\n heroSection(\n 'b_',\n 'Your business, done right',\n 'Tell visitors what you do and who you do it for.',\n ),\n section('b_servicesSection', 'xl', 6, [\n {\n id: 'b_services',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn('b_s1', 'Service one', 'Describe this service.'),\n featureColumn('b_s2', 'Service two', 'Describe this service.'),\n featureColumn(\n 'b_s3',\n 'Service three',\n 'Describe this service.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'about-us',\n displayName: 'About Us',\n slug: 'about-us',\n seo: { title: 'About us' },\n nodes: buildNodes([\n // Was `maxWidth: 720` — the one unambiguous PROSE band in the set,\n // and the case the Prose Container preset exists for. MD (900px)\n // lands near the 65–75 characters a line that reads comfortably.\n section('a_wrapSection', 'md', 8, [\n {\n id: 'a_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('a_title', 'h3', 'About us'),\n text(\n 'a_body',\n 'body1',\n 'Share your story: how you started, what you believe, and ' +\n 'why customers trust you.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'contact-us',\n displayName: 'Contact Us',\n slug: 'contact-us',\n seo: { title: 'Contact' },\n nodes: buildNodes([\n // Was `maxWidth: 560`. Same reading as the landing page's contact\n // band: text plus a form column, so MD.\n section('c_wrapSection', 'md', 8, [\n {\n id: 'c_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('c_title', 'h3', 'Contact us'),\n text(\n 'c_body',\n 'body1',\n 'Questions or quotes — send a message and we reply within ' +\n 'one business day.',\n ),\n contactForm('c_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'portfolio',\n displayName: 'Portfolio',\n description: 'Personal portfolio: intro, work grid, and contact form.',\n category: 'Personal',\n screens: [\n {\n key: 'portfolio',\n displayName: 'Portfolio',\n slug: 'portfolio',\n seo: { title: 'Portfolio' },\n nodes: buildNodes([\n heroSection(\n 'p_',\n 'Hi, I make things',\n 'Designer / developer / photographer — introduce yourself here.',\n ),\n section('p_gridSection', 'xl', 6, [\n {\n id: 'p_grid',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n {\n id: 'p_img1',\n componentId: 'image',\n // `height` is an image's own intrinsic sizing prop, not a\n // band cap — out of scope for the container standard.\n props: { alt: 'Project one', height: '220px' },\n },\n {\n id: 'p_img2',\n componentId: 'image',\n props: { alt: 'Project two', height: '220px' },\n },\n {\n id: 'p_img3',\n componentId: 'image',\n props: { alt: 'Project three', height: '220px' },\n },\n ],\n },\n ]),\n // Was `maxWidth: 560`, the third of the three contact bands.\n section('p_contactSection', 'md', 6, [\n {\n id: 'p_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('p_contactTitle', 'h4', 'Work with me'),\n contactForm('p_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n]\n\n/**\n * Every starter in this build, in gallery order: the platform's own, then\n * each plugin's in config order (AGL-3080). A plugin whose elements a starter\n * is built around — a storefront's product grid and cart — declares it under\n * `starterTemplates` in plugins.config.json, and the manifest generator\n * compiles what it returns, so the gallery, the seed route and a model\n * shown examples all read one list without loading any plugin.\n */\nexport const STARTER_TEMPLATES: StarterTemplate[] = [\n ...PLATFORM_STARTER_TEMPLATES,\n ...PLUGIN_STARTER_TEMPLATES,\n]\n"],"names":["PLUGIN_STARTER_TEMPLATES","normalizeScreenSlug","buildStarterNodes","buildNodes","starterHeroSection","heroSection","starterSection","section","starterText","text","starterTextField","starterTemplateDocId","starterId","screenKey","buildStarterTemplateDocs","starter","screens","map","screen","index","slug","id","key","data","kind","displayName","description","category","seo","nodes","source","type","starterName","starterDescription","starterOrder","buildAllStarterTemplateDocs","STARTER_TEMPLATES","flatMap","featureColumn","title","body","componentId","props","spacing","sx","flex","paddingTop","paddingRight","paddingBottom","paddingLeft","children","contactForm","prefix","formName","submitLabel","successMessage","name","label","required","multiline","buildDefaultHomeScreen","PLATFORM_STARTER_TEMPLATES","direction","alt","height"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../libs/aglyn/src/lib/app-utils/starter-templates.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n// Leaf imports only, deliberately (AGL-687). This module is imported from\n// BOTH graphs — the console's client gallery renders virtual starters from\n// it, the console's seed route materializes them, and the AI plugin's server\n// shows them to a model — and neither barrel works in both: the full\n// `@aglyn/aglyn` barrel breaks the RSC/server-route graph, while the server\n// entry drags `node:fs` into the client bundle. The starter kit imports only\n// the canvas constants, a leaf with no imports of their own, and the\n// entrance-motion pass, which adds only the animation vocabulary; the compiled\n// plugin starters are data behind a type-only import; and `screen-route`\n// imports only a TYPE from foundation — so all three are safe on either side.\nimport { PLUGIN_STARTER_TEMPLATES } from './plugin-starter-templates.generated'\nimport { applyElementMotion } from './element-motion'\nimport { normalizeScreenSlug } from './screen-route'\nimport {\n buildStarterNodes as buildNodes,\n starterHeroSection as heroSection,\n starterSection as section,\n starterText as text,\n starterTextField,\n type StarterNodeSpec as NodeSpec,\n type StarterTemplate,\n} from './starter-template-nodes'\n\nexport type {\n StarterNodeSpec,\n StarterSectionWidth,\n StarterTemplate,\n StarterTemplateScreen,\n} from './starter-template-nodes'\nexport {\n buildStarterNodes,\n starterHeroSection,\n starterSection,\n starterText,\n starterTextField,\n} from './starter-template-nodes'\n\n/**\n * First-party starter definitions (AGL-78/79), SEED INPUT (AGL-687).\n *\n * These used to be rendered straight into the gallery and instantiated from\n * code, which made a starter a second kind of template: no version history,\n * no placeholders, no editor, changeable only by shipping a release. Now\n * they are seeded into each host's own template library as ordinary\n * `hosts/{hostId}/templates` documents (see the console's\n * `utils/server/seed-starter-templates.ts` for why copy-on-use rather than a\n * global read-only collection), and nothing reads this file at render time.\n *\n * They are also the page examples the AI plugin's template generator shows a\n * model (AGL-2909), which is why they live in the core package rather than\n * beside the console gallery that renders them.\n *\n * Node ids stay template-local (unique within each screen's version doc);\n * `createPageFromTemplate` re-keys screen and version ids at instantiation.\n *\n * PERSISTED IDENTIFIERS: `StarterTemplate.id`, `StarterTemplateScreen.key`\n * and every node id below appear in stored documents (seeded template doc\n * ids are derived from the first two). They must never be renamed.\n */\nexport interface StarterTemplateDoc {\n id: string\n /** Which starter this page belongs to — lets one starter be selected. */\n starterId: string\n data: Record<string, unknown>\n}\n\n/**\n * Deterministic document id for a seeded starter screen (AGL-687).\n *\n * Derived from the starter id and the screen key — never randomly generated\n * — so re-running the seed addresses the same documents instead of stacking\n * duplicate copies of every starter on every run.\n */\nexport function starterTemplateDocId(\n starterId: string,\n screenKey: string,\n): string {\n return `starter-${starterId}-${screenKey}`\n}\n\n/**\n * Expands one starter into the template documents it seeds as.\n *\n * One page template per screen, matching what a marketplace install of a\n * multi-screen site template already produces (AGL-669) — the alternative,\n * a bespoke multi-page template shape, would be a third lifecycle on top of\n * the two this issue exists to merge. The bundle's name/description/order\n * ride `source` so the gallery can still present the starter as one card.\n */\nexport function buildStarterTemplateDocs(\n starter: StarterTemplate,\n): StarterTemplateDoc[] {\n return starter.screens.map((screen, index) => {\n // Normalized on the way into the document so the persisted address is in\n // the routing-map format the tenant matches against — in particular the\n // root, which the previous `screen.slug ? …` guard dropped entirely when\n // home was spelled `''` (AGL-1575). A slug that sanitizes away carries no\n // field, and the apply path derives one from the display name.\n const slug = normalizeScreenSlug(screen.slug)\n return {\n id: starterTemplateDocId(starter.id, screen.key),\n starterId: starter.id,\n data: {\n kind: 'page',\n displayName: screen.displayName,\n // Only the screen's own description, never the starter blurb: a\n // template's `description` is carried onto the page it creates, and a\n // screen description is the live site's meta-description fallback.\n // The bundle blurb lives on `source.starterDescription` instead.\n ...(screen.description ? { description: screen.description } : {}),\n category: starter.category,\n ...(slug ? { slug } : {}),\n ...(screen.seo ? { seo: screen.seo } : {}),\n // A starter's page moves the way the attributes panel would set it\n // up (AGL-3838). Written here, as it is seeded, and not into the\n // starter itself: the starters are also the page examples a model is\n // shown, and those carry no motion — the AI's is decided in code.\n nodes: applyElementMotion(screen.nodes),\n source: {\n type: 'starter',\n starterId: starter.id,\n starterName: starter.displayName,\n starterDescription: starter.description,\n starterOrder: index,\n },\n },\n }\n })\n}\n\n/** Every document a starter COULD materialize as, in bundle order. */\nexport function buildAllStarterTemplateDocs(): StarterTemplateDoc[] {\n return STARTER_TEMPLATES.flatMap(buildStarterTemplateDocs)\n}\n\nconst featureColumn = (id: string, title: string, body: string): NodeSpec => ({\n id,\n componentId: 'muiStack',\n props: { spacing: 1 },\n // `flex: 1` is a column's share of its row, not a width cap — it survives\n // the sweep deliberately. Only `maxWidth` in raw pixels was the violation.\n sx: {\n flex: 1,\n paddingTop: 2,\n paddingRight: 2,\n paddingBottom: 2,\n paddingLeft: 2,\n },\n children: [text(`${id}T`, 'h5', title), text(`${id}B`, 'body1', body)],\n})\n\nconst contactForm = (prefix: string): NodeSpec => ({\n id: `${prefix}form`,\n componentId: 'form',\n props: {\n formName: 'Contact',\n submitLabel: 'Send message',\n successMessage: 'Thanks — we will get back to you soon.',\n },\n children: [\n starterTextField(`${prefix}fName`, { name: 'name', label: 'Name', required: true }),\n starterTextField(`${prefix}fEmail`, {\n name: 'email',\n label: 'Email',\n type: 'email',\n required: true,\n }),\n starterTextField(`${prefix}fMessage`, {\n name: 'message',\n label: 'Message',\n multiline: true,\n required: true,\n }),\n ],\n})\n\n\n/**\n * The home page every new site is born with now lives with the rest of the\n * site it is born into — layout, theme and SEO — in `default-site.ts`\n * (AGL-3497). Re-exported so the name keeps its home here.\n */\nexport { buildDefaultHomeScreen } from './default-site'\n\n/** The platform's own starters, which name no plugin's elements. */\nconst PLATFORM_STARTER_TEMPLATES: StarterTemplate[] = [\n {\n id: 'landing',\n displayName: 'Landing Page',\n description:\n 'One-page launch site: hero, three selling points, and a contact form.',\n category: 'Marketing',\n screens: [\n {\n key: 'landing',\n displayName: 'Landing',\n slug: 'landing',\n seo: {\n title: 'Welcome',\n description: 'Everything you need to know, on one page.',\n },\n nodes: buildNodes([\n heroSection(\n 'l_',\n 'A headline that sells your idea',\n 'One clear sentence about the value you deliver.',\n ),\n section('l_featuresSection', 'xl', 6, [\n {\n id: 'l_features',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn(\n 'l_f1',\n 'Fast',\n 'Explain the first reason customers pick you.',\n ),\n featureColumn(\n 'l_f2',\n 'Simple',\n 'Explain the second reason customers pick you.',\n ),\n featureColumn(\n 'l_f3',\n 'Reliable',\n 'Explain the third reason customers pick you.',\n ),\n ],\n },\n ]),\n // Was `maxWidth: 560`. A band, not an inner measure: it sat at the\n // top level carrying the section's own gutters and rhythm, so the\n // pixel value was capping the BAND. MD is the stock width for a\n // narrow, form-led column.\n section('l_contactSection', 'md', 6, [\n {\n id: 'l_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('l_contactTitle', 'h4', 'Get in touch'),\n contactForm('l_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'business',\n displayName: 'Business',\n description:\n 'Company site: home with services, an about page, and a contact page.',\n category: 'Business',\n screens: [\n {\n key: 'home',\n displayName: 'Business Home',\n slug: 'home',\n seo: { title: 'Home' },\n nodes: buildNodes([\n heroSection(\n 'b_',\n 'Your business, done right',\n 'Tell visitors what you do and who you do it for.',\n ),\n section('b_servicesSection', 'xl', 6, [\n {\n id: 'b_services',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n featureColumn('b_s1', 'Service one', 'Describe this service.'),\n featureColumn('b_s2', 'Service two', 'Describe this service.'),\n featureColumn(\n 'b_s3',\n 'Service three',\n 'Describe this service.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'about-us',\n displayName: 'About Us',\n slug: 'about-us',\n seo: { title: 'About us' },\n nodes: buildNodes([\n // Was `maxWidth: 720` — the one unambiguous PROSE band in the set,\n // and the case the Prose Container preset exists for. MD (900px)\n // lands near the 65–75 characters a line that reads comfortably.\n section('a_wrapSection', 'md', 8, [\n {\n id: 'a_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('a_title', 'h3', 'About us'),\n text(\n 'a_body',\n 'body1',\n 'Share your story: how you started, what you believe, and ' +\n 'why customers trust you.',\n ),\n ],\n },\n ]),\n ]),\n },\n {\n key: 'contact-us',\n displayName: 'Contact Us',\n slug: 'contact-us',\n seo: { title: 'Contact' },\n nodes: buildNodes([\n // Was `maxWidth: 560`. Same reading as the landing page's contact\n // band: text plus a form column, so MD.\n section('c_wrapSection', 'md', 8, [\n {\n id: 'c_wrap',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('c_title', 'h3', 'Contact us'),\n text(\n 'c_body',\n 'body1',\n 'Questions or quotes — send a message and we reply within ' +\n 'one business day.',\n ),\n contactForm('c_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n {\n id: 'portfolio',\n displayName: 'Portfolio',\n description: 'Personal portfolio: intro, work grid, and contact form.',\n category: 'Personal',\n screens: [\n {\n key: 'portfolio',\n displayName: 'Portfolio',\n slug: 'portfolio',\n seo: { title: 'Portfolio' },\n nodes: buildNodes([\n heroSection(\n 'p_',\n 'Hi, I make things',\n 'Designer / developer / photographer — introduce yourself here.',\n ),\n section('p_gridSection', 'xl', 6, [\n {\n id: 'p_grid',\n componentId: 'muiStack',\n props: { direction: 'row', spacing: 2 },\n children: [\n {\n id: 'p_img1',\n componentId: 'image',\n // `height` is an image's own intrinsic sizing prop, not a\n // band cap — out of scope for the container standard.\n props: { alt: 'Project one', height: '220px' },\n },\n {\n id: 'p_img2',\n componentId: 'image',\n props: { alt: 'Project two', height: '220px' },\n },\n {\n id: 'p_img3',\n componentId: 'image',\n props: { alt: 'Project three', height: '220px' },\n },\n ],\n },\n ]),\n // Was `maxWidth: 560`, the third of the three contact bands.\n section('p_contactSection', 'md', 6, [\n {\n id: 'p_contact',\n componentId: 'muiStack',\n props: { spacing: 2 },\n children: [\n text('p_contactTitle', 'h4', 'Work with me'),\n contactForm('p_'),\n ],\n },\n ]),\n ]),\n },\n ],\n },\n]\n\n/**\n * Every starter in this build, in gallery order: the platform's own, then\n * each plugin's in config order (AGL-3080). A plugin whose elements a starter\n * is built around — a storefront's product grid and cart — declares it under\n * `starterTemplates` in plugins.config.json, and the manifest generator\n * compiles what it returns, so the gallery, the seed route and a model\n * shown examples all read one list without loading any plugin.\n */\nexport const STARTER_TEMPLATES: StarterTemplate[] = [\n ...PLATFORM_STARTER_TEMPLATES,\n ...PLUGIN_STARTER_TEMPLATES,\n]\n"],"names":["PLUGIN_STARTER_TEMPLATES","applyElementMotion","normalizeScreenSlug","buildStarterNodes","buildNodes","starterHeroSection","heroSection","starterSection","section","starterText","text","starterTextField","starterTemplateDocId","starterId","screenKey","buildStarterTemplateDocs","starter","screens","map","screen","index","slug","id","key","data","kind","displayName","description","category","seo","nodes","source","type","starterName","starterDescription","starterOrder","buildAllStarterTemplateDocs","STARTER_TEMPLATES","flatMap","featureColumn","title","body","componentId","props","spacing","sx","flex","paddingTop","paddingRight","paddingBottom","paddingLeft","children","contactForm","prefix","formName","submitLabel","successMessage","name","label","required","multiline","buildDefaultHomeScreen","PLATFORM_STARTER_TEMPLATES","direction","alt","height"],"mappings":"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"}