@12ui/design 0.2.47 → 0.2.49
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-execution.js +2 -2
- package/dist/branch-execution.js.map +1 -1
- package/dist/branch-page-conversion.d.ts +4 -1
- package/dist/branch-page-conversion.d.ts.map +1 -1
- package/dist/branch-page-conversion.js +27 -17
- package/dist/branch-page-conversion.js.map +1 -1
- package/dist/branch-run-record.d.ts +2 -0
- package/dist/branch-run-record.d.ts.map +1 -1
- package/dist/branch-run-record.js.map +1 -1
- package/dist/cli-branch-command.d.ts +2 -0
- package/dist/cli-branch-command.d.ts.map +1 -1
- package/dist/cli-branch-command.js +3 -0
- package/dist/cli-branch-command.js.map +1 -1
- package/dist/cli-convert-command.js +2 -2
- package/dist/cli-convert-command.js.map +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 +21 -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-help/meta.d.ts +10 -0
- package/dist/cli-help/meta.d.ts.map +1 -1
- package/dist/cli-help/meta.js +20 -0
- package/dist/cli-help/meta.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 +520 -0
- package/dist/cli-redesign-command.js.map +1 -0
- package/dist/cli-run.d.ts.map +1 -1
- package/dist/cli-run.js +16 -0
- package/dist/cli-run.js.map +1 -1
- package/dist/cli-spend-command.d.ts +20 -0
- package/dist/cli-spend-command.d.ts.map +1 -0
- package/dist/cli-spend-command.js +46 -0
- package/dist/cli-spend-command.js.map +1 -0
- package/dist/cli-usage.d.ts.map +1 -1
- package/dist/cli-usage.js +9 -1
- package/dist/cli-usage.js.map +1 -1
- package/dist/conversion-receipt-store.d.ts +2 -2
- package/dist/conversion-receipt-store.d.ts.map +1 -1
- package/dist/conversion-receipt-store.js +11 -5
- package/dist/conversion-receipt-store.js.map +1 -1
- package/dist/conversion-receipt.d.ts +2 -3
- package/dist/conversion-receipt.d.ts.map +1 -1
- package/dist/conversion-receipt.js +17 -12
- package/dist/conversion-receipt.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/package-client.d.ts +11 -0
- package/dist/package-client.d.ts.map +1 -1
- package/dist/package-client.js.map +1 -1
- package/dist/package-submission.d.ts +12 -0
- package/dist/package-submission.d.ts.map +1 -1
- package/dist/package-submission.js +5 -0
- package/dist/package-submission.js.map +1 -1
- package/dist/prototype-label-artifact.d.ts +29 -0
- package/dist/prototype-label-artifact.d.ts.map +1 -0
- package/dist/prototype-label-artifact.js +104 -0
- package/dist/prototype-label-artifact.js.map +1 -0
- package/dist/prototype-runner.d.ts +1 -0
- package/dist/prototype-runner.d.ts.map +1 -1
- package/dist/prototype-runner.js +18 -2
- package/dist/prototype-runner.js.map +1 -1
- package/dist/redesign-brand-asset-inventory.d.ts +71 -0
- package/dist/redesign-brand-asset-inventory.d.ts.map +1 -0
- package/dist/redesign-brand-asset-inventory.js +318 -0
- package/dist/redesign-brand-asset-inventory.js.map +1 -0
- package/dist/redesign-capture.d.ts +57 -0
- package/dist/redesign-capture.d.ts.map +1 -0
- package/dist/redesign-capture.js +181 -0
- package/dist/redesign-capture.js.map +1 -0
- package/dist/redesign-pinned-asset.d.ts +20 -0
- package/dist/redesign-pinned-asset.d.ts.map +1 -0
- package/dist/redesign-pinned-asset.js +35 -0
- package/dist/redesign-pinned-asset.js.map +1 -0
- package/dist/redesign-plan-client.d.ts +49 -0
- package/dist/redesign-plan-client.d.ts.map +1 -0
- package/dist/redesign-plan-client.js +90 -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 +16 -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 +142 -0
- package/dist/redesign-workspace.d.ts.map +1 -0
- package/dist/redesign-workspace.js +82 -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 +16 -0
- package/dist/run-journal-commands.js.map +1 -1
- package/dist/spend-client.d.ts +97 -0
- package/dist/spend-client.d.ts.map +1 -0
- package/dist/spend-client.js +31 -0
- package/dist/spend-client.js.map +1 -0
- package/dist/spend-report-lines.d.ts +4 -0
- package/dist/spend-report-lines.d.ts.map +1 -0
- package/dist/spend-report-lines.js +131 -0
- package/dist/spend-report-lines.js.map +1 -0
- 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
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type ConversionConfig } from './config.js';
|
|
2
|
+
/**
|
|
3
|
+
* The account spend contract, as the CLI reads it.
|
|
4
|
+
*
|
|
5
|
+
* The service answers `GET /api/v1/spend` with rollups it computed; the CLI
|
|
6
|
+
* never re-derives a total from the run list, because the run list is capped
|
|
7
|
+
* and the rollups are not. Fields the service may withhold — settled provider
|
|
8
|
+
* cost, which is an operator figure — arrive as null, and the CLI reports them
|
|
9
|
+
* as withheld rather than as zero.
|
|
10
|
+
*/
|
|
11
|
+
export type SpendFundingSource = 'wallet' | 'sponsored' | 'admin' | 'unattributed';
|
|
12
|
+
export type SpendFunctionality = 'convert' | 'draft' | 'branch' | 'search';
|
|
13
|
+
export type SpendCostEvidence = 'provider_reported' | 'estimated' | 'zero_cost_reuse' | 'legacy_unknown';
|
|
14
|
+
export type SpendAmounts = {
|
|
15
|
+
chargedMicros: number;
|
|
16
|
+
listPriceMicros: number;
|
|
17
|
+
providerCostMicros: number | null;
|
|
18
|
+
};
|
|
19
|
+
export type SpendRun = SpendAmounts & {
|
|
20
|
+
runId: string;
|
|
21
|
+
functionality: SpendFunctionality;
|
|
22
|
+
fundingSource: SpendFundingSource;
|
|
23
|
+
sponsorshipGrantId: string | null;
|
|
24
|
+
costEvidence: SpendCostEvidence;
|
|
25
|
+
providerCostEvents: number;
|
|
26
|
+
firstEventAt: string;
|
|
27
|
+
lastEventAt: string;
|
|
28
|
+
};
|
|
29
|
+
export type SpendDay = SpendAmounts & {
|
|
30
|
+
day: string;
|
|
31
|
+
runs: number;
|
|
32
|
+
};
|
|
33
|
+
export type SpendFundingTotal = SpendAmounts & {
|
|
34
|
+
fundingSource: SpendFundingSource;
|
|
35
|
+
sponsorshipGrantId: string | null;
|
|
36
|
+
runs: number;
|
|
37
|
+
};
|
|
38
|
+
export type SpendWallet = {
|
|
39
|
+
status: 'ok';
|
|
40
|
+
accountStatus: string;
|
|
41
|
+
availableMicros: number;
|
|
42
|
+
reservedMicros: number;
|
|
43
|
+
debtMicros: number;
|
|
44
|
+
lifetimeFundedMicros: number;
|
|
45
|
+
lifetimeCapturedMicros: number;
|
|
46
|
+
lifetimeReversedMicros: number;
|
|
47
|
+
} | {
|
|
48
|
+
status: 'unavailable';
|
|
49
|
+
};
|
|
50
|
+
export type SpendQuotaBucket = {
|
|
51
|
+
limit: number;
|
|
52
|
+
used: number;
|
|
53
|
+
remaining: number;
|
|
54
|
+
available: boolean;
|
|
55
|
+
};
|
|
56
|
+
export type SpendQuota = {
|
|
57
|
+
tier: string;
|
|
58
|
+
windowStart: string;
|
|
59
|
+
resetsAt: string;
|
|
60
|
+
buckets: Record<string, SpendQuotaBucket>;
|
|
61
|
+
} | null;
|
|
62
|
+
export type SpendReport = {
|
|
63
|
+
account: {
|
|
64
|
+
accountId: string;
|
|
65
|
+
kind: 'user' | 'organization';
|
|
66
|
+
operator: boolean;
|
|
67
|
+
};
|
|
68
|
+
window: {
|
|
69
|
+
days: number;
|
|
70
|
+
start: string;
|
|
71
|
+
end: string;
|
|
72
|
+
unit: string;
|
|
73
|
+
};
|
|
74
|
+
currency: string;
|
|
75
|
+
providerCostVisibility: 'operator' | 'restricted';
|
|
76
|
+
totals: SpendAmounts & {
|
|
77
|
+
runs: number;
|
|
78
|
+
};
|
|
79
|
+
funding: SpendFundingTotal[];
|
|
80
|
+
days: SpendDay[];
|
|
81
|
+
runs: SpendRun[];
|
|
82
|
+
truncated: boolean;
|
|
83
|
+
wallet: SpendWallet;
|
|
84
|
+
quota: SpendQuota;
|
|
85
|
+
};
|
|
86
|
+
export type SpendRequest = {
|
|
87
|
+
days?: number;
|
|
88
|
+
limit?: number;
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
};
|
|
91
|
+
type Fetch = typeof fetch;
|
|
92
|
+
export declare const fetchAccountSpend: (request?: SpendRequest, options?: {
|
|
93
|
+
config?: ConversionConfig;
|
|
94
|
+
fetch?: Fetch;
|
|
95
|
+
}) => Promise<SpendReport>;
|
|
96
|
+
export {};
|
|
97
|
+
//# sourceMappingURL=spend-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spend-client.d.ts","sourceRoot":"","sources":["../src/spend-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { declareClientVersion } from './client-version.js';
|
|
2
|
+
import { appendClientAuthGuidance } from './client-auth-guidance.js';
|
|
3
|
+
import { readConversionConfig } from './config.js';
|
|
4
|
+
export const fetchAccountSpend = async (request = {}, options = {}) => {
|
|
5
|
+
const config = options.config ?? readConversionConfig();
|
|
6
|
+
const fetchImpl = options.fetch ?? fetch;
|
|
7
|
+
const query = new URLSearchParams();
|
|
8
|
+
if (request.days !== undefined)
|
|
9
|
+
query.set('days', String(request.days));
|
|
10
|
+
if (request.limit !== undefined)
|
|
11
|
+
query.set('limit', String(request.limit));
|
|
12
|
+
const suffix = query.size > 0 ? `?${query.toString()}` : '';
|
|
13
|
+
const headers = declareClientVersion(new Headers({
|
|
14
|
+
accept: 'application/json',
|
|
15
|
+
authorization: `Bearer ${config.apiKey}`,
|
|
16
|
+
}));
|
|
17
|
+
const response = await fetchImpl(`${config.origin}/api/v1/spend${suffix}`, {
|
|
18
|
+
headers,
|
|
19
|
+
...(request.signal ? { signal: request.signal } : {}),
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
const detail = await response.text().catch(() => '');
|
|
23
|
+
throw new Error(appendClientAuthGuidance(`12ui spend request failed (${response.status}): ${detail.slice(0, 500)}`, response.status));
|
|
24
|
+
}
|
|
25
|
+
const body = await response.json().catch(() => null);
|
|
26
|
+
if (!body || typeof body !== 'object' || !body.account || !Array.isArray(body.runs)) {
|
|
27
|
+
throw new Error('12ui returned an unreadable spend report');
|
|
28
|
+
}
|
|
29
|
+
return body;
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=spend-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spend-client.js","sourceRoot":"","sources":["../src/spend-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAyB,MAAM,aAAa,CAAC;AA6F1E,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,EACpC,UAAwB,EAAE,EAC1B,UAAwD,EAAE,EACpC,EAAE;IACxB,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,oBAAoB,EAAE,CAAC;IACxD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,eAAe,EAAE,CAAC;IACpC,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;QAAE,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;IACxE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3E,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,MAAM,OAAO,GAAG,oBAAoB,CAAC,IAAI,OAAO,CAAC;QAC/C,MAAM,EAAE,kBAAkB;QAC1B,aAAa,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;KACzC,CAAC,CAAC,CAAC;IACJ,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,gBAAgB,MAAM,EAAE,EAAE;QACzE,OAAO;QACP,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACtD,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,wBAAwB,CACtC,8BAA8B,QAAQ,CAAC,MAAM,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EACzE,QAAQ,CAAC,MAAM,CAChB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAuB,CAAC;IAC3E,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACpF,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spend-report-lines.d.ts","sourceRoot":"","sources":["../src/spend-report-lines.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAe3B,eAAO,MAAM,eAAe,GAAI,QAAQ,MAAM,GAAG,IAAI,KAAG,MAKvD,CAAC;AAmFF,eAAO,MAAM,gBAAgB,GAAI,QAAQ,WAAW,KAAG,MAAM,EAyD5D,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The human rendering of a spend report, as pure functions over the payload.
|
|
3
|
+
*
|
|
4
|
+
* Formatting lives here rather than in the command so the exact lines an
|
|
5
|
+
* operator reads can be asserted without a network, and so one rule decides how
|
|
6
|
+
* money is written: money is never rounded to a figure that reads as zero when
|
|
7
|
+
* it is not. A $0.0004 embedding call prints as $0.0004, because a ledger that
|
|
8
|
+
* silently rounds spend to $0.00 is the instrument that caused the doubt this
|
|
9
|
+
* command exists to settle.
|
|
10
|
+
*/
|
|
11
|
+
const MICROS_PER_DOLLAR = 1_000_000;
|
|
12
|
+
export const formatUsdMicros = (micros) => {
|
|
13
|
+
if (micros === null)
|
|
14
|
+
return '—';
|
|
15
|
+
const dollars = micros / MICROS_PER_DOLLAR;
|
|
16
|
+
if (micros !== 0 && Math.abs(dollars) < 0.01)
|
|
17
|
+
return `$${dollars.toFixed(4)}`;
|
|
18
|
+
return `$${dollars.toFixed(2)}`;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Labels for the vocabularies the service owns.
|
|
22
|
+
*
|
|
23
|
+
* Both maps are read through {@link labelled}, which falls back to the raw
|
|
24
|
+
* value: the service coins ledger labels as its evidence vocabulary changes,
|
|
25
|
+
* and a report that crashes — or silently blanks a column — on a label this
|
|
26
|
+
* build has not heard of is worse than one that prints the new label as-is.
|
|
27
|
+
*/
|
|
28
|
+
const FUNDING_LABELS = {
|
|
29
|
+
wallet: 'wallet',
|
|
30
|
+
sponsored: 'sponsored',
|
|
31
|
+
admin: 'operator',
|
|
32
|
+
unattributed: 'unattributed',
|
|
33
|
+
};
|
|
34
|
+
const EVIDENCE_LABELS = {
|
|
35
|
+
provider_reported: 'settled',
|
|
36
|
+
estimated: 'estimated',
|
|
37
|
+
zero_cost_reuse: 'reused',
|
|
38
|
+
legacy_unknown: 'unknown',
|
|
39
|
+
};
|
|
40
|
+
const labelled = (labels, value) => (labels[value] ?? value);
|
|
41
|
+
const day = (timestamp) => timestamp.slice(0, 10);
|
|
42
|
+
const padded = (value, width) => value.padEnd(width, ' ');
|
|
43
|
+
const rightPadded = (value, width) => value.padStart(width, ' ');
|
|
44
|
+
/** Money columns are right-aligned so magnitudes line up; labels stay left. */
|
|
45
|
+
const table = (headings, rows, rightAligned) => {
|
|
46
|
+
const widths = headings.map((heading, column) => Math.max(heading.length, ...rows.map((row) => (row[column] ?? '').length)));
|
|
47
|
+
const line = (cells) => cells
|
|
48
|
+
.map((cell, column) => (rightAligned.has(column)
|
|
49
|
+
? rightPadded(cell ?? '', widths[column] ?? 0)
|
|
50
|
+
: padded(cell ?? '', widths[column] ?? 0)))
|
|
51
|
+
.join(' ')
|
|
52
|
+
.trimEnd();
|
|
53
|
+
return [line(headings), ...rows.map(line)];
|
|
54
|
+
};
|
|
55
|
+
const fundingSummary = (report) => {
|
|
56
|
+
if (report.funding.length === 0)
|
|
57
|
+
return 'Funding: nothing settled in this window.';
|
|
58
|
+
const parts = report.funding.map((entry) => {
|
|
59
|
+
const grant = entry.sponsorshipGrantId ? `, grant ${entry.sponsorshipGrantId}` : '';
|
|
60
|
+
return `${labelled(FUNDING_LABELS, entry.fundingSource)} ${formatUsdMicros(entry.chargedMicros)}`
|
|
61
|
+
+ ` (${entry.runs} ${entry.runs === 1 ? 'run' : 'runs'}${grant})`;
|
|
62
|
+
});
|
|
63
|
+
return `Funding: ${parts.join(' · ')}`;
|
|
64
|
+
};
|
|
65
|
+
const walletLine = (report) => {
|
|
66
|
+
const { wallet } = report;
|
|
67
|
+
if (wallet.status !== 'ok') {
|
|
68
|
+
return 'Wallet: unavailable — billing did not answer; ledger figures above are unaffected.';
|
|
69
|
+
}
|
|
70
|
+
return `Wallet: ${formatUsdMicros(wallet.availableMicros)} available,`
|
|
71
|
+
+ ` ${formatUsdMicros(wallet.reservedMicros)} reserved,`
|
|
72
|
+
+ ` lifetime ${formatUsdMicros(wallet.lifetimeFundedMicros)} funded`
|
|
73
|
+
+ ` / ${formatUsdMicros(wallet.lifetimeCapturedMicros)} charged`
|
|
74
|
+
+ (wallet.accountStatus === 'active' ? '' : ` (account ${wallet.accountStatus})`);
|
|
75
|
+
};
|
|
76
|
+
const quotaLine = (report) => {
|
|
77
|
+
const { quota } = report;
|
|
78
|
+
if (!quota)
|
|
79
|
+
return 'Quota: unavailable.';
|
|
80
|
+
const buckets = Object.entries(quota.buckets)
|
|
81
|
+
.map(([bucket, value]) => `${bucket} ${value.used}/${value.limit}`)
|
|
82
|
+
.join(' · ');
|
|
83
|
+
return `Quota (${quota.tier}): ${buckets} — resets ${quota.resetsAt}`;
|
|
84
|
+
};
|
|
85
|
+
export const spendReportLines = (report) => {
|
|
86
|
+
const restricted = report.providerCostVisibility !== 'operator';
|
|
87
|
+
const lines = [
|
|
88
|
+
`Spend for ${report.account.accountId} · last ${report.window.days}`
|
|
89
|
+
+ ` ${report.window.days === 1 ? 'day' : 'days'}`
|
|
90
|
+
+ ` (${day(report.window.start)} to ${day(report.window.end)}, UTC)`,
|
|
91
|
+
`Settled: ${formatUsdMicros(report.totals.chargedMicros)} charged`
|
|
92
|
+
+ ` · ${formatUsdMicros(report.totals.listPriceMicros)} at list price`
|
|
93
|
+
+ (restricted
|
|
94
|
+
? ' · provider cost withheld (operator figure)'
|
|
95
|
+
: ` · ${formatUsdMicros(report.totals.providerCostMicros)} provider cost`)
|
|
96
|
+
+ ` · ${report.totals.runs} ${report.totals.runs === 1 ? 'run' : 'runs'}`,
|
|
97
|
+
fundingSummary(report),
|
|
98
|
+
walletLine(report),
|
|
99
|
+
quotaLine(report),
|
|
100
|
+
];
|
|
101
|
+
if (report.days.length > 0) {
|
|
102
|
+
lines.push('', ...table(['Day', 'Runs', 'Charged', ...(restricted ? [] : ['Provider'])], report.days.map((entry) => [
|
|
103
|
+
entry.day,
|
|
104
|
+
String(entry.runs),
|
|
105
|
+
formatUsdMicros(entry.chargedMicros),
|
|
106
|
+
...(restricted ? [] : [formatUsdMicros(entry.providerCostMicros)]),
|
|
107
|
+
]), new Set(restricted ? [1, 2] : [1, 2, 3])));
|
|
108
|
+
}
|
|
109
|
+
if (report.runs.length > 0) {
|
|
110
|
+
lines.push('', ...table(['Run', 'Work', 'Funding', 'Charged', ...(restricted ? [] : ['Provider']), 'Evidence', 'Last event'], report.runs.map((run) => [
|
|
111
|
+
run.runId,
|
|
112
|
+
run.functionality,
|
|
113
|
+
run.sponsorshipGrantId
|
|
114
|
+
? `${labelled(FUNDING_LABELS, run.fundingSource)} ${run.sponsorshipGrantId}`
|
|
115
|
+
: labelled(FUNDING_LABELS, run.fundingSource),
|
|
116
|
+
formatUsdMicros(run.chargedMicros),
|
|
117
|
+
...(restricted ? [] : [formatUsdMicros(run.providerCostMicros)]),
|
|
118
|
+
labelled(EVIDENCE_LABELS, run.costEvidence),
|
|
119
|
+
run.lastEventAt,
|
|
120
|
+
]), new Set(restricted ? [3] : [3, 4])));
|
|
121
|
+
if (report.truncated) {
|
|
122
|
+
lines.push(`Showing ${report.runs.length} of ${report.totals.runs} runs; totals above cover the`
|
|
123
|
+
+ ' whole window. Raise --limit to list more.');
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
lines.push('', 'No settled work in this window.');
|
|
128
|
+
}
|
|
129
|
+
return lines;
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=spend-report-lines.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spend-report-lines.js","sourceRoot":"","sources":["../src/spend-report-lines.ts"],"names":[],"mappings":"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"}
|
package/open-design.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"specVersion": "1.0.0",
|
|
4
4
|
"name": "12ui-design",
|
|
5
5
|
"title": "12UI Design",
|
|
6
|
-
"version": "0.2.
|
|
6
|
+
"version": "0.2.49",
|
|
7
7
|
"description": "Explore corpus-grounded interface directions, choose one, and convert it into production-ready responsive or native artifacts with 12UI.",
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Just Every",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"command": "npx",
|
|
50
50
|
"args": [
|
|
51
51
|
"-y",
|
|
52
|
-
"@12ui/design@0.2.
|
|
52
|
+
"@12ui/design@0.2.49",
|
|
53
53
|
"mcp"
|
|
54
54
|
]
|
|
55
55
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@12ui/design",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.49",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The canonical 12ui CLI, SDK, MCP server, and visual interface design skills",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"start": "node dist/cli.js",
|
|
24
24
|
"test": "vitest",
|
|
25
25
|
"test:run": "vitest run",
|
|
26
|
-
"test:prototype": "node prototype-kit/polish/tests/rules-check.mjs && node prototype-kit/polish/tests/harm-check.mjs && node prototype-kit/polish/tests/breathing-check.mjs && node prototype-kit/polish/tests/report-check.mjs && node prototype-kit/tests/rules-check.mjs && node prototype-kit/tests/offline-check.mjs && node prototype-kit/tests/shell-stitching-runtime.mjs && node prototype-kit/tests/adversarial-runtime.mjs --assert",
|
|
26
|
+
"test:prototype": "node prototype-kit/polish/tests/rules-check.mjs && node prototype-kit/polish/tests/harm-check.mjs && node prototype-kit/polish/tests/breathing-check.mjs && node prototype-kit/polish/tests/report-check.mjs && node prototype-kit/tests/rules-check.mjs && node prototype-kit/tests/offline-check.mjs && node prototype-kit/tests/shell-stitching-runtime.mjs && node prototype-kit/tests/shell-stitching-runtime.mjs --reintroduce-defect && node prototype-kit/tests/adversarial-runtime.mjs --assert",
|
|
27
27
|
"typecheck": "tsc --noEmit && tsc -p tsconfig.type-tests.json",
|
|
28
28
|
"prepublishOnly": "npm run build"
|
|
29
29
|
},
|
package/prototype-kit/build.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import fs from 'node:fs';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { parseHTML } from 'linkedom';
|
|
6
6
|
import { resolveDropdownAction } from './lib/actions.mjs';
|
|
7
|
-
import { restoreActionPointerReachability } from './lib/action-target.mjs';
|
|
7
|
+
import { restoreActionPointerReachability, selectUniqueActionOwners } from './lib/action-target.mjs';
|
|
8
8
|
import {
|
|
9
9
|
HOLDING_SHELL_ATTRIBUTE,
|
|
10
10
|
holdingBlockGeometry,
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
shellPlacementCss,
|
|
14
14
|
} from './lib/holding-composition.mjs';
|
|
15
15
|
import { injectReviewFraming } from './lib/review-framing.mjs';
|
|
16
|
+
import { removeRemoteHeadLinks } from './lib/local-document.mjs';
|
|
16
17
|
import {
|
|
17
18
|
isolateTransplantedShell,
|
|
18
19
|
rulesFor,
|
|
@@ -156,6 +157,7 @@ const report = {
|
|
|
156
157
|
unbound: [],
|
|
157
158
|
invalidDropdowns: [],
|
|
158
159
|
actionDemotions: [],
|
|
160
|
+
duplicateActionOwners: [],
|
|
159
161
|
shellSizeDelta: [],
|
|
160
162
|
shellAmbiguities: model.shellAmbiguities || [],
|
|
161
163
|
suppressedShellOverlaps: [],
|
|
@@ -234,7 +236,9 @@ function actionsFor(s) {
|
|
|
234
236
|
if (a) acts.set(c.id, a);
|
|
235
237
|
else report.counts.none++;
|
|
236
238
|
}
|
|
237
|
-
|
|
239
|
+
const normalized = selectUniqueActionOwners(loadDoc(s.file).document, acts);
|
|
240
|
+
report.duplicateActionOwners.push(...normalized.removed.map((removal) => ({ state: s.stateId, ...removal })));
|
|
241
|
+
return normalized.actions;
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
function bindAction(el, action) {
|
|
@@ -410,6 +414,7 @@ for (const u of units) {
|
|
|
410
414
|
const cfg = { unit: u.id, states: u.states.map((s) => s.stateId), tokens: model.tokens, contentFile: `${u.id}.json`, activeNav: activeNav?.text || null };
|
|
411
415
|
document.body.insertAdjacentHTML('beforeend', `<script id="proto-state" type="application/json">${JSON.stringify(cfg).replace(/</g, '\\u003c')}</script><script src="proto.js"></script>`);
|
|
412
416
|
if (review) injectReviewFraming(document, review);
|
|
417
|
+
removeRemoteHeadLinks(document);
|
|
413
418
|
fs.writeFileSync(path.join(outDir, outputFileOfUnit(u)), '<!doctype html>\n' + document.documentElement.outerHTML);
|
|
414
419
|
fs.writeFileSync(path.join(outDir, 'content', u.id + '.json'), JSON.stringify(content, null, 1));
|
|
415
420
|
report.units.push({
|
|
@@ -612,6 +617,7 @@ for (const u of units) {
|
|
|
612
617
|
const cfg = { unit: hslug, holding: true, holdingBands: bandDeclarations, tokens: model.tokens, contentFile: null, activeNav: navMatch?.text || null };
|
|
613
618
|
document.body.insertAdjacentHTML('beforeend', `<script id="proto-state" type="application/json">${JSON.stringify(cfg)}</script><script src="proto.js"></script>`);
|
|
614
619
|
if (review) injectReviewFraming(document, review);
|
|
620
|
+
removeRemoteHeadLinks(document);
|
|
615
621
|
fs.writeFileSync(path.join(outDir, outputFile), '<!doctype html>\n' + document.documentElement.outerHTML);
|
|
616
622
|
}
|
|
617
623
|
console.error(`holding pages: ${usedHoldings.size}`);
|
|
@@ -623,5 +629,5 @@ fs.writeFileSync(path.join(outDir, 'index.html'), `<!doctype html><meta http-equ
|
|
|
623
629
|
const manifestHoldings = review
|
|
624
630
|
? [...usedHoldings.entries()].map(([holding, title]) => [holding.replace(/\.html$/, ''), title])
|
|
625
631
|
: [...usedHoldings.entries()];
|
|
626
|
-
fs.writeFileSync(path.join(outDir, 'proto-manifest.json'), JSON.stringify({ units: report.units, nav: navRows.map((n) => ({ text: n.text, target: n.target, holding: n.holding, rowId: n.rowId })), holdings: manifestHoldings, counts: report.counts, unbound: report.unbound, invalidDropdowns: report.invalidDropdowns, ...(report.actionDemotions.length > 0 ? { actionDemotions: report.actionDemotions } : {}), shellSizeDelta: report.shellSizeDelta, shellAmbiguities: report.shellAmbiguities, suppressedShellOverlaps: report.suppressedShellOverlaps, skippedShellTransplants: report.skippedShellTransplants, replacedShellActions: report.replacedShellActions, shellActionRemaps: report.shellActionRemaps, scriptDirections: states.map(({ stateId, scriptDirection, writingMode }) => ({ stateId, scriptDirection, writingMode })), canonicalShell: model.canonicalShell, canonicalShellPage: report.canonicalShellPage, shellConsistency: report.shellConsistency, ...(review ? { review } : {}) }, null, 1));
|
|
632
|
+
fs.writeFileSync(path.join(outDir, 'proto-manifest.json'), JSON.stringify({ units: report.units, nav: navRows.map((n) => ({ text: n.text, target: n.target, holding: n.holding, rowId: n.rowId })), holdings: manifestHoldings, counts: report.counts, unbound: report.unbound, invalidDropdowns: report.invalidDropdowns, ...(report.actionDemotions.length > 0 ? { actionDemotions: report.actionDemotions } : {}), ...(report.duplicateActionOwners.length > 0 ? { duplicateActionOwners: report.duplicateActionOwners } : {}), shellSizeDelta: report.shellSizeDelta, shellAmbiguities: report.shellAmbiguities, suppressedShellOverlaps: report.suppressedShellOverlaps, skippedShellTransplants: report.skippedShellTransplants, replacedShellActions: report.replacedShellActions, shellActionRemaps: report.shellActionRemaps, scriptDirections: states.map(({ stateId, scriptDirection, writingMode }) => ({ stateId, scriptDirection, writingMode })), canonicalShell: model.canonicalShell, canonicalShellPage: report.canonicalShellPage, shellConsistency: report.shellConsistency, ...(review ? { review } : {}) }, null, 1));
|
|
627
633
|
console.error('counts', JSON.stringify(report.counts), 'unbound', report.unbound.length);
|
|
@@ -8,3 +8,29 @@ export function restoreActionPointerReachability(element) {
|
|
|
8
8
|
element.setAttribute('data-proto-pointer-restored', 'true');
|
|
9
9
|
return true;
|
|
10
10
|
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Give one semantic action to one rendered owner. A label response can name
|
|
14
|
+
* both a layout wrapper and one or more controls inside it. When descendants
|
|
15
|
+
* carry the same action kind, only the terminal-most elements are real action
|
|
16
|
+
* targets; retaining the wrapper adds a duplicate listener and an impossible
|
|
17
|
+
* hit-test identity. Different nested action kinds remain independent.
|
|
18
|
+
*/
|
|
19
|
+
export function selectUniqueActionOwners(document, actions) {
|
|
20
|
+
const selected = new Map(actions);
|
|
21
|
+
const removed = [];
|
|
22
|
+
for (const [wrapperId, wrapperAction] of actions) {
|
|
23
|
+
const wrapper = document.getElementById(wrapperId);
|
|
24
|
+
if (!wrapper) continue;
|
|
25
|
+
const descendants = [...wrapper.querySelectorAll('[id]')].filter((candidate) => (
|
|
26
|
+
actions.get(candidate.id)?.action === wrapperAction.action
|
|
27
|
+
));
|
|
28
|
+
if (descendants.length === 0) continue;
|
|
29
|
+
const ownerIds = descendants.filter((candidate) => ![...candidate.querySelectorAll('[id]')].some((nested) => (
|
|
30
|
+
actions.get(nested.id)?.action === wrapperAction.action
|
|
31
|
+
))).map((candidate) => candidate.id);
|
|
32
|
+
selected.delete(wrapperId);
|
|
33
|
+
removed.push({ wrapperId, ownerIds, action: wrapperAction.action });
|
|
34
|
+
}
|
|
35
|
+
return { actions: selected, removed };
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const REMOTE_HREF = /^(?:https?:)?\/\//iu;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prototype files are owner-review artifacts that must open directly from
|
|
5
|
+
* disk. Remote head links turn an otherwise self-contained page into a noisy
|
|
6
|
+
* runtime failure when the review browser correctly blocks the network.
|
|
7
|
+
*/
|
|
8
|
+
export function removeRemoteHeadLinks(document) {
|
|
9
|
+
let removed = 0;
|
|
10
|
+
for (const link of document.querySelectorAll('head link[href]')) {
|
|
11
|
+
if (!REMOTE_HREF.test(link.getAttribute('href')?.trim() ?? '')) continue;
|
|
12
|
+
link.remove();
|
|
13
|
+
removed += 1;
|
|
14
|
+
}
|
|
15
|
+
return removed;
|
|
16
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
|
|
4
|
+
import {
|
|
5
|
+
compareCanonicalShellSamples,
|
|
6
|
+
SHELL_RUNTIME_GEOMETRY_EPSILON_PX,
|
|
7
|
+
} from './shell-runtime-equivalence.mjs';
|
|
8
|
+
|
|
4
9
|
function errorText(error) {
|
|
5
10
|
return error instanceof Error ? error.message : String(error);
|
|
6
11
|
}
|
|
@@ -21,7 +26,23 @@ function configuredWidths(protoDir, file) {
|
|
|
21
26
|
async function smokeInteraction(page, selector, assertion) {
|
|
22
27
|
const locator = page.locator(selector).first();
|
|
23
28
|
if (!await locator.count()) return { present: false, ok: true };
|
|
29
|
+
let reviewClearanceAdded = false;
|
|
24
30
|
try {
|
|
31
|
+
reviewClearanceAdded = await locator.evaluate((element) => {
|
|
32
|
+
const framing = document.getElementById('proto-review-framing');
|
|
33
|
+
if (!framing) return false;
|
|
34
|
+
const box = framing.getBoundingClientRect();
|
|
35
|
+
const target = element.getBoundingClientRect();
|
|
36
|
+
const overlaps = Math.min(box.right, target.right) - Math.max(box.left, target.left) > 0
|
|
37
|
+
&& Math.min(box.bottom, target.bottom) - Math.max(box.top, target.top) > 0;
|
|
38
|
+
if (!overlaps) return false;
|
|
39
|
+
const clearance = document.createElement('div');
|
|
40
|
+
clearance.setAttribute('data-proto-runtime-clearance', 'true');
|
|
41
|
+
clearance.setAttribute('aria-hidden', 'true');
|
|
42
|
+
clearance.style.cssText = `display:block;position:relative;flex:none;width:1px;height:${box.height + 36}px;pointer-events:none`;
|
|
43
|
+
document.body.appendChild(clearance);
|
|
44
|
+
return true;
|
|
45
|
+
});
|
|
25
46
|
await locator.scrollIntoViewIfNeeded();
|
|
26
47
|
// Playwright considers an element "in view" even when a fixed review
|
|
27
48
|
// panel covers every point in its box. Center it before hit-testing so
|
|
@@ -55,6 +76,10 @@ async function smokeInteraction(page, selector, assertion) {
|
|
|
55
76
|
return { present: true, ok, point, opened };
|
|
56
77
|
} catch (error) {
|
|
57
78
|
return { present: true, ok: false, error: errorText(error) };
|
|
79
|
+
} finally {
|
|
80
|
+
if (reviewClearanceAdded) {
|
|
81
|
+
await page.locator('[data-proto-runtime-clearance="true"]').evaluateAll((nodes) => nodes.forEach((node) => node.remove())).catch(() => null);
|
|
82
|
+
}
|
|
58
83
|
}
|
|
59
84
|
}
|
|
60
85
|
|
|
@@ -286,7 +311,12 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
286
311
|
const host = screen?.querySelector('.ly-anchor-canvas') || screen;
|
|
287
312
|
const hostZoom = host ? parseFloat(getComputedStyle(host).zoom) || 1 : 1;
|
|
288
313
|
const round = (value) => Math.round(value * 10) / 10;
|
|
289
|
-
const
|
|
314
|
+
const digestJson = async (value) => {
|
|
315
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value));
|
|
316
|
+
const digest = await crypto.subtle.digest('SHA-256', bytes);
|
|
317
|
+
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
318
|
+
};
|
|
319
|
+
const visualSample = async (shell, declaration) => {
|
|
290
320
|
const rootBox = shell.getBoundingClientRect();
|
|
291
321
|
const markerIds = new Set(declaration.activeMarkerIds || []);
|
|
292
322
|
const visualNodes = [shell, ...shell.querySelectorAll('*')]
|
|
@@ -306,10 +336,10 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
306
336
|
label: node.getAttribute('alt') || node.getAttribute('aria-label') || '',
|
|
307
337
|
source: node.getAttribute('src') || '',
|
|
308
338
|
box: [
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
339
|
+
(box.left - rootBox.left) / hostZoom,
|
|
340
|
+
(box.top - rootBox.top) / hostZoom,
|
|
341
|
+
box.width / hostZoom,
|
|
342
|
+
box.height / hostZoom,
|
|
313
343
|
],
|
|
314
344
|
style: [
|
|
315
345
|
style.display, node === shell ? 'shell-root' : style.position,
|
|
@@ -319,9 +349,13 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
319
349
|
],
|
|
320
350
|
};
|
|
321
351
|
}).filter((node) => node.box[2] > 0 && node.box[3] > 0);
|
|
322
|
-
const
|
|
323
|
-
const
|
|
324
|
-
return
|
|
352
|
+
const appearanceNodes = visualNodes.map(({ box, ...appearance }) => appearance);
|
|
353
|
+
const diagnosticNodes = visualNodes.map((node) => ({ ...node, box: node.box.map(round) }));
|
|
354
|
+
return {
|
|
355
|
+
appearanceSignature: await digestJson({ region: declaration.region, visualNodes: appearanceNodes }),
|
|
356
|
+
diagnosticSignature: await digestJson({ region: declaration.region, visualNodes: diagnosticNodes }),
|
|
357
|
+
geometry: visualNodes.map((node) => node.box),
|
|
358
|
+
};
|
|
325
359
|
};
|
|
326
360
|
for (const declaration of declared.regions || []) {
|
|
327
361
|
const shell = document.getElementById(declaration.shellId);
|
|
@@ -329,15 +363,16 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
329
363
|
failures.push({ kind: 'shell-consistency', region: declaration.region, reason: 'missing-declared-shell' });
|
|
330
364
|
continue;
|
|
331
365
|
}
|
|
332
|
-
const
|
|
366
|
+
const sample = await visualSample(shell, declaration);
|
|
333
367
|
const shellBox = shell.getBoundingClientRect();
|
|
334
368
|
const hostBox = host?.getBoundingClientRect() || { left: 0, top: 0 };
|
|
335
|
-
const
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
369
|
+
const comparisonGeometry = [
|
|
370
|
+
(shellBox.left - hostBox.left) / hostZoom,
|
|
371
|
+
(shellBox.top - hostBox.top) / hostZoom,
|
|
372
|
+
shellBox.width / hostZoom,
|
|
373
|
+
shellBox.height / hostZoom,
|
|
340
374
|
];
|
|
375
|
+
const actualGeometry = comparisonGeometry.map(round);
|
|
341
376
|
let activeMatches = true;
|
|
342
377
|
if (declaration.active?.status === 'linked') {
|
|
343
378
|
const marker = document.getElementById(declaration.active.markerId);
|
|
@@ -364,8 +399,11 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
364
399
|
regions.push({
|
|
365
400
|
region: declaration.region,
|
|
366
401
|
shellId: declaration.shellId,
|
|
367
|
-
actualSignature,
|
|
402
|
+
actualSignature: sample.diagnosticSignature,
|
|
368
403
|
actualGeometry,
|
|
404
|
+
appearanceSignature: sample.appearanceSignature,
|
|
405
|
+
visualGeometry: sample.geometry,
|
|
406
|
+
comparisonGeometry,
|
|
369
407
|
inventorySampleSignature: declaration.canonicalSignature,
|
|
370
408
|
consistencyStatus: declaration.consistencyStatus || 'canonical',
|
|
371
409
|
activeStatus: declaration.active?.status || 'silent',
|
|
@@ -427,23 +465,48 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
427
465
|
.map((failure) => ({ file: page.file, width: viewport.width, ...failure })) || []
|
|
428
466
|
)));
|
|
429
467
|
const shellRuntimeGroups = new Map();
|
|
468
|
+
const shellConsistencyTolerances = [];
|
|
430
469
|
for (const page of pages) for (const viewport of page.viewports) {
|
|
431
470
|
if (!unitByFile.has(page.file)) continue;
|
|
432
471
|
for (const region of viewport.shellState?.regions || []) {
|
|
433
472
|
if (region.consistencyStatus !== 'canonical') continue;
|
|
434
473
|
const key = `${viewport.width}:${viewport.shellState.activeBand}:${region.region}`;
|
|
435
474
|
if (!shellRuntimeGroups.has(key)) shellRuntimeGroups.set(key, []);
|
|
436
|
-
shellRuntimeGroups.get(key).push({
|
|
475
|
+
shellRuntimeGroups.get(key).push({
|
|
476
|
+
file: page.file,
|
|
477
|
+
signature: region.actualSignature,
|
|
478
|
+
geometry: region.actualGeometry,
|
|
479
|
+
appearanceSignature: region.appearanceSignature,
|
|
480
|
+
visualGeometry: region.visualGeometry,
|
|
481
|
+
shellGeometry: region.comparisonGeometry,
|
|
482
|
+
});
|
|
437
483
|
}
|
|
438
484
|
}
|
|
439
485
|
for (const [key, entries] of shellRuntimeGroups) {
|
|
440
|
-
const
|
|
441
|
-
|
|
486
|
+
const reference = entries[0];
|
|
487
|
+
const comparisons = entries.slice(1).map((entry) => compareCanonicalShellSamples(reference, entry));
|
|
488
|
+
const equivalent = comparisons.every((comparison) => comparison.equivalent);
|
|
489
|
+
const pagesInGroup = entries.map(({ file, signature, geometry }) => ({ file, signature, geometry }));
|
|
490
|
+
if (!equivalent) shellConsistencyFailures.push({
|
|
442
491
|
kind: 'shell-consistency',
|
|
443
492
|
reason: 'prototype-shell-signature-mismatch',
|
|
444
493
|
group: key,
|
|
445
|
-
pages:
|
|
494
|
+
pages: pagesInGroup,
|
|
446
495
|
});
|
|
496
|
+
else {
|
|
497
|
+
const diagnosticFingerprints = new Set(entries.map((entry) => JSON.stringify([entry.signature, entry.geometry])));
|
|
498
|
+
if (diagnosticFingerprints.size > 1) shellConsistencyTolerances.push({
|
|
499
|
+
group: key,
|
|
500
|
+
epsilonPx: SHELL_RUNTIME_GEOMETRY_EPSILON_PX,
|
|
501
|
+
maxGeometryDelta: Math.max(0, ...comparisons.map((comparison) => comparison.maxGeometryDelta || 0)),
|
|
502
|
+
pages: pagesInGroup,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
for (const page of pages) for (const viewport of page.viewports) for (const region of viewport.shellState?.regions || []) {
|
|
507
|
+
delete region.appearanceSignature;
|
|
508
|
+
delete region.visualGeometry;
|
|
509
|
+
delete region.comparisonGeometry;
|
|
447
510
|
}
|
|
448
511
|
const activeNavigationFailures = pages.flatMap((page) => page.viewports.flatMap((viewport) => (
|
|
449
512
|
viewport.shellState?.failures?.filter((failure) => failure.kind === 'active-navigation')
|
|
@@ -458,6 +521,7 @@ export async function checkRuntime({ browser, baseUrl, relativeProtoPath, protoD
|
|
|
458
521
|
interactionFailures: pages.filter((page) => page.dropdown?.ok === false || page.modal?.ok === false).map((page) => page.file),
|
|
459
522
|
compositionFailures,
|
|
460
523
|
shellConsistencyFailures,
|
|
524
|
+
shellConsistencyTolerances,
|
|
461
525
|
activeNavigationFailures,
|
|
462
526
|
deepLinkBack,
|
|
463
527
|
};
|