@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,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "VAPP-92: the RECIPE contract. A theme styles a component PART under a `when` selector (the node's recipe props + the interaction `state`); this file fixes what a theme may name. `states` = the interaction states a painter reports (`default` is the absence of all; round 1 adds `invalid` = a field whose checks failed, `dragover` = a drop zone a drag hovers); `keys` = the whitelist a recipe style may set: a SUBSET of style.json's visual keys plus the padding/gap/size keys a part's box needs and `native` (true = the painter may use the platform's own control for this part, geometry still from the tokens); round 1 lets a recipe set `transition`/`transitionEasing` (so a theme's motion tokens finally animate hover/press/open), `transform` (a chevron that rotates when `open`), `borderStyle`, the four per-side border widths (an app bar's bottom rule, a tab bar's top rule), `textDecoration`, `letterSpacing`, `fontStyle`, `textTransform`. A part-level discriminator the painter supplies per drawn piece: CodeBlock/token `kind` (the codeToken enum), Table/headerCell+cell `align` and Table/row `striped` (true on ODD rows of a striped table), Stepper's step `status` (a macro `$recipe`). `native` lists every native component's PARTS (the painter paints each part with its resolved recipe) and the PROPS a `when` may key on (the component's enum/boolean props; the catalog default fills an unset one, so a theme always sees a concrete value; a part-level discriminator the PAINTER supplies is listed too, see below). Macro parts come from macros.json (every template `part`) and their `when` props from the macro's `recipeProps` (+ a part's `$recipe` keys); src/recipes.ts derives them. Both lists are append-only. Round 2 (docs/round-2-contract.md §6): `parts` lists EVERY part a painter lays out (the conformance dump names each `<node id>.<part>[.<index or row key>]`), so the layout-only parts are here too (Switch/body, Radio/items, Slider/header, Table/body, CodeBlock/body+code, FileUpload/title+browse+fileIcon, the fields' `input`, Tooltip/anchor, Accordion/count, List/section, Carousel/controls+previous+next); keys add `backdropBlur` (`$blur.*`) and `animation`; Resizable is new. EXP-1259: SearchList is new (its parts are also a searchable Menu submenu's: head, search, count, list, empty, fade), Tabs gain `close` + `new` (props `closable`, `addable`), Skeleton gains its rows (`row`, `dot`, `bar`). EXP-1324: CodeBlock gains the `variant` (default|destructive) and `size` (default|sm) props, shadcn's code-block. VAPP-162 (docs/round-5-contract.md): Segmented gains `orientation` + the `badge` part (an option `count`), VoiceWave the `variant` prop + the `bar` part (the `bars` meter), QrCode and QrScanner are new.",
|
|
3
|
+
"states": ["hover", "pressed", "focus", "disabled", "checked", "open", "selected", "invalid", "dragover"],
|
|
4
|
+
"keys": [
|
|
5
|
+
"backgroundColor", "color", "borderColor", "borderWidth", "borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth", "borderRadius", "borderStyle",
|
|
6
|
+
"padding", "paddingHorizontal", "paddingVertical", "gap",
|
|
7
|
+
"width", "height", "minWidth", "minHeight",
|
|
8
|
+
"fontSize", "fontWeight", "lineHeight", "fontFamily", "letterSpacing", "textDecoration", "textTransform", "fontStyle",
|
|
9
|
+
"boxShadow", "opacity", "transition", "transitionEasing", "transform", "backdropBlur", "animation", "native"
|
|
10
|
+
],
|
|
11
|
+
"native": {
|
|
12
|
+
"Box": { "parts": ["root"], "props": ["pressable"] },
|
|
13
|
+
"List": { "parts": ["root", "divider", "section"], "props": ["direction", "divided", "stickyHeaders"] },
|
|
14
|
+
"Resizable": { "parts": ["root", "panel", "handle", "grip"], "props": ["direction", "handle"] },
|
|
15
|
+
"Text": { "parts": ["root"], "props": ["variant", "tone"] },
|
|
16
|
+
"Markdown": { "parts": ["root", "paragraph", "heading", "link", "code", "codeBlock", "quote", "table", "listItem"], "props": [] },
|
|
17
|
+
"Image": { "parts": ["root", "fallback"], "props": ["fit"] },
|
|
18
|
+
"Icon": { "parts": ["root"], "props": ["size", "tone"] },
|
|
19
|
+
"Video": { "parts": ["root", "controls", "poster"], "props": ["autoplay"] },
|
|
20
|
+
"AudioPlayer": { "parts": ["root", "controls", "track"], "props": [] },
|
|
21
|
+
"Avatar": { "parts": ["root", "image", "fallback"], "props": ["size"] },
|
|
22
|
+
"QrCode": { "parts": ["root", "plate", "module", "label"], "props": ["level"] },
|
|
23
|
+
"Carousel": { "parts": ["root", "page", "indicator", "controls", "previous", "next"], "props": ["loop", "indicators"] },
|
|
24
|
+
"Tabs": { "parts": ["root", "list", "tab", "indicator", "content", "close", "new"], "props": ["fill", "closable", "addable"] },
|
|
25
|
+
"Segmented": { "parts": ["root", "item", "icon", "label", "badge"], "props": ["type", "variant", "size", "fill", "orientation"] },
|
|
26
|
+
"Accordion": { "parts": ["root", "item", "trigger", "content", "count"], "props": ["type"] },
|
|
27
|
+
"Dialog": { "parts": ["overlay", "content", "title", "description", "footer", "close"], "props": ["dismissible"] },
|
|
28
|
+
"Drawer": { "parts": ["overlay", "content", "handle", "title", "description", "footer"], "props": ["side", "dismissible"] },
|
|
29
|
+
"Popover": { "parts": ["content", "arrow"], "props": ["side", "openOn"] },
|
|
30
|
+
"Tooltip": { "parts": ["content", "arrow", "anchor"], "props": ["side"] },
|
|
31
|
+
"Menu": { "parts": ["trigger", "content", "item", "separator", "label", "shortcut", "check", "submenuIndicator", "head", "search", "count", "list", "empty", "fade"], "props": ["openOn"] },
|
|
32
|
+
"Toast": { "parts": ["root", "icon", "title", "description", "action", "close"], "props": ["type"] },
|
|
33
|
+
"Ring": { "parts": ["root", "track", "fill", "label"], "props": ["size", "tone"] },
|
|
34
|
+
"Spinner": { "parts": ["root"], "props": ["size"] },
|
|
35
|
+
"Skeleton": { "parts": ["root", "row", "dot", "bar"], "props": ["rounded", "dot"] },
|
|
36
|
+
"VoiceWave": { "parts": ["root", "outer", "mid", "inner", "child", "bar"], "props": ["activity", "size", "variant"] },
|
|
37
|
+
"Chart": { "parts": ["root", "title", "axis", "grid", "legend", "tooltip", "valueLabel"], "props": ["kind"] },
|
|
38
|
+
"Table": { "parts": ["root", "caption", "header", "headerCell", "sortIcon", "row", "cell", "checkbox", "empty", "body"], "props": ["selectable", "striped", "density", "stickyHeader", "align"] },
|
|
39
|
+
"Button": { "parts": ["root", "icon", "label", "spinner"], "props": ["variant", "size", "disabled", "loading"] },
|
|
40
|
+
"Link": { "parts": ["root"], "props": ["external"] },
|
|
41
|
+
"Toggle": { "parts": ["root", "icon", "label"], "props": ["variant", "size", "pressed"] },
|
|
42
|
+
"Form": { "parts": ["root", "summary"], "props": ["disabled", "busy"] },
|
|
43
|
+
"Input": { "parts": ["root", "label", "field", "placeholder", "description", "error"], "props": ["type", "disabled"] },
|
|
44
|
+
"Textarea": { "parts": ["root", "label", "field", "placeholder", "description", "error"], "props": ["disabled"] },
|
|
45
|
+
"NumberField": { "parts": ["root", "label", "field", "decrement", "increment", "unit", "description", "error", "input"], "props": ["disabled"] },
|
|
46
|
+
"Checkbox": { "parts": ["root", "box", "check", "label", "description"], "props": ["checked", "disabled"] },
|
|
47
|
+
"Radio": { "parts": ["root", "item", "dot", "label", "items"], "props": ["orientation"] },
|
|
48
|
+
"ChoiceTiles": { "parts": ["root", "tile", "picture", "label", "description"], "props": ["type", "columns"] },
|
|
49
|
+
"Switch": { "parts": ["root", "track", "thumb", "label", "description", "body"], "props": ["checked", "disabled"] },
|
|
50
|
+
"Slider": { "parts": ["root", "track", "range", "thumb", "label", "value", "header"], "props": [] },
|
|
51
|
+
"Select": { "parts": ["root", "label", "trigger", "placeholder", "content", "search", "item", "check", "empty"], "props": ["multiple", "searchable", "disabled"] },
|
|
52
|
+
"SearchList": { "parts": ["root", "head", "search", "count", "list", "item", "icon", "identifier", "label", "check", "empty", "fade"], "props": [] },
|
|
53
|
+
"ChipInput": { "parts": ["root", "label", "field", "chip", "chipLabel", "remove", "placeholder", "description", "error", "input"], "props": ["disabled"] },
|
|
54
|
+
"DatePicker": { "parts": ["root", "label", "trigger", "calendar", "day"], "props": [] },
|
|
55
|
+
"DateRangePicker": { "parts": ["root", "label", "trigger", "calendar", "day", "range"], "props": [] },
|
|
56
|
+
"TimePicker": { "parts": ["root", "label", "trigger", "placeholder", "list", "item"], "props": ["disabled"] },
|
|
57
|
+
"FileUpload": { "parts": ["root", "label", "dropzone", "icon", "hint", "file", "fileName", "fileMeta", "remove", "description", "error", "title", "browse", "fileIcon"], "props": ["multiple", "disabled"] },
|
|
58
|
+
"QrScanner": { "parts": ["root", "viewfinder", "feed", "corner", "scanLine", "placeholder", "placeholderIcon", "hint"], "props": ["disabled"] },
|
|
59
|
+
"CodeBlock": { "parts": ["root", "header", "title", "copy", "gutter", "line", "lineNumber", "token", "body", "code"], "props": ["lineNumbers", "wrap", "kind", "variant", "size"] },
|
|
60
|
+
"Composer": { "parts": ["root", "field", "placeholder", "attachment", "send"], "props": ["attachments", "busy"] },
|
|
61
|
+
"TreeGuides": { "parts": ["root", "line"], "props": ["tee"] },
|
|
62
|
+
"Unknown": { "parts": ["root", "label"], "props": [] }
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "Round 1 (docs/round-1-contract.md §4): the built-in UI STRINGS every renderer shows — the copy no author writes (a Select's search placeholder, the calendar's month buttons, the copy button, the Pagination arrows' labels). Keys are the ids, values the English defaults. A renderer ships this table (generated into TS/Swift/Kotlin/Rust as STRING_IDS + STRING_DEFAULTS) and lets the HOST override any id per surface (`strings: {search: \"Suchen…\"}`); the catalog refers to an entry as `$string.<id>` in a prop default or a macro template (`$string.previous`), which the renderer resolves at bind time like a token. `{name}` placeholders are filled by the renderer (src/strings.ts formatString; a macro template uses `$fill` → the core `fill` function): a sentence is ONE id with placeholders, never concatenated fragments. Round 2 adds the names the renderers still hard-coded (`invalidValue` = a failed check without a message, `message` = the Composer field, `codeBlock` = an untitled CodeBlock, `dialog` = an untitled Dialog/Drawer, `table` = an uncaptioned Table, `carousel`/`slide` = the carousel's role descriptions, `resize` = a Resizable handle). EXP-1259 adds `closeTab` and `newTab` (the Tabs close glyph and + button). Append-only (round 1 dropped the unused fragment `of`). VAPP-162 adds the call buttons (`mute`, `unmute`, `speaker`, `speakerOff`, `endCall`), the Plan answers (`yes`, `no`), the MessageBubble delivery labels (`sending`, `sent`, `read`) and the QrScanner's `scanQrCode` + `cameraUnavailable`.",
|
|
3
|
+
"strings": {
|
|
4
|
+
"back": "Back",
|
|
5
|
+
"breadcrumb": "Breadcrumb",
|
|
6
|
+
"browse": "Browse",
|
|
7
|
+
"cameraUnavailable": "Camera unavailable",
|
|
8
|
+
"cancel": "Cancel",
|
|
9
|
+
"carousel": "Carousel",
|
|
10
|
+
"chartSummary": "Chart of {series}, values from {min} to {max}",
|
|
11
|
+
"choose": "Choose",
|
|
12
|
+
"clear": "Clear",
|
|
13
|
+
"close": "Close",
|
|
14
|
+
"closeTab": "Close {name}",
|
|
15
|
+
"codeBlock": "Code",
|
|
16
|
+
"collapse": "Collapse",
|
|
17
|
+
"confirm": "Confirm",
|
|
18
|
+
"copied": "Copied",
|
|
19
|
+
"copy": "Copy",
|
|
20
|
+
"decrement": "Decrease",
|
|
21
|
+
"dialog": "Dialog",
|
|
22
|
+
"dismiss": "Dismiss",
|
|
23
|
+
"dropFiles": "Drop files here",
|
|
24
|
+
"endCall": "End call",
|
|
25
|
+
"expand": "Expand",
|
|
26
|
+
"fileTooLarge": "File too large",
|
|
27
|
+
"fileTooLargeNamed": "{name}: File too large",
|
|
28
|
+
"increment": "Increase",
|
|
29
|
+
"invalidFields": "Check the highlighted fields",
|
|
30
|
+
"invalidValue": "Invalid value",
|
|
31
|
+
"loading": "Loading",
|
|
32
|
+
"menu": "Menu",
|
|
33
|
+
"message": "Message",
|
|
34
|
+
"more": "More",
|
|
35
|
+
"mute": "Mute",
|
|
36
|
+
"newTab": "New tab",
|
|
37
|
+
"next": "Next",
|
|
38
|
+
"nextMonth": "Next month",
|
|
39
|
+
"no": "No",
|
|
40
|
+
"noResults": "No results",
|
|
41
|
+
"optional": "Optional",
|
|
42
|
+
"pageOf": "Page {page} of {total}",
|
|
43
|
+
"pagination": "Pagination",
|
|
44
|
+
"previous": "Previous",
|
|
45
|
+
"previousMonth": "Previous month",
|
|
46
|
+
"ratingStar": "{value} of {max}",
|
|
47
|
+
"ratingValue": "Rated {value} of {max}",
|
|
48
|
+
"read": "Read",
|
|
49
|
+
"remove": "Remove",
|
|
50
|
+
"removeItem": "Remove {name}",
|
|
51
|
+
"required": "Required",
|
|
52
|
+
"resize": "Resize",
|
|
53
|
+
"scanQrCode": "Scan a QR code",
|
|
54
|
+
"search": "Search…",
|
|
55
|
+
"selectAll": "Select all",
|
|
56
|
+
"selectedCount": "{count} selected",
|
|
57
|
+
"selectRow": "Select row",
|
|
58
|
+
"send": "Send",
|
|
59
|
+
"sending": "Sending",
|
|
60
|
+
"sent": "Sent",
|
|
61
|
+
"showLess": "Show less",
|
|
62
|
+
"showMore": "Show more",
|
|
63
|
+
"slide": "Slide",
|
|
64
|
+
"sortAscending": "Sort ascending",
|
|
65
|
+
"sortByAscending": "{name}: Sort ascending",
|
|
66
|
+
"sortByDescending": "{name}: Sort descending",
|
|
67
|
+
"sortDescending": "Sort descending",
|
|
68
|
+
"sparklineSummary": "Trend from {first} to {last}, low {min}, high {max}",
|
|
69
|
+
"speaker": "Speaker",
|
|
70
|
+
"speakerOff": "Speaker off",
|
|
71
|
+
"stepCurrent": "Step {step} of {total}: {label}, current",
|
|
72
|
+
"stepDone": "Step {step} of {total}: {label}, completed",
|
|
73
|
+
"stepUpcoming": "Step {step} of {total}: {label}",
|
|
74
|
+
"stop": "Stop",
|
|
75
|
+
"submit": "Submit",
|
|
76
|
+
"table": "Table",
|
|
77
|
+
"today": "Today",
|
|
78
|
+
"unknownComponent": "Unknown component",
|
|
79
|
+
"unmute": "Unmute",
|
|
80
|
+
"unsupportedFile": "Unsupported file",
|
|
81
|
+
"unsupportedFileNamed": "{name}: Unsupported file",
|
|
82
|
+
"yes": "Yes"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$comment": "VAPP-85: the `Box` style whitelist, verbatim from the VAPP-4 spike (`vapp_spike::WHITELIST` + the logical inline properties the spike added), extended by round 1 (docs/round-1-contract.md) — ONE source for the TS keys (`src/style.ts`), the JSON Schema (`core.schema.json#/$defs/style`) and the Swift/Kotlin/Rust key lists. Numbers are px (≡ pt ≡ dp); `length` also takes `\"N%\"`, `\"Npx\"` and `\"auto\"`; every value may be a token reference `$<group>.<name>` from tokens.json; a COLOUR (`backgroundColor`, `color`, `borderColor`, gradient stops) is ONLY a `$color.<name>` token (round 4, VAPP-103: a literal `#hex` ignores the theme's light and dark modes, so validateStyle refuses it; themes keep literals). `borderWidth` and the four per-side widths are the visual keys with layout effect (a number or `$border.<name>`, VAPP-92); `fontFamily` (VAPP-92) takes only `$type.family.<name>`; `transition` takes only `$motion.<name>` (the duration every opacity/colour/transform/size change animates with; nothing animates without it) and `transitionEasing` only `$ease.<name>`; `transform` is PAINT-ONLY (`translate(Xpx, Ypx)`, `scale(N)`, `rotate(Ndeg)`, space-separated, never layout); `backgroundGradient` = `{angle, stops: [{color, offset 0..1}]}` painted OVER `backgroundColor` (CSS order: the colour shows only through transparent stops); `visibility: hidden` keeps the box in layout and paints nothing (`display: none` removes it); `cursor` is a pointer-platform hint touch platforms ignore. CONDITIONS nest one level and apply in SOURCE ORDER after the base (write narrow-first): `@media` keys match the ONE grammar in `conditions.media` — `min-width`/`max-width`/`min-height`/`max-height` against the SURFACE box (`min-*` = `size >= N`, `max-*` = `size < N`; `N` = px or a `$breakpoint.<name>` token the theme resolves BEFORE matching), `orientation` of the surface box, `hover: hover|none` = a hover-capable pointer, `prefers-reduced-motion: reduce|no-preference` from the platform; the state keys `:hover`, `:focus-visible`, `:pressed` against the node's own interaction state, applied after every media block in that order (pressed wins). `src/style.ts resolveConditions` is the reference flattening and fixtures/style-conditions.json locks it. Still not in v1 (confirmed by the spike, kept by round 1): calc(), named grid lines, gridAutoFlow, justifyItems, order, zIndex (paint order = tree order, absolute nodes LAST among siblings, overlays stack by LAYER), per-side border colours (gpui paints ONE border colour per box; per-side WIDTHS are in), multi-value shorthands. After macro expansion a style VALUE may be a binding or a function call (a bound macro input, e.g. Progress fill width = percent{…}); the renderer resolves it at bind time and then applies it like an authored value; authors cannot write one (validateStyle refuses). Round 2 (docs/round-2-contract.md §2): `position: sticky` = relative in layout, then pinned inside the nearest scrolling ancestor (`overflow` scroll/auto, else the surface scroller) by its `top`/`bottom`/`left`/`right`/`inset*` offsets while its parent is in view; `backdropBlur` takes only `$blur.<name>` (blurs what is BEHIND the box through its translucent background; a platform without backdrop blur paints the background alone, so pair it with a translucent `backgroundColor`); `animation` names a keyframe set of `animations` (`animationDuration` = `$motion.<name>` only); `direction` may sit on ANY node and is inherited (flex rows, logical insets/paddings, `textAlign` start/end and mirrored glyphs follow the node's resolved direction).",
|
|
3
|
+
"layout": {
|
|
4
|
+
"display": { "enum": ["flex", "grid", "block", "none"] },
|
|
5
|
+
"flexDirection": { "enum": ["row", "column", "row-reverse", "column-reverse"] },
|
|
6
|
+
"flexWrap": { "enum": ["nowrap", "wrap", "wrap-reverse"] },
|
|
7
|
+
"justifyContent": { "enum": ["flex-start", "flex-end", "start", "end", "center", "stretch", "space-between", "space-around", "space-evenly"] },
|
|
8
|
+
"alignItems": { "enum": ["flex-start", "flex-end", "start", "end", "center", "stretch", "baseline"] },
|
|
9
|
+
"alignContent": { "enum": ["flex-start", "flex-end", "start", "end", "center", "stretch", "space-between", "space-around", "space-evenly"] },
|
|
10
|
+
"alignSelf": { "enum": ["flex-start", "flex-end", "start", "end", "center", "stretch", "baseline"] },
|
|
11
|
+
"justifySelf": { "enum": ["flex-start", "flex-end", "start", "end", "center", "stretch", "baseline"] },
|
|
12
|
+
"flexGrow": { "type": "number" },
|
|
13
|
+
"flexShrink": { "type": "number" },
|
|
14
|
+
"flexBasis": { "type": "length" },
|
|
15
|
+
"gap": { "type": "length" },
|
|
16
|
+
"rowGap": { "type": "length" },
|
|
17
|
+
"columnGap": { "type": "length" },
|
|
18
|
+
"direction": { "enum": ["ltr", "rtl"] },
|
|
19
|
+
"overflow": { "enum": ["visible", "hidden", "clip", "scroll", "auto"] },
|
|
20
|
+
"overflowX": { "enum": ["visible", "hidden", "clip", "scroll", "auto"] },
|
|
21
|
+
"overflowY": { "enum": ["visible", "hidden", "clip", "scroll", "auto"] },
|
|
22
|
+
"width": { "type": "length" },
|
|
23
|
+
"height": { "type": "length" },
|
|
24
|
+
"minWidth": { "type": "length" },
|
|
25
|
+
"minHeight": { "type": "length" },
|
|
26
|
+
"maxWidth": { "type": "length" },
|
|
27
|
+
"maxHeight": { "type": "length" },
|
|
28
|
+
"aspectRatio": { "type": "ratio" },
|
|
29
|
+
"position": { "enum": ["relative", "absolute", "sticky"] },
|
|
30
|
+
"top": { "type": "length" },
|
|
31
|
+
"right": { "type": "length" },
|
|
32
|
+
"bottom": { "type": "length" },
|
|
33
|
+
"left": { "type": "length" },
|
|
34
|
+
"inset": { "type": "length" },
|
|
35
|
+
"insetInlineStart": { "type": "length" },
|
|
36
|
+
"insetInlineEnd": { "type": "length" },
|
|
37
|
+
"insetBlockStart": { "type": "length" },
|
|
38
|
+
"insetBlockEnd": { "type": "length" },
|
|
39
|
+
"padding": { "type": "length" },
|
|
40
|
+
"paddingTop": { "type": "length" },
|
|
41
|
+
"paddingRight": { "type": "length" },
|
|
42
|
+
"paddingBottom": { "type": "length" },
|
|
43
|
+
"paddingLeft": { "type": "length" },
|
|
44
|
+
"paddingHorizontal": { "type": "length" },
|
|
45
|
+
"paddingVertical": { "type": "length" },
|
|
46
|
+
"paddingInlineStart": { "type": "length" },
|
|
47
|
+
"paddingInlineEnd": { "type": "length" },
|
|
48
|
+
"margin": { "type": "length" },
|
|
49
|
+
"marginTop": { "type": "length" },
|
|
50
|
+
"marginRight": { "type": "length" },
|
|
51
|
+
"marginBottom": { "type": "length" },
|
|
52
|
+
"marginLeft": { "type": "length" },
|
|
53
|
+
"marginHorizontal": { "type": "length" },
|
|
54
|
+
"marginVertical": { "type": "length" },
|
|
55
|
+
"marginInlineStart": { "type": "length" },
|
|
56
|
+
"marginInlineEnd": { "type": "length" },
|
|
57
|
+
"gridTemplateColumns": { "type": "tracks" },
|
|
58
|
+
"gridTemplateRows": { "type": "tracks" },
|
|
59
|
+
"gridTemplateAreas": { "type": "areas" },
|
|
60
|
+
"gridArea": { "type": "string" },
|
|
61
|
+
"gridColumn": { "type": "string" },
|
|
62
|
+
"gridRow": { "type": "string" }
|
|
63
|
+
},
|
|
64
|
+
"visual": {
|
|
65
|
+
"backgroundColor": { "type": "color" },
|
|
66
|
+
"backgroundGradient": { "type": "gradient" },
|
|
67
|
+
"color": { "type": "color" },
|
|
68
|
+
"borderWidth": { "type": "number", "layoutEffect": true },
|
|
69
|
+
"borderTopWidth": { "type": "number", "layoutEffect": true },
|
|
70
|
+
"borderRightWidth": { "type": "number", "layoutEffect": true },
|
|
71
|
+
"borderBottomWidth": { "type": "number", "layoutEffect": true },
|
|
72
|
+
"borderLeftWidth": { "type": "number", "layoutEffect": true },
|
|
73
|
+
"borderColor": { "type": "color" },
|
|
74
|
+
"borderStyle": { "enum": ["solid", "dashed", "dotted"] },
|
|
75
|
+
"borderRadius": { "type": "length" },
|
|
76
|
+
"borderTopLeftRadius": { "type": "length" },
|
|
77
|
+
"borderTopRightRadius": { "type": "length" },
|
|
78
|
+
"borderBottomRightRadius": { "type": "length" },
|
|
79
|
+
"borderBottomLeftRadius": { "type": "length" },
|
|
80
|
+
"opacity": { "type": "number" },
|
|
81
|
+
"boxShadow": { "type": "token", "group": "shadow" },
|
|
82
|
+
"fontSize": { "type": "length" },
|
|
83
|
+
"fontWeight": { "enum": [300, 400, 500, 600, 700, 800] },
|
|
84
|
+
"lineHeight": { "type": "length" },
|
|
85
|
+
"letterSpacing": { "type": "length" },
|
|
86
|
+
"textAlign": { "enum": ["left", "right", "center", "start", "end", "justify"] },
|
|
87
|
+
"textDecoration": { "enum": ["none", "underline", "line-through"] },
|
|
88
|
+
"textTransform": { "enum": ["none", "uppercase", "lowercase", "capitalize"] },
|
|
89
|
+
"fontStyle": { "enum": ["normal", "italic"] },
|
|
90
|
+
"fontFamily": { "type": "token", "group": "type.family" },
|
|
91
|
+
"transition": { "type": "token", "group": "motion" },
|
|
92
|
+
"transitionEasing": { "type": "token", "group": "ease" },
|
|
93
|
+
"transform": { "type": "transform" },
|
|
94
|
+
"visibility": { "enum": ["visible", "hidden"] },
|
|
95
|
+
"pointerEvents": { "enum": ["auto", "none"] },
|
|
96
|
+
"userSelect": { "enum": ["auto", "none", "text"] },
|
|
97
|
+
"cursor": { "enum": ["auto", "default", "pointer", "text", "not-allowed", "grab", "grabbing", "move", "col-resize", "row-resize"] },
|
|
98
|
+
"backdropBlur": { "type": "token", "group": "blur" },
|
|
99
|
+
"animation": { "enum": ["pulse", "spin", "fade-in", "slide-in-up", "slide-in-down", "slide-in-left", "slide-in-right", "shimmer"] },
|
|
100
|
+
"animationDuration": { "type": "token", "group": "motion" }
|
|
101
|
+
},
|
|
102
|
+
"animations": {
|
|
103
|
+
"$comment": "Round 2 (docs/round-2-contract.md §2): the keyframes behind the `animation` key. Channels: `opacity` (multiplies the node's), `translateX`/`translateY` (px), `rotate` (deg), `scale`, applied OUTSIDE the node's own `transform` about the box centre; `band` = the shimmer highlight's position across the box (-1 = one box width before it, 1 = one after), painted over the content and clipped to the border box. Duration = the theme's `duration` token (or the node's `animationDuration`) × `factor`; `easing` applies PER keyframe segment (CSS) and is a `$ease` token or `linear`; `iterations` = a count or `infinite`. An animation starts when the node enters the tree (first paint, `visible` turning true, a template item appearing). Reduced motion: no animation, the node shows the keyframe at `rest` (0 = the first, 1 = the last; shimmer draws no band). Channels are PHYSICAL, like `transform`: `direction` never mirrors them (in rtl too, `slide-in-left` moves leftwards and the shimmer band sweeps left → right).",
|
|
104
|
+
"pulse": { "duration": "$motion.slow", "factor": 7, "easing": "$ease.standard", "iterations": "infinite", "rest": 0, "keyframes": [{ "offset": 0, "opacity": 1 }, { "offset": 0.5, "opacity": 0.5 }, { "offset": 1, "opacity": 1 }] },
|
|
105
|
+
"spin": { "duration": "$motion.slow", "factor": 4, "easing": "linear", "iterations": "infinite", "rest": 0, "keyframes": [{ "offset": 0, "rotate": 0 }, { "offset": 1, "rotate": 360 }] },
|
|
106
|
+
"fade-in": { "duration": "$motion.standard", "factor": 1, "easing": "$ease.decelerate", "iterations": 1, "rest": 1, "keyframes": [{ "offset": 0, "opacity": 0 }, { "offset": 1, "opacity": 1 }] },
|
|
107
|
+
"slide-in-up": { "duration": "$motion.standard", "factor": 1, "easing": "$ease.decelerate", "iterations": 1, "rest": 1, "keyframes": [{ "offset": 0, "opacity": 0, "translateY": 8 }, { "offset": 1, "opacity": 1, "translateY": 0 }] },
|
|
108
|
+
"slide-in-down": { "duration": "$motion.standard", "factor": 1, "easing": "$ease.decelerate", "iterations": 1, "rest": 1, "keyframes": [{ "offset": 0, "opacity": 0, "translateY": -8 }, { "offset": 1, "opacity": 1, "translateY": 0 }] },
|
|
109
|
+
"slide-in-left": { "duration": "$motion.standard", "factor": 1, "easing": "$ease.decelerate", "iterations": 1, "rest": 1, "keyframes": [{ "offset": 0, "opacity": 0, "translateX": 8 }, { "offset": 1, "opacity": 1, "translateX": 0 }] },
|
|
110
|
+
"slide-in-right": { "duration": "$motion.standard", "factor": 1, "easing": "$ease.decelerate", "iterations": 1, "rest": 1, "keyframes": [{ "offset": 0, "opacity": 0, "translateX": -8 }, { "offset": 1, "opacity": 1, "translateX": 0 }] },
|
|
111
|
+
"shimmer": { "duration": "$motion.slow", "factor": 5, "easing": "linear", "iterations": "infinite", "rest": 0, "keyframes": [{ "offset": 0, "band": -1 }, { "offset": 1, "band": 1 }], "band": { "color": "$color.background", "stops": [{ "offset": 0, "alpha": 0 }, { "offset": 0.5, "alpha": 0.5 }, { "offset": 1, "alpha": 0 }] } }
|
|
112
|
+
},
|
|
113
|
+
"conditions": {
|
|
114
|
+
"media": "^@media \\((min-width|max-width|min-height|max-height): (\\d+(\\.\\d+)?px|\\$breakpoint\\.[a-zA-Z0-9]+)\\)$|^@media \\(orientation: (portrait|landscape)\\)$|^@media \\(hover: (hover|none)\\)$|^@media \\(prefers-reduced-motion: (reduce|no-preference)\\)$",
|
|
115
|
+
"states": [":hover", ":focus-visible", ":pressed"],
|
|
116
|
+
"transform": "^((translate\\(-?\\d+(\\.\\d+)?px, -?\\d+(\\.\\d+)?px\\)|scale\\(-?\\d+(\\.\\d+)?\\)|rotate\\(-?\\d+(\\.\\d+)?deg\\))( (?=\\S)|$))+$"
|
|
117
|
+
},
|
|
118
|
+
"textShimmer": {
|
|
119
|
+
"$comment": "EXP-1324: the Text `shimmer` prop (a running TraceItem title) = packages/design-tokens motion.shimmer (shadcn's `shimmer` utility), copied here because the SDK imports nothing from the app (src/text-shimmer.test.ts keeps them equal). The glyphs are filled with a linear gradient at 90deg + `angleDeg` (CSS angle: 90deg = left → right): base = the text colour, highlight (dark mode) = the text colour in OKLCH at lightness min(1, max(`highlightLightnessMin`, L + `highlightLightnessBoost`)), same a/b, alpha min(1, alpha + `highlightAlphaBoost`); light mode = the text colour at alpha × `lightAlphaFactor`; mid = the 50% OKLab mix of the two, alpha-premultiplied. Stops: base @ 50% - spread, mid @ 50% - spread/2, highlight @ 50%, mid @ 50% + spread/2, base @ 50% + spread (spread = `spreadPx`). The gradient is 200% of the text box width + 2 × spread wide and slides linearly from CSS background-position `100% 0` (its right edge on the box's right edge) to `0 0` (its left edge on the box's left edge) over `durationMs`, forever; rtl reverses. Reduced motion = the plain text. src/text-shimmer.ts textShimmer(colour, dark) is the reference (the Rust core `text_shimmer`, FFI `text_shimmer_json`); fixtures/text-shimmer.json locks the colours.",
|
|
120
|
+
"durationMs": 2000,
|
|
121
|
+
"angleDeg": 20,
|
|
122
|
+
"spreadPx": 60,
|
|
123
|
+
"highlightLightnessMin": 0.8,
|
|
124
|
+
"highlightLightnessBoost": 0.4,
|
|
125
|
+
"highlightAlphaBoost": 0.4,
|
|
126
|
+
"lightAlphaFactor": 0.2
|
|
127
|
+
},
|
|
128
|
+
"voiceWave": {
|
|
129
|
+
"$comment": "NEW-77: the VoiceWave native (the EXP-1268 draft made exact): a microphone or speaker level drawn as three closed polar waves around one optional centre child. Per ring k (0 outer, 1 mid, 2 inner): r(t) = size × radius[k] + Σ_j amp_k / (j + 1) × band(k, j) × sin(harmonics[k][j] × t + (phase0[k] + phase) × (j + 1)), t = 2π i / steps for i = 0..steps (the last point repeats the first), x = size/2 + r cos t, y = size/2 + r sin t (y down). amp_k = ringAmp[k] × size / 200 × (activity listening | speaking: ampBase + ampLevel × level; thinking: thinkingAmp; idle | muted: 0). band(k, j) = bands[(3k + j) mod len] clamped to 0..1, or 1 when bands is empty or the activity is thinking; level is clamped to 0..1 and only the first maxBands bands count. phase = the elapsed seconds × phaseSpeed (rad/s) while listening, thinking or speaking; 0 when idle or muted and under reduced motion. Each ring is filled with its part colour at fillAlpha[k] and stroked at strokeAlpha[k], width max(1, strokeWidth[k] × size / 200); the root recipe's opacity applies to the whole wave. THE CHILD RULE: the core lays the one child out centred in the box at its own size (flex, centre, centre); childSize = childScale × size (lg and xl: childScaleLarge; 0 at `sm`, below 40 px); the painter draws the child scaled about the box centre by min(1, childSize / the child's longer edge), never up, and at childSize 0 keeps it in the tree but paints nothing (no hit target, hidden from assistive tech). voiceWaveChildBox(size, child) is that box; the fixture's `child` cases lock it and every painter measures its drawn child against them. src/voice-wave.ts voiceWave(...) is the reference (the Rust core `voice_wave`, FFI `voice_wave_json`); fixtures/voice-wave.json locks the points. VAPP-162: this is `variant: ring` (the default); `variant: bars` is the level meter of `bars` below (same activity, level, bands and phase inputs).",
|
|
130
|
+
"steps": 120,
|
|
131
|
+
"radius": [0.33, 0.3, 0.26],
|
|
132
|
+
"harmonics": [[11, 17, 23], [9, 15, 21], [7, 13]],
|
|
133
|
+
"phase0": [0.4, 1.9, 3.1],
|
|
134
|
+
"ringAmp": [1, 0.85, 0.45],
|
|
135
|
+
"ampBase": 2,
|
|
136
|
+
"ampLevel": 6,
|
|
137
|
+
"thinkingAmp": 2.5,
|
|
138
|
+
"phaseSpeed": 0.9,
|
|
139
|
+
"fillAlpha": [0.1, 0.1, 0.15],
|
|
140
|
+
"strokeAlpha": [0.7, 0.8, 0.4],
|
|
141
|
+
"strokeWidth": [1.5, 1.5, 1],
|
|
142
|
+
"childScale": 0.3,
|
|
143
|
+
"childScaleLarge": 0.22,
|
|
144
|
+
"maxBands": 8,
|
|
145
|
+
"bars": {
|
|
146
|
+
"$comment": "VAPP-162: the `bars` variant (a horizontal level meter, no child; a child is kept in the tree but never painted, like `sm`): `count` rounded vertical bars side by side, sized per `sizes[size]` = {height, barWidth, gap} (the root box = count \u00d7 barWidth + (count \u2212 1) \u00d7 gap wide, `height` tall: the root recipe's `variant: bars` rules). Bar i (0-based): drive d = listening | speaking: (ampBase + ampLevel \u00d7 level) / (ampBase + ampLevel); thinking: thinkingAmp / (ampBase + ampLevel); idle | muted: 0; band b = bands[i mod len] clamped (1 when bands is empty or thinking); envelope e = 1 \u2212 envelope \u00d7 |2i / (count \u2212 1) \u2212 1| (the middle bars stand tallest); shape s = 0.5 + 0.5 \u00d7 sin(shapeFreq \u00d7 i + phase \u00d7 (1 + (i mod 3) \u00d7 0.5)) with the ring's phase (voiceWavePhase: 0 when idle, muted or under reduced motion = a static meter); fraction f = minFraction + (1 \u2212 minFraction) \u00d7 d \u00d7 b \u00d7 e \u00d7 (shapeBase + (1 \u2212 shapeBase) \u00d7 s); the bar is height \u00d7 f tall (rounded to 0.01), centred vertically (y = (height \u2212 h) / 2), at x = i \u00d7 (barWidth + gap), corner radius barWidth / 2, filled with the `bar` part colour at the root recipe's opacity. voiceWaveBars(input) is the reference; fixtures/voice-wave.json `bars` cases lock it.",
|
|
147
|
+
"count": 20,
|
|
148
|
+
"minFraction": 0.12,
|
|
149
|
+
"envelope": 0.5,
|
|
150
|
+
"shapeFreq": 1.7,
|
|
151
|
+
"shapeBase": 0.35,
|
|
152
|
+
"sizes": {
|
|
153
|
+
"sm": { "height": 16, "barWidth": 2, "gap": 2 },
|
|
154
|
+
"md": { "height": 24, "barWidth": 3, "gap": 3 },
|
|
155
|
+
"lg": { "height": 40, "barWidth": 4, "gap": 4 },
|
|
156
|
+
"xl": { "height": 56, "barWidth": 5, "gap": 5 }
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"qrCode": {
|
|
161
|
+
"$comment": "VAPP-162: the QrCode native. THE MATRIX COMES FROM THE CORE: src/qr-code.ts qrMatrix(value, level) (a port of Project Nayuki's QR Code generator, MIT: byte mode over the value's UTF-8 bytes, the smallest version 1\u201340 that fits at the given ECC level, the level kept exact (no boost), the mask with the lowest penalty) \u2192 {size n, rows: n strings of '1' (dark) / '0' (light)}, no quiet zone; the Rust core ports it and fixtures/qr-code.json locks every matrix bit. An empty value, or one longer than maxValueBytes UTF-8 bytes at level L (fewer at higher levels: whatever version 40 holds), has NO matrix (`qrMatrix` returns null): the painter shows the plate empty with the label, never throws. PAINT: the root is a column (centred, gap labelGap) of the plate and the label. The plate is `size` px square (the prop, default 160) with radius = the plate recipe's borderRadius; it holds the matrix inside a quiet zone of quietZone modules on every side, so module = size / (n + 2 \u00d7 quietZone) px (fractional; painters snap module edges to device pixels, never round the module itself, so the code keeps its exact outer size). DARK MODULES ON A LIGHT PLATE ALWAYS: of the two resolved recipe colours (plate and module) the lighter one (higher relative luminance) fills the plate and the darker one the modules, so a dark theme still scans. The label (when given) is a caption Text, muted, centred, under the plate, and the accessibility label of the img.",
|
|
162
|
+
"quietZone": 4,
|
|
163
|
+
"labelGap": 8,
|
|
164
|
+
"maxValueBytes": 2953
|
|
165
|
+
},
|
|
166
|
+
"qrScanner": {
|
|
167
|
+
"$comment": "VAPP-162: the QrScanner native = the VIEWFINDER CHROME; THE CAMERA IS HOST-OWNED (like FileUpload's bytes). The renderer asks its host for a camera feed through a RENDERER-LEVEL hook each lane defines in its own package API (never catalog/host.json): React/Solid/element a `qrScanner` host option, SwiftUI an environment value, Compose a CompositionLocal, gpui a host trait; the host shows its live feed inside `feed`, decodes codes of the given `formats` and reports a decoded string, which the renderer dispatches as `scanned {value}` (once per distinct value until the value changes or the scanner re-mounts; `disabled` stops reporting). Geometry: the root is a column (centred, gap hintGap); the viewfinder is a square (aspectRatio 1) as wide as the root up to maxWidth px, radius = the viewfinder recipe's borderRadius, clipping; `feed` fills it (the host's video; without one the `feed` recipe colour at feedAlpha alpha); four `corner` brackets sit at the inside corners, each two strokes cornerLength px long and cornerStroke px wide in the corner recipe colour (rounded caps, the outer edge following the viewfinder radius); the `scanLine` is a scanLineHeight px bar inset scanLineInset px from the left and right edges, sweeping top \u2192 bottom \u2192 top over sweepMs (ease standard, alternate) while a feed shows; reduce motion (or disabled) = the line static at the vertical centre. No host camera = no feed and no line: the `placeholderIcon` (concept ui-scan = Lucide scan-line) placeholderIconSize px centred in the placeholderIcon colour and the built-in string `cameraUnavailable` as a caption under it. The `hint` (when given) is a muted Text centred under the viewfinder.",
|
|
168
|
+
"maxWidth": 280,
|
|
169
|
+
"aspectRatio": 1,
|
|
170
|
+
"cornerLength": 24,
|
|
171
|
+
"cornerStroke": 3,
|
|
172
|
+
"feedAlpha": 0.06,
|
|
173
|
+
"scanLineHeight": 2,
|
|
174
|
+
"scanLineInset": 16,
|
|
175
|
+
"sweepMs": 2000,
|
|
176
|
+
"hintGap": 12,
|
|
177
|
+
"placeholderIcon": "ui-scan",
|
|
178
|
+
"placeholderIconSize": 32
|
|
179
|
+
}
|
|
180
|
+
}
|