@xipkg/calls-utils 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -10,6 +10,9 @@ declare const useKeyPress: (targetKey: string, callback: (event: KeyboardEvent)
10
10
 
11
11
  declare const useMedia: (query: string, defaultState?: boolean) => boolean;
12
12
 
13
+ /** Только телефон: планшеты и iPad используют десктопный compact-вид. */
14
+ declare function usePhoneLayout(): boolean;
15
+
13
16
  declare function isFireFox(): boolean;
14
17
  declare function isChromiumBased(): boolean;
15
18
  declare function isSafari(): boolean;
@@ -43,4 +46,4 @@ declare const NOISE_CANCELLATION_EVENTS: {
43
46
  type NoiseCancellationEvent = (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];
44
47
  declare function trackNoiseCancellationEvent(event: NoiseCancellationEvent, data?: Record<string, string>): void;
45
48
 
46
- export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia };
49
+ export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia, usePhoneLayout };
package/dist/index.mjs CHANGED
@@ -73,6 +73,11 @@ var useMedia = (query, defaultState) => {
73
73
  }, [query]);
74
74
  return state;
75
75
  };
76
+
77
+ // src/usePhoneLayout.ts
78
+ function usePhoneLayout() {
79
+ return useMedia("(max-width: 720px)");
80
+ }
76
81
  function isFireFox() {
77
82
  return getBrowser()?.name === "Firefox";
78
83
  }
@@ -166,6 +171,6 @@ function trackNoiseCancellationEvent(event, data) {
166
171
  }
167
172
  }
168
173
 
169
- export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia };
174
+ export { NOISE_CANCELLATION_EVENTS, isChromiumBased, isFireFox, isLocal, isMacintosh, isSafari, playSound, trackNoiseCancellationEvent, useFullScreen, useKeyPress, useMedia, usePhoneLayout };
170
175
  //# sourceMappingURL=index.mjs.map
171
176
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useFullScreen.ts","../src/useKeyPress.ts","../src/useMedia.ts","../src/livekit.ts","../src/sounds.ts","../src/noiseCancellationTelemetry.ts"],"names":["useEffect","useState"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useEffect } from 'react';\n\nexport const useFullScreen = (containerId: string) => {\n const [isFullScreen, setIsFullScreen] = useState(false);\n\n const hasStandardFullscreen =\n typeof document !== 'undefined' &&\n document.fullscreenEnabled &&\n typeof HTMLElement.prototype.requestFullscreen === 'function';\n\n const isFullScreenSupported = hasStandardFullscreen;\n\n const toggleFullScreen = () => {\n const element = document.getElementById(containerId);\n const fullScreen = document.fullscreenElement;\n\n if (fullScreen) {\n document.exitFullscreen();\n setIsFullScreen(false);\n } else {\n element?.requestFullscreen();\n setIsFullScreen(true);\n }\n };\n\n useEffect(() => {\n if (!isFullScreenSupported) return;\n\n const handleFullScreenChange = () => {\n const fullScreen = !!document.fullscreenElement;\n setIsFullScreen(fullScreen);\n };\n\n document.addEventListener('fullscreenchange', handleFullScreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullScreenChange);\n };\n }, [isFullScreenSupported]);\n\n return { toggleFullScreen, isFullScreen, isFullScreenSupported };\n};\n","import { useEffect } from 'react';\n\nexport const useKeyPress = (targetKey: string, callback: (event: KeyboardEvent) => void) => {\n useEffect(() => {\n // console.log(`Added listener for ${targetKey}`);\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === targetKey) {\n callback(event);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => {\n // console.log(`Removed listener for ${targetKey}`);\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [targetKey, callback]);\n};\n","import { useEffect, useState } from 'react';\n\nconst isBrowser = typeof window !== 'undefined';\n\nconst getInitialState = (query: string, defaultState?: boolean) => {\n // Prevent a React hydration mismatch when a default value\n // is provided by not defaulting to window.matchMedia(query).matches.\n if (defaultState !== undefined) {\n return defaultState;\n }\n\n if (isBrowser) {\n return window.matchMedia(query).matches;\n }\n\n return false;\n};\n\nexport const useMedia = (query: string, defaultState?: boolean) => {\n const [state, setState] = useState(getInitialState(query, defaultState));\n\n useEffect(() => {\n let mounted = true;\n const mql = window.matchMedia(query);\n const onChange = () => {\n if (!mounted) {\n return;\n }\n setState(!!mql.matches);\n };\n\n mql.addEventListener('change', onChange);\n setState(mql.matches);\n\n return () => {\n mounted = false;\n mql.removeEventListener('change', onChange);\n };\n }, [query]);\n\n return state;\n};\n","import { getBrowser, LocalParticipant, LogLevel, Participant, setLogLevel } from 'livekit-client';\n\nexport const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {\n setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug);\n};\n\nexport function isFireFox(): boolean {\n return getBrowser()?.name === 'Firefox';\n}\n\nexport function isChromiumBased(): boolean {\n return getBrowser()?.name === 'Chrome';\n}\n\nexport function isSafari(): boolean {\n return getBrowser()?.name === 'Safari';\n}\n\nexport function isLocal(p: Participant) {\n return p instanceof LocalParticipant;\n}\n\nexport function isMacintosh() {\n return navigator.platform.indexOf('Mac') > -1;\n}\n","/**\n * Утилита для воспроизведения звуков уведомлений в ВКС\n */\n\nconst SOUND_PATHS = {\n chatMessage: '/sounds/chat-message.wav',\n handRaise: '/sounds/hand-raise.wav',\n userJoin: '/sounds/user-join-to-call.mp3',\n userLeft: '/sounds/user-left-from-call.mp3',\n} as const;\n\ntype SoundType = keyof typeof SOUND_PATHS;\n\n/** Для join/left — воспроизводим только начало файла (мс). */\nconst JOIN_LEAVE_PLAY_MS = 500;\n\ntype TrimmedSoundType = Extract<SoundType, 'userJoin' | 'userLeft'>;\nconst trimTimeouts = new Map<TrimmedSoundType, ReturnType<typeof setTimeout>>();\n\n// Кэш для Audio объектов, чтобы не создавать их каждый раз\nconst soundCache = new Map<SoundType, HTMLAudioElement>();\n\n/**\n * Получает или создает Audio объект для звука\n */\nconst getAudio = (soundType: SoundType): HTMLAudioElement => {\n if (!soundCache.has(soundType)) {\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.preload = 'auto';\n soundCache.set(soundType, audio);\n }\n return soundCache.get(soundType)!;\n};\n\nconst clearJoinLeaveTrim = (soundType: TrimmedSoundType) => {\n const id = trimTimeouts.get(soundType);\n if (id !== undefined) {\n clearTimeout(id);\n trimTimeouts.delete(soundType);\n }\n};\n\nconst scheduleJoinLeaveTrim = (audio: HTMLAudioElement, soundType: TrimmedSoundType) => {\n clearJoinLeaveTrim(soundType);\n const id = setTimeout(() => {\n try {\n audio.pause();\n audio.currentTime = 0;\n } catch {\n /* ignore */\n }\n trimTimeouts.delete(soundType);\n }, JOIN_LEAVE_PLAY_MS);\n trimTimeouts.set(soundType, id);\n};\n\n/**\n * Воспроизводит звук с указанной громкостью\n * @param soundType - тип звука (chatMessage или handRaise)\n * @param volume - громкость от 0 до 1 (0 = беззвучно, 1 = максимальная громкость)\n */\nexport const playSound = (soundType: SoundType, volume: number = 1): void => {\n try {\n const audio = getAudio(soundType);\n\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n clearJoinLeaveTrim(soundType);\n }\n\n // Устанавливаем громкость (от 0 до 1)\n const clampedVolume = Math.max(0, Math.min(1, volume));\n audio.volume = clampedVolume;\n\n // Сбрасываем позицию на начало и воспроизводим\n audio.currentTime = 0;\n void audio\n .play()\n .then(() => {\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n scheduleJoinLeaveTrim(audio, soundType);\n }\n })\n .catch((error) => {\n // Игнорируем ошибки воспроизведения (например, если пользователь не взаимодействовал со страницей)\n console.warn(`⚠️ Failed to play sound ${soundType}:`, error);\n });\n } catch (error) {\n console.error(`❌ Error playing sound ${soundType}:`, error);\n }\n};\n","/** События телеметрии шумоподавления для umami. */\nexport const NOISE_CANCELLATION_EVENTS = {\n MODE_CHANGED: 'noise_cancellation_mode_changed',\n APPLY_FAILED: 'noise_cancellation_apply_failed',\n FALLBACK_TO_WEBRTC: 'noise_cancellation_fallback_to_webrtc',\n KRISP_UNSUPPORTED_BROWSER: 'noise_cancellation_krisp_unsupported_browser',\n} as const;\n\ntype NoiseCancellationEvent =\n (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];\n\nfunction getUmami(): { track: (name: string, data?: Record<string, string>) => void } | undefined {\n if (typeof window === 'undefined') return undefined;\n const win = window as Window & {\n umami?: { track: (name: string, data?: Record<string, string>) => void };\n };\n return win.umami;\n}\n\nexport function trackNoiseCancellationEvent(\n event: NoiseCancellationEvent,\n data?: Record<string, string>,\n): void {\n const umami = getUmami();\n if (umami) {\n umami.track(event, data);\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/useFullScreen.ts","../src/useKeyPress.ts","../src/useMedia.ts","../src/usePhoneLayout.ts","../src/livekit.ts","../src/sounds.ts","../src/noiseCancellationTelemetry.ts"],"names":["useEffect","useState"],"mappings":"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","file":"index.mjs","sourcesContent":["import { useState, useEffect } from 'react';\n\nexport const useFullScreen = (containerId: string) => {\n const [isFullScreen, setIsFullScreen] = useState(false);\n\n const hasStandardFullscreen =\n typeof document !== 'undefined' &&\n document.fullscreenEnabled &&\n typeof HTMLElement.prototype.requestFullscreen === 'function';\n\n const isFullScreenSupported = hasStandardFullscreen;\n\n const toggleFullScreen = () => {\n const element = document.getElementById(containerId);\n const fullScreen = document.fullscreenElement;\n\n if (fullScreen) {\n document.exitFullscreen();\n setIsFullScreen(false);\n } else {\n element?.requestFullscreen();\n setIsFullScreen(true);\n }\n };\n\n useEffect(() => {\n if (!isFullScreenSupported) return;\n\n const handleFullScreenChange = () => {\n const fullScreen = !!document.fullscreenElement;\n setIsFullScreen(fullScreen);\n };\n\n document.addEventListener('fullscreenchange', handleFullScreenChange);\n\n return () => {\n document.removeEventListener('fullscreenchange', handleFullScreenChange);\n };\n }, [isFullScreenSupported]);\n\n return { toggleFullScreen, isFullScreen, isFullScreenSupported };\n};\n","import { useEffect } from 'react';\n\nexport const useKeyPress = (targetKey: string, callback: (event: KeyboardEvent) => void) => {\n useEffect(() => {\n // console.log(`Added listener for ${targetKey}`);\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === targetKey) {\n callback(event);\n }\n };\n\n window.addEventListener('keydown', handleKeyDown);\n return () => {\n // console.log(`Removed listener for ${targetKey}`);\n window.removeEventListener('keydown', handleKeyDown);\n };\n }, [targetKey, callback]);\n};\n","import { useEffect, useState } from 'react';\n\nconst isBrowser = typeof window !== 'undefined';\n\nconst getInitialState = (query: string, defaultState?: boolean) => {\n // Prevent a React hydration mismatch when a default value\n // is provided by not defaulting to window.matchMedia(query).matches.\n if (defaultState !== undefined) {\n return defaultState;\n }\n\n if (isBrowser) {\n return window.matchMedia(query).matches;\n }\n\n return false;\n};\n\nexport const useMedia = (query: string, defaultState?: boolean) => {\n const [state, setState] = useState(getInitialState(query, defaultState));\n\n useEffect(() => {\n let mounted = true;\n const mql = window.matchMedia(query);\n const onChange = () => {\n if (!mounted) {\n return;\n }\n setState(!!mql.matches);\n };\n\n mql.addEventListener('change', onChange);\n setState(mql.matches);\n\n return () => {\n mounted = false;\n mql.removeEventListener('change', onChange);\n };\n }, [query]);\n\n return state;\n};\n","import { useMedia } from './useMedia';\n\n/** Только телефон: планшеты и iPad используют десктопный compact-вид. */\nexport function usePhoneLayout(): boolean {\n return useMedia('(max-width: 720px)');\n}\n","import { getBrowser, LocalParticipant, LogLevel, Participant, setLogLevel } from 'livekit-client';\n\nexport const silenceLiveKitLogs = (shouldSilenceLogs: boolean) => {\n setLogLevel(shouldSilenceLogs ? LogLevel.silent : LogLevel.debug);\n};\n\nexport function isFireFox(): boolean {\n return getBrowser()?.name === 'Firefox';\n}\n\nexport function isChromiumBased(): boolean {\n return getBrowser()?.name === 'Chrome';\n}\n\nexport function isSafari(): boolean {\n return getBrowser()?.name === 'Safari';\n}\n\nexport function isLocal(p: Participant) {\n return p instanceof LocalParticipant;\n}\n\nexport function isMacintosh() {\n return navigator.platform.indexOf('Mac') > -1;\n}\n","/**\n * Утилита для воспроизведения звуков уведомлений в ВКС\n */\n\nconst SOUND_PATHS = {\n chatMessage: '/sounds/chat-message.wav',\n handRaise: '/sounds/hand-raise.wav',\n userJoin: '/sounds/user-join-to-call.mp3',\n userLeft: '/sounds/user-left-from-call.mp3',\n} as const;\n\ntype SoundType = keyof typeof SOUND_PATHS;\n\n/** Для join/left — воспроизводим только начало файла (мс). */\nconst JOIN_LEAVE_PLAY_MS = 500;\n\ntype TrimmedSoundType = Extract<SoundType, 'userJoin' | 'userLeft'>;\nconst trimTimeouts = new Map<TrimmedSoundType, ReturnType<typeof setTimeout>>();\n\n// Кэш для Audio объектов, чтобы не создавать их каждый раз\nconst soundCache = new Map<SoundType, HTMLAudioElement>();\n\n/**\n * Получает или создает Audio объект для звука\n */\nconst getAudio = (soundType: SoundType): HTMLAudioElement => {\n if (!soundCache.has(soundType)) {\n const audio = new Audio(SOUND_PATHS[soundType]);\n audio.preload = 'auto';\n soundCache.set(soundType, audio);\n }\n return soundCache.get(soundType)!;\n};\n\nconst clearJoinLeaveTrim = (soundType: TrimmedSoundType) => {\n const id = trimTimeouts.get(soundType);\n if (id !== undefined) {\n clearTimeout(id);\n trimTimeouts.delete(soundType);\n }\n};\n\nconst scheduleJoinLeaveTrim = (audio: HTMLAudioElement, soundType: TrimmedSoundType) => {\n clearJoinLeaveTrim(soundType);\n const id = setTimeout(() => {\n try {\n audio.pause();\n audio.currentTime = 0;\n } catch {\n /* ignore */\n }\n trimTimeouts.delete(soundType);\n }, JOIN_LEAVE_PLAY_MS);\n trimTimeouts.set(soundType, id);\n};\n\n/**\n * Воспроизводит звук с указанной громкостью\n * @param soundType - тип звука (chatMessage или handRaise)\n * @param volume - громкость от 0 до 1 (0 = беззвучно, 1 = максимальная громкость)\n */\nexport const playSound = (soundType: SoundType, volume: number = 1): void => {\n try {\n const audio = getAudio(soundType);\n\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n clearJoinLeaveTrim(soundType);\n }\n\n // Устанавливаем громкость (от 0 до 1)\n const clampedVolume = Math.max(0, Math.min(1, volume));\n audio.volume = clampedVolume;\n\n // Сбрасываем позицию на начало и воспроизводим\n audio.currentTime = 0;\n void audio\n .play()\n .then(() => {\n if (soundType === 'userJoin' || soundType === 'userLeft') {\n scheduleJoinLeaveTrim(audio, soundType);\n }\n })\n .catch((error) => {\n // Игнорируем ошибки воспроизведения (например, если пользователь не взаимодействовал со страницей)\n console.warn(`⚠️ Failed to play sound ${soundType}:`, error);\n });\n } catch (error) {\n console.error(`❌ Error playing sound ${soundType}:`, error);\n }\n};\n","/** События телеметрии шумоподавления для umami. */\nexport const NOISE_CANCELLATION_EVENTS = {\n MODE_CHANGED: 'noise_cancellation_mode_changed',\n APPLY_FAILED: 'noise_cancellation_apply_failed',\n FALLBACK_TO_WEBRTC: 'noise_cancellation_fallback_to_webrtc',\n KRISP_UNSUPPORTED_BROWSER: 'noise_cancellation_krisp_unsupported_browser',\n} as const;\n\ntype NoiseCancellationEvent =\n (typeof NOISE_CANCELLATION_EVENTS)[keyof typeof NOISE_CANCELLATION_EVENTS];\n\nfunction getUmami(): { track: (name: string, data?: Record<string, string>) => void } | undefined {\n if (typeof window === 'undefined') return undefined;\n const win = window as Window & {\n umami?: { track: (name: string, data?: Record<string, string>) => void };\n };\n return win.umami;\n}\n\nexport function trackNoiseCancellationEvent(\n event: NoiseCancellationEvent,\n data?: Record<string, string>,\n): void {\n const umami = getUmami();\n if (umami) {\n umami.track(event, data);\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xipkg/calls-utils",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {