@exponential_app/eui 0.2.0 → 0.3.0

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.
Files changed (69) hide show
  1. package/catalog/a11y.json +15 -1
  2. package/catalog/core.catalog.json +142 -0
  3. package/catalog/core.schema.json +198 -1
  4. package/catalog/host.json +10 -0
  5. package/catalog/macros.json +541 -1
  6. package/catalog/recipes.json +2 -2
  7. package/catalog/strings.json +5 -0
  8. package/catalog/theme.schema.json +2066 -212
  9. package/conformance/manifest.json +5 -5
  10. package/dist/chunks/{index-gtk2gwsb.js → index-4z7f2bp1.js} +160 -33
  11. package/dist/chunks/{index-gtk2gwsb.js.map → index-4z7f2bp1.js.map} +6 -5
  12. package/dist/chunks/{index-5fmaa0xq.js → index-abcjanhc.js} +629 -3
  13. package/dist/chunks/{index-5fmaa0xq.js.map → index-abcjanhc.js.map} +2 -2
  14. package/dist/chunks/{index-62yzr94z.js → index-dx45asrf.js} +99 -3
  15. package/dist/chunks/{index-62yzr94z.js.map → index-dx45asrf.js.map} +2 -2
  16. package/dist/chunks/{index-bg2jsgjq.js → index-h3zgfghf.js} +304 -6
  17. package/dist/chunks/{index-bg2jsgjq.js.map → index-h3zgfghf.js.map} +2 -2
  18. package/dist/conformance.js +7901 -614
  19. package/dist/conformance.js.map +2 -2
  20. package/dist/entries/prompt.js +2 -2
  21. package/dist/entries/schema.js +2 -2
  22. package/dist/host/index.js +20 -4
  23. package/dist/host/index.js.map +1 -1
  24. package/dist/index.js +46 -5
  25. package/dist/index.js.map +4 -3
  26. package/dist/types/src/catalog.generated.d.ts +23 -3
  27. package/dist/types/src/host/contract.d.ts +17 -0
  28. package/dist/types/src/host/exposes.d.ts +68 -0
  29. package/dist/types/src/host/index.d.ts +1 -0
  30. package/dist/types/src/host/package.d.ts +8 -0
  31. package/dist/types/src/index.d.ts +1 -0
  32. package/dist/types/src/text-clusters.d.ts +11 -0
  33. package/dist/types/src/web/primitives/carousel.d.ts +17 -0
  34. package/dist/types/src/web/primitives/index.d.ts +3 -0
  35. package/dist/types/src/web/primitives/mosaic.d.ts +30 -0
  36. package/dist/types/src/web/primitives/outline.d.ts +22 -0
  37. package/dist/web/index.js +8 -2
  38. package/dist/web/index.js.map +4 -4
  39. package/dist/web/primitives/index.js +59 -1
  40. package/dist/web/primitives/index.js.map +6 -3
  41. package/docs/components.generated.json +150 -1
  42. package/docs/package-format.md +56 -1
  43. package/docs/round-6-contract.md +108 -0
  44. package/docs/themes.generated.json +39 -2
  45. package/fixtures/bind-time.json +1464 -0
  46. package/fixtures/catalog-components.json +96 -0
  47. package/fixtures/catalog-macros.json +1159 -48
  48. package/fixtures/conformance-baseline.json +46073 -44448
  49. package/fixtures/conformance-known.json +76 -64
  50. package/fixtures/host-router.json +539 -1
  51. package/fixtures/kitchen-sink.expanded.json +614 -1
  52. package/fixtures/kitchen-sink.json +69 -1
  53. package/fixtures/primitive-specimens.json +3 -0
  54. package/fixtures/prompt-budget.json +14 -14
  55. package/fixtures/specimens.json +122 -0
  56. package/fixtures/text-clusters.json +65 -0
  57. package/fixtures/theme-recipes.json +4617 -1691
  58. package/fixtures/theme-resolved.json +1154 -2
  59. package/generated/ExponentialUICatalog.generated.kt +13 -13
  60. package/generated/ExponentialUICatalog.generated.swift +13 -13
  61. package/generated/ExponentialUIThemes.generated.kt +7 -7
  62. package/generated/ExponentialUIThemes.generated.swift +7 -7
  63. package/generated/catalog.descriptions.generated.rs +1 -1
  64. package/generated/catalog.generated.rs +18 -18
  65. package/generated/catalog.themes.generated.rs +8 -8
  66. package/generated/core.descriptions.json +96 -0
  67. package/generated/core.runtime.json +86 -0
  68. package/package.json +1 -1
  69. package/themes/neutral.theme.json +288 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "$comment": "VAPP-85: the ONE declarative macro table. A macro component (core.catalog.json `kind: macro`) expands into this template before painting; the TS reference expander (src/macros.ts) and the Rust core apply the same table and fixtures/catalog-macros.json locks the output. Template node = { part, component, props?, style?, children?, slots?, $if?, $any?, $each?, $as?, $on?, $context?, $set?, $recipe?, $a11y? }. Values: `\"{props.x}\"` alone = the raw prop (a data BINDING passes through untouched, so two-way binding survives); `\"{props.x|fallback}\"` = with a default; `{…}` inside a longer string interpolates; `{id}` = the macro node's id; `{len(props.rows)}` = an array length; `{range(props.max|5)}` = the indexes 0..n-1 (for `$each`); `{index}` = the current $each index; `{!path}` negates. Value objects: $map {from, cases, default}, $cond [condition, then, else], $eq [a, b] (a number equals a string spelling it), $lt [a, b], $add [a, b], $sub [a, b], $clamp [value, min, max] (a missing bound does not clamp), $percent [value, max] (→ \"N%\"), $coalesce [a, b, …], $text value (→ the value as a string, absent when missing), $not condition, $fill [template, {name: value}] (ALWAYS emits the core `fill` call: the template is usually a `$string.<id>` sentence with `{name}` placeholders the surface's string table resolves at bind time). Object KEYS interpolate too (`\"@media (max-width: $breakpoint.{props.collapseBelow|md})\"`). `$if` = a condition or an array of conditions (all must hold); `$any` = an array where one must hold. `$each` repeats a part per item of an array (`$as` names the item, default `item`). Children: `\"$children\"` splices the author's children, `\"$slot:name\"` splices an author slot in place; `slots: {\"trigger\": \"$slot:trigger\"}` copies an author slot, `slots: {\"footer\": {template node}}` builds one as a part. `$on: {\"press\": \"change\"}` routes the author's `on.change` handler to this part's `press` (merging `$context` into an event action's context); without `$on` the author's `on` map stays on the root. `$set: {\"press\": {\"prop\": \"open\", \"value\": \"{!props.open}\"}}` = TWO-WAY BINDING (round 1): when the author's `props.open` is a data binding `{path}`, this part's `press` action also carries `functionCall: {call: \"set\", args: {path, value}}` (the value evaluated like any template value, so usually a call), key order `event` then `functionCall`; a literal prop adds nothing (the host owns the state through the routed event); when the author's routed handler is itself a FUNCTION (an Action carries one), it wins, no `set` is added and the reducer reports an issue. `$a11y: {role, label, description, level, expanded, current, selected, pressed, checked, valueNow, valueMin, valueMax, hidden, autoFocus}` = the part's ACCESSIBILITY (docs/round-1-contract.md §6): evaluated like props (a bound input emits a call) into the expanded node's `accessibility`; `role` is one of catalog/a11y.json `roles`; on the root the author's `accessibility` label/description win. Ids: the root keeps the author's id, parts get `<id>.<part>` and `<id>.<part>.<index>` under `$each`. Every expanded node carries `recipe: {macro, part, props}` where props = `recipeProps` evaluated from the author's props (+ a part's `$recipe`); the THEME (VAPP-92) keys its visual recipes on that, so nothing here states a colour, a border or a font — only structure and token-named spacing, radius and control heights. VAPP-163: the expansion ROOT's recipe also carries `authored` = the top-level keys of the style written ON the macro node (condition blocks count as one key), SORTED, omitted when none; painters apply native recipe < node style < part recipe < those authored keys, so an author's colour or size on a Heading/Badge beats the theme's part recipe while the template's own structure does not (only the author's keys count: a macro node an outer template drew passes on just its root's `authored`, never template style). Macros may use other macros (Row → TreeGuides); expansion recurses until every node is native (innermost recipe wins). ROUND 1 (docs/round-1-contract.md §1, BOUND INPUTS): when an expression input is a data binding `{path}` or a function call, the expander does NOT evaluate — it EMITS an A2UI function call the renderer evaluates at bind time, over the core functions: interpolation → concat, `|fallback` → fallback (as an operand; a whole-string MEMBER of props/style/context passes the binding itself through, so two-way binding survives), `!` → not, len → len, $map → map, $cond → cond, $eq → eq, $lt → lt, $add → add, $sub → sub, $percent → percent, $text → text, $coalesce → coalesce, $not → not; a `$if`/`$any` whose conditions are bound emits the part WITH `visible` = the bound condition (`and{values}` over several bound `$if`s, `or{values}` over several bound `$any`s; literal conditions still decide at expansion); `$each` over a bound array emits nothing (use List with a template); a `$recipe` value may therefore be a call too (renderers resolve recipe props like props). RESPONSIVE PROPS: an author prop `{base, sm?, md?, lg?, xl?}` on a `responsive` prop is evaluated per breakpoint: a part's style is computed with the `base` values, then once per breakpoint named (ascending sm<md<lg<xl, each key inheriting the last smaller one) and every key that changed lands in a `\"@media (min-width: $breakpoint.<bp>)\"` block of that part; props and conditions see the `base` value. Strings of the form `$string.<id>` (catalog/strings.json) are built-in UI copy the renderer shows in the surface locale. Round 3 (VAPP-102): AFTER the table is applied the core runs ONE post-expansion pass, src/tree-guides.ts applyTreeGuides (Rust: tree_guides::apply): over every node's children in order, each Row root (`recipe.macro` Row, part root) contributes its numeric `recipe.props.depth` (anything else counts as depth 0 and ends every subtree), the ×4 treeGuides rule (elbow at depth-1, tee while a later sibling follows, pass-through for every ancestor level that continues) fills the Row's `guides` part (a TreeGuides) with `elbowAt`/`tee`/`passThrough`; fixtures/tree-guides.json locks the rule, catalog-macros.json the filled trees.",
2
+ "$comment": "VAPP-85: the ONE declarative macro table. A macro component (core.catalog.json `kind: macro`) expands into this template before painting; the TS reference expander (src/macros.ts) and the Rust core apply the same table and fixtures/catalog-macros.json locks the output. Template node = { part, component, props?, style?, children?, slots?, $if?, $any?, $each?, $as?, $on?, $context?, $set?, $recipe?, $a11y? }. Values: `\"{props.x}\"` alone = the raw prop (a data BINDING passes through untouched, so two-way binding survives); `\"{props.x|fallback}\"` = with a default; `{…}` inside a longer string interpolates; `{id}` = the macro node's id; `{len(props.rows)}` = an array length; `{range(props.max|5)}` = the indexes 0..n-1 (for `$each`); `{index}` = the current $each index; `{!path}` negates. Value objects: $map {from, cases, default}, $cond [condition, then, else], $eq [a, b] (a number equals a string spelling it), $lt [a, b], $add [a, b], $sub [a, b], $clamp [value, min, max] (a missing bound does not clamp), $percent [value, max] (→ \"N%\"), $coalesce [a, b, …], $text value (→ the value as a string, absent when missing), $not condition, $fill [template, {name: value}] (ALWAYS emits the core `fill` call: the template is usually a `$string.<id>` sentence with `{name}` placeholders the surface's string table resolves at bind time). Object KEYS interpolate too (`\"@media (max-width: $breakpoint.{props.collapseBelow|md})\"`). `$if` = a condition or an array of conditions (all must hold); `$any` = an array where one must hold. `$each` repeats a part per item of an array (`$as` names the item, default `item`). Children: `\"$children\"` splices the author's children, `\"$slot:name\"` splices an author slot in place; `slots: {\"trigger\": \"$slot:trigger\"}` copies an author slot, `slots: {\"footer\": {template node}}` builds one as a part. `$on: {\"press\": \"change\"}` routes the author's `on.change` handler to this part's `press` (merging `$context` into an event action's context); without `$on` the author's `on` map stays on the root. `$set: {\"press\": {\"prop\": \"open\", \"value\": \"{!props.open}\"}}` = TWO-WAY BINDING (round 1): when the author's `props.open` is a data binding `{path}`, this part's `press` action also carries `functionCall: {call: \"set\", args: {path, value}}` (the value evaluated like any template value, so usually a call), key order `event` then `functionCall`; a literal prop adds nothing (the host owns the state through the routed event); when the author's routed handler is itself a FUNCTION (an Action carries one), it wins, no `set` is added and the reducer reports an issue. `$a11y: {role, label, description, level, expanded, current, selected, pressed, checked, valueNow, valueMin, valueMax, hidden, autoFocus}` = the part's ACCESSIBILITY (docs/round-1-contract.md §6): evaluated like props (a bound input emits a call) into the expanded node's `accessibility`; `role` is one of catalog/a11y.json `roles`; on the root the author's `accessibility` label/description win. Ids: the root keeps the author's id, parts get `<id>.<part>` and `<id>.<part>.<index>` under `$each`. Every expanded node carries `recipe: {macro, part, props}` where props = `recipeProps` evaluated from the author's props (+ a part's `$recipe`); the THEME (VAPP-92) keys its visual recipes on that, so nothing here states a colour, a border or a font — only structure and token-named spacing, radius and control heights. VAPP-163: the expansion ROOT's recipe also carries `authored` = the top-level keys of the style written ON the macro node (condition blocks count as one key), SORTED, omitted when none; painters apply native recipe < node style < part recipe < those authored keys, so an author's colour or size on a Heading/Badge beats the theme's part recipe while the template's own structure does not (only the author's keys count: a macro node an outer template drew passes on just its root's `authored`, never template style). Macros may use other macros (Row → TreeGuides); expansion recurses until every node is native (innermost recipe wins). ROUND 1 (docs/round-1-contract.md §1, BOUND INPUTS): when an expression input is a data binding `{path}` or a function call, the expander does NOT evaluate — it EMITS an A2UI function call the renderer evaluates at bind time, over the core functions: interpolation → concat, `|fallback` → fallback (as an operand; a whole-string MEMBER of props/style/context passes the binding itself through, so two-way binding survives), `!` → not, len → len, $map → map, $cond → cond, $eq → eq, $lt → lt, $add → add, $sub → sub, $percent → percent, $text → text, $coalesce → coalesce, $not → not; a `$if`/`$any` whose conditions are bound emits the part WITH `visible` = the bound condition (`and{values}` over several bound `$if`s, `or{values}` over several bound `$any`s; literal conditions still decide at expansion); `$each` over a bound array emits nothing (use List with a template); a `$recipe` value may therefore be a call too (renderers resolve recipe props like props). RESPONSIVE PROPS: an author prop `{base, sm?, md?, lg?, xl?}` on a `responsive` prop is evaluated per breakpoint: a part's style is computed with the `base` values, then once per breakpoint named (ascending sm<md<lg<xl, each key inheriting the last smaller one) and every key that changed lands in a `\"@media (min-width: $breakpoint.<bp>)\"` block of that part; props and conditions see the `base` value. Strings of the form `$string.<id>` (catalog/strings.json) are built-in UI copy the renderer shows in the surface locale. Round 3 (VAPP-102): AFTER the table is applied the core runs ONE post-expansion pass, src/tree-guides.ts applyTreeGuides (Rust: tree_guides::apply): over every node's children in order, each Row root (`recipe.macro` Row, part root) contributes its numeric `recipe.props.depth` (anything else counts as depth 0 and ends every subtree), the ×4 treeGuides rule (elbow at depth-1, tee while a later sibling follows, pass-through for every ancestor level that continues) fills the Row's `guides` part (a TreeGuides) with `elbowAt`/`tee`/`passThrough`; fixtures/tree-guides.json locks the rule, catalog-macros.json the filled trees. EXP-1495 (docs/round-6-contract.md): Outline (the anchor list) and Mosaic (the wide picture group) are macros over Box/Text/Image.",
3
3
  "macros": {
4
4
  "Stack": {
5
5
  "recipeProps": [],
@@ -2333,6 +2333,169 @@
2333
2333
  ]
2334
2334
  }
2335
2335
  },
2336
+ "Outline": {
2337
+ "recipeProps": [],
2338
+ "root": {
2339
+ "part": "root",
2340
+ "component": "Box",
2341
+ "$a11y": {
2342
+ "role": "navigation",
2343
+ "label": "{props.caption|$string.outline}"
2344
+ },
2345
+ "style": {
2346
+ "display": "flex",
2347
+ "flexDirection": "column",
2348
+ "gap": "$spacing.xxs",
2349
+ "minWidth": 0
2350
+ },
2351
+ "children": [
2352
+ {
2353
+ "part": "caption",
2354
+ "component": "Text",
2355
+ "$if": "props.caption",
2356
+ "props": {
2357
+ "text": "{props.caption}",
2358
+ "variant": "caption",
2359
+ "lines": 1
2360
+ },
2361
+ "$a11y": {
2362
+ "hidden": true
2363
+ }
2364
+ },
2365
+ {
2366
+ "part": "row",
2367
+ "component": "Box",
2368
+ "$each": "props.items",
2369
+ "$as": "item",
2370
+ "props": {
2371
+ "pressable": true
2372
+ },
2373
+ "style": {
2374
+ "display": "flex",
2375
+ "flexDirection": "row",
2376
+ "alignItems": "center",
2377
+ "gap": "$spacing.xs",
2378
+ "position": "relative",
2379
+ "height": "$control.rowCompact",
2380
+ "paddingHorizontal": "$spacing.sm",
2381
+ "minWidth": 0,
2382
+ "overflow": "hidden",
2383
+ "cursor": "pointer"
2384
+ },
2385
+ "$recipe": {
2386
+ "active": {
2387
+ "$eq": [
2388
+ "{item.id}",
2389
+ "{props.value}"
2390
+ ]
2391
+ }
2392
+ },
2393
+ "$a11y": {
2394
+ "role": "link",
2395
+ "label": {
2396
+ "$cond": [
2397
+ "item.count",
2398
+ {
2399
+ "$fill": [
2400
+ "$string.outlineRow",
2401
+ {
2402
+ "title": "{item.title}",
2403
+ "count": "{item.count}"
2404
+ }
2405
+ ]
2406
+ },
2407
+ "{item.title}"
2408
+ ]
2409
+ },
2410
+ "current": {
2411
+ "$cond": [
2412
+ {
2413
+ "$eq": [
2414
+ "{item.id}",
2415
+ "{props.value}"
2416
+ ]
2417
+ },
2418
+ "location",
2419
+ false
2420
+ ]
2421
+ }
2422
+ },
2423
+ "$on": {
2424
+ "press": "select"
2425
+ },
2426
+ "$context": {
2427
+ "id": "{item.id}"
2428
+ },
2429
+ "$set": {
2430
+ "press": {
2431
+ "prop": "value",
2432
+ "value": "{item.id}"
2433
+ }
2434
+ },
2435
+ "children": [
2436
+ {
2437
+ "part": "bar",
2438
+ "component": "Box",
2439
+ "$if": {
2440
+ "$eq": [
2441
+ "{item.id}",
2442
+ "{props.value}"
2443
+ ]
2444
+ },
2445
+ "$a11y": {
2446
+ "hidden": true
2447
+ },
2448
+ "style": {
2449
+ "position": "absolute",
2450
+ "insetInlineStart": 0,
2451
+ "top": 0,
2452
+ "bottom": 0,
2453
+ "width": 2
2454
+ }
2455
+ },
2456
+ {
2457
+ "part": "title",
2458
+ "component": "Text",
2459
+ "props": {
2460
+ "text": "{item.title}",
2461
+ "lines": 1
2462
+ },
2463
+ "$recipe": {
2464
+ "active": {
2465
+ "$eq": [
2466
+ "{item.id}",
2467
+ "{props.value}"
2468
+ ]
2469
+ }
2470
+ },
2471
+ "style": {
2472
+ "flexGrow": 1,
2473
+ "flexShrink": 1,
2474
+ "flexBasis": 0,
2475
+ "minWidth": 0
2476
+ }
2477
+ },
2478
+ {
2479
+ "part": "count",
2480
+ "component": "Text",
2481
+ "$if": "item.count",
2482
+ "props": {
2483
+ "text": "· {item.count}",
2484
+ "variant": "caption",
2485
+ "lines": 1
2486
+ },
2487
+ "$a11y": {
2488
+ "hidden": true
2489
+ },
2490
+ "style": {
2491
+ "flexShrink": 0
2492
+ }
2493
+ }
2494
+ ]
2495
+ }
2496
+ ]
2497
+ }
2498
+ },
2336
2499
  "Stepper": {
2337
2500
  "recipeProps": [
2338
2501
  "orientation"
@@ -4147,6 +4310,383 @@
4147
4310
  }
4148
4311
  ]
4149
4312
  }
4313
+ },
4314
+ "Mosaic": {
4315
+ "recipeProps": [],
4316
+ "root": {
4317
+ "part": "root",
4318
+ "component": "Box",
4319
+ "$a11y": {
4320
+ "role": "group",
4321
+ "label": "{props.label}"
4322
+ },
4323
+ "style": {
4324
+ "display": "flex",
4325
+ "flexDirection": "row",
4326
+ "width": "100%",
4327
+ "aspectRatio": 1.6,
4328
+ "minWidth": 0
4329
+ },
4330
+ "children": [
4331
+ {
4332
+ "part": "lead",
4333
+ "component": "Box",
4334
+ "$if": "props.pictures.0",
4335
+ "props": {
4336
+ "pressable": true
4337
+ },
4338
+ "style": {
4339
+ "position": "relative",
4340
+ "overflow": "hidden",
4341
+ "minWidth": 0,
4342
+ "minHeight": 0,
4343
+ "cursor": "pointer",
4344
+ "flexGrow": {
4345
+ "$cond": [
4346
+ {
4347
+ "$lt": [
4348
+ 2,
4349
+ "{len(props.pictures)}"
4350
+ ]
4351
+ },
4352
+ 0,
4353
+ 1
4354
+ ]
4355
+ },
4356
+ "flexShrink": {
4357
+ "$cond": [
4358
+ {
4359
+ "$lt": [
4360
+ 2,
4361
+ "{len(props.pictures)}"
4362
+ ]
4363
+ },
4364
+ 0,
4365
+ 1
4366
+ ]
4367
+ },
4368
+ "flexBasis": {
4369
+ "$cond": [
4370
+ {
4371
+ "$lt": [
4372
+ 2,
4373
+ "{len(props.pictures)}"
4374
+ ]
4375
+ },
4376
+ "60%",
4377
+ 0
4378
+ ]
4379
+ },
4380
+ "height": "100%"
4381
+ },
4382
+ "$recipe": {
4383
+ "size": {
4384
+ "$cond": [
4385
+ {
4386
+ "$lt": [
4387
+ 2,
4388
+ "{len(props.pictures)}"
4389
+ ]
4390
+ },
4391
+ "large",
4392
+ {
4393
+ "$cond": [
4394
+ {
4395
+ "$lt": [
4396
+ 1,
4397
+ "{len(props.pictures)}"
4398
+ ]
4399
+ },
4400
+ "half",
4401
+ "large"
4402
+ ]
4403
+ }
4404
+ ]
4405
+ }
4406
+ },
4407
+ "$a11y": {
4408
+ "role": "button",
4409
+ "label": "{props.pictures.0.alt}"
4410
+ },
4411
+ "$on": {
4412
+ "press": "select"
4413
+ },
4414
+ "$context": {
4415
+ "index": 0
4416
+ },
4417
+ "children": [
4418
+ {
4419
+ "part": "image",
4420
+ "component": "Image",
4421
+ "props": {
4422
+ "src": "{props.pictures.0.src}",
4423
+ "alt": "{props.pictures.0.alt}",
4424
+ "fit": "cover"
4425
+ },
4426
+ "$a11y": {
4427
+ "hidden": true
4428
+ },
4429
+ "style": {
4430
+ "position": "absolute",
4431
+ "inset": 0,
4432
+ "width": "100%",
4433
+ "height": "100%"
4434
+ }
4435
+ }
4436
+ ]
4437
+ },
4438
+ {
4439
+ "part": "pair",
4440
+ "component": "Box",
4441
+ "$if": [
4442
+ {
4443
+ "$eq": [
4444
+ "{len(props.pictures)}",
4445
+ 2
4446
+ ]
4447
+ }
4448
+ ],
4449
+ "props": {
4450
+ "pressable": true
4451
+ },
4452
+ "style": {
4453
+ "position": "relative",
4454
+ "overflow": "hidden",
4455
+ "minWidth": 0,
4456
+ "minHeight": 0,
4457
+ "cursor": "pointer",
4458
+ "flexGrow": 1,
4459
+ "flexShrink": 1,
4460
+ "flexBasis": 0,
4461
+ "height": "100%"
4462
+ },
4463
+ "$recipe": {
4464
+ "size": "half"
4465
+ },
4466
+ "$a11y": {
4467
+ "role": "button",
4468
+ "label": "{props.pictures.1.alt}"
4469
+ },
4470
+ "$on": {
4471
+ "press": "select"
4472
+ },
4473
+ "$context": {
4474
+ "index": 1
4475
+ },
4476
+ "children": [
4477
+ {
4478
+ "part": "image",
4479
+ "component": "Image",
4480
+ "props": {
4481
+ "src": "{props.pictures.1.src}",
4482
+ "alt": "{props.pictures.1.alt}",
4483
+ "fit": "cover"
4484
+ },
4485
+ "$a11y": {
4486
+ "hidden": true
4487
+ },
4488
+ "style": {
4489
+ "position": "absolute",
4490
+ "inset": 0,
4491
+ "width": "100%",
4492
+ "height": "100%"
4493
+ }
4494
+ }
4495
+ ]
4496
+ },
4497
+ {
4498
+ "part": "side",
4499
+ "component": "Box",
4500
+ "$if": {
4501
+ "$lt": [
4502
+ 2,
4503
+ "{len(props.pictures)}"
4504
+ ]
4505
+ },
4506
+ "style": {
4507
+ "display": "flex",
4508
+ "flexDirection": "column",
4509
+ "flexGrow": 1,
4510
+ "flexShrink": 1,
4511
+ "flexBasis": 0,
4512
+ "minWidth": 0,
4513
+ "height": "100%"
4514
+ },
4515
+ "children": [
4516
+ {
4517
+ "part": "top",
4518
+ "component": "Box",
4519
+ "$if": "props.pictures.1",
4520
+ "props": {
4521
+ "pressable": true
4522
+ },
4523
+ "style": {
4524
+ "position": "relative",
4525
+ "overflow": "hidden",
4526
+ "minWidth": 0,
4527
+ "minHeight": 0,
4528
+ "cursor": "pointer",
4529
+ "flexGrow": 1,
4530
+ "flexShrink": 1,
4531
+ "flexBasis": 0,
4532
+ "width": "100%"
4533
+ },
4534
+ "$recipe": {
4535
+ "size": "small"
4536
+ },
4537
+ "$a11y": {
4538
+ "role": "button",
4539
+ "label": "{props.pictures.1.alt}"
4540
+ },
4541
+ "$on": {
4542
+ "press": "select"
4543
+ },
4544
+ "$context": {
4545
+ "index": 1
4546
+ },
4547
+ "children": [
4548
+ {
4549
+ "part": "image",
4550
+ "component": "Image",
4551
+ "props": {
4552
+ "src": "{props.pictures.1.src}",
4553
+ "alt": "{props.pictures.1.alt}",
4554
+ "fit": "cover"
4555
+ },
4556
+ "$a11y": {
4557
+ "hidden": true
4558
+ },
4559
+ "style": {
4560
+ "position": "absolute",
4561
+ "inset": 0,
4562
+ "width": "100%",
4563
+ "height": "100%"
4564
+ }
4565
+ }
4566
+ ]
4567
+ },
4568
+ {
4569
+ "part": "bottom",
4570
+ "component": "Box",
4571
+ "$if": "props.pictures.2",
4572
+ "props": {
4573
+ "pressable": true
4574
+ },
4575
+ "style": {
4576
+ "position": "relative",
4577
+ "overflow": "hidden",
4578
+ "minWidth": 0,
4579
+ "minHeight": 0,
4580
+ "cursor": "pointer",
4581
+ "flexGrow": 1,
4582
+ "flexShrink": 1,
4583
+ "flexBasis": 0,
4584
+ "width": "100%"
4585
+ },
4586
+ "$recipe": {
4587
+ "size": "small"
4588
+ },
4589
+ "$a11y": {
4590
+ "role": "button",
4591
+ "label": {
4592
+ "$cond": [
4593
+ {
4594
+ "$lt": [
4595
+ 3,
4596
+ "{len(props.pictures)}"
4597
+ ]
4598
+ },
4599
+ {
4600
+ "$fill": [
4601
+ "$string.morePicturesLabel",
4602
+ {
4603
+ "alt": "{props.pictures.2.alt}",
4604
+ "count": {
4605
+ "$sub": [
4606
+ "{len(props.pictures)}",
4607
+ 3
4608
+ ]
4609
+ }
4610
+ }
4611
+ ]
4612
+ },
4613
+ "{props.pictures.2.alt}"
4614
+ ]
4615
+ }
4616
+ },
4617
+ "$on": {
4618
+ "press": "select"
4619
+ },
4620
+ "$context": {
4621
+ "index": 2
4622
+ },
4623
+ "children": [
4624
+ {
4625
+ "part": "image",
4626
+ "component": "Image",
4627
+ "props": {
4628
+ "src": "{props.pictures.2.src}",
4629
+ "alt": "{props.pictures.2.alt}",
4630
+ "fit": "cover"
4631
+ },
4632
+ "$a11y": {
4633
+ "hidden": true
4634
+ },
4635
+ "style": {
4636
+ "position": "absolute",
4637
+ "inset": 0,
4638
+ "width": "100%",
4639
+ "height": "100%"
4640
+ }
4641
+ },
4642
+ {
4643
+ "part": "more",
4644
+ "component": "Box",
4645
+ "$if": {
4646
+ "$lt": [
4647
+ 3,
4648
+ "{len(props.pictures)}"
4649
+ ]
4650
+ },
4651
+ "$a11y": {
4652
+ "hidden": true
4653
+ },
4654
+ "style": {
4655
+ "position": "absolute",
4656
+ "inset": 0,
4657
+ "display": "flex",
4658
+ "alignItems": "center",
4659
+ "justifyContent": "center"
4660
+ },
4661
+ "children": [
4662
+ {
4663
+ "part": "moreLabel",
4664
+ "component": "Text",
4665
+ "props": {
4666
+ "text": {
4667
+ "$fill": [
4668
+ "$string.morePictures",
4669
+ {
4670
+ "count": {
4671
+ "$sub": [
4672
+ "{len(props.pictures)}",
4673
+ 3
4674
+ ]
4675
+ }
4676
+ }
4677
+ ]
4678
+ },
4679
+ "lines": 1
4680
+ }
4681
+ }
4682
+ ]
4683
+ }
4684
+ ]
4685
+ }
4686
+ ]
4687
+ }
4688
+ ]
4689
+ }
4150
4690
  }
4151
4691
  }
4152
4692
  }
@@ -1,5 +1,5 @@
1
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.",
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. EXP-1495 (docs/round-6-contract.md): Carousel gains `counter` (prop + part) and the `thumbnails` strip (parts `thumbnails`, `thumbnail`, state `selected` = the current page).",
3
3
  "states": ["hover", "pressed", "focus", "disabled", "checked", "open", "selected", "invalid", "dragover"],
4
4
  "keys": [
5
5
  "backgroundColor", "color", "borderColor", "borderWidth", "borderTopWidth", "borderRightWidth", "borderBottomWidth", "borderLeftWidth", "borderRadius", "borderStyle",
@@ -20,7 +20,7 @@
20
20
  "AudioPlayer": { "parts": ["root", "controls", "track"], "props": [] },
21
21
  "Avatar": { "parts": ["root", "image", "fallback"], "props": ["size"] },
22
22
  "QrCode": { "parts": ["root", "plate", "module", "label"], "props": ["level"] },
23
- "Carousel": { "parts": ["root", "page", "indicator", "controls", "previous", "next"], "props": ["loop", "indicators"] },
23
+ "Carousel": { "parts": ["root", "page", "indicator", "controls", "previous", "next", "counter", "thumbnails", "thumbnail"], "props": ["loop", "indicators", "counter"] },
24
24
  "Tabs": { "parts": ["root", "list", "tab", "indicator", "content", "close", "new"], "props": ["fill", "closable", "addable"] },
25
25
  "Segmented": { "parts": ["root", "item", "icon", "label", "badge"], "props": ["type", "variant", "size", "fill", "orientation"] },
26
26
  "Accordion": { "parts": ["root", "item", "trigger", "content", "count"], "props": ["type"] },
@@ -32,6 +32,8 @@
32
32
  "menu": "Menu",
33
33
  "message": "Message",
34
34
  "more": "More",
35
+ "morePictures": "+{count}",
36
+ "morePicturesLabel": "{alt}, {count} more",
35
37
  "mute": "Mute",
36
38
  "newTab": "New tab",
37
39
  "next": "Next",
@@ -39,6 +41,9 @@
39
41
  "no": "No",
40
42
  "noResults": "No results",
41
43
  "optional": "Optional",
44
+ "outline": "Outline",
45
+ "outlineRow": "{title}, {count}",
46
+ "pageCounter": "{page} / {total}",
42
47
  "pageOf": "Page {page} of {total}",
43
48
  "pagination": "Pagination",
44
49
  "previous": "Previous",