@fusion.dev/helix-core 0.56.1 → 0.58.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 (25) hide show
  1. package/custom-elements.json +870 -65
  2. package/dist/controllers/cue-controller/cue-controller.d.ts +85 -0
  3. package/dist/controllers/cue-controller/cue-controller.js +45 -0
  4. package/dist/controllers/fullscreen-controller/fullscreen-controller.d.ts +32 -0
  5. package/dist/controllers/fullscreen-controller/fullscreen-controller.js +28 -0
  6. package/dist/controllers/idle-controller/idle-controller.d.ts +35 -0
  7. package/dist/controllers/idle-controller/idle-controller.js +24 -0
  8. package/dist/controllers/media-session-controller/media-session-controller.d.ts +54 -0
  9. package/dist/controllers/media-session-controller/media-session-controller.js +49 -0
  10. package/dist/controllers/remote-playback-controller/remote-playback-controller.d.ts +34 -0
  11. package/dist/controllers/remote-playback-controller/remote-playback-controller.js +38 -0
  12. package/dist/controllers/token-color-controller/token-color-controller.d.ts +3 -3
  13. package/dist/controllers/token-color-controller/token-color-controller.js +9 -13
  14. package/dist/index.d.ts +8 -1
  15. package/dist/index.js +8 -1
  16. package/dist/utils/css-color/css-color.d.ts +76 -0
  17. package/dist/utils/css-color/css-color.js +168 -0
  18. package/dist/utils/focusable-elements/focusable-elements.d.ts +9 -10
  19. package/dist/utils/media-copy/media-copy.d.ts +27 -0
  20. package/dist/utils/media-copy/media-copy.js +18 -0
  21. package/dist/utils/read-color/read-color.d.ts +36 -0
  22. package/dist/utils/read-color/read-color.js +36 -0
  23. package/package.json +4 -4
  24. package/dist/controllers/focus-trap-controller/focus-trap-controller.d.ts +0 -26
  25. package/dist/controllers/focus-trap-controller/focus-trap-controller.js +0 -55
@@ -457,6 +457,210 @@
457
457
  }
458
458
  ]
459
459
  },
460
+ {
461
+ "kind": "javascript-module",
462
+ "path": "src/controllers/cue-controller/cue-controller.ts",
463
+ "declarations": [
464
+ {
465
+ "kind": "function",
466
+ "name": "captionTracks",
467
+ "return": {
468
+ "type": {
469
+ "text": "TextTrack[]"
470
+ }
471
+ },
472
+ "parameters": [
473
+ {
474
+ "name": "media",
475
+ "type": {
476
+ "text": "HTMLMediaElement | null"
477
+ }
478
+ }
479
+ ],
480
+ "description": "The tracks a reader could choose between, in the order they were written.\n\nFiltered by kind, because `textTracks` also carries `chapters`, `metadata`\nand `descriptions`, and a menu offering \"Chapters\" as a subtitle language\nwould be offering something that draws no words. Read off the media element\nrather than off the slot: a `<track>` added later is in one and not the\nother, and the element is the one that has resolved them."
481
+ },
482
+ {
483
+ "kind": "function",
484
+ "name": "showCaption",
485
+ "return": {
486
+ "type": {
487
+ "text": "void"
488
+ }
489
+ },
490
+ "parameters": [
491
+ {
492
+ "name": "tracks",
493
+ "type": {
494
+ "text": "readonly TextTrack[]"
495
+ }
496
+ },
497
+ {
498
+ "name": "index",
499
+ "type": {
500
+ "text": "number"
501
+ }
502
+ }
503
+ ],
504
+ "description": "Shows one track and disables every other, or shows none at `-1`.\n\nEvery other, rather than only the one that was on. Two tracks in `hidden`\nboth fire `cuechange`, so the rendered cue becomes whichever fired last and\na reader who switched language gets the two interleaved. Measured as two\nsubtitles alternating line by line.\n\n`hidden`, never `showing`. Hidden is the mode where a track still fires\n`cuechange` and still fills `activeCues`, and the browser draws nothing,\nwhich is exactly what is wanted when something else is doing the drawing."
505
+ },
506
+ {
507
+ "kind": "function",
508
+ "name": "adoptTrack",
509
+ "return": {
510
+ "type": {
511
+ "text": "boolean"
512
+ }
513
+ },
514
+ "parameters": [
515
+ {
516
+ "name": "track",
517
+ "type": {
518
+ "text": "TextTrack"
519
+ }
520
+ }
521
+ ],
522
+ "description": "Whether a track the platform started showing is now ours to draw.\n\nA `<track default>` arrives `showing`, which is the browser drawing it.\nMoved to `hidden` so the cues keep coming and we draw them instead. What\ncomes back is whether captions are on, which is not always \"off\": a button\nthat began there reported the opposite of what was on screen."
523
+ },
524
+ {
525
+ "kind": "function",
526
+ "name": "captionsAmong",
527
+ "return": {
528
+ "type": {
529
+ "text": "Caption[]"
530
+ }
531
+ },
532
+ "parameters": [
533
+ {
534
+ "name": "media",
535
+ "type": {
536
+ "text": "readonly TrackCopy[]"
537
+ }
538
+ }
539
+ ],
540
+ "description": "The caption tracks among the copies, before the render that draws them.\n\nBy the platform's own reading of `kind`, which is what makes a `<track>` a\ncaption at all: missing is `subtitles` and anything it does not know is\n`metadata`. Read from the video's tracks after the render instead, the count\narrived a render late and asked for one more. Choosing one still goes\nthrough the video's tracks, which hold them in the same order."
541
+ },
542
+ {
543
+ "kind": "function",
544
+ "name": "activeCue",
545
+ "return": {
546
+ "type": {
547
+ "text": "DocumentFragment | undefined"
548
+ }
549
+ },
550
+ "parameters": [
551
+ {
552
+ "name": "track",
553
+ "type": {
554
+ "text": "TextTrack | undefined"
555
+ }
556
+ }
557
+ ],
558
+ "description": "The cue on screen now, as nodes, or nothing between cues.\n\nNodes rather than a string, because `VTTCue.text` is the cue's *source*:\nWebVTT carries markup, and a caption file in the wild is full of it. `<v\nHildy>` names who is speaking, `<b>` and `<i>` and `<u>` mark emphasis, and\n`<c.loud>` classes a line. Rendered as text every one of those arrives on\nscreen as visible angle brackets.\n\n`getCueAsHTML` is the platform parsing its own format, so nothing here has\nto know the grammar and no cue text is ever treated as HTML: what comes\nback is a fragment the browser built, not a string anybody interpolated."
559
+ },
560
+ {
561
+ "kind": "class",
562
+ "description": "Listens to one track's cues at a time, so the next one can take it off.\n\nTaken off whatever it was on before adding it here. `follow` runs from\n`loadedmetadata`, which fires again for every source a reader moves to, and\na consumer adding a `<track>` later gets another. Added without removing, a\nplaylist of six left six listeners on tracks that between them fire for\nevery cue in the video.",
563
+ "name": "CueController",
564
+ "members": [
565
+ {
566
+ "kind": "field",
567
+ "name": "track",
568
+ "type": {
569
+ "text": "TextTrack | undefined"
570
+ },
571
+ "privacy": "private"
572
+ },
573
+ {
574
+ "kind": "method",
575
+ "name": "follow",
576
+ "return": {
577
+ "type": {
578
+ "text": "void"
579
+ }
580
+ },
581
+ "parameters": [
582
+ {
583
+ "name": "track",
584
+ "type": {
585
+ "text": "TextTrack"
586
+ }
587
+ }
588
+ ]
589
+ },
590
+ {
591
+ "kind": "method",
592
+ "name": "hostDisconnected",
593
+ "return": {
594
+ "type": {
595
+ "text": "void"
596
+ }
597
+ }
598
+ },
599
+ {
600
+ "kind": "method",
601
+ "name": "stop",
602
+ "privacy": "private",
603
+ "return": {
604
+ "type": {
605
+ "text": "void"
606
+ }
607
+ }
608
+ }
609
+ ],
610
+ "helix": {}
611
+ }
612
+ ],
613
+ "exports": [
614
+ {
615
+ "kind": "js",
616
+ "name": "captionTracks",
617
+ "declaration": {
618
+ "name": "captionTracks",
619
+ "module": "src/controllers/cue-controller/cue-controller.ts"
620
+ }
621
+ },
622
+ {
623
+ "kind": "js",
624
+ "name": "showCaption",
625
+ "declaration": {
626
+ "name": "showCaption",
627
+ "module": "src/controllers/cue-controller/cue-controller.ts"
628
+ }
629
+ },
630
+ {
631
+ "kind": "js",
632
+ "name": "adoptTrack",
633
+ "declaration": {
634
+ "name": "adoptTrack",
635
+ "module": "src/controllers/cue-controller/cue-controller.ts"
636
+ }
637
+ },
638
+ {
639
+ "kind": "js",
640
+ "name": "captionsAmong",
641
+ "declaration": {
642
+ "name": "captionsAmong",
643
+ "module": "src/controllers/cue-controller/cue-controller.ts"
644
+ }
645
+ },
646
+ {
647
+ "kind": "js",
648
+ "name": "activeCue",
649
+ "declaration": {
650
+ "name": "activeCue",
651
+ "module": "src/controllers/cue-controller/cue-controller.ts"
652
+ }
653
+ },
654
+ {
655
+ "kind": "js",
656
+ "name": "CueController",
657
+ "declaration": {
658
+ "name": "CueController",
659
+ "module": "src/controllers/cue-controller/cue-controller.ts"
660
+ }
661
+ }
662
+ ]
663
+ },
460
664
  {
461
665
  "kind": "javascript-module",
462
666
  "path": "src/controllers/disabled-cascade-controller/disabled-cascade-controller.ts",
@@ -1016,73 +1220,90 @@
1016
1220
  },
1017
1221
  {
1018
1222
  "kind": "javascript-module",
1019
- "path": "src/controllers/focus-trap-controller/focus-trap-controller.ts",
1223
+ "path": "src/controllers/fullscreen-controller/fullscreen-controller.ts",
1020
1224
  "declarations": [
1021
1225
  {
1022
1226
  "kind": "class",
1023
- "description": "Keeps Tab inside a container while it is open, and gives focus back after.\n\nA dialog that does not trap focus is one a keyboard user tabs straight out\nof, into a page they cannot see and cannot get back from. Restoring focus on\nrelease matters just as much: without it, closing a drawer drops the reader\nat the top of the document.\n\nFinding the focusable elements is `focusableElements`, which is a utility\nrather than part of this, because that half is what roving tabindex and\narrow-key navigation need and neither of them traps anything.",
1024
- "name": "FocusTrapController",
1227
+ "description": "Into fullscreen and back, and honest about where the element is.\n\nThe state follows `fullscreenchange` rather than the request, so a refused\nrequest leaves a button that correctly reports \"not fullscreen\".\n\nThe document, rather than the host, is what the event is read from: the\nplatform fires it at the document whichever element went full, so a host that\nasked and was overruled by another one still hears about it.",
1228
+ "name": "FullscreenController",
1025
1229
  "members": [
1026
1230
  {
1027
- "kind": "field",
1028
- "name": "container",
1029
- "type": {
1030
- "text": "HTMLElement | null"
1031
- },
1032
- "privacy": "private",
1033
- "default": "null"
1034
- },
1035
- {
1036
- "kind": "field",
1037
- "name": "frame",
1038
- "type": {
1039
- "text": "number | undefined"
1040
- },
1041
- "privacy": "private",
1042
- "description": "The frame waiting to take focus, so a release can call it off."
1043
- },
1044
- {
1045
- "kind": "field",
1046
- "name": "previous",
1047
- "type": {
1048
- "text": "HTMLElement | null"
1231
+ "kind": "method",
1232
+ "name": "toggle",
1233
+ "return": {
1234
+ "type": {
1235
+ "text": "Promise<void>"
1236
+ }
1049
1237
  },
1050
- "privacy": "private",
1051
- "default": "null"
1052
- },
1053
- {
1054
- "kind": "field",
1055
- "name": "handleKeydown",
1056
- "privacy": "private",
1057
- "readonly": true
1238
+ "description": "Into fullscreen and back, and quiet about being refused.\n\n`requestFullscreen` rejects rather than returning false, and the two\nrefusals a consumer will actually meet are a Permissions Policy that does\nnot grant `fullscreen` to the frame the player is in, and a call without\nuser activation. Both are `TypeError: Permissions check failed`, which is\nthe same message for two different causes.\n\nCaught rather than `void`ed, or a refusal becomes an unhandled rejection\nnaming neither the element nor the reason."
1058
1239
  },
1059
1240
  {
1060
1241
  "kind": "method",
1061
- "name": "activate",
1242
+ "name": "hostConnected",
1062
1243
  "return": {
1063
1244
  "type": {
1064
1245
  "text": "void"
1065
1246
  }
1066
- },
1067
- "parameters": [
1068
- {
1069
- "name": "container",
1070
- "type": {
1071
- "text": "HTMLElement"
1072
- }
1073
- }
1074
- ],
1075
- "description": "Starts trapping, remembering where focus came from."
1247
+ }
1076
1248
  },
1077
1249
  {
1078
1250
  "kind": "method",
1079
- "name": "deactivate",
1251
+ "name": "hostDisconnected",
1080
1252
  "return": {
1081
1253
  "type": {
1082
1254
  "text": "void"
1083
1255
  }
1256
+ }
1257
+ },
1258
+ {
1259
+ "kind": "field",
1260
+ "name": "handleChange",
1261
+ "privacy": "private",
1262
+ "readonly": true
1263
+ }
1264
+ ],
1265
+ "helix": {}
1266
+ }
1267
+ ],
1268
+ "exports": [
1269
+ {
1270
+ "kind": "js",
1271
+ "name": "FullscreenController",
1272
+ "declaration": {
1273
+ "name": "FullscreenController",
1274
+ "module": "src/controllers/fullscreen-controller/fullscreen-controller.ts"
1275
+ }
1276
+ }
1277
+ ]
1278
+ },
1279
+ {
1280
+ "kind": "javascript-module",
1281
+ "path": "src/controllers/idle-controller/idle-controller.ts",
1282
+ "declarations": [
1283
+ {
1284
+ "kind": "class",
1285
+ "description": "How long the reader has been still, which is what an overlay hides itself on.\n\nA wait rather than leaving with the pointer, or a reader moving across a\nsurface to reach a control watches the control vanish under them. The wait is\nonly ever about *idling*: whether anything else keeps the overlay up, a\npaused player or an open menu, is the host's decision, and this only says\nwhen the reader stopped.\n\nThe delay is asked for on every restart rather than taken once, because it\nis a property the consumer can change while the video plays.",
1286
+ "name": "IdleController",
1287
+ "members": [
1288
+ {
1289
+ "kind": "field",
1290
+ "name": "timer",
1291
+ "type": {
1292
+ "text": "ReturnType<typeof setTimeout> | undefined"
1084
1293
  },
1085
- "description": "Stops trapping and returns focus to wherever it was."
1294
+ "privacy": "private"
1295
+ },
1296
+ {
1297
+ "kind": "field",
1298
+ "name": "stir",
1299
+ "readonly": true,
1300
+ "description": "Restarts the wait, on anything that counts as the reader being here.\n\nPointer movement and focus both count, and both have to: a reader using the\nkeyboard never moves a pointer, and a bar that only answered the mouse\nwould leave them nothing to look at. Zero turns the wait off and the bar\ngoes immediately; a large number keeps it up for as long as anyone is\nlikely to look."
1301
+ },
1302
+ {
1303
+ "kind": "field",
1304
+ "name": "leave",
1305
+ "readonly": true,
1306
+ "description": "Leaving is the one case that does not wait: the reader has gone somewhere\nelse, and there is nothing to reach for."
1086
1307
  },
1087
1308
  {
1088
1309
  "kind": "method",
@@ -1100,10 +1321,10 @@
1100
1321
  "exports": [
1101
1322
  {
1102
1323
  "kind": "js",
1103
- "name": "FocusTrapController",
1324
+ "name": "IdleController",
1104
1325
  "declaration": {
1105
- "name": "FocusTrapController",
1106
- "module": "src/controllers/focus-trap-controller/focus-trap-controller.ts"
1326
+ "name": "IdleController",
1327
+ "module": "src/controllers/idle-controller/idle-controller.ts"
1107
1328
  }
1108
1329
  }
1109
1330
  ]
@@ -1372,6 +1593,99 @@
1372
1593
  }
1373
1594
  ]
1374
1595
  },
1596
+ {
1597
+ "kind": "javascript-module",
1598
+ "path": "src/controllers/media-session-controller/media-session-controller.ts",
1599
+ "declarations": [
1600
+ {
1601
+ "kind": "variable",
1602
+ "name": "SKIP_SECONDS",
1603
+ "type": {
1604
+ "text": "number"
1605
+ },
1606
+ "default": "10",
1607
+ "description": "How far a skip control moves, in seconds.\n\nTen, which is the number every player that has one uses, and the number the\nplatform names in `seekbackward` and `seekforward` when it hands over a\ndefault. A different length here would be correct only on this player."
1608
+ },
1609
+ {
1610
+ "kind": "class",
1611
+ "description": "Holds the page's media session for one player, and tells the platform what\nis playing.\n\nThe handlers are what make a headset's play, pause and skip work at all.\nWithout them a platform falls back to guessing which element was meant,\nwhich is right often enough to hide the fault and wrong exactly when there\nare two.",
1612
+ "name": "MediaSessionController",
1613
+ "members": [
1614
+ {
1615
+ "kind": "method",
1616
+ "name": "claim",
1617
+ "return": {
1618
+ "type": {
1619
+ "text": "void"
1620
+ }
1621
+ },
1622
+ "parameters": [
1623
+ {
1624
+ "name": "metadata",
1625
+ "type": {
1626
+ "text": "{ artwork: string; title: string }"
1627
+ }
1628
+ },
1629
+ {
1630
+ "name": "handlers",
1631
+ "type": {
1632
+ "text": "SessionHandlers"
1633
+ }
1634
+ }
1635
+ ],
1636
+ "description": "Takes the session.\n\nOn play rather than on render, which is the ownership rule: a page with two\nplayers gives the session to whichever one the reader started, so a\nheadset's buttons land on what they are listening to rather than on\nwhichever came first in the markup."
1637
+ },
1638
+ {
1639
+ "kind": "method",
1640
+ "name": "report",
1641
+ "return": {
1642
+ "type": {
1643
+ "text": "void"
1644
+ }
1645
+ },
1646
+ "parameters": [
1647
+ {
1648
+ "name": "playing",
1649
+ "type": {
1650
+ "text": "boolean"
1651
+ }
1652
+ }
1653
+ ],
1654
+ "description": "Tells the platform whether the owner is playing, and nothing if it is not the owner."
1655
+ },
1656
+ {
1657
+ "kind": "method",
1658
+ "name": "hostDisconnected",
1659
+ "return": {
1660
+ "type": {
1661
+ "text": "void"
1662
+ }
1663
+ },
1664
+ "description": "The page's one media session goes back to nobody, but only if this\nelement is the one holding it."
1665
+ }
1666
+ ],
1667
+ "helix": {}
1668
+ }
1669
+ ],
1670
+ "exports": [
1671
+ {
1672
+ "kind": "js",
1673
+ "name": "SKIP_SECONDS",
1674
+ "declaration": {
1675
+ "name": "SKIP_SECONDS",
1676
+ "module": "src/controllers/media-session-controller/media-session-controller.ts"
1677
+ }
1678
+ },
1679
+ {
1680
+ "kind": "js",
1681
+ "name": "MediaSessionController",
1682
+ "declaration": {
1683
+ "name": "MediaSessionController",
1684
+ "module": "src/controllers/media-session-controller/media-session-controller.ts"
1685
+ }
1686
+ }
1687
+ ]
1688
+ },
1375
1689
  {
1376
1690
  "kind": "javascript-module",
1377
1691
  "path": "src/controllers/popover-controller/popover-controller.ts",
@@ -1514,6 +1828,90 @@
1514
1828
  }
1515
1829
  ]
1516
1830
  },
1831
+ {
1832
+ "kind": "javascript-module",
1833
+ "path": "src/controllers/remote-playback-controller/remote-playback-controller.ts",
1834
+ "declarations": [
1835
+ {
1836
+ "kind": "class",
1837
+ "description": "Watches for something to play the media on, and stops on the way out.\n\nThe Remote Playback API is the standard one, and it is what Safari\nimplements for AirPlay and Chrome for Cast: one call covers both, where the\nolder `webkitShowPlaybackTargetPicker` covers only the first. Firefox has\nneither, and `remote` simply being absent is what turns the control off\nthere.\n\n`watchAvailability` returns an id that has to be canceled, or the callback\noutlives the element that owns it: a callback holding a removed element is a\nleak with a `@state` write on the end.",
1838
+ "name": "RemotePlaybackController",
1839
+ "members": [
1840
+ {
1841
+ "kind": "field",
1842
+ "name": "media",
1843
+ "type": {
1844
+ "text": "HTMLMediaElement | undefined"
1845
+ },
1846
+ "privacy": "private"
1847
+ },
1848
+ {
1849
+ "kind": "field",
1850
+ "name": "watch",
1851
+ "type": {
1852
+ "text": "number | undefined"
1853
+ },
1854
+ "privacy": "private",
1855
+ "description": "Cancels the availability watch, since it outlives a single render."
1856
+ },
1857
+ {
1858
+ "kind": "method",
1859
+ "name": "attach",
1860
+ "return": {
1861
+ "type": {
1862
+ "text": "Promise<void>"
1863
+ }
1864
+ },
1865
+ "parameters": [
1866
+ {
1867
+ "name": "media",
1868
+ "type": {
1869
+ "text": "HTMLMediaElement"
1870
+ }
1871
+ }
1872
+ ],
1873
+ "description": "Starts watching the media element, once there is one."
1874
+ },
1875
+ {
1876
+ "kind": "method",
1877
+ "name": "prompt",
1878
+ "return": {
1879
+ "type": {
1880
+ "text": "Promise<void>"
1881
+ }
1882
+ },
1883
+ "description": "Opens the platform's own device picker. It has no list of its own."
1884
+ },
1885
+ {
1886
+ "kind": "method",
1887
+ "name": "hostDisconnected",
1888
+ "return": {
1889
+ "type": {
1890
+ "text": "void"
1891
+ }
1892
+ }
1893
+ },
1894
+ {
1895
+ "kind": "field",
1896
+ "name": "handleState",
1897
+ "privacy": "private",
1898
+ "readonly": true
1899
+ }
1900
+ ],
1901
+ "helix": {}
1902
+ }
1903
+ ],
1904
+ "exports": [
1905
+ {
1906
+ "kind": "js",
1907
+ "name": "RemotePlaybackController",
1908
+ "declaration": {
1909
+ "name": "RemotePlaybackController",
1910
+ "module": "src/controllers/remote-playback-controller/remote-playback-controller.ts"
1911
+ }
1912
+ }
1913
+ ]
1914
+ },
1517
1915
  {
1518
1916
  "kind": "javascript-module",
1519
1917
  "path": "src/controllers/source-controller/source-controller.ts",
@@ -1697,19 +2095,9 @@
1697
2095
  "declarations": [
1698
2096
  {
1699
2097
  "kind": "class",
1700
- "description": "Reads a design token as the sRGB triple a shader or a 2D canvas can take, and\ntells the host when the palette underneath it moves.\n\n**Two hops, and both are load-bearing.** A custom property's own value is the\nunevaluated function text, so `getComputedStyle` on a probe is the only way\nto see what it resolves to. And what it resolves to is not `rgb()`: the\npalette derives from an `oklch()` seed, and Chrome answers `lab(100 0 0)` for\nwhite. Parsing that with a regular expression for numbers reads the three\nchannels as 100, 0 and 0, which divided by 255 is a dark red.\n\nA screenshot cannot catch that: a warm cast on a saturated gradient is also\nwhat chroma subsampling produces, so the picture agrees with the bug.\n\nThe conversion is therefore the browser's own: fill a pixel and read it back.\nThat holds for `lab()`, `oklch()`, `color-mix()` and whatever a token is\nwritten in next, which is the property worth having in a design system whose\npalette is derived rather than listed.\n\n**A controller and not a mixin**, by this repository's own line: it adds no\nproperty or method a consumer sets. It reacts to lifecycle, holding a probe\nthat has to be created after the host connects and removed when it leaves,\nand it watches for the color mode changing so the host can repaint.\n\n**Client only, and that is the contract rather than a limitation.** There is\nno computed style on a server and no canvas to convert with, so `read`\nanswers the fallback there. Every caller is drawing into a canvas, which does\nnot exist on a server either.",
2098
+ "description": "Reads a design token as the sRGB triple a shader or a 2D canvas can take, and\ntells the host when the palette underneath it moves.\n\n**Two hops, and both are load-bearing.** A custom property's own value is the\nunevaluated function text, so `getComputedStyle` on a probe is the only way\nto see what it resolves to. The second hop is `readColor`, which is shared\nwith every other caller that has a resolved color and needs channels: what it\nresolves to is not `rgb()`, the\npalette derives from an `oklch()` seed, and Chrome answers `lab(100 0 0)` for\nwhite. Parsing that with a regular expression for numbers reads the three\nchannels as 100, 0 and 0, which divided by 255 is a dark red.\n\nA screenshot cannot catch that: a warm cast on a saturated gradient is also\nwhat chroma subsampling produces, so the picture agrees with the bug.\n\nThe conversion is therefore the browser's own: fill a pixel and read it back.\nThat holds for `lab()`, `oklch()`, `color-mix()` and whatever a token is\nwritten in next, which is the property worth having in a design system whose\npalette is derived rather than listed.\n\n**A controller and not a mixin**, by this repository's own line: it adds no\nproperty or method a consumer sets. It reacts to lifecycle, holding a probe\nthat has to be created after the host connects and removed when it leaves,\nand it watches for the color mode changing so the host can repaint.\n\n**Client only, and that is the contract rather than a limitation.** There is\nno computed style on a server and no canvas to convert with, so `read`\nanswers the fallback there. Every caller is drawing into a canvas, which does\nnot exist on a server either.",
1701
2099
  "name": "TokenColorController",
1702
2100
  "members": [
1703
- {
1704
- "kind": "field",
1705
- "name": "context",
1706
- "type": {
1707
- "text": "CanvasRenderingContext2D | null"
1708
- },
1709
- "privacy": "private",
1710
- "default": "null",
1711
- "description": "Where a resolved color is converted, one pixel at a time."
1712
- },
1713
2101
  {
1714
2102
  "kind": "field",
1715
2103
  "name": "modes",
@@ -4523,6 +4911,62 @@
4523
4911
  "module": "./controllers/copy-controller/copy-controller.js"
4524
4912
  }
4525
4913
  },
4914
+ {
4915
+ "kind": "js",
4916
+ "name": "activeCue",
4917
+ "declaration": {
4918
+ "name": "activeCue",
4919
+ "module": "./controllers/cue-controller/cue-controller.js"
4920
+ }
4921
+ },
4922
+ {
4923
+ "kind": "js",
4924
+ "name": "adoptTrack",
4925
+ "declaration": {
4926
+ "name": "adoptTrack",
4927
+ "module": "./controllers/cue-controller/cue-controller.js"
4928
+ }
4929
+ },
4930
+ {
4931
+ "kind": "js",
4932
+ "name": "Caption",
4933
+ "declaration": {
4934
+ "name": "Caption",
4935
+ "module": "./controllers/cue-controller/cue-controller.js"
4936
+ }
4937
+ },
4938
+ {
4939
+ "kind": "js",
4940
+ "name": "CueController",
4941
+ "declaration": {
4942
+ "name": "CueController",
4943
+ "module": "./controllers/cue-controller/cue-controller.js"
4944
+ }
4945
+ },
4946
+ {
4947
+ "kind": "js",
4948
+ "name": "captionsAmong",
4949
+ "declaration": {
4950
+ "name": "captionsAmong",
4951
+ "module": "./controllers/cue-controller/cue-controller.js"
4952
+ }
4953
+ },
4954
+ {
4955
+ "kind": "js",
4956
+ "name": "captionTracks",
4957
+ "declaration": {
4958
+ "name": "captionTracks",
4959
+ "module": "./controllers/cue-controller/cue-controller.js"
4960
+ }
4961
+ },
4962
+ {
4963
+ "kind": "js",
4964
+ "name": "showCaption",
4965
+ "declaration": {
4966
+ "name": "showCaption",
4967
+ "module": "./controllers/cue-controller/cue-controller.js"
4968
+ }
4969
+ },
4526
4970
  {
4527
4971
  "kind": "js",
4528
4972
  "name": "DisabledCascadeController",
@@ -4605,10 +5049,18 @@
4605
5049
  },
4606
5050
  {
4607
5051
  "kind": "js",
4608
- "name": "FocusTrapController",
5052
+ "name": "FullscreenController",
5053
+ "declaration": {
5054
+ "name": "FullscreenController",
5055
+ "module": "./controllers/fullscreen-controller/fullscreen-controller.js"
5056
+ }
5057
+ },
5058
+ {
5059
+ "kind": "js",
5060
+ "name": "IdleController",
4609
5061
  "declaration": {
4610
- "name": "FocusTrapController",
4611
- "module": "./controllers/focus-trap-controller/focus-trap-controller.js"
5062
+ "name": "IdleController",
5063
+ "module": "./controllers/idle-controller/idle-controller.js"
4612
5064
  }
4613
5065
  },
4614
5066
  {
@@ -4651,6 +5103,30 @@
4651
5103
  "module": "./controllers/keyboard-navigation-controller/keyboard-navigation-controller.js"
4652
5104
  }
4653
5105
  },
5106
+ {
5107
+ "kind": "js",
5108
+ "name": "MediaSessionController",
5109
+ "declaration": {
5110
+ "name": "MediaSessionController",
5111
+ "module": "./controllers/media-session-controller/media-session-controller.js"
5112
+ }
5113
+ },
5114
+ {
5115
+ "kind": "js",
5116
+ "name": "SessionHandlers",
5117
+ "declaration": {
5118
+ "name": "SessionHandlers",
5119
+ "module": "./controllers/media-session-controller/media-session-controller.js"
5120
+ }
5121
+ },
5122
+ {
5123
+ "kind": "js",
5124
+ "name": "SKIP_SECONDS",
5125
+ "declaration": {
5126
+ "name": "SKIP_SECONDS",
5127
+ "module": "./controllers/media-session-controller/media-session-controller.js"
5128
+ }
5129
+ },
4654
5130
  {
4655
5131
  "kind": "js",
4656
5132
  "name": "PopoverController",
@@ -4667,6 +5143,22 @@
4667
5143
  "module": "./controllers/popover-controller/popover-controller.js"
4668
5144
  }
4669
5145
  },
5146
+ {
5147
+ "kind": "js",
5148
+ "name": "RemotePlaybackController",
5149
+ "declaration": {
5150
+ "name": "RemotePlaybackController",
5151
+ "module": "./controllers/remote-playback-controller/remote-playback-controller.js"
5152
+ }
5153
+ },
5154
+ {
5155
+ "kind": "js",
5156
+ "name": "RemoteReport",
5157
+ "declaration": {
5158
+ "name": "RemoteReport",
5159
+ "module": "./controllers/remote-playback-controller/remote-playback-controller.js"
5160
+ }
5161
+ },
4670
5162
  {
4671
5163
  "kind": "js",
4672
5164
  "name": "SourceController",
@@ -5475,6 +5967,54 @@
5475
5967
  "module": "./utils/create-sheet/create-sheet.js"
5476
5968
  }
5477
5969
  },
5970
+ {
5971
+ "kind": "js",
5972
+ "name": "Color",
5973
+ "declaration": {
5974
+ "name": "Color",
5975
+ "module": "./utils/css-color/css-color.js"
5976
+ }
5977
+ },
5978
+ {
5979
+ "kind": "js",
5980
+ "name": "ColorFormat",
5981
+ "declaration": {
5982
+ "name": "ColorFormat",
5983
+ "module": "./utils/css-color/css-color.js"
5984
+ }
5985
+ },
5986
+ {
5987
+ "kind": "js",
5988
+ "name": "formatColor",
5989
+ "declaration": {
5990
+ "name": "formatColor",
5991
+ "module": "./utils/css-color/css-color.js"
5992
+ }
5993
+ },
5994
+ {
5995
+ "kind": "js",
5996
+ "name": "parseColor",
5997
+ "declaration": {
5998
+ "name": "parseColor",
5999
+ "module": "./utils/css-color/css-color.js"
6000
+ }
6001
+ },
6002
+ {
6003
+ "kind": "js",
6004
+ "name": "splitColors",
6005
+ "declaration": {
6006
+ "name": "splitColors",
6007
+ "module": "./utils/css-color/css-color.js"
6008
+ }
6009
+ },
6010
+ {
6011
+ "kind": "js",
6012
+ "name": "toCss",
6013
+ "declaration": {
6014
+ "name": "toCss",
6015
+ "module": "./utils/css-color/css-color.js"
6016
+ }
6017
+ },
5478
6018
  {
5479
6019
  "kind": "js",
5480
6020
  "name": "customElement",
@@ -5659,6 +6199,30 @@
5659
6199
  "module": "./utils/mark-region/mark-region.js"
5660
6200
  }
5661
6201
  },
6202
+ {
6203
+ "kind": "js",
6204
+ "name": "copiesOf",
6205
+ "declaration": {
6206
+ "name": "copiesOf",
6207
+ "module": "./utils/media-copy/media-copy.js"
6208
+ }
6209
+ },
6210
+ {
6211
+ "kind": "js",
6212
+ "name": "MediaCopy",
6213
+ "declaration": {
6214
+ "name": "MediaCopy",
6215
+ "module": "./utils/media-copy/media-copy.js"
6216
+ }
6217
+ },
6218
+ {
6219
+ "kind": "js",
6220
+ "name": "sameCopies",
6221
+ "declaration": {
6222
+ "name": "sameCopies",
6223
+ "module": "./utils/media-copy/media-copy.js"
6224
+ }
6225
+ },
5662
6226
  {
5663
6227
  "kind": "js",
5664
6228
  "name": "onUnlessFalse",
@@ -5667,6 +6231,22 @@
5667
6231
  "module": "./utils/on-unless-false/on-unless-false.js"
5668
6232
  }
5669
6233
  },
6234
+ {
6235
+ "kind": "js",
6236
+ "name": "ColorBytes",
6237
+ "declaration": {
6238
+ "name": "ColorBytes",
6239
+ "module": "./utils/read-color/read-color.js"
6240
+ }
6241
+ },
6242
+ {
6243
+ "kind": "js",
6244
+ "name": "readColor",
6245
+ "declaration": {
6246
+ "name": "readColor",
6247
+ "module": "./utils/read-color/read-color.js"
6248
+ }
6249
+ },
5670
6250
  {
5671
6251
  "kind": "js",
5672
6252
  "name": "Row",
@@ -9277,6 +9857,130 @@
9277
9857
  }
9278
9858
  ]
9279
9859
  },
9860
+ {
9861
+ "kind": "javascript-module",
9862
+ "path": "src/utils/css-color/css-color.ts",
9863
+ "declarations": [
9864
+ {
9865
+ "kind": "function",
9866
+ "name": "splitColors",
9867
+ "return": {
9868
+ "type": {
9869
+ "text": "string[]"
9870
+ }
9871
+ },
9872
+ "parameters": [
9873
+ {
9874
+ "name": "value",
9875
+ "type": {
9876
+ "text": "string"
9877
+ }
9878
+ }
9879
+ ],
9880
+ "description": "Splits a comma-separated list of colors, ignoring the commas inside them.\n\nA plain `split(',')` cannot hold the legacy syntaxes:\n`#ef4444, rgb(249 115 22), rgba(34,197,94,.5)` comes apart into six fragments,\nfour of them not colors, and the row quietly renders three swatches instead of\nfour. The modern space-separated forms survive it, which is what makes such a\nlist look like it works.\n\nDepth rather than a regular expression, because the thing that matters is\nwhether a comma is inside a function call and that is exactly what a counter\nanswers. No color syntax nests, so a single counter is enough."
9881
+ },
9882
+ {
9883
+ "kind": "function",
9884
+ "name": "parseColor",
9885
+ "return": {
9886
+ "type": {
9887
+ "text": "Color | null"
9888
+ }
9889
+ },
9890
+ "parameters": [
9891
+ {
9892
+ "name": "value",
9893
+ "type": {
9894
+ "text": "string"
9895
+ }
9896
+ }
9897
+ ],
9898
+ "description": "Any of the syntaxes to the working model, or `null` where it is not a color.\n\nNamed colors are not here. Resolving `rebeccapurple` means either shipping\nthe table of a hundred and forty or asking the browser, and the browser is not\npresent on the server: a field that accepted a name client-side and rejected\nit during a server render would be worse than one that never took it."
9899
+ },
9900
+ {
9901
+ "kind": "function",
9902
+ "name": "formatColor",
9903
+ "return": {
9904
+ "type": {
9905
+ "text": "string"
9906
+ }
9907
+ },
9908
+ "parameters": [
9909
+ {
9910
+ "name": "color",
9911
+ "type": {
9912
+ "text": "Color"
9913
+ }
9914
+ },
9915
+ {
9916
+ "name": "format",
9917
+ "type": {
9918
+ "text": "ColorFormat"
9919
+ }
9920
+ },
9921
+ {
9922
+ "name": "withAlpha",
9923
+ "type": {
9924
+ "text": "boolean"
9925
+ }
9926
+ }
9927
+ ],
9928
+ "description": "The working model back out, in the syntax asked for.\n\nThe alpha is written only where the component carries one. A consumer who did\nnot ask for transparency should not have to strip `ff` off every hex or read\npast a fourth argument they never wanted."
9929
+ },
9930
+ {
9931
+ "kind": "function",
9932
+ "name": "toCss",
9933
+ "return": {
9934
+ "type": {
9935
+ "text": "string"
9936
+ }
9937
+ },
9938
+ "parameters": [
9939
+ {
9940
+ "name": "color",
9941
+ "type": {
9942
+ "text": "Color"
9943
+ }
9944
+ }
9945
+ ],
9946
+ "description": "The color as CSS, for the swatches and the tracks. Always with its alpha."
9947
+ }
9948
+ ],
9949
+ "exports": [
9950
+ {
9951
+ "kind": "js",
9952
+ "name": "splitColors",
9953
+ "declaration": {
9954
+ "name": "splitColors",
9955
+ "module": "src/utils/css-color/css-color.ts"
9956
+ }
9957
+ },
9958
+ {
9959
+ "kind": "js",
9960
+ "name": "parseColor",
9961
+ "declaration": {
9962
+ "name": "parseColor",
9963
+ "module": "src/utils/css-color/css-color.ts"
9964
+ }
9965
+ },
9966
+ {
9967
+ "kind": "js",
9968
+ "name": "formatColor",
9969
+ "declaration": {
9970
+ "name": "formatColor",
9971
+ "module": "src/utils/css-color/css-color.ts"
9972
+ }
9973
+ },
9974
+ {
9975
+ "kind": "js",
9976
+ "name": "toCss",
9977
+ "declaration": {
9978
+ "name": "toCss",
9979
+ "module": "src/utils/css-color/css-color.ts"
9980
+ }
9981
+ }
9982
+ ]
9983
+ },
9280
9984
  {
9281
9985
  "kind": "javascript-module",
9282
9986
  "path": "src/utils/custom-element/custom-element.ts",
@@ -9521,7 +10225,7 @@
9521
10225
  }
9522
10226
  }
9523
10227
  ],
9524
- "description": "Everything inside `root` the reader can reach with Tab, in document order.\n\nSeparate from the focus trap because finding is the half most callers want:\nroving tabindex, arrow-key navigation and \"focus the first thing\" all need\nthis list and none of them traps anything.\n\nVisibility is tested with `getClientRects()` rather than `offsetParent`,\nwhich is null for anything inside a `position: fixed` container. A drawer is\nexactly that, so the `offsetParent` test excluded every control in the thing\nbeing trapped."
10228
+ "description": "Everything inside `root` the reader can reach with Tab, in document order.\n\nRoving tabindex, arrow-key navigation and \"focus the first thing\" all need\nthis list, which is why it is a utility rather than part of anything that\nasks for it.\n\nVisibility is tested with `getClientRects()` rather than `offsetParent`,\nwhich is null for anything inside a `position: fixed` container, so the\n`offsetParent` test excluded every control on a raised surface."
9525
10229
  },
9526
10230
  {
9527
10231
  "kind": "function",
@@ -9555,7 +10259,7 @@
9555
10259
  }
9556
10260
  }
9557
10261
  ],
9558
- "description": "Whether `node` sits inside `root`, following shadow boundaries upward.\n\n`Node.contains` walks `parentNode` and a shadow root's `parentNode` is null,\nso it answers false for anything inside a component. Paired with\n`deepActiveElement`, which returns exactly such a node, that pairing is a trap\nof its own: the two look like they belong together and one of them crosses.\n\n`contains(deepActiveElement())` is the shape to avoid: focus on a\n`helix-button` reports as the `<button>` in its shadow root, which is in no\nlight-DOM container anywhere, so a trap written that way reads every reader\nas already outside it.\n\n`host` is reached without naming `ShadowRoot`, which the server has no global\nfor. This runs from a keydown handler and would never execute there, but a\nbare reference is the kind that surfaces as a crash in Lit's result collector\nthe first time something calls it early."
10262
+ "description": "Whether `node` sits inside `root`, following shadow boundaries upward.\n\n`Node.contains` walks `parentNode` and a shadow root's `parentNode` is null,\nso it answers false for anything inside a component. Paired with\n`deepActiveElement`, which returns exactly such a node, the pair is a hazard of\nits own: the two look like they belong together and one of them crosses.\n\n`contains(deepActiveElement())` is the shape to avoid: focus on a\n`helix-button` reports as the `<button>` in its shadow root, which is in no\nlight-DOM container anywhere, so a containment test written that way reads\nevery reader as already outside.\n\n`host` is reached without naming `ShadowRoot`, which the server has no global\nfor. This runs from a keydown handler and would never execute there, but a\nbare reference is the kind that surfaces as a crash in Lit's result collector\nthe first time something calls it early."
9559
10263
  }
9560
10264
  ],
9561
10265
  "exports": [
@@ -9857,6 +10561,72 @@
9857
10561
  }
9858
10562
  ]
9859
10563
  },
10564
+ {
10565
+ "kind": "javascript-module",
10566
+ "path": "src/utils/media-copy/media-copy.ts",
10567
+ "declarations": [
10568
+ {
10569
+ "kind": "function",
10570
+ "name": "copiesOf",
10571
+ "return": {
10572
+ "type": {
10573
+ "text": "MediaCopy[]"
10574
+ }
10575
+ },
10576
+ "parameters": [
10577
+ {
10578
+ "name": "elements",
10579
+ "type": {
10580
+ "text": "readonly Element[]"
10581
+ }
10582
+ }
10583
+ ],
10584
+ "description": "The copies to make of what the default slot is given."
10585
+ },
10586
+ {
10587
+ "kind": "function",
10588
+ "name": "sameCopies",
10589
+ "return": {
10590
+ "type": {
10591
+ "text": "boolean"
10592
+ }
10593
+ },
10594
+ "parameters": [
10595
+ {
10596
+ "name": "one",
10597
+ "type": {
10598
+ "text": "readonly MediaCopy[]"
10599
+ }
10600
+ },
10601
+ {
10602
+ "name": "two",
10603
+ "type": {
10604
+ "text": "readonly MediaCopy[]"
10605
+ }
10606
+ }
10607
+ ],
10608
+ "description": "Whether two reads describe the same media.\n\nThe same set again is not a change. A video reloads for every new list, and\nthe first `slotchange` reports the children the element has already read."
10609
+ }
10610
+ ],
10611
+ "exports": [
10612
+ {
10613
+ "kind": "js",
10614
+ "name": "copiesOf",
10615
+ "declaration": {
10616
+ "name": "copiesOf",
10617
+ "module": "src/utils/media-copy/media-copy.ts"
10618
+ }
10619
+ },
10620
+ {
10621
+ "kind": "js",
10622
+ "name": "sameCopies",
10623
+ "declaration": {
10624
+ "name": "sameCopies",
10625
+ "module": "src/utils/media-copy/media-copy.ts"
10626
+ }
10627
+ }
10628
+ ]
10629
+ },
9860
10630
  {
9861
10631
  "kind": "javascript-module",
9862
10632
  "path": "src/utils/on-unless-false/on-unless-false.ts",
@@ -9882,6 +10652,41 @@
9882
10652
  }
9883
10653
  ]
9884
10654
  },
10655
+ {
10656
+ "kind": "javascript-module",
10657
+ "path": "src/utils/read-color/read-color.ts",
10658
+ "declarations": [
10659
+ {
10660
+ "kind": "function",
10661
+ "name": "readColor",
10662
+ "return": {
10663
+ "type": {
10664
+ "text": "ColorBytes | undefined"
10665
+ }
10666
+ },
10667
+ "parameters": [
10668
+ {
10669
+ "name": "value",
10670
+ "type": {
10671
+ "text": "string"
10672
+ },
10673
+ "description": "A resolved color, as `getComputedStyle` reports one."
10674
+ }
10675
+ ],
10676
+ "description": "A computed color as three sRGB bytes, or nothing where it cannot be read.\n\n**Painting it is the only way to read it.** A color-typed property is\nresolved before it is reported, but resolved is not the same as converted:\nChrome hands back `lab(100 0 0)` for a token built through `oklab`, and\nscraping the first three numbers out of that and calling them red, green and\nblue made white arrive as `rgb(100, 0, 0)`. In light mode the near dots were\n`lab(0 0 0)`, which scrapes to black and looks right, so only dark mode\nshowed it: dark red mixed toward black, on a near-black ground.\n\n**Painted twice, over two different grounds, which is what makes the refusal\nhonest.** Assigning a value the engine will not parse leaves `fillStyle`\nexactly where it was, so a single paint cannot tell a color apart from the\nground it failed to replace. Over black and over white, a value that parsed\ngives the same answer twice and one that did not gives two. A caller then\nchooses its own fallback rather than being handed a color nobody picked,\nwhich is the half both earlier copies of this had to guess at.\n\nA readback is not free, so the answer is held against the string it came\nfrom, refusals included. The oldest goes when the store is full rather than\nall of them, or a page deriving colors on a slider pays for every frame\ntwice."
10677
+ }
10678
+ ],
10679
+ "exports": [
10680
+ {
10681
+ "kind": "js",
10682
+ "name": "readColor",
10683
+ "declaration": {
10684
+ "name": "readColor",
10685
+ "module": "src/utils/read-color/read-color.ts"
10686
+ }
10687
+ }
10688
+ ]
10689
+ },
9885
10690
  {
9886
10691
  "kind": "javascript-module",
9887
10692
  "path": "src/utils/read-series/read-series.ts",