@12ui/design 0.2.48 → 0.2.50
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/cli-draft-command.d.ts +11 -5
- package/dist/cli-draft-command.d.ts.map +1 -1
- package/dist/cli-draft-command.js +37 -35
- package/dist/cli-draft-command.js.map +1 -1
- package/dist/cli-help/design.d.ts +10 -0
- package/dist/cli-help/design.d.ts.map +1 -1
- package/dist/cli-help/design.js +29 -0
- package/dist/cli-help/design.js.map +1 -1
- package/dist/cli-help/index.d.ts +11 -0
- package/dist/cli-help/index.d.ts.map +1 -1
- package/dist/cli-help/index.js +18 -2
- package/dist/cli-help/index.js.map +1 -1
- package/dist/cli-next-command.d.ts.map +1 -1
- package/dist/cli-next-command.js +26 -14
- package/dist/cli-next-command.js.map +1 -1
- package/dist/cli-progress.d.ts +2 -0
- package/dist/cli-progress.d.ts.map +1 -1
- package/dist/cli-progress.js +2 -0
- package/dist/cli-progress.js.map +1 -1
- package/dist/cli-redesign-command.d.ts +42 -0
- package/dist/cli-redesign-command.d.ts.map +1 -0
- package/dist/cli-redesign-command.js +469 -0
- package/dist/cli-redesign-command.js.map +1 -0
- package/dist/cli-run.d.ts.map +1 -1
- package/dist/cli-run.js +8 -0
- package/dist/cli-run.js.map +1 -1
- package/dist/cli-usage.d.ts.map +1 -1
- package/dist/cli-usage.js +8 -1
- package/dist/cli-usage.js.map +1 -1
- package/dist/draft-dispatch-attempt.d.ts +0 -7
- package/dist/draft-dispatch-attempt.d.ts.map +1 -1
- package/dist/draft-dispatch-attempt.js +1 -25
- package/dist/draft-dispatch-attempt.js.map +1 -1
- package/dist/draft-identity.d.ts +2 -0
- package/dist/draft-identity.d.ts.map +1 -1
- package/dist/draft-identity.js +1 -0
- package/dist/draft-identity.js.map +1 -1
- package/dist/draft-run.d.ts +2 -0
- package/dist/draft-run.d.ts.map +1 -1
- package/dist/draft-run.js +4 -3
- package/dist/draft-run.js.map +1 -1
- package/dist/redesign-capture.d.ts +49 -0
- package/dist/redesign-capture.d.ts.map +1 -0
- package/dist/redesign-capture.js +135 -0
- package/dist/redesign-capture.js.map +1 -0
- package/dist/redesign-plan-client.d.ts +32 -0
- package/dist/redesign-plan-client.d.ts.map +1 -0
- package/dist/redesign-plan-client.js +80 -0
- package/dist/redesign-plan-client.js.map +1 -0
- package/dist/redesign-plan-identity.d.ts +8 -0
- package/dist/redesign-plan-identity.d.ts.map +1 -0
- package/dist/redesign-plan-identity.js +15 -0
- package/dist/redesign-plan-identity.js.map +1 -0
- package/dist/redesign-plan.d.ts +30 -0
- package/dist/redesign-plan.d.ts.map +1 -0
- package/dist/redesign-plan.js +99 -0
- package/dist/redesign-plan.js.map +1 -0
- package/dist/redesign-workspace.d.ts +116 -0
- package/dist/redesign-workspace.d.ts.map +1 -0
- package/dist/redesign-workspace.js +157 -0
- package/dist/redesign-workspace.js.map +1 -0
- package/dist/run-journal-commands.d.ts.map +1 -1
- package/dist/run-journal-commands.js +10 -0
- package/dist/run-journal-commands.js.map +1 -1
- package/open-design.json +2 -2
- package/package.json +2 -2
- package/prototype-kit/build.mjs +9 -3
- package/prototype-kit/lib/action-target.mjs +26 -0
- package/prototype-kit/lib/local-document.mjs +16 -0
- package/prototype-kit/lib/runtime-check.mjs +83 -19
- package/prototype-kit/lib/shell-runtime-equivalence.mjs +41 -0
- package/prototype-kit/polish/harm.mjs +27 -5
- package/prototype-kit/polish/measure.mjs +101 -5
- package/prototype-kit/polish/overrides.mjs +32 -3
- package/prototype-kit/polish/polish.mjs +23 -11
- package/prototype-kit/polish/rules/align.mjs +73 -3
- package/prototype-kit/polish/rules/left-rail.mjs +2 -3
- package/prototype-kit/polish/rules/section-breathing.mjs +0 -1
- package/prototype-kit/polish/tests/fixtures/align/expect.json +1 -1
- package/prototype-kit/polish/tests/fixtures/align/fire.html +4 -1
- package/prototype-kit/polish/tests/fixtures/align/silent.html +8 -0
- package/prototype-kit/polish/tests/fixtures/align/zoom-constrained.html +17 -0
- package/prototype-kit/polish/tests/fixtures/harm/left-rail-contained.html +16 -0
- package/prototype-kit/polish/tests/fixtures/harm/left-rail-overflow.html +16 -0
- package/prototype-kit/polish/tests/fixtures/harm/nested-actions.html +6 -0
- package/prototype-kit/polish/tests/fixtures/harm/unidentified-parent.html +3 -0
- package/prototype-kit/polish/tests/fixtures/shell-seam/fire.html +1 -1
- package/prototype-kit/polish/tests/harm-check.mjs +131 -2
- package/prototype-kit/polish/tests/rules-check.mjs +7 -2
- package/prototype-kit/runtime/proto.js +4 -1
- package/prototype-kit/tests/adversarial-runtime.mjs +3 -0
- package/prototype-kit/tests/align-rule-check.mjs +105 -0
- package/prototype-kit/tests/fixtures/interaction-runtime/review-bottom-action.html +8 -0
- package/prototype-kit/tests/offline-check.mjs +17 -1
- package/prototype-kit/tests/rules-check.mjs +67 -1
- package/prototype-kit/tests/shell-stitching-runtime.mjs +15 -8
|
@@ -4,13 +4,29 @@ import { createHash } from 'node:crypto';
|
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import path from 'node:path';
|
|
7
|
+
import { parseHTML } from 'linkedom';
|
|
7
8
|
import { reviewFramingMarkup } from '../lib/review-framing.mjs';
|
|
9
|
+
import { removeRemoteHeadLinks } from '../lib/local-document.mjs';
|
|
8
10
|
import { buildPrototype } from './lib/run-process.mjs';
|
|
9
11
|
|
|
10
12
|
const testsDir = path.dirname(new URL(import.meta.url).pathname);
|
|
11
13
|
const kitDir = path.resolve(testsDir, '..');
|
|
12
14
|
const scratch = fs.mkdtempSync(path.join(os.tmpdir(), 'proto-kit-offline-'));
|
|
13
15
|
|
|
16
|
+
const localDocument = parseHTML([
|
|
17
|
+
'<html><head>',
|
|
18
|
+
'<link rel="preconnect" href="https://fonts.example">',
|
|
19
|
+
'<link rel="stylesheet" href="//cdn.example/font.css">',
|
|
20
|
+
'<link rel="stylesheet" href="proto.css">',
|
|
21
|
+
'</head></html>',
|
|
22
|
+
].join('')).document;
|
|
23
|
+
assert.equal(removeRemoteHeadLinks(localDocument), 2, 'must fire: direct-file prototypes remove remote head links');
|
|
24
|
+
assert.deepEqual(
|
|
25
|
+
[...localDocument.querySelectorAll('head link')].map((link) => link.getAttribute('href')),
|
|
26
|
+
['proto.css'],
|
|
27
|
+
'must stay silent: prototype-local stylesheets survive offline sanitization',
|
|
28
|
+
);
|
|
29
|
+
|
|
14
30
|
function buildFixture(name) {
|
|
15
31
|
const runDir = path.join(testsDir, 'fixtures', name);
|
|
16
32
|
return buildPrototype({ kitDir, run: { runDir }, runWorkDir: path.join(scratch, name) });
|
|
@@ -44,7 +60,7 @@ const outputDigest = (directory) => {
|
|
|
44
60
|
};
|
|
45
61
|
assert.equal(
|
|
46
62
|
outputDigest(threeShell.protoDir),
|
|
47
|
-
'
|
|
63
|
+
'0a24a0c220106a1592598f1ad11685081337cb60d3044953e56e11e239dbc8bd',
|
|
48
64
|
'must stay silent: review support leaves the accepted shell/navigation complete output byte-identical',
|
|
49
65
|
);
|
|
50
66
|
const threeShellModel = JSON.parse(fs.readFileSync(threeShell.modelFile, 'utf8'));
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { parseHTML } from 'linkedom';
|
|
4
4
|
import { resolveDropdownAction, suppressDeterministicAction, usableDropdownOptions } from '../lib/actions.mjs';
|
|
5
|
-
import { restoreActionPointerReachability } from '../lib/action-target.mjs';
|
|
5
|
+
import { restoreActionPointerReachability, selectUniqueActionOwners } from '../lib/action-target.mjs';
|
|
6
|
+
import { checkAlignRule } from './align-rule-check.mjs';
|
|
6
7
|
import { declaredActiveMarkers, linkedNavForUnit, markerOverlapsNav } from '../lib/nav-active-state.mjs';
|
|
7
8
|
import { matchPlannedPage } from '../lib/navigation.mjs';
|
|
8
9
|
import { needsCanonicalShell, selectCanonicalShellPage } from '../lib/shell-consistency.mjs';
|
|
9
10
|
import { isolateTransplantedShell, shellElementsForSourceId } from '../lib/shell-identity.mjs';
|
|
11
|
+
import { compareCanonicalShellSamples, SHELL_RUNTIME_GEOMETRY_EPSILON_PX } from '../lib/shell-runtime-equivalence.mjs';
|
|
10
12
|
import { stitchUnitShells } from '../lib/shell-stitching.mjs';
|
|
11
13
|
import { compareCanonicalShells, isWithinShell, qualifiesShellCandidate, selectOneShellPerRegion, shouldSuppressDroppedShell, shouldTransplantShell } from '../lib/shells.mjs';
|
|
12
14
|
import { slugText } from '../lib/text.mjs';
|
|
@@ -69,6 +71,68 @@ const reachableBefore = reachableDropdown.outerHTML;
|
|
|
69
71
|
assert.equal(restoreActionPointerReachability(reachableDropdown), false, 'must stay silent: an already reachable dropdown target is unchanged');
|
|
70
72
|
assert.equal(reachableDropdown.outerHTML, reachableBefore);
|
|
71
73
|
|
|
74
|
+
const repeatedDropdown = { action: 'dropdown', options: ['All opportunities', 'High intent', 'Low intent'] };
|
|
75
|
+
const distinctDropdown = { action: 'dropdown', options: ['All countries', 'Australia', 'Canada'] };
|
|
76
|
+
const landsFilterDocument = parseHTML(`<div id="filters"><button id="filter-1"></button><button id="filter-2"></button><button id="filter-3"></button><button id="filter-4"></button><button id="filter-5"></button><button id="filter-6"></button></div>`).document;
|
|
77
|
+
const landsFilterActions = new Map([
|
|
78
|
+
['filters', repeatedDropdown],
|
|
79
|
+
['filter-1', repeatedDropdown],
|
|
80
|
+
['filter-2', distinctDropdown],
|
|
81
|
+
['filter-3', distinctDropdown],
|
|
82
|
+
['filter-4', distinctDropdown],
|
|
83
|
+
['filter-5', distinctDropdown],
|
|
84
|
+
['filter-6', distinctDropdown],
|
|
85
|
+
]);
|
|
86
|
+
const uniqueLandsFilters = selectUniqueActionOwners(landsFilterDocument, landsFilterActions);
|
|
87
|
+
assert.deepEqual(uniqueLandsFilters.removed, [{ wrapperId: 'filters', ownerIds: ['filter-1', 'filter-2', 'filter-3', 'filter-4', 'filter-5', 'filter-6'], action: 'dropdown' }], 'must fire: the six-child Ghost Sitemap filter shape gives the wrapper action to its terminal controls');
|
|
88
|
+
assert.deepEqual([...uniqueLandsFilters.actions.keys()], ['filter-1', 'filter-2', 'filter-3', 'filter-4', 'filter-5', 'filter-6'], 'must stay silent: every native filter control remains independently actionable');
|
|
89
|
+
const landsModalDocument = parseHTML('<div id="submit"><button id="submit-button"></button></div>').document;
|
|
90
|
+
const landsModalActions = new Map([
|
|
91
|
+
['submit', { action: 'modal', modal: { title: 'Analyze my site?', confirm: 'Analyze my site' } }],
|
|
92
|
+
['submit-button', { action: 'modal', modal: { title: 'Analyze my site?', confirm: 'Analyze my site' } }],
|
|
93
|
+
]);
|
|
94
|
+
const uniqueLandsModal = selectUniqueActionOwners(landsModalDocument, landsModalActions);
|
|
95
|
+
assert.deepEqual(uniqueLandsModal.removed, [{ wrapperId: 'submit', ownerIds: ['submit-button'], action: 'modal' }], 'must fire: the One Question modal wrapper yields to its terminal button');
|
|
96
|
+
assert.deepEqual([...uniqueLandsModal.actions.keys()], ['submit-button']);
|
|
97
|
+
const territoryFilterDocument = parseHTML('<aside id="filter-bar"><button id="filter-a"></button><button id="filter-b"></button><button id="filter-c"></button><button id="filter-d"></button></aside>').document;
|
|
98
|
+
const territoryFilterActions = new Map([
|
|
99
|
+
['filter-bar', { action: 'dropdown', options: ['All', 'Open', 'Closed'] }],
|
|
100
|
+
['filter-a', { action: 'dropdown', options: ['All territories', 'East', 'West'] }],
|
|
101
|
+
['filter-b', { action: 'dropdown', options: ['All visits', 'High', 'Low'] }],
|
|
102
|
+
['filter-c', { action: 'dropdown', options: ['All claims', 'Claimed', 'Open'] }],
|
|
103
|
+
['filter-d', { action: 'dropdown', options: ['Sort', 'Visits', 'Fit'] }],
|
|
104
|
+
]);
|
|
105
|
+
const uniqueTerritoryFilters = selectUniqueActionOwners(territoryFilterDocument, territoryFilterActions);
|
|
106
|
+
assert.deepEqual(uniqueTerritoryFilters.removed, [{ wrapperId: 'filter-bar', ownerIds: ['filter-a', 'filter-b', 'filter-c', 'filter-d'], action: 'dropdown' }], 'must fire: the Territory Map four-button aside yields to every terminal dropdown even when their option payloads differ');
|
|
107
|
+
assert.deepEqual([...uniqueTerritoryFilters.actions.keys()], ['filter-a', 'filter-b', 'filter-c', 'filter-d']);
|
|
108
|
+
const distinctNestedDocument = parseHTML('<div id="parent"><button id="child"></button></div>').document;
|
|
109
|
+
const distinctNestedActions = new Map([
|
|
110
|
+
['parent', { action: 'dropdown', options: ['One', 'Two', 'Three'] }],
|
|
111
|
+
['child', { action: 'modal', modal: { title: 'Child' } }],
|
|
112
|
+
]);
|
|
113
|
+
const uniqueDistinctNested = selectUniqueActionOwners(distinctNestedDocument, distinctNestedActions);
|
|
114
|
+
assert.deepEqual([...uniqueDistinctNested.actions.keys()], ['parent', 'child'], 'must stay silent: distinct nested actions retain the runtime child-wins contract');
|
|
115
|
+
|
|
116
|
+
const shellSample = ({ visualOffset = 0, shellOffset = 0, appearanceSignature = 'same' } = {}) => ({
|
|
117
|
+
appearanceSignature,
|
|
118
|
+
visualGeometry: [[0, 0, 390, 72], [20 + visualOffset, 20, 100, 28]],
|
|
119
|
+
shellGeometry: [shellOffset, 0, 390, 72],
|
|
120
|
+
});
|
|
121
|
+
assert.equal(SHELL_RUNTIME_GEOMETRY_EPSILON_PX, 0.5, 'fixture contract: canonical shell geometry tolerance is half a CSS pixel');
|
|
122
|
+
const subpixelShellComparison = compareCanonicalShellSamples(shellSample(), shellSample({ visualOffset: 0.4 }));
|
|
123
|
+
assert.equal(subpixelShellComparison.equivalent, true, 'must stay silent: a sub-half-pixel glyph measurement drift is visually equivalent');
|
|
124
|
+
assert.ok(Math.abs(subpixelShellComparison.maxGeometryDelta - 0.4) < 1e-9, 'fixture contract: the comparator reports the measured subpixel drift');
|
|
125
|
+
assert.deepEqual(compareCanonicalShellSamples(shellSample(), shellSample({ visualOffset: 24 })), {
|
|
126
|
+
equivalent: false,
|
|
127
|
+
reason: 'geometry',
|
|
128
|
+
maxGeometryDelta: 24,
|
|
129
|
+
}, 'must fire: the b13-class displaced header descendant remains a canonical-shell mismatch');
|
|
130
|
+
assert.deepEqual(compareCanonicalShellSamples(shellSample(), shellSample({ appearanceSignature: 'changed' })), {
|
|
131
|
+
equivalent: false,
|
|
132
|
+
reason: 'appearance',
|
|
133
|
+
maxGeometryDelta: null,
|
|
134
|
+
}, 'must fire: geometry tolerance never conceals structural or style differences');
|
|
135
|
+
|
|
72
136
|
const headerNav = { containerId: 'nav', region: 'header', semantic: true, items: 8, share: 0.8, box: [0, 40, 1536, 76] };
|
|
73
137
|
const announcement = { containerId: 'announcement', region: 'header', semantic: false, items: 3, share: 1, box: [0, 0, 1536, 40] };
|
|
74
138
|
const footer = { containerId: 'footer', region: 'footer', semantic: true, items: 5, share: 0.8, box: [0, 900, 1536, 124] };
|
|
@@ -205,4 +269,6 @@ assert.equal(fireResult.bands[0].regions[0].active.navText, 'Menu');
|
|
|
205
269
|
assert.equal(fireResult.bands[0].regions[0].shellId, isolatedHeader.id);
|
|
206
270
|
assert.equal(fireResult.bands[0].regions[0].active.markerId, isolatedMarker.id);
|
|
207
271
|
|
|
272
|
+
checkAlignRule();
|
|
273
|
+
|
|
208
274
|
process.stdout.write('rules: ok\n');
|
|
@@ -8,9 +8,9 @@ import { checkRuntime } from '../lib/runtime-check.mjs';
|
|
|
8
8
|
import { startStaticServer } from '../lib/static-server.mjs';
|
|
9
9
|
import { buildPrototype } from './lib/run-process.mjs';
|
|
10
10
|
|
|
11
|
-
const
|
|
12
|
-
const unexpectedArgs = process.argv.slice(2).filter((arg) => arg !== '--
|
|
13
|
-
if (unexpectedArgs.length > 0) throw new Error(`usage: shell-stitching-runtime.mjs [--
|
|
11
|
+
const reintroduceDefect = process.argv.includes('--reintroduce-defect');
|
|
12
|
+
const unexpectedArgs = process.argv.slice(2).filter((arg) => arg !== '--reintroduce-defect');
|
|
13
|
+
if (unexpectedArgs.length > 0) throw new Error(`usage: shell-stitching-runtime.mjs [--reintroduce-defect]`);
|
|
14
14
|
|
|
15
15
|
const testsDir = path.dirname(new URL(import.meta.url).pathname);
|
|
16
16
|
const kitDir = path.resolve(testsDir, '..');
|
|
@@ -49,6 +49,13 @@ try {
|
|
|
49
49
|
bandHeight(inventory, 'p2-canvas-control', 'mobile-390'),
|
|
50
50
|
], [900, 1400], 'fixture contract: the portrait control has a substantially taller canvas');
|
|
51
51
|
|
|
52
|
+
if (reintroduceDefect) {
|
|
53
|
+
const collisionFile = path.join(built.protoDir, 'id-collision.html');
|
|
54
|
+
const collisionHtml = fs.readFileSync(collisionFile, 'utf8');
|
|
55
|
+
const displaced = '<style id="reintroduced-b13-defect">@media(max-width:600px){[data-proto-shell-source-id="mobile-390-header-brand"]{transform:translateX(24px)!important}}</style>';
|
|
56
|
+
fs.writeFileSync(collisionFile, collisionHtml.replace('</head>', `${displaced}</head>`));
|
|
57
|
+
}
|
|
58
|
+
|
|
52
59
|
server = await startStaticServer(runWorkDir);
|
|
53
60
|
browser = await chromium.launch();
|
|
54
61
|
const audit = await checkRuntime({
|
|
@@ -77,11 +84,11 @@ try {
|
|
|
77
84
|
assert.equal(collision1536.actualSignature, donor1536.actualSignature, 'must stay silent: distinct native identities render the same canonical header');
|
|
78
85
|
|
|
79
86
|
const groups = audit.shellConsistencyFailures.map((failure) => failure.group);
|
|
80
|
-
if (
|
|
81
|
-
assert.deepEqual(groups, ['390:mobile-390:header'], '
|
|
82
|
-
assert.notEqual(collision390.actualSignature, donor390.actualSignature, 'must fire
|
|
87
|
+
if (reintroduceDefect) {
|
|
88
|
+
assert.deepEqual(groups, ['390:mobile-390:header'], 'reintroduced b13 evidence must isolate one canonical collision to the mobile header');
|
|
89
|
+
assert.notEqual(collision390.actualSignature, donor390.actualSignature, 'must fire with the defect present: a 24px recipient displacement alters transplanted donor descendants');
|
|
83
90
|
const failure = audit.shellConsistencyFailures[0];
|
|
84
|
-
assert.equal(failure.reason, 'prototype-shell-signature-mismatch', '
|
|
91
|
+
assert.equal(failure.reason, 'prototype-shell-signature-mismatch', 'reintroduced evidence is the rendered canonical-shell mismatch');
|
|
85
92
|
assert.deepEqual(failure.pages, [
|
|
86
93
|
{ file: 'donor.html', signature: donor390.actualSignature, geometry: donor390.actualGeometry },
|
|
87
94
|
{ file: 'id-collision.html', signature: collision390.actualSignature, geometry: collision390.actualGeometry },
|
|
@@ -92,7 +99,7 @@ try {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
process.stdout.write(JSON.stringify({
|
|
95
|
-
expectation:
|
|
102
|
+
expectation: reintroduceDefect ? 'reintroduced-defect-failure' : 'post-fix-pass',
|
|
96
103
|
groups,
|
|
97
104
|
mobile: {
|
|
98
105
|
donor: donor390.actualSignature,
|