@chai-ui/react 0.1.0 → 0.2.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 (55) hide show
  1. package/DESIGN.md +6 -1
  2. package/README.md +69 -6
  3. package/dist/Composer.d.ts +19 -2
  4. package/dist/Composer.d.ts.map +1 -1
  5. package/dist/Composer.js +36 -10
  6. package/dist/Composer.js.map +1 -1
  7. package/dist/EditCard.d.ts +52 -0
  8. package/dist/EditCard.d.ts.map +1 -0
  9. package/dist/EditCard.js +360 -0
  10. package/dist/EditCard.js.map +1 -0
  11. package/dist/ResultCard.d.ts +2 -1
  12. package/dist/ResultCard.d.ts.map +1 -1
  13. package/dist/ResultCard.js +19 -63
  14. package/dist/ResultCard.js.map +1 -1
  15. package/dist/icons.d.ts +27 -0
  16. package/dist/icons.d.ts.map +1 -0
  17. package/dist/icons.js +50 -0
  18. package/dist/icons.js.map +1 -0
  19. package/dist/index.d.ts +3 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/media-actions.d.ts +15 -0
  24. package/dist/media-actions.d.ts.map +1 -0
  25. package/dist/media-actions.js +47 -0
  26. package/dist/media-actions.js.map +1 -0
  27. package/dist/page-slide.d.ts +32 -0
  28. package/dist/page-slide.d.ts.map +1 -0
  29. package/dist/page-slide.js +43 -0
  30. package/dist/page-slide.js.map +1 -0
  31. package/dist/primitives/Pagination.d.ts +3 -1
  32. package/dist/primitives/Pagination.d.ts.map +1 -1
  33. package/dist/primitives/Pagination.js +2 -2
  34. package/dist/primitives/Pagination.js.map +1 -1
  35. package/dist/regions.d.ts +3 -0
  36. package/dist/regions.d.ts.map +1 -0
  37. package/dist/regions.js +7 -0
  38. package/dist/regions.js.map +1 -0
  39. package/dist/style.css +507 -82
  40. package/dist/useComposer.d.ts +22 -1
  41. package/dist/useComposer.d.ts.map +1 -1
  42. package/dist/useComposer.js +126 -24
  43. package/dist/useComposer.js.map +1 -1
  44. package/package.json +3 -3
  45. package/src/Composer.tsx +101 -13
  46. package/src/EditCard.tsx +688 -0
  47. package/src/ResultCard.tsx +60 -125
  48. package/src/icons.tsx +115 -0
  49. package/src/index.ts +8 -0
  50. package/src/media-actions.ts +45 -0
  51. package/src/page-slide.tsx +82 -0
  52. package/src/primitives/Pagination.tsx +4 -2
  53. package/src/regions.ts +7 -0
  54. package/src/style.css +507 -82
  55. package/src/useComposer.ts +147 -18
package/dist/style.css CHANGED
@@ -844,6 +844,38 @@
844
844
  cursor: not-allowed;
845
845
  }
846
846
 
847
+ /* Edit regions, above the prompt field: each chip in its region's color
848
+ (set inline as --chai-region-color), with the numbered badge that marks
849
+ the same region on the image. */
850
+ .chai-composer__regions {
851
+ display: flex;
852
+ flex-wrap: wrap;
853
+ gap: var(--chai-space-2);
854
+ margin: 0;
855
+ padding: 0;
856
+ list-style: none;
857
+ }
858
+
859
+ .chai-composer__chip--region {
860
+ gap: 6px;
861
+ padding-left: 6px;
862
+ border-color: var(--chai-region-color);
863
+ }
864
+
865
+ .chai-composer__region-badge {
866
+ display: inline-flex;
867
+ align-items: center;
868
+ justify-content: center;
869
+ min-width: 18px;
870
+ height: 18px;
871
+ padding: 0 4px;
872
+ border-radius: var(--chai-radius-full);
873
+ background: var(--chai-region-color);
874
+ color: var(--chai-color-semantic-text-on-accent);
875
+ font-size: var(--chai-type-size-label-small);
876
+ line-height: 1;
877
+ }
878
+
847
879
  .chai-composer__submit {
848
880
  flex: none;
849
881
  /* Flat always, both states — the mock has no FAB shadow at all. */
@@ -1201,7 +1233,11 @@
1201
1233
  overflow: hidden;
1202
1234
  }
1203
1235
 
1236
+ /* The media fills the face, with the controls floating over its bottom
1237
+ (see "Floating card controls"). `--chai-card-bottom-clear` is how much of
1238
+ the bottom they cover, for content that must stay clear of them. */
1204
1239
  .chai-result-card__front {
1240
+ --chai-card-bottom-clear: 56px;
1205
1241
  position: relative;
1206
1242
  background: var(--chai-color-semantic-control-surface);
1207
1243
  /* `.back` is later in DOM order and stacks above `.front` regardless of
@@ -1244,6 +1280,20 @@
1244
1280
  * room around a still-centered, still-unstretched image, not a fixed gray
1245
1281
  * box the image sits inside by default.
1246
1282
  */
1283
+ /* Holds the paging slide: clips the page sliding out, and passes the face's
1284
+ column layout through so the media still sizes as before. */
1285
+ .chai-result-card__media-frame,
1286
+ .chai-result-card__media-page {
1287
+ position: relative;
1288
+ flex: 1;
1289
+ display: flex;
1290
+ flex-direction: column;
1291
+ }
1292
+
1293
+ .chai-result-card__media-frame {
1294
+ overflow: hidden;
1295
+ }
1296
+
1247
1297
  .chai-result-card__media {
1248
1298
  position: relative;
1249
1299
  flex: 1;
@@ -1263,6 +1313,13 @@
1263
1313
  .chai-result-card__media--cancelled {
1264
1314
  min-height: 220px;
1265
1315
  padding: var(--chai-space-5);
1316
+ /* Text, audio and status messages end above the floating controls. */
1317
+ padding-bottom: var(--chai-card-bottom-clear);
1318
+ }
1319
+
1320
+ /* With page dots, the floating controls stand taller. */
1321
+ .chai-result-card__front:has(.chai-pagination) {
1322
+ --chai-card-bottom-clear: 96px;
1266
1323
  }
1267
1324
 
1268
1325
  /* A sibling of `.chai-result-card` itself, not a descendant — see the
@@ -1432,15 +1489,6 @@
1432
1489
  color: var(--chai-color-semantic-danger);
1433
1490
  }
1434
1491
 
1435
- .chai-result-card__media-actions {
1436
- position: absolute;
1437
- z-index: 1;
1438
- top: 10px;
1439
- right: 10px;
1440
- display: flex;
1441
- gap: 2px;
1442
- }
1443
-
1444
1492
  /* Play/scrubber sit relative to their nearest positioned ancestor —
1445
1493
  `.chai-result-card__media` inline, `.chai-result-card__expand-frame`
1446
1494
  when expanded — not the `<video>` element itself. Both are exactly the
@@ -1482,7 +1530,8 @@
1482
1530
  z-index: 1;
1483
1531
  left: 12px;
1484
1532
  right: 12px;
1485
- bottom: calc(10px - (24px - var(--chai-scrubber-track)) / 2);
1533
+ /* Above the floating controls in the card; near the bottom when expanded. */
1534
+ bottom: calc(var(--chai-card-bottom-clear, 10px) - (24px - var(--chai-scrubber-track)) / 2);
1486
1535
  height: 24px;
1487
1536
  border-radius: 6px;
1488
1537
  cursor: pointer;
@@ -1547,78 +1596,6 @@
1547
1596
  height: 14px;
1548
1597
  }
1549
1598
 
1550
- /* --- Bottom bar --- */
1551
-
1552
- /* Reference: Figma "Result Card - Image - Paginated" — an
1553
- 11px inset around the icon row, and when there's more than one result the
1554
- pager drops to its own centered row beneath it (8px from the card's
1555
- bottom edge) rather than squeezing between the icon groups. Without a
1556
- pager the icon row is simply centered in a comparably roomy bar. */
1557
- .chai-result-card__bar {
1558
- display: flex;
1559
- flex-direction: column;
1560
- align-items: center;
1561
- padding: 16px 11px;
1562
- background: var(--chai-color-semantic-control-surface-raised);
1563
- }
1564
-
1565
- .chai-result-card__bar:has(.chai-pagination) {
1566
- padding: 11px 11px 8px;
1567
- }
1568
-
1569
- .chai-result-card__bar-row {
1570
- align-self: stretch;
1571
- display: flex;
1572
- align-items: center;
1573
- justify-content: space-between;
1574
- gap: var(--chai-space-2);
1575
- }
1576
-
1577
- .chai-result-card__bar-start {
1578
- display: flex;
1579
- align-items: center;
1580
- gap: 2px;
1581
- }
1582
-
1583
- /* The shared hover-circle treatment — reference: the design
1584
- note that the "like" button's hover state in the mock is how every
1585
- small icon button on the card should behave, not just that one. `
1586
- --active` (a persisted, not just hovered, tint) is this component's own
1587
- extension for like/dislike/more, since the mock only documents hover. */
1588
- .chai-result-card__icon-btn {
1589
- position: relative;
1590
- width: 24px;
1591
- height: 24px;
1592
- padding: 0;
1593
- border: none;
1594
- border-radius: 50%;
1595
- background: transparent;
1596
- color: var(--chai-color-semantic-control-text-muted);
1597
- display: flex;
1598
- align-items: center;
1599
- justify-content: center;
1600
- cursor: pointer;
1601
- }
1602
-
1603
- .chai-result-card__icon-btn svg {
1604
- width: 18px;
1605
- height: 18px;
1606
- }
1607
-
1608
- .chai-result-card__icon-btn:hover:not(:disabled) {
1609
- background: var(--chai-color-semantic-control-surface-hover);
1610
- }
1611
-
1612
- .chai-result-card__icon-btn--active {
1613
- color: var(--chai-color-semantic-accent-text);
1614
- background: var(--chai-color-semantic-control-surface-hover);
1615
- }
1616
-
1617
- .chai-result-card__icon-btn:disabled {
1618
- opacity: 0.4;
1619
- cursor: not-allowed;
1620
- }
1621
-
1622
1599
  /* --- More menu (reference: Figma "Result Card - More menu expanded") --- */
1623
1600
 
1624
1601
  .chai-result-card__more {
@@ -1816,6 +1793,452 @@
1816
1793
  border: 0;
1817
1794
  }
1818
1795
 
1796
+ /* --- Edit card ------------------------------------------------------------
1797
+ The result card's frame (rounded outline, white action bar) around an
1798
+ image the person marks regions on. The viewport clips the image to the
1799
+ card's corners at every zoom; the layer inside it holds the image and its
1800
+ regions, so both move and scale together. */
1801
+
1802
+ .chai-edit-card {
1803
+ width: var(--chai-edit-card-width, 421px);
1804
+ max-width: 100%;
1805
+ display: flex;
1806
+ flex-direction: column;
1807
+ border-radius: 29px;
1808
+ border: 1px solid var(--chai-color-semantic-control-border-soft);
1809
+ background: var(--chai-color-semantic-control-surface-raised);
1810
+ overflow: hidden;
1811
+ font-family: var(--chai-type-family-material);
1812
+ }
1813
+
1814
+ .chai-edit-card__viewport {
1815
+ position: relative;
1816
+ overflow: hidden;
1817
+ background: var(--chai-color-semantic-control-surface);
1818
+ touch-action: none;
1819
+ user-select: none;
1820
+ }
1821
+
1822
+ .chai-edit-card__viewport--zoomed {
1823
+ cursor: grab;
1824
+ }
1825
+
1826
+ .chai-edit-card__viewport--zoomed:active {
1827
+ cursor: grabbing;
1828
+ }
1829
+
1830
+ .chai-edit-card__layer {
1831
+ position: relative;
1832
+ transform-origin: 0 0;
1833
+ }
1834
+
1835
+ .chai-edit-card__image {
1836
+ display: block;
1837
+ width: 100%;
1838
+ height: auto;
1839
+ pointer-events: none;
1840
+ }
1841
+
1842
+ .chai-edit-card__placeholder {
1843
+ min-height: 220px;
1844
+ }
1845
+
1846
+ /* Region boxes. Borders, badges and handles divide by the zoom so they stay
1847
+ the same size on screen while the image scales. A faint dark outline keeps
1848
+ each colored border visible on light and dark images alike. */
1849
+ .chai-edit-card__region {
1850
+ --chai-edit-line: calc(2px / var(--chai-edit-zoom, 1));
1851
+ position: absolute;
1852
+ box-sizing: border-box;
1853
+ border: var(--chai-edit-line) solid var(--chai-region-color);
1854
+ box-shadow: 0 0 0 calc(1px / var(--chai-edit-zoom, 1)) var(--chai-color-semantic-overlay);
1855
+ }
1856
+
1857
+ .chai-edit-card__region--selected,
1858
+ .chai-edit-card__region--draft {
1859
+ border-style: dashed;
1860
+ }
1861
+
1862
+ /* Regions a version was made with: shown for reference, without a number
1863
+ badge and not selectable, so they don't read as regions to send. */
1864
+ .chai-edit-card__region--applied {
1865
+ pointer-events: none;
1866
+ }
1867
+
1868
+ .chai-edit-card__region-body {
1869
+ position: absolute;
1870
+ inset: 0;
1871
+ width: 100%;
1872
+ height: 100%;
1873
+ padding: 0;
1874
+ border: none;
1875
+ background: transparent;
1876
+ cursor: move;
1877
+ }
1878
+
1879
+ .chai-edit-card__region-body:disabled {
1880
+ cursor: default;
1881
+ }
1882
+
1883
+ .chai-edit-card__badge {
1884
+ position: absolute;
1885
+ top: 0;
1886
+ left: 0;
1887
+ display: flex;
1888
+ align-items: center;
1889
+ justify-content: center;
1890
+ min-width: 16px;
1891
+ height: 16px;
1892
+ padding: 0 3px;
1893
+ box-sizing: border-box;
1894
+ border-radius: var(--chai-radius-full);
1895
+ background: var(--chai-region-color);
1896
+ color: var(--chai-color-semantic-text-on-accent);
1897
+ font-size: var(--chai-type-size-label-small);
1898
+ font-weight: var(--chai-type-weight-material-medium);
1899
+ line-height: 1;
1900
+ transform: translate(-50%, -50%) scale(calc(1 / var(--chai-edit-zoom, 1)));
1901
+ pointer-events: none;
1902
+ }
1903
+
1904
+ .chai-edit-card__handle {
1905
+ position: absolute;
1906
+ width: 12px;
1907
+ height: 12px;
1908
+ box-sizing: border-box;
1909
+ border: 1px solid var(--chai-region-color);
1910
+ background: var(--chai-color-semantic-on-overlay);
1911
+ transform: translate(-50%, -50%) scale(calc(1 / var(--chai-edit-zoom, 1)));
1912
+ touch-action: none;
1913
+ }
1914
+
1915
+ .chai-edit-card__handle--nw { top: 0; left: 0; cursor: nwse-resize; }
1916
+ .chai-edit-card__handle--ne { top: 0; left: 100%; cursor: nesw-resize; }
1917
+ .chai-edit-card__handle--sw { top: 100%; left: 0; cursor: nesw-resize; }
1918
+ .chai-edit-card__handle--se { top: 100%; left: 100%; cursor: nwse-resize; }
1919
+
1920
+ /* An edit in progress (or stopped, or failed) over the image it came from. */
1921
+ .chai-edit-card__status {
1922
+ position: absolute;
1923
+ inset: 0;
1924
+ z-index: 2;
1925
+ display: flex;
1926
+ align-items: center;
1927
+ justify-content: center;
1928
+ padding: var(--chai-space-5);
1929
+ background: var(--chai-color-semantic-overlay);
1930
+ }
1931
+
1932
+ .chai-edit-card__spinner {
1933
+ width: 32px;
1934
+ height: 32px;
1935
+ border-radius: 50%;
1936
+ border: 3px solid var(--chai-color-semantic-control-border-faint);
1937
+ border-top-color: var(--chai-color-semantic-accent);
1938
+ animation: chai-spin var(--chai-motion-duration-steep) linear infinite;
1939
+ }
1940
+
1941
+ .chai-edit-card__status-text {
1942
+ margin: 0;
1943
+ text-align: center;
1944
+ font-size: var(--chai-type-size-body-medium);
1945
+ color: var(--chai-color-semantic-on-overlay);
1946
+ }
1947
+
1948
+ .chai-edit-card__zoom-level {
1949
+ min-width: 34px;
1950
+ text-align: center;
1951
+ font-size: var(--chai-type-size-label-small);
1952
+ font-weight: var(--chai-type-weight-material-medium);
1953
+ letter-spacing: var(--chai-type-tracking-label-small);
1954
+ color: var(--chai-color-semantic-on-overlay);
1955
+ }
1956
+
1957
+ few words instead of running past the card's edge. */
1958
+ .chai-edit-card__popover {
1959
+ position: absolute;
1960
+ z-index: 4;
1961
+ width: 220px;
1962
+ box-sizing: border-box;
1963
+ display: flex;
1964
+ flex-direction: column;
1965
+ gap: 6px;
1966
+ padding: 6px 8px 8px;
1967
+ border-radius: var(--chai-radius-sm);
1968
+ background: var(--chai-color-semantic-scrim);
1969
+ cursor: default;
1970
+ }
1971
+
1972
+ .chai-edit-card__popover--above {
1973
+ transform: translateY(-100%);
1974
+ }
1975
+
1976
+ .chai-edit-card__popover-header {
1977
+ display: flex;
1978
+ align-items: center;
1979
+ gap: 2px;
1980
+ }
1981
+
1982
+ .chai-edit-card__popover-title {
1983
+ flex: 1;
1984
+ font-size: var(--chai-type-size-label-small);
1985
+ font-weight: var(--chai-type-weight-material-medium);
1986
+ letter-spacing: var(--chai-type-tracking-label-small);
1987
+ color: var(--chai-color-semantic-on-overlay);
1988
+ }
1989
+
1990
+ .chai-edit-card__field {
1991
+ width: 100%;
1992
+ box-sizing: border-box;
1993
+ max-height: 120px;
1994
+ padding: 4px 8px;
1995
+ border: 1px solid var(--chai-color-semantic-on-overlay);
1996
+ border-radius: var(--chai-radius-sm);
1997
+ background: transparent;
1998
+ color: var(--chai-color-semantic-on-overlay);
1999
+ font-family: inherit;
2000
+ font-size: var(--chai-type-size-label-small);
2001
+ line-height: var(--chai-type-leading-label-small);
2002
+ resize: none;
2003
+ overflow-y: auto;
2004
+ }
2005
+
2006
+ .chai-edit-card__field::placeholder {
2007
+ color: var(--chai-color-semantic-on-overlay);
2008
+ opacity: 0.75;
2009
+ }
2010
+
2011
+ @media (prefers-reduced-motion: reduce) {
2012
+ .chai-edit-card__spinner {
2013
+ animation-duration: calc(var(--chai-motion-duration-steep) * 3);
2014
+ }
2015
+ }
2016
+
2017
+ /* --- Floating card controls -------------------------------------------------
2018
+ Shared by the image cards (result card, edit card), whose media fills the
2019
+ card. Controls along the top sit in dark translucent groups; controls
2020
+ along the bottom sit on light translucent surfaces: actions on the left,
2021
+ one round button on the right, and the page dots centered above them.
2022
+ Each reads on any image without sampling its colors. */
2023
+
2024
+ .chai-card-top {
2025
+ position: absolute;
2026
+ z-index: 3;
2027
+ top: 16px;
2028
+ display: flex;
2029
+ align-items: center;
2030
+ gap: 2px;
2031
+ padding: 2px;
2032
+ border-radius: var(--chai-radius-sm);
2033
+ background: var(--chai-color-semantic-overlay);
2034
+ }
2035
+
2036
+ .chai-card-top--start {
2037
+ left: 16px;
2038
+ }
2039
+
2040
+ .chai-card-top--end {
2041
+ right: 16px;
2042
+ }
2043
+
2044
+ .chai-card-top-btn {
2045
+ width: 24px;
2046
+ height: 24px;
2047
+ padding: 0;
2048
+ border: none;
2049
+ border-radius: var(--chai-radius-sm);
2050
+ background: transparent;
2051
+ color: var(--chai-color-semantic-on-overlay);
2052
+ display: flex;
2053
+ align-items: center;
2054
+ justify-content: center;
2055
+ cursor: pointer;
2056
+ }
2057
+
2058
+ .chai-card-top-btn svg {
2059
+ width: 18px;
2060
+ height: 18px;
2061
+ }
2062
+
2063
+ .chai-card-top-btn:hover:not(:disabled) {
2064
+ background: var(--chai-color-semantic-overlay);
2065
+ }
2066
+
2067
+ .chai-card-top-btn:disabled {
2068
+ opacity: 0.4;
2069
+ cursor: not-allowed;
2070
+ }
2071
+
2072
+ /* The bottom: dots above the actions, in one column so a wrapped action row
2073
+ pushes the dots up rather than covering them. */
2074
+ .chai-card-bottom {
2075
+ position: absolute;
2076
+ z-index: 3;
2077
+ left: 16px;
2078
+ right: 16px;
2079
+ bottom: 12px;
2080
+ display: flex;
2081
+ flex-direction: column;
2082
+ align-items: center;
2083
+ gap: var(--chai-space-2);
2084
+ }
2085
+
2086
+ .chai-card-pager {
2087
+ display: flex;
2088
+ padding: 0 4px;
2089
+ border-radius: var(--chai-radius-full);
2090
+ background: var(--chai-color-semantic-overlay-light);
2091
+ }
2092
+
2093
+ /* Over an image, the unselected dots need more contrast than on white. */
2094
+ .chai-card-pager .chai-pagination__dot:not(.chai-pagination__dot--filled)::before {
2095
+ background: var(--chai-color-semantic-control-text-muted);
2096
+ }
2097
+
2098
+ .chai-card-actions {
2099
+ align-self: stretch;
2100
+ display: flex;
2101
+ align-items: center;
2102
+ justify-content: space-between;
2103
+ gap: var(--chai-space-2);
2104
+ }
2105
+
2106
+ .chai-card-chips {
2107
+ display: flex;
2108
+ flex-wrap: wrap;
2109
+ gap: var(--chai-space-2);
2110
+ }
2111
+
2112
+ /* A labeled action (the edit card's region chips). */
2113
+ .chai-card-chip {
2114
+ display: inline-flex;
2115
+ align-items: center;
2116
+ gap: var(--chai-space-2);
2117
+ height: 32px;
2118
+ padding: 0 12px 0 8px;
2119
+ border: none;
2120
+ border-radius: var(--chai-radius-sm);
2121
+ background: var(--chai-color-semantic-overlay-light);
2122
+ color: var(--chai-color-semantic-control-text-strong);
2123
+ font-family: inherit;
2124
+ font-size: var(--chai-type-size-label-large);
2125
+ font-weight: var(--chai-type-weight-material-medium);
2126
+ letter-spacing: var(--chai-type-tracking-label-large);
2127
+ white-space: nowrap;
2128
+ cursor: pointer;
2129
+ }
2130
+
2131
+ /* Icon-only actions in one light pill (the result card's votes and retry). */
2132
+ .chai-card-pill {
2133
+ display: flex;
2134
+ align-items: center;
2135
+ height: 32px;
2136
+ padding: 0 2px;
2137
+ border-radius: var(--chai-radius-full);
2138
+ background: var(--chai-color-semantic-overlay-light);
2139
+ }
2140
+
2141
+ .chai-card-pill-btn,
2142
+ .chai-card-round {
2143
+ flex: none;
2144
+ width: 32px;
2145
+ height: 32px;
2146
+ padding: 0;
2147
+ border: none;
2148
+ border-radius: 50%;
2149
+ background: transparent;
2150
+ color: var(--chai-color-semantic-control-text-strong);
2151
+ display: flex;
2152
+ align-items: center;
2153
+ justify-content: center;
2154
+ cursor: pointer;
2155
+ }
2156
+
2157
+ /* The one round button on the right (show/hide regions, details). */
2158
+ .chai-card-round {
2159
+ background: var(--chai-color-semantic-overlay-light);
2160
+ }
2161
+
2162
+ .chai-card-chip svg,
2163
+ .chai-card-pill-btn svg,
2164
+ .chai-card-round svg {
2165
+ width: 18px;
2166
+ height: 18px;
2167
+ }
2168
+
2169
+ .chai-card-chip:hover:not(:disabled),
2170
+ .chai-card-pill-btn:hover:not(:disabled),
2171
+ .chai-card-round:hover:not(:disabled) {
2172
+ background: var(--chai-color-semantic-control-surface-raised);
2173
+ }
2174
+
2175
+ /* A vote that's on: green, on a solid surface. */
2176
+ .chai-card-pill-btn--active {
2177
+ color: var(--chai-color-semantic-accent-text);
2178
+ background: var(--chai-color-semantic-control-surface-raised);
2179
+ }
2180
+
2181
+ .chai-card-chip:disabled,
2182
+ .chai-card-pill-btn:disabled,
2183
+ .chai-card-round:disabled {
2184
+ color: var(--chai-color-semantic-control-text-muted);
2185
+ cursor: not-allowed;
2186
+ }
2187
+
2188
+ /* --- Page slide ------------------------------------------------------------
2189
+ Shared by the paginated cards: the new page slides in from the side it was
2190
+ paged from while the last image slides out the other way. Skipped (in
2191
+ page-slide.tsx) for people who ask for reduced motion. */
2192
+ .chai-slide-in--left {
2193
+ animation: chai-slide-in-left var(--chai-motion-duration-slow) var(--chai-motion-easing-standard);
2194
+ }
2195
+
2196
+ .chai-slide-in--right {
2197
+ animation: chai-slide-in-right var(--chai-motion-duration-slow) var(--chai-motion-easing-standard);
2198
+ }
2199
+
2200
+ /* The outgoing image stays in the layout, holding the card's height while
2201
+ the new page (laid over it) loads. */
2202
+ .chai-slide-out {
2203
+ display: block;
2204
+ width: 100%;
2205
+ height: auto;
2206
+ pointer-events: none;
2207
+ }
2208
+
2209
+ .chai-slide-in--over {
2210
+ position: absolute;
2211
+ inset: 0;
2212
+ }
2213
+
2214
+ .chai-slide-out--left {
2215
+ animation: chai-slide-out-left var(--chai-motion-duration-slow) var(--chai-motion-easing-standard) forwards;
2216
+ }
2217
+
2218
+ .chai-slide-out--right {
2219
+ animation: chai-slide-out-right var(--chai-motion-duration-slow) var(--chai-motion-easing-standard) forwards;
2220
+ }
2221
+
2222
+ @keyframes chai-slide-in-left {
2223
+ from { transform: translateX(100%); }
2224
+ to { transform: translateX(0); }
2225
+ }
2226
+
2227
+ @keyframes chai-slide-in-right {
2228
+ from { transform: translateX(-100%); }
2229
+ to { transform: translateX(0); }
2230
+ }
2231
+
2232
+ @keyframes chai-slide-out-left {
2233
+ from { transform: translateX(0); }
2234
+ to { transform: translateX(-100%); }
2235
+ }
2236
+
2237
+ @keyframes chai-slide-out-right {
2238
+ from { transform: translateX(0); }
2239
+ to { transform: translateX(100%); }
2240
+ }
2241
+
1819
2242
  /* --- Focus rings ------------------------------------------------------------
1820
2243
  On by default: every CHAI control shows a visible keyboard-focus ring,
1821
2244
  which WCAG 2.4.7 Focus Visible requires. Buttons and menu items
@@ -1839,6 +2262,7 @@
1839
2262
  .chai-media-analyzer,
1840
2263
  .chai-result-card,
1841
2264
  .chai-result-card__expand-overlay,
2265
+ .chai-edit-card,
1842
2266
  .chai-search-menu,
1843
2267
  .chai-pagination,
1844
2268
  .chai-switch
@@ -1853,6 +2277,7 @@
1853
2277
  .chai-media-analyzer,
1854
2278
  .chai-result-card,
1855
2279
  .chai-result-card__expand-overlay,
2280
+ .chai-edit-card,
1856
2281
  .chai-search-menu,
1857
2282
  .chai-pagination
1858
2283
  ) :focus-visible {
@@ -1,5 +1,6 @@
1
- import { type GenerationEngine, type GenerationUseCase, type MediaKind, type ModelRoutingInput, type ModelRoutingResult, type Run } from "@chai-ui/core";
1
+ import { type DroppedMedia, type EditRegion, type GenerationEngine, type GenerationUseCase, type MediaKind, type ModelOption, type ModelRoutingInput, type ModelRoutingResult, type Run } from "@chai-ui/core";
2
2
  import type { ComposerSubmitPayload } from "./Composer.js";
3
+ import type { EditVersion } from "./EditCard.js";
3
4
  /** A result's `modelId` when nothing was picked: the engine runs its own default model. */
4
5
  export declare const DEFAULT_MODEL_ID = "default";
5
6
  export interface UseComposerOptions {
@@ -51,6 +52,19 @@ export interface UseComposerOptions {
51
52
  onRoutingFallback?: (result: Extract<ModelRoutingResult, {
52
53
  status: "fallback";
53
54
  }>) => void;
55
+ /**
56
+ * The image to edit, when the Composer's use case is an edit (e.g.
57
+ * `EDIT_IMAGE_USE_CASE`) and an image is attached. While it's set, the hook
58
+ * returns `edit` for an `EditCard`, and each edit's results become new
59
+ * versions of the image. Changing it to another image starts over.
60
+ */
61
+ editImage?: DroppedMedia | null;
62
+ /** Replaces how an edit's prompt and regions become the model's prompt. Defaults to `buildRegionEditPrompt` in `@chai-ui/core`, in the model's `regionFormat`. */
63
+ editPrompt?: (args: {
64
+ prompt: string;
65
+ regions: EditRegion[];
66
+ model?: ModelOption;
67
+ }) => string;
54
68
  onRun?: (run: Run) => void;
55
69
  onResult?: (run: Run, resultId: string) => void;
56
70
  onError?: (run: Run, resultId: string) => void;
@@ -70,6 +84,13 @@ export declare function useComposer(options?: UseComposerOptions): {
70
84
  enhance: ((value: string) => Promise<string>) | undefined;
71
85
  enhancing: boolean;
72
86
  routing: boolean;
87
+ edit: {
88
+ versions: EditVersion[];
89
+ activeVersion: number;
90
+ onActiveVersionChange: import("react").Dispatch<import("react").SetStateAction<number>>;
91
+ regions: EditRegion[];
92
+ onRegionsChange: import("react").Dispatch<import("react").SetStateAction<EditRegion[]>>;
93
+ } | null;
73
94
  };
74
95
  export type UseComposerReturn = ReturnType<typeof useComposer>;
75
96
  //# sourceMappingURL=useComposer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useComposer.d.ts","sourceRoot":"","sources":["../src/useComposer.ts"],"names":[],"mappings":"AACA,OAAO,EAaL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAEvB,KAAK,GAAG,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAI3D,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAa1C,MAAM,WAAW,kBAAkB;IACjC,sFAAsF;IACtF,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,uJAAuJ;IACvJ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,8FAA8F;IAC9F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gIAAgI;IAChI,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,iBAAiB,KAAK,MAAM,CAAC;IACpE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChD,uFAAuF;IACvF,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,CAAC;IACrD;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IAC1F,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB;;;sBA2F9C,qBAAqB;;sBA0EV,MAAM;;;EAa9B;AAED,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC"}
1
+ {"version":3,"file":"useComposer.d.ts","sourceRoot":"","sources":["../src/useComposer.ts"],"names":[],"mappings":"AACA,OAAO,EAgBL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAEvB,KAAK,GAAG,EACT,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAqB,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAIjD,2FAA2F;AAC3F,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAa1C,MAAM,WAAW,kBAAkB;IACjC,sFAAsF;IACtF,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC;IAC5D;;;;OAIG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAC9B,uJAAuJ;IACvJ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,8FAA8F;IAC9F,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gIAAgI;IAChI,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,iBAAiB,KAAK,MAAM,CAAC;IACpE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAC1C,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChD,uFAAuF;IACvF,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,MAAM,CAAC;IACrD;;;;OAIG;IACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,EAAE;QAAE,MAAM,EAAE,UAAU,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;IAC1F;;;;;OAKG;IACH,SAAS,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IAChC,kKAAkK;IAClK,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,UAAU,EAAE,CAAC;QAAC,KAAK,CAAC,EAAE,WAAW,CAAA;KAAE,KAAK,MAAM,CAAC;IAC9F,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,KAAK,IAAI,CAAC;IAC3B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAChD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB;;;sBA4K9C,qBAAqB;;sBA2FV,MAAM;;;;;;;;;;EAwB9B;AAMD,MAAM,MAAM,iBAAiB,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC"}