From 3d39511629c961e5ead0b2e4d3d0265ae779e657 Mon Sep 17 00:00:00 2001 From: Michael Salim Date: Mon, 25 Nov 2024 18:25:54 +0000 Subject: [PATCH] Feat: Initial worship pads --- plugins/worship-pads/.eslintrc.cjs | 8 + plugins/worship-pads/.prettierrc.json | 1 + plugins/worship-pads/package.json | 47 ++++ plugins/worship-pads/src/consts.ts | 3 + plugins/worship-pads/src/index.ts | 125 ++++++++++ plugins/worship-pads/src/types.ts | 12 + plugins/worship-pads/tsconfig.json | 17 ++ plugins/worship-pads/view/Remote/index.tsx | 116 ++++++++++ .../view/Renderer/HowlerPlayer.ts | 105 +++++++++ plugins/worship-pads/view/Renderer/index.tsx | 83 +++++++ .../worship-pads/view/entries/RemoteEntry.tsx | 26 +++ .../view/entries/RendererEntry.tsx | 11 + plugins/worship-pads/view/entries/remote.tsx | 18 ++ .../worship-pads/view/entries/renderer.tsx | 18 ++ plugins/worship-pads/view/pluginApi.ts | 15 ++ plugins/worship-pads/view/trpc.ts | 5 + plugins/worship-pads/view/tsconfig.json | 8 + plugins/worship-pads/vite.config.ts | 27 +++ production.Dockerfile | 7 + yarn.lock | 214 +----------------- 20 files changed, 653 insertions(+), 213 deletions(-) create mode 100644 plugins/worship-pads/.eslintrc.cjs create mode 100644 plugins/worship-pads/.prettierrc.json create mode 100644 plugins/worship-pads/package.json create mode 100644 plugins/worship-pads/src/consts.ts create mode 100644 plugins/worship-pads/src/index.ts create mode 100644 plugins/worship-pads/src/types.ts create mode 100644 plugins/worship-pads/tsconfig.json create mode 100644 plugins/worship-pads/view/Remote/index.tsx create mode 100644 plugins/worship-pads/view/Renderer/HowlerPlayer.ts create mode 100644 plugins/worship-pads/view/Renderer/index.tsx create mode 100644 plugins/worship-pads/view/entries/RemoteEntry.tsx create mode 100644 plugins/worship-pads/view/entries/RendererEntry.tsx create mode 100644 plugins/worship-pads/view/entries/remote.tsx create mode 100644 plugins/worship-pads/view/entries/renderer.tsx create mode 100644 plugins/worship-pads/view/pluginApi.ts create mode 100644 plugins/worship-pads/view/trpc.ts create mode 100644 plugins/worship-pads/view/tsconfig.json create mode 100644 plugins/worship-pads/vite.config.ts diff --git a/plugins/worship-pads/.eslintrc.cjs b/plugins/worship-pads/.eslintrc.cjs new file mode 100644 index 0000000..7e6a5ba --- /dev/null +++ b/plugins/worship-pads/.eslintrc.cjs @@ -0,0 +1,8 @@ +module.exports = { + root: true, + extends: ["@repo/eslint-config/vite.js"], + parser: "@typescript-eslint/parser", + parserOptions: { + project: true, + }, +}; diff --git a/plugins/worship-pads/.prettierrc.json b/plugins/worship-pads/.prettierrc.json new file mode 100644 index 0000000..d48a9d9 --- /dev/null +++ b/plugins/worship-pads/.prettierrc.json @@ -0,0 +1 @@ +"@repo/prettier-config/index.js" \ No newline at end of file diff --git a/plugins/worship-pads/package.json b/plugins/worship-pads/package.json new file mode 100644 index 0000000..157ca6f --- /dev/null +++ b/plugins/worship-pads/package.json @@ -0,0 +1,47 @@ +{ + "name": "@repo/plugin-worship-pads", + "private": true, + "version": "0.0.0", + "main": "./dist/index.js", + "exports": { + ".": { + "default": "./dist/index.mjs" + } + }, + "scripts": { + "build": "pkgroll && vite build", + "dev": "concurrently \"pkgroll --watch\" \"vite build --watch\"" + }, + "dependencies": { + "@chakra-ui/react": "^2.10.3", + "@emotion/react": "^11.13.3", + "@emotion/styled": "^11.13.0", + "@r2wc/react-to-web-component": "^2.0.3", + "@repo/base-plugin": "*", + "@repo/ui": "*", + "@tanstack/react-query": "^5.59.16", + "@trpc/client": "^11.0.0-rc.593", + "@trpc/react-query": "^11.0.0-rc.593", + "@trpc/server": "^11.0.0-rc.593", + "@types/react": "npm:types-react@rc", + "@types/react-dom": "npm:types-react-dom@rc", + "framer-motion": "^11.11.10", + "howler": "^2.2.4", + "react": "rc", + "react-dom": "rc", + "react-icons": "^5.3.0", + "valtio": "^2.1.1", + "valtio-yjs": "^0.6.0", + "yjs": "^13.6.20" + }, + "devDependencies": { + "@repo/eslint-config": "*", + "@repo/prettier-config": "*", + "@repo/typescript-config": "*", + "@types/howler": "^2", + "@vitejs/plugin-react-swc": "^3.7.1", + "eslint": "^8.57.1", + "pkgroll": "^2.5.1", + "vite": "5.4.10" + } +} diff --git a/plugins/worship-pads/src/consts.ts b/plugins/worship-pads/src/consts.ts new file mode 100644 index 0000000..8ca10c6 --- /dev/null +++ b/plugins/worship-pads/src/consts.ts @@ -0,0 +1,3 @@ +export const pluginName = "worship-pads"; +export const remoteWebComponentTag = "worship-pads-remote"; +export const rendererWebComponentTag = "worship-pads-renderer"; diff --git a/plugins/worship-pads/src/index.ts b/plugins/worship-pads/src/index.ts new file mode 100644 index 0000000..11a38ee --- /dev/null +++ b/plugins/worship-pads/src/index.ts @@ -0,0 +1,125 @@ +import { + ObjectToTypedMap, + Plugin, + ServerPluginApi, + TRPCObject, +} from "@repo/base-plugin/server"; +import { proxy } from "valtio"; +import { bind } from "valtio-yjs"; + +import { + pluginName, + remoteWebComponentTag, + rendererWebComponentTag, +} from "./consts"; +import { PluginBaseData } from "./types"; + +export const init = (serverPluginApi: ServerPluginApi) => { + serverPluginApi.registerTrpcAppRouter(getAppRouter); + serverPluginApi.onPluginDataCreated(pluginName, onPluginDataCreated); + serverPluginApi.onPluginDataLoaded(pluginName, onPluginDataLoaded); + serverPluginApi.registerSceneCreator(pluginName, { + title: "Worship Pads", + description: "Play an ambient pad to back your worship session", + categories: ["Audio"], + }); + + serverPluginApi.serveStatic(pluginName, "out"); + + serverPluginApi.loadJsOnRemoteView(pluginName, `${pluginName}-remote.es.js`); + serverPluginApi.registerRemoteViewWebComponent( + pluginName, + remoteWebComponentTag, + ); + serverPluginApi.loadJsOnRendererView( + pluginName, + `${pluginName}-renderer.es.js`, + ); + serverPluginApi.registerRendererViewWebComponent( + pluginName, + rendererWebComponentTag, + ); +}; + +const onPluginDataCreated = ( + pluginInfo: ObjectToTypedMap>, +) => { + const data = proxy(pluginInfo.toJSON() as Plugin); + const unbind = bind(data, pluginInfo as any); + + data.pluginData.files = [ + { + key: "C", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/C_Major.mp3", + }, + { + key: "Db", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/Db_Major.mp3", + }, + { + key: "D", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/D_Major.mp3", + }, + { + key: "Eb", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/Eb_Major.mp3", + }, + { + key: "E", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/E_Major.mp3", + }, + { + key: "F", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/F_Major.mp3", + }, + { + key: "Gb", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/Gb_Major.mp3", + }, + { + key: "G", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/G_Major.mp3", + }, + { + key: "Ab", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/Ab_Major.mp3", + }, + { + key: "A", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/A_Major.mp3", + }, + { + key: "Bb", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/Bb_Major.mp3", + }, + { + key: "B", + url: "https://raw.githubusercontent.com/Vija02/theopenpresenter-static/refs/heads/main/worship-pads/karl-verkade-bridge/B_Major.mp3", + }, + ]; + + unbind(); + + return {}; +}; + +const onPluginDataLoaded = ( + pluginInfo: ObjectToTypedMap>, +) => { + const data = proxy(pluginInfo.toJSON() as Plugin); + const unbind = bind(data, pluginInfo as any); + + return { + dispose: () => { + unbind(); + }, + }; +}; + +const getAppRouter = (t: TRPCObject) => { + return t.router({}); +}; + +export type AppRouter = ReturnType; + +export * from "./types"; diff --git a/plugins/worship-pads/src/types.ts b/plugins/worship-pads/src/types.ts new file mode 100644 index 0000000..80a5cf5 --- /dev/null +++ b/plugins/worship-pads/src/types.ts @@ -0,0 +1,12 @@ +export type PluginBaseData = { + files: { + key: string; + url: string; + }[]; +}; + +export type PluginRendererData = { + currentKey: string; + isPlaying: boolean; + volume: number; +}; diff --git a/plugins/worship-pads/tsconfig.json b/plugins/worship-pads/tsconfig.json new file mode 100644 index 0000000..28efb62 --- /dev/null +++ b/plugins/worship-pads/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "@repo/typescript-config/base.json", + "compilerOptions": { + "noEmit": false, + "rootDir": ".", + "outDir": "dist", + "tsBuildInfoFile": "dist/tsconfig.tsbuildinfo", + "declarationDir": "dist", + "lib": ["es2018", "esnext.asynciterable"], + "target": "es2018", + "module": "Preserve", + "esModuleInterop": true, + "moduleResolution": "Node" + }, + "include": ["src", "vite.config.ts"], + "references": [{ "path": "../../packages/base-plugin" }] +} diff --git a/plugins/worship-pads/view/Remote/index.tsx b/plugins/worship-pads/view/Remote/index.tsx new file mode 100644 index 0000000..763726d --- /dev/null +++ b/plugins/worship-pads/view/Remote/index.tsx @@ -0,0 +1,116 @@ +import { + Box, + Button, + Grid, + Heading, + Slider, + SliderFilledTrack, + SliderMark, + SliderThumb, + SliderTrack, + Stack, + Text, + useBreakpointValue, +} from "@chakra-ui/react"; +import { FaStop } from "react-icons/fa"; + +import { usePluginAPI } from "../pluginApi"; + +const WorshipPadsRemote = () => { + const pluginApi = usePluginAPI(); + + const files = pluginApi.scene.useData((x) => x.pluginData.files); + const isPlaying = pluginApi.renderer.useData((x) => x.isPlaying); + const volume = pluginApi.renderer.useData((x) => x.volume); + const currentKey = pluginApi.renderer.useData((x) => x.currentKey); + + const mutableRendererData = pluginApi.renderer.useValtioData(); + + const orientation = useBreakpointValue({ + base: "vertical", + md: "horizontal", + }) as "vertical" | "horizontal"; + + return ( + + Worship Pads + + + + {files.map((file) => ( + + ))} + + + + {isPlaying && ( + + )} + + + { + mutableRendererData.volume = v; + }} + height={{ base: "70vh", md: "100%" }} + width={{ base: "80px", md: "100%" }} + > + + 25% + + + 50% + + + 75% + + + + + + + + + ); +}; + +export default WorshipPadsRemote; diff --git a/plugins/worship-pads/view/Renderer/HowlerPlayer.ts b/plugins/worship-pads/view/Renderer/HowlerPlayer.ts new file mode 100644 index 0000000..ab29270 --- /dev/null +++ b/plugins/worship-pads/view/Renderer/HowlerPlayer.ts @@ -0,0 +1,105 @@ +import { Howl } from "howler"; + +const silent = 0.01; + +export class HowlerPlayer { + public sounds: Map; + public currentTrack: string | null; + + public targetVolume: number = 1; + + constructor() { + this.sounds = new Map(); + this.currentTrack = null; + } + + loadTrack(id: string, url: string) { + if (this.sounds.has(id)) return; + + const sound = new Howl({ + src: [url], + html5: true, + loop: true, + preload: true, + }); + this.sounds.set(id, sound); + } + + play(id: string) { + const sound = this.sounds.get(id); + if (sound) { + sound.volume(this.targetVolume); + sound.play(); + this.currentTrack = id; + } + } + + crossFade(fromId: string, toId: string, duration = 1000) { + const fromSound = this.sounds.get(fromId); + const toSound = this.sounds.get(toId); + + if (fromSound && toSound) { + // Start new track at volume 0 + toSound.volume(silent); + toSound.play(); + + // Fade out current track + this.smoothFade(fromSound, fromSound.volume(), silent, duration); + + // Fade in new track + setTimeout(() => { + this.smoothFade(toSound, silent, this.targetVolume, duration); + }, 10); + + // Update current track + this.currentTrack = toId; + + // Stop the old track after fade + setTimeout(() => { + fromSound.stop(); + }, duration); + } + } + + stop(id: string, duration = 1000) { + const sound = this.sounds.get(id); + if (sound) { + this.currentTrack = null; + this.smoothFade(sound, sound.volume(), silent, duration); + setTimeout(() => { + sound.stop(); + }, duration); + } + } + + smoothFade(sound: Howl, from: number, to: number, duration: number) { + const startTime = performance.now(); + const diff = to - from; + + function update() { + const elapsed = performance.now() - startTime; + const progress = Math.min(elapsed / duration, 1); + + // Use easing function for smoother transition + const eased = 0.5 * (1 - Math.cos(progress * Math.PI)); + const newVolume = from + diff * eased; + + sound.volume(newVolume); + + if (progress < 1) { + requestAnimationFrame(update); + } else if (to === 0) { + sound.stop(); + } + } + + requestAnimationFrame(update); + } + + setVolume(id: string, volume: number) { + const sound = this.sounds.get(id); + if (sound) { + sound.volume(volume); + } + } +} diff --git a/plugins/worship-pads/view/Renderer/index.tsx b/plugins/worship-pads/view/Renderer/index.tsx new file mode 100644 index 0000000..8fad65b --- /dev/null +++ b/plugins/worship-pads/view/Renderer/index.tsx @@ -0,0 +1,83 @@ +import { useEffect, useRef, useState } from "react"; + +import { usePluginAPI } from "../pluginApi"; +import { HowlerPlayer } from "./HowlerPlayer"; + +const WorshipPadsRenderer = () => { + const pluginApi = usePluginAPI(); + + const volume = pluginApi.renderer.useData((x) => x.volume); + + const canPlay = pluginApi.audio.useCanPlay(); + + const player = useRef(null); + const [playerLoaded, setPlayerLoaded] = useState(false); + + useEffect(() => { + player.current = new HowlerPlayer(); + player.current.targetVolume = volume; + setPlayerLoaded(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (!canPlay || !playerLoaded) { + return null; + } + + return ; +}; + +const FADE_DURATION = 3000; + +const Player = ({ + player, +}: { + player: React.RefObject; +}) => { + const pluginApi = usePluginAPI(); + + const files = pluginApi.scene.useData((x) => x.pluginData.files); + + const currentKey = pluginApi.renderer.useData((x) => x.currentKey); + const isPlaying = pluginApi.renderer.useData((x) => x.isPlaying); + const volume = pluginApi.renderer.useData((x) => x.volume); + + useEffect(() => { + if (isPlaying) { + // If playing, let's check whether we need to do anything + const file = files.find((x) => x.key === currentKey)!; + player.current?.loadTrack(file.key, file.url); + + if (player.current?.currentTrack) { + // If we're already playing, let's cross fade it + if (player.current.currentTrack !== currentKey) { + player.current?.crossFade( + player.current.currentTrack, + currentKey, + FADE_DURATION, + ); + } + } else { + // Otherwise lets just play normally + player.current?.play(currentKey); + } + } else if (player.current?.currentTrack) { + // If we're not playing, then we should stop + player.current.stop(player.current.currentTrack, FADE_DURATION); + } + }, [currentKey, files, isPlaying, player]); + + // Sync the target volume + useEffect(() => { + if (player.current) { + player.current.targetVolume = volume; + if (player.current.currentTrack) { + player.current.setVolume(player.current.currentTrack, volume); + } + } + }, [player, volume]); + + return null; +}; + +export default WorshipPadsRenderer; diff --git a/plugins/worship-pads/view/entries/RemoteEntry.tsx b/plugins/worship-pads/view/entries/RemoteEntry.tsx new file mode 100644 index 0000000..8bc29be --- /dev/null +++ b/plugins/worship-pads/view/entries/RemoteEntry.tsx @@ -0,0 +1,26 @@ +import { ChakraProvider } from "@chakra-ui/react"; +import { PluginAPIProvider, WebComponentProps } from "@repo/base-plugin/client"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import type { TRPCUntypedClient } from "@trpc/client"; + +import { AppRouter } from "../../src"; +import Remote from "../Remote"; +import { trpc } from "../trpc"; + +const queryClient = new QueryClient(); + +export default function RemoteEntry( + props: WebComponentProps>, +) { + return ( + + + + + + + + + + ); +} diff --git a/plugins/worship-pads/view/entries/RendererEntry.tsx b/plugins/worship-pads/view/entries/RendererEntry.tsx new file mode 100644 index 0000000..8b9746e --- /dev/null +++ b/plugins/worship-pads/view/entries/RendererEntry.tsx @@ -0,0 +1,11 @@ +import { PluginAPIProvider, WebComponentProps } from "@repo/base-plugin/client"; + +import Renderer from "../Renderer"; + +export default function RendererEntry(props: WebComponentProps) { + return ( + + + + ); +} diff --git a/plugins/worship-pads/view/entries/remote.tsx b/plugins/worship-pads/view/entries/remote.tsx new file mode 100644 index 0000000..55cf473 --- /dev/null +++ b/plugins/worship-pads/view/entries/remote.tsx @@ -0,0 +1,18 @@ +import r2wc from "@r2wc/react-to-web-component"; +import { withSuspense } from "@repo/ui"; +import { lazy } from "react"; + +import { remoteWebComponentTag } from "../../src/consts"; + +const Component = r2wc(withSuspense(lazy(() => import("./RemoteEntry"))), { + //@ts-ignore + props: { + yjsPluginSceneData: "", + yjsPluginRendererData: "", + awarenessContext: "", + pluginContext: "", + setRenderCurrentScene: "", + trpcClient: "", + }, +}); +customElements.define(remoteWebComponentTag, Component); diff --git a/plugins/worship-pads/view/entries/renderer.tsx b/plugins/worship-pads/view/entries/renderer.tsx new file mode 100644 index 0000000..2611b04 --- /dev/null +++ b/plugins/worship-pads/view/entries/renderer.tsx @@ -0,0 +1,18 @@ +import r2wc from "@r2wc/react-to-web-component"; +import { withSuspense } from "@repo/ui"; +import { lazy } from "react"; + +import { rendererWebComponentTag } from "../../src/consts"; + +const Component = r2wc(withSuspense(lazy(() => import("./RendererEntry"))), { + //@ts-ignore + props: { + yjsPluginSceneData: "", + yjsPluginRendererData: "", + awarenessContext: "", + pluginContext: "", + setRenderCurrentScene: "", + trpcClient: "", + }, +}); +customElements.define(rendererWebComponentTag, Component); diff --git a/plugins/worship-pads/view/pluginApi.ts b/plugins/worship-pads/view/pluginApi.ts new file mode 100644 index 0000000..4360b2b --- /dev/null +++ b/plugins/worship-pads/view/pluginApi.ts @@ -0,0 +1,15 @@ +import { initPluginApi } from "@repo/base-plugin/client"; +import { PluginAPIContext } from "@repo/base-plugin/client"; +import { useContext } from "react"; + +import { PluginBaseData, PluginRendererData } from "../src/types"; + +type InitPluginApiFunc = typeof initPluginApi< + PluginBaseData, + PluginRendererData +>; + +export function usePluginAPI() { + return useContext(PluginAPIContext) + .pluginAPI as ReturnType; +} diff --git a/plugins/worship-pads/view/trpc.ts b/plugins/worship-pads/view/trpc.ts new file mode 100644 index 0000000..d1bf269 --- /dev/null +++ b/plugins/worship-pads/view/trpc.ts @@ -0,0 +1,5 @@ +import { createTRPCReact } from "@trpc/react-query"; + +import { AppRouter } from "../src"; + +export const trpc = createTRPCReact(); diff --git a/plugins/worship-pads/view/tsconfig.json b/plugins/worship-pads/view/tsconfig.json new file mode 100644 index 0000000..46b9711 --- /dev/null +++ b/plugins/worship-pads/view/tsconfig.json @@ -0,0 +1,8 @@ +{ + "exclude": ["node_modules"], + "extends": "@repo/typescript-config/vite.json", + "compilerOptions": { + "rootDir": "../" + }, + "include": [".", "../src"] +} diff --git a/plugins/worship-pads/vite.config.ts b/plugins/worship-pads/vite.config.ts new file mode 100644 index 0000000..71a611e --- /dev/null +++ b/plugins/worship-pads/vite.config.ts @@ -0,0 +1,27 @@ +import react from "@vitejs/plugin-react-swc"; +import { defineConfig } from "vite"; + +import { pluginName } from "./src/consts"; + +// https://vitejs.dev/config/ +export default defineConfig({ + define: { + "process.env": { + NODE_ENV: "production", + }, + }, + plugins: [react()], + build: { + outDir: "out", + lib: { + entry: ["./view/entries/remote.tsx", "./view/entries/renderer.tsx"], + name: `${pluginName}-views`, + fileName: (format, entryName) => + `${pluginName}-${entryName}.${format}.js`, + }, + rollupOptions: { + external: ["yjs", "react", "react-dom", "react-dom/client"], + }, + target: "esnext", + }, +}); diff --git a/production.Dockerfile b/production.Dockerfile index 42c06ad..2b9e2e1 100644 --- a/production.Dockerfile +++ b/production.Dockerfile @@ -44,6 +44,7 @@ COPY plugins/simple-image/package.json /app/plugins/simple-image/package.json COPY plugins/audio-recorder/package.json /app/plugins/audio-recorder/package.json COPY plugins/video-player/package.json /app/plugins/video-player/package.json COPY plugins/radio/package.json /app/plugins/radio/package.json +COPY plugins/worship-pads/package.json /app/plugins/worship-pads/package.json RUN yarn install @@ -131,6 +132,9 @@ RUN yarn workspace @repo/plugin-video-player build COPY plugins/radio/ /app/plugins/radio/ RUN yarn workspace @repo/plugin-radio build +COPY plugins/worship-pads/ /app/plugins/worship-pads/ +RUN yarn workspace @repo/plugin-worship-pads build + ################################################################################ # Build stage 6 - Combine deps and build, taking only needed files @@ -187,6 +191,9 @@ COPY --from=builder-plugin /app/plugins/video-player/out/ /app/plugins/video-pla COPY --from=builder-plugin /app/plugins/radio/package.json /app/plugins/radio/ COPY --from=builder-plugin /app/plugins/radio/dist/ /app/plugins/radio/dist/ COPY --from=builder-plugin /app/plugins/radio/out/ /app/plugins/radio/out/ +COPY --from=builder-plugin /app/plugins/worship-pads/package.json /app/plugins/worship-pads/ +COPY --from=builder-plugin /app/plugins/worship-pads/dist/ /app/plugins/worship-pads/dist/ +COPY --from=builder-plugin /app/plugins/worship-pads/out/ /app/plugins/worship-pads/out/ # Shared args shouldn't be overridable at runtime (because they're baked into # the built JS). diff --git a/yarn.lock b/yarn.lock index f111f6b..53dacb1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2400,13 +2400,6 @@ __metadata: languageName: node linkType: hard -"@eshaz/web-worker@npm:1.2.2": - version: 1.2.2 - resolution: "@eshaz/web-worker@npm:1.2.2" - checksum: 10c0/4f1ad8287587d294af3c9dabfa88f7ffea54b958cf89ee50d928406436f10b7c4d733bf47e6566a57988c24f3773144fc1ba540dc4a7dd8031e6abb85ba76ba2 - languageName: node - linkType: hard - "@eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": version: 4.4.1 resolution: "@eslint-community/eslint-utils@npm:4.4.1" @@ -5152,15 +5145,13 @@ __metadata: "@types/react": "npm:types-react@rc" "@types/react-dom": "npm:types-react-dom@rc" "@vitejs/plugin-react-swc": "npm:^3.7.1" - audio-buffer-utils: "npm:^5.1.2" eslint: "npm:^8.57.1" framer-motion: "npm:^11.11.10" howler: "npm:^2.2.4" - ogg-opus-decoder: "npm:^1.6.14" - opus-stream-decoder: "npm:1.2.6" pkgroll: "npm:^2.5.1" react: "npm:rc" react-dom: "npm:rc" + react-icons: "npm:^5.3.0" valtio: "npm:^2.1.1" valtio-yjs: "npm:^0.6.0" vite: "npm:5.4.10" @@ -7566,16 +7557,6 @@ __metadata: languageName: node linkType: hard -"@wasm-audio-decoders/common@npm:9.0.5": - version: 9.0.5 - resolution: "@wasm-audio-decoders/common@npm:9.0.5" - dependencies: - "@eshaz/web-worker": "npm:1.2.2" - simple-yenc: "npm:^1.0.4" - checksum: 10c0/59e72231bee7ba5f01fd4f812b626248a1772b5a0fa1e602bbeb66745ee7618209ee9d35e93fb4c1ac586cde6bf00aeb6c5adcb69db9644dc82fd6673c808f52 - languageName: node - linkType: hard - "@whatwg-node/fetch@npm:^0.10.0": version: 0.10.1 resolution: "@whatwg-node/fetch@npm:0.10.1" @@ -8198,13 +8179,6 @@ __metadata: languageName: node linkType: hard -"atob-lite@npm:^2.0.0": - version: 2.0.0 - resolution: "atob-lite@npm:2.0.0" - checksum: 10c0/8073795465dad14aa92b2cd3322472e93dbc8b87da5740150bbae9d716ee6cc254af1c375b7310a475d876eb24c25011584ae9c1277bdb3eb53ebb4cd236f501 - languageName: node - linkType: hard - "atomic-sleep@npm:^1.0.0": version: 1.0.0 resolution: "atomic-sleep@npm:1.0.0" @@ -8212,66 +8186,6 @@ __metadata: languageName: node linkType: hard -"audio-buffer-from@npm:^1.0.0": - version: 1.1.1 - resolution: "audio-buffer-from@npm:1.1.1" - dependencies: - audio-buffer: "npm:^4.0.4" - audio-context: "npm:^1.0.1" - audio-format: "npm:^2.0.0" - is-audio-buffer: "npm:^1.0.11" - is-plain-obj: "npm:^1.1.0" - pcm-convert: "npm:^1.6.0" - pick-by-alias: "npm:^1.2.0" - string-to-arraybuffer: "npm:^1.0.0" - checksum: 10c0/347f67a233cd4536b2f2d201ac200c237ad3a29402f6c822717c42ec738d73f13a19f339e16af5a4cc778f2f094c076003048e568f28ee3ea36f558566083a5b - languageName: node - linkType: hard - -"audio-buffer-utils@npm:^5.1.2": - version: 5.1.2 - resolution: "audio-buffer-utils@npm:5.1.2" - dependencies: - audio-buffer: "npm:^4.0.0" - audio-buffer-from: "npm:^1.0.0" - audio-context: "npm:^1.0.0" - clamp: "npm:^1.0.1" - is-audio-buffer: "npm:^1.0.8" - is-browser: "npm:^2.0.1" - is-buffer: "npm:^2.0.0" - checksum: 10c0/925a05aa042b088f5c6277f3cf841543904bcabd203956779df19dc32e258c6f84ba341b60f6c28a50a6a91f8a842cc14b51252f5c8dded100a5c3708a71c6e7 - languageName: node - linkType: hard - -"audio-buffer@npm:^4.0.0, audio-buffer@npm:^4.0.4": - version: 4.0.4 - resolution: "audio-buffer@npm:4.0.4" - dependencies: - audio-context: "npm:^1.0.0" - checksum: 10c0/03258d23ad049a0bda0f1e49b4bd131cf27023fabb3dc073692d267e32a80a3d1b8cb83fd856eced0b71f7ca9f64ecb716d771e9d1dba70c6ec11a4a5ae8dc55 - languageName: node - linkType: hard - -"audio-context@npm:^1.0.0, audio-context@npm:^1.0.1": - version: 1.0.3 - resolution: "audio-context@npm:1.0.3" - checksum: 10c0/496a48c24602977207d99e7701265af7b0e2fa6a9658136f0b526a6f6651d31e90ddefb670d2f95d6cc81e091d8b74d3edd766d7ad9d43f594ff21a022750cb6 - languageName: node - linkType: hard - -"audio-format@npm:^2.0.0, audio-format@npm:^2.3.2": - version: 2.3.2 - resolution: "audio-format@npm:2.3.2" - dependencies: - is-audio-buffer: "npm:^1.0.11" - is-buffer: "npm:^1.1.5" - is-plain-obj: "npm:^1.1.0" - pick-by-alias: "npm:^1.2.0" - sample-rate: "npm:^2.0.0" - checksum: 10c0/8c63467ea778fcde5419aa6549ecb274a6863e2aec159c11004a9dd595403b809cf072da7dce8b7d08c1ef380f606731ed3e43b8725c2f4fd39f3126e083a9df - languageName: node - linkType: hard - "auto-bind@npm:~4.0.0": version: 4.0.0 resolution: "auto-bind@npm:4.0.0" @@ -9141,13 +9055,6 @@ __metadata: languageName: node linkType: hard -"clamp@npm:^1.0.1": - version: 1.0.1 - resolution: "clamp@npm:1.0.1" - checksum: 10c0/8f95ccbc5d646a98c1d690bce820f3d060a5267242083e8994f70dc504ce441dbc7b8ef13b93819129fc166e7a5b6abd320f109bdba0f49b8dcc794710987c97 - languageName: node - linkType: hard - "classnames@npm:^2.2.6": version: 2.5.1 resolution: "classnames@npm:2.5.1" @@ -9300,13 +9207,6 @@ __metadata: languageName: node linkType: hard -"codec-parser@npm:2.5.0": - version: 2.5.0 - resolution: "codec-parser@npm:2.5.0" - checksum: 10c0/ee017ce76251f75a7feacb5c11ae6c8269d8b76818facf4270e4a0e01c5fc5daaca840372d314a27d7aa12823012d2b46de30bb6c44726c0ca6898a266300c06 - languageName: node - linkType: hard - "collect-v8-coverage@npm:^1.0.0": version: 1.0.2 resolution: "collect-v8-coverage@npm:1.0.2" @@ -13819,20 +13719,6 @@ __metadata: languageName: node linkType: hard -"is-audio-buffer@npm:^1.0.11, is-audio-buffer@npm:^1.0.8": - version: 1.1.0 - resolution: "is-audio-buffer@npm:1.1.0" - checksum: 10c0/af5163b00de2a67c74a6f5c81e59985e7dbc65f3a3fc3dc82a3c9d0e09fdf2c881c18fe8e15fbc4eb6e444844837f78cc039a4b165a3ab4df4b73b36ff7faa9f - languageName: node - linkType: hard - -"is-base64@npm:^0.1.0": - version: 0.1.0 - resolution: "is-base64@npm:0.1.0" - checksum: 10c0/640eabae25a24107200c2bb52a6e06686e94259e1b1df38f2aee112305f596327c5b98ea7ae56bff9109f55c0f04de7d507c9c76889028ca692469127d040945 - languageName: node - linkType: hard - "is-bigint@npm:^1.0.1": version: 1.0.4 resolution: "is-bigint@npm:1.0.4" @@ -13861,27 +13747,6 @@ __metadata: languageName: node linkType: hard -"is-browser@npm:^2.0.1": - version: 2.1.0 - resolution: "is-browser@npm:2.1.0" - checksum: 10c0/107cb5211009823df2c3001e419bd9806472f9f2ca81e87b2e60b36c0a4ac709dc5f093d415c372d37758a39f22c98fd5bff060c1d0cfbb74c694b41d3df75c9 - languageName: node - linkType: hard - -"is-buffer@npm:^1.1.5": - version: 1.1.6 - resolution: "is-buffer@npm:1.1.6" - checksum: 10c0/ae18aa0b6e113d6c490ad1db5e8df9bdb57758382b313f5a22c9c61084875c6396d50bbf49315f5b1926d142d74dfb8d31b40d993a383e0a158b15fea7a82234 - languageName: node - linkType: hard - -"is-buffer@npm:^2.0.0": - version: 2.0.5 - resolution: "is-buffer@npm:2.0.5" - checksum: 10c0/e603f6fced83cf94c53399cff3bda1a9f08e391b872b64a73793b0928be3e5f047f2bcece230edb7632eaea2acdbfcb56c23b33d8a20c820023b230f1485679a - languageName: node - linkType: hard - "is-builtin-module@npm:^3.2.1": version: 3.2.1 resolution: "is-builtin-module@npm:3.2.1" @@ -14086,13 +13951,6 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^1.1.0": - version: 1.1.0 - resolution: "is-plain-obj@npm:1.1.0" - checksum: 10c0/daaee1805add26f781b413fdf192fc91d52409583be30ace35c82607d440da63cc4cac0ac55136716688d6c0a2c6ef3edb2254fecbd1fe06056d6bd15975ee8c - languageName: node - linkType: hard - "is-plain-obj@npm:^4.1.0": version: 4.1.0 resolution: "is-plain-obj@npm:4.1.0" @@ -17421,17 +17279,6 @@ __metadata: languageName: node linkType: hard -"ogg-opus-decoder@npm:^1.6.14": - version: 1.6.14 - resolution: "ogg-opus-decoder@npm:1.6.14" - dependencies: - "@wasm-audio-decoders/common": "npm:9.0.5" - codec-parser: "npm:2.5.0" - opus-decoder: "npm:0.7.7" - checksum: 10c0/9a378ca77c3f6b3e0d4880f23f8becbeb7af19afd0966d2818fabb3924f82b1d43b65fc03a638a983f13b47a8b7562a269422ef3944780027398c88903c3b3e0 - languageName: node - linkType: hard - "ohash@npm:^1.1.4": version: 1.1.4 resolution: "ohash@npm:1.1.4" @@ -17534,22 +17381,6 @@ __metadata: languageName: node linkType: hard -"opus-decoder@npm:0.7.7": - version: 0.7.7 - resolution: "opus-decoder@npm:0.7.7" - dependencies: - "@wasm-audio-decoders/common": "npm:9.0.5" - checksum: 10c0/35cd037b75183db8859eafdf65080c097117192b1284dd8abc3ffbd7b896f9a92c3f307dd8f17b83ce9d0d58cc388fe4fc629e0f509b1b8cf10347a6fb3152a7 - languageName: node - linkType: hard - -"opus-stream-decoder@npm:1.2.6": - version: 1.2.6 - resolution: "opus-stream-decoder@npm:1.2.6" - checksum: 10c0/f0de5c5370505ac8b904cfd12f1616b84bfe188336e88280718112a083056f1f09a4a7413ea07a71e6870793c13837758cbf8d18873778cd214bc6d806104233 - languageName: node - linkType: hard - "ora@npm:^5.4.1": version: 5.4.1 resolution: "ora@npm:5.4.1" @@ -18008,18 +17839,6 @@ __metadata: languageName: node linkType: hard -"pcm-convert@npm:^1.6.0": - version: 1.6.5 - resolution: "pcm-convert@npm:1.6.5" - dependencies: - audio-format: "npm:^2.3.2" - is-audio-buffer: "npm:^1.0.11" - is-buffer: "npm:^1.1.5" - object-assign: "npm:^4.1.1" - checksum: 10c0/58e62ec5b8f805f7bc3c431629d8195f053770e7cf09e0e89beb60067f014347a0507e776b8eb0e79db94cf57d400860cab92bfbd6f1cfbe5a59de79681633ec - languageName: node - linkType: hard - "peberminta@npm:^0.9.0": version: 0.9.0 resolution: "peberminta@npm:0.9.0" @@ -18158,13 +17977,6 @@ __metadata: languageName: node linkType: hard -"pick-by-alias@npm:^1.2.0": - version: 1.2.0 - resolution: "pick-by-alias@npm:1.2.0" - checksum: 10c0/2336088c95a04f50b088986394e35d2d0b63a95d85ffec547dcdb0fe1ccd710ab36569607fcf72d8654bbbf49e1f983a25f2a7c57e8ff2d587cf86070fc669b6 - languageName: node - linkType: hard - "picocolors@npm:^1.0.0, picocolors@npm:^1.0.1, picocolors@npm:^1.1.0, picocolors@npm:^1.1.1": version: 1.1.1 resolution: "picocolors@npm:1.1.1" @@ -21015,13 +20827,6 @@ __metadata: languageName: node linkType: hard -"sample-rate@npm:^2.0.0": - version: 2.0.1 - resolution: "sample-rate@npm:2.0.1" - checksum: 10c0/ba8cd01fb0bd6bb9b90d14899771c5b3b287c4e1997cb92e3ad8f0f4654a788c753304a994d81e61b7812dcfa947ebea7b127981ce358e954b576aca0ec09495 - languageName: node - linkType: hard - "sass@npm:^1.80.4": version: 1.80.7 resolution: "sass@npm:1.80.7" @@ -21456,13 +21261,6 @@ __metadata: languageName: node linkType: hard -"simple-yenc@npm:^1.0.4": - version: 1.0.4 - resolution: "simple-yenc@npm:1.0.4" - checksum: 10c0/5a476cf328e5e5b6006ac44af01daeda8a9832d3d2205c80d71fbdf3a9ff1ee5092ed41b909c77532c80b9f6db65b577e2c1283fcbb5a9b194f1e96b0077de73 - languageName: node - linkType: hard - "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -21879,16 +21677,6 @@ __metadata: languageName: node linkType: hard -"string-to-arraybuffer@npm:^1.0.0": - version: 1.0.2 - resolution: "string-to-arraybuffer@npm:1.0.2" - dependencies: - atob-lite: "npm:^2.0.0" - is-base64: "npm:^0.1.0" - checksum: 10c0/cc3db6f74ab3e5562c22390b713bc0b1ded8545c048b7615f9282669041f7251d7ca8f2a37fb6c37554204e12292c5fa5e0d30d46c3ecf5fc9761ab0b9b6c492 - languageName: node - linkType: hard - "string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3"