@exponential_app/eui 0.0.0-stage → 0.2.1
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/LICENSE +201 -0
- package/NOTICE +54 -0
- package/README.md +377 -2
- package/catalog/a11y.json +705 -0
- package/catalog/basic-map.json +104 -0
- package/catalog/code.json +58 -0
- package/catalog/core.catalog.json +5190 -0
- package/catalog/core.schema.json +24542 -0
- package/catalog/host.json +95 -0
- package/catalog/layout.json +20 -0
- package/catalog/limits.json +9 -0
- package/catalog/locale.json +48 -0
- package/catalog/macros.json +4152 -0
- package/catalog/recipes.json +64 -0
- package/catalog/strings.json +84 -0
- package/catalog/style.json +180 -0
- package/catalog/theme.schema.json +47754 -0
- package/catalog/tokens.json +47 -0
- package/conformance/README.md +218 -0
- package/conformance/baseline.ts +101 -0
- package/conformance/browser-page.ts +146 -0
- package/conformance/browser-suites.ts +699 -0
- package/conformance/compare.ts +201 -0
- package/conformance/dom.ts +97 -0
- package/conformance/dump.ts +163 -0
- package/conformance/fonts.json +157 -0
- package/conformance/manifest.json +233 -0
- package/conformance/parity-compare.ts +191 -0
- package/conformance/parity-report.schema.json +73 -0
- package/conformance/parity.ts +412 -0
- package/conformance/pixel.ts +177 -0
- package/conformance/primitives-page.ts +49 -0
- package/conformance/report.schema.json +66 -0
- package/conformance/run.ts +118 -0
- package/conformance/voice-wave-child.ts +102 -0
- package/dist/chunks/index-5fmaa0xq.js +8384 -0
- package/dist/chunks/index-5fmaa0xq.js.map +10 -0
- package/dist/chunks/index-62yzr94z.js +2784 -0
- package/dist/chunks/index-62yzr94z.js.map +10 -0
- package/dist/chunks/index-bg2jsgjq.js +8541 -0
- package/dist/chunks/index-bg2jsgjq.js.map +15 -0
- package/dist/chunks/index-fy7y2nz8.js +186 -0
- package/dist/chunks/index-fy7y2nz8.js.map +9 -0
- package/dist/chunks/index-gtk2gwsb.js +4555 -0
- package/dist/chunks/index-gtk2gwsb.js.map +31 -0
- package/dist/chunks/index-t6qz1770.js +95 -0
- package/dist/chunks/index-t6qz1770.js.map +11 -0
- package/dist/conformance.js +230462 -0
- package/dist/conformance.js.map +10 -0
- package/dist/entries/prompt.js +251 -0
- package/dist/entries/prompt.js.map +11 -0
- package/dist/entries/schema.js +1725 -0
- package/dist/entries/schema.js.map +11 -0
- package/dist/host/index.js +194 -0
- package/dist/host/index.js.map +9 -0
- package/dist/index.js +3851 -0
- package/dist/index.js.map +30 -0
- package/dist/types/src/a11y.d.ts +21 -0
- package/dist/types/src/animation.d.ts +76 -0
- package/dist/types/src/basic-map.d.ts +38 -0
- package/dist/types/src/builder.d.ts +50 -0
- package/dist/types/src/catalog.d.ts +54 -0
- package/dist/types/src/catalog.described.d.ts +33 -0
- package/dist/types/src/catalog.generated.d.ts +904 -0
- package/dist/types/src/chart.d.ts +46 -0
- package/dist/types/src/code.d.ts +44 -0
- package/dist/types/src/color.d.ts +20 -0
- package/dist/types/src/conformance.d.ts +39 -0
- package/dist/types/src/connector/app.d.ts +14 -0
- package/dist/types/src/connector/connector.d.ts +77 -0
- package/dist/types/src/connector/index.d.ts +4 -0
- package/dist/types/src/css.d.ts +27 -0
- package/dist/types/src/define.d.ts +66 -0
- package/dist/types/src/direction.d.ts +16 -0
- package/dist/types/src/dynamic.d.ts +161 -0
- package/dist/types/src/entries/prompt.d.ts +11 -0
- package/dist/types/src/entries/schema.d.ts +8 -0
- package/dist/types/src/expr.d.ts +71 -0
- package/dist/types/src/extension.d.ts +4 -0
- package/dist/types/src/format.d.ts +112 -0
- package/dist/types/src/geometry.d.ts +40 -0
- package/dist/types/src/host/contract.d.ts +225 -0
- package/dist/types/src/host/decoders.d.ts +61 -0
- package/dist/types/src/host/index.d.ts +15 -0
- package/dist/types/src/host/package.d.ts +102 -0
- package/dist/types/src/host/policy.d.ts +107 -0
- package/dist/types/src/host/record-store.d.ts +103 -0
- package/dist/types/src/host/records.d.ts +45 -0
- package/dist/types/src/host/router.d.ts +38 -0
- package/dist/types/src/host/runtime.d.ts +213 -0
- package/dist/types/src/host/signature.d.ts +52 -0
- package/dist/types/src/host/sources.d.ts +22 -0
- package/dist/types/src/host/tools.d.ts +42 -0
- package/dist/types/src/host/transports.d.ts +109 -0
- package/dist/types/src/index.d.ts +70 -0
- package/dist/types/src/layout.d.ts +20 -0
- package/dist/types/src/limits.d.ts +21 -0
- package/dist/types/src/list.d.ts +133 -0
- package/dist/types/src/locale.d.ts +22 -0
- package/dist/types/src/macros.d.ts +29 -0
- package/dist/types/src/overlay.d.ts +27 -0
- package/dist/types/src/prompt.d.ts +44 -0
- package/dist/types/src/qr-code.d.ts +30 -0
- package/dist/types/src/recipes.d.ts +17 -0
- package/dist/types/src/reducer.d.ts +41 -0
- package/dist/types/src/resizable.d.ts +38 -0
- package/dist/types/src/schema.d.ts +7 -0
- package/dist/types/src/strings.d.ts +13 -0
- package/dist/types/src/style.d.ts +201 -0
- package/dist/types/src/test-preload.d.ts +1 -0
- package/dist/types/src/text-clusters.d.ts +11 -0
- package/dist/types/src/text-shimmer.d.ts +24 -0
- package/dist/types/src/theme-types.d.ts +162 -0
- package/dist/types/src/theme.d.ts +75 -0
- package/dist/types/src/themes.d.ts +22 -0
- package/dist/types/src/tree-guides.d.ts +17 -0
- package/dist/types/src/types.d.ts +320 -0
- package/dist/types/src/validate.d.ts +31 -0
- package/dist/types/src/voice-wave.d.ts +181 -0
- package/dist/types/src/web/base-css.d.ts +1 -0
- package/dist/types/src/web/box-css.d.ts +63 -0
- package/dist/types/src/web/coerce.d.ts +32 -0
- package/dist/types/src/web/custom-icons.generated.d.ts +4 -0
- package/dist/types/src/web/data.d.ts +107 -0
- package/dist/types/src/web/dates.d.ts +12 -0
- package/dist/types/src/web/form.d.ts +44 -0
- package/dist/types/src/web/host-types.d.ts +126 -0
- package/dist/types/src/web/index.d.ts +35 -0
- package/dist/types/src/web/inputs.d.ts +55 -0
- package/dist/types/src/web/list.d.ts +16 -0
- package/dist/types/src/web/listbox.d.ts +14 -0
- package/dist/types/src/web/markdown.d.ts +96 -0
- package/dist/types/src/web/media.d.ts +63 -0
- package/dist/types/src/web/parts.d.ts +4 -0
- package/dist/types/src/web/primitives/alert-dialog.d.ts +10 -0
- package/dist/types/src/web/primitives/alert.d.ts +9 -0
- package/dist/types/src/web/primitives/avatar-color.d.ts +2 -0
- package/dist/types/src/web/primitives/avatar.d.ts +13 -0
- package/dist/types/src/web/primitives/badge.d.ts +10 -0
- package/dist/types/src/web/primitives/breadcrumb.d.ts +10 -0
- package/dist/types/src/web/primitives/button.d.ts +6 -0
- package/dist/types/src/web/primitives/calendar.d.ts +43 -0
- package/dist/types/src/web/primitives/card.d.ts +9 -0
- package/dist/types/src/web/primitives/checkbox.d.ts +5 -0
- package/dist/types/src/web/primitives/choice-tiles.d.ts +32 -0
- package/dist/types/src/web/primitives/cn.d.ts +3 -0
- package/dist/types/src/web/primitives/code-block.d.ts +24 -0
- package/dist/types/src/web/primitives/command.d.ts +134 -0
- package/dist/types/src/web/primitives/context-ring.d.ts +24 -0
- package/dist/types/src/web/primitives/dialog-arms.d.ts +6 -0
- package/dist/types/src/web/primitives/dialog.d.ts +24 -0
- package/dist/types/src/web/primitives/disclosure-header.d.ts +5 -0
- package/dist/types/src/web/primitives/dock.d.ts +42 -0
- package/dist/types/src/web/primitives/dropdown-menu.d.ts +14 -0
- package/dist/types/src/web/primitives/empty-state.d.ts +15 -0
- package/dist/types/src/web/primitives/glass-card.d.ts +4 -0
- package/dist/types/src/web/primitives/hover-card.d.ts +7 -0
- package/dist/types/src/web/primitives/icon-disc.d.ts +12 -0
- package/dist/types/src/web/primitives/index.d.ts +50 -0
- package/dist/types/src/web/primitives/input.d.ts +5 -0
- package/dist/types/src/web/primitives/kbd.d.ts +4 -0
- package/dist/types/src/web/primitives/label.d.ts +3 -0
- package/dist/types/src/web/primitives/measure.d.ts +16 -0
- package/dist/types/src/web/primitives/menu-surface.d.ts +17 -0
- package/dist/types/src/web/primitives/meter.d.ts +21 -0
- package/dist/types/src/web/primitives/popover.d.ts +3 -0
- package/dist/types/src/web/primitives/progress-ring.d.ts +5 -0
- package/dist/types/src/web/primitives/progress.d.ts +8 -0
- package/dist/types/src/web/primitives/scroll-area.d.ts +49 -0
- package/dist/types/src/web/primitives/segmented-control.d.ts +5 -0
- package/dist/types/src/web/primitives/select.d.ts +47 -0
- package/dist/types/src/web/primitives/separator.d.ts +3 -0
- package/dist/types/src/web/primitives/sheet-chrome.d.ts +4 -0
- package/dist/types/src/web/primitives/sheet.d.ts +20 -0
- package/dist/types/src/web/primitives/skeleton.d.ts +3 -0
- package/dist/types/src/web/primitives/spinner.d.ts +3 -0
- package/dist/types/src/web/primitives/switch.d.ts +4 -0
- package/dist/types/src/web/primitives/table.d.ts +11 -0
- package/dist/types/src/web/primitives/tabs.d.ts +13 -0
- package/dist/types/src/web/primitives/textarea.d.ts +3 -0
- package/dist/types/src/web/primitives/toast.d.ts +163 -0
- package/dist/types/src/web/primitives/tooltip.d.ts +3 -0
- package/dist/types/src/web/primitives/truncate.d.ts +7 -0
- package/dist/types/src/web/primitives/typeahead-portal.d.ts +12 -0
- package/dist/types/src/web/primitives/voice-wave.d.ts +50 -0
- package/dist/types/src/web/round5.d.ts +107 -0
- package/dist/types/src/web/share.d.ts +6 -0
- package/dist/types/src/web/table.d.ts +20 -0
- package/dist/types/src/web/theme-css.d.ts +69 -0
- package/dist/types/src/web/urls.d.ts +21 -0
- package/dist/web/index.js +2565 -0
- package/dist/web/index.js.map +28 -0
- package/dist/web/primitives/index.js +1476 -0
- package/dist/web/primitives/index.js.map +57 -0
- package/docs/components.generated.json +6783 -0
- package/docs/package-format.md +182 -0
- package/docs/round-1-contract.md +679 -0
- package/docs/round-2-contract.md +476 -0
- package/docs/round-3-contract.md +174 -0
- package/docs/round-5-contract.md +268 -0
- package/docs/themes.generated.json +3647 -0
- package/fixtures/animations.json +2890 -0
- package/fixtures/bench-list.json +58 -0
- package/fixtures/bind-time.json +54734 -0
- package/fixtures/catalog-basic-map.json +1692 -0
- package/fixtures/catalog-components.json +10293 -0
- package/fixtures/catalog-extension.json +620 -0
- package/fixtures/catalog-macros.json +51805 -0
- package/fixtures/code-tokens.json +1993 -0
- package/fixtures/conformance-baseline.json +59016 -0
- package/fixtures/conformance-cases.json +49 -0
- package/fixtures/conformance-known.json +2111 -0
- package/fixtures/control-geometry.json +5129 -0
- package/fixtures/dashboard.data.json +74 -0
- package/fixtures/dashboard.json +781 -0
- package/fixtures/demo-surface.json +47 -0
- package/fixtures/format.json +870 -0
- package/fixtures/host-package.json +1262 -0
- package/fixtures/host-policy.json +1209 -0
- package/fixtures/host-router.json +2034 -0
- package/fixtures/host-transport.json +590 -0
- package/fixtures/interactions-round1.json +1058 -0
- package/fixtures/kitchen-sink.expanded.json +11972 -0
- package/fixtures/kitchen-sink.json +3140 -0
- package/fixtures/layout-geometry-round1.json +7278 -0
- package/fixtures/layout-geometry.json +2072 -0
- package/fixtures/overlay-geometry.json +272 -0
- package/fixtures/pointer.json +89 -0
- package/fixtures/primitive-specimens.json +59 -0
- package/fixtures/prompt-budget.json +33 -0
- package/fixtures/qr-code.json +480 -0
- package/fixtures/resizable.json +714 -0
- package/fixtures/round4-contract.json +2985 -0
- package/fixtures/specimens.json +11168 -0
- package/fixtures/style-conditions.json +336 -0
- package/fixtures/template-items.json +2215 -0
- package/fixtures/text-clusters.json +65 -0
- package/fixtures/text-direction.json +309 -0
- package/fixtures/text-shimmer.json +96 -0
- package/fixtures/theme-extends.json +573 -0
- package/fixtures/theme-invalid.json +1300 -0
- package/fixtures/theme-recipes.json +61762 -0
- package/fixtures/theme-resolved.json +25969 -0
- package/fixtures/toast-stack.json +215 -0
- package/fixtures/tree-guides.json +224 -0
- package/fixtures/virtual-list.json +733 -0
- package/fixtures/voice-wave.json +19236 -0
- package/generated/ExponentialUICatalog.generated.kt +67 -0
- package/generated/ExponentialUICatalog.generated.swift +67 -0
- package/generated/ExponentialUIThemes.generated.kt +23 -0
- package/generated/ExponentialUIThemes.generated.swift +23 -0
- package/generated/catalog.descriptions.generated.rs +6 -0
- package/generated/catalog.generated.rs +80 -0
- package/generated/catalog.themes.generated.rs +34 -0
- package/generated/core.descriptions.json +2726 -0
- package/generated/core.runtime.json +3944 -0
- package/package.json +87 -4
- package/themes/exponential.recipes.json +49 -0
- package/themes/exponential.theme.json +768 -0
- package/themes/neutral.theme.json +6178 -0
- package/themes/orange.theme.json +43 -0
- package/themes/playful.theme.json +219 -0
- package/vendor/a2ui/LICENSE.txt +203 -0
- package/vendor/a2ui/README.md +27 -0
- package/vendor/a2ui/v0_9/basic_catalog.json +1387 -0
- package/vendor/a2ui/v0_9/basic_catalog_rules.txt +5 -0
- package/vendor/a2ui/v0_9/client_capabilities.json +97 -0
- package/vendor/a2ui/v0_9/client_to_server.json +104 -0
- package/vendor/a2ui/v0_9/common_types.json +315 -0
- package/vendor/a2ui/v0_9/server_to_client.json +132 -0
- package/vendor/json-render/LICENSE.txt +201 -0
- package/vendor/json-render/README.md +13 -0
- package/vendor/nayuki-qr/LICENSE.txt +20 -0
- package/vendor/nayuki-qr/README.md +10 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://ui.exponential.at/conformance/report.schema.json",
|
|
4
|
+
"title": "Exponential UI conformance report",
|
|
5
|
+
"description": "VAPP-91: what a renderer's conformance runner writes. checkReport() / conformance:check decide conformance against conformance/manifest.json.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": [
|
|
8
|
+
"renderer",
|
|
9
|
+
"platform",
|
|
10
|
+
"version",
|
|
11
|
+
"conformanceVersion",
|
|
12
|
+
"suites"
|
|
13
|
+
],
|
|
14
|
+
"properties": {
|
|
15
|
+
"renderer": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "The renderer's package name, e.g. @exponential_app/eui-react, exponential-ui-gpui, ExponentialUI, at.exponential:eui-compose."
|
|
18
|
+
},
|
|
19
|
+
"platform": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "web | desktop | ios | macos | android | \u2026"
|
|
22
|
+
},
|
|
23
|
+
"version": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "The renderer's version."
|
|
26
|
+
},
|
|
27
|
+
"conformanceVersion": {
|
|
28
|
+
"type": "integer"
|
|
29
|
+
},
|
|
30
|
+
"suites": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"additionalProperties": {
|
|
33
|
+
"type": "object",
|
|
34
|
+
"required": [
|
|
35
|
+
"cases",
|
|
36
|
+
"passed",
|
|
37
|
+
"failed"
|
|
38
|
+
],
|
|
39
|
+
"properties": {
|
|
40
|
+
"cases": {
|
|
41
|
+
"type": "integer",
|
|
42
|
+
"minimum": 0
|
|
43
|
+
},
|
|
44
|
+
"passed": {
|
|
45
|
+
"type": "integer",
|
|
46
|
+
"minimum": 0
|
|
47
|
+
},
|
|
48
|
+
"failed": {
|
|
49
|
+
"type": "array",
|
|
50
|
+
"items": {
|
|
51
|
+
"type": "string"
|
|
52
|
+
},
|
|
53
|
+
"description": "The failed cases' names (and a short reason)."
|
|
54
|
+
},
|
|
55
|
+
"skipped": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"items": {
|
|
58
|
+
"type": "string"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"additionalProperties": false
|
|
66
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// `bun run --filter @exponential_app/eui conformance` — the renderer
|
|
3
|
+
// conformance harness, everything this machine can run:
|
|
4
|
+
// 1. the WEB dump: the React renderer in headless Chromium with the
|
|
5
|
+
// conformance fonts (packages/exponential-ui-react/scripts/conformance-web.ts),
|
|
6
|
+
// compared with the committed baseline (fixtures/conformance-baseline.json)
|
|
7
|
+
// 2. the DESKTOP dump: the gpui painter headless with gpui's own Linux text
|
|
8
|
+
// system and the same font files (cargo example `conformance_dump` of
|
|
9
|
+
// apps/desktop/crates/eui-gpui), compared with the baseline
|
|
10
|
+
// 3. (`--pixels`) the perceptual step: the web surface PNG next to the
|
|
11
|
+
// desktop window's capture (Xvfb), a pixel-difference ratio + a global
|
|
12
|
+
// SSIM per case — REPORTED, never gating
|
|
13
|
+
// and prints the tables, the divergence origins grouped across cases and
|
|
14
|
+
// writes the dumps + a JSON report to `--out` (default $TMPDIR/xui-conformance).
|
|
15
|
+
//
|
|
16
|
+
// --only <substring> cases whose key contains it
|
|
17
|
+
// --web <dump.json> reuse a web dump --skip-web
|
|
18
|
+
// --desktop <dump> reuse a desktop dump --skip-desktop
|
|
19
|
+
// --write-baseline rewrite the baseline from the web dump (whole matrix)
|
|
20
|
+
// --details every case's divergence list (default: grouped)
|
|
21
|
+
// --pixels [substring] the perceptual step (default: kitchen-sink/*/dark/900/ltr
|
|
22
|
+
// and responsive/*/dark/390/rtl)
|
|
23
|
+
|
|
24
|
+
import { spawnSync } from "node:child_process"
|
|
25
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
|
26
|
+
import { tmpdir } from "node:os"
|
|
27
|
+
import { join } from "node:path"
|
|
28
|
+
import { decodeBaseline, type Baseline } from "./baseline"
|
|
29
|
+
import { compareDumps, formatCase, formatTable, groupFindings, type Report } from "./compare"
|
|
30
|
+
import { allCases, BASELINE_PATH, MANIFEST_PATH, type ConformanceManifest, type Dump } from "./dump"
|
|
31
|
+
|
|
32
|
+
const repoRoot = join(import.meta.dir, `..`, `..`, `..`)
|
|
33
|
+
const argv = process.argv.slice(2)
|
|
34
|
+
const flag = (name: string) => argv.includes(`--${name}`)
|
|
35
|
+
const arg = (name: string) => {
|
|
36
|
+
const i = argv.indexOf(`--${name}`)
|
|
37
|
+
return i >= 0 && argv[i + 1] && !argv[i + 1].startsWith(`--`) ? argv[i + 1] : undefined
|
|
38
|
+
}
|
|
39
|
+
const outDir = arg(`out`) ?? join(tmpdir(), `xui-conformance`)
|
|
40
|
+
mkdirSync(outDir, { recursive: true })
|
|
41
|
+
const manifest = JSON.parse(readFileSync(join(repoRoot, MANIFEST_PATH), `utf8`)) as ConformanceManifest
|
|
42
|
+
const only = arg(`only`)
|
|
43
|
+
const keys = allCases(manifest)
|
|
44
|
+
.map((c) => c.key)
|
|
45
|
+
.filter((k) => !only || k.includes(only))
|
|
46
|
+
const tol = manifest.tolerance
|
|
47
|
+
|
|
48
|
+
function run(cmd: string, args: string[], cwd: string): boolean {
|
|
49
|
+
console.error(`$ ${cmd} ${args.join(` `)}`)
|
|
50
|
+
const r = spawnSync(cmd, args, { cwd, stdio: [`ignore`, `inherit`, `inherit`], env: process.env })
|
|
51
|
+
if (r.error) console.error(String(r.error))
|
|
52
|
+
return r.status === 0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const readDump = (path: string) => JSON.parse(readFileSync(path, `utf8`)) as Dump
|
|
56
|
+
const baselinePath = join(repoRoot, BASELINE_PATH)
|
|
57
|
+
const loadBaseline = () => (existsSync(baselinePath) ? decodeBaseline(JSON.parse(readFileSync(baselinePath, `utf8`)) as Baseline) : null)
|
|
58
|
+
|
|
59
|
+
// 1. web
|
|
60
|
+
let web: Dump | null = null
|
|
61
|
+
const webPath = arg(`web`) ?? join(outDir, `web.json`)
|
|
62
|
+
if (!flag(`skip-web`)) {
|
|
63
|
+
if (!arg(`web`)) {
|
|
64
|
+
const args = [`scripts/conformance-web.ts`, `--out`, webPath]
|
|
65
|
+
if (only) args.push(`--only`, only)
|
|
66
|
+
if (flag(`write-baseline`)) args.push(`--write-baseline`)
|
|
67
|
+
if (!run(`bun`, args, join(repoRoot, `packages/exponential-ui-react`))) console.error(`web dump FAILED (is Chromium installed? bunx playwright install chromium)`)
|
|
68
|
+
}
|
|
69
|
+
if (existsSync(webPath)) web = readDump(webPath)
|
|
70
|
+
}
|
|
71
|
+
const baseline = loadBaseline()
|
|
72
|
+
const reports: Record<string, Report> = {}
|
|
73
|
+
if (!baseline) console.error(`no baseline at ${BASELINE_PATH}: run with --write-baseline`)
|
|
74
|
+
if (web && baseline) {
|
|
75
|
+
reports.web = compareDumps(baseline, web, tol, keys)
|
|
76
|
+
console.log(`\n# web vs the committed baseline\n`)
|
|
77
|
+
console.log(formatTable(reports.web))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 2. desktop
|
|
81
|
+
const desktopPath = arg(`desktop`) ?? join(outDir, `desktop.json`)
|
|
82
|
+
if (!flag(`skip-desktop`)) {
|
|
83
|
+
if (!arg(`desktop`)) {
|
|
84
|
+
const args = [`run`, `-q`, `-p`, `eui-gpui`, `--example`, `conformance_dump`, `--`, `--out`, desktopPath]
|
|
85
|
+
if (only) args.push(`--only`, only)
|
|
86
|
+
if (process.platform !== `linux` && process.platform !== `freebsd`) console.error(`desktop dump: Linux/FreeBSD only for now — skipped`)
|
|
87
|
+
else if (!run(`cargo`, args, join(repoRoot, `apps/desktop`))) console.error(`desktop dump FAILED`)
|
|
88
|
+
}
|
|
89
|
+
if (existsSync(desktopPath) && baseline) {
|
|
90
|
+
reports.desktop = compareDumps(baseline, readDump(desktopPath), tol, keys)
|
|
91
|
+
console.log(`\n# desktop (gpui, headless) vs the web baseline\n`)
|
|
92
|
+
console.log(formatTable(reports.desktop))
|
|
93
|
+
const findings = groupFindings(reports.desktop)
|
|
94
|
+
console.log(`\n## divergence origins across cases (${findings.length}; Δ = desktop − web)\n`)
|
|
95
|
+
for (const f of findings.slice(0, flag(`details`) ? findings.length : 80))
|
|
96
|
+
console.log(` ${String(f.cases).padStart(3)}× ${f.kinds.padEnd(13)} ${f.id} (${f.component}) Δw ${f.dw[0]}…${f.dw[1]} Δh ${f.dh[0]}…${f.dh[1]}${f.text ? ` "${f.text.slice(0, 32)}"` : ``}\n e.g. ${f.example}`)
|
|
97
|
+
if (!flag(`details`) && findings.length > 80) console.log(` … ${findings.length - 80} more (--details)`)
|
|
98
|
+
if (flag(`details`)) for (const c of reports.desktop.cases) console.log(`\n${formatCase(c)}`)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 3. pixels
|
|
103
|
+
if (flag(`pixels`)) {
|
|
104
|
+
const sel = arg(`pixels`)
|
|
105
|
+
const pixelKeys = keys.filter((k) => (sel ? k.includes(sel) : /^(kitchen-sink\/[^/]+\/dark\/900\/ltr|responsive\/[^/]+\/dark\/390\/rtl)$/.test(k)))
|
|
106
|
+
const { pixelStep } = await import(`./pixel`)
|
|
107
|
+
const rows = await pixelStep(repoRoot, outDir, pixelKeys)
|
|
108
|
+
console.log(`\n# perceptual (reported, never gating)\n`)
|
|
109
|
+
for (const r of rows) console.log(` ${r.key.padEnd(44)} ${r.note ?? `diff ${(r.diffRatio! * 100).toFixed(2)}% SSIM ${r.ssim!.toFixed(4)} (${r.size}) ${r.files}`}`)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
writeFileSync(join(outDir, `report.json`), JSON.stringify(reports, null, 1))
|
|
113
|
+
console.log(`\ndumps + report.json in ${outDir}`)
|
|
114
|
+
const webBad = reports.web ? reports.web.cases.some((c) => c.diffs.length || c.onlyRef.length || c.onlyCand.length) || reports.web.missingCases.length > 0 : false
|
|
115
|
+
if (webBad) {
|
|
116
|
+
console.log(`\nthe WEB dump drifted from the baseline (see above): fix the renderer, or rewrite the baseline with --write-baseline when the change is intended`)
|
|
117
|
+
process.exitCode = 1
|
|
118
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// NEW-77: THE CHILD RULE of VoiceWave (catalog/style.json `voiceWave`)
|
|
2
|
+
// measured on a WEB painter in headless Chromium: every `child` case of
|
|
3
|
+
// fixtures/voice-wave.json is painted (the native through the harness's
|
|
4
|
+
// `?view=mount`, the primitive through `?view=primitives&id=plain/voice-wave`,
|
|
5
|
+
// whose first five waves are these cases in order) and the DRAWN child box
|
|
6
|
+
// (getBoundingClientRect, transforms included, relative to the wave's root)
|
|
7
|
+
// must sit within 0.5 px of `expected.child`. The renderers' browser tests
|
|
8
|
+
// (voice-wave-child.test.ts) call these.
|
|
9
|
+
|
|
10
|
+
import { readFileSync } from "node:fs"
|
|
11
|
+
import { join } from "node:path"
|
|
12
|
+
import type { Page } from "playwright"
|
|
13
|
+
import type { NestedNode } from "../src/index"
|
|
14
|
+
|
|
15
|
+
export interface VoiceWaveChildCase {
|
|
16
|
+
name: string
|
|
17
|
+
input: { size: number; activity: string; level?: number; bands?: number[]; child: { width: number; height: number } }
|
|
18
|
+
expected: { x: number; y: number; width: number; height: number; scale: number }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface DrawnBox {
|
|
22
|
+
x: number
|
|
23
|
+
y: number
|
|
24
|
+
width: number
|
|
25
|
+
height: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The px edge → the size enum (the neutral theme's `VoiceWave/root`, the
|
|
29
|
+
* primitives' VOICE_WAVE_SIZE_PX). */
|
|
30
|
+
const SIZE_NAME: Record<number, string> = { 32: `sm`, 56: `md`, 160: `lg`, 200: `xl` }
|
|
31
|
+
|
|
32
|
+
export const VOICE_WAVE_CHILD_TOLERANCE = 0.5
|
|
33
|
+
|
|
34
|
+
export function voiceWaveChildCases(): VoiceWaveChildCase[] {
|
|
35
|
+
const fixture = JSON.parse(readFileSync(join(import.meta.dirname, `..`, `fixtures`, `voice-wave.json`), `utf8`)) as { cases: { name: string; input: VoiceWaveChildCase[`input`] & { child?: unknown }; expected: { child?: VoiceWaveChildCase[`expected`] } }[] }
|
|
36
|
+
return fixture.cases.filter((c) => c.input.child && c.expected.child).map((c) => ({ name: c.name, input: c.input, expected: c.expected.child! }))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** One VoiceWave per case (`vw<i>`), its one child a Box of the case's
|
|
40
|
+
* child size (`vw<i>-c`), in a wrapping row. */
|
|
41
|
+
export function voiceWaveChildTree(cases = voiceWaveChildCases()): NestedNode {
|
|
42
|
+
return {
|
|
43
|
+
id: `root`,
|
|
44
|
+
component: `Box`,
|
|
45
|
+
style: { display: `flex`, flexDirection: `row`, flexWrap: `wrap`, alignItems: `center`, gap: 24, padding: 24 },
|
|
46
|
+
children: cases.map((c, i) => {
|
|
47
|
+
const size = SIZE_NAME[c.input.size]
|
|
48
|
+
if (!size) throw new Error(`${c.name}: no size enum for ${c.input.size} px`)
|
|
49
|
+
return {
|
|
50
|
+
id: `vw${i}`,
|
|
51
|
+
component: `VoiceWave`,
|
|
52
|
+
props: { activity: c.input.activity, level: c.input.level ?? 0, ...(c.input.bands ? { bands: c.input.bands } : {}), size },
|
|
53
|
+
children: [{ id: `vw${i}-c`, component: `Box`, style: { width: c.input.child.width, height: c.input.child.height } }],
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
} as NestedNode
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** `true`, or what is off (x, y, width, height beyond the tolerance). */
|
|
60
|
+
export function checkVoiceWaveChild(expected: VoiceWaveChildCase[`expected`], drawn: DrawnBox | null): true | string {
|
|
61
|
+
if (!drawn) return `child not painted`
|
|
62
|
+
const off = ([`x`, `y`, `width`, `height`] as const).filter((k) => !(Math.abs(drawn[k] - expected[k]) <= VOICE_WAVE_CHILD_TOLERANCE))
|
|
63
|
+
return off.length === 0 || off.map((k) => `${k} ${expected[k]}≠${drawn[k].toFixed(2)}`).join(`, `)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The drawn child of each [root selector, child selector] pair, relative
|
|
67
|
+
* to its root (null = not found), after two frames (the measure lands
|
|
68
|
+
* before the first paint; the frames let any re-measure settle). */
|
|
69
|
+
export function drawnChildBoxes(page: Page, pairs: [string, string][]): Promise<(DrawnBox | null)[]> {
|
|
70
|
+
return page.evaluate(async (pairs) => {
|
|
71
|
+
await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)))
|
|
72
|
+
return pairs.map(([rootSel, childSel]) => {
|
|
73
|
+
const root = document.querySelector(rootSel)
|
|
74
|
+
const child = document.querySelector(childSel)
|
|
75
|
+
if (!root || !child) return null
|
|
76
|
+
const r = root.getBoundingClientRect()
|
|
77
|
+
const c = child.getBoundingClientRect()
|
|
78
|
+
return { x: c.left - r.left, y: c.top - r.top, width: c.width, height: c.height }
|
|
79
|
+
})
|
|
80
|
+
}, pairs)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The native (a page at `?view=mount`): mounts the tree, measures. */
|
|
84
|
+
export async function measureNativeVoiceWaveChildren(page: Page, cases = voiceWaveChildCases()): Promise<{ name: string; verdict: true | string }[]> {
|
|
85
|
+
await page.waitForFunction(() => typeof (window as unknown as { __xuiMount?: unknown }).__xuiMount === `function`)
|
|
86
|
+
await page.evaluate((tree) => (window as unknown as { __xuiMount: (t: unknown, o?: unknown) => Promise<void> }).__xuiMount(tree, { theme: `neutral` }), voiceWaveChildTree(cases))
|
|
87
|
+
const boxes = await drawnChildBoxes(
|
|
88
|
+
page,
|
|
89
|
+
cases.map((_, i) => [`[data-xui-id="vw${i}"]`, `[data-xui-id="vw${i}-c"]`])
|
|
90
|
+
)
|
|
91
|
+
return cases.map((c, i) => ({ name: c.name, verdict: checkVoiceWaveChild(c.expected, boxes[i]!) }))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The primitive (a page at `?view=primitives&id=plain/voice-wave`). */
|
|
95
|
+
export async function measurePrimitiveVoiceWaveChildren(page: Page, cases = voiceWaveChildCases()): Promise<{ name: string; verdict: true | string }[]> {
|
|
96
|
+
await page.waitForSelector(`[data-voice-case="${cases.length - 1}"] [data-slot="voice-wave"]`, { state: `attached` })
|
|
97
|
+
const boxes = await drawnChildBoxes(
|
|
98
|
+
page,
|
|
99
|
+
cases.map((_, i) => [`[data-voice-case="${i}"] [data-slot="voice-wave"]`, `[data-voice-case="${i}"] [data-voice-child]`])
|
|
100
|
+
)
|
|
101
|
+
return cases.map((c, i) => ({ name: c.name, verdict: checkVoiceWaveChild(c.expected, boxes[i]!) }))
|
|
102
|
+
}
|