{
	"$schema": "https://shadcn-svelte.com/schema/registry-item.json",
	"name": "voice-chat-02",
	"title": "Voice Chat 02",
	"type": "registry:block",
	"description": "A compact voice-chat card with an animated orb, connection status, and call toggle.",
	"devDependencies": [
		"@lucide/svelte@^1.7.0"
	],
	"registryDependencies": [
		"button",
		"card",
		"https://sv11.ui.twango.dev/r/orb.json",
		"https://sv11.ui.twango.dev/r/shimmering-text.json"
	],
	"files": [
		{
			"content": "<script lang=\"ts\">\n\timport { fly } from \"svelte/transition\";\n\timport Loader2Icon from \"@lucide/svelte/icons/loader-2\";\n\timport PhoneIcon from \"@lucide/svelte/icons/phone\";\n\timport PhoneOffIcon from \"@lucide/svelte/icons/phone-off\";\n\timport { Button } from \"$UI$/button/index.js\";\n\timport { Card } from \"$UI$/card/index.js\";\n\timport { Orb } from \"$UI$/orb/index.js\";\n\timport { ShimmeringText } from \"$UI$/shimmering-text/index.js\";\n\timport { cn } from \"$UTILS$.js\";\n\n\tconst DEFAULT_AGENT = {\n\t\tname: \"Customer Support\",\n\t\tdescription: \"Tap to start voice chat\",\n\t};\n\n\ttype AgentState = \"disconnected\" | \"connecting\" | \"connected\" | \"disconnecting\" | null;\n\n\tlet agentState = $state<AgentState>(\"disconnected\");\n\tlet errorMessage = $state<string | null>(null);\n\tlet timers: ReturnType<typeof setTimeout>[] = [];\n\n\tfunction clearTimers() {\n\t\ttimers.forEach(clearTimeout);\n\t\ttimers = [];\n\t}\n\n\tfunction startConversation() {\n\t\terrorMessage = null;\n\t\tagentState = \"connecting\";\n\t\ttimers.push(\n\t\t\tsetTimeout(() => {\n\t\t\t\tagentState = \"connected\";\n\t\t\t}, 1500)\n\t\t);\n\t}\n\n\tfunction endConversation() {\n\t\tagentState = \"disconnecting\";\n\t\ttimers.push(\n\t\t\tsetTimeout(() => {\n\t\t\t\tagentState = \"disconnected\";\n\t\t\t}, 1000)\n\t\t);\n\t}\n\n\tfunction handleCall() {\n\t\tclearTimers();\n\t\tif (agentState === \"disconnected\" || agentState === null) {\n\t\t\tstartConversation();\n\t\t} else if (agentState === \"connected\") {\n\t\t\tendConversation();\n\t\t}\n\t}\n\n\t$effect(() => {\n\t\treturn () => clearTimers();\n\t});\n\n\tconst isCallActive = $derived(agentState === \"connected\");\n\tconst isTransitioning = $derived(agentState === \"connecting\" || agentState === \"disconnecting\");\n\n\t// Canned volume: smooth sine waves when connected, zero otherwise.\n\tfunction getInputVolume(): number {\n\t\tif (agentState !== \"connected\") return 0;\n\t\treturn 0.3 + 0.25 * Math.sin(performance.now() / 500);\n\t}\n\n\tfunction getOutputVolume(): number {\n\t\tif (agentState !== \"connected\") return 0;\n\t\treturn 0.3 + 0.25 * Math.sin(performance.now() / 700 + 1);\n\t}\n</script>\n\n<Card class=\"flex h-[400px] w-full flex-col items-center justify-center overflow-hidden p-6\">\n\t<div class=\"flex flex-col items-center gap-6\">\n\t\t<div class=\"relative size-32\">\n\t\t\t<div\n\t\t\t\tclass=\"bg-muted relative h-full w-full rounded-full p-1 shadow-[inset_0_2px_8px_rgba(0,0,0,0.1)] dark:shadow-[inset_0_2px_8px_rgba(0,0,0,0.5)]\"\n\t\t\t>\n\t\t\t\t<div\n\t\t\t\t\tclass=\"bg-background h-full w-full overflow-hidden rounded-full shadow-[inset_0_0_12px_rgba(0,0,0,0.05)] dark:shadow-[inset_0_0_12px_rgba(0,0,0,0.3)]\"\n\t\t\t\t>\n\t\t\t\t\t<Orb class=\"h-full w-full\" volumeMode=\"manual\" {getInputVolume} {getOutputVolume} />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\n\t\t<div class=\"flex flex-col items-center gap-2\">\n\t\t\t<h2 class=\"text-xl font-semibold\">{DEFAULT_AGENT.name}</h2>\n\t\t\t{#if errorMessage}\n\t\t\t\t<p\n\t\t\t\t\tclass=\"text-destructive text-center text-sm\"\n\t\t\t\t\tin:fly={{ y: -10, duration: 200 }}\n\t\t\t\t\tout:fly={{ y: 10, duration: 200 }}\n\t\t\t\t>\n\t\t\t\t\t{errorMessage}\n\t\t\t\t</p>\n\t\t\t{:else if agentState === \"disconnected\" || agentState === null}\n\t\t\t\t<p\n\t\t\t\t\tclass=\"text-muted-foreground text-sm\"\n\t\t\t\t\tin:fly={{ y: -10, duration: 200 }}\n\t\t\t\t\tout:fly={{ y: 10, duration: 200 }}\n\t\t\t\t>\n\t\t\t\t\t{DEFAULT_AGENT.description}\n\t\t\t\t</p>\n\t\t\t{:else}\n\t\t\t\t<div\n\t\t\t\t\tclass=\"flex items-center gap-2\"\n\t\t\t\t\tin:fly={{ y: -10, duration: 200 }}\n\t\t\t\t\tout:fly={{ y: 10, duration: 200 }}\n\t\t\t\t>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclass={cn(\n\t\t\t\t\t\t\t\"h-2 w-2 rounded-full transition-all duration-300\",\n\t\t\t\t\t\t\tagentState === \"connected\" && \"bg-green-500\",\n\t\t\t\t\t\t\tisTransitioning && \"bg-primary/60 animate-pulse\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t></div>\n\t\t\t\t\t<span class=\"text-sm capitalize\">\n\t\t\t\t\t\t{#if isTransitioning}\n\t\t\t\t\t\t\t<ShimmeringText text={agentState} />\n\t\t\t\t\t\t{:else}\n\t\t\t\t\t\t\t<span class=\"text-green-600\">Connected</span>\n\t\t\t\t\t\t{/if}\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t{/if}\n\t\t</div>\n\n\t\t<Button\n\t\t\tonclick={handleCall}\n\t\t\tdisabled={isTransitioning}\n\t\t\tsize=\"icon\"\n\t\t\tvariant={isCallActive ? \"secondary\" : \"default\"}\n\t\t\tclass=\"h-12 w-12 rounded-full\"\n\t\t>\n\t\t\t{#if isTransitioning}\n\t\t\t\t<Loader2Icon class=\"h-5 w-5 animate-spin\" />\n\t\t\t{:else if isCallActive}\n\t\t\t\t<PhoneOffIcon class=\"h-5 w-5\" />\n\t\t\t{:else}\n\t\t\t\t<PhoneIcon class=\"h-5 w-5\" />\n\t\t\t{/if}\n\t\t</Button>\n\t</div>\n</Card>\n",
			"type": "registry:block",
			"target": "components/blocks/voice-chat-02/voice-chat-02.svelte"
		},
		{
			"content": "import VoiceChat02 from \"./voice-chat-02.svelte\";\n\nexport { VoiceChat02, VoiceChat02 as default };\n",
			"type": "registry:block",
			"target": "components/blocks/voice-chat-02/index.ts"
		}
	]
}