@kitn.ai/ui 0.18.2 → 0.20.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.
- package/LICENSE +6 -0
- package/NOTICE +54 -0
- package/dist/components/audio-visualizer/audio-visualizer.voice-fixture.d.ts +59 -0
- package/dist/components/audio-visualizer/aurora.glsl.d.ts +114 -0
- package/dist/components/audio-visualizer/index.d.ts +155 -0
- package/dist/components/audio-visualizer/shader-canvas.d.ts +130 -0
- package/dist/components/audio-visualizer/sizes.d.ts +25 -0
- package/dist/components/audio-visualizer/variant-aurora.d.ts +89 -0
- package/dist/components/audio-visualizer/variant-bar.d.ts +46 -0
- package/dist/components/audio-visualizer/variant-custom.d.ts +73 -0
- package/dist/components/audio-visualizer/variant-grid.d.ts +22 -0
- package/dist/components/audio-visualizer/variant-radial.d.ts +13 -0
- package/dist/components/audio-visualizer/variant-wave.d.ts +11 -0
- package/dist/components/audio-visualizer/wave.glsl.d.ts +16 -0
- package/dist/components/thread.d.ts +54 -0
- package/dist/create-tween-hvpmSuwW.js +7 -0
- package/dist/custom-elements.json +477 -0
- package/dist/elements/agent-card.js +1 -1
- package/dist/elements/artifact.js +1 -1
- package/dist/elements/attachments.js +1 -1
- package/dist/elements/audio-visualizer.d.ts +1 -0
- package/dist/elements/audio-visualizer.js +1 -0
- package/dist/elements/autoloader.js +1 -1
- package/dist/elements/avatar.js +1 -1
- package/dist/elements/badge.js +1 -1
- package/dist/elements/button.js +1 -1
- package/dist/elements/card.js +1 -1
- package/dist/elements/cards.js +1 -1
- package/dist/elements/chain-of-thought.js +1 -1
- package/dist/elements/chat-scope-picker.js +1 -1
- package/dist/elements/chat-workspace.js +1 -1
- package/dist/elements/chat.js +1 -1
- package/dist/elements/checkpoint.js +1 -1
- package/dist/elements/choice.js +1 -1
- package/dist/elements/chunks/{Icon-C2LTALdM.js → Icon-Tj58ES_d.js} +1 -1
- package/dist/elements/chunks/action-icons-ns1Fnedj.js +1 -0
- package/dist/elements/chunks/arrow-left-DQocUFvy.js +1 -0
- package/dist/elements/chunks/{attachments-DLJcWCDV.js → attachments-mCB5F1Fq.js} +1 -1
- package/dist/elements/chunks/audio-visualizer-BqNy7dfm.js +1 -0
- package/dist/elements/chunks/{badge-BTNZTODL.js → badge-B4Zuajl2.js} +1 -1
- package/dist/elements/chunks/{button-DBDZebGr.js → button-ZWNif3--.js} +1 -1
- package/dist/elements/chunks/{card-YfCyI4fH.js → card-BkHbp4BA.js} +1 -1
- package/dist/elements/chunks/chat-thread-CkRN6pZ7.js +1 -0
- package/dist/elements/chunks/check-B9-gn6SS.js +1 -0
- package/dist/elements/chunks/chevron-down-BNsxc2hk.js +1 -0
- package/dist/elements/chunks/chevron-right-Hv00StBO.js +1 -0
- package/dist/elements/chunks/circle-0e3KJTDr.js +1 -0
- package/dist/elements/chunks/circle-check-BNAS0JEy.js +1 -0
- package/dist/elements/chunks/{circle-x-pDtzlghQ.js → circle-x-B7faJFlY.js} +1 -1
- package/dist/elements/chunks/{code-block-DZ6mz6vs.js → code-block-BFUHn6Va.js} +1 -1
- package/dist/elements/chunks/collapsible-DM1Ckd4e.js +1 -0
- package/dist/elements/chunks/{composer-BZyPU6gD.js → composer-BguS0QL_.js} +7 -3
- package/dist/elements/chunks/{context-Cx6eQA_G.js → context-Cb7k4F-v.js} +1 -1
- package/dist/elements/chunks/{conversation-list-C4xTQSpT.js → conversation-list-CvoAkWhN.js} +1 -1
- package/dist/elements/chunks/create-tween-oVvAIG8r.js +7 -0
- package/dist/elements/chunks/{default-input-BZcwyKcP.js → default-input-Cqqilg9t.js} +1 -1
- package/dist/elements/chunks/define-D1din7Ld.js +1 -0
- package/dist/elements/chunks/{disclosure-BcBs--Jf.js → disclosure-CIZ5c8nO.js} +1 -1
- package/dist/elements/chunks/{download-BDQwcFav.js → download-Dsbg3ste.js} +1 -1
- package/dist/elements/chunks/{dropdown-wgs8xCJZ.js → dropdown-BDd1-Zgi.js} +1 -1
- package/dist/elements/chunks/{ellipsis-CUiEJ6R1.js → ellipsis-Dw6qrss-.js} +1 -1
- package/dist/elements/chunks/{external-link-DJUmC7g2.js → external-link-Vt74zAoL.js} +1 -1
- package/dist/elements/chunks/{file-text-DOYfYvrh.js → file-text-CZrZF6_v.js} +1 -1
- package/dist/elements/chunks/{file-tree-DQzNQrbr.js → file-tree-DF4okKyo.js} +1 -1
- package/dist/elements/chunks/{folder-DBDUBANz.js → folder-glP5rha4.js} +1 -1
- package/dist/elements/chunks/hover-card-Cmm6Q53m.js +1 -0
- package/dist/elements/chunks/icon-BLb8beoi.js +1 -0
- package/dist/elements/chunks/{info-DBQl-m6e.js → info-CIsv92JK.js} +1 -1
- package/dist/elements/chunks/{input-DZD67EvW.js → input-Dds1kFyt.js} +1 -1
- package/dist/elements/chunks/{kbd-DbyVrlnU.js → kbd-De_xbgi9.js} +1 -1
- package/dist/elements/chunks/{link-vko7AttA.js → link-i4r_k7z4.js} +1 -1
- package/dist/elements/chunks/{loader-Cg6rO8tQ.js → loader-BmQH4s3V.js} +1 -1
- package/dist/elements/chunks/{markdown-n3Xbj3PR.js → markdown-Zi21WhUz.js} +7 -7
- package/dist/elements/chunks/{message-CgKytiSB.js → message-BqyXDN3-.js} +1 -1
- package/dist/elements/chunks/{message-feedback-Ds3-US0e.js → message-feedback-Djr3z6Yb.js} +1 -1
- package/dist/elements/chunks/message-square-vFAgbDaX.js +1 -0
- package/dist/elements/chunks/{minimize-2-3kCdE0q3.js → minimize-2--ifeCGrl.js} +1 -1
- package/dist/elements/chunks/{model-switcher-BMtXGwLU.js → model-switcher-B94YAKsM.js} +1 -1
- package/dist/elements/chunks/{overlay-B1-MBI6h.js → overlay-kD8dwRv_.js} +1 -1
- package/dist/elements/chunks/{paperclip-DDEfmkHc.js → paperclip-q21dYOSK.js} +1 -1
- package/dist/elements/chunks/{progress-bar-Dwbiyv3s.js → progress-bar-BrAIIaaJ.js} +1 -1
- package/dist/elements/chunks/{prompt-suggestion-9lvgFcEa.js → prompt-suggestion-Bjn2K-fR.js} +1 -1
- package/dist/elements/chunks/{reasoning-DzHWsdAJ.js → reasoning-DTpCGtnY.js} +1 -1
- package/dist/elements/chunks/resizable-Dp4JExql.js +1 -0
- package/dist/elements/chunks/{rotate-cw-jpisOdOV.js → rotate-cw-CFWWkpj2.js} +1 -1
- package/dist/elements/chunks/{scroll-area-g5Ze3K6f.js → scroll-area-Bb0kHH1j.js} +1 -1
- package/dist/elements/chunks/scroll-button-B0cH5fXf.js +1 -0
- package/dist/elements/chunks/{separator-Bmjc5XyX.js → separator-9kRgr1X9.js} +1 -1
- package/dist/elements/chunks/{settings-Cz1bMJP9.js → settings-B8lmec4D.js} +1 -1
- package/dist/elements/chunks/{settings-group-9imY3kRA.js → settings-group-I0hEuXiN.js} +1 -1
- package/dist/elements/chunks/{share-BdenF0wQ.js → share-C3lTiYGt.js} +1 -1
- package/dist/elements/chunks/skeleton-DSXW5Yya.js +1 -0
- package/dist/elements/chunks/{slots-DkZ-Cmha.js → slots--V19HwUb.js} +1 -1
- package/dist/elements/chunks/{star-DY6z1UUH.js → star-CSJtGxgv.js} +1 -1
- package/dist/elements/chunks/{store-th4VyJXM.js → store-CHyW95TP.js} +1 -1
- package/dist/elements/chunks/{text-shimmer-CEi-tlXr.js → text-shimmer-Cn9TmNpI.js} +1 -1
- package/dist/elements/chunks/textarea-CUwqAX0G.js +1 -0
- package/dist/elements/chunks/{thumbs-up-BR33KLfA.js → thumbs-up-PNVVThEF.js} +1 -1
- package/dist/elements/chunks/{toast-store-BEMda0fv.js → toast-store-DKbQn3Wn.js} +1 -1
- package/dist/elements/chunks/{tool-TMued3WO.js → tool-DjTRpIpl.js} +1 -1
- package/dist/elements/chunks/tooltip-CgBZCVKp.js +1 -0
- package/dist/elements/chunks/{triangle-alert-B07fX3Q8.js → triangle-alert-DsHTHCPm.js} +1 -1
- package/dist/elements/chunks/use-card-resolution-BSu_XHjH.js +1 -0
- package/dist/elements/chunks/variant-aurora-Dc4nQUia.js +218 -0
- package/dist/elements/chunks/variant-custom-y_3kVQhS.js +1 -0
- package/dist/elements/chunks/variant-wave-D5OR1-9Z.js +116 -0
- package/dist/elements/chunks/{video-MKp3LmNT.js → video-B7vmenoC.js} +1 -1
- package/dist/elements/chunks/x-GRUeeQFT.js +1 -0
- package/dist/elements/coachmark.js +1 -1
- package/dist/elements/code-block.js +1 -1
- package/dist/elements/command.js +1 -1
- package/dist/elements/compare.js +1 -1
- package/dist/elements/composer.js +1 -1
- package/dist/elements/confirm-card.js +1 -1
- package/dist/elements/context-meter.js +1 -1
- package/dist/elements/conversation-list.js +1 -1
- package/dist/elements/define.d.ts +11 -0
- package/dist/elements/dialog.js +1 -1
- package/dist/elements/editable-label.js +1 -1
- package/dist/elements/embed.js +1 -1
- package/dist/elements/empty.js +1 -1
- package/dist/elements/feedback-bar.js +1 -1
- package/dist/elements/file-tree.js +1 -1
- package/dist/elements/file-upload.js +1 -1
- package/dist/elements/form.js +1 -1
- package/dist/elements/hover-card.js +1 -1
- package/dist/elements/icon.js +1 -1
- package/dist/elements/image.js +1 -1
- package/dist/elements/input.js +1 -1
- package/dist/elements/kbd.js +1 -1
- package/dist/elements/link-preview.js +1 -1
- package/dist/elements/loader.js +1 -1
- package/dist/elements/markdown.js +1 -1
- package/dist/elements/menu.js +1 -1
- package/dist/elements/message-skills.js +1 -1
- package/dist/elements/message.js +1 -1
- package/dist/elements/model-switcher.js +1 -1
- package/dist/elements/nav.js +1 -1
- package/dist/elements/notice.js +1 -1
- package/dist/elements/pane-group.js +1 -1
- package/dist/elements/pane.js +1 -1
- package/dist/elements/popover.js +1 -1
- package/dist/elements/progress-bar.js +1 -1
- package/dist/elements/prompt-dock.js +1 -1
- package/dist/elements/prompt-input.js +1 -1
- package/dist/elements/prompt-suggestions.js +1 -1
- package/dist/elements/reasoning.js +1 -1
- package/dist/elements/remote.js +1 -1
- package/dist/elements/resizable.js +1 -1
- package/dist/elements/response-stream.js +1 -1
- package/dist/elements/screen.js +1 -1
- package/dist/elements/scroll-area.js +1 -1
- package/dist/elements/scroll-button.js +1 -1
- package/dist/elements/search.js +1 -1
- package/dist/elements/segmented.js +1 -1
- package/dist/elements/separator.js +1 -1
- package/dist/elements/setting-item.js +1 -1
- package/dist/elements/settings-group.js +1 -1
- package/dist/elements/skeleton.js +1 -1
- package/dist/elements/slots.d.ts +15 -0
- package/dist/elements/source.js +1 -1
- package/dist/elements/status.js +1 -1
- package/dist/elements/switch.js +1 -1
- package/dist/elements/tabs.js +1 -1
- package/dist/elements/tasks.js +1 -1
- package/dist/elements/text-shimmer.js +1 -1
- package/dist/elements/thinking-bar.js +1 -1
- package/dist/elements/thread.d.ts +1 -0
- package/dist/elements/thread.js +1 -0
- package/dist/elements/toast.js +1 -1
- package/dist/elements/tool.js +1 -1
- package/dist/elements/tooltip.js +1 -1
- package/dist/elements/voice-input.js +1 -1
- package/dist/elements/voice-output.js +1 -1
- package/dist/elements.d.ts +62 -0
- package/dist/index.js +3411 -3328
- package/dist/kai.es.js +1 -1
- package/dist/llms/llms-full.txt +70 -4
- package/dist/llms/llms.txt +3 -3
- package/dist/primitives/audio-bands.d.ts +106 -0
- package/dist/primitives/create-tween.d.ts +33 -0
- package/dist/primitives/use-audio-analysis.d.ts +149 -0
- package/dist/primitives/use-sequencer.d.ts +13 -0
- package/dist/primitives/use-voice-recorder.d.ts +1 -0
- package/dist/primitives/visualizer-sequences.d.ts +39 -0
- package/dist/react/index.d.ts +105 -0
- package/dist/react/use-voice-input.d.ts +15 -0
- package/dist/react.js +269 -240
- package/dist/register-impl-DQyejdfb.js +145 -0
- package/dist/test-utils/fake-clock.d.ts +45 -0
- package/dist/ui/resizable.d.ts +9 -3
- package/dist/variant-aurora-DVtGqVH6.js +218 -0
- package/dist/variant-custom-Dif-IV4u.js +1 -0
- package/dist/variant-wave-DbfNdvSi.js +116 -0
- package/frameworks/react/index.tsx +74 -1
- package/frameworks/react/use-voice-input.tsx +59 -0
- package/llms-full.txt +70 -4
- package/llms.txt +3 -3
- package/package.json +32 -7
- package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +257 -0
- package/src/components/audio-visualizer/aurora.glsl.ts +330 -0
- package/src/components/audio-visualizer/index.tsx +442 -0
- package/src/components/audio-visualizer/shader-canvas.tsx +846 -0
- package/src/components/audio-visualizer/sizes.ts +54 -0
- package/src/components/audio-visualizer/variant-aurora.tsx +299 -0
- package/src/components/audio-visualizer/variant-bar.tsx +143 -0
- package/src/components/audio-visualizer/variant-custom.tsx +338 -0
- package/src/components/audio-visualizer/variant-grid.tsx +170 -0
- package/src/components/audio-visualizer/variant-radial.tsx +158 -0
- package/src/components/audio-visualizer/variant-wave.tsx +125 -0
- package/src/components/audio-visualizer/wave.glsl.ts +130 -0
- package/src/components/component-meta.json +34 -32
- package/src/components/composer.tsx +5 -0
- package/src/components/thread.tsx +177 -0
- package/src/elements/audio-visualizer.tsx +183 -0
- package/src/elements/chat-workspace.tsx +1 -1
- package/src/elements/compiled.css +1 -1
- package/src/elements/define.tsx +12 -1
- package/src/elements/element-manifest.json +8 -0
- package/src/elements/element-meta.json +287 -0
- package/src/elements/element-types.d.ts +62 -0
- package/src/elements/icon-names.json +2 -0
- package/src/elements/menu.tsx +7 -4
- package/src/elements/register-impl.ts +3 -0
- package/src/elements/resizable.tsx +78 -2
- package/src/elements/slots.ts +42 -0
- package/src/elements/thread.tsx +105 -0
- package/src/primitives/audio-bands.ts +174 -0
- package/src/primitives/create-tween.ts +214 -0
- package/src/primitives/use-audio-analysis.ts +321 -0
- package/src/primitives/use-sequencer.ts +53 -0
- package/src/primitives/use-voice-recorder.ts +16 -4
- package/src/primitives/visualizer-sequences.ts +350 -0
- package/src/test-utils/fake-clock.ts +88 -0
- package/src/ui/action-icons.ts +2 -1
- package/src/ui/icon.tsx +4 -0
- package/src/ui/overlay.tsx +8 -1
- package/src/ui/resizable.tsx +47 -9
- package/dist/elements/chunks/action-icons-Dtl8Ox5D.js +0 -1
- package/dist/elements/chunks/arrow-left-GbBceGBE.js +0 -1
- package/dist/elements/chunks/chat-thread-Dz2qbzvI.js +0 -1
- package/dist/elements/chunks/check-fhmRksq_.js +0 -1
- package/dist/elements/chunks/chevron-down-DcRg1aRU.js +0 -1
- package/dist/elements/chunks/chevron-right-ChzBcNJ-.js +0 -1
- package/dist/elements/chunks/circle-B4fTe9v6.js +0 -1
- package/dist/elements/chunks/circle-check-CJV-3Rem.js +0 -1
- package/dist/elements/chunks/collapsible-CWUAShMC.js +0 -1
- package/dist/elements/chunks/define-CNSKFZCd.js +0 -1
- package/dist/elements/chunks/hover-card-BQGdGAQ3.js +0 -1
- package/dist/elements/chunks/icon-CAyRqVrg.js +0 -1
- package/dist/elements/chunks/message-square-MmRkvc-M.js +0 -1
- package/dist/elements/chunks/resizable-Bq_eHxUm.js +0 -1
- package/dist/elements/chunks/skeleton-Pjc5jiaY.js +0 -1
- package/dist/elements/chunks/textarea-CboigarR.js +0 -1
- package/dist/elements/chunks/tooltip-Cft_Rxiw.js +0 -1
- package/dist/elements/chunks/use-card-resolution-M36zVciB.js +0 -1
- package/dist/elements/chunks/x-Ckwicf6g.js +0 -1
- package/dist/register-impl-DcjZDELj.js +0 -141
package/LICENSE
CHANGED
|
@@ -19,3 +19,9 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This package includes third-party components under other licenses, including
|
|
26
|
+
files licensed under the Apache License 2.0. See the NOTICE file for the
|
|
27
|
+
per-file attribution and license terms that apply to them.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
@kitn.ai/ui
|
|
2
|
+
Copyright (c) kitn.ai
|
|
3
|
+
|
|
4
|
+
This product includes software developed at LiveKit
|
|
5
|
+
(https://github.com/livekit/components-js), licensed under the
|
|
6
|
+
Apache License, Version 2.0.
|
|
7
|
+
|
|
8
|
+
The following files are ports or verbatim copies of LiveKit work:
|
|
9
|
+
|
|
10
|
+
src/primitives/audio-bands.ts
|
|
11
|
+
from packages/react/src/hooks/useTrackVolume.ts
|
|
12
|
+
src/primitives/visualizer-sequences.ts
|
|
13
|
+
from packages/shadcn/hooks/agents-ui/use-agent-audio-visualizer-{bar,grid,radial,wave}.ts
|
|
14
|
+
src/components/audio-visualizer/sizes.ts
|
|
15
|
+
src/components/audio-visualizer/variant-bar.tsx
|
|
16
|
+
src/components/audio-visualizer/variant-grid.tsx
|
|
17
|
+
src/components/audio-visualizer/variant-radial.tsx
|
|
18
|
+
src/components/audio-visualizer/variant-wave.tsx
|
|
19
|
+
from packages/shadcn/components/agents-ui/agent-audio-visualizer-{bar,grid,radial,wave}.tsx
|
|
20
|
+
src/components/audio-visualizer/wave.glsl.ts
|
|
21
|
+
verbatim, from agent-audio-visualizer-wave.tsx
|
|
22
|
+
|
|
23
|
+
The following file contains no ported code, but takes animation values from
|
|
24
|
+
LiveKit work:
|
|
25
|
+
|
|
26
|
+
src/components/audio-visualizer/variant-aurora.tsx
|
|
27
|
+
from packages/shadcn/hooks/agents-ui/use-agent-audio-visualizer-aura.ts
|
|
28
|
+
(Apache-2.0). Its per-state animation targets -- speed, scale,
|
|
29
|
+
amplitude, frequency and brightness per agent state, the listening
|
|
30
|
+
spring (1.0s, bounce 0.35), and the tween and pulse cadences -- match
|
|
31
|
+
that hook, as do the analyser settings (fftSize 512,
|
|
32
|
+
smoothingTimeConstant 0.55). The implementation is our own; the
|
|
33
|
+
values are attributed here.
|
|
34
|
+
|
|
35
|
+
You may obtain a copy of the Apache License 2.0 at
|
|
36
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
37
|
+
|
|
38
|
+
The following file is ORIGINAL work and is NOT derived from LiveKit or any
|
|
39
|
+
third party:
|
|
40
|
+
|
|
41
|
+
src/components/audio-visualizer/aurora.glsl.ts
|
|
42
|
+
|
|
43
|
+
The variant-aurora.tsx attribution above does not extend to this file. That
|
|
44
|
+
attribution covers per-state animation values only; none of it reaches the
|
|
45
|
+
rendering. LiveKit's own aura shader is separately licensed, was never read,
|
|
46
|
+
and contributed nothing here.
|
|
47
|
+
|
|
48
|
+
This file's own provenance (whether it may itself later be attributed,
|
|
49
|
+
relicensed, or otherwise handled) is a separate, pending decision and is not
|
|
50
|
+
resolved by this NOTICE.
|
|
51
|
+
|
|
52
|
+
The full clean-room derivation record for that file, including its evidence
|
|
53
|
+
index, is at docs/provenance/aurora-clean-room.md in the source repository
|
|
54
|
+
(https://github.com/kitn-ai/ui).
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A real recorded voice, baked into a short seamless loop for the
|
|
3
|
+
* `speaking` tiles in the stories -- the genuine article, not a generator.
|
|
4
|
+
*
|
|
5
|
+
* Real voice, recorded by the project owner: "This is a test, testing one
|
|
6
|
+
* two three, hello world." Captured mono at 44.1kHz, then extracted through
|
|
7
|
+
* the component's ALIGNED analysis chain -- the same values upstream
|
|
8
|
+
* LiveKit's components run: AnalyserNode fftSize 2048 / smoothing 0.8,
|
|
9
|
+
* window bins 100-200 (the components' own loPass/hiPass; see DEFAULTS in
|
|
10
|
+
* primitives/use-audio-analysis.ts), `reduceToBands`' linear-proportional
|
|
11
|
+
* split as it exists in THIS tree, ~32ms cadence. Third bake of this
|
|
12
|
+
* fixture: the first was 5 linear bands over a wide raw-mic window
|
|
13
|
+
* (bins 4-120), the second 3 geometric bands over the same wide window;
|
|
14
|
+
* both predate the pipeline realignment that reverted window and split to
|
|
15
|
+
* upstream's.
|
|
16
|
+
*
|
|
17
|
+
* GAIN: the raw take is boosted 8x (+18dB) through a GainNode before the
|
|
18
|
+
* analysers -- an honest approximation of capture-side AGC, which every
|
|
19
|
+
* real input to the aligned window gets (LiveKit captures local mics with
|
|
20
|
+
* autoGainControl on; the mic stories request the same) but this recording,
|
|
21
|
+
* made raw, never had. Un-gained, quiet natural-volume speech sits at or
|
|
22
|
+
* under the -100dB floor across bins 100-200 and the loop's time-averaged
|
|
23
|
+
* band profile comes out flat-to-inverted (edges louder than centre);
|
|
24
|
+
* measured at +18dB the bake hits the live-AGC targets: speech-active
|
|
25
|
+
* volume through the aligned byte path (minDecibels -100 / maxDecibels
|
|
26
|
+
* -80) averages 0.73, no speech frame reads all-zero bands, and the
|
|
27
|
+
* time-averaged mirrored md profile is centre-strictly-max --
|
|
28
|
+
* [0.41, 0.42, 0.44, 0.42, 0.41]. The trade, recorded honestly: +18dB also
|
|
29
|
+
* lifts part of the room tone over the floor, so rest frames idle near
|
|
30
|
+
* [0.2, 0.04, 0] instead of the exact zeros an un-gained bake would show
|
|
31
|
+
* -- which is also what a real AGC mic does between words.
|
|
32
|
+
*
|
|
33
|
+
* 3 bands per frame is HALF width on purpose: `Math.ceil(count / 2)`, the
|
|
34
|
+
* exact band count the component itself requests at `md` before mirroring
|
|
35
|
+
* (see `bandCount()` in index.tsx). The `bands` prop is a raw passthrough
|
|
36
|
+
* by contract, so the stories mirror these half bands out to the full
|
|
37
|
+
* element count themselves through `mirrorBandsCenterOut` /
|
|
38
|
+
* `mirrorBandsAroundRing` -- the same primitives the live-audio path runs.
|
|
39
|
+
* See `useVoiceBands` in the stories file.
|
|
40
|
+
*
|
|
41
|
+
* Trimmed to the same 196-frame window (~6.4s) as the previous bake:
|
|
42
|
+
* about half a second of rest before the first word, just under a second
|
|
43
|
+
* after the last, and BOTH natural pauses in the middle -- one short gap
|
|
44
|
+
* around "test," / "testing", the longer one after "three," before
|
|
45
|
+
* "hello" -- since those are what prove the visualizer falls back to rest
|
|
46
|
+
* and recovers. Loops by wrapping frame 195 back to frame 0. Re-verified
|
|
47
|
+
* at the aligned values: the endpoints differ by 0.034 pre-quantization
|
|
48
|
+
* ([0.22,0.05,0] vs [0.2,0.03,0] after it), which sits between the median
|
|
49
|
+
* (0.025) and p90 (0.053) of ADJACENT quiet frames' natural motion at this
|
|
50
|
+
* gain -- the wrap step is indistinguishable from ordinary idle flicker.
|
|
51
|
+
* (The previous bake's endpoints quantized identical, but only because its
|
|
52
|
+
* un-gained rest frames were all zeros -- trivial, not preserved here.)
|
|
53
|
+
*
|
|
54
|
+
* Quantized to 2 decimals. Playback treats the cadence as a flat
|
|
55
|
+
* `VOICE_FRAME_MS`; the capture's real ticks land at 32-33ms (setTimeout
|
|
56
|
+
* jitter), a ~2% difference nobody can see. Body is ~3.8KB.
|
|
57
|
+
*/
|
|
58
|
+
export declare const VOICE_FRAME_MS = 32;
|
|
59
|
+
export declare const VOICE_BANDS: readonly (readonly number[])[];
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aurora ("veil") fragment shader.
|
|
3
|
+
*
|
|
4
|
+
* ORIGINAL WORK for @kitn.ai/ui. Written for Task 14 of
|
|
5
|
+
* docs/superpowers/plans/2026-08-07-audio-visualizers.md, from two inputs
|
|
6
|
+
* only:
|
|
7
|
+
* 1. .superpowers/sdd/2026-08-07-audio-visualizers/reference/aura-prototype/
|
|
8
|
+
* lk-aura-factsheet.md -- a two-team clean-room functional spec (facts
|
|
9
|
+
* and mathematics; no code and no expression, and no identifiers
|
|
10
|
+
* beyond the public prop names and the uniform inventory, both
|
|
11
|
+
* disclosed in that document's own preamble) covering LiveKit's
|
|
12
|
+
* public "aura" visualizer look. What was checked, and how, is in
|
|
13
|
+
* `docs/provenance/aurora-clean-room.md` section 4.
|
|
14
|
+
* 2. The adopted prototype's own `aura-proto.html` (mode 3, "veil"), which
|
|
15
|
+
* was itself implemented from that fact sheet by an author who never
|
|
16
|
+
* saw LiveKit's source.
|
|
17
|
+
*
|
|
18
|
+
* This file is NOT derived from, and was not written with reference to,
|
|
19
|
+
* LiveKit's `agent-audio-visualizer-aura.tsx` or the aura GLSL inlined in
|
|
20
|
+
* it. That component is Polyform Non-Resale 1.0.0, (c) UNCRN LLC -- a
|
|
21
|
+
* license incompatible with publishing this package -- and was never
|
|
22
|
+
* opened. The fact sheet's numbers and formulas (independently re-derived
|
|
23
|
+
* and checked by hand below, not copied) are what stand in for it.
|
|
24
|
+
*
|
|
25
|
+
* Its sibling `use-agent-audio-visualizer-aura.ts`, the DRIVING hook, is a
|
|
26
|
+
* separate matter: that file is Apache-2.0 (repo license, no Polyform
|
|
27
|
+
* header) and carries only the per-state behaviour, not the shader. It
|
|
28
|
+
* contributes nothing to this file; the state targets it describes live in
|
|
29
|
+
* `variant-aurora.tsx`.
|
|
30
|
+
*
|
|
31
|
+
* The construction: 36 phase-offset copies of one ring, each pushed through
|
|
32
|
+
* the same 4-octave directional-sine warp cascade, fused into soft veils by
|
|
33
|
+
* an analytic neighbour-distance blur -- see `auroraWarp` and the strand
|
|
34
|
+
* loop in `mainImage`.
|
|
35
|
+
*
|
|
36
|
+
* One deliberate deviation from the fact sheet: section 1 states the
|
|
37
|
+
* shipped output is "hotter than premultiplied" (written RGB may exceed
|
|
38
|
+
* written alpha), and section 4 confirms the light-mode branch never
|
|
39
|
+
* multiplies RGB by alpha or brightness at all. That is exactly the pattern
|
|
40
|
+
* that produces dark fringes on translucent edges once composited with a
|
|
41
|
+
* `premultipliedAlpha: true` canvas context over a light page background --
|
|
42
|
+
* see the doc on `ShaderCanvas`'s `fragment` prop. This port keeps every
|
|
43
|
+
* other number and formula but replaces the final write with true
|
|
44
|
+
* premultiplied output, `fragColor = vec4(rgb * alpha, alpha)`.
|
|
45
|
+
*
|
|
46
|
+
* PARITY RE-TUNE (campaign task #6). A pixel-level behavioral audit
|
|
47
|
+
* (examples/internal/livekit-parity scripts/aurora-audit.mjs) measured the
|
|
48
|
+
* rendered output against the reference and this file was re-tuned to the
|
|
49
|
+
* MEASURED targets -- numbers from screenshots and Rob's reference
|
|
50
|
+
* recording, never from restricted source. Four departures from the
|
|
51
|
+
* original fact-sheet pipeline, all clean-room rendering choices:
|
|
52
|
+
* 1. Wind direction + solid-body rotation: the octave phase runs on
|
|
53
|
+
* NEGATIVE tau (the audit measured our lobes drifting counter-
|
|
54
|
+
* clockwise where the reference turns clockwise, ~+13..+20 deg/s when
|
|
55
|
+
* speaking), plus a per-state `uRotation` trim of the coordinate
|
|
56
|
+
* frame so each state's measured rotation lands on its reference
|
|
57
|
+
* value (see `auroraTargets` in variant-aurora.tsx).
|
|
58
|
+
* 2. Dark tonemap is chroma-preserving with a steeper knee (11x/(1+11x)
|
|
59
|
+
* on the max channel, rescaled) plus an HSV shape-up: hue +0.013
|
|
60
|
+
* turns, saturation scaled by (1.02 + 0.35 * value) capped at 0.98.
|
|
61
|
+
* Rendered targets: hue ~197.5deg, saturation ~0.95 rising with
|
|
62
|
+
* brightness, never white (the audit measured the original
|
|
63
|
+
* per-channel 4x/(1+4x) map desaturating to ~0.72 and dragging hue
|
|
64
|
+
* toward green).
|
|
65
|
+
* 3. Brightness is applied through strongly compressive curves
|
|
66
|
+
* ((b/1.5)^0.07 on colour, 0.97*(b/1.5)^0.09 on alpha, with a
|
|
67
|
+
* smoothstep alpha knee): the thinking/connecting pulse DRIVES
|
|
68
|
+
* 0.5..2.5 but must RENDER inside roughly 0.5..0.65 mean brightness
|
|
69
|
+
* (the reference holds 0.53..0.65 where the linear map swung
|
|
70
|
+
* 0.16..0.83 and blinked the ring out entirely at pulse minima).
|
|
71
|
+
* 4. Edge falloff tightened (exp(2.3*spacing), was exp(2*spacing)) and
|
|
72
|
+
* made asymmetric: the OUTER side of the ring renders at 0.66x the
|
|
73
|
+
* inner width -- the reference's outer edge is consistently sharper
|
|
74
|
+
* than its inner one at every volume.
|
|
75
|
+
*
|
|
76
|
+
* Uniforms are declared by ShaderCanvas. Do not declare them here.
|
|
77
|
+
*
|
|
78
|
+
* Uniform contract (see the module doc in the Task 14 report for the full
|
|
79
|
+
* per-state table):
|
|
80
|
+
* uColor vec3 -- accent RGB, 0..1.
|
|
81
|
+
* uIntensity float -- brightness/gain, fact sheet section 5's
|
|
82
|
+
* "brightness" column directly (roughly 0.5..2.5,
|
|
83
|
+
* NOT the 0..1 convention other variants use).
|
|
84
|
+
* uSpeed float -- octave phase rate in rad/s, fact sheet section
|
|
85
|
+
* 5's `S / 20` directly (roughly 0.5..3.5).
|
|
86
|
+
* uComplexity float -- warp spatial-frequency parameter, fact sheet
|
|
87
|
+
* section 5's `freqParam` directly (roughly
|
|
88
|
+
* 0.4..1.25, default 0.5); mapped internally to
|
|
89
|
+
* F = 2 + 13 * uComplexity.
|
|
90
|
+
* uAmplitude float -- warp displacement amplitude, fact sheet section
|
|
91
|
+
* 5's `amplitude` column directly (roughly
|
|
92
|
+
* 0.5..1.2, default 1.0).
|
|
93
|
+
* uScale float -- ring radius, fraction of half-canvas, fact sheet
|
|
94
|
+
* section 5's `scale` column directly (0.2..0.3).
|
|
95
|
+
* The caller is responsible for folding in the
|
|
96
|
+
* instant voice-driven growth
|
|
97
|
+
* (`0.2 + 0.2 * volume`, speaking state only,
|
|
98
|
+
* fact sheet section 5) before setting this --
|
|
99
|
+
* the shader does not read a separate volume
|
|
100
|
+
* uniform to avoid double-applying it.
|
|
101
|
+
* uTheme float -- < 0.5 selects the dark colour pipeline, >= 0.5
|
|
102
|
+
* the light one (fact sheet section 4). This is a
|
|
103
|
+
* genuine branch in the math, not just a
|
|
104
|
+
* compositing background -- set it from the kit's
|
|
105
|
+
* active theme, not a fixed constant.
|
|
106
|
+
* uRotation float -- solid-body rotation rate of the whole figure,
|
|
107
|
+
* rad/s, positive = CLOCKWISE on screen. A
|
|
108
|
+
* parity-calibrated per-state trim on top of the
|
|
109
|
+
* wind's own drift (see the module doc above and
|
|
110
|
+
* `auroraTargets`); the caller pins it to 0 under
|
|
111
|
+
* reduced motion, like uSpeed.
|
|
112
|
+
*/
|
|
113
|
+
declare const _default: "\nconst float PI = 3.14159265359;\n// Strand phase span: 0.5 + pi (fact sheet section 2's \"sigma\", the\n// midpoint of the interval [1, 2*pi] with the interpolation knob fixed\n// at 0.5).\nconst float STRAND_SIGMA = 0.5 + PI;\nconst int STRAND_COUNT = 36;\n\n// 4-octave directional-sine warp cascade. M0 and the per-octave rotation B\n// (cos 0.6, sin 0.8, an exact ~53.13 degree rotation) are fact sheet\n// section 2's stated matrices; M1/M2/M3 = B composed with the previous\n// octave's matrix, and each v_k is the first row of M_k. Hand-derived and\n// checked against the fact sheet's own listed v_0..v_3 below, not copied\n// from any source.\nconst mat2 WARP_M0 = mat2(0.6, 0.25, -0.25, 0.9);\nconst mat2 WARP_M1 = mat2(0.16, 0.63, -0.87, 0.34);\nconst mat2 WARP_M2 = mat2(-0.408, 0.506, -0.794, -0.492);\nconst mat2 WARP_M3 = mat2(-0.6496, -0.0228, -0.0828, -0.9304);\nconst vec2 WARP_V0 = vec2(0.600, -0.250);\nconst vec2 WARP_V1 = vec2(0.160, -0.870);\nconst vec2 WARP_V2 = vec2(-0.408, -0.794);\nconst vec2 WARP_V3 = vec2(-0.650, -0.083);\n\n// Displaces p through all 4 octaves. Octave k's phase carries k*tau, so\n// octave 0 is static in time and octave 3 moves fastest -- fine\n// corrugation slides across coarse folds, which reads as flowing wind\n// (fact sheet section 2, \"Mechanism of the fold / wind\").\nvec2 auroraWarp(vec2 p, float phi, float tau, float freq, float amp) {\n vec2 x = p;\n float f = freq;\n float a = amp;\n\n vec2 q = WARP_M0 * x;\n vec2 w = sin(f * q + vec2(phi));\n x += (a / f) * WARP_V0 * w;\n f *= 1.4;\n a *= 1.0 + 0.1 * (max(w.x, w.y) - 1.0);\n\n q = WARP_M1 * x;\n w = sin(f * q + vec2(tau + phi));\n x += (a / f) * WARP_V1 * w;\n f *= 1.4;\n a *= 1.0 + 0.1 * (max(w.x, w.y) - 1.0);\n\n q = WARP_M2 * x;\n w = sin(f * q + vec2(2.0 * tau + phi));\n x += (a / f) * WARP_V2 * w;\n f *= 1.4;\n a *= 1.0 + 0.1 * (max(w.x, w.y) - 1.0);\n\n q = WARP_M3 * x;\n w = sin(f * q + vec2(3.0 * tau + phi));\n x += (a / f) * WARP_V3 * w;\n\n return x;\n}\n\nvec3 auroraRgb2Hsv(vec3 c) {\n vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n float d = q.x - min(q.w, q.y);\n return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + 1.0e-10)), d / (q.x + 1.0e-10), q.x);\n}\n\nvec3 auroraHsv2Rgb(vec3 c) {\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\nvoid mainImage(out vec4 fragColor, in vec2 fragCoord) {\n // Fact sheet section 2: u in [0,1]^2, centered point p = u - 0.5, no\n // aspect correction -- the element is forced square by CSS, so this is\n // only correct on a square canvas (true for every audio-visualizer size).\n vec2 uv = fragCoord / iResolution.xy;\n vec2 p = uv - 0.5;\n\n // Solid-body rotation of the sampling frame (module doc, departure 1).\n // gl_FragCoord's y-up frame is mirrored against the screen's y-down one,\n // so the sign here is what makes positive uRotation read CLOCKWISE on\n // screen -- verified empirically against the audit's estimator (which\n // measures +10 for uRotation = +10 deg/s with the wind frozen).\n float rotA = -uRotation * iTime;\n float rc = cos(rotA);\n float rs = sin(rotA);\n p = mat2(rc, -rs, rs, rc) * p;\n\n // Negative tau: time-reversing the octave phases flips the wind's\n // emergent angular drift from counter-clockwise (measured on the original)\n // to the reference's clockwise, without touching any static property --\n // lobes, radii, spectra are distribution-identical (module doc,\n // departure 1).\n float tau = -iTime * uSpeed;\n float freq = max(2.0 + 13.0 * uComplexity, 1.0);\n float amp = uAmplitude;\n float ringRadius = uScale;\n\n vec3 hsv = auroraRgb2Hsv(uColor);\n // Chain-seed strand, phase -1/36 NOT scaled by STRAND_SIGMA (fact sheet\n // section 2), so inter-strand spacing is defined for strand 1 too.\n vec2 prevStrand = auroraWarp(p, -1.0 / float(STRAND_COUNT), tau, freq, amp);\n\n vec3 accum = vec3(0.0);\n for (int j = 1; j <= STRAND_COUNT; j++) {\n float frac = float(j) / float(STRAND_COUNT);\n float phi = frac * STRAND_SIGMA;\n vec2 strand = auroraWarp(p, phi, tau, freq, amp);\n\n // Signed distance to the (warped) ring, per strand: negative inside,\n // positive outside -- the sign feeds the asymmetric edge width below.\n float sd = length(strand) - ringRadius;\n float dist = abs(sd);\n // Inter-strand spacing through the chain: an analytic blur exactly\n // proportional to how far this strand's warp has drifted from its\n // neighbour's. Fanned regions (large spacing) fuse into soft veils;\n // bunched regions (small spacing) stay crisp -- this is what turns 36\n // discrete rings into an aurora (fact sheet section 2). The 2.3 growth\n // rate (fact sheet's own is 2.0) and the 0.66x outer-side factor are\n // the parity edge calibration: the reference holds a 10-90% rise of\n // ~6-10px(2x) with the OUTER edge always the sharper of the two\n // (module doc, departure 4).\n float spacing = length(strand - prevStrand);\n float edgeWidth = 0.01 + max(exp(2.3 * spacing) - 1.0, 0.001);\n edgeWidth *= mix(1.0, 0.66, step(0.0, sd));\n float coverage = 1.0 - smoothstep(0.0, 1.0, dist / edgeWidth);\n\n // Fixed hue drift across the strand fan, matching the fact sheet's\n // documented colorShift default (0.05 turns at weight 0.3, i.e. 0.015\n // turns end to end); earliest strand shifts most.\n vec3 strandColor = auroraHsv2Rgb(vec3(fract(hsv.x + (1.0 - frac) * 0.015), hsv.y, hsv.z));\n accum += coverage * strandColor;\n prevStrand = strand;\n }\n\n // Mean soft coverage across all 36 strands (fact sheet section 2's image\n // accumulator I). The inverse-distance glow accumulator G is omitted: the\n // fact sheet states its gain ships as 0, so it contributes nothing.\n vec3 image = accum / float(STRAND_COUNT);\n\n // Deterministic per-pixel dither, +/- 1/510, identical on all channels\n // (fact sheet section 3). Kept as a plain vec3 offset, not pre-mixed into\n // the image accumulator, so each branch can add it at the point the fact\n // sheet actually specifies (see the dark branch below).\n float n = fract(sin(dot(fragCoord, vec2(12.9898, 78.233))) * 43758.5453);\n vec3 dither = vec3((n - 0.5) / 255.0);\n\n float brightness = uIntensity;\n vec3 rgb;\n float alpha;\n\n if (uTheme < 0.5) {\n // Dark pipeline, parity-calibrated (module doc, departures 2 + 3).\n // RGB1 = 1.2 * I, THEN add dither (fact sheet order: pre-gain first so\n // the dither's written amplitude stays +/- 1/510), then floor at 0 --\n // a negative dither on a near-black pixel would otherwise be blown up\n // by the chroma-preserving rescale's division right below.\n vec3 x1 = max(1.2 * image + dither, 0.0);\n\n // Chroma-preserving tonemap: knee the MAX channel (11x/(1+11x) -- a\n // steeper knee than the fact sheet's per-channel 4x/(1+4x)) and rescale\n // the vector, so ribbon cores land on a stable brightness level largely\n // independent of how thin the veils have fanned (the audit measured the\n // per-channel map crushing spread-out states to ~half the reference's\n // rendered brightness) while hue and saturation pass through untouched\n // for the HSV shape-up to own.\n float mxc = max(x1.r, max(x1.g, x1.b));\n float tmMax = 11.0 * mxc / (1.0 + 11.0 * mxc);\n vec3 tm = x1 * (tmMax / max(mxc, 1.0e-5));\n\n // Rendered-colour calibration: hue +0.013 turns (audit target\n // ~197.5deg for the default accent), saturation proportional to the\n // caller's own (a desaturated accent stays desaturated) but rising\n // with the pixel's value and capped at 0.98 -- Rob's reference:\n // saturation RISES with brightness, and nothing is ever white.\n vec3 hv = auroraRgb2Hsv(tm);\n hv.x = fract(hv.x + 0.013);\n hv.y = min(hv.y * (1.02 + 0.35 * hv.z), 0.98);\n tm = auroraHsv2Rgb(hv);\n\n // Brightness compression (departure 3): uIntensity DRIVES 0.5..2.5 at\n // the thinking/connecting pulse but must RENDER inside roughly\n // 0.5..0.65 mean brightness, never dropping the ring below visibility.\n // Two compressive powers (colour flatter than alpha) plus a smoothstep\n // alpha knee that lifts ribbon cores to full opacity while cutting the\n // dim tails -- which is also what keeps the centre transparent and the\n // edges crisp. max(brightness, 0.05) only guards pow() against a\n // (contractually impossible) zero or negative uniform.\n float bVal = pow(max(brightness, 0.05) / 1.5, 0.07);\n float bAlpha = 0.97 * pow(max(brightness, 0.05) / 1.5, 0.09);\n float luma = dot(tm, vec3(0.299, 0.587, 0.114));\n // Clamped to [0,1] before the premultiply below, mirroring the light\n // branch, so rgb * alpha <= alpha stays enforced for every input.\n rgb = clamp(tm * bVal, 0.0, 1.0);\n alpha = clamp(smoothstep(0.0, 0.50, luma) * bAlpha, 0.0, 1.0);\n } else {\n // Light pipeline (fact sheet section 4): RGB1 = I, then add dither;\n // brightness curve on vector length, hue direction kept, saturation\n // extrapolated 3x about gray.\n vec3 x1 = image + dither;\n float mag = length(x1);\n vec3 dir = x1 / max(mag, 1.0e-5);\n float mag2 = 2.0 * mag / (1.0 + 2.0 * mag);\n vec3 x2 = dir * mag2;\n float gray = dot(x2, vec3(0.2, 0.5, 0.1));\n rgb = clamp(vec3(gray) + 3.0 * (x2 - vec3(gray)), 0.0, 1.0);\n alpha = clamp(mag2 * clamp(brightness, 1.0, 2.0), 0.0, 1.0);\n }\n\n // PREMULTIPLIED output. Both branches above clamp their natural rgb to\n // [0,1] before this line, which is what makes rgb * alpha <= alpha\n // (componentwise) a property the code enforces unconditionally, not one\n // that happens to hold for the inputs this was tested against. See the\n // module doc above for why this departs from the fact sheet's own\n // (deliberately non-premultiplied) pipeline.\n fragColor = vec4(rgb * alpha, alpha);\n}\n";
|
|
114
|
+
export default _default;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { Accessor, JSX } from 'solid-js';
|
|
2
|
+
import { VariantProps } from './variant-bar';
|
|
3
|
+
import { VisualizerSize } from './sizes';
|
|
4
|
+
import { UniformType } from './shader-canvas';
|
|
5
|
+
export type VisualizerVariant = 'bar' | 'grid' | 'radial' | 'wave' | 'aurora' | 'custom';
|
|
6
|
+
export declare function normalizeVariant(input: string | undefined): VisualizerVariant;
|
|
7
|
+
/** A consumer-supplied fragment shader, for `variant="custom"`. */
|
|
8
|
+
export interface ShaderSpec {
|
|
9
|
+
/**
|
|
10
|
+
* GLSL source defining `mainImage(out vec4 fragColor, in vec2 fragCoord)`.
|
|
11
|
+
*
|
|
12
|
+
* MUST output premultiplied colour: `fragColor = vec4(rgb * alpha, alpha);`,
|
|
13
|
+
* never `vec4(rgb, alpha)`. The canvas composites using the browser's
|
|
14
|
+
* default `premultipliedAlpha: true`; a translucent edge written the
|
|
15
|
+
* natural (straight-alpha) way gets a dark fringe where it meets a light
|
|
16
|
+
* page background.
|
|
17
|
+
*/
|
|
18
|
+
fragment: string;
|
|
19
|
+
/**
|
|
20
|
+
* Custom uniforms. The canvas DECLARES these for you; declaring them in the
|
|
21
|
+
* shader too is a compile error.
|
|
22
|
+
*
|
|
23
|
+
* `type` is narrowed to `UniformType`, not a bare string: an unrecognized
|
|
24
|
+
* type here is caught by TypeScript for a TS-authored `shader` prop, before
|
|
25
|
+
* it can produce `uniform undefined <name>;` in the assembled source (a
|
|
26
|
+
* confusing compile error). A consumer reaching this through the
|
|
27
|
+
* `<kai-audio-visualizer>` custom element or an attribute-driven wrapper
|
|
28
|
+
* bypasses this check entirely (it is JS at that boundary, not TS) --
|
|
29
|
+
* `variant-custom.tsx`'s `customUniforms` re-checks `type` at runtime for
|
|
30
|
+
* exactly that reason.
|
|
31
|
+
*/
|
|
32
|
+
uniforms?: Record<string, {
|
|
33
|
+
type: UniformType;
|
|
34
|
+
value: number | number[];
|
|
35
|
+
}>;
|
|
36
|
+
}
|
|
37
|
+
export interface AudioVisualizerProps {
|
|
38
|
+
variant?: VisualizerVariant;
|
|
39
|
+
state?: string;
|
|
40
|
+
size?: VisualizerSize;
|
|
41
|
+
barCount?: number;
|
|
42
|
+
/** Grid only: rows and columns of the square grid. */
|
|
43
|
+
count?: number;
|
|
44
|
+
/** Radial only: distance from center, in px. */
|
|
45
|
+
radius?: number;
|
|
46
|
+
/** Grid only: ring distance for the connecting animation, in cells. */
|
|
47
|
+
spread?: number;
|
|
48
|
+
/** Grid only: ms between scripted frames. */
|
|
49
|
+
interval?: number;
|
|
50
|
+
color?: string;
|
|
51
|
+
/** Shader variants only: pattern density, 0..1. */
|
|
52
|
+
complexity?: number;
|
|
53
|
+
/** Setting this makes the element an announced image instead of decorative. */
|
|
54
|
+
label?: string;
|
|
55
|
+
stream?: MediaStream;
|
|
56
|
+
audioElement?: HTMLMediaElement;
|
|
57
|
+
/** Pre-computed levels. Set this and no AudioContext is ever constructed. */
|
|
58
|
+
bands?: number[];
|
|
59
|
+
/**
|
|
60
|
+
* Custom fragment shader for `variant="custom"`. See `ShaderSpec` for the
|
|
61
|
+
* full contract -- most importantly, `fragment` MUST output premultiplied
|
|
62
|
+
* colour (`vec4(rgb * alpha, alpha)`, not `vec4(rgb, alpha)`), or
|
|
63
|
+
* translucent edges get dark fringes on light backgrounds.
|
|
64
|
+
*/
|
|
65
|
+
shader?: ShaderSpec;
|
|
66
|
+
/**
|
|
67
|
+
* Shader variants only (`wave`/`aurora`/`custom`): keep animating while the
|
|
68
|
+
* element is scrolled off screen. Default `false`, which stops the draw loop
|
|
69
|
+
* and releases the WebGL context until it comes back -- see `ShaderCanvas`'s
|
|
70
|
+
* `animateWhenNotVisible` for the context-budget reasoning and the cost of
|
|
71
|
+
* opting out. Does not override `prefers-reduced-motion`.
|
|
72
|
+
*/
|
|
73
|
+
animateWhenNotVisible?: boolean;
|
|
74
|
+
class?: string;
|
|
75
|
+
/**
|
|
76
|
+
* Render each DOM variant's items yourself -- the same render-prop the
|
|
77
|
+
* underlying bar/grid/radial component takes, imported rather than
|
|
78
|
+
* redeclared so the two cannot drift. Not used by the shader variants
|
|
79
|
+
* (`wave`/`aurora`/`custom`): a fragment shader has no per-item DOM for it
|
|
80
|
+
* to replace.
|
|
81
|
+
*/
|
|
82
|
+
children?: VariantProps['children'];
|
|
83
|
+
/**
|
|
84
|
+
* Explicit `'light'` or `'dark'` wins; `'auto'` (the default) follows a
|
|
85
|
+
* live `prefers-color-scheme` listener -- the same rule
|
|
86
|
+
* `elements/define.tsx`'s `createDarkMode` applies for every `kai-*`
|
|
87
|
+
* element. Only the shader variants read this (aurora/wave pick a colour
|
|
88
|
+
* pipeline with it): the three DOM variants already get dark-mode styling
|
|
89
|
+
* for free via CSS custom properties, which a shader baking colour into a
|
|
90
|
+
* GLSL uniform cannot do. `<kai-audio-visualizer>` forwards its own
|
|
91
|
+
* already-resolved `theme` attribute through this prop; a bare
|
|
92
|
+
* `<AudioVisualizer>` with no wrapping element needs it set directly.
|
|
93
|
+
*/
|
|
94
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
95
|
+
}
|
|
96
|
+
/** Tracks `prefers-reduced-motion`, live. */
|
|
97
|
+
export declare function usePrefersReducedMotion(): Accessor<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Resolves `theme` (`'light' | 'dark' | 'auto'`) to a boolean, mirroring
|
|
100
|
+
* `elements/define.tsx`'s `createDarkMode` rule exactly: an explicit value
|
|
101
|
+
* wins, `'auto'` (the default) follows a live `prefers-color-scheme`
|
|
102
|
+
* listener.
|
|
103
|
+
*
|
|
104
|
+
* This is a SEPARATE implementation of that rule, not an import of it:
|
|
105
|
+
* `components/` is the framework-agnostic layer `elements/` wraps (see the
|
|
106
|
+
* kit's architecture), so it cannot depend on `elements/define.tsx` without
|
|
107
|
+
* inverting that direction. When driven through `<kai-audio-visualizer>`,
|
|
108
|
+
* the facade has already resolved `'auto'` against its OWN listener (the one
|
|
109
|
+
* already wired to the visible `.dark` class) before handing this an
|
|
110
|
+
* explicit `'light'`/`'dark'` -- so this hook's own listener only ever
|
|
111
|
+
* actually decides anything for a bare `<AudioVisualizer>` used directly,
|
|
112
|
+
* with no wrapping element at all.
|
|
113
|
+
*/
|
|
114
|
+
export declare function useResolvedDark(theme: () => string | undefined): Accessor<boolean>;
|
|
115
|
+
/**
|
|
116
|
+
* Props a shader variant (`wave`, `aurora`, `custom`) receives from the
|
|
117
|
+
* dispatcher: everything a DOM variant gets, minus `children` (a fragment
|
|
118
|
+
* shader has no per-item DOM for a render-prop to replace, so the field is
|
|
119
|
+
* dropped rather than inherited-but-unused), plus the scalar `volume` reading
|
|
120
|
+
* (shaders read one number, not per-band levels) and the shader-specific
|
|
121
|
+
* knobs. This is the compile-time contract Tasks 12 to 15 build against --
|
|
122
|
+
* keep it honest rather than casting around a looser type at the render site.
|
|
123
|
+
*/
|
|
124
|
+
export interface ShaderVariantProps extends Omit<VariantProps, 'children'> {
|
|
125
|
+
volume: number;
|
|
126
|
+
/** Pattern density, 0..1. */
|
|
127
|
+
complexity?: number;
|
|
128
|
+
/** Only meaningful for `variant="custom"`. */
|
|
129
|
+
shader?: ShaderSpec;
|
|
130
|
+
/**
|
|
131
|
+
* Already-resolved: `true` selects the dark colour pipeline, `false`
|
|
132
|
+
* selects light -- matching `elements/define.tsx`'s `createDarkMode`
|
|
133
|
+
* output exactly (`classList={{ dark: isDark() }}`). This is that SAME
|
|
134
|
+
* resolved value forwarded down, not a re-derivation, so a shader baking
|
|
135
|
+
* colour into a GLSL uniform never needs its own `prefers-color-scheme`
|
|
136
|
+
* listener for the common case. Optional: a shader mounted standalone (no
|
|
137
|
+
* dispatcher resolving `theme` above it) may fall back to reading the
|
|
138
|
+
* media query itself.
|
|
139
|
+
*/
|
|
140
|
+
dark?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Keep animating while off screen instead of releasing the WebGL context.
|
|
143
|
+
* Relayed straight through to `ShaderCanvas`, which owns the behaviour and
|
|
144
|
+
* documents the trade-off; the variants are pure conduits for it.
|
|
145
|
+
*/
|
|
146
|
+
animateWhenNotVisible?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Call this if the shader cannot render at all -- most commonly
|
|
149
|
+
* `canvas.getContext('webgl')` returning null. Permanent for this mount:
|
|
150
|
+
* the dispatcher swaps to the bar fallback and will not retry the shader
|
|
151
|
+
* on any later reactive update, only on switching to a different variant.
|
|
152
|
+
*/
|
|
153
|
+
onUnavailable: () => void;
|
|
154
|
+
}
|
|
155
|
+
export declare function AudioVisualizer(props: AudioVisualizerProps): JSX.Element;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { JSX } from 'solid-js';
|
|
2
|
+
export type UniformType = '1f' | '1i' | '1fv' | '2f' | '3f' | '3fv' | '4f' | '4fv' | 'Matrix2fv' | 'Matrix3fv' | 'Matrix4fv';
|
|
3
|
+
export interface UniformSpec {
|
|
4
|
+
type: UniformType;
|
|
5
|
+
value: number | number[];
|
|
6
|
+
/**
|
|
7
|
+
* For array uniforms (`1fv`, `3fv`, `4fv`, or a matrix type repeated), the
|
|
8
|
+
* declared length. Optional: when omitted, it is inferred from
|
|
9
|
+
* `value.length` -- see `inferArraySize`. The inferred length is exactly
|
|
10
|
+
* what the recompile check uses too (see `effectiveArraySize`), so a
|
|
11
|
+
* length change recompiles the shader whether `arraySize` was passed
|
|
12
|
+
* explicitly or left to be inferred -- e.g. a per-band uniform whose
|
|
13
|
+
* length tracks a reactive `size`/`barCount` prop stays correct across a
|
|
14
|
+
* band-count change with no extra care from the caller.
|
|
15
|
+
*/
|
|
16
|
+
arraySize?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface ShaderCanvasProps {
|
|
19
|
+
/**
|
|
20
|
+
* GLSL defining `mainImage(out vec4 fragColor, in vec2 fragCoord)`.
|
|
21
|
+
*
|
|
22
|
+
* MUST output PREMULTIPLIED colour: `fragColor = vec4(rgb * alpha, alpha);`,
|
|
23
|
+
* not `vec4(rgb, alpha)`. The canvas context uses the browser default
|
|
24
|
+
* `premultipliedAlpha: true` (see `ShaderCanvas`'s doc for why), so a
|
|
25
|
+
* naturally written soft/anti-aliased edge that returns straight (not
|
|
26
|
+
* premultiplied) colour composites with a dark fringe or halo -- most
|
|
27
|
+
* visible on a light page background.
|
|
28
|
+
*/
|
|
29
|
+
fragment: string;
|
|
30
|
+
/**
|
|
31
|
+
* Custom uniforms. THIS CANVAS DECLARES THEM FOR YOU by injecting
|
|
32
|
+
* `uniform <type> <name>;` into the shader source. Declaring them yourself
|
|
33
|
+
* in `fragment` too is a GLSL redefinition and fails to compile.
|
|
34
|
+
*/
|
|
35
|
+
uniforms?: Record<string, UniformSpec>;
|
|
36
|
+
precision?: 'lowp' | 'mediump' | 'highp';
|
|
37
|
+
/**
|
|
38
|
+
* Called when the shader cannot render at all: no WebGL context, or a
|
|
39
|
+
* compile/link failure. Not called again for a later value-only uniform
|
|
40
|
+
* update (see the reactivity note on `ShaderCanvas` below) -- only when
|
|
41
|
+
* the shader itself is rebuilt and that rebuild fails.
|
|
42
|
+
*/
|
|
43
|
+
onError?: (message: string) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Keep animating while the canvas is off screen. Default `false`.
|
|
46
|
+
*
|
|
47
|
+
* By default an off-screen canvas stops drawing AND hands its WebGL context
|
|
48
|
+
* back to the browser (see `ShaderCanvas`'s doc), because contexts are
|
|
49
|
+
* rationed at roughly 16 per renderer process and a page of visualizers
|
|
50
|
+
* blows through that. Set this when a canvas must keep running unseen --
|
|
51
|
+
* capturing frames, or a shader whose state must not visibly jump when it
|
|
52
|
+
* scrolls back in. The cost is one permanently-held context per canvas, so
|
|
53
|
+
* it does not scale: a page that sets this on more than a handful of
|
|
54
|
+
* visualizers is back to the eviction problem the default exists to avoid.
|
|
55
|
+
*
|
|
56
|
+
* Named to match upstream's prop of the same name, though ours opts out of
|
|
57
|
+
* something stronger: theirs only pauses the draw loop, ours also releases
|
|
58
|
+
* the context.
|
|
59
|
+
*
|
|
60
|
+
* This does NOT override `prefers-reduced-motion`. Reduced motion is applied
|
|
61
|
+
* a layer up, by the variants zeroing their own speed uniforms, so a frozen
|
|
62
|
+
* shader stays a still image whether or not this is set -- this only decides
|
|
63
|
+
* whether frames keep being drawn, never what they contain.
|
|
64
|
+
*/
|
|
65
|
+
animateWhenNotVisible?: boolean;
|
|
66
|
+
class?: string;
|
|
67
|
+
}
|
|
68
|
+
/** Default accent for the shader variants, matching upstream's. */
|
|
69
|
+
export declare const DEFAULT_SHADER_COLOR = "#1FD5F9";
|
|
70
|
+
/**
|
|
71
|
+
* `#rrggbb` to three 0..1 floats, for a `3fv` uniform. Falls back to the
|
|
72
|
+
* default on malformed input (short forms like `#fff`, bare colour names,
|
|
73
|
+
* empty strings) rather than throwing into a render loop.
|
|
74
|
+
*
|
|
75
|
+
* Lives here, not duplicated, because wave, aurora, and custom variants all
|
|
76
|
+
* need it.
|
|
77
|
+
*/
|
|
78
|
+
export declare function hexToRgb(hex: string): [number, number, number];
|
|
79
|
+
/**
|
|
80
|
+
* Assemble the full fragment shader: precision, built-ins, auto-declared
|
|
81
|
+
* custom uniforms, the caller's body, and a `main()` that forwards to
|
|
82
|
+
* `mainImage`.
|
|
83
|
+
*
|
|
84
|
+
* Declares every entry in `uniforms` for the caller (`uniform <type>
|
|
85
|
+
* <name>;`, injected right after the precision qualifier). Declaring the
|
|
86
|
+
* SAME uniform name inside `fragment` too is a GLSL redefinition and fails
|
|
87
|
+
* to compile -- that is the contract this function exists to protect.
|
|
88
|
+
*
|
|
89
|
+
* Split out from the component because it is the only part testable without
|
|
90
|
+
* a GPU (jsdom has no WebGL at all), and it is where both a compile-breaking
|
|
91
|
+
* duplicate declaration and an array-size mismatch would show up.
|
|
92
|
+
*/
|
|
93
|
+
export declare function buildFragmentSource(fragment: string, uniforms: Record<string, UniformSpec>, precision: string): string;
|
|
94
|
+
/**
|
|
95
|
+
* A full-screen fragment shader on a canvas, ShaderToy-compatible.
|
|
96
|
+
*
|
|
97
|
+
* Deliberately does NOT support textures, `iChannel` inputs, video,
|
|
98
|
+
* multipass, or device orientation. Those are what make upstream's runner
|
|
99
|
+
* 988 lines, and an audio visualizer needs none of them.
|
|
100
|
+
*
|
|
101
|
+
* Renders with a fully transparent clear colour (`gl.clearColor(0,0,0,0)`).
|
|
102
|
+
* Compositing over the page comes from the canvas context's default
|
|
103
|
+
* `premultipliedAlpha: true`, NOT from `gl.blendFunc` (see the comment at
|
|
104
|
+
* the blend call site for why that call has no effect on today's
|
|
105
|
+
* single-pass output). Because of that default, `fragment` MUST output
|
|
106
|
+
* premultiplied colour -- see the doc on the `fragment` prop for the exact
|
|
107
|
+
* contract.
|
|
108
|
+
*
|
|
109
|
+
* Reactivity note: only `fragment`, `precision`, and the STRUCTURE of
|
|
110
|
+
* `uniforms` (which names exist, with which types/sizes) trigger a rebuild
|
|
111
|
+
* of the GL program. A uniform's VALUE is read fresh every animation frame
|
|
112
|
+
* without forcing a rebuild -- see `uniformShapeKey` above. This matters
|
|
113
|
+
* because a caller typically re-creates the `uniforms` object every render
|
|
114
|
+
* (e.g. `{ uVolume: { type: '1f', value: volume() } }`); tracking that
|
|
115
|
+
* object directly would tear down and recompile the whole program on every
|
|
116
|
+
* frame the volume changes. `fragment` and `precision` are ALSO read through
|
|
117
|
+
* their own memos, not directly, for the same reason: a variant's `precision`
|
|
118
|
+
* is commonly computed from a prop (`size`) that arrives via a dispatcher
|
|
119
|
+
* spread bundling unrelated fast-changing signals (`bands`), so an unmemoized
|
|
120
|
+
* read can mark this effect stale on every one of those, even though
|
|
121
|
+
* `precision`'s resolved value never changes. See the comment above
|
|
122
|
+
* `fragmentMemo`/`precisionMemo` below for the measured failure this caused.
|
|
123
|
+
*
|
|
124
|
+
* Visibility: while the canvas is off screen this canvas RELEASES its WebGL
|
|
125
|
+
* context outright, and takes it back when the canvas returns -- see the
|
|
126
|
+
* `ContextState` block inside the component for the whole mechanism and the
|
|
127
|
+
* context-budget measurement that forced it. There is no prop for this; it is
|
|
128
|
+
* always on.
|
|
129
|
+
*/
|
|
130
|
+
export declare function ShaderCanvas(props: ShaderCanvasProps): JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Size scales for the audio visualizer variants.
|
|
3
|
+
*
|
|
4
|
+
* Values carried from livekit/components-js `packages/shadcn/components/agents-ui/`
|
|
5
|
+
* (Apache License 2.0). Kept as numbers rather than Tailwind classes so bar
|
|
6
|
+
* counts and radii can be interpolated: upstream hardcodes arbitrary values
|
|
7
|
+
* (`w-[4px]`) in cva variants, which cannot vary with a runtime count.
|
|
8
|
+
*/
|
|
9
|
+
export type VisualizerSize = 'icon' | 'sm' | 'md' | 'lg' | 'xl';
|
|
10
|
+
export declare const SIZES: readonly VisualizerSize[];
|
|
11
|
+
/** Overall height of the bar and radial containers, in px. */
|
|
12
|
+
export declare const CONTAINER_HEIGHT: Record<VisualizerSize, number>;
|
|
13
|
+
/** Gap between bars, in px. */
|
|
14
|
+
export declare const GAP: Record<VisualizerSize, number>;
|
|
15
|
+
/** Bar width, in px. Doubles as the bar's min-height so a dot shows at zero. */
|
|
16
|
+
export declare const BAR_WIDTH: Record<VisualizerSize, number>;
|
|
17
|
+
/** Grid cell diameter, in px. */
|
|
18
|
+
export declare const GRID_CELL: Record<VisualizerSize, number>;
|
|
19
|
+
/** Grid gap, in px. Note this diverges from GAP above at lg and xl. */
|
|
20
|
+
export declare const GRID_GAP: Record<VisualizerSize, number>;
|
|
21
|
+
/** Distance from center to the radial ring, in px. */
|
|
22
|
+
export declare const RADIAL_RADIUS: Record<VisualizerSize, number>;
|
|
23
|
+
export declare function defaultBarCount(size: VisualizerSize): number;
|
|
24
|
+
export declare function defaultRadialBarCount(size: VisualizerSize): number;
|
|
25
|
+
export declare function defaultGridCount(size: VisualizerSize): number;
|