{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "smokey-cursor",
  "type": "registry:block",
  "title": "Smokey cursor",
  "description": "Smokey cursor",
  "files": [
    {
      "path": "components/usages/smokeycursorusage.tsx",
      "content": "import React, { useState } from \"react\";\n\nimport SmokeyCursor from \"@/registry/open-source/smokey-cursor\";\n\nimport { Button } from \"../ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"../ui/card\";\nimport { Label } from \"../ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"../ui/select\";\nimport { Slider } from \"../ui/slider\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\";\n\nexport default function SmokeyCursorDemo() {\n\t// State for cursor settings\n\tconst [settings, setSettings] = useState({\n\t\tsimResolution: 128,\n\t\tdyeResolution: 1024,\n\t\tdensityDissipation: 3.5,\n\t\tvelocityDissipation: 2,\n\t\tpressure: 0.1,\n\t\tpressureIterations: 20,\n\t\tcurl: 3,\n\t\tsplatRadius: 0.2,\n\t\tsplatForce: 6000,\n\t\tshading: true,\n\t\tcolorUpdateSpeed: 10,\n\t\tbackColor: { r: 0.5, g: 0, b: 0 },\n\t\ttransparent: true,\n\t\tisActive: true,\n\t});\n\n\t// Toggle cursor on/off\n\tconst toggleCursor = () => {\n\t\tsetSettings((prev) => ({ ...prev, isActive: !prev.isActive }));\n\t};\n\n\t// Handle color change\n\tconst handleColorChange = (component: \"r\" | \"g\" | \"b\", value: number) => {\n\t\tsetSettings((prev) => ({\n\t\t\t...prev,\n\t\t\tbackColor: {\n\t\t\t\t...prev.backColor,\n\t\t\t\t[component]: value / 100,\n\t\t\t},\n\t\t}));\n\t};\n\n\t// Handle slider change\n\tconst handleSliderChange = (name: string, value: number) => {\n\t\tsetSettings((prev) => ({\n\t\t\t...prev,\n\t\t\t[name]: value,\n\t\t}));\n\t};\n\n\t// Handle preset selection\n\tconst handlePresetChange = (preset: string) => {\n\t\tswitch (preset) {\n\t\t\tcase \"water\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 2.5,\n\t\t\t\t\tvelocityDissipation: 1.5,\n\t\t\t\t\tcurl: 4,\n\t\t\t\t\tsplatRadius: 0.3,\n\t\t\t\t\tsplatForce: 5000,\n\t\t\t\t\tbackColor: { r: 0.0, g: 0.3, b: 0.8 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\tcase \"fire\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 4.5,\n\t\t\t\t\tvelocityDissipation: 3,\n\t\t\t\t\tcurl: 5,\n\t\t\t\t\tsplatRadius: 0.15,\n\t\t\t\t\tsplatForce: 8000,\n\t\t\t\t\tbackColor: { r: 0.8, g: 0.2, b: 0.0 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\tcase \"smoke\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 2.0,\n\t\t\t\t\tvelocityDissipation: 1.8,\n\t\t\t\t\tcurl: 2,\n\t\t\t\t\tsplatRadius: 0.25,\n\t\t\t\t\tsplatForce: 4000,\n\t\t\t\t\tbackColor: { r: 0.2, g: 0.2, b: 0.2 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\tcase \"neon\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 3.2,\n\t\t\t\t\tvelocityDissipation: 2.5,\n\t\t\t\t\tcurl: 4.5,\n\t\t\t\t\tsplatRadius: 0.18,\n\t\t\t\t\tsplatForce: 7000,\n\t\t\t\t\tbackColor: { r: 0.6, g: 0.0, b: 0.9 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-8\">\n\t\t\t<div className=\"relative w-full h-[300px] rounded-lg overflow-hidden border flex items-center justify-center\">\n\t\t\t\t<div className=\"z-10 text-center\">\n\t\t\t\t\t<h3 className=\"text-2xl font-medium mb-4\">\n\t\t\t\t\t\tMouse Fluid Simulation\n\t\t\t\t\t</h3>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tonClick={toggleCursor}\n\t\t\t\t\t\tclassName=\"animate-pulse\"\n\t\t\t\t\t\tvariant={settings.isActive ? \"destructive\" : \"default\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t{settings.isActive ? \"Deactivate Effect\" : \"Activate Effect\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{settings.isActive && (\n\t\t\t\t\t<SmokeyCursor\n\t\t\t\t\t\tSIM_RESOLUTION={settings.simResolution}\n\t\t\t\t\t\tDYE_RESOLUTION={settings.dyeResolution}\n\t\t\t\t\t\tDENSITY_DISSIPATION={settings.densityDissipation}\n\t\t\t\t\t\tVELOCITY_DISSIPATION={settings.velocityDissipation}\n\t\t\t\t\t\tPRESSURE={settings.pressure}\n\t\t\t\t\t\tPRESSURE_ITERATIONS={settings.pressureIterations}\n\t\t\t\t\t\tCURL={settings.curl}\n\t\t\t\t\t\tSPLAT_RADIUS={settings.splatRadius}\n\t\t\t\t\t\tSPLAT_FORCE={settings.splatForce}\n\t\t\t\t\t\tSHADING={settings.shading}\n\t\t\t\t\t\tCOLOR_UPDATE_SPEED={settings.colorUpdateSpeed}\n\t\t\t\t\t\tBACK_COLOR={settings.backColor}\n\t\t\t\t\t\tTRANSPARENT={settings.transparent}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<Tabs defaultValue=\"basic\" className=\"w-full\">\n\t\t\t\t<TabsList className=\"grid w-full grid-cols-3\">\n\t\t\t\t\t<TabsTrigger value=\"basic\">Basic Settings</TabsTrigger>\n\t\t\t\t\t<TabsTrigger value=\"advanced\">Advanced Settings</TabsTrigger>\n\t\t\t\t\t<TabsTrigger value=\"presets\">Presets</TabsTrigger>\n\t\t\t\t</TabsList>\n\n\t\t\t\t<TabsContent value=\"basic\" className=\"space-y-4\">\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Basic Configuration</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\t\t\tAdjust the fluid dynamics behavior\n\t\t\t\t\t\t\t</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent className=\"space-y-4\">\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tDensity Dissipation:{\" \"}\n\t\t\t\t\t\t\t\t\t\t{settings.densityDissipation.toFixed(1)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.densityDissipation]}\n\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\tmax={5}\n\t\t\t\t\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"densityDissipation\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tVelocity Dissipation:{\" \"}\n\t\t\t\t\t\t\t\t\t\t{settings.velocityDissipation.toFixed(1)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.velocityDissipation]}\n\t\t\t\t\t\t\t\t\t\tmin={0.5}\n\t\t\t\t\t\t\t\t\t\tmax={4}\n\t\t\t\t\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"velocityDissipation\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tSplat Radius: {settings.splatRadius.toFixed(2)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.splatRadius]}\n\t\t\t\t\t\t\t\t\t\tmin={0.1}\n\t\t\t\t\t\t\t\t\t\tmax={0.5}\n\t\t\t\t\t\t\t\t\t\tstep={0.01}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"splatRadius\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>Splat Force: {settings.splatForce}</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.splatForce]}\n\t\t\t\t\t\t\t\t\t\tmin={1000}\n\t\t\t\t\t\t\t\t\t\tmax={10000}\n\t\t\t\t\t\t\t\t\t\tstep={100}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"splatForce\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tR: {(settings.backColor.r * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.backColor.r * 100]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleColorChange(\"r\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tG: {(settings.backColor.g * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.backColor.g * 100]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleColorChange(\"g\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tB: {(settings.backColor.b * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.backColor.b * 100]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleColorChange(\"b\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</TabsContent>\n\n\t\t\t\t<TabsContent value=\"advanced\" className=\"space-y-4\">\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Advanced Parameters</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\t\t\tFine-tune the fluid simulation performance\n\t\t\t\t\t\t\t</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent className=\"space-y-4\">\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tSimulation Resolution: {settings.simResolution}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\tvalue={settings.simResolution.toString()}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"simResolution\",\n\t\t\t\t\t\t\t\t\t\t\t\tparseInt(value)\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t\t<SelectValue placeholder=\"Select resolution\" />\n\t\t\t\t\t\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t<SelectContent>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"64\">Low (64)</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"128\">\n\t\t\t\t\t\t\t\t\t\t\t\tMedium (128)\n\t\t\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"256\">High (256)</SelectItem>\n\t\t\t\t\t\t\t\t\t\t</SelectContent>\n\t\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tDye Resolution: {settings.dyeResolution}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\tvalue={settings.dyeResolution.toString()}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"dyeResolution\",\n\t\t\t\t\t\t\t\t\t\t\t\tparseInt(value)\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t\t<SelectValue placeholder=\"Select resolution\" />\n\t\t\t\t\t\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t<SelectContent>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"512\">Low (512)</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"1024\">\n\t\t\t\t\t\t\t\t\t\t\t\tMedium (1024)\n\t\t\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"1440\">\n\t\t\t\t\t\t\t\t\t\t\t\tHigh (1440)\n\t\t\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t\t</SelectContent>\n\t\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>Curl: {settings.curl.toFixed(1)}</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.curl]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={10}\n\t\t\t\t\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"curl\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tPressure: {settings.pressure.toFixed(2)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.pressure]}\n\t\t\t\t\t\t\t\t\t\tmin={0.01}\n\t\t\t\t\t\t\t\t\t\tmax={0.5}\n\t\t\t\t\t\t\t\t\t\tstep={0.01}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"pressure\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tPressure Iterations: {settings.pressureIterations}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.pressureIterations]}\n\t\t\t\t\t\t\t\t\t\tmin={10}\n\t\t\t\t\t\t\t\t\t\tmax={50}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"pressureIterations\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tColor Update Speed: {settings.colorUpdateSpeed}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.colorUpdateSpeed]}\n\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\tmax={20}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"colorUpdateSpeed\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* <div className=\"space-y-4 pt-2\">\n                <div className=\"flex items-center justify-between\">\n                  <Label htmlFor=\"shading\">Enable Shading</Label>\n                  <Switch\n                    id=\"shading\"\n                    checked={!!settings.shading} // force boolean\n                    onCheckedChange={(checked) =>\n                      handleSliderChange(\"shading\", checked)\n                    }\n                  />\n                </div>\n\n                <div className=\"flex items-center justify-between\">\n                  <Label htmlFor=\"transparent\">Transparent Background</Label>\n                  <Switch\n                    id=\"transparent\"\n                    checked={settings.transparent}\n                    onCheckedChange={(checked) =>\n                      handleSliderChange(\"transparent\", checked)\n                    }\n                  />\n                </div>\n              </div> */}\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</TabsContent>\n\n\t\t\t\t<TabsContent value=\"presets\" className=\"space-y-4\">\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Ready-Made Presets</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\t\t\tChoose from pre-configured effects\n\t\t\t\t\t\t\t</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"water\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Water Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tFlowing blue liquid simulation\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"fire\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Fire Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tEnergetic orange-red flames\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"smoke\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Smoke Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tGentle grayscale swirls\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"neon\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Neon Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tVibrant purple glowing trails\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</TabsContent>\n\t\t\t</Tabs>\n\t\t</div>\n\t);\n}\n",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/smokeycursorusage.tsx",
      "content": "import React, { useState } from \"react\";\n\nimport SmokeyCursor from \"@/registry/open-source/smokey-cursor\";\n\nimport { Button } from \"../ui/button\";\nimport {\n\tCard,\n\tCardContent,\n\tCardDescription,\n\tCardHeader,\n\tCardTitle,\n} from \"../ui/card\";\nimport { Label } from \"../ui/label\";\nimport {\n\tSelect,\n\tSelectContent,\n\tSelectItem,\n\tSelectTrigger,\n\tSelectValue,\n} from \"../ui/select\";\nimport { Slider } from \"../ui/slider\";\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from \"../ui/tabs\";\n\nexport default function SmokeyCursorDemo() {\n\t// State for cursor settings\n\tconst [settings, setSettings] = useState({\n\t\tsimResolution: 128,\n\t\tdyeResolution: 1024,\n\t\tdensityDissipation: 3.5,\n\t\tvelocityDissipation: 2,\n\t\tpressure: 0.1,\n\t\tpressureIterations: 20,\n\t\tcurl: 3,\n\t\tsplatRadius: 0.2,\n\t\tsplatForce: 6000,\n\t\tshading: true,\n\t\tcolorUpdateSpeed: 10,\n\t\tbackColor: { r: 0.5, g: 0, b: 0 },\n\t\ttransparent: true,\n\t\tisActive: true,\n\t});\n\n\t// Toggle cursor on/off\n\tconst toggleCursor = () => {\n\t\tsetSettings((prev) => ({ ...prev, isActive: !prev.isActive }));\n\t};\n\n\t// Handle color change\n\tconst handleColorChange = (component: \"r\" | \"g\" | \"b\", value: number) => {\n\t\tsetSettings((prev) => ({\n\t\t\t...prev,\n\t\t\tbackColor: {\n\t\t\t\t...prev.backColor,\n\t\t\t\t[component]: value / 100,\n\t\t\t},\n\t\t}));\n\t};\n\n\t// Handle slider change\n\tconst handleSliderChange = (name: string, value: number) => {\n\t\tsetSettings((prev) => ({\n\t\t\t...prev,\n\t\t\t[name]: value,\n\t\t}));\n\t};\n\n\t// Handle preset selection\n\tconst handlePresetChange = (preset: string) => {\n\t\tswitch (preset) {\n\t\t\tcase \"water\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 2.5,\n\t\t\t\t\tvelocityDissipation: 1.5,\n\t\t\t\t\tcurl: 4,\n\t\t\t\t\tsplatRadius: 0.3,\n\t\t\t\t\tsplatForce: 5000,\n\t\t\t\t\tbackColor: { r: 0.0, g: 0.3, b: 0.8 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\tcase \"fire\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 4.5,\n\t\t\t\t\tvelocityDissipation: 3,\n\t\t\t\t\tcurl: 5,\n\t\t\t\t\tsplatRadius: 0.15,\n\t\t\t\t\tsplatForce: 8000,\n\t\t\t\t\tbackColor: { r: 0.8, g: 0.2, b: 0.0 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\tcase \"smoke\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 2.0,\n\t\t\t\t\tvelocityDissipation: 1.8,\n\t\t\t\t\tcurl: 2,\n\t\t\t\t\tsplatRadius: 0.25,\n\t\t\t\t\tsplatForce: 4000,\n\t\t\t\t\tbackColor: { r: 0.2, g: 0.2, b: 0.2 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t\tcase \"neon\":\n\t\t\t\tsetSettings((prev) => ({\n\t\t\t\t\t...prev,\n\t\t\t\t\tdensityDissipation: 3.2,\n\t\t\t\t\tvelocityDissipation: 2.5,\n\t\t\t\t\tcurl: 4.5,\n\t\t\t\t\tsplatRadius: 0.18,\n\t\t\t\t\tsplatForce: 7000,\n\t\t\t\t\tbackColor: { r: 0.6, g: 0.0, b: 0.9 },\n\t\t\t\t}));\n\t\t\t\tbreak;\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-8\">\n\t\t\t<div className=\"relative w-full h-[300px] rounded-lg overflow-hidden border flex items-center justify-center\">\n\t\t\t\t<div className=\"z-10 text-center\">\n\t\t\t\t\t<h3 className=\"text-2xl font-medium mb-4\">\n\t\t\t\t\t\tMouse Fluid Simulation\n\t\t\t\t\t</h3>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tonClick={toggleCursor}\n\t\t\t\t\t\tclassName=\"animate-pulse\"\n\t\t\t\t\t\tvariant={settings.isActive ? \"destructive\" : \"default\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t{settings.isActive ? \"Deactivate Effect\" : \"Activate Effect\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{settings.isActive && (\n\t\t\t\t\t<SmokeyCursor\n\t\t\t\t\t\tSIM_RESOLUTION={settings.simResolution}\n\t\t\t\t\t\tDYE_RESOLUTION={settings.dyeResolution}\n\t\t\t\t\t\tDENSITY_DISSIPATION={settings.densityDissipation}\n\t\t\t\t\t\tVELOCITY_DISSIPATION={settings.velocityDissipation}\n\t\t\t\t\t\tPRESSURE={settings.pressure}\n\t\t\t\t\t\tPRESSURE_ITERATIONS={settings.pressureIterations}\n\t\t\t\t\t\tCURL={settings.curl}\n\t\t\t\t\t\tSPLAT_RADIUS={settings.splatRadius}\n\t\t\t\t\t\tSPLAT_FORCE={settings.splatForce}\n\t\t\t\t\t\tSHADING={settings.shading}\n\t\t\t\t\t\tCOLOR_UPDATE_SPEED={settings.colorUpdateSpeed}\n\t\t\t\t\t\tBACK_COLOR={settings.backColor}\n\t\t\t\t\t\tTRANSPARENT={settings.transparent}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<Tabs defaultValue=\"basic\" className=\"w-full\">\n\t\t\t\t<TabsList className=\"grid w-full grid-cols-3\">\n\t\t\t\t\t<TabsTrigger value=\"basic\">Basic Settings</TabsTrigger>\n\t\t\t\t\t<TabsTrigger value=\"advanced\">Advanced Settings</TabsTrigger>\n\t\t\t\t\t<TabsTrigger value=\"presets\">Presets</TabsTrigger>\n\t\t\t\t</TabsList>\n\n\t\t\t\t<TabsContent value=\"basic\" className=\"space-y-4\">\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Basic Configuration</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\t\t\tAdjust the fluid dynamics behavior\n\t\t\t\t\t\t\t</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent className=\"space-y-4\">\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tDensity Dissipation:{\" \"}\n\t\t\t\t\t\t\t\t\t\t{settings.densityDissipation.toFixed(1)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.densityDissipation]}\n\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\tmax={5}\n\t\t\t\t\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"densityDissipation\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tVelocity Dissipation:{\" \"}\n\t\t\t\t\t\t\t\t\t\t{settings.velocityDissipation.toFixed(1)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.velocityDissipation]}\n\t\t\t\t\t\t\t\t\t\tmin={0.5}\n\t\t\t\t\t\t\t\t\t\tmax={4}\n\t\t\t\t\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"velocityDissipation\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tSplat Radius: {settings.splatRadius.toFixed(2)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.splatRadius]}\n\t\t\t\t\t\t\t\t\t\tmin={0.1}\n\t\t\t\t\t\t\t\t\t\tmax={0.5}\n\t\t\t\t\t\t\t\t\t\tstep={0.01}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"splatRadius\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>Splat Force: {settings.splatForce}</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.splatForce]}\n\t\t\t\t\t\t\t\t\t\tmin={1000}\n\t\t\t\t\t\t\t\t\t\tmax={10000}\n\t\t\t\t\t\t\t\t\t\tstep={100}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"splatForce\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-1 md:grid-cols-3 gap-4\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tR: {(settings.backColor.r * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.backColor.r * 100]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleColorChange(\"r\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tG: {(settings.backColor.g * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.backColor.g * 100]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleColorChange(\"g\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tB: {(settings.backColor.b * 100).toFixed(0)}%\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.backColor.b * 100]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleColorChange(\"b\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</TabsContent>\n\n\t\t\t\t<TabsContent value=\"advanced\" className=\"space-y-4\">\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Advanced Parameters</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\t\t\tFine-tune the fluid simulation performance\n\t\t\t\t\t\t\t</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent className=\"space-y-4\">\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tSimulation Resolution: {settings.simResolution}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\tvalue={settings.simResolution.toString()}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"simResolution\",\n\t\t\t\t\t\t\t\t\t\t\t\tparseInt(value)\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t\t<SelectValue placeholder=\"Select resolution\" />\n\t\t\t\t\t\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t<SelectContent>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"64\">Low (64)</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"128\">\n\t\t\t\t\t\t\t\t\t\t\t\tMedium (128)\n\t\t\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"256\">High (256)</SelectItem>\n\t\t\t\t\t\t\t\t\t\t</SelectContent>\n\t\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tDye Resolution: {settings.dyeResolution}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\tvalue={settings.dyeResolution.toString()}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"dyeResolution\",\n\t\t\t\t\t\t\t\t\t\t\t\tparseInt(value)\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t\t<SelectValue placeholder=\"Select resolution\" />\n\t\t\t\t\t\t\t\t\t\t</SelectTrigger>\n\t\t\t\t\t\t\t\t\t\t<SelectContent>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"512\">Low (512)</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"1024\">\n\t\t\t\t\t\t\t\t\t\t\t\tMedium (1024)\n\t\t\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t\t\t<SelectItem value=\"1440\">\n\t\t\t\t\t\t\t\t\t\t\t\tHigh (1440)\n\t\t\t\t\t\t\t\t\t\t\t</SelectItem>\n\t\t\t\t\t\t\t\t\t\t</SelectContent>\n\t\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>Curl: {settings.curl.toFixed(1)}</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.curl]}\n\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\tmax={10}\n\t\t\t\t\t\t\t\t\t\tstep={0.1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"curl\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tPressure: {settings.pressure.toFixed(2)}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.pressure]}\n\t\t\t\t\t\t\t\t\t\tmin={0.01}\n\t\t\t\t\t\t\t\t\t\tmax={0.5}\n\t\t\t\t\t\t\t\t\t\tstep={0.01}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\"pressure\", value?.[0])\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tPressure Iterations: {settings.pressureIterations}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.pressureIterations]}\n\t\t\t\t\t\t\t\t\t\tmin={10}\n\t\t\t\t\t\t\t\t\t\tmax={50}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"pressureIterations\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Label>\n\t\t\t\t\t\t\t\t\t\tColor Update Speed: {settings.colorUpdateSpeed}\n\t\t\t\t\t\t\t\t\t</Label>\n\t\t\t\t\t\t\t\t\t<Slider\n\t\t\t\t\t\t\t\t\t\tvalue={[settings.colorUpdateSpeed]}\n\t\t\t\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\t\t\t\tmax={20}\n\t\t\t\t\t\t\t\t\t\tstep={1}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\t\t\t\t\t\thandleSliderChange(\n\t\t\t\t\t\t\t\t\t\t\t\t\"colorUpdateSpeed\",\n\t\t\t\t\t\t\t\t\t\t\t\tvalue?.[0]\n\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* <div className=\"space-y-4 pt-2\">\n                <div className=\"flex items-center justify-between\">\n                  <Label htmlFor=\"shading\">Enable Shading</Label>\n                  <Switch\n                    id=\"shading\"\n                    checked={!!settings.shading} // force boolean\n                    onCheckedChange={(checked) =>\n                      handleSliderChange(\"shading\", checked)\n                    }\n                  />\n                </div>\n\n                <div className=\"flex items-center justify-between\">\n                  <Label htmlFor=\"transparent\">Transparent Background</Label>\n                  <Switch\n                    id=\"transparent\"\n                    checked={settings.transparent}\n                    onCheckedChange={(checked) =>\n                      handleSliderChange(\"transparent\", checked)\n                    }\n                  />\n                </div>\n              </div> */}\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</TabsContent>\n\n\t\t\t\t<TabsContent value=\"presets\" className=\"space-y-4\">\n\t\t\t\t\t<Card>\n\t\t\t\t\t\t<CardHeader>\n\t\t\t\t\t\t\t<CardTitle>Ready-Made Presets</CardTitle>\n\t\t\t\t\t\t\t<CardDescription>\n\t\t\t\t\t\t\t\tChoose from pre-configured effects\n\t\t\t\t\t\t\t</CardDescription>\n\t\t\t\t\t\t</CardHeader>\n\t\t\t\t\t\t<CardContent className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"water\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Water Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tFlowing blue liquid simulation\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"fire\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Fire Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tEnergetic orange-red flames\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"smoke\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Smoke Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tGentle grayscale swirls\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"h-24 flex flex-col gap-2\"\n\t\t\t\t\t\t\t\tonClick={() => handlePresetChange(\"neon\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">Neon Effect</span>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\t\tVibrant purple glowing trails\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</CardContent>\n\t\t\t\t\t</Card>\n\t\t\t\t</TabsContent>\n\t\t\t</Tabs>\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/smokey-cursor.tsx",
      "content": "\"use client\";\n\nimport React, { useEffect, useRef } from \"react\";\n\n// Credit:\n// https://pro.lightswind.com/\n\ninterface ColorRGB {\n\tr: number;\n\tg: number;\n\tb: number;\n}\n\ninterface SmokeyCursorProps {\n\tSIM_RESOLUTION?: number;\n\tDYE_RESOLUTION?: number;\n\tCAPTURE_RESOLUTION?: number;\n\tDENSITY_DISSIPATION?: number;\n\tVELOCITY_DISSIPATION?: number;\n\tPRESSURE?: number;\n\tPRESSURE_ITERATIONS?: number;\n\tCURL?: number;\n\tSPLAT_RADIUS?: number;\n\tSPLAT_FORCE?: number;\n\tSHADING?: boolean;\n\tCOLOR_UPDATE_SPEED?: number;\n\tBACK_COLOR?: ColorRGB;\n\tTRANSPARENT?: boolean;\n}\n\ninterface Pointer {\n\tid: number;\n\ttexcoordX: number;\n\ttexcoordY: number;\n\tprevTexcoordX: number;\n\tprevTexcoordY: number;\n\tdeltaX: number;\n\tdeltaY: number;\n\tdown: boolean;\n\tmoved: boolean;\n\tcolor: ColorRGB;\n}\n\nfunction pointerPrototype(): Pointer {\n\treturn {\n\t\tid: -1,\n\t\ttexcoordX: 0,\n\t\ttexcoordY: 0,\n\t\tprevTexcoordX: 0,\n\t\tprevTexcoordY: 0,\n\t\tdeltaX: 0,\n\t\tdeltaY: 0,\n\t\tdown: false,\n\t\tmoved: false,\n\t\tcolor: { r: 0, g: 0, b: 0 },\n\t};\n}\n\nexport default function SmokeyCursor({\n\tSIM_RESOLUTION = 128,\n\tDYE_RESOLUTION = 1440,\n\tCAPTURE_RESOLUTION = 512,\n\tDENSITY_DISSIPATION = 3.5,\n\tVELOCITY_DISSIPATION = 2,\n\tPRESSURE = 0.1,\n\tPRESSURE_ITERATIONS = 20,\n\tCURL = 3,\n\tSPLAT_RADIUS = 0.2,\n\tSPLAT_FORCE = 6000,\n\tSHADING = true,\n\tCOLOR_UPDATE_SPEED = 10,\n\tBACK_COLOR = { r: 0.5, g: 0, b: 0 },\n\tTRANSPARENT = true,\n}: SmokeyCursorProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\n\tuseEffect(() => {\n\t\tconst canvas = canvasRef.current;\n\t\tif (!canvas) return; // Guard canvas early\n\n\t\t// Pointer and config setup\n\t\tlet pointers: Pointer[] = [pointerPrototype()];\n\n\t\t// All these are guaranteed numbers due to destructuring defaults\n\t\t// So we cast them to remove TS warnings:\n\t\tlet config = {\n\t\t\tSIM_RESOLUTION: SIM_RESOLUTION!,\n\t\t\tDYE_RESOLUTION: DYE_RESOLUTION!,\n\t\t\tCAPTURE_RESOLUTION: CAPTURE_RESOLUTION!,\n\t\t\tDENSITY_DISSIPATION: DENSITY_DISSIPATION!,\n\t\t\tVELOCITY_DISSIPATION: VELOCITY_DISSIPATION!,\n\t\t\tPRESSURE: PRESSURE!,\n\t\t\tPRESSURE_ITERATIONS: PRESSURE_ITERATIONS!,\n\t\t\tCURL: CURL!,\n\t\t\tSPLAT_RADIUS: SPLAT_RADIUS!,\n\t\t\tSPLAT_FORCE: SPLAT_FORCE!,\n\t\t\tSHADING,\n\t\t\tCOLOR_UPDATE_SPEED: COLOR_UPDATE_SPEED!,\n\t\t\tPAUSED: false,\n\t\t\tBACK_COLOR,\n\t\t\tTRANSPARENT,\n\t\t};\n\n\t\t// Get WebGL context (WebGL1 or WebGL2)\n\t\tconst { gl, ext } = getWebGLContext(canvas);\n\t\tif (!gl || !ext) return;\n\n\t\t// If no linear filtering, reduce resolution\n\t\tif (!ext.supportLinearFiltering) {\n\t\t\tconfig.DYE_RESOLUTION = 256;\n\t\t\tconfig.SHADING = false;\n\t\t}\n\n\t\tfunction getWebGLContext(canvas: HTMLCanvasElement) {\n\t\t\tconst params = {\n\t\t\t\talpha: true,\n\t\t\t\tdepth: false,\n\t\t\t\tstencil: false,\n\t\t\t\tantialias: false,\n\t\t\t\tpreserveDrawingBuffer: false,\n\t\t\t};\n\n\t\t\tlet gl = canvas.getContext(\n\t\t\t\t\"webgl2\",\n\t\t\t\tparams\n\t\t\t) as WebGL2RenderingContext | null;\n\n\t\t\tif (!gl) {\n\t\t\t\tgl = (canvas.getContext(\"webgl\", params) ||\n\t\t\t\t\tcanvas.getContext(\n\t\t\t\t\t\t\"experimental-webgl\",\n\t\t\t\t\t\tparams\n\t\t\t\t\t)) as WebGL2RenderingContext | null;\n\t\t\t}\n\n\t\t\tif (!gl) {\n\t\t\t\tthrow new Error(\"Unable to initialize WebGL.\");\n\t\t\t}\n\n\t\t\tconst isWebGL2 = \"drawBuffers\" in gl;\n\n\t\t\tlet supportLinearFiltering = false;\n\t\t\tlet halfFloat = null;\n\n\t\t\tif (isWebGL2) {\n\t\t\t\t// For WebGL2\n\t\t\t\t(gl as WebGL2RenderingContext).getExtension(\n\t\t\t\t\t\"EXT_color_buffer_float\"\n\t\t\t\t);\n\t\t\t\tsupportLinearFiltering = !!(\n\t\t\t\t\tgl as WebGL2RenderingContext\n\t\t\t\t).getExtension(\"OES_texture_float_linear\");\n\t\t\t} else {\n\t\t\t\t// For WebGL1\n\t\t\t\thalfFloat = gl.getExtension(\"OES_texture_half_float\");\n\t\t\t\tsupportLinearFiltering = !!gl.getExtension(\n\t\t\t\t\t\"OES_texture_half_float_linear\"\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tgl.clearColor(0, 0, 0, 1);\n\n\t\t\tconst halfFloatTexType = isWebGL2\n\t\t\t\t? (gl as WebGL2RenderingContext).HALF_FLOAT\n\t\t\t\t: (halfFloat && (halfFloat as any).HALF_FLOAT_OES) || 0;\n\n\t\t\tlet formatRGBA: any;\n\t\t\tlet formatRG: any;\n\t\t\tlet formatR: any;\n\n\t\t\tif (isWebGL2) {\n\t\t\t\tformatRGBA = getSupportedFormat(\n\t\t\t\t\tgl,\n\t\t\t\t\t(gl as WebGL2RenderingContext).RGBA16F,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\thalfFloatTexType\n\t\t\t\t);\n\t\t\t\tformatRG = getSupportedFormat(\n\t\t\t\t\tgl,\n\t\t\t\t\t(gl as WebGL2RenderingContext).RG16F,\n\t\t\t\t\t(gl as WebGL2RenderingContext).RG,\n\t\t\t\t\thalfFloatTexType\n\t\t\t\t);\n\t\t\t\tformatR = getSupportedFormat(\n\t\t\t\t\tgl,\n\t\t\t\t\t(gl as WebGL2RenderingContext).R16F,\n\t\t\t\t\t(gl as WebGL2RenderingContext).RED,\n\t\t\t\t\thalfFloatTexType\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tformatRGBA = getSupportedFormat(\n\t\t\t\t\tgl,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\thalfFloatTexType\n\t\t\t\t);\n\t\t\t\tformatRG = getSupportedFormat(\n\t\t\t\t\tgl,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\thalfFloatTexType\n\t\t\t\t);\n\t\t\t\tformatR = getSupportedFormat(\n\t\t\t\t\tgl,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\tgl.RGBA,\n\t\t\t\t\thalfFloatTexType\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn {\n\t\t\t\tgl,\n\t\t\t\text: {\n\t\t\t\t\tformatRGBA,\n\t\t\t\t\tformatRG,\n\t\t\t\t\tformatR,\n\t\t\t\t\thalfFloatTexType,\n\t\t\t\t\tsupportLinearFiltering,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tfunction getSupportedFormat(\n\t\t\tgl: WebGLRenderingContext | WebGL2RenderingContext,\n\t\t\tinternalFormat: number,\n\t\t\tformat: number,\n\t\t\ttype: number\n\t\t): { internalFormat: number; format: number } | null {\n\t\t\tif (!supportRenderTextureFormat(gl, internalFormat, format, type)) {\n\t\t\t\t// For WebGL2 fallback:\n\t\t\t\tif (\"drawBuffers\" in gl) {\n\t\t\t\t\tconst gl2 = gl as WebGL2RenderingContext;\n\t\t\t\t\tswitch (internalFormat) {\n\t\t\t\t\t\tcase gl2.R16F:\n\t\t\t\t\t\t\treturn getSupportedFormat(gl2, gl2.RG16F, gl2.RG, type);\n\t\t\t\t\t\tcase gl2.RG16F:\n\t\t\t\t\t\t\treturn getSupportedFormat(\n\t\t\t\t\t\t\t\tgl2,\n\t\t\t\t\t\t\t\tgl2.RGBA16F,\n\t\t\t\t\t\t\t\tgl2.RGBA,\n\t\t\t\t\t\t\t\ttype\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\treturn { internalFormat, format };\n\t\t}\n\n\t\tfunction supportRenderTextureFormat(\n\t\t\tgl: WebGLRenderingContext | WebGL2RenderingContext,\n\t\t\tinternalFormat: number,\n\t\t\tformat: number,\n\t\t\ttype: number\n\t\t) {\n\t\t\tconst texture = gl.createTexture();\n\t\t\tif (!texture) return false;\n\n\t\t\tgl.bindTexture(gl.TEXTURE_2D, texture);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\t\tgl.texImage2D(\n\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t0,\n\t\t\t\tinternalFormat,\n\t\t\t\t4,\n\t\t\t\t4,\n\t\t\t\t0,\n\t\t\t\tformat,\n\t\t\t\ttype,\n\t\t\t\tnull\n\t\t\t);\n\n\t\t\tconst fbo = gl.createFramebuffer();\n\t\t\tif (!fbo) return false;\n\n\t\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n\t\t\tgl.framebufferTexture2D(\n\t\t\t\tgl.FRAMEBUFFER,\n\t\t\t\tgl.COLOR_ATTACHMENT0,\n\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\ttexture,\n\t\t\t\t0\n\t\t\t);\n\t\t\tconst status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);\n\t\t\treturn status === gl.FRAMEBUFFER_COMPLETE;\n\t\t}\n\n\t\tfunction hashCode(s: string) {\n\t\t\tif (!s.length) return 0;\n\t\t\tlet hash = 0;\n\t\t\tfor (let i = 0; i < s.length; i++) {\n\t\t\t\thash = (hash << 5) - hash + s.charCodeAt(i);\n\t\t\t\thash |= 0;\n\t\t\t}\n\t\t\treturn hash;\n\t\t}\n\n\t\tfunction addKeywords(source: string, keywords: string[] | null) {\n\t\t\tif (!keywords) return source;\n\t\t\tlet keywordsString = \"\";\n\t\t\tfor (const keyword of keywords) {\n\t\t\t\tkeywordsString += `#define ${keyword}\\n`;\n\t\t\t}\n\t\t\treturn keywordsString + source;\n\t\t}\n\n\t\tfunction compileShader(\n\t\t\ttype: number,\n\t\t\tsource: string,\n\t\t\tkeywords: string[] | null = null\n\t\t): WebGLShader | null {\n\t\t\tconst shaderSource = addKeywords(source, keywords);\n\t\t\tconst shader = gl.createShader(type);\n\t\t\tif (!shader) return null;\n\t\t\tgl.shaderSource(shader, shaderSource);\n\t\t\tgl.compileShader(shader);\n\t\t\tif (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {\n\t\t\t\tconsole.trace(gl.getShaderInfoLog(shader));\n\t\t\t}\n\t\t\treturn shader;\n\t\t}\n\n\t\tfunction createProgram(\n\t\t\tvertexShader: WebGLShader | null,\n\t\t\tfragmentShader: WebGLShader | null\n\t\t): WebGLProgram | null {\n\t\t\tif (!vertexShader || !fragmentShader) return null;\n\t\t\tconst program = gl.createProgram();\n\t\t\tif (!program) return null;\n\t\t\tgl.attachShader(program, vertexShader);\n\t\t\tgl.attachShader(program, fragmentShader);\n\t\t\tgl.linkProgram(program);\n\t\t\tif (!gl.getProgramParameter(program, gl.LINK_STATUS)) {\n\t\t\t\tconsole.trace(gl.getProgramInfoLog(program));\n\t\t\t}\n\t\t\treturn program;\n\t\t}\n\n\t\tfunction getUniforms(program: WebGLProgram) {\n\t\t\tlet uniforms: Record<string, WebGLUniformLocation | null> = {};\n\t\t\tconst uniformCount = gl.getProgramParameter(\n\t\t\t\tprogram,\n\t\t\t\tgl.ACTIVE_UNIFORMS\n\t\t\t);\n\t\t\tfor (let i = 0; i < uniformCount; i++) {\n\t\t\t\tconst uniformInfo = gl.getActiveUniform(program, i);\n\t\t\t\tif (uniformInfo) {\n\t\t\t\t\tuniforms[uniformInfo.name] = gl.getUniformLocation(\n\t\t\t\t\t\tprogram,\n\t\t\t\t\t\tuniformInfo.name\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn uniforms;\n\t\t}\n\n\t\tclass Program {\n\t\t\tprogram: WebGLProgram | null;\n\t\t\tuniforms: Record<string, WebGLUniformLocation | null>;\n\n\t\t\tconstructor(\n\t\t\t\tvertexShader: WebGLShader | null,\n\t\t\t\tfragmentShader: WebGLShader | null\n\t\t\t) {\n\t\t\t\tthis.program = createProgram(vertexShader, fragmentShader);\n\t\t\t\tthis.uniforms = this.program ? getUniforms(this.program) : {};\n\t\t\t}\n\n\t\t\tbind() {\n\t\t\t\tif (this.program) gl.useProgram(this.program);\n\t\t\t}\n\t\t}\n\n\t\tclass Material {\n\t\t\tvertexShader: WebGLShader | null;\n\t\t\tfragmentShaderSource: string;\n\t\t\tprograms: Record<number, WebGLProgram | null>;\n\t\t\tactiveProgram: WebGLProgram | null;\n\t\t\tuniforms: Record<string, WebGLUniformLocation | null>;\n\n\t\t\tconstructor(\n\t\t\t\tvertexShader: WebGLShader | null,\n\t\t\t\tfragmentShaderSource: string\n\t\t\t) {\n\t\t\t\tthis.vertexShader = vertexShader;\n\t\t\t\tthis.fragmentShaderSource = fragmentShaderSource;\n\t\t\t\tthis.programs = {};\n\t\t\t\tthis.activeProgram = null;\n\t\t\t\tthis.uniforms = {};\n\t\t\t}\n\n\t\t\tsetKeywords(keywords: string[]) {\n\t\t\t\tlet hash = 0;\n\t\t\t\tfor (const kw of keywords) {\n\t\t\t\t\thash += hashCode(kw);\n\t\t\t\t}\n\t\t\t\tlet program = this.programs[hash];\n\t\t\t\tif (program == null) {\n\t\t\t\t\tconst fragmentShader = compileShader(\n\t\t\t\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t\t\t\tthis.fragmentShaderSource,\n\t\t\t\t\t\tkeywords\n\t\t\t\t\t);\n\t\t\t\t\tprogram = createProgram(this.vertexShader, fragmentShader);\n\t\t\t\t\tthis.programs[hash] = program;\n\t\t\t\t}\n\t\t\t\tif (program === this.activeProgram) return;\n\t\t\t\tif (program) {\n\t\t\t\t\tthis.uniforms = getUniforms(program);\n\t\t\t\t}\n\t\t\t\tthis.activeProgram = program;\n\t\t\t}\n\n\t\t\tbind() {\n\t\t\t\tif (this.activeProgram) {\n\t\t\t\t\tgl.useProgram(this.activeProgram);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// -------------------- Shaders --------------------\n\t\tconst baseVertexShader = compileShader(\n\t\t\tgl.VERTEX_SHADER,\n\t\t\t`\n      precision highp float;\n      attribute vec2 aPosition;\n      varying vec2 vUv;\n      varying vec2 vL;\n      varying vec2 vR;\n      varying vec2 vT;\n      varying vec2 vB;\n      uniform vec2 texelSize;\n\n      void main () {\n        vUv = aPosition * 0.5 + 0.5;\n        vL = vUv - vec2(texelSize.x, 0.0);\n        vR = vUv + vec2(texelSize.x, 0.0);\n        vT = vUv + vec2(0.0, texelSize.y);\n        vB = vUv - vec2(0.0, texelSize.y);\n        gl_Position = vec4(aPosition, 0.0, 1.0);\n      }\n    `\n\t\t);\n\n\t\tconst copyShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision mediump float;\n      precision mediump sampler2D;\n      varying highp vec2 vUv;\n      uniform sampler2D uTexture;\n\n      void main () {\n          gl_FragColor = texture2D(uTexture, vUv);\n      }\n    `\n\t\t);\n\n\t\tconst clearShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision mediump float;\n      precision mediump sampler2D;\n      varying highp vec2 vUv;\n      uniform sampler2D uTexture;\n      uniform float value;\n\n      void main () {\n          gl_FragColor = value * texture2D(uTexture, vUv);\n      }\n    `\n\t\t);\n\n\t\tconst displayShaderSource = `\n      precision highp float;\n      precision highp sampler2D;\n      varying vec2 vUv;\n      varying vec2 vL;\n      varying vec2 vR;\n      varying vec2 vT;\n      varying vec2 vB;\n      uniform sampler2D uTexture;\n      uniform sampler2D uDithering;\n      uniform vec2 ditherScale;\n      uniform vec2 texelSize;\n\n      vec3 linearToGamma (vec3 color) {\n          color = max(color, vec3(0));\n          return max(1.055 * pow(color, vec3(0.416666667)) - 0.055, vec3(0));\n      }\n\n      void main () {\n          vec3 c = texture2D(uTexture, vUv).rgb;\n          #ifdef SHADING\n              vec3 lc = texture2D(uTexture, vL).rgb;\n              vec3 rc = texture2D(uTexture, vR).rgb;\n              vec3 tc = texture2D(uTexture, vT).rgb;\n              vec3 bc = texture2D(uTexture, vB).rgb;\n\n              float dx = length(rc) - length(lc);\n              float dy = length(tc) - length(bc);\n\n              vec3 n = normalize(vec3(dx, dy, length(texelSize)));\n              vec3 l = vec3(0.0, 0.0, 1.0);\n\n              float diffuse = clamp(dot(n, l) + 0.7, 0.7, 1.0);\n              c *= diffuse;\n          #endif\n\n          float a = max(c.r, max(c.g, c.b));\n          gl_FragColor = vec4(c, a);\n      }\n    `;\n\n\t\tconst splatShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision highp float;\n      precision highp sampler2D;\n      varying vec2 vUv;\n      uniform sampler2D uTarget;\n      uniform float aspectRatio;\n      uniform vec3 color;\n      uniform vec2 point;\n      uniform float radius;\n\n      void main () {\n          vec2 p = vUv - point.xy;\n          p.x *= aspectRatio;\n          vec3 splat = exp(-dot(p, p) / radius) * color;\n          vec3 base = texture2D(uTarget, vUv).xyz;\n          gl_FragColor = vec4(base + splat, 1.0);\n      }\n    `\n\t\t);\n\n\t\tconst advectionShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision highp float;\n      precision highp sampler2D;\n      varying vec2 vUv;\n      uniform sampler2D uVelocity;\n      uniform sampler2D uSource;\n      uniform vec2 texelSize;\n      uniform vec2 dyeTexelSize;\n      uniform float dt;\n      uniform float dissipation;\n\n      vec4 bilerp (sampler2D sam, vec2 uv, vec2 tsize) {\n          vec2 st = uv / tsize - 0.5;\n          vec2 iuv = floor(st);\n          vec2 fuv = fract(st);\n\n          vec4 a = texture2D(sam, (iuv + vec2(0.5, 0.5)) * tsize);\n          vec4 b = texture2D(sam, (iuv + vec2(1.5, 0.5)) * tsize);\n          vec4 c = texture2D(sam, (iuv + vec2(0.5, 1.5)) * tsize);\n          vec4 d = texture2D(sam, (iuv + vec2(1.5, 1.5)) * tsize);\n\n          return mix(mix(a, b, fuv.x), mix(c, d, fuv.x), fuv.y);\n      }\n\n      void main () {\n          #ifdef MANUAL_FILTERING\n              vec2 coord = vUv - dt * bilerp(uVelocity, vUv, texelSize).xy * texelSize;\n              vec4 result = bilerp(uSource, coord, dyeTexelSize);\n          #else\n              vec2 coord = vUv - dt * texture2D(uVelocity, vUv).xy * texelSize;\n              vec4 result = texture2D(uSource, coord);\n          #endif\n          float decay = 1.0 + dissipation * dt;\n          gl_FragColor = result / decay;\n      }\n    `,\n\t\t\text.supportLinearFiltering ? null : [\"MANUAL_FILTERING\"]\n\t\t);\n\n\t\tconst divergenceShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision mediump float;\n      precision mediump sampler2D;\n      varying highp vec2 vUv;\n      varying highp vec2 vL;\n      varying highp vec2 vR;\n      varying highp vec2 vT;\n      varying highp vec2 vB;\n      uniform sampler2D uVelocity;\n\n      void main () {\n          float L = texture2D(uVelocity, vL).x;\n          float R = texture2D(uVelocity, vR).x;\n          float T = texture2D(uVelocity, vT).y;\n          float B = texture2D(uVelocity, vB).y;\n\n          vec2 C = texture2D(uVelocity, vUv).xy;\n          if (vL.x < 0.0) { L = -C.x; }\n          if (vR.x > 1.0) { R = -C.x; }\n          if (vT.y > 1.0) { T = -C.y; }\n          if (vB.y < 0.0) { B = -C.y; }\n\n          float div = 0.5 * (R - L + T - B);\n          gl_FragColor = vec4(div, 0.0, 0.0, 1.0);\n      }\n    `\n\t\t);\n\n\t\tconst curlShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision mediump float;\n      precision mediump sampler2D;\n      varying highp vec2 vUv;\n      varying highp vec2 vL;\n      varying highp vec2 vR;\n      varying highp vec2 vT;\n      varying highp vec2 vB;\n      uniform sampler2D uVelocity;\n\n      void main () {\n          float L = texture2D(uVelocity, vL).y;\n          float R = texture2D(uVelocity, vR).y;\n          float T = texture2D(uVelocity, vT).x;\n          float B = texture2D(uVelocity, vB).x;\n          float vorticity = R - L - T + B;\n          gl_FragColor = vec4(0.5 * vorticity, 0.0, 0.0, 1.0);\n      }\n    `\n\t\t);\n\n\t\tconst vorticityShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision highp float;\n      precision highp sampler2D;\n      varying vec2 vUv;\n      varying vec2 vL;\n      varying vec2 vR;\n      varying vec2 vT;\n      varying vec2 vB;\n      uniform sampler2D uVelocity;\n      uniform sampler2D uCurl;\n      uniform float curl;\n      uniform float dt;\n\n      void main () {\n          float L = texture2D(uCurl, vL).x;\n          float R = texture2D(uCurl, vR).x;\n          float T = texture2D(uCurl, vT).x;\n          float B = texture2D(uCurl, vB).x;\n          float C = texture2D(uCurl, vUv).x;\n\n          vec2 force = 0.5 * vec2(abs(T) - abs(B), abs(R) - abs(L));\n          force /= length(force) + 0.0001;\n          force *= curl * C;\n          force.y *= -1.0;\n\n          vec2 velocity = texture2D(uVelocity, vUv).xy;\n          velocity += force * dt;\n          velocity = min(max(velocity, -1000.0), 1000.0);\n          gl_FragColor = vec4(velocity, 0.0, 1.0);\n      }\n    `\n\t\t);\n\n\t\tconst pressureShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision mediump float;\n      precision mediump sampler2D;\n      varying highp vec2 vUv;\n      varying highp vec2 vL;\n      varying highp vec2 vR;\n      varying highp vec2 vT;\n      varying highp vec2 vB;\n      uniform sampler2D uPressure;\n      uniform sampler2D uDivergence;\n\n      void main () {\n          float L = texture2D(uPressure, vL).x;\n          float R = texture2D(uPressure, vR).x;\n          float T = texture2D(uPressure, vT).x;\n          float B = texture2D(uPressure, vB).x;\n          float C = texture2D(uPressure, vUv).x;\n          float divergence = texture2D(uDivergence, vUv).x;\n          float pressure = (L + R + B + T - divergence) * 0.25;\n          gl_FragColor = vec4(pressure, 0.0, 0.0, 1.0);\n      }\n    `\n\t\t);\n\n\t\tconst gradientSubtractShader = compileShader(\n\t\t\tgl.FRAGMENT_SHADER,\n\t\t\t`\n      precision mediump float;\n      precision mediump sampler2D;\n      varying highp vec2 vUv;\n      varying highp vec2 vL;\n      varying highp vec2 vR;\n      varying highp vec2 vT;\n      varying highp vec2 vB;\n      uniform sampler2D uPressure;\n      uniform sampler2D uVelocity;\n\n      void main () {\n          float L = texture2D(uPressure, vL).x;\n          float R = texture2D(uPressure, vR).x;\n          float T = texture2D(uPressure, vT).x;\n          float B = texture2D(uPressure, vB).x;\n          vec2 velocity = texture2D(uVelocity, vUv).xy;\n          velocity.xy -= vec2(R - L, T - B);\n          gl_FragColor = vec4(velocity, 0.0, 1.0);\n      }\n    `\n\t\t);\n\n\t\t// -------------------- Fullscreen Triangles --------------------\n\t\tconst blit = (() => {\n\t\t\tconst buffer = gl.createBuffer()!;\n\t\t\tgl.bindBuffer(gl.ARRAY_BUFFER, buffer);\n\t\t\tgl.bufferData(\n\t\t\t\tgl.ARRAY_BUFFER,\n\t\t\t\tnew Float32Array([-1, -1, -1, 1, 1, 1, 1, -1]),\n\t\t\t\tgl.STATIC_DRAW\n\t\t\t);\n\t\t\tconst elemBuffer = gl.createBuffer()!;\n\t\t\tgl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elemBuffer);\n\t\t\tgl.bufferData(\n\t\t\t\tgl.ELEMENT_ARRAY_BUFFER,\n\t\t\t\tnew Uint16Array([0, 1, 2, 0, 2, 3]),\n\t\t\t\tgl.STATIC_DRAW\n\t\t\t);\n\t\t\tgl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);\n\t\t\tgl.enableVertexAttribArray(0);\n\n\t\t\treturn (target: FBO | null, doClear = false) => {\n\t\t\t\tif (!gl) return;\n\t\t\t\tif (!target) {\n\t\t\t\t\tgl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);\n\t\t\t\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, null);\n\t\t\t\t} else {\n\t\t\t\t\tgl.viewport(0, 0, target.width, target.height);\n\t\t\t\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, target.fbo);\n\t\t\t\t}\n\t\t\t\tif (doClear) {\n\t\t\t\t\tgl.clearColor(0, 0, 0, 1);\n\t\t\t\t\tgl.clear(gl.COLOR_BUFFER_BIT);\n\t\t\t\t}\n\t\t\t\tgl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0);\n\t\t\t};\n\t\t})();\n\n\t\t// Types for Framebuffers\n\t\tinterface FBO {\n\t\t\ttexture: WebGLTexture;\n\t\t\tfbo: WebGLFramebuffer;\n\t\t\twidth: number;\n\t\t\theight: number;\n\t\t\ttexelSizeX: number;\n\t\t\ttexelSizeY: number;\n\t\t\tattach: (id: number) => number;\n\t\t}\n\n\t\tinterface DoubleFBO {\n\t\t\twidth: number;\n\t\t\theight: number;\n\t\t\ttexelSizeX: number;\n\t\t\ttexelSizeY: number;\n\t\t\tread: FBO;\n\t\t\twrite: FBO;\n\t\t\tswap: () => void;\n\t\t}\n\n\t\t// FBO variables\n\t\tlet dye: DoubleFBO;\n\t\tlet velocity: DoubleFBO;\n\t\tlet divergence: FBO;\n\t\tlet curl: FBO;\n\t\tlet pressure: DoubleFBO;\n\n\t\t// WebGL Programs\n\t\tconst copyProgram = new Program(baseVertexShader, copyShader);\n\t\tconst clearProgram = new Program(baseVertexShader, clearShader);\n\t\tconst splatProgram = new Program(baseVertexShader, splatShader);\n\t\tconst advectionProgram = new Program(baseVertexShader, advectionShader);\n\t\tconst divergenceProgram = new Program(baseVertexShader, divergenceShader);\n\t\tconst curlProgram = new Program(baseVertexShader, curlShader);\n\t\tconst vorticityProgram = new Program(baseVertexShader, vorticityShader);\n\t\tconst pressureProgram = new Program(baseVertexShader, pressureShader);\n\t\tconst gradienSubtractProgram = new Program(\n\t\t\tbaseVertexShader,\n\t\t\tgradientSubtractShader\n\t\t);\n\t\tconst displayMaterial = new Material(\n\t\t\tbaseVertexShader,\n\t\t\tdisplayShaderSource\n\t\t);\n\n\t\t// -------------------- FBO creation --------------------\n\t\tfunction createFBO(\n\t\t\tw: number,\n\t\t\th: number,\n\t\t\tinternalFormat: number,\n\t\t\tformat: number,\n\t\t\ttype: number,\n\t\t\tparam: number\n\t\t): FBO {\n\t\t\tgl.activeTexture(gl.TEXTURE0);\n\t\t\tconst texture = gl.createTexture()!;\n\t\t\tgl.bindTexture(gl.TEXTURE_2D, texture);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, param);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, param);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n\t\t\tgl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n\t\t\tgl.texImage2D(\n\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\t0,\n\t\t\t\tinternalFormat,\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t\t0,\n\t\t\t\tformat,\n\t\t\t\ttype,\n\t\t\t\tnull\n\t\t\t);\n\t\t\tconst fbo = gl.createFramebuffer()!;\n\t\t\tgl.bindFramebuffer(gl.FRAMEBUFFER, fbo);\n\t\t\tgl.framebufferTexture2D(\n\t\t\t\tgl.FRAMEBUFFER,\n\t\t\t\tgl.COLOR_ATTACHMENT0,\n\t\t\t\tgl.TEXTURE_2D,\n\t\t\t\ttexture,\n\t\t\t\t0\n\t\t\t);\n\t\t\tgl.viewport(0, 0, w, h);\n\t\t\tgl.clear(gl.COLOR_BUFFER_BIT);\n\n\t\t\tconst texelSizeX = 1 / w;\n\t\t\tconst texelSizeY = 1 / h;\n\n\t\t\treturn {\n\t\t\t\ttexture,\n\t\t\t\tfbo,\n\t\t\t\twidth: w,\n\t\t\t\theight: h,\n\t\t\t\ttexelSizeX,\n\t\t\t\ttexelSizeY,\n\t\t\t\tattach(id: number) {\n\t\t\t\t\tgl.activeTexture(gl.TEXTURE0 + id);\n\t\t\t\t\tgl.bindTexture(gl.TEXTURE_2D, texture);\n\t\t\t\t\treturn id;\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tfunction createDoubleFBO(\n\t\t\tw: number,\n\t\t\th: number,\n\t\t\tinternalFormat: number,\n\t\t\tformat: number,\n\t\t\ttype: number,\n\t\t\tparam: number\n\t\t): DoubleFBO {\n\t\t\tconst fbo1 = createFBO(w, h, internalFormat, format, type, param);\n\t\t\tconst fbo2 = createFBO(w, h, internalFormat, format, type, param);\n\t\t\treturn {\n\t\t\t\twidth: w,\n\t\t\t\theight: h,\n\t\t\t\ttexelSizeX: fbo1.texelSizeX,\n\t\t\t\ttexelSizeY: fbo1.texelSizeY,\n\t\t\t\tread: fbo1,\n\t\t\t\twrite: fbo2,\n\t\t\t\tswap() {\n\t\t\t\t\tconst tmp = this.read;\n\t\t\t\t\tthis.read = this.write;\n\t\t\t\t\tthis.write = tmp;\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tfunction resizeFBO(\n\t\t\ttarget: FBO,\n\t\t\tw: number,\n\t\t\th: number,\n\t\t\tinternalFormat: number,\n\t\t\tformat: number,\n\t\t\ttype: number,\n\t\t\tparam: number\n\t\t) {\n\t\t\tconst newFBO = createFBO(w, h, internalFormat, format, type, param);\n\t\t\tcopyProgram.bind();\n\t\t\tif (copyProgram.uniforms.uTexture)\n\t\t\t\tgl.uniform1i(copyProgram.uniforms.uTexture, target.attach(0));\n\t\t\tblit(newFBO, false);\n\t\t\treturn newFBO;\n\t\t}\n\n\t\tfunction resizeDoubleFBO(\n\t\t\ttarget: DoubleFBO,\n\t\t\tw: number,\n\t\t\th: number,\n\t\t\tinternalFormat: number,\n\t\t\tformat: number,\n\t\t\ttype: number,\n\t\t\tparam: number\n\t\t) {\n\t\t\tif (target.width === w && target.height === h) return target;\n\t\t\ttarget.read = resizeFBO(\n\t\t\t\ttarget.read,\n\t\t\t\tw,\n\t\t\t\th,\n\t\t\t\tinternalFormat,\n\t\t\t\tformat,\n\t\t\t\ttype,\n\t\t\t\tparam\n\t\t\t);\n\t\t\ttarget.write = createFBO(w, h, internalFormat, format, type, param);\n\t\t\ttarget.width = w;\n\t\t\ttarget.height = h;\n\t\t\ttarget.texelSizeX = 1 / w;\n\t\t\ttarget.texelSizeY = 1 / h;\n\t\t\treturn target;\n\t\t}\n\n\t\tfunction initFramebuffers() {\n\t\t\tconst simRes = getResolution(config.SIM_RESOLUTION!);\n\t\t\tconst dyeRes = getResolution(config.DYE_RESOLUTION!);\n\n\t\t\tconst texType = ext.halfFloatTexType;\n\t\t\tconst rgba = ext.formatRGBA;\n\t\t\tconst rg = ext.formatRG;\n\t\t\tconst r = ext.formatR;\n\t\t\tconst filtering = ext.supportLinearFiltering ? gl.LINEAR : gl.NEAREST;\n\t\t\tgl.disable(gl.BLEND);\n\n\t\t\tif (!dye) {\n\t\t\t\tdye = createDoubleFBO(\n\t\t\t\t\tdyeRes.width,\n\t\t\t\t\tdyeRes.height,\n\t\t\t\t\trgba.internalFormat,\n\t\t\t\t\trgba.format,\n\t\t\t\t\ttexType,\n\t\t\t\t\tfiltering\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tdye = resizeDoubleFBO(\n\t\t\t\t\tdye,\n\t\t\t\t\tdyeRes.width,\n\t\t\t\t\tdyeRes.height,\n\t\t\t\t\trgba.internalFormat,\n\t\t\t\t\trgba.format,\n\t\t\t\t\ttexType,\n\t\t\t\t\tfiltering\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (!velocity) {\n\t\t\t\tvelocity = createDoubleFBO(\n\t\t\t\t\tsimRes.width,\n\t\t\t\t\tsimRes.height,\n\t\t\t\t\trg.internalFormat,\n\t\t\t\t\trg.format,\n\t\t\t\t\ttexType,\n\t\t\t\t\tfiltering\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tvelocity = resizeDoubleFBO(\n\t\t\t\t\tvelocity,\n\t\t\t\t\tsimRes.width,\n\t\t\t\t\tsimRes.height,\n\t\t\t\t\trg.internalFormat,\n\t\t\t\t\trg.format,\n\t\t\t\t\ttexType,\n\t\t\t\t\tfiltering\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tdivergence = createFBO(\n\t\t\t\tsimRes.width,\n\t\t\t\tsimRes.height,\n\t\t\t\tr.internalFormat,\n\t\t\t\tr.format,\n\t\t\t\ttexType,\n\t\t\t\tgl.NEAREST\n\t\t\t);\n\t\t\tcurl = createFBO(\n\t\t\t\tsimRes.width,\n\t\t\t\tsimRes.height,\n\t\t\t\tr.internalFormat,\n\t\t\t\tr.format,\n\t\t\t\ttexType,\n\t\t\t\tgl.NEAREST\n\t\t\t);\n\t\t\tpressure = createDoubleFBO(\n\t\t\t\tsimRes.width,\n\t\t\t\tsimRes.height,\n\t\t\t\tr.internalFormat,\n\t\t\t\tr.format,\n\t\t\t\ttexType,\n\t\t\t\tgl.NEAREST\n\t\t\t);\n\t\t}\n\n\t\tfunction updateKeywords() {\n\t\t\tconst displayKeywords: string[] = [];\n\t\t\tif (config.SHADING) displayKeywords.push(\"SHADING\");\n\t\t\tdisplayMaterial.setKeywords(displayKeywords);\n\t\t}\n\n\t\tfunction getResolution(resolution: number) {\n\t\t\tconst w = gl.drawingBufferWidth;\n\t\t\tconst h = gl.drawingBufferHeight;\n\t\t\tconst aspectRatio = w / h;\n\t\t\tlet aspect = aspectRatio < 1 ? 1 / aspectRatio : aspectRatio;\n\t\t\tconst min = Math.round(resolution);\n\t\t\tconst max = Math.round(resolution * aspect);\n\t\t\tif (w > h) {\n\t\t\t\treturn { width: max, height: min };\n\t\t\t}\n\t\t\treturn { width: min, height: max };\n\t\t}\n\n\t\tfunction scaleByPixelRatio(input: number) {\n\t\t\tconst pixelRatio = window.devicePixelRatio || 1;\n\t\t\treturn Math.floor(input * pixelRatio);\n\t\t}\n\n\t\t// -------------------- Simulation Setup --------------------\n\t\tupdateKeywords();\n\t\tinitFramebuffers();\n\n\t\tlet lastUpdateTime = Date.now();\n\t\tlet colorUpdateTimer = 0.0;\n\n\t\tfunction updateFrame() {\n\t\t\tconst dt = calcDeltaTime();\n\t\t\tif (resizeCanvas()) initFramebuffers();\n\t\t\tupdateColors(dt);\n\t\t\tapplyInputs();\n\t\t\tstep(dt);\n\t\t\trender(null);\n\t\t\trequestAnimationFrame(updateFrame);\n\t\t}\n\n\t\tfunction calcDeltaTime() {\n\t\t\tconst now = Date.now();\n\t\t\tlet dt = (now - lastUpdateTime) / 1000;\n\t\t\tdt = Math.min(dt, 0.016666);\n\t\t\tlastUpdateTime = now;\n\t\t\treturn dt;\n\t\t}\n\n\t\tfunction resizeCanvas() {\n\t\t\tconst width = scaleByPixelRatio(canvas!.clientWidth);\n\t\t\tconst height = scaleByPixelRatio(canvas!.clientHeight);\n\t\t\tif (canvas!.width !== width || canvas!.height !== height) {\n\t\t\t\tcanvas!.width = width;\n\t\t\t\tcanvas!.height = height;\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction updateColors(dt: number) {\n\t\t\tcolorUpdateTimer += dt * config.COLOR_UPDATE_SPEED;\n\t\t\tif (colorUpdateTimer >= 1) {\n\t\t\t\tcolorUpdateTimer = wrap(colorUpdateTimer, 0, 1);\n\t\t\t\tpointers.forEach((p) => {\n\t\t\t\t\tp.color = generateColor();\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\tfunction applyInputs() {\n\t\t\tfor (const p of pointers) {\n\t\t\t\tif (p.moved) {\n\t\t\t\t\tp.moved = false;\n\t\t\t\t\tsplatPointer(p);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction step(dt: number) {\n\t\t\tgl.disable(gl.BLEND);\n\n\t\t\t// Curl\n\t\t\tcurlProgram.bind();\n\t\t\tif (curlProgram.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tcurlProgram.uniforms.texelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (curlProgram.uniforms.uVelocity) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tcurlProgram.uniforms.uVelocity,\n\t\t\t\t\tvelocity.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(curl);\n\n\t\t\t// Vorticity\n\t\t\tvorticityProgram.bind();\n\t\t\tif (vorticityProgram.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tvorticityProgram.uniforms.texelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (vorticityProgram.uniforms.uVelocity) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tvorticityProgram.uniforms.uVelocity,\n\t\t\t\t\tvelocity.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (vorticityProgram.uniforms.uCurl) {\n\t\t\t\tgl.uniform1i(vorticityProgram.uniforms.uCurl, curl.attach(1));\n\t\t\t}\n\t\t\tif (vorticityProgram.uniforms.curl) {\n\t\t\t\tgl.uniform1f(vorticityProgram.uniforms.curl, config.CURL);\n\t\t\t}\n\t\t\tif (vorticityProgram.uniforms.dt) {\n\t\t\t\tgl.uniform1f(vorticityProgram.uniforms.dt, dt);\n\t\t\t}\n\t\t\tblit(velocity.write);\n\t\t\tvelocity.swap();\n\n\t\t\t// Divergence\n\t\t\tdivergenceProgram.bind();\n\t\t\tif (divergenceProgram.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tdivergenceProgram.uniforms.texelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (divergenceProgram.uniforms.uVelocity) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tdivergenceProgram.uniforms.uVelocity,\n\t\t\t\t\tvelocity.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(divergence);\n\n\t\t\t// Clear pressure\n\t\t\tclearProgram.bind();\n\t\t\tif (clearProgram.uniforms.uTexture) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tclearProgram.uniforms.uTexture,\n\t\t\t\t\tpressure.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (clearProgram.uniforms.value) {\n\t\t\t\tgl.uniform1f(clearProgram.uniforms.value, config.PRESSURE);\n\t\t\t}\n\t\t\tblit(pressure.write);\n\t\t\tpressure.swap();\n\n\t\t\t// Pressure\n\t\t\tpressureProgram.bind();\n\t\t\tif (pressureProgram.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tpressureProgram.uniforms.texelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (pressureProgram.uniforms.uDivergence) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tpressureProgram.uniforms.uDivergence,\n\t\t\t\t\tdivergence.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tfor (let i = 0; i < config.PRESSURE_ITERATIONS; i++) {\n\t\t\t\tif (pressureProgram.uniforms.uPressure) {\n\t\t\t\t\tgl.uniform1i(\n\t\t\t\t\t\tpressureProgram.uniforms.uPressure,\n\t\t\t\t\t\tpressure.read.attach(1)\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tblit(pressure.write);\n\t\t\t\tpressure.swap();\n\t\t\t}\n\n\t\t\t// Gradient Subtract\n\t\t\tgradienSubtractProgram.bind();\n\t\t\tif (gradienSubtractProgram.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tgradienSubtractProgram.uniforms.texelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (gradienSubtractProgram.uniforms.uPressure) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tgradienSubtractProgram.uniforms.uPressure,\n\t\t\t\t\tpressure.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (gradienSubtractProgram.uniforms.uVelocity) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tgradienSubtractProgram.uniforms.uVelocity,\n\t\t\t\t\tvelocity.read.attach(1)\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(velocity.write);\n\t\t\tvelocity.swap();\n\n\t\t\t// Advection - velocity\n\t\t\tadvectionProgram.bind();\n\t\t\tif (advectionProgram.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tadvectionProgram.uniforms.texelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (\n\t\t\t\t!ext.supportLinearFiltering &&\n\t\t\t\tadvectionProgram.uniforms.dyeTexelSize\n\t\t\t) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tadvectionProgram.uniforms.dyeTexelSize,\n\t\t\t\t\tvelocity.texelSizeX,\n\t\t\t\t\tvelocity.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst velocityId = velocity.read.attach(0);\n\t\t\tif (advectionProgram.uniforms.uVelocity) {\n\t\t\t\tgl.uniform1i(advectionProgram.uniforms.uVelocity, velocityId);\n\t\t\t}\n\t\t\tif (advectionProgram.uniforms.uSource) {\n\t\t\t\tgl.uniform1i(advectionProgram.uniforms.uSource, velocityId);\n\t\t\t}\n\t\t\tif (advectionProgram.uniforms.dt) {\n\t\t\t\tgl.uniform1f(advectionProgram.uniforms.dt, dt);\n\t\t\t}\n\t\t\tif (advectionProgram.uniforms.dissipation) {\n\t\t\t\tgl.uniform1f(\n\t\t\t\t\tadvectionProgram.uniforms.dissipation,\n\t\t\t\t\tconfig.VELOCITY_DISSIPATION\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(velocity.write);\n\t\t\tvelocity.swap();\n\n\t\t\t// Advection - dye\n\t\t\tif (\n\t\t\t\t!ext.supportLinearFiltering &&\n\t\t\t\tadvectionProgram.uniforms.dyeTexelSize\n\t\t\t) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tadvectionProgram.uniforms.dyeTexelSize,\n\t\t\t\t\tdye.texelSizeX,\n\t\t\t\t\tdye.texelSizeY\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (advectionProgram.uniforms.uVelocity) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tadvectionProgram.uniforms.uVelocity,\n\t\t\t\t\tvelocity.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (advectionProgram.uniforms.uSource) {\n\t\t\t\tgl.uniform1i(advectionProgram.uniforms.uSource, dye.read.attach(1));\n\t\t\t}\n\t\t\tif (advectionProgram.uniforms.dissipation) {\n\t\t\t\tgl.uniform1f(\n\t\t\t\t\tadvectionProgram.uniforms.dissipation,\n\t\t\t\t\tconfig.DENSITY_DISSIPATION\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(dye.write);\n\t\t\tdye.swap();\n\t\t}\n\n\t\tfunction render(target: FBO | null) {\n\t\t\tgl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n\t\t\tgl.enable(gl.BLEND);\n\t\t\tdrawDisplay(target);\n\t\t}\n\n\t\tfunction drawDisplay(target: FBO | null) {\n\t\t\tconst width = target ? target.width : gl.drawingBufferWidth;\n\t\t\tconst height = target ? target.height : gl.drawingBufferHeight;\n\t\t\tdisplayMaterial.bind();\n\t\t\tif (config.SHADING && displayMaterial.uniforms.texelSize) {\n\t\t\t\tgl.uniform2f(\n\t\t\t\t\tdisplayMaterial.uniforms.texelSize,\n\t\t\t\t\t1 / width,\n\t\t\t\t\t1 / height\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (displayMaterial.uniforms.uTexture) {\n\t\t\t\tgl.uniform1i(displayMaterial.uniforms.uTexture, dye.read.attach(0));\n\t\t\t}\n\t\t\tblit(target, false);\n\t\t}\n\n\t\t// -------------------- Interaction --------------------\n\t\tfunction splatPointer(pointer: Pointer) {\n\t\t\tconst dx = pointer.deltaX * config.SPLAT_FORCE;\n\t\t\tconst dy = pointer.deltaY * config.SPLAT_FORCE;\n\t\t\tsplat(pointer.texcoordX, pointer.texcoordY, dx, dy, pointer.color);\n\t\t}\n\n\t\tfunction clickSplat(pointer: Pointer) {\n\t\t\tconst color = generateColor();\n\t\t\tcolor.r *= 10;\n\t\t\tcolor.g *= 10;\n\t\t\tcolor.b *= 10;\n\t\t\tconst dx = 10 * (Math.random() - 0.5);\n\t\t\tconst dy = 30 * (Math.random() - 0.5);\n\t\t\tsplat(pointer.texcoordX, pointer.texcoordY, dx, dy, color);\n\t\t}\n\n\t\tfunction splat(\n\t\t\tx: number,\n\t\t\ty: number,\n\t\t\tdx: number,\n\t\t\tdy: number,\n\t\t\tcolor: ColorRGB\n\t\t) {\n\t\t\tsplatProgram.bind();\n\t\t\tif (splatProgram.uniforms.uTarget) {\n\t\t\t\tgl.uniform1i(\n\t\t\t\t\tsplatProgram.uniforms.uTarget,\n\t\t\t\t\tvelocity.read.attach(0)\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (splatProgram.uniforms.aspectRatio) {\n\t\t\t\tgl.uniform1f(\n\t\t\t\t\tsplatProgram.uniforms.aspectRatio,\n\t\t\t\t\tcanvas!.width / canvas!.height\n\t\t\t\t);\n\t\t\t}\n\t\t\tif (splatProgram.uniforms.point) {\n\t\t\t\tgl.uniform2f(splatProgram.uniforms.point, x, y);\n\t\t\t}\n\t\t\tif (splatProgram.uniforms.color) {\n\t\t\t\tgl.uniform3f(splatProgram.uniforms.color, dx, dy, 0);\n\t\t\t}\n\t\t\tif (splatProgram.uniforms.radius) {\n\t\t\t\tgl.uniform1f(\n\t\t\t\t\tsplatProgram.uniforms.radius,\n\t\t\t\t\tcorrectRadius(config.SPLAT_RADIUS / 100)!\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(velocity.write);\n\t\t\tvelocity.swap();\n\n\t\t\tif (splatProgram.uniforms.uTarget) {\n\t\t\t\tgl.uniform1i(splatProgram.uniforms.uTarget, dye.read.attach(0));\n\t\t\t}\n\t\t\tif (splatProgram.uniforms.color) {\n\t\t\t\tgl.uniform3f(\n\t\t\t\t\tsplatProgram.uniforms.color,\n\t\t\t\t\tcolor.r,\n\t\t\t\t\tcolor.g,\n\t\t\t\t\tcolor.b\n\t\t\t\t);\n\t\t\t}\n\t\t\tblit(dye.write);\n\t\t\tdye.swap();\n\t\t}\n\n\t\tfunction correctRadius(radius: number) {\n\t\t\t// Use non-null assertion (canvas can't be null here)\n\t\t\tconst aspectRatio = canvas!.width / canvas!.height;\n\t\t\tif (aspectRatio > 1) radius *= aspectRatio;\n\t\t\treturn radius;\n\t\t}\n\n\t\tfunction updatePointerDownData(\n\t\t\tpointer: Pointer,\n\t\t\tid: number,\n\t\t\tposX: number,\n\t\t\tposY: number\n\t\t) {\n\t\t\tpointer.id = id;\n\t\t\tpointer.down = true;\n\t\t\tpointer.moved = false;\n\t\t\tpointer.texcoordX = posX / canvas!.width;\n\t\t\tpointer.texcoordY = 1 - posY / canvas!.height;\n\t\t\tpointer.prevTexcoordX = pointer.texcoordX;\n\t\t\tpointer.prevTexcoordY = pointer.texcoordY;\n\t\t\tpointer.deltaX = 0;\n\t\t\tpointer.deltaY = 0;\n\t\t\tpointer.color = generateColor();\n\t\t}\n\n\t\tfunction updatePointerMoveData(\n\t\t\tpointer: Pointer,\n\t\t\tposX: number,\n\t\t\tposY: number,\n\t\t\tcolor: ColorRGB\n\t\t) {\n\t\t\tpointer.prevTexcoordX = pointer.texcoordX;\n\t\t\tpointer.prevTexcoordY = pointer.texcoordY;\n\t\t\tpointer.texcoordX = posX / canvas!.width;\n\t\t\tpointer.texcoordY = 1 - posY / canvas!.height;\n\t\t\tpointer.deltaX = correctDeltaX(\n\t\t\t\tpointer.texcoordX - pointer.prevTexcoordX\n\t\t\t)!;\n\t\t\tpointer.deltaY = correctDeltaY(\n\t\t\t\tpointer.texcoordY - pointer.prevTexcoordY\n\t\t\t)!;\n\t\t\tpointer.moved =\n\t\t\t\tMath.abs(pointer.deltaX) > 0 || Math.abs(pointer.deltaY) > 0;\n\t\t\tpointer.color = color;\n\t\t}\n\n\t\tfunction updatePointerUpData(pointer: Pointer) {\n\t\t\tpointer.down = false;\n\t\t}\n\n\t\tfunction correctDeltaX(delta: number) {\n\t\t\tconst aspectRatio = canvas!.width / canvas!.height;\n\t\t\tif (aspectRatio < 1) delta *= aspectRatio;\n\t\t\treturn delta;\n\t\t}\n\n\t\tfunction correctDeltaY(delta: number) {\n\t\t\tconst aspectRatio = canvas!.width / canvas!.height;\n\t\t\tif (aspectRatio > 1) delta /= aspectRatio;\n\t\t\treturn delta;\n\t\t}\n\n\t\tfunction generateColor(): ColorRGB {\n\t\t\tconst c = HSVtoRGB(Math.random(), 1.0, 1.0);\n\t\t\tc.r *= 0.15;\n\t\t\tc.g *= 0.15;\n\t\t\tc.b *= 0.15;\n\t\t\treturn c;\n\t\t}\n\n\t\tfunction HSVtoRGB(h: number, s: number, v: number): ColorRGB {\n\t\t\tlet r = 0,\n\t\t\t\tg = 0,\n\t\t\t\tb = 0;\n\t\t\tconst i = Math.floor(h * 6);\n\t\t\tconst f = h * 6 - i;\n\t\t\tconst p = v * (1 - s);\n\t\t\tconst q = v * (1 - f * s);\n\t\t\tconst t = v * (1 - (1 - f) * s);\n\n\t\t\tswitch (i % 6) {\n\t\t\t\tcase 0:\n\t\t\t\t\tr = v;\n\t\t\t\t\tg = t;\n\t\t\t\t\tb = p;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tr = q;\n\t\t\t\t\tg = v;\n\t\t\t\t\tb = p;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tr = p;\n\t\t\t\t\tg = v;\n\t\t\t\t\tb = t;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 3:\n\t\t\t\t\tr = p;\n\t\t\t\t\tg = q;\n\t\t\t\t\tb = v;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tr = t;\n\t\t\t\t\tg = p;\n\t\t\t\t\tb = v;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 5:\n\t\t\t\t\tr = v;\n\t\t\t\t\tg = p;\n\t\t\t\t\tb = q;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t\treturn { r, g, b };\n\t\t}\n\n\t\tfunction wrap(value: number, min: number, max: number) {\n\t\t\tconst range = max - min;\n\t\t\tif (range === 0) return min;\n\t\t\treturn ((value - min) % range) + min;\n\t\t}\n\n\t\t// -------------------- Event Listeners --------------------\n\t\twindow.addEventListener(\"mousedown\", (e) => {\n\t\t\tconst pointer = pointers?.[0];\n\t\t\tconst posX = scaleByPixelRatio(e.clientX);\n\t\t\tconst posY = scaleByPixelRatio(e.clientY);\n\t\t\tupdatePointerDownData(pointer, -1, posX, posY);\n\t\t\tclickSplat(pointer);\n\t\t});\n\n\t\t// Start rendering on first mouse move\n\t\tfunction handleFirstMouseMove(e: MouseEvent) {\n\t\t\tconst pointer = pointers?.[0];\n\t\t\tconst posX = scaleByPixelRatio(e.clientX);\n\t\t\tconst posY = scaleByPixelRatio(e.clientY);\n\t\t\tconst color = generateColor();\n\t\t\tupdateFrame();\n\t\t\tupdatePointerMoveData(pointer, posX, posY, color);\n\t\t\tdocument.body.removeEventListener(\"mousemove\", handleFirstMouseMove);\n\t\t}\n\t\tdocument.body.addEventListener(\"mousemove\", handleFirstMouseMove);\n\n\t\twindow.addEventListener(\"mousemove\", (e) => {\n\t\t\tconst pointer = pointers?.[0];\n\t\t\tconst posX = scaleByPixelRatio(e.clientX);\n\t\t\tconst posY = scaleByPixelRatio(e.clientY);\n\t\t\tconst color = pointer.color;\n\t\t\tupdatePointerMoveData(pointer, posX, posY, color);\n\t\t});\n\n\t\t// Start rendering on first touch\n\t\tfunction handleFirstTouchStart(e: TouchEvent) {\n\t\t\tconst touches = e.targetTouches;\n\t\t\tconst pointer = pointers?.[0];\n\t\t\tfor (let i = 0; i < touches.length; i++) {\n\t\t\t\tconst posX = scaleByPixelRatio(touches[i].clientX);\n\t\t\t\tconst posY = scaleByPixelRatio(touches[i].clientY);\n\t\t\t\tupdateFrame();\n\t\t\t\tupdatePointerDownData(pointer, touches[i].identifier, posX, posY);\n\t\t\t}\n\t\t\tdocument.body.removeEventListener(\"touchstart\", handleFirstTouchStart);\n\t\t}\n\t\tdocument.body.addEventListener(\"touchstart\", handleFirstTouchStart);\n\n\t\twindow.addEventListener(\n\t\t\t\"touchstart\",\n\t\t\t(e) => {\n\t\t\t\tconst touches = e.targetTouches;\n\t\t\t\tconst pointer = pointers?.[0];\n\t\t\t\tfor (let i = 0; i < touches.length; i++) {\n\t\t\t\t\tconst posX = scaleByPixelRatio(touches[i].clientX);\n\t\t\t\t\tconst posY = scaleByPixelRatio(touches[i].clientY);\n\t\t\t\t\tupdatePointerDownData(\n\t\t\t\t\t\tpointer,\n\t\t\t\t\t\ttouches[i].identifier,\n\t\t\t\t\t\tposX,\n\t\t\t\t\t\tposY\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t},\n\t\t\tfalse\n\t\t);\n\n\t\twindow.addEventListener(\n\t\t\t\"touchmove\",\n\t\t\t(e) => {\n\t\t\t\tconst touches = e.targetTouches;\n\t\t\t\tconst pointer = pointers?.[0];\n\t\t\t\tfor (let i = 0; i < touches.length; i++) {\n\t\t\t\t\tconst posX = scaleByPixelRatio(touches[i].clientX);\n\t\t\t\t\tconst posY = scaleByPixelRatio(touches[i].clientY);\n\t\t\t\t\tupdatePointerMoveData(pointer, posX, posY, pointer.color);\n\t\t\t\t}\n\t\t\t},\n\t\t\tfalse\n\t\t);\n\n\t\twindow.addEventListener(\"touchend\", (e) => {\n\t\t\tconst touches = e.changedTouches;\n\t\t\tconst pointer = pointers?.[0];\n\t\t\tfor (let i = 0; i < touches.length; i++) {\n\t\t\t\tupdatePointerUpData(pointer);\n\t\t\t}\n\t\t});\n\t\t// ------------------------------------------------------------\n\t}, [\n\t\tSIM_RESOLUTION,\n\t\tDYE_RESOLUTION,\n\t\tCAPTURE_RESOLUTION,\n\t\tDENSITY_DISSIPATION,\n\t\tVELOCITY_DISSIPATION,\n\t\tPRESSURE,\n\t\tPRESSURE_ITERATIONS,\n\t\tCURL,\n\t\tSPLAT_RADIUS,\n\t\tSPLAT_FORCE,\n\t\tSHADING,\n\t\tCOLOR_UPDATE_SPEED,\n\t\tBACK_COLOR,\n\t\tTRANSPARENT,\n\t]);\n\n\treturn (\n\t\t<div className=\"fixed top-0 left-0 z-50 pointer-events-none w-full h-full cursor-none\">\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\tid=\"fluid\"\n\t\t\t\tclassName=\"w-screen h-screen block\"\n\t\t\t></canvas>\n\t\t</div>\n\t);\n}\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/button.tsx",
      "content": "import * as React from \"react\";\r\n\r\nimport { cn } from \"@/registry/utilities/cn\";\r\nimport { Slot } from \"@radix-ui/react-slot\";\r\nimport { cva, type VariantProps } from \"class-variance-authority\";\r\n\r\nconst buttonVariants = cva(\r\n\t\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm text-white hover:text-gray-400 font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\",\r\n\t{\r\n\t\tvariants: {\r\n\t\t\tvariant: {\r\n\t\t\t\tsimple:\r\n\t\t\t\t\t\"bg-secondary relative z-10 bg-transparent hover:border-secondary hover:bg-secondary/50  border border-transparent dark:text-white text-sm md:text-sm transition font-medium duration-200  rounded-md px-4 py-2  flex items-center justify-center\",\r\n\t\t\t\tdefault: \"bg-primary text-primary-foreground hover:bg-primary/90\",\r\n\t\t\t\tdestructive:\r\n\t\t\t\t\t\"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\r\n\t\t\t\toutline:\r\n\t\t\t\t\t\"border border-input bg-background hover:bg-accent hover:text-accent-foreground\",\r\n\t\t\t\tsecondary:\r\n\t\t\t\t\t\"bg-secondary text-secondary-foreground hover:bg-secondary/80\",\r\n\t\t\t\tghost: \"hover:bg-accent hover:text-black hover:stroke-black dark:text-white text-black\",\r\n\t\t\t\tlink: \"text-primary underline-offset-4 hover:underline\",\r\n\t\t\t},\r\n\t\t\tsize: {\r\n\t\t\t\tdefault: \"h-10 px-4 py-2\",\r\n\t\t\t\tsm: \"h-9 rounded-md px-3\",\r\n\t\t\t\tlg: \"h-11 rounded-md px-8\",\r\n\t\t\t\ticon: \"h-10 w-10\",\r\n\t\t\t},\r\n\t\t},\r\n\t\tdefaultVariants: {\r\n\t\t\tvariant: \"default\",\r\n\t\t\tsize: \"default\",\r\n\t\t},\r\n\t}\r\n);\r\n\r\nexport interface ButtonProps\r\n\textends React.ButtonHTMLAttributes<HTMLButtonElement>,\r\n\t\tVariantProps<typeof buttonVariants> {\r\n\tasChild?: boolean;\r\n}\r\n\r\nconst Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\r\n\t({ className, variant, size, asChild = false, ...props }, ref) => {\r\n\t\tconst Comp = asChild ? Slot : \"button\";\r\n\t\treturn (\r\n\t\t\t<Comp\r\n\t\t\t\tclassName={cn(buttonVariants({ variant, size, className }))}\r\n\t\t\t\tref={ref}\r\n\t\t\t\t{...props}\r\n\t\t\t/>\r\n\t\t);\r\n\t}\r\n);\r\nButton.displayName = \"Button\";\r\n\r\nexport { Button, buttonVariants };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "registry/utilities/cn.ts",
      "content": "import { ClassValue, clsx } from \"clsx\";\r\nimport { twMerge } from \"tailwind-merge\";\r\n\r\nexport function cn(...inputs: ClassValue[]) {\r\n\treturn twMerge(clsx(inputs));\r\n}\r\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/label.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport { cn } from \"@/registry/utilities/cn\";\nimport * as LabelPrimitive from \"@radix-ui/react-label\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nconst labelVariants = cva(\n\t\"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70\"\n);\n\nconst Label = React.forwardRef<\n\tReact.ElementRef<typeof LabelPrimitive.Root>,\n\tReact.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &\n\t\tVariantProps<typeof labelVariants>\n>(({ className, ...props }, ref) => (\n\t<LabelPrimitive.Root\n\t\tref={ref}\n\t\tclassName={cn(labelVariants(), className)}\n\t\t{...props}\n\t/>\n));\nLabel.displayName = LabelPrimitive.Root.displayName;\n\nexport { Label };\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/slider.tsx",
      "content": "\"use client\";\r\n\r\nimport React from \"react\";\r\n\r\nimport { cn } from \"@/registry/utilities/cn\";\r\nimport * as SliderPrimitive from \"@radix-ui/react-slider\";\r\n\r\nconst Slider = React.forwardRef<\r\n\tReact.ElementRef<typeof SliderPrimitive.Root>,\r\n\tReact.ComponentPropsWithoutRef<typeof SliderPrimitive.Root>\r\n>(({ className, ...props }, ref) => (\r\n\t<SliderPrimitive.Root\r\n\t\tref={ref}\r\n\t\tclassName={cn(\r\n\t\t\t\"relative flex w-full touch-none select-none items-center\",\r\n\t\t\tclassName\r\n\t\t)}\r\n\t\t{...props}\r\n\t>\r\n\t\t<SliderPrimitive.Track className=\"relative h-2 w-full grow overflow-hidden rounded-full bg-secondary\">\r\n\t\t\t<SliderPrimitive.Range className=\"absolute h-full bg-primary\" />\r\n\t\t</SliderPrimitive.Track>\r\n\t\t<SliderPrimitive.Thumb className=\"block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\" />\r\n\t\t<SliderPrimitive.Thumb className=\"block h-5 w-5 rounded-full border-2 border-primary bg-background ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50\" />\r\n\t</SliderPrimitive.Root>\r\n));\r\nSlider.displayName = SliderPrimitive.Root.displayName;\r\n\r\nexport { Slider };\r\n",
      "type": "registry:ui"
    },
    {
      "path": "components/ui/tabs.tsx",
      "content": "\"use client\";\n\nimport React from \"react\";\n\nimport { cn } from \"@/registry/utilities/cn\";\nimport * as TabsPrimitive from \"@radix-ui/react-tabs\";\n\nconst Tabs = TabsPrimitive.Root;\n\nconst TabsList = React.forwardRef<\n\tReact.ElementRef<typeof TabsPrimitive.List>,\n\tReact.ComponentPropsWithoutRef<typeof TabsPrimitive.List>\n>(({ className, ...props }, ref) => (\n\t<TabsPrimitive.List\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground\",\n\t\t\tclassName\n\t\t)}\n\t\t{...props}\n\t/>\n));\nTabsList.displayName = TabsPrimitive.List.displayName;\n\nconst TabsTrigger = React.forwardRef<\n\tReact.ElementRef<typeof TabsPrimitive.Trigger>,\n\tReact.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>\n>(({ className, ...props }, ref) => (\n\t<TabsPrimitive.Trigger\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-xs\",\n\t\t\tclassName\n\t\t)}\n\t\t{...props}\n\t/>\n));\nTabsTrigger.displayName = TabsPrimitive.Trigger.displayName;\n\nconst TabsContent = React.forwardRef<\n\tReact.ElementRef<typeof TabsPrimitive.Content>,\n\tReact.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>\n>(({ className, ...props }, ref) => (\n\t<TabsPrimitive.Content\n\t\tref={ref}\n\t\tclassName={cn(\n\t\t\t\"mt-2 ring-offset-background focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\",\n\t\t\tclassName\n\t\t)}\n\t\t{...props}\n\t/>\n));\nTabsContent.displayName = TabsPrimitive.Content.displayName;\n\nexport { Tabs, TabsList, TabsTrigger, TabsContent };\n",
      "type": "registry:ui"
    }
  ]
}