diff --git a/client/.yarn/install-state.gz b/client/.yarn/install-state.gz index ce5a4f7..219de17 100644 Binary files a/client/.yarn/install-state.gz and b/client/.yarn/install-state.gz differ diff --git a/client/app/app.vue b/client/app/app.vue index 0ca5345..88a6744 100644 --- a/client/app/app.vue +++ b/client/app/app.vue @@ -1,24 +1,7 @@ - - - - - - + diff --git a/client/app/assets/styles/main.scss b/client/app/assets/styles/main.scss index 29092ff..ae8c309 100644 --- a/client/app/assets/styles/main.scss +++ b/client/app/assets/styles/main.scss @@ -1,5 +1,13 @@ + html, body { font-family: 'Inter', sans-serif; - font-size: 14px; + font-size: 16px; + + height: 100%; + overflow: hidden; +} + +#__nuxt { + height: 100%; } \ No newline at end of file diff --git a/client/app/assets/styles/primeicons.css b/client/app/assets/styles/primeicons.css new file mode 100644 index 0000000..84760ed --- /dev/null +++ b/client/app/assets/styles/primeicons.css @@ -0,0 +1 @@ +@import 'primeicons/primeicons.css'; diff --git a/client/app/assets/styles/tailwind.css b/client/app/assets/styles/tailwind.css new file mode 100644 index 0000000..bfdbdb9 --- /dev/null +++ b/client/app/assets/styles/tailwind.css @@ -0,0 +1,2 @@ +@import "tailwindcss"; +@import "tailwindcss-primeui"; \ No newline at end of file diff --git a/client/app/components.d.ts b/client/app/components.d.ts index 6bdd537..3b00e52 100644 --- a/client/app/components.d.ts +++ b/client/app/components.d.ts @@ -8,9 +8,27 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + PrimeAccordion: typeof import('primevue/accordion')['default'] + PrimeAccordionContent: typeof import('primevue/accordioncontent')['default'] + PrimeAccordionHeader: typeof import('primevue/accordionheader')['default'] + PrimeAccordionPanel: typeof import('primevue/accordionpanel')['default'] + PrimeAvatar: typeof import('primevue/avatar')['default'] + PrimeBadge: typeof import('primevue/badge')['default'] PrimeButton: typeof import('primevue/button')['default'] - PrimeChip: typeof import('primevue/chip')['default'] - PrimeProgressSpinner: typeof import('primevue/progressspinner')['default'] + PrimeButtonGroup: typeof import('primevue/buttongroup')['default'] + PrimeCard: typeof import('primevue/card')['default'] + PrimeContextMenu: typeof import('primevue/contextmenu')['default'] + PrimeDivider: typeof import('primevue/divider')['default'] + PrimeDrawer: typeof import('primevue/drawer')['default'] + PrimeFloatLabel: typeof import('primevue/floatlabel')['default'] + PrimeInputText: typeof import('primevue/inputtext')['default'] + PrimeMenu: typeof import('primevue/menu')['default'] + PrimeOverlayBadge: typeof import('primevue/overlaybadge')['default'] + PrimePopover: typeof import('primevue/popover')['default'] + PrimeScrollPanel: typeof import('primevue/scrollpanel')['default'] + PrimeSlider: typeof import('primevue/slider')['default'] + PrimeTag: typeof import('primevue/tag')['default'] + PrimeToolbar: typeof import('primevue/toolbar')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] } diff --git a/client/app/components/ClientRow.vue b/client/app/components/ClientRow.vue new file mode 100644 index 0000000..0442fa2 --- /dev/null +++ b/client/app/components/ClientRow.vue @@ -0,0 +1,54 @@ + + + + + + + {{ client.username }} + + + {{ client.id }} + + + + + + + + + + + + Volume + {{ volume }} + + + + + + + + + diff --git a/client/app/composables/use-global-state.ts b/client/app/composables/use-global-state.ts new file mode 100644 index 0000000..4819da5 --- /dev/null +++ b/client/app/composables/use-global-state.ts @@ -0,0 +1,18 @@ +import type { Client } from '#shared/types' +import { createGlobalState, useLocalStorage } from '@vueuse/core' + +export const useGlobalState = createGlobalState(() => { + const username = useLocalStorage('username', '') + + const clients = shallowRef([]) + + function reset() { + clients.value = [] + } + + return { + username, + clients, + reset, + } +}) diff --git a/client/app/composables/use-mediasoup.ts b/client/app/composables/use-mediasoup.ts index 25b1e73..fd1a083 100644 --- a/client/app/composables/use-mediasoup.ts +++ b/client/app/composables/use-mediasoup.ts @@ -1,6 +1,5 @@ -import type { type Consumer, Producer } from 'mediasoup-client/types' import type { Socket } from 'socket.io-client' -import { createGlobalState } from '@vueuse/core' +import { createSharedComposable } from '@vueuse/core' import * as mediasoupClient from 'mediasoup-client' import { io } from 'socket.io-client' @@ -17,175 +16,362 @@ const ICE_SERVERS: RTCIceServer[] = [ { urls: 'stun:stun4.l.google.com:5349' }, ] -export const useMediasoup = createGlobalState(() => { - const socket: Socket = io('https://api.koptilnya.xyz/webrtc', { - path: '/chad/ws', - transports: ['websocket'], - }) +export const useMediasoup = createSharedComposable(() => { + const state = useGlobalState() - const initializing = ref(false) - const connected = ref(false) - const streams = shallowRef>({}) - let device: mediasoupClient.Device - let sendTransport: mediasoupClient.types.Transport - let recvTransport: mediasoupClient.types.Transport + const socket = shallowRef() - socket.on('producers', async (producers) => { - watch(connected, async () => { - if (!connected.value) - return + const device = shallowRef() + const rtpCapabilities = shallowRef() + const sendTransport = shallowRef() + const recvTransport = shallowRef() - for (const producer of producers) { - await consume(producer.producerId) - } - }, { immediate: true }) - }) + const micProducer = shallowRef() + const webcamProducer = shallowRef() + const shareProducer = shallowRef() - socket.on('newProducer', async ({ producerId }) => { - await consume(producerId) - }) + const producers = shallowRef>(new Map()) + const consumers = shallowRef>(new Map()) - socket.on('producerClosed', async (producerId: Producer['id']) => { - delete streams.value[producerId] + // + // socket.on('producers', async (producers) => { + // watch(connected, async () => { + // if (!connected.value) + // return + // + // for (const producer of producers) { + // await consume(producer.producerId) + // } + // }, { immediate: true }) + // }) + // + // socket.on('newProducer', async ({ producerId }) => { + // await consume(producerId) + // }) + // + // socket.on('producerClosed', async (producerId: Producer['id']) => { + // delete streams.value[producerId] + // + // triggerRef(streams) + // }) + // + // async function consume(producerId: number) { + // const params = await socket.emitWithAck('consume', { + // producerId, + // transportId: recvTransport.id, + // rtpCapabilities: device.rtpCapabilities, + // }) + // + // if (params?.error) { + // console.error('consume error:', params.error) + // return + // } + // + // const consumer = await recvTransport.consume({ + // ...params, + // id: params.consumerId, + // }) + // + // const stream = new MediaStream([consumer.track]) + // + // streams.value[producerId] = stream + // + // triggerRef(streams) + // } - triggerRef(streams) - }) + watch(socket, (socket, prevSocket) => { + if (prevSocket) { + prevSocket.close() - async function consume(producerId: number) { - const params = await socket.emitWithAck('consume', { - producerId, - transportId: recvTransport.id, - rtpCapabilities: device.rtpCapabilities, - }) + dispose() + state.reset() + } - if (params?.error) { - console.error('consume error:', params.error) + if (!socket) { return } - const consumer = await recvTransport.consume({ - ...params, - id: params.consumerId, + socket.onAny((event, ...args) => { + console.log('[onAny]', event, args) }) - const stream = new MediaStream([consumer.track]) - - streams.value[producerId] = stream - - triggerRef(streams) - } - - async function loadDevice() { - device = new mediasoupClient.Device() - const rtpCapabilities = await socket.emitWithAck('getRtpCapabilities') - await device.load({ routerRtpCapabilities: rtpCapabilities }) - } - - async function createSendTransport() { - const params = await socket.emitWithAck('createTransport') - sendTransport = device.createSendTransport({ - ...params, - iceServers: [ - ...ICE_SERVERS, - ...(params.iceServers ?? []), - ], + socket.onAnyOutgoing((event, ...args) => { + console.log('[onAnyOutgoing]', event, args) }) - sendTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { - try { - await socket.emitWithAck('connectTransport', { - transportId: sendTransport.id, - dtlsParameters, - }) + socket.on('connect', () => { + if (!state.username.value) + state.username.value = socket.id! - callback() - } - catch (error) { - if (error instanceof Error) { - errback(error) - } - } + join() }) - sendTransport.on('produce', async ({ kind, rtpParameters }, callback, errback) => { - try { - const { producerId } = await socket.emitWithAck('produce', { - transportId: sendTransport.id, + socket.on('newPeer', (client) => { + state.clients.value.push(client) + triggerRef(state.clients) + }) + + socket.on('peerClosed', (id) => { + state.clients.value = state.clients.value.filter(client => client.id !== id) + }) + + socket.on( + 'newConsumer', + async ( + { id, producerId, kind, rtpParameters, peerId, appData, producerPaused }, + cb, + ) => { + console.log({ id, producerId, kind, rtpParameters, peerId, appData }, cb) + + if (!recvTransport.value) + return + + const consumer = await recvTransport.value.consume({ + id, + producerId, kind, rtpParameters, + streamId: `${peerId}-${appData.share ? 'share' : 'mic-webcam'}`, + appData: { ...appData, peerId }, }) - callback({ id: producerId }) - } - catch (error) { - if (error instanceof Error) { - errback(error) - } - } + + consumers.value.set(consumer.id, consumer) + triggerRef(consumers) + + consumer.on('transportclose', () => { + consumers.value.delete(consumer.id) + triggerRef(consumers) + }) + }, + ) + + socket.on('disconnect', () => { + sendTransport.value?.close() + sendTransport.value = undefined + + recvTransport.value?.close() + recvTransport.value = undefined }) + }, { immediate: true, flush: 'sync' }) + + onBeforeUnmount(() => { + socket.value?.close() + }) + + async function join() { + if (!socket.value) + return + + device.value = new mediasoupClient.Device() + rtpCapabilities.value = await socket.value.emitWithAck('getRtpCapabilities') + + await device.value.load({ routerRtpCapabilities: rtpCapabilities.value! }) + + // Send transport + { + const transportInfo = await socket.value.emitWithAck('createTransport', { producing: true, consuming: false }) + sendTransport.value = device.value.createSendTransport({ + ...transportInfo, + iceServers: [ + ...ICE_SERVERS, + ...(transportInfo.iceServers ?? []), + ], + }) + + sendTransport.value.on('connect', async ({ dtlsParameters }, callback, errback) => { + try { + await socket.value!.emitWithAck('connectTransport', { + transportId: sendTransport.value!.id, + dtlsParameters, + }) + + callback() + } + catch (error) { + if (error instanceof Error) { + errback(error) + } + } + }) + + sendTransport.value.on('produce', async ({ kind, rtpParameters, appData }, callback, errback) => { + try { + const { id } = await socket.value!.emitWithAck('produce', { + transportId: sendTransport.value!.id, + kind, + rtpParameters, + appData, + }) + callback({ id }) + } + catch (error) { + if (error instanceof Error) { + errback(error) + } + } + }) + } + + // Recv Transport + { + const transportInfo = await socket.value.emitWithAck('createTransport', { producing: false, consuming: true }) + recvTransport.value = device.value.createRecvTransport({ + ...transportInfo, + iceServers: [ + ...ICE_SERVERS, + ...(transportInfo.iceServers ?? []), + ], + }) + + recvTransport.value.on('connect', async ({ dtlsParameters }, callback, errback) => { + try { + await socket.value!.emitWithAck('connectTransport', { + transportId: recvTransport.value!.id, + dtlsParameters, + }) + + callback() + } + catch (error) { + if (error instanceof Error) { + errback(error) + } + } + }) + } + + const result = await socket.value.emitWithAck('join', { username: state.username.value, rtpCapabilities: rtpCapabilities.value }) + + state.clients.value = result + + await enableMic() } - async function publishMic() { - const devices = await navigator.mediaDevices.enumerateDevices() - console.log(devices) + async function enableMic() { + if (micProducer.value) + return + + if (!device.value || !sendTransport.value) + return + + if (!device.value.canProduce('audio')) + return const stream = await navigator.mediaDevices.getUserMedia({ audio: { autoGainControl: false, noiseSuppression: true, echoCancellation: false, + channelCount: 2, }, }) const track = stream.getAudioTracks()[0] - await sendTransport.produce({ track }) - } - - async function createRecvTransport() { - const params = await socket.emitWithAck('createTransport') - recvTransport = device.createRecvTransport({ - ...params, - iceServers: [ - ...ICE_SERVERS, - ...(params.iceServers ?? []), - ], + micProducer.value = await sendTransport.value.produce({ + track, + codecOptions: { + opusStereo: true, + opusDtx: true, // Меньше пакетов летит когда тишина + opusFec: false, // Фиксит пакет лос + }, }) - recvTransport.on('connect', async ({ dtlsParameters }, callback, errback) => { - try { - await socket.emitWithAck('connectTransport', { - transportId: recvTransport.id, - dtlsParameters, - }) + producers.value.set(micProducer.value.id, micProducer.value) + triggerRef(producers) - callback() - } - catch (error) { - if (error instanceof Error) { - errback(error) - } - } + micProducer.value.on('transportclose', () => { + micProducer.value = undefined + }) + + micProducer.value.on('trackended', () => { + disableMic() }) } - (async () => { - if (initializing.value || connected.value) + async function disableMic() { + if (!micProducer.value) return - initializing.value = true - connected.value = false + producers.value.delete(micProducer.value.id) + triggerRef(producers) - await loadDevice() - await createSendTransport() - await createRecvTransport() - await publishMic() + try { + micProducer.value.close() - initializing.value = false - connected.value = true - })() + await socket.value?.emitWithAck('closeProducer', { + producerId: micProducer.value.id, + }) + } + catch { + } + + micProducer.value = undefined + } + + async function muteMic() { + if (!micProducer.value) + return + + try { + micProducer.value.pause() + + await socket.value?.emitWithAck('pauseProducer', { + producerId: micProducer.value.id, + }) + } + catch { + } + } + + async function unmuteMic() { + if (!micProducer.value) + return + + try { + micProducer.value?.resume() + + await socket.value?.emitWithAck('resumeProducer', { + producerId: micProducer.value.id, + }) + } + catch { + } + } + + function init() { + if (socket.value) + return + + socket.value = io('https://api.koptilnya.xyz/webrtc', { + path: '/chad/ws', + transports: ['websocket'], + }) + } + + function dispose() { + device.value = undefined + rtpCapabilities.value = undefined + sendTransport.value = undefined + recvTransport.value = undefined + + micProducer.value = undefined + webcamProducer.value = undefined + shareProducer.value = undefined + + consumers.value = new Map() + } return { - initializing, - connected, - streams, + init, + sendTransport, + recvTransport, + socket, + rtpCapabilities, + device, + producers, + consumers, + micProducer, + webcamProducer, + shareProducer, } }) diff --git a/client/app/middleware/build-info.global.ts b/client/app/middleware/01.build-info.global.ts similarity index 100% rename from client/app/middleware/build-info.global.ts rename to client/app/middleware/01.build-info.global.ts diff --git a/client/app/middleware/02.auth.global.ts b/client/app/middleware/02.auth.global.ts new file mode 100644 index 0000000..25c51c8 --- /dev/null +++ b/client/app/middleware/02.auth.global.ts @@ -0,0 +1,19 @@ +export default defineNuxtRouteMiddleware((to, from) => { + const { username } = useGlobalState() + + if (!username.value && to.name !== 'Login') { + console.log('yes') + return navigateTo({ name: 'Login' }) + } + + if (!username.value) + return + + const { init } = useMediasoup() + + init() + + if (to.name === 'Login') { + return navigateTo('/') + } +}) diff --git a/client/app/pages/index.vue b/client/app/pages/index.vue new file mode 100644 index 0000000..249917d --- /dev/null +++ b/client/app/pages/index.vue @@ -0,0 +1,47 @@ + + + + + + Шальные сиськи 18+ + + + + + + + + + + + + + + + + + + + + diff --git a/client/app/pages/login.vue b/client/app/pages/login.vue new file mode 100644 index 0000000..ba5669e --- /dev/null +++ b/client/app/pages/login.vue @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + diff --git a/client/nuxt.config.ts b/client/nuxt.config.ts index cbb404c..55a7ae6 100644 --- a/client/nuxt.config.ts +++ b/client/nuxt.config.ts @@ -1,4 +1,5 @@ import Aura from '@primeuix/themes/aura' +import tailwindcss from '@tailwindcss/vite' export default defineNuxtConfig({ compatibilityDate: '2025-09-29', @@ -18,11 +19,18 @@ export default defineNuxtConfig({ prefix: 'Prime', }, }, - css: ['@/assets/styles/main.scss'], + css: [ + '@/assets/styles/tailwind.css', + '@/assets/styles/primeicons.css', + '@/assets/styles/main.scss', + ], devServer: { // host: '0', }, vite: { + plugins: [ + tailwindcss(), + ], clearScreen: false, envPrefix: ['VITE_', 'TAURI_'], server: { diff --git a/client/package.json b/client/package.json index a4dbc71..d3d5a24 100644 --- a/client/package.json +++ b/client/package.json @@ -10,13 +10,19 @@ "postinstall": "nuxt prepare" }, "dependencies": { + "@formkit/auto-animate": "^0.9.0", "@nuxt/fonts": "^0.11.4", "@primeuix/themes": "^1.2.5", + "@tailwindcss/vite": "^4.1.14", "@vueuse/core": "^13.9.0", "mediasoup-client": "^3.16.7", "nuxt": "^4.1.2", + "postcss": "^8.5.6", + "primeicons": "^7.0.0", "primevue": "^4.4.0", "socket.io-client": "^4.8.1", + "tailwindcss": "^4.1.14", + "tailwindcss-primeui": "^0.6.1", "vue": "^3.5.22", "vue-router": "^4.5.1" }, diff --git a/client/shared/types.ts b/client/shared/types.ts new file mode 100644 index 0000000..8728f86 --- /dev/null +++ b/client/shared/types.ts @@ -0,0 +1,7 @@ +import type * as mediasoupClient from 'mediasoup-client' + +export interface Client { + id: string + username: string + consumers: mediasoupClient.types.Consumer['id'][] +} diff --git a/client/yarn.lock b/client/yarn.lock index db1f1e5..164c9e9 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -494,7 +494,7 @@ __metadata: languageName: node linkType: hard -"@emnapi/wasi-threads@npm:1.1.0": +"@emnapi/wasi-threads@npm:1.1.0, @emnapi/wasi-threads@npm:^1.1.0": version: 1.1.0 resolution: "@emnapi/wasi-threads@npm:1.1.0" dependencies: @@ -849,6 +849,13 @@ __metadata: languageName: node linkType: hard +"@formkit/auto-animate@npm:^0.9.0": + version: 0.9.0 + resolution: "@formkit/auto-animate@npm:0.9.0" + checksum: 10c0/5337bb905761d347cc07cf323b54fbd9967c1eac5469cb63f6ad67cf6705b08493c80ddafc470f317eee82fcc1c05f34391eb05896d41aa9f2f8a02738614ce4 + languageName: node + linkType: hard + "@humanfs/core@npm:^0.19.1": version: 0.19.1 resolution: "@humanfs/core@npm:0.19.1" @@ -920,7 +927,7 @@ __metadata: languageName: node linkType: hard -"@jridgewell/remapping@npm:^2.3.5": +"@jridgewell/remapping@npm:^2.3.4, @jridgewell/remapping@npm:^2.3.5": version: 2.3.5 resolution: "@jridgewell/remapping@npm:2.3.5" dependencies: @@ -1024,6 +1031,17 @@ __metadata: languageName: node linkType: hard +"@napi-rs/wasm-runtime@npm:^1.0.5": + version: 1.0.6 + resolution: "@napi-rs/wasm-runtime@npm:1.0.6" + dependencies: + "@emnapi/core": "npm:^1.5.0" + "@emnapi/runtime": "npm:^1.5.0" + "@tybys/wasm-util": "npm:^0.10.1" + checksum: 10c0/af48168c6e13c970498fda3ce7238234a906bc69dd474dc9abd560cdf8a7dea6410147afec8f0191a1d19767c8347d8ec0125a8a93225312f7ac37e06e8c15ad + languageName: node + linkType: hard + "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -2353,6 +2371,172 @@ __metadata: languageName: node linkType: hard +"@tailwindcss/node@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/node@npm:4.1.14" + dependencies: + "@jridgewell/remapping": "npm:^2.3.4" + enhanced-resolve: "npm:^5.18.3" + jiti: "npm:^2.6.0" + lightningcss: "npm:1.30.1" + magic-string: "npm:^0.30.19" + source-map-js: "npm:^1.2.1" + tailwindcss: "npm:4.1.14" + checksum: 10c0/dcdb53217534b5220e8ffd0357848b542935aa5ebcae691ff1ac2924c5c0b89d6150d938ff69c776d9835e53fdb29b6db78367930985bd50b367ddb646778239 + languageName: node + linkType: hard + +"@tailwindcss/oxide-android-arm64@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-android-arm64@npm:4.1.14" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-arm64@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-darwin-arm64@npm:4.1.14" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-darwin-x64@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-darwin-x64@npm:4.1.14" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-freebsd-x64@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-freebsd-x64@npm:4.1.14" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-linux-arm-gnueabihf@npm:4.1.14" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-gnu@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-linux-arm64-gnu@npm:4.1.14" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-arm64-musl@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-linux-arm64-musl@npm:4.1.14" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-gnu@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-linux-x64-gnu@npm:4.1.14" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@tailwindcss/oxide-linux-x64-musl@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-linux-x64-musl@npm:4.1.14" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@tailwindcss/oxide-wasm32-wasi@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-wasm32-wasi@npm:4.1.14" + dependencies: + "@emnapi/core": "npm:^1.5.0" + "@emnapi/runtime": "npm:^1.5.0" + "@emnapi/wasi-threads": "npm:^1.1.0" + "@napi-rs/wasm-runtime": "npm:^1.0.5" + "@tybys/wasm-util": "npm:^0.10.1" + tslib: "npm:^2.4.0" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-arm64-msvc@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-win32-arm64-msvc@npm:4.1.14" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@tailwindcss/oxide-win32-x64-msvc@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide-win32-x64-msvc@npm:4.1.14" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@tailwindcss/oxide@npm:4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/oxide@npm:4.1.14" + dependencies: + "@tailwindcss/oxide-android-arm64": "npm:4.1.14" + "@tailwindcss/oxide-darwin-arm64": "npm:4.1.14" + "@tailwindcss/oxide-darwin-x64": "npm:4.1.14" + "@tailwindcss/oxide-freebsd-x64": "npm:4.1.14" + "@tailwindcss/oxide-linux-arm-gnueabihf": "npm:4.1.14" + "@tailwindcss/oxide-linux-arm64-gnu": "npm:4.1.14" + "@tailwindcss/oxide-linux-arm64-musl": "npm:4.1.14" + "@tailwindcss/oxide-linux-x64-gnu": "npm:4.1.14" + "@tailwindcss/oxide-linux-x64-musl": "npm:4.1.14" + "@tailwindcss/oxide-wasm32-wasi": "npm:4.1.14" + "@tailwindcss/oxide-win32-arm64-msvc": "npm:4.1.14" + "@tailwindcss/oxide-win32-x64-msvc": "npm:4.1.14" + detect-libc: "npm:^2.0.4" + tar: "npm:^7.5.1" + dependenciesMeta: + "@tailwindcss/oxide-android-arm64": + optional: true + "@tailwindcss/oxide-darwin-arm64": + optional: true + "@tailwindcss/oxide-darwin-x64": + optional: true + "@tailwindcss/oxide-freebsd-x64": + optional: true + "@tailwindcss/oxide-linux-arm-gnueabihf": + optional: true + "@tailwindcss/oxide-linux-arm64-gnu": + optional: true + "@tailwindcss/oxide-linux-arm64-musl": + optional: true + "@tailwindcss/oxide-linux-x64-gnu": + optional: true + "@tailwindcss/oxide-linux-x64-musl": + optional: true + "@tailwindcss/oxide-wasm32-wasi": + optional: true + "@tailwindcss/oxide-win32-arm64-msvc": + optional: true + "@tailwindcss/oxide-win32-x64-msvc": + optional: true + checksum: 10c0/7fdf5345272d0348624cd003f431f10715372d585f0180d32d3c8dd18f5417cdfe7e8c4e86fc504fa1aefd19324fb4c4b174bbefdc054882ae6919ed1160d86c + languageName: node + linkType: hard + +"@tailwindcss/vite@npm:^4.1.14": + version: 4.1.14 + resolution: "@tailwindcss/vite@npm:4.1.14" + dependencies: + "@tailwindcss/node": "npm:4.1.14" + "@tailwindcss/oxide": "npm:4.1.14" + tailwindcss: "npm:4.1.14" + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + checksum: 10c0/38a34602a29fcad23eb80bdb6f3162473d191a1d8ec0bffdee73a1c7b9716de13a1c3705b95baf40eed6ed70e806df35ba03f3cfe541f1758a3440ddb03b6e81 + languageName: node + linkType: hard + "@tauri-apps/cli-darwin-arm64@npm:2.8.4": version: 2.8.4 resolution: "@tauri-apps/cli-darwin-arm64@npm:2.8.4" @@ -3540,18 +3724,24 @@ __metadata: resolution: "chad-client@workspace:." dependencies: "@antfu/eslint-config": "npm:^5.4.1" + "@formkit/auto-animate": "npm:^0.9.0" "@nuxt/fonts": "npm:^0.11.4" "@primeuix/themes": "npm:^1.2.5" "@primevue/nuxt-module": "npm:^4.4.0" + "@tailwindcss/vite": "npm:^4.1.14" "@tauri-apps/cli": "npm:^2.8.4" "@vueuse/core": "npm:^13.9.0" eslint: "npm:^9.36.0" eslint-plugin-format: "npm:^1.0.2" mediasoup-client: "npm:^3.16.7" nuxt: "npm:^4.1.2" + postcss: "npm:^8.5.6" + primeicons: "npm:^7.0.0" primevue: "npm:^4.4.0" sass-embedded: "npm:^1.93.2" socket.io-client: "npm:^4.8.1" + tailwindcss: "npm:^4.1.14" + tailwindcss-primeui: "npm:^0.6.1" typescript: "npm:^5.9.3" vue: "npm:^3.5.22" vue-router: "npm:^4.5.1" @@ -4176,7 +4366,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0": +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.3, detect-libc@npm:^2.0.4": version: 2.1.1 resolution: "detect-libc@npm:2.1.1" checksum: 10c0/97053299c1f68c7c4adf7b78c8d506e1d5f3a3fbc775920aaa0ecf7f8fcc6dfa46338a6ca82fe4500b4a51937def314584265a4ec9d565577485c4496aa7d64e @@ -4359,7 +4549,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.17.1": +"enhanced-resolve@npm:^5.17.1, enhanced-resolve@npm:^5.18.3": version: 5.18.3 resolution: "enhanced-resolve@npm:5.18.3" dependencies: @@ -6006,6 +6196,15 @@ __metadata: languageName: node linkType: hard +"jiti@npm:^2.6.0": + version: 2.6.1 + resolution: "jiti@npm:2.6.1" + bin: + jiti: lib/jiti-cli.mjs + checksum: 10c0/79b2e96a8e623f66c1b703b98ec1b8be4500e1d217e09b09e343471bbb9c105381b83edbb979d01cef18318cc45ce6e153571b6c83122170eefa531c64b6789b + languageName: node + linkType: hard + "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -6178,6 +6377,116 @@ __metadata: languageName: node linkType: hard +"lightningcss-darwin-arm64@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-darwin-arm64@npm:1.30.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-darwin-x64@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-darwin-x64@npm:1.30.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-freebsd-x64@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-freebsd-x64@npm:1.30.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"lightningcss-linux-arm-gnueabihf@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-linux-arm-gnueabihf@npm:1.30.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"lightningcss-linux-arm64-gnu@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-linux-arm64-gnu@npm:1.30.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-arm64-musl@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-linux-arm64-musl@npm:1.30.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-linux-x64-gnu@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-linux-x64-gnu@npm:1.30.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"lightningcss-linux-x64-musl@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-linux-x64-musl@npm:1.30.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"lightningcss-win32-arm64-msvc@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-win32-arm64-msvc@npm:1.30.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"lightningcss-win32-x64-msvc@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss-win32-x64-msvc@npm:1.30.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"lightningcss@npm:1.30.1": + version: 1.30.1 + resolution: "lightningcss@npm:1.30.1" + dependencies: + detect-libc: "npm:^2.0.3" + lightningcss-darwin-arm64: "npm:1.30.1" + lightningcss-darwin-x64: "npm:1.30.1" + lightningcss-freebsd-x64: "npm:1.30.1" + lightningcss-linux-arm-gnueabihf: "npm:1.30.1" + lightningcss-linux-arm64-gnu: "npm:1.30.1" + lightningcss-linux-arm64-musl: "npm:1.30.1" + lightningcss-linux-x64-gnu: "npm:1.30.1" + lightningcss-linux-x64-musl: "npm:1.30.1" + lightningcss-win32-arm64-msvc: "npm:1.30.1" + lightningcss-win32-x64-msvc: "npm:1.30.1" + dependenciesMeta: + lightningcss-darwin-arm64: + optional: true + lightningcss-darwin-x64: + optional: true + lightningcss-freebsd-x64: + optional: true + lightningcss-linux-arm-gnueabihf: + optional: true + lightningcss-linux-arm64-gnu: + optional: true + lightningcss-linux-arm64-musl: + optional: true + lightningcss-linux-x64-gnu: + optional: true + lightningcss-linux-x64-musl: + optional: true + lightningcss-win32-arm64-msvc: + optional: true + lightningcss-win32-x64-msvc: + optional: true + checksum: 10c0/1e1ad908f3c68bf39d964a6735435a8dd5474fb2765076732d64a7b6aa2af1f084da65a9462443a9adfebf7dcfb02fb532fce1d78697f2a9de29c8f40f09aee3 + languageName: node + linkType: hard + "lilconfig@npm:^3.1.3": version: 3.1.3 resolution: "lilconfig@npm:3.1.3" @@ -8369,6 +8678,13 @@ __metadata: languageName: node linkType: hard +"primeicons@npm:^7.0.0": + version: 7.0.0 + resolution: "primeicons@npm:7.0.0" + checksum: 10c0/a7f6344d6db49e785d2040b35f8cae761d15bb8f8ee70ade8981d45438245414e913fe7235921122aade2b0a11a032c98b44dc527be2da11aa791ff145099c06 + languageName: node + linkType: hard + "primevue@npm:4.4.0, primevue@npm:^4.4.0": version: 4.4.0 resolution: "primevue@npm:4.4.0" @@ -9591,6 +9907,22 @@ __metadata: languageName: node linkType: hard +"tailwindcss-primeui@npm:^0.6.1": + version: 0.6.1 + resolution: "tailwindcss-primeui@npm:0.6.1" + peerDependencies: + tailwindcss: ">=3.1.0" + checksum: 10c0/a7a576660d4b42dd31d8851c69a1c67199bf79b74bb0ac7e7ff3c8f721e8f05b41278d9af47550a78b234426e9df68cea802ddd5d28a97b00af28b2979226f74 + languageName: node + linkType: hard + +"tailwindcss@npm:4.1.14, tailwindcss@npm:^4.1.14": + version: 4.1.14 + resolution: "tailwindcss@npm:4.1.14" + checksum: 10c0/c7e9ebfb241707b2a3eb7d465fd326cc8fcfa22e7215e01f67cccec32db8a49a19e17d1f694fc5d0435d55350ea3f863521c52c9bbe6bd790c2009dc8ff516a1 + languageName: node + linkType: hard + "tapable@npm:^2.2.0": version: 2.2.3 resolution: "tapable@npm:2.2.3" @@ -9609,7 +9941,7 @@ __metadata: languageName: node linkType: hard -"tar@npm:^7.4.0, tar@npm:^7.4.3": +"tar@npm:^7.4.0, tar@npm:^7.4.3, tar@npm:^7.5.1": version: 7.5.1 resolution: "tar@npm:7.5.1" dependencies: