@12ui/design 0.2.36 → 0.2.38
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.
- package/SKILL.md +1 -1
- package/dist/branch-client.d.ts +12 -0
- package/dist/branch-client.d.ts.map +1 -1
- package/dist/branch-client.js.map +1 -1
- package/dist/branch-completion.d.ts.map +1 -1
- package/dist/branch-completion.js +1 -0
- package/dist/branch-completion.js.map +1 -1
- package/dist/branch-execution-types.d.ts +3 -0
- package/dist/branch-execution-types.d.ts.map +1 -1
- package/dist/branch-execution.d.ts.map +1 -1
- package/dist/branch-execution.js +13 -0
- package/dist/branch-execution.js.map +1 -1
- package/dist/branch-identity.d.ts +1 -1
- package/dist/branch-identity.js +1 -1
- package/dist/branch-plan.d.ts +3 -1
- package/dist/branch-plan.d.ts.map +1 -1
- package/dist/branch-plan.js +2 -0
- package/dist/branch-plan.js.map +1 -1
- package/dist/branch-run-record.d.ts +18 -0
- package/dist/branch-run-record.d.ts.map +1 -1
- package/dist/branch-run-record.js.map +1 -1
- package/dist/branch-winner.d.ts.map +1 -1
- package/dist/branch-winner.js +7 -0
- package/dist/branch-winner.js.map +1 -1
- package/dist/cli-branch-command.d.ts.map +1 -1
- package/dist/cli-branch-command.js +61 -7
- package/dist/cli-branch-command.js.map +1 -1
- package/dist/cli-help/design.d.ts +1 -1
- package/dist/cli-help/design.d.ts.map +1 -1
- package/dist/cli-help/design.js +1 -0
- package/dist/cli-help/design.js.map +1 -1
- package/dist/cli-help/index.d.ts +1 -1
- package/dist/cli-prototype-command.d.ts +3 -2
- package/dist/cli-prototype-command.d.ts.map +1 -1
- package/dist/cli-prototype-command.js +9 -3
- package/dist/cli-prototype-command.js.map +1 -1
- package/dist/cli-review-command.js +2 -2
- package/dist/cli-review-command.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/prototype-branch-contract.d.ts +67 -0
- package/dist/prototype-branch-contract.d.ts.map +1 -0
- package/dist/prototype-branch-contract.js +403 -0
- package/dist/prototype-branch-contract.js.map +1 -0
- package/dist/prototype-poststep.d.ts +27 -0
- package/dist/prototype-poststep.d.ts.map +1 -0
- package/dist/prototype-poststep.js +97 -0
- package/dist/prototype-poststep.js.map +1 -0
- package/dist/prototype-run-record.d.ts +92 -0
- package/dist/prototype-run-record.d.ts.map +1 -0
- package/dist/prototype-run-record.js +236 -0
- package/dist/prototype-run-record.js.map +1 -0
- package/dist/prototype-runner.d.ts +13 -1
- package/dist/prototype-runner.d.ts.map +1 -1
- package/dist/prototype-runner.js +79 -20
- package/dist/prototype-runner.js.map +1 -1
- package/open-design.json +2 -2
- package/package.json +1 -1
- package/prototype-kit/analyze.mjs +48 -6
- package/prototype-kit/build.mjs +249 -65
- package/prototype-kit/inventory.mjs +290 -2
- package/prototype-kit/lib/holding-composition.mjs +67 -0
- package/prototype-kit/lib/nav-active-state.mjs +67 -0
- package/prototype-kit/lib/review-framing.mjs +72 -0
- package/prototype-kit/lib/runtime-check.mjs +361 -28
- package/prototype-kit/lib/shell-consistency.mjs +68 -0
- package/prototype-kit/lib/shell-stitching.mjs +221 -0
- package/prototype-kit/runtime/proto.css +8 -3
- package/prototype-kit/tests/README.md +4 -1
- package/prototype-kit/tests/adversarial-runtime.mjs +85 -2
- package/prototype-kit/tests/fixtures/holding-runtime/PROVENANCE.md +22 -0
- package/prototype-kit/tests/fixtures/holding-runtime/band-ownership-fire.html +10 -0
- package/prototype-kit/tests/fixtures/holding-runtime/band-ownership-silent.html +10 -0
- package/prototype-kit/tests/fixtures/holding-runtime/declared-width-fire.html +11 -0
- package/prototype-kit/tests/fixtures/holding-runtime/declared-width-silent.html +11 -0
- package/prototype-kit/tests/fixtures/holding-runtime/ground-registration-mismatch-fire.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/ground-registration-mismatch-silent.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/holding-contrast-fire.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/holding-contrast-silent.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/positioned-ground-fire.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/positioned-ground-silent.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/shell-geometry-fire.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/shell-geometry-silent.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/shell-intersection-fire.html +5 -0
- package/prototype-kit/tests/fixtures/holding-runtime/shell-intersection-silent.html +5 -0
- package/prototype-kit/tests/fixtures/runtime/history.html +1 -1
- package/prototype-kit/tests/fixtures/shell-nav-runtime/PROVENANCE.md +6 -0
- package/prototype-kit/tests/fixtures/shell-nav-runtime/active-fire.html +3 -0
- package/prototype-kit/tests/fixtures/shell-nav-runtime/proto-manifest.json +20 -0
- package/prototype-kit/tests/fixtures/shell-nav-runtime/shell-fire.html +3 -0
- package/prototype-kit/tests/fixtures/shell-nav-runtime/shell-silent.html +3 -0
- package/prototype-kit/tests/fixtures/shell-nav-runtime/unlinked-silent.html +3 -0
- package/prototype-kit/tests/fixtures/three-shell/pages/p0-home.html +3 -3
- package/prototype-kit/tests/fixtures/three-shell/pages/p1-reports.html +3 -3
- package/prototype-kit/tests/lib/run-process.mjs +8 -2
- package/prototype-kit/tests/offline-check.mjs +143 -0
- package/prototype-kit/tests/population-sweep.mjs +26 -7
- package/prototype-kit/tests/rules-check.mjs +80 -0
- package/prototype-kit/verify-runtime.mjs +5 -1
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { declaredActiveMarkers, linkedNavForUnit } from './nav-active-state.mjs';
|
|
2
|
+
import { needsCanonicalShell, shellForBand } from './shell-consistency.mjs';
|
|
3
|
+
import { shouldTransplantShell } from './shells.mjs';
|
|
4
|
+
import { normalizeText } from './text.mjs';
|
|
5
|
+
|
|
6
|
+
function bandRoot(document, key) {
|
|
7
|
+
const roots = [...document.querySelectorAll('.ly-band[data-ly-band]')];
|
|
8
|
+
if (roots.length === 0) return key === 'native' ? document.body : null;
|
|
9
|
+
return roots.find((root) => root.getAttribute('data-ly-band') === key) || null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function shellHost(root) {
|
|
13
|
+
const screen = root?.querySelector('.generated-screen') || (root?.tagName === 'BODY' ? root : null);
|
|
14
|
+
return screen?.querySelector('.ly-anchor-canvas') || screen || null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function navItems(shell, navRows, region) {
|
|
18
|
+
const names = new Set(navRows.filter((row) => row.region === region).map((row) => normalizeText(row.text)));
|
|
19
|
+
return (shell?.items || []).filter((item) => item.text && names.has(normalizeText(item.text)));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function markerSnapshots({ document, shell, markers, rulesFor, styleText }) {
|
|
23
|
+
const css = styleText(document);
|
|
24
|
+
return markers.flatMap((marker) => {
|
|
25
|
+
const element = document.getElementById(marker.id);
|
|
26
|
+
if (!element) return [];
|
|
27
|
+
return [{
|
|
28
|
+
...marker,
|
|
29
|
+
html: element.outerHTML,
|
|
30
|
+
css: rulesFor(css, marker.id),
|
|
31
|
+
item: shell.items.find((item) => item.id === marker.id),
|
|
32
|
+
targetItem: shell.items.find((item) => item.id === marker.targetId),
|
|
33
|
+
}];
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function removeMarkers(document, markers) {
|
|
38
|
+
for (const marker of markers) document.getElementById(marker.id)?.remove();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function placeMarker({ shellElement, shell, marker, targetItem, extraCss }) {
|
|
42
|
+
shellElement.insertAdjacentHTML('beforeend', marker.html);
|
|
43
|
+
if (marker.css) extraCss.push(marker.css);
|
|
44
|
+
const sourceTarget = marker.targetItem;
|
|
45
|
+
const source = marker.item;
|
|
46
|
+
if (!source) return;
|
|
47
|
+
const desiredX = targetItem && sourceTarget ? targetItem.x + (source.x - sourceTarget.x) : source.x;
|
|
48
|
+
const desiredY = targetItem && sourceTarget ? targetItem.y + (source.y - sourceTarget.y) : source.y;
|
|
49
|
+
extraCss.push(`#${marker.id}{position:absolute!important;left:${desiredX - shell.box[0]}px!important;top:${desiredY - shell.box[1]}px!important;`
|
|
50
|
+
+ `width:${source.w}px!important;height:${source.h}px!important;margin:0!important;translate:none!important}`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function decisionForRegion({ unit, donorPage, recipientPage, region, canonicalShell }) {
|
|
54
|
+
if (recipientPage.pageId === donorPage.pageId) return { commit: false, reason: 'canonical-page' };
|
|
55
|
+
const semantic = unit.states.flatMap((state) => state.shell).find((shell) => shell.region === region);
|
|
56
|
+
if (!semantic) return { commit: false, reason: 'absent-analyzed-region' };
|
|
57
|
+
if (!shouldTransplantShell(semantic.box, canonicalShell.box)) {
|
|
58
|
+
return { commit: false, reason: 'incompatible-analyzed-geometry' };
|
|
59
|
+
}
|
|
60
|
+
const pairs = [];
|
|
61
|
+
for (const band of recipientPage.bands || []) {
|
|
62
|
+
const recipient = shellForBand(recipientPage, band.key, region);
|
|
63
|
+
const canonical = shellForBand(donorPage, band.key, region);
|
|
64
|
+
if (!recipient || !canonical) return { commit: false, reason: `missing-${band.key}-declaration` };
|
|
65
|
+
if (!shouldTransplantShell(recipient.box, canonical.box)) {
|
|
66
|
+
return { commit: false, reason: `incompatible-${band.key}-geometry` };
|
|
67
|
+
}
|
|
68
|
+
pairs.push({ key: band.key, recipient, canonical });
|
|
69
|
+
}
|
|
70
|
+
return { commit: pairs.some((pair) => needsCanonicalShell(pair.recipient, pair.canonical)), reason: 'compatible', pairs };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Atomically commits one donor page's complete region across every declared
|
|
75
|
+
* band of a compatible member page. A failed band proof makes the whole region
|
|
76
|
+
* a literal no-write path.
|
|
77
|
+
*/
|
|
78
|
+
export function stitchUnitShells({
|
|
79
|
+
document,
|
|
80
|
+
donorDocument,
|
|
81
|
+
donorPage,
|
|
82
|
+
recipientPage,
|
|
83
|
+
unit,
|
|
84
|
+
canonicalShell,
|
|
85
|
+
navRows,
|
|
86
|
+
rulesFor,
|
|
87
|
+
styleText,
|
|
88
|
+
}) {
|
|
89
|
+
const extraCss = [];
|
|
90
|
+
const bands = new Map((recipientPage.bands || []).map((band) => [band.key, { key: band.key, regions: [] }]));
|
|
91
|
+
const decisions = [];
|
|
92
|
+
const sourceMarkerSnapshots = new Map();
|
|
93
|
+
for (const region of Object.keys(canonicalShell || {})) for (const band of recipientPage.bands || []) {
|
|
94
|
+
const sourceShell = shellForBand(recipientPage, band.key, region);
|
|
95
|
+
if (!sourceShell) continue;
|
|
96
|
+
const sourceItems = navItems(sourceShell, navRows, region);
|
|
97
|
+
sourceMarkerSnapshots.set(`${band.key}:${region}`, markerSnapshots({
|
|
98
|
+
document,
|
|
99
|
+
shell: sourceShell,
|
|
100
|
+
markers: declaredActiveMarkers(sourceShell, sourceItems),
|
|
101
|
+
rulesFor,
|
|
102
|
+
styleText,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
let changes = 0;
|
|
106
|
+
for (const [region, canonicalSemantic] of Object.entries(canonicalShell || {})) {
|
|
107
|
+
const decision = decisionForRegion({
|
|
108
|
+
unit,
|
|
109
|
+
donorPage,
|
|
110
|
+
recipientPage,
|
|
111
|
+
region,
|
|
112
|
+
canonicalShell: canonicalSemantic,
|
|
113
|
+
});
|
|
114
|
+
decisions.push({ region, commit: decision.commit, reason: decision.reason });
|
|
115
|
+
const pairs = decision.pairs || (recipientPage.bands || []).flatMap((band) => {
|
|
116
|
+
const recipient = shellForBand(recipientPage, band.key, region);
|
|
117
|
+
const canonical = shellForBand(donorPage, band.key, region);
|
|
118
|
+
return recipient && canonical ? [{ key: band.key, recipient, canonical }] : [];
|
|
119
|
+
});
|
|
120
|
+
for (const pair of pairs) {
|
|
121
|
+
const sourceNavItems = navItems(pair.recipient, navRows, region);
|
|
122
|
+
const canonicalNavItems = navItems(pair.canonical, navRows, region);
|
|
123
|
+
const sourceMarkers = declaredActiveMarkers(pair.recipient, sourceNavItems);
|
|
124
|
+
const canonicalMarkers = declaredActiveMarkers(pair.canonical, canonicalNavItems);
|
|
125
|
+
const linkedNav = linkedNavForUnit({ navRows, unit, sourceMarkers, region });
|
|
126
|
+
const outputNavItems = decision.commit ? canonicalNavItems : sourceNavItems;
|
|
127
|
+
const targetItem = linkedNav
|
|
128
|
+
? outputNavItems.find((item) => normalizeText(item.text) === normalizeText(linkedNav.text)) || null
|
|
129
|
+
: null;
|
|
130
|
+
let outputShellId = pair.recipient.containerId;
|
|
131
|
+
let outputMarkers = sourceMarkers;
|
|
132
|
+
if (decision.commit) {
|
|
133
|
+
const sourceSnapshots = sourceMarkerSnapshots.get(`${pair.key}:${region}`) || [];
|
|
134
|
+
const donorSnapshots = markerSnapshots({
|
|
135
|
+
document: donorDocument,
|
|
136
|
+
shell: pair.canonical,
|
|
137
|
+
markers: canonicalMarkers,
|
|
138
|
+
rulesFor,
|
|
139
|
+
styleText,
|
|
140
|
+
});
|
|
141
|
+
const recipientElement = document.getElementById(pair.recipient.containerId);
|
|
142
|
+
const donorElement = donorDocument.getElementById(pair.canonical.containerId);
|
|
143
|
+
const root = bandRoot(document, pair.key);
|
|
144
|
+
const host = shellHost(root);
|
|
145
|
+
const alreadyPlaced = document.getElementById(pair.canonical.containerId);
|
|
146
|
+
if (!donorElement || !root || !host || (!root.contains(recipientElement) && !root.contains(alreadyPlaced))) {
|
|
147
|
+
throw new Error(`Canonical shell commit lost declared ${region} DOM in band ${pair.key}.`);
|
|
148
|
+
}
|
|
149
|
+
if (recipientElement && root.contains(recipientElement)) {
|
|
150
|
+
const recipientId = recipientElement.id;
|
|
151
|
+
recipientElement.insertAdjacentHTML('beforebegin', donorElement.outerHTML);
|
|
152
|
+
recipientElement.replaceChildren();
|
|
153
|
+
if (recipientId === pair.canonical.containerId) {
|
|
154
|
+
const placeholderId = `${recipientId}-proto-shell-space`;
|
|
155
|
+
const placeholderCss = rulesFor(styleText(document), recipientId)
|
|
156
|
+
.replaceAll(`#${recipientId}`, `#${placeholderId}`);
|
|
157
|
+
recipientElement.id = placeholderId;
|
|
158
|
+
if (placeholderCss) extraCss.push(placeholderCss);
|
|
159
|
+
}
|
|
160
|
+
recipientElement.setAttribute('data-proto-shell-space', region);
|
|
161
|
+
recipientElement.setAttribute('aria-hidden', 'true');
|
|
162
|
+
extraCss.push(`#${recipientElement.id}{visibility:hidden!important;pointer-events:none!important}`);
|
|
163
|
+
}
|
|
164
|
+
outputShellId = pair.canonical.containerId;
|
|
165
|
+
const outputShell = document.getElementById(outputShellId);
|
|
166
|
+
if (!outputShell || !root.contains(outputShell)) {
|
|
167
|
+
throw new Error(`Canonical shell commit did not place ${region} in band ${pair.key}.`);
|
|
168
|
+
}
|
|
169
|
+
extraCss.push(rulesFor(styleText(donorDocument), pair.canonical.containerId));
|
|
170
|
+
const donorBand = donorPage.bands.find((band) => band.key === pair.key);
|
|
171
|
+
const parentShell = donorBand?.shells.find((candidate) => {
|
|
172
|
+
if (candidate.containerId === pair.canonical.containerId) return false;
|
|
173
|
+
const candidateElement = donorDocument.getElementById(candidate.containerId);
|
|
174
|
+
return candidateElement?.contains(donorElement);
|
|
175
|
+
}) || null;
|
|
176
|
+
if (!parentShell && outputShell.parentElement !== host) host.appendChild(outputShell);
|
|
177
|
+
const [x, y] = parentShell
|
|
178
|
+
? [
|
|
179
|
+
pair.canonical.box[0] - parentShell.box[0],
|
|
180
|
+
pair.canonical.box[1] - parentShell.box[1],
|
|
181
|
+
]
|
|
182
|
+
: pair.canonical.box;
|
|
183
|
+
extraCss.push(`#${pair.canonical.containerId}{position:absolute!important;left:${x}px!important;top:${y}px!important;`
|
|
184
|
+
+ 'margin:0!important;z-index:100000!important}');
|
|
185
|
+
removeMarkers(document, canonicalMarkers);
|
|
186
|
+
const matchingSource = linkedNav
|
|
187
|
+
? sourceSnapshots.find((marker) => normalizeText(marker.targetText) === normalizeText(linkedNav.text))
|
|
188
|
+
: null;
|
|
189
|
+
const markerToPlace = matchingSource || (linkedNav ? donorSnapshots[0] : null);
|
|
190
|
+
if (markerToPlace) {
|
|
191
|
+
placeMarker({ shellElement: outputShell, shell: pair.canonical, marker: markerToPlace, targetItem, extraCss });
|
|
192
|
+
outputMarkers = [{ ...markerToPlace, id: markerToPlace.id }];
|
|
193
|
+
} else if (!linkedNav) {
|
|
194
|
+
for (const marker of sourceSnapshots) {
|
|
195
|
+
placeMarker({ shellElement: outputShell, shell: pair.canonical, marker, targetItem: null, extraCss });
|
|
196
|
+
}
|
|
197
|
+
outputMarkers = sourceSnapshots;
|
|
198
|
+
} else outputMarkers = [];
|
|
199
|
+
changes++;
|
|
200
|
+
}
|
|
201
|
+
const activeMarker = linkedNav
|
|
202
|
+
? outputMarkers.find((marker) => normalizeText(marker.targetText) === normalizeText(linkedNav.text))
|
|
203
|
+
|| outputMarkers[0]
|
|
204
|
+
|| null
|
|
205
|
+
: null;
|
|
206
|
+
bands.get(pair.key)?.regions.push({
|
|
207
|
+
region,
|
|
208
|
+
shellId: outputShellId,
|
|
209
|
+
canonicalSignature: pair.canonical.signature,
|
|
210
|
+
consistencyStatus: decision.commit || decision.reason === 'canonical-page' ? 'canonical' : 'silent',
|
|
211
|
+
activeMarkerIds: outputMarkers.map((marker) => marker.id),
|
|
212
|
+
active: linkedNav && targetItem && activeMarker
|
|
213
|
+
? { status: 'linked', navText: linkedNav.text, navId: targetItem.id, markerId: activeMarker.id }
|
|
214
|
+
: linkedNav ? { status: 'unproven', navText: linkedNav.text, navId: targetItem?.id || null, markerId: null }
|
|
215
|
+
: { status: 'silent', navText: null, navId: null, markerId: null },
|
|
216
|
+
changed: decision.commit,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
return { extraCss, bands: [...bands.values()], decisions, changes };
|
|
221
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/* Prototype runtime chrome. Colours/fonts come from CSS variables set from the page's own tokens. */
|
|
2
|
-
.proto-clickable { cursor: pointer; }
|
|
2
|
+
.proto-clickable { color: inherit; cursor: pointer; }
|
|
3
3
|
.proto-clickable:hover { background-color: rgba(0, 0, 0, 0.035); }
|
|
4
|
-
|
|
4
|
+
a.proto-clickable:hover { text-decoration: underline; text-underline-offset: 3px; }
|
|
5
|
+
.proto-clickable:focus-visible { outline: 2px solid var(--proto-accent); outline-offset: 3px; }
|
|
5
6
|
|
|
6
7
|
#proto-overlay-root { position: absolute; inset: 0; z-index: 2147483000; pointer-events: none; }
|
|
7
8
|
#proto-overlay-root > * { pointer-events: auto; }
|
|
@@ -37,10 +38,14 @@ p.proto-clickable:hover { text-decoration: underline; text-underline-offset: 3px
|
|
|
37
38
|
.proto-holding-sub { font-size: 15px; line-height: 1.5; color: var(--proto-muted); margin: 0 0 28px; max-width: 560px; }
|
|
38
39
|
.proto-holding-cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }
|
|
39
40
|
.proto-holding-card { background: var(--proto-surface); border: 1px solid var(--proto-border); border-radius: var(--proto-radius); padding: 20px; min-height: 140px; }
|
|
40
|
-
.proto-holding-card-title { font-size: 14px; font-weight: 600; color: var(--proto-heading); margin: 0 0 10px; }
|
|
41
41
|
.proto-holding-card-line { height: 10px; border-radius: 5px; background: rgba(0,0,0,0.06); margin: 8px 0; }
|
|
42
42
|
.proto-holding-card-line.short { width: 60%; }
|
|
43
43
|
.proto-holding-note { margin-top: 24px; font-size: 12px; color: var(--proto-muted); }
|
|
44
|
+
.proto-holding-block[data-proto-holding-layout="mobile"] { max-width:calc(100vw - 40px); }
|
|
45
|
+
.proto-holding-block[data-proto-holding-layout="mobile"] .proto-holding-cards { grid-template-columns:1fr; gap:10px; }
|
|
46
|
+
.proto-holding-block[data-proto-holding-layout="mobile"] .proto-holding-card { min-height:72px; padding:14px; }
|
|
47
|
+
.proto-holding-block[data-proto-holding-layout="mobile"] .proto-holding-sub { margin-bottom:18px; }
|
|
48
|
+
.proto-holding-block[data-proto-holding-layout="mobile"] .proto-holding-note { margin-top:16px; }
|
|
44
49
|
|
|
45
50
|
@keyframes proto-fade { from { opacity: 0; } to { opacity: 1; } }
|
|
46
51
|
@keyframes proto-pop { from { opacity: 0; transform: translateY(4px) scale(0.985); } to { opacity: 1; transform: none; } }
|
|
@@ -25,4 +25,7 @@ node population-sweep.mjs \
|
|
|
25
25
|
The JSONL is append-only and resumable by run fingerprint. A row records the
|
|
26
26
|
failed stage or, for successful builds, real/holding page counts, action counts,
|
|
27
27
|
runtime page errors, broken targets, shell-size residuals, and same-page
|
|
28
|
-
deep-link/back behavior.
|
|
28
|
+
deep-link/back behavior. Runtime loads every generated page at 1536 and 390;
|
|
29
|
+
holding routes additionally load every declared band's representative width and
|
|
30
|
+
record active-band ownership, viewport-specific host/shell geometry and intersection, exact emitted
|
|
31
|
+
positioned-scene registration/neutralization, and holding-copy contrast.
|
|
@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { chromium } from '@playwright/test';
|
|
6
|
+
import { checkRuntime } from '../lib/runtime-check.mjs';
|
|
6
7
|
import { startStaticServer } from '../lib/static-server.mjs';
|
|
7
8
|
|
|
8
9
|
const outIndex = process.argv.indexOf('--out');
|
|
@@ -66,16 +67,60 @@ const deepLinkBack = {
|
|
|
66
67
|
afterBack: historyPage.url(),
|
|
67
68
|
historyLength: [beforeLength, afterLength],
|
|
68
69
|
};
|
|
69
|
-
|
|
70
|
+
const backLink = historyPage.getByRole('link', { name: 'Back to destination' });
|
|
71
|
+
await backLink.focus();
|
|
72
|
+
const accessibleBack = {
|
|
73
|
+
roleCount: await backLink.count(),
|
|
74
|
+
focused: await backLink.evaluate((element) => element === document.activeElement),
|
|
75
|
+
};
|
|
76
|
+
await historyPage.keyboard.press('Enter');
|
|
70
77
|
await historyPage.waitForURL('**/history-destination.html');
|
|
71
78
|
const afterPageClick = historyPage.url();
|
|
72
79
|
await historyPage.goBack({ waitUntil: 'domcontentloaded', timeout: 2_000 });
|
|
73
80
|
const pageBack = { afterPageClick, afterBack: historyPage.url() };
|
|
74
81
|
await historyPage.close();
|
|
75
82
|
|
|
83
|
+
const holdingRuntime = await checkRuntime({
|
|
84
|
+
browser,
|
|
85
|
+
baseUrl: server.baseUrl,
|
|
86
|
+
relativeProtoPath: 'tests/fixtures/holding-runtime',
|
|
87
|
+
protoDir: path.join(kitDir, 'tests', 'fixtures', 'holding-runtime'),
|
|
88
|
+
});
|
|
89
|
+
const holdingPage = (file) => holdingRuntime.pages.find((page) => page.file === file);
|
|
90
|
+
const failuresFor = (file) => (holdingPage(file)?.viewports || []).flatMap((viewport) => (
|
|
91
|
+
(viewport.composition?.failures || []).map((failure) => ({ width: viewport.width, failure }))
|
|
92
|
+
));
|
|
93
|
+
const holdingComposition = {
|
|
94
|
+
bandOwnershipFire: failuresFor('band-ownership-fire.html'),
|
|
95
|
+
bandOwnershipSilent: failuresFor('band-ownership-silent.html'),
|
|
96
|
+
shellIntersectionFire: failuresFor('shell-intersection-fire.html'),
|
|
97
|
+
shellIntersectionSilent: failuresFor('shell-intersection-silent.html'),
|
|
98
|
+
positionedGroundFire: failuresFor('positioned-ground-fire.html'),
|
|
99
|
+
positionedGroundSilent: failuresFor('positioned-ground-silent.html'),
|
|
100
|
+
declaredWidthFire: failuresFor('declared-width-fire.html'),
|
|
101
|
+
declaredWidthSilent: failuresFor('declared-width-silent.html'),
|
|
102
|
+
groundRegistrationMismatchFire: failuresFor('ground-registration-mismatch-fire.html'),
|
|
103
|
+
groundRegistrationMismatchSilent: failuresFor('ground-registration-mismatch-silent.html'),
|
|
104
|
+
shellGeometryFire: failuresFor('shell-geometry-fire.html'),
|
|
105
|
+
shellGeometrySilent: failuresFor('shell-geometry-silent.html'),
|
|
106
|
+
holdingContrastFire: failuresFor('holding-contrast-fire.html'),
|
|
107
|
+
holdingContrastSilent: failuresFor('holding-contrast-silent.html'),
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const shellNavRuntime = await checkRuntime({
|
|
111
|
+
browser,
|
|
112
|
+
baseUrl: server.baseUrl,
|
|
113
|
+
relativeProtoPath: 'tests/fixtures/shell-nav-runtime',
|
|
114
|
+
protoDir: path.join(kitDir, 'tests', 'fixtures', 'shell-nav-runtime'),
|
|
115
|
+
});
|
|
116
|
+
const shellNavigation = {
|
|
117
|
+
shellConsistencyFailures: shellNavRuntime.shellConsistencyFailures,
|
|
118
|
+
activeNavigationFailures: shellNavRuntime.activeNavigationFailures,
|
|
119
|
+
};
|
|
120
|
+
|
|
76
121
|
await browser.close();
|
|
77
122
|
await server.close();
|
|
78
|
-
const result = { doubleBinding, nestedBinding, dropdownGuard, hoverStacking, deepLinkBack, pageBack };
|
|
123
|
+
const result = { doubleBinding, nestedBinding, dropdownGuard, hoverStacking, deepLinkBack, accessibleBack, pageBack, holdingComposition, shellNavigation };
|
|
79
124
|
if (process.argv.includes('--assert')) {
|
|
80
125
|
assert.deepEqual(doubleBinding, { overlayRoots: 1, modals: 1 }, 'runtime must bind exactly once when injected twice');
|
|
81
126
|
assert.deepEqual(nestedBinding, { modals: 1, menus: 0 }, 'the innermost action must stop a parent binding from also firing');
|
|
@@ -83,7 +128,45 @@ if (process.argv.includes('--assert')) {
|
|
|
83
128
|
assert.deepEqual(hoverStacking, { stackedOpened: 1, plainOpened: 1 }, 'hover styling must not reorder either overlapping or ordinary actions');
|
|
84
129
|
assert.equal(afterLength, beforeLength + 1, 'same-page navigation must add one history entry');
|
|
85
130
|
assert.equal(deepLinkBack.afterBack, historyStart, 'browser Back must return to the pre-anchor URL');
|
|
131
|
+
assert.deepEqual(accessibleBack, { roleCount: 1, focused: true }, 'Back must be a native focusable link that activates with Enter');
|
|
86
132
|
assert.equal(pageBack.afterBack, historyStart, 'browser Back must return from a different prototype page');
|
|
133
|
+
assert.deepEqual(holdingComposition.bandOwnershipFire, [{ width: 390, failure: 'holding-surface-count' }], 'must fire: a visible responsive band without exactly one holding surface is gated');
|
|
134
|
+
assert.deepEqual(holdingComposition.bandOwnershipSilent, [], 'must stay silent: one holding surface in every responsive band passes');
|
|
135
|
+
assert.deepEqual(holdingComposition.shellIntersectionFire, [
|
|
136
|
+
{ width: 1536, failure: 'shell-intersection' },
|
|
137
|
+
{ width: 390, failure: 'shell-intersection' },
|
|
138
|
+
], 'must fire: a base-state shell intersecting the holding rectangle is gated at both widths');
|
|
139
|
+
assert.deepEqual(holdingComposition.shellIntersectionSilent, [], 'must stay silent: a measured footer outside the holding rectangle passes at both widths');
|
|
140
|
+
assert.deepEqual(holdingComposition.positionedGroundFire, [
|
|
141
|
+
{ width: 1536, failure: 'donor-ground-active' },
|
|
142
|
+
{ width: 390, failure: 'donor-ground-active' },
|
|
143
|
+
], 'must fire: an explicitly registered positioned-scene ground cannot paint on a holding route');
|
|
144
|
+
assert.deepEqual(holdingComposition.positionedGroundSilent, [], 'must stay silent: an undeclared ground fact is never inferred from paint or selector names');
|
|
145
|
+
assert.deepEqual(holdingComposition.declaredWidthFire, [
|
|
146
|
+
{ width: 768, failure: 'holding-surface-count' },
|
|
147
|
+
], 'must fire: every declared representative breakpoint is audited, including tablet');
|
|
148
|
+
assert.deepEqual(holdingComposition.declaredWidthSilent, [], 'must stay silent: a three-band holding with one surface per band passes');
|
|
149
|
+
assert.deepEqual(holdingComposition.groundRegistrationMismatchFire, [
|
|
150
|
+
{ width: 1536, failure: 'ground-registration-mismatch' },
|
|
151
|
+
{ width: 390, failure: 'ground-registration-mismatch' },
|
|
152
|
+
], 'must fire: runtime cannot replace the emitted ground fact with config-only optimism');
|
|
153
|
+
assert.deepEqual(holdingComposition.groundRegistrationMismatchSilent, [], 'must stay silent: the emitted and inventoried positioned-scene fact agrees after neutralization');
|
|
154
|
+
assert.deepEqual(holdingComposition.shellGeometryFire, [
|
|
155
|
+
{ width: 390, failure: 'shell-geometry-mismatch' },
|
|
156
|
+
], 'must fire: freezing a nonzero desktop host origin at mobile width is gated');
|
|
157
|
+
assert.deepEqual(holdingComposition.shellGeometrySilent, [], 'must stay silent: viewport-specific measured host and shell geometry passes at both widths');
|
|
158
|
+
assert.deepEqual(holdingComposition.holdingContrastFire, [
|
|
159
|
+
{ width: 1536, failure: 'holding-contrast' },
|
|
160
|
+
{ width: 390, failure: 'holding-contrast' },
|
|
161
|
+
], 'must fire: unreadable product copy on the selected neutral surface is gated');
|
|
162
|
+
assert.deepEqual(holdingComposition.holdingContrastSilent, [], 'must stay silent: readable holding copy passes at both widths');
|
|
163
|
+
assert.equal(shellNavigation.shellConsistencyFailures.length, 2, 'must fire: one structurally divergent member shell is gated at desktop and mobile widths');
|
|
164
|
+
assert.deepEqual(shellNavigation.shellConsistencyFailures.map((failure) => failure.group), [
|
|
165
|
+
'1536:native:header',
|
|
166
|
+
'390:native:header',
|
|
167
|
+
], 'must stay silent: identical canonical shell members share one runtime fingerprint at each width');
|
|
168
|
+
assert.equal(shellNavigation.activeNavigationFailures.length, 2, 'must fire: a visible marker under the wrong linked nav item is gated at both widths');
|
|
169
|
+
assert.ok(shellNavigation.activeNavigationFailures.every((failure) => failure.file === 'active-fire.html'), 'must stay silent: already-correct and unlinked source ink produce no active-navigation failure');
|
|
87
170
|
}
|
|
88
171
|
const json = JSON.stringify(result, null, 2) + '\n';
|
|
89
172
|
if (outFile) fs.writeFileSync(outFile, json);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Holding-composition paired fixtures
|
|
2
|
+
|
|
3
|
+
These reduced fixtures freeze the exact three pairs named by the
|
|
4
|
+
`ws2-prototype-holding-leak` population study. They deliberately keep only the
|
|
5
|
+
DOM and paint needed to exercise the production runtime gate.
|
|
6
|
+
|
|
7
|
+
| Guard | Must fire provenance | Must stay silent provenance | Reduced fixtures |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| Active band owns holding content | Salt Marsh `holding-our-story`, Field Notes `holding-journal`, and the 16 progress v2b/v2c holdings | worker-v189 Salt `holding-about`, plus both repaired r2 outputs | `band-ownership-fire.html` / `band-ownership-silent.html` |
|
|
10
|
+
| Retained shell intersects holding | Salt Marsh `holding-our-story` base footer | Field Notes `holding-journal` absolute footer | `shell-intersection-fire.html` / `shell-intersection-silent.html` |
|
|
11
|
+
| Declared positioned-scene ground survives | Field Notes `holding-journal` donor plate | Salt Marsh flat donor ground | `positioned-ground-fire.html` / `positioned-ground-silent.html` |
|
|
12
|
+
|
|
13
|
+
The population and r2 replays exercise the named full artifacts. These fixtures
|
|
14
|
+
are the deterministic suite form: each must-fire changes exactly one gate
|
|
15
|
+
condition, and each must-stay-silent preserves the adjacent condition that the
|
|
16
|
+
study requires the gate not to infer.
|
|
17
|
+
|
|
18
|
+
The additional declared-width, ground-registration-mismatch, shell-geometry,
|
|
19
|
+
and holding-contrast pairs protect the evidence chain itself: every measured
|
|
20
|
+
band is visited, the runtime declaration agrees with the emitted producer fact,
|
|
21
|
+
screen geometry is converted into the composition host's coordinate frame, and
|
|
22
|
+
product copy remains readable on the selected neutral surface.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.ly-band{display:none;position:relative;height:1024px}.generated-screen{position:relative;height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
@media(max-width:959px){.ly-band[data-ly-band="mobile-390"]{display:block}}
|
|
4
|
+
@media(min-width:960px){.ly-band[data-ly-band="native"]{display:block}}
|
|
5
|
+
[data-proto-holding-surface]{position:absolute;left:40px;top:180px;width:300px;height:240px}
|
|
6
|
+
</style></head><body>
|
|
7
|
+
<div class="ly-band" data-ly-band="mobile-390"><div class="generated-screen"></div></div>
|
|
8
|
+
<div class="ly-band" data-ly-band="native"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
9
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"mobile-390","shellIds":[],"groundRegistration":null},{"key":"native","shellIds":[],"groundRegistration":null}]}</script>
|
|
10
|
+
</body></html>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.ly-band{display:none;position:relative;height:1024px}.generated-screen{position:relative;height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
@media(max-width:959px){.ly-band[data-ly-band="mobile-390"]{display:block}}
|
|
4
|
+
@media(min-width:960px){.ly-band[data-ly-band="native"]{display:block}}
|
|
5
|
+
[data-proto-holding-surface]{position:absolute;left:40px;top:180px;width:300px;height:240px}
|
|
6
|
+
</style></head><body>
|
|
7
|
+
<div class="ly-band" data-ly-band="mobile-390"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
8
|
+
<div class="ly-band" data-ly-band="native"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
9
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"mobile-390","shellIds":[],"groundRegistration":null},{"key":"native","shellIds":[],"groundRegistration":null}]}</script>
|
|
10
|
+
</body></html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.ly-band{display:none;height:1024px}.generated-screen{height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
@media(max-width:600px){.ly-band[data-ly-band="mobile"]{display:block}}
|
|
4
|
+
@media(min-width:601px) and (max-width:1000px){.ly-band[data-ly-band="tablet"]{display:block}}
|
|
5
|
+
@media(min-width:1001px){.ly-band[data-ly-band="native"]{display:block}}
|
|
6
|
+
</style></head><body>
|
|
7
|
+
<div class="ly-band" data-ly-band="mobile"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
8
|
+
<div class="ly-band" data-ly-band="tablet"><div class="generated-screen"></div></div>
|
|
9
|
+
<div class="ly-band" data-ly-band="native"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
10
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"mobile","viewportWidth":390,"shellIds":[],"groundRegistration":null},{"key":"tablet","viewportWidth":768,"shellIds":[],"groundRegistration":null},{"key":"native","viewportWidth":1536,"shellIds":[],"groundRegistration":null}]}</script>
|
|
11
|
+
</body></html>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.ly-band{display:none;height:1024px}.generated-screen{height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
@media(max-width:600px){.ly-band[data-ly-band="mobile"]{display:block}}
|
|
4
|
+
@media(min-width:601px) and (max-width:1000px){.ly-band[data-ly-band="tablet"]{display:block}}
|
|
5
|
+
@media(min-width:1001px){.ly-band[data-ly-band="native"]{display:block}}
|
|
6
|
+
</style></head><body>
|
|
7
|
+
<div class="ly-band" data-ly-band="mobile"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
8
|
+
<div class="ly-band" data-ly-band="tablet"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
9
|
+
<div class="ly-band" data-ly-band="native"><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
10
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"mobile","viewportWidth":390,"shellIds":[],"groundRegistration":null},{"key":"tablet","viewportWidth":768,"shellIds":[],"groundRegistration":null},{"key":"native","viewportWidth":1536,"shellIds":[],"groundRegistration":null}]}</script>
|
|
11
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.generated-screen{height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","viewportWidth":1536,"shellIds":[],"groundRegistration":"positioned-scene"}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.generated-screen{height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen" data-ly-ground-registration="positioned-scene"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","viewportWidth":1536,"shellIds":[],"groundRegistration":"positioned-scene"}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.generated-screen{height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#eee}
|
|
3
|
+
</style></head><body><div class="generated-screen" data-ly-ground-registration="positioned-scene"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","viewportWidth":1536,"shellIds":[],"groundRegistration":"positioned-scene"}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.generated-screen{height:1024px;background:#fff}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen" data-ly-ground-registration="positioned-scene"><main data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","viewportWidth":1536,"shellIds":[],"groundRegistration":"positioned-scene"}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0;height:1024px}.generated-screen{position:relative;height:1024px;background-color:#fff;background-image:linear-gradient(#123,#456)}.holding{position:absolute;left:40px;top:120px;width:300px;height:240px}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen" data-ly-ground-registration="positioned-scene"><main class="holding" data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","shellIds":[],"groundRegistration":"positioned-scene"}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0;height:1024px}.generated-screen{position:relative;height:1024px;background-color:#fff;background-image:linear-gradient(#123,#456)}.holding{position:absolute;left:40px;top:120px;width:300px;height:240px}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen"><main class="holding" data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","shellIds":[],"groundRegistration":null}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.generated-screen{height:1024px;background:#fff}.host{position:relative;margin-left:max(0px,calc((100vw - 1000px)/2));width:1000px;height:1024px;zoom:min(1,calc(100vw / 1000px))}.shell{position:absolute;left:30px;top:100px;width:300px;height:50px}.holding{position:absolute;left:30px;top:300px}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen"><div class="host" data-proto-holding-band="native"><header id="measured-header" class="shell"></header><main class="holding" data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","viewportWidth":1536,"hostOrigins":[{"viewportWidth":1536,"origin":[268,0]},{"viewportWidth":390,"origin":[268,0]}],"shellIds":["measured-header"],"shellBoxes":[{"id":"measured-header","box":[30,100,300,50]}],"groundRegistration":null}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0}.generated-screen{height:1024px;background:#fff}.host{position:relative;margin-left:max(0px,calc((100vw - 1000px)/2));width:1000px;height:1024px;zoom:min(1,calc(100vw / 1000px))}.shell{position:absolute;left:30px;top:100px;width:300px;height:50px}.holding{position:absolute;left:30px;top:300px}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen"><div class="host" data-proto-holding-band="native"><header id="measured-header" class="shell"></header><main class="holding" data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","viewportWidth":1536,"hostOrigins":[{"viewportWidth":1536,"origin":[268,0]},{"viewportWidth":390,"origin":[0,0]}],"shellIds":["measured-header"],"shellBoxes":[{"id":"measured-header","box":[30,100,300,50]}],"groundRegistration":null}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0;height:1024px}.generated-screen{position:relative;height:1024px;background:#fff}.shell{position:absolute;left:0;top:0;width:100%;height:100px}.holding{position:absolute;left:40px;top:60px;width:300px;height:240px}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen"><header id="base-header" class="shell"></header><main class="holding" data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","shellIds":["base-header"],"groundRegistration":null}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<!doctype html><html><head><style>
|
|
2
|
+
html,body{margin:0;height:1024px}.generated-screen{position:relative;height:1024px;background:#fff}.shell{position:absolute;left:0;top:900px;width:100%;height:100px}.holding{position:absolute;left:40px;top:120px;width:300px;height:240px}.proto-holding-title,.proto-holding-sub{color:#111}
|
|
3
|
+
</style></head><body><div class="generated-screen"><footer id="base-footer" class="shell"></footer><main class="holding" data-proto-holding-surface="true"><h1 class="proto-holding-title">Holding</h1><p class="proto-holding-sub">Planned destination.</p></main></div>
|
|
4
|
+
<script id="proto-state" type="application/json">{"holding":true,"holdingBands":[{"key":"native","shellIds":["base-footer"],"groundRegistration":null}]}</script>
|
|
5
|
+
</body></html>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!doctype html><html><head><meta charset="utf-8"><style>body{margin:0}section{height:900px;padding:20px}</style></head><body>
|
|
2
|
-
<section><button id="jump" data-proto-action="navigate" data-proto-target="history.html#target">Jump</button><
|
|
2
|
+
<section><button id="jump" data-proto-action="navigate" data-proto-target="history.html#target">Jump</button><a id="next" href="history-destination.html" data-proto-action="navigate" data-proto-target="history-destination.html" aria-label="Back to destination">Back</a></section>
|
|
3
3
|
<section id="target"><h1>Target</h1></section>
|
|
4
4
|
<script id="proto-state" type="application/json">{"unit":"history","tokens":{},"contentFile":null}</script>
|
|
5
5
|
<script src="../../../runtime/proto.js"></script>
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Shell and navigation runtime fixture
|
|
2
|
+
|
|
3
|
+
Reduced synthetic pair for the measured prototype runtime contract. The fire
|
|
4
|
+
pages introduce one structural shell mismatch and one misplaced active marker;
|
|
5
|
+
the silent pages keep identical shells, including one page with deliberately
|
|
6
|
+
unlinked source ink.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><style>
|
|
2
|
+
html,body{margin:0;width:100%;height:100%}.shell{position:relative;width:100%;height:72px;background:#fff}.nav{position:absolute;top:20px;font:16px Arial}.menu{left:30px}.journal{left:130px}.ink{position:absolute;left:130px;top:42px;width:40px;height:3px;background:#a43}
|
|
3
|
+
</style></head><body><header id="active-fire-header" class="shell"><span id="active-fire-menu" class="nav menu">Menu</span><span id="active-fire-journal" class="nav journal">Journal</span><span id="active-fire-ink" class="ink"></span></header></body></html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"units": [
|
|
3
|
+
{
|
|
4
|
+
"id": "shell-silent",
|
|
5
|
+
"shellBands": [{"key":"native","regions":[{"region":"header","shellId":"shell-silent-header","canonicalSignature":"fixture","consistencyStatus":"canonical","activeMarkerIds":["shell-silent-ink"],"active":{"status":"linked","navText":"Menu","navId":"shell-silent-menu","markerId":"shell-silent-ink"}}]}]
|
|
6
|
+
},
|
|
7
|
+
{
|
|
8
|
+
"id": "unlinked-silent",
|
|
9
|
+
"shellBands": [{"key":"native","regions":[{"region":"header","shellId":"unlinked-silent-header","canonicalSignature":"fixture","consistencyStatus":"canonical","activeMarkerIds":["unlinked-silent-ink"],"active":{"status":"silent","navText":null,"navId":null,"markerId":null}}]}]
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"id": "shell-fire",
|
|
13
|
+
"shellBands": [{"key":"native","regions":[{"region":"header","shellId":"shell-fire-header","canonicalSignature":"fixture","consistencyStatus":"canonical","activeMarkerIds":["shell-fire-ink"],"active":{"status":"linked","navText":"Menu","navId":"shell-fire-menu","markerId":"shell-fire-ink"}}]}]
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"id": "active-fire",
|
|
17
|
+
"shellBands": [{"key":"native","regions":[{"region":"header","shellId":"active-fire-header","canonicalSignature":"fixture","consistencyStatus":"canonical","activeMarkerIds":["active-fire-ink"],"active":{"status":"linked","navText":"Menu","navId":"active-fire-menu","markerId":"active-fire-ink"}}]}]
|
|
18
|
+
}
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><style>
|
|
2
|
+
html,body{margin:0;width:100%;height:100%}.shell{position:relative;width:100%;height:72px;background:#fff}.nav{position:absolute;top:20px;font:16px Arial}.menu{left:30px}.journal{left:130px}.extra{position:absolute;right:20px;top:20px;font:16px Arial}.ink{position:absolute;left:30px;top:42px;width:40px;height:3px;background:#a43}
|
|
3
|
+
</style></head><body><header id="shell-fire-header" class="shell"><span id="shell-fire-menu" class="nav menu">Menu</span><span id="shell-fire-journal" class="nav journal">Journal</span><span id="shell-fire-extra" class="extra">Overflow</span><span id="shell-fire-ink" class="ink"></span></header></body></html>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><style>
|
|
2
|
+
html,body{margin:0;width:100%;height:100%}.shell{position:relative;width:100%;height:72px;background:#fff}.nav{position:absolute;top:20px;font:16px Arial}.menu{left:30px}.journal{left:130px}.ink{position:absolute;left:30px;top:42px;width:40px;height:3px;background:#a43}
|
|
3
|
+
</style></head><body><header id="shell-silent-header" class="shell"><span id="shell-silent-menu" class="nav menu">Menu</span><span id="shell-silent-journal" class="nav journal">Journal</span><span id="shell-silent-ink" class="ink"></span></header></body></html>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="utf-8"><style>
|
|
2
|
+
html,body{margin:0;width:100%;height:100%}.shell{position:relative;width:100%;height:72px;background:#fff}.nav{position:absolute;top:20px;font:16px Arial}.menu{left:30px}.journal{left:130px}.ink{position:absolute;left:130px;top:42px;width:40px;height:3px;background:#a43}
|
|
3
|
+
</style></head><body><header id="unlinked-silent-header" class="shell"><span id="unlinked-silent-menu" class="nav menu">Menu</span><span id="unlinked-silent-journal" class="nav journal">Journal</span><span id="unlinked-silent-ink" class="ink"></span></header></body></html>
|