@kitn.ai/ui 0.28.0 → 0.29.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 (376) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-tooling/construct/public.d.ts +15 -0
  3. package/dist/agent-tooling/construct/schema.d.ts +111 -0
  4. package/dist/construct-cli.es.js +3 -3
  5. package/dist/construct.d.ts +5 -0
  6. package/dist/construct.js +327 -0
  7. package/dist/core-C8fzo39E.js +1 -12
  8. package/dist/engine-javascript-C1x7zo1_.js +1 -141
  9. package/dist/mcp.es.js +1 -1
  10. package/package.json +16 -16
  11. package/bin/route.test.js +0 -58
  12. package/src/agent-tooling/README.md +0 -192
  13. package/src/agent-tooling/archetypes.ts +0 -205
  14. package/src/agent-tooling/catalog/README.md +0 -676
  15. package/src/agent-tooling/catalog/catalog-types.ts +0 -231
  16. package/src/agent-tooling/catalog/fabrications.ts +0 -96
  17. package/src/agent-tooling/catalog/invariants.ts +0 -284
  18. package/src/agent-tooling/catalog/labs-titles.ts +0 -114
  19. package/src/agent-tooling/catalog/scenarios.ts +0 -87
  20. package/src/agent-tooling/catalog/surfaces.ts +0 -347
  21. package/src/agent-tooling/construct/cli-entry.ts +0 -8
  22. package/src/agent-tooling/construct/cli.ts +0 -147
  23. package/src/agent-tooling/construct/codegen.ts +0 -1635
  24. package/src/agent-tooling/construct/construct.v1.schema.json +0 -319
  25. package/src/agent-tooling/construct/dev.ts +0 -148
  26. package/src/agent-tooling/construct/fixtures/demo-widget.construct.json +0 -7
  27. package/src/agent-tooling/construct/fixtures/ops-console.construct.json +0 -44
  28. package/src/agent-tooling/construct/fixtures/owner-widget.construct.json +0 -32
  29. package/src/agent-tooling/construct/schema.ts +0 -452
  30. package/src/agent-tooling/integrations/anthropic.ts +0 -442
  31. package/src/agent-tooling/integrations/cloudflare.ts +0 -218
  32. package/src/agent-tooling/integrations/langgraph.ts +0 -133
  33. package/src/agent-tooling/integrations/mastra.ts +0 -182
  34. package/src/agent-tooling/integrations/mock.ts +0 -148
  35. package/src/agent-tooling/integrations/ollama.ts +0 -97
  36. package/src/agent-tooling/integrations/openai.ts +0 -95
  37. package/src/agent-tooling/integrations/openrouter.ts +0 -82
  38. package/src/agent-tooling/integrations/pi.ts +0 -176
  39. package/src/agent-tooling/integrations/pydantic-ai.ts +0 -108
  40. package/src/agent-tooling/integrations/vercel-ai-sdk.ts +0 -398
  41. package/src/agent-tooling/mcp/css-raw.d.ts +0 -12
  42. package/src/agent-tooling/mcp/manifest.ts +0 -422
  43. package/src/agent-tooling/mcp/server.ts +0 -116
  44. package/src/agent-tooling/mcp/stdio.ts +0 -18
  45. package/src/agent-tooling/mcp/tools/construct.ts +0 -130
  46. package/src/agent-tooling/mcp/tools/debug.ts +0 -621
  47. package/src/agent-tooling/mcp/tools/reference.ts +0 -980
  48. package/src/agent-tooling/mcp/tools/scaffold.ts +0 -6708
  49. package/src/agent-tooling/mcp/tools/theme.ts +0 -456
  50. package/src/agent-tooling/mcp/tools/types.ts +0 -14
  51. package/src/agent-tooling/mcp/validate-args.ts +0 -141
  52. package/src/agent-tooling/recipes/composed-thread.ts +0 -714
  53. package/src/agent-tooling/recipes/index.ts +0 -22
  54. package/src/agent-tooling/recipes/types.ts +0 -32
  55. package/src/agent-tooling/registry.ts +0 -132
  56. package/src/agent-tooling/route-emit.ts +0 -305
  57. package/src/agent-tooling/types.ts +0 -390
  58. package/src/components/artifact-card.tsx +0 -120
  59. package/src/components/artifact.tsx +0 -914
  60. package/src/components/attachment-types.ts +0 -66
  61. package/src/components/attachments.tsx +0 -536
  62. package/src/components/audio-visualizer/audio-visualizer.voice-fixture.ts +0 -257
  63. package/src/components/audio-visualizer/aurora.glsl.ts +0 -330
  64. package/src/components/audio-visualizer/fit-scale.ts +0 -112
  65. package/src/components/audio-visualizer/index.tsx +0 -528
  66. package/src/components/audio-visualizer/labs/lab-choreography.ts +0 -187
  67. package/src/components/audio-visualizer/labs/lab-shaders.ts +0 -363
  68. package/src/components/audio-visualizer/labs/lab-visualizer.tsx +0 -102
  69. package/src/components/audio-visualizer/shader-canvas.tsx +0 -876
  70. package/src/components/audio-visualizer/sizes.ts +0 -54
  71. package/src/components/audio-visualizer/variant-aurora.tsx +0 -305
  72. package/src/components/audio-visualizer/variant-bar.tsx +0 -175
  73. package/src/components/audio-visualizer/variant-custom.tsx +0 -344
  74. package/src/components/audio-visualizer/variant-grid.tsx +0 -175
  75. package/src/components/audio-visualizer/variant-radial.tsx +0 -163
  76. package/src/components/audio-visualizer/variant-wave.tsx +0 -130
  77. package/src/components/audio-visualizer/wave.glsl.ts +0 -130
  78. package/src/components/card-fallback.tsx +0 -60
  79. package/src/components/card-renderer.tsx +0 -220
  80. package/src/components/card.tsx +0 -212
  81. package/src/components/chain-of-thought.tsx +0 -348
  82. package/src/components/chat-container.tsx +0 -89
  83. package/src/components/chat-scope-picker.tsx +0 -57
  84. package/src/components/chat-thread.tsx +0 -1014
  85. package/src/components/checkpoint.tsx +0 -88
  86. package/src/components/choice-card.tsx +0 -743
  87. package/src/components/coachmark.tsx +0 -249
  88. package/src/components/code-block.tsx +0 -181
  89. package/src/components/composer-dom.ts +0 -159
  90. package/src/components/composer-highlight.ts +0 -242
  91. package/src/components/composer-history.ts +0 -82
  92. package/src/components/composer.tsx +0 -1075
  93. package/src/components/confirm-card.tsx +0 -402
  94. package/src/components/context.tsx +0 -377
  95. package/src/components/conversation-item.tsx +0 -241
  96. package/src/components/conversation-list.tsx +0 -429
  97. package/src/components/conversation-panel.tsx +0 -118
  98. package/src/components/dismissed-stub.tsx +0 -80
  99. package/src/components/embed.tsx +0 -200
  100. package/src/components/empty.tsx +0 -177
  101. package/src/components/feedback-bar.tsx +0 -166
  102. package/src/components/file-tree.tsx +0 -530
  103. package/src/components/file-upload.tsx +0 -167
  104. package/src/components/form-widgets.tsx +0 -525
  105. package/src/components/form.tsx +0 -1296
  106. package/src/components/home-panel.tsx +0 -159
  107. package/src/components/image.tsx +0 -65
  108. package/src/components/link-preview.tsx +0 -195
  109. package/src/components/loader.tsx +0 -367
  110. package/src/components/markdown.tsx +0 -162
  111. package/src/components/message-skills.tsx +0 -40
  112. package/src/components/message.tsx +0 -765
  113. package/src/components/model-switcher.tsx +0 -132
  114. package/src/components/prompt-input.tsx +0 -188
  115. package/src/components/prompt-suggestion.tsx +0 -150
  116. package/src/components/reasoning.tsx +0 -293
  117. package/src/components/response-compare-types.ts +0 -152
  118. package/src/components/response-compare.tsx +0 -455
  119. package/src/components/response-stream.tsx +0 -109
  120. package/src/components/screen.tsx +0 -181
  121. package/src/components/scroll-button.tsx +0 -93
  122. package/src/components/source.tsx +0 -192
  123. package/src/components/tasks-card.tsx +0 -801
  124. package/src/components/text-shimmer.tsx +0 -37
  125. package/src/components/thinking-bar.tsx +0 -50
  126. package/src/components/thread.tsx +0 -228
  127. package/src/components/toast.tsx +0 -503
  128. package/src/components/tool-classify.ts +0 -26
  129. package/src/components/tool-types.ts +0 -49
  130. package/src/components/tool.tsx +0 -190
  131. package/src/components/use-card-resolution.ts +0 -44
  132. package/src/components/voice-input.tsx +0 -222
  133. package/src/components/voice-output.tsx +0 -242
  134. package/src/components/widget-tab-bar.tsx +0 -73
  135. package/src/components/workspace-shell.tsx +0 -285
  136. package/src/diagnostics/hook.ts +0 -352
  137. package/src/diagnostics/index.ts +0 -82
  138. package/src/diagnostics/report-request.ts +0 -296
  139. package/src/elements/agent-card.tsx +0 -67
  140. package/src/elements/artifact.tsx +0 -194
  141. package/src/elements/attachments.tsx +0 -140
  142. package/src/elements/audio-visualizer.tsx +0 -227
  143. package/src/elements/autoloader.ts +0 -89
  144. package/src/elements/avatar.tsx +0 -33
  145. package/src/elements/badge.tsx +0 -32
  146. package/src/elements/button.tsx +0 -149
  147. package/src/elements/card.tsx +0 -133
  148. package/src/elements/cards.tsx +0 -266
  149. package/src/elements/chain-of-thought.tsx +0 -141
  150. package/src/elements/chat-scope-picker.tsx +0 -65
  151. package/src/elements/chat-types.ts +0 -102
  152. package/src/elements/chat-workspace.tsx +0 -188
  153. package/src/elements/chat.tsx +0 -304
  154. package/src/elements/checkbox-group.tsx +0 -164
  155. package/src/elements/checkbox.tsx +0 -134
  156. package/src/elements/checkpoint.tsx +0 -47
  157. package/src/elements/choice.tsx +0 -100
  158. package/src/elements/coachmark.tsx +0 -90
  159. package/src/elements/code-block.tsx +0 -60
  160. package/src/elements/command.tsx +0 -197
  161. package/src/elements/compare.tsx +0 -111
  162. package/src/elements/compiled.css +0 -2
  163. package/src/elements/composer.tsx +0 -162
  164. package/src/elements/confirm-card.tsx +0 -76
  165. package/src/elements/context-meter.tsx +0 -107
  166. package/src/elements/conversation-item.tsx +0 -120
  167. package/src/elements/conversation-list.tsx +0 -203
  168. package/src/elements/css.ts +0 -5
  169. package/src/elements/default-input.tsx +0 -392
  170. package/src/elements/define-entry.ts +0 -12
  171. package/src/elements/define.tsx +0 -563
  172. package/src/elements/diagnostic-events.ts +0 -114
  173. package/src/elements/dialog.tsx +0 -145
  174. package/src/elements/disclosure.ts +0 -95
  175. package/src/elements/dock.tsx +0 -215
  176. package/src/elements/dropdown.tsx +0 -123
  177. package/src/elements/editable-label.tsx +0 -140
  178. package/src/elements/element-data-types.ts +0 -152
  179. package/src/elements/element-diagnostics.ts +0 -392
  180. package/src/elements/element-manifest.json +0 -354
  181. package/src/elements/element-nonscalar.json +0 -187
  182. package/src/elements/element-types.d.ts +0 -4353
  183. package/src/elements/embed.tsx +0 -35
  184. package/src/elements/empty.tsx +0 -29
  185. package/src/elements/feedback-bar.tsx +0 -66
  186. package/src/elements/file-tree.tsx +0 -62
  187. package/src/elements/file-upload.tsx +0 -44
  188. package/src/elements/form.tsx +0 -94
  189. package/src/elements/hover-card.tsx +0 -80
  190. package/src/elements/icon.tsx +0 -41
  191. package/src/elements/image.tsx +0 -32
  192. package/src/elements/input.tsx +0 -492
  193. package/src/elements/kbd.tsx +0 -45
  194. package/src/elements/link-preview.tsx +0 -34
  195. package/src/elements/loader.tsx +0 -25
  196. package/src/elements/markdown.tsx +0 -38
  197. package/src/elements/menu.tsx +0 -241
  198. package/src/elements/message-skills.tsx +0 -83
  199. package/src/elements/message.tsx +0 -361
  200. package/src/elements/model-switcher.tsx +0 -121
  201. package/src/elements/nav.tsx +0 -87
  202. package/src/elements/notice.tsx +0 -53
  203. package/src/elements/pane-grid.tsx +0 -111
  204. package/src/elements/pane-group.tsx +0 -119
  205. package/src/elements/pane.tsx +0 -129
  206. package/src/elements/popover.tsx +0 -81
  207. package/src/elements/progress-bar.tsx +0 -39
  208. package/src/elements/prompt-dock.tsx +0 -82
  209. package/src/elements/prompt-input.tsx +0 -240
  210. package/src/elements/prompt-suggestions.tsx +0 -113
  211. package/src/elements/radio-group.tsx +0 -129
  212. package/src/elements/reasoning.tsx +0 -71
  213. package/src/elements/register-impl.ts +0 -148
  214. package/src/elements/register.ts +0 -76
  215. package/src/elements/remote.tsx +0 -225
  216. package/src/elements/resizable.globals.d.ts +0 -36
  217. package/src/elements/resizable.tsx +0 -704
  218. package/src/elements/response-stream.tsx +0 -40
  219. package/src/elements/screen.tsx +0 -90
  220. package/src/elements/scroll-area.tsx +0 -33
  221. package/src/elements/scroll-button.tsx +0 -179
  222. package/src/elements/search.tsx +0 -185
  223. package/src/elements/segmented.tsx +0 -129
  224. package/src/elements/select.tsx +0 -165
  225. package/src/elements/separator.tsx +0 -36
  226. package/src/elements/setting-item.tsx +0 -50
  227. package/src/elements/settings-group.tsx +0 -37
  228. package/src/elements/skeleton.tsx +0 -45
  229. package/src/elements/slider.tsx +0 -171
  230. package/src/elements/slot-text.ts +0 -73
  231. package/src/elements/slots.ts +0 -787
  232. package/src/elements/source.tsx +0 -126
  233. package/src/elements/status.tsx +0 -42
  234. package/src/elements/styles.css +0 -328
  235. package/src/elements/switch.tsx +0 -118
  236. package/src/elements/tabs.tsx +0 -95
  237. package/src/elements/tasks.tsx +0 -103
  238. package/src/elements/text-shimmer.tsx +0 -28
  239. package/src/elements/thinking-bar.tsx +0 -43
  240. package/src/elements/thread.tsx +0 -139
  241. package/src/elements/toast.tsx +0 -106
  242. package/src/elements/tool.tsx +0 -78
  243. package/src/elements/tooltip.tsx +0 -85
  244. package/src/elements/validate-messages.ts +0 -76
  245. package/src/elements/voice-input.tsx +0 -100
  246. package/src/elements/voice-output.tsx +0 -82
  247. package/src/index.ts +0 -430
  248. package/src/primitives/audio-bands.ts +0 -174
  249. package/src/primitives/card-component-types.ts +0 -63
  250. package/src/primitives/card-contract.ts +0 -80
  251. package/src/primitives/card-data-types.ts +0 -361
  252. package/src/primitives/card-host.tsx +0 -37
  253. package/src/primitives/card-recovery.ts +0 -160
  254. package/src/primitives/card-registry.tsx +0 -78
  255. package/src/primitives/card-resolution.ts +0 -36
  256. package/src/primitives/card-routing.ts +0 -85
  257. package/src/primitives/card-schemas/artifact.schema.json +0 -92
  258. package/src/primitives/card-schemas/card-envelope.schema.json +0 -14
  259. package/src/primitives/card-schemas/card-event.schema.json +0 -12
  260. package/src/primitives/card-schemas/choice.schema.json +0 -75
  261. package/src/primitives/card-schemas/confirm.schema.json +0 -65
  262. package/src/primitives/card-schemas/embed.schema.json +0 -63
  263. package/src/primitives/card-schemas/form.result.schema.json +0 -7
  264. package/src/primitives/card-schemas/form.schema.json +0 -53
  265. package/src/primitives/card-schemas/link.schema.json +0 -54
  266. package/src/primitives/card-schemas/tasks.result.schema.json +0 -16
  267. package/src/primitives/card-schemas/tasks.schema.json +0 -83
  268. package/src/primitives/card-tags.ts +0 -61
  269. package/src/primitives/card-validate-cards.ts +0 -255
  270. package/src/primitives/card-validate-schemas.ts +0 -58
  271. package/src/primitives/card-validate.ts +0 -131
  272. package/src/primitives/chat-config.tsx +0 -103
  273. package/src/primitives/composer-model.ts +0 -49
  274. package/src/primitives/composer-triggers.ts +0 -35
  275. package/src/primitives/controllable.ts +0 -21
  276. package/src/primitives/conversation-store.ts +0 -248
  277. package/src/primitives/create-kai-chat.ts +0 -66
  278. package/src/primitives/create-tween.ts +0 -266
  279. package/src/primitives/embed-providers.ts +0 -254
  280. package/src/primitives/field-mask.ts +0 -256
  281. package/src/primitives/field-semantics.ts +0 -115
  282. package/src/primitives/highlighter.ts +0 -157
  283. package/src/primitives/input-mask.ts +0 -853
  284. package/src/primitives/link-preview.ts +0 -87
  285. package/src/primitives/message-feedback.ts +0 -124
  286. package/src/primitives/pdf-preview.ts +0 -121
  287. package/src/primitives/toast-store.ts +0 -338
  288. package/src/primitives/url-scheme-policy.ts +0 -70
  289. package/src/primitives/use-audio-analysis.ts +0 -340
  290. package/src/primitives/use-auto-resize.ts +0 -31
  291. package/src/primitives/use-resize-observer.ts +0 -45
  292. package/src/primitives/use-sequencer.ts +0 -72
  293. package/src/primitives/use-speech-recognition.ts +0 -146
  294. package/src/primitives/use-stick-to-bottom.ts +0 -75
  295. package/src/primitives/use-text-stream.ts +0 -143
  296. package/src/primitives/use-voice-recorder.ts +0 -62
  297. package/src/primitives/visualizer-sequences.ts +0 -350
  298. package/src/remote/host-embed.ts +0 -345
  299. package/src/remote/index.ts +0 -2
  300. package/src/remote/origin.ts +0 -30
  301. package/src/remote/provider-runtime.ts +0 -262
  302. package/src/remote/provider.ts +0 -2
  303. package/src/remote/validate.ts +0 -22
  304. package/src/remote/version.ts +0 -12
  305. package/src/remote/wire.ts +0 -48
  306. package/src/schemas/from-tool-call.ts +0 -151
  307. package/src/schemas/index.ts +0 -341
  308. package/src/schemas/provider-subsets.ts +0 -538
  309. package/src/schemas/registry.ts +0 -355
  310. package/src/schemas/tool-defs.ts +0 -793
  311. package/src/solid.ts +0 -173
  312. package/src/state/index.ts +0 -44
  313. package/src/state/messages.ts +0 -53
  314. package/src/state/mock.ts +0 -297
  315. package/src/state/parts.ts +0 -295
  316. package/src/state/persistence.ts +0 -180
  317. package/src/state/stream.ts +0 -246
  318. package/src/state/suggestions.ts +0 -9
  319. package/src/state/threads.ts +0 -142
  320. package/src/test-utils/fake-clock.ts +0 -88
  321. package/src/types.ts +0 -95
  322. package/src/ui/action-icons.ts +0 -53
  323. package/src/ui/agent-card.tsx +0 -218
  324. package/src/ui/avatar.tsx +0 -23
  325. package/src/ui/badge.tsx +0 -21
  326. package/src/ui/button.tsx +0 -49
  327. package/src/ui/card.tsx +0 -245
  328. package/src/ui/checkbox-group.tsx +0 -153
  329. package/src/ui/checkbox.tsx +0 -63
  330. package/src/ui/collapsible.tsx +0 -163
  331. package/src/ui/command.tsx +0 -103
  332. package/src/ui/dialog.tsx +0 -223
  333. package/src/ui/dock.tsx +0 -630
  334. package/src/ui/dropdown.tsx +0 -636
  335. package/src/ui/editable-label.tsx +0 -112
  336. package/src/ui/hover-card.tsx +0 -345
  337. package/src/ui/icon.tsx +0 -167
  338. package/src/ui/input.tsx +0 -391
  339. package/src/ui/kbd.tsx +0 -118
  340. package/src/ui/nav.tsx +0 -282
  341. package/src/ui/notice.tsx +0 -84
  342. package/src/ui/overlay.tsx +0 -277
  343. package/src/ui/pane-grid.tsx +0 -117
  344. package/src/ui/pane-group.tsx +0 -291
  345. package/src/ui/pane.tsx +0 -193
  346. package/src/ui/popover.tsx +0 -106
  347. package/src/ui/progress-bar.tsx +0 -83
  348. package/src/ui/prompt-dock.tsx +0 -145
  349. package/src/ui/radio.tsx +0 -150
  350. package/src/ui/resizable.tsx +0 -693
  351. package/src/ui/scroll-area.tsx +0 -26
  352. package/src/ui/segmented.tsx +0 -109
  353. package/src/ui/select.tsx +0 -168
  354. package/src/ui/separator.tsx +0 -10
  355. package/src/ui/settings-group.tsx +0 -67
  356. package/src/ui/skeleton.tsx +0 -73
  357. package/src/ui/slider.tsx +0 -178
  358. package/src/ui/status.tsx +0 -48
  359. package/src/ui/switch.tsx +0 -119
  360. package/src/ui/tabs.tsx +0 -168
  361. package/src/ui/textarea.tsx +0 -21
  362. package/src/ui/tooltip.tsx +0 -118
  363. package/src/utils/cn.ts +0 -30
  364. package/src/wire/chunk.ts +0 -248
  365. package/src/wire/consume.ts +0 -599
  366. package/src/wire/diagnostics.ts +0 -727
  367. package/src/wire/encode-probe.ts +0 -214
  368. package/src/wire/encode.ts +0 -947
  369. package/src/wire/files.ts +0 -342
  370. package/src/wire/formats/anthropic.ts +0 -274
  371. package/src/wire/formats/openai.ts +0 -228
  372. package/src/wire/index.ts +0 -95
  373. package/src/wire/media-types.ts +0 -344
  374. package/src/wire/read.ts +0 -396
  375. package/src/wire/sink-helpers.ts +0 -50
  376. package/src/wire/sse.ts +0 -153
@@ -1,344 +0,0 @@
1
- import { createEffect, createMemo, Show, type JSX } from 'solid-js';
2
- import { ShaderCanvas, hexToRgb, DEFAULT_SHADER_COLOR, type UniformSpec, type UniformType } from './shader-canvas';
3
- import { createTween } from '../../primitives/create-tween';
4
- import { shaderTargets } from '../../primitives/visualizer-sequences';
5
- import { CONTAINER_HEIGHT } from './sizes';
6
- import { amplitudeRenderState } from './variant-bar';
7
- import type { ShaderVariantProps } from './index';
8
-
9
- /**
10
- * Every `UniformType` shader-canvas.tsx knows how to declare and push to the
11
- * GPU, duplicated here because that module does not export its `GLSL_TYPE`
12
- * map. `ShaderSpec.uniforms` narrows `type` to `UniformType` at the TS level
13
- * (see `index.tsx`), but a consumer can still reach this component through
14
- * the `<kai-audio-visualizer>` custom element with a plain JS object cast
15
- * past that check (`el.shader = {...}`), or any attribute-driven wrapper
16
- * that never touches TypeScript at all -- so an unrecognized `type` string
17
- * is checked again here, at runtime, against genuinely untrusted input.
18
- *
19
- * A `Record<UniformType, true>` literal, not a `Set`: if `UniformType` ever
20
- * grows a member, this object literal fails to typecheck until a matching
21
- * key is added here too, so the two cannot silently drift apart. Same
22
- * compiler-enforced-completeness technique as `effectiveArraySize` in
23
- * shader-canvas.tsx. Membership is checked with a strict `=== true` value
24
- * comparison (`uniformProblem` below), not `in` or `.hasOwnProperty`: a
25
- * bracket lookup with an untrusted key can resolve to an INHERITED
26
- * `Object.prototype` member (e.g. `spec.type === 'toString'`), and `in`
27
- * would treat that as present. Comparing the looked-up VALUE to `true`
28
- * rejects that case too, since `Object.prototype.toString` is a function,
29
- * never `true`.
30
- */
31
- const KNOWN_UNIFORM_TYPES: Record<UniformType, true> = {
32
- '1f': true, '1i': true, '1fv': true, '2f': true, '3f': true, '3fv': true,
33
- '4f': true, '4fv': true, Matrix2fv: true, Matrix3fv: true, Matrix4fv: true,
34
- };
35
-
36
- /** `1f` and `1i` take a plain number. Every other type takes an array. */
37
- const SCALAR_UNIFORM_TYPES = new Set<UniformType>(['1f', '1i']);
38
-
39
- /**
40
- * Exact element count for the uniform types whose arity is a fixed constant
41
- * of the type name -- `2f` a vec2 (2), `3f` a vec3 (3), `4f` a vec4 (4), and
42
- * `Matrix2fv`/`Matrix3fv`/`Matrix4fv` a 2x2/3x3/4x4 matrix (4/9/16) -- and
43
- * needs no import from shader-canvas.tsx's private unit-size table to know.
44
- *
45
- * Deliberately excludes `1fv`/`3fv`/`4fv`: shader-canvas.tsx's own
46
- * `inferArraySize` treats THOSE three as a legitimate multi-instance array
47
- * whenever `value.length` is a multiple of their unit greater than one, with
48
- * no explicit `arraySize` required to opt in -- `uBands` below is exactly
49
- * that pattern (`1fv`, an unbounded length). A fixed-length check on them
50
- * would reject a consumer's genuinely valid array uniform, not just a
51
- * mistake. A matrix ARRAY is technically eligible for the same inference in
52
- * shader-canvas.tsx, but has no legitimate use in this component's
53
- * single-pass ShaderToy-style shader model, and `ShaderSpec.uniforms`
54
- * exposes no `arraySize` a consumer could use to request one deliberately --
55
- * so treating a matrix uniform as always-one-instance here is a reasonable,
56
- * simpler default. A consumer who genuinely needs an array of matrices is
57
- * not served by this check; see the task report for that tradeoff.
58
- */
59
- const FIXED_ARITY: Partial<Record<UniformType, number>> = {
60
- '2f': 2, '3f': 3, '4f': 4,
61
- Matrix2fv: 4, Matrix3fv: 9, Matrix4fv: 16,
62
- };
63
-
64
- /**
65
- * Checks one consumer-declared uniform's `value` against its `type`,
66
- * returning a problem description, or `undefined` if it is safe to hand to
67
- * WebGL.
68
- *
69
- * The scalar-vs-array check is what matters most for safety: a `1f` handed
70
- * an array, or a `3fv` handed a bare number, is what makes WebGL's
71
- * `uniform*` setters throw a `TypeError` -- the WebIDL binding fails to
72
- * convert the argument at all. The fixed-arity check below it is a
73
- * correctness improvement on top of that, not a safety one: an array of the
74
- * wrong length for a type WebGL treats as fixed-size is a GL-level
75
- * `INVALID_OPERATION` recorded on the context, not a JS exception -- it
76
- * degrades the picture rather than crashing the render loop -- but it is
77
- * cheap to catch here with a clear message instead of a silent blank patch
78
- * on screen.
79
- */
80
- function uniformProblem(name: string, spec: UniformSpec): string | undefined {
81
- if (spec == null || typeof spec !== 'object') {
82
- return `uniform "${name}" is not a valid declaration (expected an object with "type" and "value").`;
83
- }
84
- if (KNOWN_UNIFORM_TYPES[spec.type] !== true) {
85
- return `uniform "${name}" has an unrecognized type "${String(spec.type)}".`;
86
- }
87
- if (SCALAR_UNIFORM_TYPES.has(spec.type)) {
88
- if (typeof spec.value !== 'number' || !Number.isFinite(spec.value)) {
89
- return `uniform "${name}" is declared "${spec.type}" (a number) but its value is not a finite number.`;
90
- }
91
- return undefined;
92
- }
93
- if (
94
- !Array.isArray(spec.value) ||
95
- spec.value.some((v) => typeof v !== 'number' || !Number.isFinite(v))
96
- ) {
97
- return `uniform "${name}" is declared "${spec.type}" (an array) but its value is not an array of finite numbers.`;
98
- }
99
- const arity = FIXED_ARITY[spec.type];
100
- if (arity !== undefined && spec.value.length !== arity) {
101
- return `uniform "${name}" is declared "${spec.type}", which takes exactly ${arity} numbers, but its value has ${spec.value.length}.`;
102
- }
103
- return undefined;
104
- }
105
-
106
- /**
107
- * Uniforms every custom shader receives without asking: the standard set
108
- * (`uColor`, `uIntensity`, `uSpeed`, `uComplexity`) plus the audio pair that
109
- * ONLY this variant gets. Upstream's shader path only ever hands a shader a
110
- * scalar volume; `uBands` -- a per-band `float` array -- has no equivalent
111
- * there, so a spectrum-reactive custom shader is possible here and not
112
- * there. `iTime`/`iResolution`/`iMouse`/`iFrame`/`iDate` are declared by
113
- * `ShaderCanvas` itself for every shader, custom or not, so they are not
114
- * repeated here.
115
- *
116
- * `extra` is a consumer's `ShaderSpec.uniforms` -- untrusted input. Every
117
- * entry is checked with `uniformProblem` before being merged in; a bad
118
- * `type`/`value` pairing throws rather than being handed to `ShaderCanvas`,
119
- * where the mismatch would otherwise throw a `TypeError` inside WebGL's
120
- * `uniform*` setters, called from `ShaderCanvas`'s OWN
121
- * `requestAnimationFrame` loop, where nothing catches it and the canvas
122
- * silently stops animating. `CustomVisualizer` below is what turns that
123
- * throw into the same `onUnavailable` seam every other shader failure uses
124
- * -- see its own doc for how.
125
- *
126
- * Pure and synchronous: reads no signals, schedules nothing. Task 15 reuses
127
- * it as-is.
128
- */
129
- export function customUniforms(
130
- values: {
131
- color: string;
132
- intensity: number;
133
- speed: number;
134
- complexity: number;
135
- volume: number;
136
- bands: number[];
137
- },
138
- extra?: Record<string, UniformSpec>,
139
- ): Record<string, UniformSpec> {
140
- // GLSL has no zero-length arrays, so an empty band list still declares
141
- // one -- and the declared length always comes from THIS array's own
142
- // length (`arraySize: bands.length`), never a separately tracked count,
143
- // so the declaration and the value pushed to the GPU can never disagree.
144
- const bands = values.bands.length ? values.bands : [0];
145
-
146
- const standard: Record<string, UniformSpec> = {
147
- uColor: { type: '3fv', value: hexToRgb(values.color) },
148
- uIntensity: { type: '1f', value: values.intensity },
149
- uSpeed: { type: '1f', value: values.speed },
150
- uComplexity: { type: '1f', value: values.complexity },
151
- uVolume: { type: '1f', value: values.volume },
152
- uBands: { type: '1fv', value: bands, arraySize: bands.length },
153
- };
154
-
155
- if (!extra) return standard;
156
-
157
- for (const [name, spec] of Object.entries(extra)) {
158
- const problem = uniformProblem(name, spec);
159
- if (problem) throw new Error(problem);
160
- }
161
-
162
- return { ...standard, ...extra };
163
- }
164
-
165
- /**
166
- * Renders a consumer-supplied fragment shader.
167
- *
168
- * The shader must define `mainImage(out vec4 fragColor, in vec2 fragCoord)`
169
- * and gets the five ShaderToy built-ins (`iTime`, `iResolution`, `iMouse`,
170
- * `iFrame`, `iDate`) plus every uniform `customUniforms` above declares.
171
- * `ShaderCanvas` DECLARES all of them for you -- declaring the SAME name
172
- * again inside the shader body is a GLSL redefinition and fails to compile.
173
- *
174
- * `fragColor` MUST be premultiplied: `vec4(rgb * alpha, alpha)`, never
175
- * `vec4(rgb, alpha)`. The canvas composites using the browser's default
176
- * `premultipliedAlpha: true`; a translucent edge written the natural
177
- * (straight-alpha) way gets a dark fringe where it meets a light page. For
178
- * example, a shader painting a soft circle that fades to transparent at its
179
- * edge:
180
- *
181
- * ```glsl
182
- * void mainImage(out vec4 fragColor, in vec2 fragCoord) {
183
- * vec2 uv = fragCoord / iResolution.xy;
184
- * float alpha = smoothstep(1.0, 0.0, length(uv - 0.5) * 2.0) * uIntensity;
185
- * fragColor = vec4(uColor * alpha, alpha); // premultiplied -- NOT vec4(uColor, alpha)
186
- * }
187
- * ```
188
- *
189
- * `uVolume` (scalar) and `uBands` (a `float[N]` array, `N` tracking the
190
- * visualizer's actual band count) are this task's differentiating feature:
191
- * upstream's shader path only ever hands a shader a scalar, so a
192
- * spectrum-reactive custom shader is only possible here.
193
- *
194
- * A bad `props.shader.uniforms` entry (an unrecognized `type`, or a `value`
195
- * that does not match it) is logged loudly via `console.error` -- it is a
196
- * bug in the consumer's shader spec they need to see -- and routed through
197
- * `props.onUnavailable`, same as a shader compile or link failure. A
198
- * missing WebGL context logs quietly via `console.warn`: that is an
199
- * expected environment limitation, not a bug. `props.shader` being absent
200
- * entirely is neither: it renders an empty placeholder and does not call
201
- * `onUnavailable` at all, since nothing was ever asked to render.
202
- */
203
- export default function CustomVisualizer(props: ShaderVariantProps): JSX.Element {
204
- const intensity = createTween(0.3);
205
- const speed = createTween(1);
206
-
207
- // The state the rendering follows: `listeningAmplitude` folds `listening`
208
- // into the speaking presentation (live-volume intensity). See
209
- // amplitudeRenderState in variant-bar. `data-kai-state` keeps the real state.
210
- const renderState = () => amplitudeRenderState(props.state, props.listeningAmplitude);
211
-
212
- // Intensity has exactly ONE writer -- this effect -- on purpose. The
213
- // volume override used to live in a second effect ("if speaking,
214
- // intensity.to(0.3 + 0.7v, instant)") alongside this one's 0.5s tween
215
- // toward the state base; both re-ran on a state flip, and Solid re-runs
216
- // sibling effects in the order they sit in the signal's observer list,
217
- // which reorders as effects re-subscribe over their lifetimes. Under the
218
- // adverse ordering the base tween landed AFTER the override and parked a
219
- // speaking shader at the 0.3 base until the next volume change -- which
220
- // never comes under static `bands`/`volume` prop drive. The same
221
- // two-writer race variant-wave.tsx carried (measured there on the parity
222
- // harness; latent here). NOT the benign disjoint-writer two-effect shape
223
- // the Task 12 review accepted -- these two wrote the SAME tween. See the
224
- // wave variant's twin comment; same effect-race class as b5795ac.
225
- createEffect(() => {
226
- const t = shaderTargets(renderState());
227
- if (renderState() === 'speaking') {
228
- // Live volume takes over intensity while speaking, with no easing so
229
- // the picture tracks the audio exactly -- and lands immediately on
230
- // re-entry regardless of effect ordering. `volume` is only tracked
231
- // in this branch, so volume ticks skip the base-target path below.
232
- intensity.to(0.3 + 0.7 * props.volume, { duration: 0 });
233
- } else {
234
- const transition = props.frozen ? { duration: 0 } : { duration: 0.5, ease: 'easeOut' as const };
235
- intensity.to(Array.isArray(t.intensity) && props.frozen ? t.intensity[0] : t.intensity, transition);
236
- }
237
- // `iTime` is never frozen by ShaderCanvas itself -- it is the raw clock,
238
- // always advancing. A custom shader written the conventional way
239
- // (`iTime * uSpeed`) is the ONLY thing that can honour reduced motion,
240
- // and only if `uSpeed` is actually pinned at 0 here. Matches
241
- // variant-aurora.tsx's `speed.to(props.frozen ? 0 : t.speed, ...)`.
242
- speed.to(props.frozen ? 0 : t.speed, { duration: 0 });
243
- });
244
-
245
- // Recomputed whenever a dependency changes -- same as every other shader
246
- // variant's uniforms object; see ShaderCanvas's reactivity note on why
247
- // that is fine (only STRUCTURE triggers a recompile, not a value change).
248
- // The try/catch is what keeps a bad `props.shader.uniforms` entry from
249
- // ever reaching ShaderCanvas: without it, `customUniforms`'s throw (see
250
- // its own doc) would happen wherever THIS expression gets evaluated,
251
- // which includes ShaderCanvas's own requestAnimationFrame loop -- a raw
252
- // browser callback nothing else catches.
253
- //
254
- // Deliberately kept a PURE derivation (no console/onUnavailable calls
255
- // here): those are side effects, and belong in the createEffect below,
256
- // not in a memo whose body should be safe to (re)run purely for its
257
- // return value.
258
- const result = createMemo<{ uniforms?: Record<string, UniformSpec>; error?: string }>(() => {
259
- try {
260
- const uniforms = customUniforms(
261
- {
262
- color: props.color ?? DEFAULT_SHADER_COLOR,
263
- intensity: intensity.value(),
264
- speed: speed.value(),
265
- complexity: props.complexity ?? 0.5,
266
- volume: props.volume,
267
- bands: props.bands,
268
- },
269
- props.shader?.uniforms,
270
- );
271
- return { uniforms };
272
- } catch (err) {
273
- return { error: err instanceof Error ? err.message : String(err) };
274
- }
275
- });
276
-
277
- // Guards against reporting the SAME failure more than once: ShaderCanvas
278
- // reads `uniforms` fresh every animation frame (see its own reactivity
279
- // note), so `result` above can keep re-evaluating to the same error more
280
- // than once before the `Show` below actually unmounts it. Reset on a
281
- // successful `result` (not left permanently `true` once tripped): the
282
- // dispatcher's own `unavailable` flag is permanent for a mount, but this
283
- // component is a public default export usable standalone (e.g. a shader
284
- // editor or a Storybook control surface) where `props.shader` can change
285
- // after mount -- a consumer who fixes one bad uniform and then introduces
286
- // a DIFFERENT one in the same mount must still see and hear about the
287
- // second mistake, not silence because the first one already fired.
288
- let reported = false;
289
- createEffect(() => {
290
- const { error } = result();
291
- if (!error) {
292
- reported = false;
293
- return;
294
- }
295
- if (reported) return;
296
- reported = true;
297
- console.error(`<kai-audio-visualizer variant="custom">: ${error}`);
298
- props.onUnavailable();
299
- });
300
-
301
- const containerStyle = (): JSX.CSSProperties => ({
302
- height: `${CONTAINER_HEIGHT[props.size]}px`,
303
- 'aspect-ratio': '1',
304
- });
305
-
306
- return (
307
- <Show
308
- when={props.shader?.fragment}
309
- fallback={<div data-kai-state={props.state} class={props.class} style={containerStyle()} />}
310
- >
311
- {(fragment) => (
312
- <Show
313
- when={result().uniforms}
314
- fallback={<div data-kai-state={props.state} class={props.class} style={containerStyle()} />}
315
- >
316
- {(u) => (
317
- <div data-kai-state={props.state} class={props.class} style={containerStyle()}>
318
- <ShaderCanvas
319
- fragment={fragment()}
320
- precision={props.size === 'icon' || props.size === 'sm' ? 'mediump' : 'highp'}
321
- uniforms={u()}
322
- animateWhenNotVisible={props.animateWhenNotVisible}
323
- onError={(message) => {
324
- // "not available" is ShaderCanvas's literal wording for a
325
- // missing WebGL context (see its own doc) -- an expected
326
- // environment limitation, not a bug, so it logs quietly.
327
- // Anything else is a compile or link failure IN THE
328
- // CONSUMER'S SHADER, which they need to see, so it is loud.
329
- const missingContext = /not available/i.test(message);
330
- if (missingContext) {
331
- console.warn('<kai-audio-visualizer variant="custom">: shader unavailable', message);
332
- } else {
333
- console.error('<kai-audio-visualizer variant="custom">: shader error', message);
334
- }
335
- props.onUnavailable();
336
- }}
337
- />
338
- </div>
339
- )}
340
- </Show>
341
- )}
342
- </Show>
343
- );
344
- }
@@ -1,175 +0,0 @@
1
- import { Index, type JSX } from 'solid-js';
2
- import { cn } from '../../utils/cn';
3
- import { normalizeVolumeBands } from '../../primitives/audio-bands';
4
- import { useSequencer } from '../../primitives/use-sequencer';
5
- import { gridSequence } from '../../primitives/visualizer-sequences';
6
- import { GRID_CELL, GRID_GAP, defaultGridCount } from './sizes';
7
- import { amplitudeRenderState, type VariantProps } from './variant-bar';
8
-
9
- /*
10
- * NOTE: The speaking thresholds below DIVERGE from upstream's GridCell math,
11
- * deliberately (Rob, 2026-08-09). Upstream computes
12
- * `threshold = |mid - y| * 1/(mid + 1)`, spreading the row thresholds evenly
13
- * across 0..1 (5 rows: 0, 1/3, 2/3). Two blind spots our element actually
14
- * exposes:
15
- *
16
- * (1) Real speech through the aligned analysis pipeline peaks at 0.51-0.54
17
- * (0.68 on the recorded fixture), so distance-2 rows (top/bottom of a
18
- * 5-row grid) NEVER light — measured zero times across 139 speech
19
- * samples, on upstream's own grid as well as ours.
20
- * (2) The middle row's threshold is 0, so it stays lit in the speaking state
21
- * even on a silent mic.
22
- *
23
- * Rob asked for both fixed: outer rows reachable by real speech (the grid
24
- * fills centre-outward as a cross) and an empty grid when the mic is silent.
25
- * So the ramp is scaled to the realistic speech ceiling, and the would-be-0
26
- * centre threshold becomes a small silence floor. 5 rows now: 0.02, ~0.217,
27
- * ~0.433 (3 rows: 0.02, 0.325; 7 rows: 0.02, ~0.163, 0.325, ~0.488).
28
- *
29
- * (3) A third divergence, also Rob's request (2026-08-09): at IDLE the grid
30
- * highlights nothing. Upstream (and we, until now — audit-verified on
31
- * both) rested one stationary centre cell there, gridSequence's default
32
- * frame. Grid only: bar and radial already idle dark (their idle
33
- * sequences are empty), so this also makes the family consistent.
34
- */
35
- /** Realistic top of the aligned pipeline's speech range (measured 0.51-0.54 live, 0.68 fixture). */
36
- const SPEECH_LEVEL_CEILING = 0.65;
37
- /** Real speech bands sit at ~0.1 and up; true silence idles at ~0, so this splits them cleanly. */
38
- const SILENCE_FLOOR = 0.02;
39
-
40
- /**
41
- * A grid of dots that pulses with the audio.
42
- *
43
- * Ported from livekit/components-js
44
- * `packages/shadcn/components/agents-ui/agent-audio-visualizer-grid.tsx`
45
- * (Apache License 2.0), except the speaking threshold remap — see the
46
- * divergence note above.
47
- */
48
- export function GridVisualizer(
49
- props: VariantProps & {
50
- /** Rows AND columns of the (always square) grid. Default from the size
51
- * preset. Replaced the former independent rowCount/columnCount pre-1.0:
52
- * columns alone carry the audio signal, so a non-square grid only ever
53
- * changed threshold-ring resolution — never expression (Rob, 2026-08-09;
54
- * upstream is square-by-default via `rowCount ?? columnCount` too). */
55
- count?: number;
56
- /** Ring distance from center for the connecting animation, in cells. */
57
- spread?: number;
58
- /** Ms between scripted frames. Default 100. */
59
- interval?: number;
60
- },
61
- ): JSX.Element {
62
- const rows = () => props.count ?? defaultGridCount(props.size);
63
- const cols = () => rows();
64
- const interval = () => props.interval ?? 100;
65
- const items = () => Array.from({ length: rows() * cols() }, (_, i) => i);
66
-
67
- // The state the rendering follows: `listeningAmplitude` folds `listening`
68
- // into the speaking presentation. See amplitudeRenderState in variant-bar.
69
- const renderState = () => amplitudeRenderState(props.state, props.listeningAmplitude);
70
-
71
- const tick = useSequencer(() =>
72
- // Speaking is driven by audio, not the clock; freezing also parks it.
73
- props.frozen || renderState() === 'speaking' ? Infinity : interval(),
74
- );
75
-
76
- const sequence = () => gridSequence(renderState(), rows(), cols(), props.spread);
77
- const active = () => sequence()[tick() % sequence().length] ?? { x: -1, y: -1 };
78
-
79
- // Bands only mean anything while speaking (or listening under the
80
- // `listeningAmplitude` opt-in, folded in by renderState). Everywhere else
81
- // the sequence is the whole story, so a stale level never leaks into a
82
- // scripted state (same guard as BarVisualizer, so the render-prop's `value`
83
- // means one thing across every variant).
84
- const levels = () =>
85
- renderState() === 'speaking' ? normalizeVolumeBands(props.bands, cols()) : new Array(cols()).fill(0);
86
-
87
- /**
88
- * While speaking, a cell lights when its column's level clears a threshold
89
- * that grows with distance from the middle row: the column fills
90
- * centre-outward, so the grid reads as a spectrum. A silent column shows
91
- * nothing (the floor), a loud one reaches the edges (the scaled ramp) —
92
- * both per the divergence note at the top of this file.
93
- */
94
- function isLit(index: number): boolean {
95
- if (renderState() === 'speaking') {
96
- const y = Math.floor(index / cols());
97
- const mid = Math.floor(rows() / 2);
98
- const scaled = (Math.abs(mid - y) / (mid + 1)) * SPEECH_LEVEL_CEILING;
99
- // max() applies the floor exactly where the ramp would be 0 (the middle
100
- // row); every other threshold already clears it.
101
- const threshold = Math.max(scaled, SILENCE_FLOOR);
102
- return (levels()[index % cols()] ?? 0) >= threshold;
103
- }
104
- // Idle shows a fully dark grid (Rob, 2026-08-09) — divergence (3) above;
105
- // upstream rests one stationary centre cell here. 'disconnected' (first-
106
- // class 2026-08-10) mirrors idle's dark grid for now, pending the LiveKit
107
- // disconnected-state measurement.
108
- if (renderState() === 'idle' || renderState() === 'disconnected') return false;
109
- return active().x === index % cols() && active().y === Math.floor(index / cols());
110
- }
111
-
112
- /** Snap on, fade off: highlighted cells transition 10x faster than they decay. */
113
- function transition(index: number): string {
114
- if (renderState() === 'speaking') return '150ms';
115
- return `${interval() / (isLit(index) ? 1000 : 100)}s`;
116
- }
117
-
118
- return (
119
- <div
120
- data-kai-state={props.state}
121
- class={cn('grid', props.class)}
122
- style={{
123
- 'grid-template-columns': `repeat(${cols()}, 1fr)`,
124
- gap: `${GRID_GAP[props.size]}px`,
125
- ...(props.color ? { color: props.color } : {}),
126
- }}
127
- >
128
- {/*
129
- <Index>, not <For>, matching BarVisualizer: `items()` positions are
130
- stable, but mapping by POSITION (rather than by `===` on the array
131
- value) is the correct primitive here too, and it keeps the render-prop
132
- contract identical across variants. See the note in variant-bar.tsx.
133
- */}
134
- <Index each={items()}>
135
- {(_value, index) => {
136
- // Every column repeats down every row, so a cell's level comes from
137
- // its column band, not its flat position: index % cols(), not index.
138
- // `highlighted`/`value` are live accessors: the callback below runs
139
- // once per position, so a consumer's render-prop must call them
140
- // itself to stay current rather than close over a one-time snapshot.
141
- const item = {
142
- index,
143
- highlighted: () => isLit(index),
144
- value: () => levels()[index % cols()] ?? 0,
145
- };
146
- return (
147
- props.children?.(item) ?? (
148
- // The lit state is a SECOND part TOKEN (`part(cell highlighted)`),
149
- // not a `data-*` attribute selector on `::part(cell)`: a CSS
150
- // attribute selector cannot follow a pseudo-element, so
151
- // `::part(cell)[data-kai-highlighted="true"]` never matches
152
- // anything from outside the shadow root. `data-kai-highlighted`
153
- // stays on the element for the render-prop and for styling from
154
- // inside the shadow root; `part` is the external seam.
155
- <div
156
- part={item.highlighted() ? 'cell highlighted' : 'cell'}
157
- data-kai-index={item.index}
158
- data-kai-highlighted={item.highlighted()}
159
- class={cn(
160
- 'place-self-center rounded-full bg-current/10 transition-all ease-out',
161
- 'data-[kai-highlighted=true]:bg-current',
162
- )}
163
- style={{
164
- width: `${GRID_CELL[props.size]}px`,
165
- height: `${GRID_CELL[props.size]}px`,
166
- 'transition-duration': transition(index),
167
- }}
168
- />
169
- )
170
- );
171
- }}
172
- </Index>
173
- </div>
174
- );
175
- }