From b4a30080b79c6ce40af2a95c227b562e09e5aad5 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Sat, 11 Jan 2025 17:29:13 +0800 Subject: [PATCH 1/9] add push to talk button --- app/globals.css | 17 + app/page.tsx | 28 +- components/PushToTalkButton.tsx | 76 +++ pnpm-lock.yaml | 1064 ++++++++++++++++++------------- 4 files changed, 714 insertions(+), 471 deletions(-) create mode 100644 components/PushToTalkButton.tsx diff --git a/app/globals.css b/app/globals.css index 9edb69a78..db3a0709b 100644 --- a/app/globals.css +++ b/app/globals.css @@ -18,3 +18,20 @@ .lk-disconnect-button { @apply h-[36px] hover:bg-[#6b221a] hover:text-[white] bg-[#31100c] border-[#6b221a]; } + +.ptt-button { + color: white; + border: none; + border-radius: 50px; + padding: 25px 50px; + font-size: 18px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); +} + +.ptt-button:hover { + transform: translateY(-2px); + box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); +} diff --git a/app/page.tsx b/app/page.tsx index 6ff3025b4..d8007bcd2 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -16,6 +16,7 @@ import { AnimatePresence, motion } from "framer-motion"; import { Room, RoomEvent } from "livekit-client"; import { useCallback, useEffect, useState } from "react"; import type { ConnectionDetails } from "./api/connection-details/route"; +import { PushToTalkButton } from "@/components/PushToTalkButton"; export default function Page() { const [room] = useState(new Room()); @@ -154,19 +155,20 @@ function ControlBar(props: { onConnectButtonClicked: () => void }) { {agentState !== "disconnected" && agentState !== "connecting" && ( - - - - - - - )} + + + + + + + + )} ); diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx new file mode 100644 index 000000000..02c487fc9 --- /dev/null +++ b/components/PushToTalkButton.tsx @@ -0,0 +1,76 @@ +import { useLocalParticipant, useParticipants } from "@livekit/components-react"; +import { useCallback, useEffect, useState, useRef } from "react"; +import { motion } from "framer-motion"; + +export function PushToTalkButton() { + const { localParticipant } = useLocalParticipant(); + const participants = useParticipants(); + const [isPressed, setIsPressed] = useState(false); + const lastReleaseTime = useRef(0); + + // Find agent participant that supports PTT + const agent = participants.find((p) => p.attributes?.['supports-ptt'] === '1'); + + const handlePushStart = useCallback(async () => { + if (!agent || !localParticipant) return; + + try { + await localParticipant.performRpc({ + destinationIdentity: agent.identity, + method: 'ptt', + payload: 'push' + }); + setIsPressed(true); + } catch (error) { + console.error('Failed to send PTT push:', error); + } + }, [agent, localParticipant]); + + const handlePushEnd = useCallback(async () => { + if (!agent || !localParticipant || !isPressed) return; + + // Prevent multiple releases within 100ms + const now = Date.now(); + if (now - lastReleaseTime.current < 100) { + return; + } + lastReleaseTime.current = now; + + try { + await localParticipant.performRpc({ + destinationIdentity: agent.identity, + method: 'ptt', + payload: 'release' + }); + setIsPressed(false); + } catch (error) { + console.error('Failed to send PTT release:', error); + } + }, [agent, localParticipant, isPressed]); + + // Clean up pressed state when component unmounts + useEffect(() => { + return () => { + if (isPressed) { + handlePushEnd(); + } + }; + }, [isPressed, handlePushEnd]); + + if (!agent) return null; + + return ( + + {isPressed ? 'Speaking...' : 'Press to Talk'} + + ); +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99a8dc656..0fa3333dd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@livekit/components-react': specifier: ^2.9.3 - version: 2.9.3(@livekit/krisp-noise-filter@0.2.16(livekit-client@2.11.3))(livekit-client@2.11.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) + version: 2.9.3(livekit-client@2.11.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1) '@livekit/components-styles': specifier: ^1.1.4 version: 1.1.5 @@ -77,8 +77,8 @@ packages: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.9': - resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} + '@babel/generator@7.27.0': + resolution: {integrity: sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==} engines: {node: '>=6.9.0'} '@babel/helper-string-parser@7.25.9': @@ -89,31 +89,37 @@ packages: resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.9': - resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} + '@babel/parser@7.27.0': + resolution: {integrity: sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/template@7.26.9': - resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} + '@babel/template@7.27.0': + resolution: {integrity: sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.9': - resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} + '@babel/traverse@7.27.0': + resolution: {integrity: sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.9': - resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} + '@babel/types@7.27.0': + resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==} engines: {node: '>=6.9.0'} - '@bufbuild/protobuf@1.10.0': - resolution: {integrity: sha512-QDdVFLoN93Zjg36NoQPZfsVH9tZew7wKDKyV5qRdj8ntT4wQCOradQjRaTdwMhWUYsgKsvCINKKm87FdEk96Ag==} - '@bufbuild/protobuf@1.10.1': resolution: {integrity: sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==} - '@eslint-community/eslint-utils@4.4.1': - resolution: {integrity: sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==} + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} + + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} + + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} + + '@eslint-community/eslint-utils@4.6.1': + resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -198,17 +204,18 @@ packages: resolution: {integrity: sha512-SocIPcwm18S28zVruvJcmiHfbUIwGTfxGbUOIp1Db78EON/iJ2v7B2g/xD5sr+c7jXoV1DNUPl2qQiFW2S9dbw==} engines: {node: '>=18'} - '@livekit/krisp-noise-filter@0.2.16': - resolution: {integrity: sha512-W7fyNkECDbWLXwBW5CDKQvuW4mxhkKBp9UAvTkTsn6dq1w7ZLTIEdUErrfZiNVuHmbXd22wI1ycjOYvaeNKMvw==} - peerDependencies: - livekit-client: ^2.0.8 - '@livekit/mutex@1.1.1': resolution: {integrity: sha512-EsshAucklmpuUAfkABPxJNhzj9v2sG7JuzFDL4ML1oJQSV14sqrpTYnsaOudMAw9yOaW53NU3QQTlUQoRs4czw==} '@livekit/protocol@1.36.1': resolution: {integrity: sha512-nN3QnITAQ5yXk7UKfotH7CRWIlEozNWeKVyFJ0/+dtSzvWP/ib+10l1DDnRYi3A1yICJOGAKFgJ5d6kmi1HCUA==} + '@livekit/protocol@1.37.0': + resolution: {integrity: sha512-TV53woej1pvKQLDUA7DxEVpQiK2hX/6nLNxUP8TZrzguIX4CreTkdb97cLyMq5rPi/YB/uLRoG373WqhCnNRbA==} + + '@napi-rs/wasm-runtime@0.2.9': + resolution: {integrity: sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==} + '@next/env@14.2.28': resolution: {integrity: sha512-PAmWhJfJQlP+kxZwCjrVd9QnR5x0R3u0mTXTiZDgSd4h5LdXmjxCCWbN9kq6hkZBOax8Rm3xDW5HagWyJuT37g==} @@ -292,8 +299,8 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@rushstack/eslint-patch@1.10.5': - resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} + '@rushstack/eslint-patch@1.11.0': + resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==} '@swc/counter@0.1.3': resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} @@ -317,8 +324,8 @@ packages: svelte: optional: true - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -337,77 +344,148 @@ packages: '@types/react@18.3.20': resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} - '@types/semver@7.5.8': - resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} - - '@typescript-eslint/eslint-plugin@7.2.0': - resolution: {integrity: sha512-mdekAHOqS9UjlmyF/LSs6AIEvfceV749GFxoBAjwAv0nkevfKHWQFDMcBZWUiIC5ft6ePWivXoS36aKQ0Cy3sw==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/eslint-plugin@8.31.0': + resolution: {integrity: sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^7.0.0 - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@7.2.0': - resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/parser@8.31.0': + resolution: {integrity: sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@7.2.0': - resolution: {integrity: sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/scope-manager@8.31.0': + resolution: {integrity: sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@7.2.0': - resolution: {integrity: sha512-xHi51adBHo9O9330J8GQYQwrKBqbIPJGZZVQTHHmy200hvkLZFWJIFtAG/7IYTWUyun6DE6w5InDReePJYJlJA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/type-utils@8.31.0': + resolution: {integrity: sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@7.2.0': - resolution: {integrity: sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/types@8.31.0': + resolution: {integrity: sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@7.2.0': - resolution: {integrity: sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/typescript-estree@8.31.0': + resolution: {integrity: sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@7.2.0': - resolution: {integrity: sha512-YfHpnMAGb1Eekpm3XRK8hcMwGLGsnT6L+7b2XyRv6ouDuJU1tZir1GS2i0+VXRatMwSI1/UfcyPe53ADkU+IuA==} - engines: {node: ^16.0.0 || >=18.0.0} + '@typescript-eslint/utils@8.31.0': + resolution: {integrity: sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - eslint: ^8.56.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.31.0': + resolution: {integrity: sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unrs/resolver-binding-darwin-arm64@1.7.2': + resolution: {integrity: sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.7.2': + resolution: {integrity: sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.7.2': + resolution: {integrity: sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2': + resolution: {integrity: sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.7.2': + resolution: {integrity: sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.7.2': + resolution: {integrity: sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.7.2': + resolution: {integrity: sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.7.2': + resolution: {integrity: sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.7.2': + resolution: {integrity: sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.7.2': + resolution: {integrity: sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.7.2': + resolution: {integrity: sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.7.2': + resolution: {integrity: sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==} + cpu: [x64] + os: [linux] - '@typescript-eslint/visitor-keys@7.2.0': - resolution: {integrity: sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==} - engines: {node: ^16.0.0 || >=18.0.0} + '@unrs/resolver-binding-linux-x64-musl@1.7.2': + resolution: {integrity: sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==} + cpu: [x64] + os: [linux] - '@ungap/structured-clone@1.2.1': - resolution: {integrity: sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA==} + '@unrs/resolver-binding-wasm32-wasi@1.7.2': + resolution: {integrity: sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.7.2': + resolution: {integrity: sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.7.2': + resolution: {integrity: sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.7.2': + resolution: {integrity: sha512-friS8NEQfHaDbkThxopGk+LuE5v3iY0StruifjQEt7SLbA46OnfgMO15sOTkbpJkol6RB+1l1TYPXh0sCddpvA==} + cpu: [x64] + os: [win32] acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} engines: {node: '>=0.4.0'} hasBin: true @@ -455,16 +533,12 @@ packages: resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} engines: {node: '>= 0.4'} - array-union@2.1.0: - resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} - engines: {node: '>=8'} - array.prototype.findlast@1.2.5: resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} engines: {node: '>= 0.4'} - array.prototype.findlastindex@1.2.5: - resolution: {integrity: sha512-zfETvRFA8o7EiNn++N5f/kaCw221hrpGsDmcpndVupkPzEc1Wuf3VgC0qby1BbHs7f5DVYjgtEU2LLh5bqeGfQ==} + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} engines: {node: '>= 0.4'} array.prototype.flat@1.3.3: @@ -486,12 +560,16 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.10.2: - resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} + axe-core@4.10.3: + resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==} engines: {node: '>=4'} axobject-query@4.1.0: @@ -519,16 +597,16 @@ packages: resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} engines: {node: '>=10.16.0'} - call-bind-apply-helpers@1.0.1: - resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} call-bind@1.0.8: resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} engines: {node: '>= 0.4'} - call-bound@1.0.3: - resolution: {integrity: sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==} + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} callsites@3.1.0: @@ -547,8 +625,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001712: - resolution: {integrity: sha512-MBqPpGYYdQ7/hfKiet9SCI+nmN5/hp4ZzveOJubl5DTAMa5oggjAuoi0Z4onBpKPFI2ePGnQuQIzF3VxDjDJig==} + caniuse-lite@1.0.30001715: + resolution: {integrity: sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -637,10 +715,6 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} - dir-glob@3.0.1: - resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} - engines: {node: '>=8'} - dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -665,10 +739,6 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - enhanced-resolve@5.18.0: - resolution: {integrity: sha512-0/r0MySGYG8YqlayBZ6MuCfECmHFdJ5qyPh8s8wa5Hnm6SaFLSK1VYCbj+NKp090Nm1caZhD+QTnmxO7esYGyQ==} - engines: {node: '>=10.13.0'} - es-abstract@1.23.9: resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} engines: {node: '>= 0.4'} @@ -685,16 +755,17 @@ packages: resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.0: - resolution: {integrity: sha512-Ujz8Al/KfOVR7fkaghAB1WvnLsdYxHDWmfoi2vlA2jZWRg31XhIC1a4B+/I24muD8iSbHxJ1JkrfqmWb65P/Mw==} + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - es-shim-unscopables@1.0.2: - resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==} + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} es-to-primitive@1.3.0: resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} @@ -722,8 +793,8 @@ packages: eslint-import-resolver-node@0.3.9: resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} - eslint-import-resolver-typescript@3.7.0: - resolution: {integrity: sha512-Vrwyi8HHxY97K5ebydMtffsWAn1SCR9eol49eCd5fJS4O1WV7PaAjbcjmbfJJSMz/t4Mal212Uz/fQZrOB8mow==} + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: eslint: '*' @@ -778,8 +849,8 @@ packages: peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 - eslint-plugin-react@7.37.4: - resolution: {integrity: sha512-BGP0jRmfYyvOyvMoRX/uoUeW+GqNj9y16bPQzqAHf3AYII/tDs+jMN0dBVkl88/OZwNGwrVFxE7riHsXVfy/LQ==} + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} engines: {node: '>=4'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 @@ -792,6 +863,10 @@ packages: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + eslint-visitor-keys@4.2.0: + resolution: {integrity: sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + eslint@8.57.1: resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -835,8 +910,16 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.18.0: - resolution: {integrity: sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.4: + resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true file-entry-cache@6.0.1: resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} @@ -854,14 +937,15 @@ packages: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} - flatted@3.3.2: - resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - for-each@0.3.3: - resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} framer-motion@11.18.2: @@ -896,8 +980,8 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - get-intrinsic@1.2.7: - resolution: {integrity: sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==} + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} get-proto@1.0.1: @@ -908,8 +992,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.8.1: - resolution: {integrity: sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==} + get-tsconfig@4.10.0: + resolution: {integrity: sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} @@ -944,10 +1028,6 @@ packages: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} - globby@11.1.0: - resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} - engines: {node: '>=10'} - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -989,8 +1069,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} imurmurhash@0.1.4: @@ -1012,8 +1092,8 @@ packages: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} - is-async-function@2.1.0: - resolution: {integrity: sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==} + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} is-bigint@1.1.0: @@ -1024,12 +1104,12 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-boolean-object@1.2.1: - resolution: {integrity: sha512-l9qO6eFlUETHtuihLcYOaLKByJ1f+N4kthcU9YjHy3N+B3hWv0y/2Nd0mu/7lTFnRQHTrSdXF50HQ3bl5fEnng==} + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} - is-bun-module@1.3.0: - resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} @@ -1111,8 +1191,8 @@ packages: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} - is-weakref@1.1.0: - resolution: {integrity: sha512-SXM8Nwyys6nT5WP6pltOwKytLV7FqQ4UiibxVmW+EIosHcmCqkkjViTb5SNssDlkCiEYRP1/pdWUKVvZBmsR2Q==} + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} engines: {node: '>= 0.4'} is-weakset@2.0.4: @@ -1250,10 +1330,6 @@ packages: minimatch@3.1.2: resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -1282,9 +1358,9 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + napi-postinstall@0.2.2: + resolution: {integrity: sha512-Wy1VI/hpKHwy1MsnFxHCJxqFwmmxD0RA/EKPL7e6mfbsY01phM2SZyJnRdU0bLvhu0Quby1DCcAZti3ghdl4/A==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true natural-compare@1.4.0: @@ -1320,8 +1396,8 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - object-inspect@1.13.3: - resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} object-keys@1.1.1: @@ -1332,8 +1408,8 @@ packages: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} - object.entries@1.1.8: - resolution: {integrity: sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==} + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} engines: {node: '>= 0.4'} object.fromentries@2.0.8: @@ -1393,10 +1469,6 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-type@4.0.0: - resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} - engines: {node: '>=8'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1404,16 +1476,20 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - possible-typed-array-names@1.0.0: - resolution: {integrity: sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==} + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} postcss-import@15.1.0: @@ -1527,8 +1603,8 @@ packages: resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} hasBin: true - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rimraf@3.0.2: @@ -1568,8 +1644,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} hasBin: true @@ -1613,16 +1689,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - slash@3.0.0: - resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} - engines: {node: '>=8'} - source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - stable-hash@0.0.4: - resolution: {integrity: sha512-LjdcbuBeLcdETCrPn9i8AYAZ1eCtu4ECAWtP7UleOiZ9LzVxRzzUZEoZ8zB24nhkQnDWyET0I+3sWokSDS3E7g==} + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} @@ -1706,10 +1778,6 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tapable@2.2.1: - resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} - engines: {node: '>=6'} - text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} @@ -1720,15 +1788,19 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + tinyglobby@0.2.13: + resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} - ts-api-utils@1.4.3: - resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} - engines: {node: '>=16'} + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} peerDependencies: - typescript: '>=4.2.0' + typescript: '>=4.8.4' ts-debounce@4.0.0: resolution: {integrity: sha512-+1iDGY6NmOGidq7i7xZGA4cm8DAa6fqdYcvO5Z6yBevH++Bdo9Qt/mN0TzHUgcCcKv1gmh9+W5dHqz8pMWbCbg==} @@ -1750,8 +1822,8 @@ packages: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} - type-fest@4.39.1: - resolution: {integrity: sha512-uW9qzd66uyHYxwyVBYiwS4Oi0qZyUqwjU+Oevr6ZogYiXt99EOYtwvzMSLw1c3lYo2HzJsep/NB23iEVEgjG/w==} + type-fest@4.40.1: + resolution: {integrity: sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==} engines: {node: '>=16'} typed-array-buffer@1.0.3: @@ -1785,6 +1857,9 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + unrs-resolver@1.7.2: + resolution: {integrity: sha512-BBKpaylOW8KbHsu378Zky/dGh4ckT/4NW/0SHRABdqRLcQJ2dAOjDo9g97p04sWflm0kqPqpUatxReNV/dqI5A==} + uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} @@ -1813,8 +1888,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.18: - resolution: {integrity: sha512-qEcY+KJYlWyLH9vNbsr6/5j59AXk5ni5aakf8ldzBvGde6Iz4sxZGkJyWSAueTG7QhOvNRYb1lDdFmL5Td0QKA==} + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -1837,8 +1912,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} engines: {node: '>= 14'} hasBin: true @@ -1856,10 +1931,10 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/generator@7.26.9': + '@babel/generator@7.27.0': dependencies: - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 '@jridgewell/gen-mapping': 0.3.8 '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 @@ -1868,38 +1943,52 @@ snapshots: '@babel/helper-validator-identifier@7.25.9': {} - '@babel/parser@7.26.9': + '@babel/parser@7.27.0': dependencies: - '@babel/types': 7.26.9 + '@babel/types': 7.27.0 - '@babel/template@7.26.9': + '@babel/template@7.27.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 + '@babel/parser': 7.27.0 + '@babel/types': 7.27.0 - '@babel/traverse@7.26.9': + '@babel/traverse@7.27.0': dependencies: '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 + '@babel/generator': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/template': 7.27.0 + '@babel/types': 7.27.0 debug: 4.4.0 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.26.9': + '@babel/types@7.27.0': dependencies: '@babel/helper-string-parser': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@bufbuild/protobuf@1.10.0': {} - '@bufbuild/protobuf@1.10.1': {} - '@eslint-community/eslint-utils@4.4.1(eslint@8.57.1)': + '@emnapi/core@1.4.3': + dependencies: + '@emnapi/wasi-threads': 1.0.2 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.4.3': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.0.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.6.1(eslint@8.57.1)': dependencies: eslint: 8.57.1 eslint-visitor-keys: 3.4.3 @@ -1913,7 +2002,7 @@ snapshots: espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 - import-fresh: 3.3.0 + import-fresh: 3.3.1 js-yaml: 4.1.0 minimatch: 3.1.2 strip-json-comments: 3.1.1 @@ -1979,7 +2068,7 @@ snapshots: rxjs: 7.8.2 tslib: 2.8.1 - '@livekit/components-react@2.9.3(@livekit/krisp-noise-filter@0.2.16(livekit-client@2.11.3))(livekit-client@2.11.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': + '@livekit/components-react@2.9.3(livekit-client@2.11.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(tslib@2.8.1)': dependencies: '@livekit/components-core': 0.12.4(livekit-client@2.11.3)(tslib@2.8.1) clsx: 2.1.1 @@ -1988,22 +2077,26 @@ snapshots: react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 usehooks-ts: 3.1.1(react@18.3.1) - optionalDependencies: - '@livekit/krisp-noise-filter': 0.2.16(livekit-client@2.11.3) '@livekit/components-styles@1.1.5': {} - '@livekit/krisp-noise-filter@0.2.16(livekit-client@2.11.3)': - dependencies: - livekit-client: 2.11.3 - optional: true - '@livekit/mutex@1.1.1': {} '@livekit/protocol@1.36.1': dependencies: '@bufbuild/protobuf': 1.10.1 + '@livekit/protocol@1.37.0': + dependencies: + '@bufbuild/protobuf': 1.10.1 + + '@napi-rs/wasm-runtime@0.2.9': + dependencies: + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 + optional: true + '@next/env@14.2.28': {} '@next/eslint-plugin-next@14.2.28': @@ -2047,7 +2140,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.18.0 + fastq: 1.19.1 '@nolyfill/is-core-module@1.0.39': {} @@ -2056,7 +2149,7 @@ snapshots: '@rtsao/scc@1.1.0': {} - '@rushstack/eslint-patch@1.10.5': {} + '@rushstack/eslint-patch@1.11.0': {} '@swc/counter@0.1.3': {} @@ -2067,17 +2160,20 @@ snapshots: '@trivago/prettier-plugin-sort-imports@5.2.2(prettier@3.5.3)': dependencies: - '@babel/generator': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 + '@babel/generator': 7.27.0 + '@babel/parser': 7.27.0 + '@babel/traverse': 7.27.0 + '@babel/types': 7.27.0 javascript-natural-sort: 0.7.1 lodash: 4.17.21 prettier: 3.5.3 transitivePeerDependencies: - supports-color - '@types/json-schema@7.0.15': {} + '@tybys/wasm-util@0.9.0': + dependencies: + tslib: 2.8.1 + optional: true '@types/json5@0.0.29': {} @@ -2096,101 +2192,143 @@ snapshots: '@types/prop-types': 15.7.14 csstype: 3.1.3 - '@types/semver@7.5.8': {} - - '@typescript-eslint/eslint-plugin@7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.31.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 7.2.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/type-utils': 7.2.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 7.2.0 - debug: 4.4.0 + '@typescript-eslint/parser': 8.31.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.31.0 + '@typescript-eslint/type-utils': 8.31.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.31.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.31.0 eslint: 8.57.1 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.8.3) - optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/scope-manager': 8.31.0 + '@typescript-eslint/types': 8.31.0 + '@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.31.0 debug: 4.4.0 eslint: 8.57.1 - optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@7.2.0': + '@typescript-eslint/scope-manager@8.31.0': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 8.31.0 + '@typescript-eslint/visitor-keys': 8.31.0 - '@typescript-eslint/type-utils@7.2.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.31.0(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.8.3) - '@typescript-eslint/utils': 7.2.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.31.0(eslint@8.57.1)(typescript@5.8.3) debug: 4.4.0 eslint: 8.57.1 - ts-api-utils: 1.4.3(typescript@5.8.3) - optionalDependencies: + ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@7.2.0': {} + '@typescript-eslint/types@8.31.0': {} - '@typescript-eslint/typescript-estree@7.2.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.31.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/visitor-keys': 7.2.0 + '@typescript-eslint/types': 8.31.0 + '@typescript-eslint/visitor-keys': 8.31.0 debug: 4.4.0 - globby: 11.1.0 + fast-glob: 3.3.3 is-glob: 4.0.3 - minimatch: 9.0.3 - semver: 7.6.3 - ts-api-utils: 1.4.3(typescript@5.8.3) - optionalDependencies: + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 2.1.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.2.0(eslint@8.57.1)(typescript@5.8.3)': + '@typescript-eslint/utils@8.31.0(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.8 - '@typescript-eslint/scope-manager': 7.2.0 - '@typescript-eslint/types': 7.2.0 - '@typescript-eslint/typescript-estree': 7.2.0(typescript@5.8.3) + '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.31.0 + '@typescript-eslint/types': 8.31.0 + '@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3) eslint: 8.57.1 - semver: 7.6.3 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - - typescript - '@typescript-eslint/visitor-keys@7.2.0': + '@typescript-eslint/visitor-keys@8.31.0': dependencies: - '@typescript-eslint/types': 7.2.0 - eslint-visitor-keys: 3.4.3 + '@typescript-eslint/types': 8.31.0 + eslint-visitor-keys: 4.2.0 + + '@ungap/structured-clone@1.3.0': {} + + '@unrs/resolver-binding-darwin-arm64@1.7.2': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.7.2': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.7.2': + optional: true - '@ungap/structured-clone@1.2.1': {} + '@unrs/resolver-binding-linux-s390x-gnu@1.7.2': + optional: true - acorn-jsx@5.3.2(acorn@8.14.0): + '@unrs/resolver-binding-linux-x64-gnu@1.7.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.7.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.7.2': dependencies: - acorn: 8.14.0 + '@napi-rs/wasm-runtime': 0.2.9 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.7.2': + optional: true - acorn@8.14.0: {} + '@unrs/resolver-binding-win32-ia32-msvc@1.7.2': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.7.2': + optional: true + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} ajv@6.12.6: dependencies: @@ -2224,7 +2362,7 @@ snapshots: array-buffer-byte-length@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-array-buffer: 3.0.5 array-includes@3.1.8: @@ -2232,43 +2370,42 @@ snapshots: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-object-atoms: 1.1.0 - get-intrinsic: 1.2.7 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 is-string: 1.1.1 - array-union@2.1.0: {} - array.prototype.findlast@1.2.5: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.1.0 - es-shim-unscopables: 1.0.2 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 - array.prototype.findlastindex@1.2.5: + array.prototype.findlastindex@1.2.6: dependencies: call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.1.0 - es-shim-unscopables: 1.0.2 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 array.prototype.tosorted@1.1.4: dependencies: @@ -2276,7 +2413,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-shim-unscopables: 1.0.2 + es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: @@ -2285,16 +2422,18 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 ast-types-flow@0.0.8: {} + async-function@1.0.0: {} + available-typed-arrays@1.0.7: dependencies: - possible-typed-array-names: 1.0.0 + possible-typed-array-names: 1.1.0 - axe-core@4.10.2: {} + axe-core@4.10.3: {} axobject-query@4.1.0: {} @@ -2319,22 +2458,22 @@ snapshots: dependencies: streamsearch: 1.1.0 - call-bind-apply-helpers@1.0.1: + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 call-bind@1.0.8: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 set-function-length: 1.2.2 - call-bound@1.0.3: + call-bound@1.0.4: dependencies: - call-bind-apply-helpers: 1.0.1 - get-intrinsic: 1.2.7 + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 callsites@3.1.0: {} @@ -2345,11 +2484,11 @@ snapshots: camelcase: 8.0.0 map-obj: 5.0.0 quick-lru: 6.1.2 - type-fest: 4.39.1 + type-fest: 4.40.1 camelcase@8.0.0: {} - caniuse-lite@1.0.30001712: {} + caniuse-lite@1.0.30001715: {} chalk@4.1.2: dependencies: @@ -2396,19 +2535,19 @@ snapshots: data-view-buffer@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 data-view-byte-length@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 data-view-byte-offset@1.0.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 @@ -2436,10 +2575,6 @@ snapshots: didyoumean@1.2.2: {} - dir-glob@3.0.1: - dependencies: - path-type: 4.0.0 - dlv@1.1.3: {} doctrine@2.1.0: @@ -2452,7 +2587,7 @@ snapshots: dunder-proto@1.0.1: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 @@ -2462,28 +2597,23 @@ snapshots: emoji-regex@9.2.2: {} - enhanced-resolve@5.18.0: - dependencies: - graceful-fs: 4.2.11 - tapable: 2.2.1 - es-abstract@1.23.9: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.0 function.prototype.name: 1.1.8 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 globalthis: 1.0.4 @@ -2500,9 +2630,9 @@ snapshots: is-shared-array-buffer: 1.0.4 is-string: 1.1.1 is-typed-array: 1.1.15 - is-weakref: 1.1.0 + is-weakref: 1.1.1 math-intrinsics: 1.1.0 - object-inspect: 1.13.3 + object-inspect: 1.13.4 object-keys: 1.1.1 object.assign: 4.1.7 own-keys: 1.0.1 @@ -2519,7 +2649,7 @@ snapshots: typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.7 unbox-primitive: 1.1.0 - which-typed-array: 1.1.18 + which-typed-array: 1.1.19 es-define-property@1.0.1: {} @@ -2528,13 +2658,13 @@ snapshots: es-iterator-helpers@1.2.1: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 es-set-tostringtag: 2.1.0 function-bind: 1.1.2 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 globalthis: 1.0.4 gopd: 1.2.0 has-property-descriptors: 1.0.2 @@ -2544,18 +2674,18 @@ snapshots: iterator.prototype: 1.1.5 safe-array-concat: 1.1.3 - es-object-atoms@1.1.0: + es-object-atoms@1.1.1: dependencies: es-errors: 1.3.0 es-set-tostringtag@2.1.0: dependencies: es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.2 - es-shim-unscopables@1.0.2: + es-shim-unscopables@1.1.0: dependencies: hasown: 2.0.2 @@ -2570,15 +2700,15 @@ snapshots: eslint-config-next@14.2.28(eslint@8.57.1)(typescript@5.8.3): dependencies: '@next/eslint-plugin-next': 14.2.28 - '@rushstack/eslint-patch': 1.10.5 - '@typescript-eslint/eslint-plugin': 7.2.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) - '@typescript-eslint/parser': 7.2.0(eslint@8.57.1)(typescript@5.8.3) + '@rushstack/eslint-patch': 1.11.0 + '@typescript-eslint/eslint-plugin': 8.31.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.31.0(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1) - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) - eslint-plugin-react: 7.37.4(eslint@8.57.1) + eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.0.0-canary-7118f5dd7-20230705(eslint@8.57.1) optionalDependencies: typescript: 5.8.3 @@ -2599,45 +2729,44 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.0 - enhanced-resolve: 5.18.0 eslint: 8.57.1 - fast-glob: 3.3.3 - get-tsconfig: 4.8.1 - is-bun-module: 1.3.0 - is-glob: 4.0.3 - stable-hash: 0.0.4 + get-tsconfig: 4.10.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.13 + unrs-resolver: 1.7.2 optionalDependencies: - eslint-plugin-import: 2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) + eslint-plugin-import: 2.31.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.31.0(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.7.0(eslint-plugin-import@2.31.0)(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.31.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1): + eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.8 - array.prototype.findlastindex: 1.2.5 + array.prototype.findlastindex: 1.2.6 array.prototype.flat: 1.3.3 array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.2.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@8.57.1) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.31.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -2649,7 +2778,7 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': 8.31.0(eslint@8.57.1)(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -2661,7 +2790,7 @@ snapshots: array-includes: 3.1.8 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.10.2 + axe-core: 4.10.3 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 @@ -2678,7 +2807,7 @@ snapshots: dependencies: eslint: 8.57.1 - eslint-plugin-react@7.37.4(eslint@8.57.1): + eslint-plugin-react@7.37.5(eslint@8.57.1): dependencies: array-includes: 3.1.8 array.prototype.findlast: 1.2.5 @@ -2691,7 +2820,7 @@ snapshots: hasown: 2.0.2 jsx-ast-utils: 3.3.5 minimatch: 3.1.2 - object.entries: 1.1.8 + object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 prop-types: 15.8.1 @@ -2707,16 +2836,18 @@ snapshots: eslint-visitor-keys@3.4.3: {} + eslint-visitor-keys@4.2.0: {} + eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.4.1(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) '@eslint-community/regexpp': 4.12.1 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.2.1 + '@ungap/structured-clone': 1.3.0 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 @@ -2752,8 +2883,8 @@ snapshots: espree@9.6.1: dependencies: - acorn: 8.14.0 - acorn-jsx: 5.3.2(acorn@8.14.0) + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) eslint-visitor-keys: 3.4.3 esquery@1.6.0: @@ -2784,9 +2915,13 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.18.0: + fastq@1.19.1: dependencies: - reusify: 1.0.4 + reusify: 1.1.0 + + fdir@6.4.4(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 file-entry-cache@6.0.1: dependencies: @@ -2803,17 +2938,17 @@ snapshots: flat-cache@3.2.0: dependencies: - flatted: 3.3.2 + flatted: 3.3.3 keyv: 4.5.4 rimraf: 3.0.2 - flatted@3.3.2: {} + flatted@3.3.3: {} - for-each@0.3.3: + for-each@0.3.5: dependencies: is-callable: 1.2.7 - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 @@ -2837,7 +2972,7 @@ snapshots: function.prototype.name@1.1.8: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 functions-have-names: 1.2.3 hasown: 2.0.2 @@ -2845,12 +2980,12 @@ snapshots: functions-have-names@1.2.3: {} - get-intrinsic@1.2.7: + get-intrinsic@1.3.0: dependencies: - call-bind-apply-helpers: 1.0.1 + call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 @@ -2861,15 +2996,15 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 get-symbol-description@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 - get-tsconfig@4.8.1: + get-tsconfig@4.10.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -2883,7 +3018,7 @@ snapshots: glob@10.3.10: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 2.3.6 minimatch: 9.0.5 minipass: 7.1.2 @@ -2891,7 +3026,7 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -2918,15 +3053,6 @@ snapshots: define-properties: 1.2.1 gopd: 1.2.0 - globby@11.1.0: - dependencies: - array-union: 2.1.0 - dir-glob: 3.0.1 - fast-glob: 3.3.3 - ignore: 5.3.2 - merge2: 1.4.1 - slash: 3.0.0 - gopd@1.2.0: {} graceful-fs@4.2.11: {} @@ -2957,7 +3083,7 @@ snapshots: ignore@5.3.2: {} - import-fresh@3.3.0: + import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 @@ -2980,12 +3106,13 @@ snapshots: is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 - get-intrinsic: 1.2.7 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 - is-async-function@2.1.0: + is-async-function@2.1.1: dependencies: - call-bound: 1.0.3 + async-function: 1.0.0 + call-bound: 1.0.4 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -2998,14 +3125,14 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-boolean-object@1.2.1: + is-boolean-object@1.2.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-bun-module@1.3.0: + is-bun-module@2.0.0: dependencies: - semver: 7.6.3 + semver: 7.7.1 is-callable@1.2.7: {} @@ -3015,26 +3142,26 @@ snapshots: is-data-view@1.0.2: dependencies: - call-bound: 1.0.3 - get-intrinsic: 1.2.7 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 is-typed-array: 1.1.15 is-date-object@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-fullwidth-code-point@3.0.0: {} is-generator-function@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 @@ -3047,7 +3174,7 @@ snapshots: is-number-object@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-number@7.0.0: {} @@ -3056,7 +3183,7 @@ snapshots: is-regex@1.2.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 hasown: 2.0.2 @@ -3065,33 +3192,33 @@ snapshots: is-shared-array-buffer@1.0.4: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-string@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-tostringtag: 1.0.2 is-symbol@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-symbols: 1.1.0 safe-regex-test: 1.1.0 is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.18 + which-typed-array: 1.1.19 is-weakmap@2.0.2: {} - is-weakref@1.1.0: + is-weakref@1.1.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 is-weakset@2.0.4: dependencies: - call-bound: 1.0.3 - get-intrinsic: 1.2.7 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 isarray@2.0.5: {} @@ -3100,8 +3227,8 @@ snapshots: iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.1.0 - get-intrinsic: 1.2.7 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 get-proto: 1.0.1 has-symbols: 1.1.0 set-function-name: 2.0.2 @@ -3182,8 +3309,8 @@ snapshots: livekit-server-sdk@2.12.0: dependencies: - '@bufbuild/protobuf': 1.10.0 - '@livekit/protocol': 1.36.1 + '@bufbuild/protobuf': 1.10.1 + '@livekit/protocol': 1.37.0 camelcase-keys: 9.1.3 jose: 5.10.0 @@ -3222,10 +3349,6 @@ snapshots: dependencies: brace-expansion: 1.1.11 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.5: dependencies: brace-expansion: 2.0.1 @@ -3250,7 +3373,7 @@ snapshots: nanoid@3.3.11: {} - nanoid@3.3.8: {} + napi-postinstall@0.2.2: {} natural-compare@1.4.0: {} @@ -3259,7 +3382,7 @@ snapshots: '@next/env': 14.2.28 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001712 + caniuse-lite: 1.0.30001715 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -3285,31 +3408,32 @@ snapshots: object-hash@3.0.0: {} - object-inspect@1.13.3: {} + object-inspect@1.13.4: {} object-keys@1.1.1: {} object.assign@4.1.7: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 has-symbols: 1.1.0 object-keys: 1.1.1 - object.entries@1.1.8: + object.entries@1.1.9: dependencies: call-bind: 1.0.8 + call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 object.fromentries@2.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 es-abstract: 1.23.9 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 object.groupby@1.0.3: dependencies: @@ -3320,9 +3444,9 @@ snapshots: object.values@1.2.1: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 once@1.4.0: dependencies: @@ -3339,7 +3463,7 @@ snapshots: own-keys@1.0.1: dependencies: - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 @@ -3370,17 +3494,17 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-type@4.0.0: {} - picocolors@1.1.1: {} picomatch@2.3.1: {} + picomatch@4.0.2: {} + pify@2.3.0: {} - pirates@4.0.6: {} + pirates@4.0.7: {} - possible-typed-array-names@1.0.0: {} + possible-typed-array-names@1.1.0: {} postcss-import@15.1.0(postcss@8.5.3): dependencies: @@ -3397,7 +3521,7 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.3): dependencies: lilconfig: 3.1.3 - yaml: 2.7.0 + yaml: 2.7.1 optionalDependencies: postcss: 8.5.3 @@ -3421,7 +3545,7 @@ snapshots: postcss@8.5.3: dependencies: - nanoid: 3.3.8 + nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -3467,8 +3591,8 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.1.0 - get-intrinsic: 1.2.7 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -3497,7 +3621,7 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.0.4: {} + reusify@1.1.0: {} rimraf@3.0.2: dependencies: @@ -3514,8 +3638,8 @@ snapshots: safe-array-concat@1.1.3: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 - get-intrinsic: 1.2.7 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 @@ -3526,7 +3650,7 @@ snapshots: safe-regex-test@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-regex: 1.2.1 @@ -3540,14 +3664,14 @@ snapshots: semver@6.3.1: {} - semver@7.6.3: {} + semver@7.7.1: {} set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 - get-intrinsic: 1.2.7 + get-intrinsic: 1.3.0 gopd: 1.2.0 has-property-descriptors: 1.0.2 @@ -3562,7 +3686,7 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 shebang-command@2.0.0: dependencies: @@ -3573,38 +3697,36 @@ snapshots: side-channel-list@1.0.0: dependencies: es-errors: 1.3.0 - object-inspect: 1.13.3 + object-inspect: 1.13.4 side-channel-map@1.0.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 - object-inspect: 1.13.3 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 side-channel-weakmap@1.0.2: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 - get-intrinsic: 1.2.7 - object-inspect: 1.13.3 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 side-channel-map: 1.0.1 side-channel@1.1.0: dependencies: es-errors: 1.3.0 - object-inspect: 1.13.3 + object-inspect: 1.13.4 side-channel-list: 1.0.0 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 signal-exit@4.1.0: {} - slash@3.0.0: {} - source-map-js@1.2.1: {} - stable-hash@0.0.4: {} + stable-hash@0.0.5: {} streamsearch@1.1.0: {} @@ -3629,12 +3751,12 @@ snapshots: string.prototype.matchall@4.0.12: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.23.9 es-errors: 1.3.0 - es-object-atoms: 1.1.0 - get-intrinsic: 1.2.7 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 @@ -3650,25 +3772,25 @@ snapshots: string.prototype.trim@1.2.10: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 es-abstract: 1.23.9 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 has-property-descriptors: 1.0.2 string.prototype.trimend@1.0.9: dependencies: call-bind: 1.0.8 - call-bound: 1.0.3 + call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.8 define-properties: 1.2.1 - es-object-atoms: 1.1.0 + es-object-atoms: 1.1.1 strip-ansi@6.0.1: dependencies: @@ -3694,7 +3816,7 @@ snapshots: glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 ts-interface-checker: 0.1.13 supports-color@7.2.0: @@ -3730,8 +3852,6 @@ snapshots: transitivePeerDependencies: - ts-node - tapable@2.2.1: {} - text-table@0.2.0: {} thenify-all@1.6.0: @@ -3742,11 +3862,16 @@ snapshots: dependencies: any-promise: 1.3.0 + tinyglobby@0.2.13: + dependencies: + fdir: 6.4.4(picomatch@4.0.2) + picomatch: 4.0.2 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - ts-api-utils@1.4.3(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: typescript: 5.8.3 @@ -3769,18 +3894,18 @@ snapshots: type-fest@0.20.2: {} - type-fest@4.39.1: {} + type-fest@4.40.1: {} typed-array-buffer@1.0.3: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 es-errors: 1.3.0 is-typed-array: 1.1.15 typed-array-byte-length@1.0.3: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 @@ -3789,7 +3914,7 @@ snapshots: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 @@ -3798,10 +3923,10 @@ snapshots: typed-array-length@1.0.7: dependencies: call-bind: 1.0.8 - for-each: 0.3.3 + for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 - possible-typed-array-names: 1.0.0 + possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 typed-emitter@2.1.0: @@ -3812,13 +3937,35 @@ snapshots: unbox-primitive@1.1.0: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 has-bigints: 1.1.0 has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 undici-types@6.19.8: {} + unrs-resolver@1.7.2: + dependencies: + napi-postinstall: 0.2.2 + optionalDependencies: + '@unrs/resolver-binding-darwin-arm64': 1.7.2 + '@unrs/resolver-binding-darwin-x64': 1.7.2 + '@unrs/resolver-binding-freebsd-x64': 1.7.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.7.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.7.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.7.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.7.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.7.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.7.2 + '@unrs/resolver-binding-linux-x64-musl': 1.7.2 + '@unrs/resolver-binding-wasm32-wasi': 1.7.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.7.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.7.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.7.2 + uri-js@4.4.1: dependencies: punycode: 2.3.1 @@ -3837,26 +3984,26 @@ snapshots: which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 - is-boolean-object: 1.2.1 + is-boolean-object: 1.2.2 is-number-object: 1.1.1 is-string: 1.1.1 is-symbol: 1.1.1 which-builtin-type@1.2.1: dependencies: - call-bound: 1.0.3 + call-bound: 1.0.4 function.prototype.name: 1.1.8 has-tostringtag: 1.0.2 - is-async-function: 2.1.0 + is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 is-generator-function: 1.1.0 is-regex: 1.2.1 - is-weakref: 1.1.0 + is-weakref: 1.1.1 isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.18 + which-typed-array: 1.1.19 which-collection@1.0.2: dependencies: @@ -3865,12 +4012,13 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.18: + which-typed-array@1.1.19: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.8 - call-bound: 1.0.3 - for-each: 0.3.3 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 gopd: 1.2.0 has-tostringtag: 1.0.2 @@ -3894,6 +4042,6 @@ snapshots: wrappy@1.0.2: {} - yaml@2.7.0: {} + yaml@2.7.1: {} yocto-queue@0.1.0: {} From 3df559f82f27e976c911486d1f5d7e0363857457 Mon Sep 17 00:00:00 2001 From: David Zhao Date: Sun, 12 Jan 2025 22:31:24 -0800 Subject: [PATCH 2/9] disable microphone unless pressed --- components/PushToTalkButton.tsx | 43 ++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index 02c487fc9..ff2e7e0d4 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -1,6 +1,9 @@ -import { useLocalParticipant, useParticipants } from "@livekit/components-react"; -import { useCallback, useEffect, useState, useRef } from "react"; +import { + useLocalParticipant, + useParticipants, +} from "@livekit/components-react"; import { motion } from "framer-motion"; +import { useCallback, useEffect, useRef, useState } from "react"; export function PushToTalkButton() { const { localParticipant } = useLocalParticipant(); @@ -9,20 +12,30 @@ export function PushToTalkButton() { const lastReleaseTime = useRef(0); // Find agent participant that supports PTT - const agent = participants.find((p) => p.attributes?.['supports-ptt'] === '1'); + const agent = participants.find( + (p) => p.attributes?.["supports-ptt"] === "1" + ); + + useEffect(() => { + // start with microphone enabled for PTT agents + if (agent && localParticipant) { + localParticipant.setMicrophoneEnabled(false); + } + }, [localParticipant, agent]); const handlePushStart = useCallback(async () => { if (!agent || !localParticipant) return; try { + await localParticipant.setMicrophoneEnabled(true); await localParticipant.performRpc({ destinationIdentity: agent.identity, - method: 'ptt', - payload: 'push' + method: "ptt", + payload: "push", }); setIsPressed(true); } catch (error) { - console.error('Failed to send PTT push:', error); + console.error("Failed to send PTT push:", error); } }, [agent, localParticipant]); @@ -37,14 +50,16 @@ export function PushToTalkButton() { lastReleaseTime.current = now; try { + await localParticipant.setMicrophoneEnabled(false); await localParticipant.performRpc({ destinationIdentity: agent.identity, - method: 'ptt', - payload: 'release' + method: "ptt", + payload: "release", }); - setIsPressed(false); } catch (error) { - console.error('Failed to send PTT release:', error); + console.error("Failed to send PTT release:", error); + } finally { + setIsPressed(false); } }, [agent, localParticipant, isPressed]); @@ -66,11 +81,11 @@ export function PushToTalkButton() { onMouseUp={handlePushEnd} initial={false} animate={{ - backgroundColor: isPressed ? '#004085' : '#007bff', - scale: isPressed ? 0.95 : 1 + backgroundColor: isPressed ? "#004085" : "#007bff", + scale: isPressed ? 0.95 : 1, }} > - {isPressed ? 'Speaking...' : 'Press to Talk'} + {isPressed ? "Speaking..." : "Press to Talk"} ); -} \ No newline at end of file +} From 8e6cada66b48666882d28a5dea77ba472b4665a7 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Wed, 15 Jan 2025 12:17:27 +0800 Subject: [PATCH 3/9] rename the ppt methods --- components/PushToTalkButton.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index ff2e7e0d4..b9964c166 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -30,8 +30,7 @@ export function PushToTalkButton() { await localParticipant.setMicrophoneEnabled(true); await localParticipant.performRpc({ destinationIdentity: agent.identity, - method: "ptt", - payload: "push", + method: "ptt.start", }); setIsPressed(true); } catch (error) { @@ -53,8 +52,7 @@ export function PushToTalkButton() { await localParticipant.setMicrophoneEnabled(false); await localParticipant.performRpc({ destinationIdentity: agent.identity, - method: "ptt", - payload: "release", + method: "ptt.end", }); } catch (error) { console.error("Failed to send PTT release:", error); From 2517094e10acb814e347b020f6e8cae402fbb849 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 28 Apr 2025 10:56:12 +0800 Subject: [PATCH 4/9] add cancel turn --- app/globals.css | 8 ++- app/page.tsx | 2 +- components/PushToTalkButton.tsx | 111 +++++++++++++++++++++++++------- 3 files changed, 95 insertions(+), 26 deletions(-) diff --git a/app/globals.css b/app/globals.css index db3a0709b..79a5afc8a 100644 --- a/app/globals.css +++ b/app/globals.css @@ -23,12 +23,16 @@ color: white; border: none; border-radius: 50px; - padding: 25px 50px; - font-size: 18px; + height: 40px; + padding: 0 30px; + font-size: 16px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: center; } .ptt-button:hover { diff --git a/app/page.tsx b/app/page.tsx index d8007bcd2..9d4916048 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -160,7 +160,7 @@ function ControlBar(props: { onConnectButtonClicked: () => void }) { animate={{ opacity: 1, top: 0 }} exit={{ opacity: 0, top: "-10px" }} transition={{ duration: 0.4, ease: [0.09, 1.04, 0.245, 1.055] }} - className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center items-center gap-4" + className="flex h-8 absolute left-1/2 -translate-x-1/2 justify-center items-center gap-3" > diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index b9964c166..245c87c01 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -3,87 +3,152 @@ import { useParticipants, } from "@livekit/components-react"; import { motion } from "framer-motion"; -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState, MouseEvent } from "react"; export function PushToTalkButton() { const { localParticipant } = useLocalParticipant(); const participants = useParticipants(); const [isPressed, setIsPressed] = useState(false); - const lastReleaseTime = useRef(0); + const [isOutside, setIsOutside] = useState(false); + const lastActionTime = useRef(0); - // Find agent participant that supports PTT + // find agent participant that supports PTT const agent = participants.find( - (p) => p.attributes?.["supports-ptt"] === "1" + (p) => p.attributes?.["push-to-talk"] === "1" ); useEffect(() => { - // start with microphone enabled for PTT agents + // start with microphone disabled for PTT agents if (agent && localParticipant) { localParticipant.setMicrophoneEnabled(false); } }, [localParticipant, agent]); - const handlePushStart = useCallback(async () => { + // when user presses the button + const handleMouseDown = useCallback(async (e: MouseEvent) => { + e.preventDefault(); // prevent default browser behavior + if (!agent || !localParticipant) return; + console.log("starting turn"); try { await localParticipant.setMicrophoneEnabled(true); await localParticipant.performRpc({ destinationIdentity: agent.identity, - method: "ptt.start", + method: "start_turn", + payload: "", }); setIsPressed(true); + setIsOutside(false); } catch (error) { - console.error("Failed to send PTT push:", error); + console.error("Failed to start turn:", error); } }, [agent, localParticipant]); - const handlePushEnd = useCallback(async () => { + // when mouse leaves the button area while pressed + const handleMouseLeave = useCallback(() => { + if (isPressed) { + console.log("mouse left button while pressed"); + setIsOutside(true); + } + }, [isPressed]); + + // when mouse re-enters the button area while pressed + const handleMouseEnter = useCallback(() => { + if (isPressed && isOutside) { + console.log("mouse re-entered button while pressed"); + setIsOutside(false); + } + }, [isPressed, isOutside]); + + // shared function to end turn with specified method + const endTurnWithMethod = useCallback(async (method: string) => { if (!agent || !localParticipant || !isPressed) return; - // Prevent multiple releases within 100ms + // Prevent multiple actions within 100ms const now = Date.now(); - if (now - lastReleaseTime.current < 100) { - return; - } - lastReleaseTime.current = now; + if (now - lastActionTime.current < 100) return; + lastActionTime.current = now; + console.log(`ending turn with method: ${method}`); try { await localParticipant.setMicrophoneEnabled(false); await localParticipant.performRpc({ destinationIdentity: agent.identity, - method: "ptt.end", + method: method, + payload: "", }); } catch (error) { - console.error("Failed to send PTT release:", error); + console.error(`Failed to ${method}:`, error); } finally { setIsPressed(false); + setIsOutside(false); } }, [agent, localParticipant, isPressed]); - // Clean up pressed state when component unmounts + // when user releases the mouse anywhere + const handleMouseUp = useCallback((e: MouseEvent) => { + e.preventDefault(); // prevent default browser behavior + + if (!isPressed) return; + + // if mouse is outside the button on release, cancel the turn + // otherwise, end the turn normally + const method = isOutside ? "cancel_turn" : "end_turn"; + endTurnWithMethod(method); + }, [isPressed, isOutside, endTurnWithMethod]); + + // ensure turn is ended when component unmounts useEffect(() => { return () => { if (isPressed) { - handlePushEnd(); + endTurnWithMethod("end_turn"); } }; - }, [isPressed, handlePushEnd]); + }, [isPressed, endTurnWithMethod]); + + // add global mouse-up handler to catch events outside the button + useEffect(() => { + if (isPressed) { + const handleGlobalMouseUp = (e: MouseEvent) => { + handleMouseUp(e); + }; + + window.addEventListener('mouseup', handleGlobalMouseUp as any); + return () => { + window.removeEventListener('mouseup', handleGlobalMouseUp as any); + }; + } + }, [isPressed, handleMouseUp]); if (!agent) return null; return ( - {isPressed ? "Speaking..." : "Press to Talk"} + {isPressed + ? isOutside + ? "Release to Cancel" + : "Speaking..." + : "Press to Talk"} ); } From 5d7e4a34c48aecc9cfa252df08f1b37ba867d583 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 28 Apr 2025 13:39:50 +0800 Subject: [PATCH 5/9] skip mute microphone --- components/PushToTalkButton.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index 245c87c01..f327631a1 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -20,7 +20,7 @@ export function PushToTalkButton() { useEffect(() => { // start with microphone disabled for PTT agents if (agent && localParticipant) { - localParticipant.setMicrophoneEnabled(false); + // localParticipant.setMicrophoneEnabled(false); } }, [localParticipant, agent]); @@ -32,7 +32,7 @@ export function PushToTalkButton() { console.log("starting turn"); try { - await localParticipant.setMicrophoneEnabled(true); + // await localParticipant.setMicrophoneEnabled(true); await localParticipant.performRpc({ destinationIdentity: agent.identity, method: "start_turn", @@ -72,7 +72,7 @@ export function PushToTalkButton() { console.log(`ending turn with method: ${method}`); try { - await localParticipant.setMicrophoneEnabled(false); + // await localParticipant.setMicrophoneEnabled(false); await localParticipant.performRpc({ destinationIdentity: agent.identity, method: method, From 4409459920169af74208497a84e809c98aadefe4 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 28 Apr 2025 13:48:28 +0800 Subject: [PATCH 6/9] fix types --- components/PushToTalkButton.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index f327631a1..fdea3bf1e 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -3,7 +3,7 @@ import { useParticipants, } from "@livekit/components-react"; import { motion } from "framer-motion"; -import { useCallback, useEffect, useRef, useState, MouseEvent } from "react"; +import { useCallback, useEffect, useRef, useState, MouseEvent as ReactMouseEvent } from "react"; export function PushToTalkButton() { const { localParticipant } = useLocalParticipant(); @@ -25,7 +25,7 @@ export function PushToTalkButton() { }, [localParticipant, agent]); // when user presses the button - const handleMouseDown = useCallback(async (e: MouseEvent) => { + const handleMouseDown = useCallback(async (e: ReactMouseEvent) => { e.preventDefault(); // prevent default browser behavior if (!agent || !localParticipant) return; @@ -87,7 +87,7 @@ export function PushToTalkButton() { }, [agent, localParticipant, isPressed]); // when user releases the mouse anywhere - const handleMouseUp = useCallback((e: MouseEvent) => { + const handleMouseUp = useCallback((e: ReactMouseEvent) => { e.preventDefault(); // prevent default browser behavior if (!isPressed) return; @@ -111,12 +111,12 @@ export function PushToTalkButton() { useEffect(() => { if (isPressed) { const handleGlobalMouseUp = (e: MouseEvent) => { - handleMouseUp(e); + handleMouseUp(e as unknown as ReactMouseEvent); }; - window.addEventListener('mouseup', handleGlobalMouseUp as any); + window.addEventListener('mouseup', handleGlobalMouseUp); return () => { - window.removeEventListener('mouseup', handleGlobalMouseUp as any); + window.removeEventListener('mouseup', handleGlobalMouseUp); }; } }, [isPressed, handleMouseUp]); From b592675cb852355917acd6af3128e927c4e48725 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 28 Apr 2025 13:49:59 +0800 Subject: [PATCH 7/9] fix format --- app/page.tsx | 30 +++---- components/PushToTalkButton.tsx | 141 ++++++++++++++++---------------- 2 files changed, 86 insertions(+), 85 deletions(-) diff --git a/app/page.tsx b/app/page.tsx index 9d4916048..61a190d29 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -2,6 +2,7 @@ import { CloseIcon } from "@/components/CloseIcon"; import { NoAgentNotification } from "@/components/NoAgentNotification"; +import { PushToTalkButton } from "@/components/PushToTalkButton"; import TranscriptionView from "@/components/TranscriptionView"; import { BarVisualizer, @@ -16,7 +17,6 @@ import { AnimatePresence, motion } from "framer-motion"; import { Room, RoomEvent } from "livekit-client"; import { useCallback, useEffect, useState } from "react"; import type { ConnectionDetails } from "./api/connection-details/route"; -import { PushToTalkButton } from "@/components/PushToTalkButton"; export default function Page() { const [room] = useState(new Room()); @@ -155,20 +155,20 @@ function ControlBar(props: { onConnectButtonClicked: () => void }) { {agentState !== "disconnected" && agentState !== "connecting" && ( - - - - - - - - )} + + + + + + + + )} ); diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index fdea3bf1e..83c58dd9c 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -1,9 +1,6 @@ -import { - useLocalParticipant, - useParticipants, -} from "@livekit/components-react"; +import { useLocalParticipant, useParticipants } from "@livekit/components-react"; import { motion } from "framer-motion"; -import { useCallback, useEffect, useRef, useState, MouseEvent as ReactMouseEvent } from "react"; +import { MouseEvent as ReactMouseEvent, useCallback, useEffect, useRef, useState } from "react"; export function PushToTalkButton() { const { localParticipant } = useLocalParticipant(); @@ -13,9 +10,7 @@ export function PushToTalkButton() { const lastActionTime = useRef(0); // find agent participant that supports PTT - const agent = participants.find( - (p) => p.attributes?.["push-to-talk"] === "1" - ); + const agent = participants.find((p) => p.attributes?.["push-to-talk"] === "1"); useEffect(() => { // start with microphone disabled for PTT agents @@ -25,25 +20,28 @@ export function PushToTalkButton() { }, [localParticipant, agent]); // when user presses the button - const handleMouseDown = useCallback(async (e: ReactMouseEvent) => { - e.preventDefault(); // prevent default browser behavior - - if (!agent || !localParticipant) return; - - console.log("starting turn"); - try { - // await localParticipant.setMicrophoneEnabled(true); - await localParticipant.performRpc({ - destinationIdentity: agent.identity, - method: "start_turn", - payload: "", - }); - setIsPressed(true); - setIsOutside(false); - } catch (error) { - console.error("Failed to start turn:", error); - } - }, [agent, localParticipant]); + const handleMouseDown = useCallback( + async (e: ReactMouseEvent) => { + e.preventDefault(); // prevent default browser behavior + + if (!agent || !localParticipant) return; + + console.log("starting turn"); + try { + // await localParticipant.setMicrophoneEnabled(true); + await localParticipant.performRpc({ + destinationIdentity: agent.identity, + method: "start_turn", + payload: "", + }); + setIsPressed(true); + setIsOutside(false); + } catch (error) { + console.error("Failed to start turn:", error); + } + }, + [agent, localParticipant] + ); // when mouse leaves the button area while pressed const handleMouseLeave = useCallback(() => { @@ -62,41 +60,47 @@ export function PushToTalkButton() { }, [isPressed, isOutside]); // shared function to end turn with specified method - const endTurnWithMethod = useCallback(async (method: string) => { - if (!agent || !localParticipant || !isPressed) return; - - // Prevent multiple actions within 100ms - const now = Date.now(); - if (now - lastActionTime.current < 100) return; - lastActionTime.current = now; - - console.log(`ending turn with method: ${method}`); - try { - // await localParticipant.setMicrophoneEnabled(false); - await localParticipant.performRpc({ - destinationIdentity: agent.identity, - method: method, - payload: "", - }); - } catch (error) { - console.error(`Failed to ${method}:`, error); - } finally { - setIsPressed(false); - setIsOutside(false); - } - }, [agent, localParticipant, isPressed]); + const endTurnWithMethod = useCallback( + async (method: string) => { + if (!agent || !localParticipant || !isPressed) return; + + // Prevent multiple actions within 100ms + const now = Date.now(); + if (now - lastActionTime.current < 100) return; + lastActionTime.current = now; + + console.log(`ending turn with method: ${method}`); + try { + // await localParticipant.setMicrophoneEnabled(false); + await localParticipant.performRpc({ + destinationIdentity: agent.identity, + method: method, + payload: "", + }); + } catch (error) { + console.error(`Failed to ${method}:`, error); + } finally { + setIsPressed(false); + setIsOutside(false); + } + }, + [agent, localParticipant, isPressed] + ); // when user releases the mouse anywhere - const handleMouseUp = useCallback((e: ReactMouseEvent) => { - e.preventDefault(); // prevent default browser behavior - - if (!isPressed) return; - - // if mouse is outside the button on release, cancel the turn - // otherwise, end the turn normally - const method = isOutside ? "cancel_turn" : "end_turn"; - endTurnWithMethod(method); - }, [isPressed, isOutside, endTurnWithMethod]); + const handleMouseUp = useCallback( + (e: ReactMouseEvent) => { + e.preventDefault(); // prevent default browser behavior + + if (!isPressed) return; + + // if mouse is outside the button on release, cancel the turn + // otherwise, end the turn normally + const method = isOutside ? "cancel_turn" : "end_turn"; + endTurnWithMethod(method); + }, + [isPressed, isOutside, endTurnWithMethod] + ); // ensure turn is ended when component unmounts useEffect(() => { @@ -114,9 +118,9 @@ export function PushToTalkButton() { handleMouseUp(e as unknown as ReactMouseEvent); }; - window.addEventListener('mouseup', handleGlobalMouseUp); + window.addEventListener("mouseup", handleGlobalMouseUp); return () => { - window.removeEventListener('mouseup', handleGlobalMouseUp); + window.removeEventListener("mouseup", handleGlobalMouseUp); }; } }, [isPressed, handleMouseUp]); @@ -139,16 +143,13 @@ export function PushToTalkButton() { : "#004085" // blue when speaking normally : "#007bff", // default blue scale: isPressed ? 0.95 : 1, - boxShadow: isOutside && isPressed - ? "0 0 0 3px rgba(217, 83, 79, 0.5)" - : "0 4px 6px rgba(0, 0, 0, 0.1)", + boxShadow: + isOutside && isPressed + ? "0 0 0 3px rgba(217, 83, 79, 0.5)" + : "0 4px 6px rgba(0, 0, 0, 0.1)", }} > - {isPressed - ? isOutside - ? "Release to Cancel" - : "Speaking..." - : "Press to Talk"} + {isPressed ? (isOutside ? "Release to Cancel" : "Speaking...") : "Press to Talk"} ); } From 0ce62cfd8c6e5fa07642a109aaa45f1285f79853 Mon Sep 17 00:00:00 2001 From: Long Chen Date: Mon, 28 Apr 2025 16:09:10 +0800 Subject: [PATCH 8/9] fix for touch screen --- app/globals.css | 32 ++++ components/PushToTalkButton.tsx | 282 ++++++++++++++++++++++++-------- 2 files changed, 250 insertions(+), 64 deletions(-) diff --git a/app/globals.css b/app/globals.css index 79a5afc8a..c90faaf60 100644 --- a/app/globals.css +++ b/app/globals.css @@ -33,9 +33,41 @@ display: flex; align-items: center; justify-content: center; + /* Prevent text selection */ + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + /* Ensure text doesn't break */ + white-space: nowrap; } .ptt-button:hover { transform: translateY(-2px); box-shadow: 0 6px 8px rgba(0, 0, 0, 0.2); } + +/* Responsive styles for mobile devices */ +@media (max-width: 768px) { + .ptt-button { + height: 50px; + padding: 0 20px; + font-size: 15px; + width: calc(100% - 20px); + max-width: 300px; + margin: 0 auto; + } +} + +/* Smaller phones */ +@media (max-width: 480px) { + .ptt-button { + height: 60px; + padding: 0 15px; + font-size: 16px; + font-weight: 600; + width: calc(100% - 10px); + } +} diff --git a/components/PushToTalkButton.tsx b/components/PushToTalkButton.tsx index 83c58dd9c..f3aa9324e 100644 --- a/components/PushToTalkButton.tsx +++ b/components/PushToTalkButton.tsx @@ -1,49 +1,128 @@ import { useLocalParticipant, useParticipants } from "@livekit/components-react"; import { motion } from "framer-motion"; -import { MouseEvent as ReactMouseEvent, useCallback, useEffect, useRef, useState } from "react"; +import { + MouseEvent as ReactMouseEvent, + TouchEvent as ReactTouchEvent, + useCallback, + useEffect, + useRef, + useState, +} from "react"; export function PushToTalkButton() { + // state and refs const { localParticipant } = useLocalParticipant(); const participants = useParticipants(); const [isPressed, setIsPressed] = useState(false); const [isOutside, setIsOutside] = useState(false); + const [isLoading, setIsLoading] = useState(false); const lastActionTime = useRef(0); + const rpcTimeoutRef = useRef | null>(null); + const buttonRef = useRef(null); // find agent participant that supports PTT const agent = participants.find((p) => p.attributes?.["push-to-talk"] === "1"); + // initialize mic state useEffect(() => { - // start with microphone disabled for PTT agents if (agent && localParticipant) { - // localParticipant.setMicrophoneEnabled(false); + localParticipant.setMicrophoneEnabled(false); } }, [localParticipant, agent]); - // when user presses the button - const handleMouseDown = useCallback( - async (e: ReactMouseEvent) => { - e.preventDefault(); // prevent default browser behavior + // perform RPC call with timeout handling + const performRpcWithTimeout = useCallback( + async (method: string, timeoutMs = 3000) => { + if (!agent || !localParticipant) return false; - if (!agent || !localParticipant) return; + let succeeded = false; + setIsLoading(true); - console.log("starting turn"); try { - // await localParticipant.setMicrophoneEnabled(true); - await localParticipant.performRpc({ - destinationIdentity: agent.identity, - method: "start_turn", - payload: "", + const timeoutPromise = new Promise((_, reject) => { + rpcTimeoutRef.current = setTimeout(() => { + reject(new Error(`RPC timeout after ${timeoutMs}ms`)); + }, timeoutMs); }); - setIsPressed(true); - setIsOutside(false); + + const startTime = Date.now(); + await Promise.race([ + (async () => { + await localParticipant.performRpc({ + destinationIdentity: agent.identity, + method, + payload: "", + }); + succeeded = true; + })(), + timeoutPromise, + ]); + + const endTime = Date.now(); + console.log(`${method} RPC completed in ${endTime - startTime}ms`); } catch (error) { - console.error("Failed to start turn:", error); + console.error(`RPC ${method} failed:`, error); + } finally { + if (rpcTimeoutRef.current) { + clearTimeout(rpcTimeoutRef.current); + rpcTimeoutRef.current = null; + } + setIsLoading(false); } + + return succeeded; }, [agent, localParticipant] ); - // when mouse leaves the button area while pressed + // shared function to end turn with specified method + const endTurn = useCallback( + async (method: string) => { + if (!agent || !localParticipant || !isPressed || isLoading) return; + + // prevent multiple actions within 250ms + const now = Date.now(); + if (now - lastActionTime.current < 250) return; + lastActionTime.current = now; + + console.log(`ending turn with method: ${method}`); + try { + await localParticipant.setMicrophoneEnabled(false); + await performRpcWithTimeout(method); + } finally { + setIsPressed(false); + setIsOutside(false); + } + }, + [agent, localParticipant, isPressed, isLoading, performRpcWithTimeout] + ); + + // start turn (mouse or touch) + const startTurn = useCallback(async () => { + if (!agent || !localParticipant || isLoading) return; + + console.log("starting turn"); + try { + await localParticipant.setMicrophoneEnabled(true); + const success = await performRpcWithTimeout("start_turn"); + if (success) { + setIsPressed(true); + setIsOutside(false); + } + } catch (error) { + console.error("Failed to start turn:", error); + } + }, [agent, localParticipant, isLoading, performRpcWithTimeout]); + + // mouse event handlers + const handleMouseDown = useCallback( + (e: ReactMouseEvent) => { + e.preventDefault(); + startTurn(); + }, + [startTurn] + ); + const handleMouseLeave = useCallback(() => { if (isPressed) { console.log("mouse left button while pressed"); @@ -51,7 +130,6 @@ export function PushToTalkButton() { } }, [isPressed]); - // when mouse re-enters the button area while pressed const handleMouseEnter = useCallback(() => { if (isPressed && isOutside) { console.log("mouse re-entered button while pressed"); @@ -59,89 +137,165 @@ export function PushToTalkButton() { } }, [isPressed, isOutside]); - // shared function to end turn with specified method - const endTurnWithMethod = useCallback( - async (method: string) => { - if (!agent || !localParticipant || !isPressed) return; + const handleMouseUp = useCallback( + (e: ReactMouseEvent) => { + e.preventDefault(); + if (!isPressed) return; - // Prevent multiple actions within 100ms - const now = Date.now(); - if (now - lastActionTime.current < 100) return; - lastActionTime.current = now; + const method = isOutside ? "cancel_turn" : "end_turn"; + endTurn(method); + }, + [isPressed, isOutside, endTurn] + ); - console.log(`ending turn with method: ${method}`); - try { - // await localParticipant.setMicrophoneEnabled(false); - await localParticipant.performRpc({ - destinationIdentity: agent.identity, - method: method, - payload: "", - }); - } catch (error) { - console.error(`Failed to ${method}:`, error); - } finally { - setIsPressed(false); + // touch event handlers + const handleTouchStart = useCallback( + (e: ReactTouchEvent) => { + e.preventDefault(); + startTurn(); + }, + [startTurn] + ); + + // check if touch is outside the button + const isTouchOutside = useCallback((clientX: number, clientY: number): boolean => { + if (!buttonRef.current) return false; + + const rect = buttonRef.current.getBoundingClientRect(); + return ( + clientX < rect.left || clientX > rect.right || clientY < rect.top || clientY > rect.bottom + ); + }, []); + + const handleTouchMove = useCallback( + (e: ReactTouchEvent) => { + if (!isPressed) return; + + const touch = e.touches[0]; + const touchIsOutside = isTouchOutside(touch.clientX, touch.clientY); + + if (touchIsOutside && !isOutside) { + console.log("touch moved outside button"); + setIsOutside(true); + } else if (!touchIsOutside && isOutside) { + console.log("touch moved back inside button"); setIsOutside(false); } }, - [agent, localParticipant, isPressed] + [isPressed, isOutside, isTouchOutside] ); - // when user releases the mouse anywhere - const handleMouseUp = useCallback( - (e: ReactMouseEvent) => { - e.preventDefault(); // prevent default browser behavior - + const handleTouchEnd = useCallback( + (e: ReactTouchEvent) => { + e.preventDefault(); if (!isPressed) return; - // if mouse is outside the button on release, cancel the turn - // otherwise, end the turn normally const method = isOutside ? "cancel_turn" : "end_turn"; - endTurnWithMethod(method); + endTurn(method); }, - [isPressed, isOutside, endTurnWithMethod] + [isPressed, isOutside, endTurn] ); - // ensure turn is ended when component unmounts + const handleTouchCancel = useCallback(() => { + if (!isPressed) return; + + console.log("touch cancelled - ending turn"); + endTurn("end_turn"); + }, [isPressed, endTurn]); + + // cleanup on unmount useEffect(() => { return () => { if (isPressed) { - endTurnWithMethod("end_turn"); + endTurn("end_turn"); + } + + if (rpcTimeoutRef.current) { + clearTimeout(rpcTimeoutRef.current); + rpcTimeoutRef.current = null; } }; - }, [isPressed, endTurnWithMethod]); + }, [isPressed, endTurn]); - // add global mouse-up handler to catch events outside the button + // global event handlers useEffect(() => { if (isPressed) { + // global mouse handler const handleGlobalMouseUp = (e: MouseEvent) => { - handleMouseUp(e as unknown as ReactMouseEvent); + const syntheticEvent = { + ...e, + preventDefault: () => e.preventDefault(), + } as unknown as ReactMouseEvent; + + handleMouseUp(syntheticEvent); }; - window.addEventListener("mouseup", handleGlobalMouseUp); + // global touch handler + const handleGlobalTouchEnd = (e: TouchEvent) => { + const syntheticEvent = { + ...e, + preventDefault: () => e.preventDefault(), + } as unknown as ReactTouchEvent; + + handleTouchEnd(syntheticEvent); + }; + + window.addEventListener("mouseup", handleGlobalMouseUp, { once: true }); + window.addEventListener("touchend", handleGlobalTouchEnd, { once: true }); + return () => { window.removeEventListener("mouseup", handleGlobalMouseUp); + window.removeEventListener("touchend", handleGlobalTouchEnd); }; } - }, [isPressed, handleMouseUp]); + }, [isPressed, handleMouseUp, handleTouchEnd]); + + // prevent context menu (long press) on touch devices + useEffect(() => { + const preventDefault = (e: Event) => e.preventDefault(); + + if (buttonRef.current) { + buttonRef.current.addEventListener("contextmenu", preventDefault); + return () => { + if (buttonRef.current) { + buttonRef.current.removeEventListener("contextmenu", preventDefault); + } + }; + } + }, []); if (!agent) return null; + // visual and text state based on current status + const getButtonColor = () => { + if (isLoading) return "#cccccc"; // gray when loading + if (isPressed) { + return isOutside ? "#d9534f" : "#004085"; // red when cancelling, blue when speaking + } + return "#007bff"; // default blue + }; + + const getButtonText = () => { + if (isLoading) return "Processing..."; + if (isPressed) return isOutside ? "Release to Cancel" : "Speaking..."; + return "Press to Talk"; + }; + return ( - {isPressed ? (isOutside ? "Release to Cancel" : "Speaking...") : "Press to Talk"} + {getButtonText()} ); } From ae871bfdb0d2a800298abd4f93b609d0df42bcfc Mon Sep 17 00:00:00 2001 From: Long Chen Date: Sun, 25 May 2025 19:59:15 +0800 Subject: [PATCH 9/9] mark the frontend supports push to talk --- app/api/connection-details/route.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/api/connection-details/route.ts b/app/api/connection-details/route.ts index 18f2f26c9..08f948cb2 100644 --- a/app/api/connection-details/route.ts +++ b/app/api/connection-details/route.ts @@ -68,5 +68,8 @@ function createParticipantToken(userInfo: AccessTokenOptions, roomName: string) canSubscribe: true, }; at.addGrant(grant); + at.attributes = { + "supports-ptt": "1", + }; return at.toJwt(); }