diff --git a/.gitignore b/.gitignore index 0cf66a490..a73158227 100644 --- a/.gitignore +++ b/.gitignore @@ -39,6 +39,7 @@ dist/ # docker utils ontime-db ontime-external/ +ontime-data/ # versioning file **/ONTIME_VERSION.js diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2d887d0b7..6a4a5d7a0 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -91,7 +91,7 @@ While it should allow for a generic setup, it might need to be modified to fit y From the project root, run the following commands -- __Build docker image from__ by running `docker build -t getontime/ontime` +- __Build docker image from__ by running `docker build -t getontime/ontime .` - __Run docker image from compose__ by running `docker-compose up -d` Other useful commands diff --git a/apps/cli/package.json b/apps/cli/package.json index 98fa3fd9c..d73c63baf 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -1,6 +1,6 @@ { "name": "@getontime/cli", - "version": "3.8.0", + "version": "3.9.5", "author": "Carlos Valente", "description": "Time keeping for live events", "repository": "https://github.com/cpvalente/ontime", diff --git a/apps/client/index.html b/apps/client/index.html index 7254d879b..a37a61f8a 100644 --- a/apps/client/index.html +++ b/apps/client/index.html @@ -1,31 +1,27 @@ - - - - - - - - - - - - - ontime - - - - -
- - + + + + + + + + + + + + + ontime + + + + +
+ + diff --git a/apps/client/package.json b/apps/client/package.json index 4a22ba68c..f982ecb37 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -1,6 +1,6 @@ { "name": "ontime-ui", - "version": "3.8.0", + "version": "3.9.5", "private": true, "type": "module", "dependencies": { @@ -13,10 +13,10 @@ "@fontsource/open-sans": "^5.0.28", "@mantine/hooks": "^7.13.3", "@react-icons/all-files": "^4.1.0", - "@sentry/react": "^8.19.0", - "@tanstack/react-query": "^5.17.9", - "@tanstack/react-query-devtools": "^5.17.9", - "@tanstack/react-table": "^8.11.3", + "@sentry/react": "^8.43.0", + "@tanstack/react-query": "^5.62.7", + "@tanstack/react-query-devtools": "^5.62.7", + "@tanstack/react-table": "^8.20.5", "autosize": "^6.0.1", "axios": "^1.2.0", "color": "^4.2.3", @@ -39,7 +39,7 @@ "build": "vite build", "build:local": "cross-env NODE_ENV=local vite build", "build:electron": "cross-env NODE_ENV=local vite build", - "build:docker": "cross-env VITE_IS_CLOUD=true vite build", + "build:docker": "cross-env VITE_IS_DOCKER=true vite build", "build:localdocker": "cross-env NODE_ENV=local vite build", "lint": "eslint . --quiet", "test": "vitest", @@ -92,4 +92,4 @@ "vite-tsconfig-paths": "^4.3.1", "vitest": "catalog:" } -} +} \ No newline at end of file diff --git a/apps/client/src/App.tsx b/apps/client/src/App.tsx index 6ebce9acb..c07768b94 100644 --- a/apps/client/src/App.tsx +++ b/apps/client/src/App.tsx @@ -11,6 +11,7 @@ import { connectSocket } from './common/utils/socket'; import theme from './theme/theme'; import { TranslationProvider } from './translation/TranslationProvider'; import AppRouter from './AppRouter'; +import { baseURI } from './externals'; connectSocket(); @@ -19,7 +20,7 @@ function App() { - +
diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index 24789c754..70e3fb8bd 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -19,7 +19,7 @@ import { ONTIME_VERSION } from './ONTIME_VERSION'; import { sentryDsn, sentryRecommendedIgnore } from './sentry.config'; const Editor = React.lazy(() => import('./features/editors/ProtectedEditor')); -const Cuesheet = React.lazy(() => import('./features/cuesheet/ProtectedCuesheet')); +const Cuesheet = React.lazy(() => import('./views/cuesheet/ProtectedCuesheet')); const Operator = React.lazy(() => import('./features/operator/OperatorExport')); const TimerView = React.lazy(() => import('./features/viewers/timer/Timer')); diff --git a/apps/client/src/common/api/db.ts b/apps/client/src/common/api/db.ts index dcc1af003..e9a29b3da 100644 --- a/apps/client/src/common/api/db.ts +++ b/apps/client/src/common/api/db.ts @@ -1,7 +1,7 @@ import axios, { AxiosResponse } from 'axios'; import { DatabaseModel, MessageResponse, ProjectData, ProjectFileListResponse, QuickStartData } from 'ontime-types'; -import { makeCSV, makeTable } from '../../features/cuesheet/cuesheetUtils'; +import { makeCSV, makeTable } from '../../views/cuesheet/cuesheet.utils'; import { apiEntryUrl } from './constants'; import { createBlob, downloadBlob } from './utils'; diff --git a/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx b/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx index 7180f2d36..e695c5b76 100644 --- a/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx +++ b/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx @@ -17,7 +17,7 @@ import { IoExpand } from '@react-icons/all-files/io5/IoExpand'; import { IoLockClosedOutline } from '@react-icons/all-files/io5/IoLockClosedOutline'; import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical'; -import { isLocalhost, serverPort } from '../../../externals'; +import { isLocalhost } from '../../../externals'; import { navigatorConstants } from '../../../viewerConfig'; import useClickOutside from '../../hooks/useClickOutside'; import { useElectronEvent } from '../../hooks/useElectronEvent'; @@ -25,7 +25,7 @@ import useInfo from '../../hooks-query/useInfo'; import { useClientStore } from '../../stores/clientStore'; import { useViewOptionsStore } from '../../stores/viewOptions'; import { isKeyEnter } from '../../utils/keyEvent'; -import { handleLinks, openLink } from '../../utils/linkUtils'; +import { handleLinks, linkToOtherHost, openLink } from '../../utils/linkUtils'; import { cx } from '../../utils/styleUtils'; import { RenameClientModal } from '../client-modal/RenameClientModal'; import CopyTag from '../copy-tag/CopyTag'; @@ -45,7 +45,7 @@ function NavigationMenu(props: NavigationMenuProps) { const { isOpen: isOpenRename, onOpen: onRenameOpen, onClose: onCloseRename } = useDisclosure(); const { fullscreen, toggle } = useFullscreen(); - const { toggleMirror } = useViewOptionsStore(); + const { mirror, toggleMirror } = useViewOptionsStore(); const location = useLocation(); const menuRef = useRef(null); @@ -65,7 +65,7 @@ function NavigationMenu(props: NavigationMenuProps) {
: }
toggleMirror()} @@ -154,7 +154,8 @@ function OtherAddresses(props: OtherAddressesProps) { return null; } - const address = `http://${nif.address}:${serverPort}${currentLocation}`; + const address = linkToOtherHost(nif.address, currentLocation); + return ( + +
+ ); +} diff --git a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.module.scss b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.module.scss index efe885c52..6de2400e0 100644 --- a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.module.scss +++ b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.module.scss @@ -1,6 +1,6 @@ .drawerFooter { display: flex; - justify-content: end; + justify-content: end; gap: $section-spacing; button { @@ -8,6 +8,23 @@ } } +.infoLabel { + display: flex; + align-items: center; + gap: $element-spacing; + padding: 1rem; + margin-bottom: 1rem; + + background-color: $gray-1100; + border-radius: 2px; + font-size: $inner-section-text-size; + + svg { + font-size: 1.5rem; + color: $info-blue; + } +} + .label { font-size: $inner-section-text-size; color: $label-gray; diff --git a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx index 25c04b998..65363e08c 100644 --- a/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx +++ b/apps/client/src/common/components/view-params-editor/ViewParamsEditor.tsx @@ -11,6 +11,9 @@ import { DrawerOverlay, useDisclosure, } from '@chakra-ui/react'; +import { IoAlertCircle } from '@react-icons/all-files/io5/IoAlertCircle'; + +import useViewSettings from '../../../common/hooks-query/useViewSettings'; import ParamInput from './ParamInput'; import { isSection, ViewOption } from './types'; @@ -87,6 +90,8 @@ interface EditFormDrawerProps { // TODO: this is a good candidate for memoisation, but needs the paramFields to be stable export default function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) { const [searchParams, setSearchParams] = useSearchParams(); + const { data: viewSettings } = useViewSettings(); + const { isOpen, onClose, onOpen } = useDisclosure(); useEffect(() => { @@ -115,8 +120,6 @@ export default function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) { const newParamsObject = Object.fromEntries(new FormData(formEvent.currentTarget)); const newSearchParams = getURLSearchParamsFromObj(newParamsObject, viewOptions); setSearchParams(newSearchParams); - - onClose(); }; return ( @@ -129,6 +132,12 @@ export default function ViewParamsEditor({ viewOptions }: EditFormDrawerProps) { + {viewSettings.overrideStyles && ( +
+ + This view style is being modified by a custom CSS file.
+
+ )}
{viewOptions.map((option) => { if (isSection(option)) { diff --git a/apps/client/src/common/hooks/useClientPath.ts b/apps/client/src/common/hooks/useClientPath.ts index 4c0d7b866..917b3de72 100644 --- a/apps/client/src/common/hooks/useClientPath.ts +++ b/apps/client/src/common/hooks/useClientPath.ts @@ -4,16 +4,23 @@ import { useLocation, useNavigate } from 'react-router-dom'; import { useClientStore } from '../stores/clientStore'; import { socketSendJson } from '../utils/socket'; +import { useIsOnline } from './useSocket'; + export const useClientPath = () => { const navigate = useNavigate(); const { pathname, search } = useLocation(); - const redirect = useClientStore((store) => store.redirect); - const setRedirect = useClientStore((store) => store.setRedirect); + const { redirect, setRedirect } = useClientStore((store) => ({ + redirect: store.redirect, + setRedirect: store.setRedirect, + })); + const isOnline = useIsOnline(); // notify of client path changes useEffect(() => { + if (!isOnline) return; + socketSendJson('set-client-path', pathname + search); - }, [pathname, search]); + }, [pathname, search, isOnline]); // navigate to new path when received from server useEffect(() => { @@ -26,7 +33,7 @@ export const useClientPath = () => { // navigate if there is a path change if (redirect !== pathname + search) { - navigate(redirect); + navigate(redirect, { replace: true }); } }, [navigate, pathname, redirect, search, setRedirect]); }; diff --git a/apps/client/src/common/hooks/useSocket.ts b/apps/client/src/common/hooks/useSocket.ts index a990bacc9..ee56a8b5a 100644 --- a/apps/client/src/common/hooks/useSocket.ts +++ b/apps/client/src/common/hooks/useSocket.ts @@ -238,3 +238,12 @@ export const usePing = () => { return useRuntimeStore(featureSelector); }; + +/** convert ping into a derived value which changes less often */ +export const useIsOnline = () => { + const featureSelector = (state: RuntimeStore) => ({ + isOnline: state.ping > 0, + }); + + return useRuntimeStore(featureSelector); +}; diff --git a/apps/client/src/common/stores/runtime.ts b/apps/client/src/common/stores/runtime.ts index 6bbb68c0b..e8a291932 100644 --- a/apps/client/src/common/stores/runtime.ts +++ b/apps/client/src/common/stores/runtime.ts @@ -16,11 +16,17 @@ export const useRuntimeStore = (selector: (state: RuntimeStore) => T) => /** * Allows patching a property of the runtime store - * @param key - * @param value */ -export function patchRuntime(key: K, value: RuntimeStore[K]): void { +export function patchRuntimeProperty(key: K, value: RuntimeStore[K]) { const state = runtimeStore.getState(); state[key] = value; runtimeStore.setState({ ...state }); } + +/** + * Allows patching the entire runtime store + */ +export function patchRuntime(patch: Partial) { + const state = runtimeStore.getState(); + runtimeStore.setState({ ...state, ...patch }); +} diff --git a/apps/client/src/common/utils/linkUtils.js b/apps/client/src/common/utils/linkUtils.js deleted file mode 100644 index a59644bbe..000000000 --- a/apps/client/src/common/utils/linkUtils.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Returns hostname - * @type {string} - */ -export const host = window?.location?.host; - -/** - * Open an external URLs: specifically for a electron / browser case - * If electron: ask main process to call a new browser window - * If browser: open in new tab - * @param url - */ -export function openLink(url) { - if (window.process?.type === 'renderer') { - window.ipcRenderer.send('send-to-link', url); - } else { - window.open(url); - } -} - -/** - * Handles opening external links - * @param event - * @param location - */ -export function handleLinks(event, location) { - // we handle the link manually - event.preventDefault(); - openLink(`http://${host}/${location}`); -} diff --git a/apps/client/src/common/utils/linkUtils.ts b/apps/client/src/common/utils/linkUtils.ts new file mode 100644 index 000000000..4dd1b78fb --- /dev/null +++ b/apps/client/src/common/utils/linkUtils.ts @@ -0,0 +1,40 @@ +import type { MouseEvent } from 'react'; + +import { baseURI, serverURL } from '../../externals'; + +/** + * Open an external URLs: specifically for a electron / browser case + * If electron: ask main process to call a new browser window + * If browser: open in new tab + * @param url + */ +export function openLink(url: string) { + if (window.process?.type === 'renderer') { + window.ipcRenderer.send('send-to-link', url); + } else { + window.open(url); + } +} + +/** + * Handles opening external links + * @param event + * @param location + */ +export function handleLinks(event: MouseEvent, location: string) { + // we handle the link manually + event.preventDefault(); + + const destination = new URL(serverURL); + destination.pathname = baseURI ? `${baseURI}/${location}` : location; + openLink(destination.toString()); +} + +export function linkToOtherHost(host: string, path?: string) { + const destination = new URL(serverURL); + destination.hostname = host; + if (path) { + destination.pathname = baseURI ? `${baseURI}/${path}` : path; + } + return destination.toString(); +} diff --git a/apps/client/src/common/utils/socket.ts b/apps/client/src/common/utils/socket.ts index ff0013bd0..4af7af816 100644 --- a/apps/client/src/common/utils/socket.ts +++ b/apps/client/src/common/utils/socket.ts @@ -14,7 +14,7 @@ import { } from '../stores/clientStore'; import { addDialog } from '../stores/dialogStore'; import { addLog } from '../stores/logger'; -import { patchRuntime, runtimeStore } from '../stores/runtime'; +import { patchRuntime, patchRuntimeProperty } from '../stores/runtime'; export let websocket: WebSocket | null = null; let reconnectTimeout: NodeJS.Timeout | null = null; @@ -39,12 +39,13 @@ export const connectSocket = () => { } socketSendJson('set-client-type', 'ontime'); - - socketSendJson('set-client-path', location.pathname + location.search); + setOnlineStatus(true); }; websocket.onclose = () => { console.warn('WebSocket disconnected'); + setOnlineStatus(false); + if (shouldReconnect) { reconnectTimeout = setTimeout(() => { console.warn('WebSocket: attempting reconnect'); @@ -73,8 +74,8 @@ export const connectSocket = () => { switch (type) { case 'pong': { const offset = (new Date().getTime() - new Date(payload).getTime()) * 0.5; - patchRuntime('ping', offset); - updateDevTools({ ping: offset }, ['PING']); + patchRuntimeProperty('ping', offset); + updateDevTools({ ping: offset }); break; } case 'client-id': { @@ -131,64 +132,65 @@ export const connectSocket = () => { break; } case 'ontime': { - runtimeStore.setState(payload as RuntimeStore); - if (!isProduction) { - ontimeQueryClient.setQueryData(RUNTIME, data.payload); - } + // eslint-disable-next-line @typescript-eslint/no-unused-vars -- removing the key from the payload + const { ping, ...serverPayload } = payload as Partial; + + patchRuntime(serverPayload); + updateDevTools(serverPayload); break; } case 'ontime-clock': { - patchRuntime('clock', payload); + patchRuntimeProperty('clock', payload); updateDevTools({ clock: payload }); break; } case 'ontime-timer': { - patchRuntime('timer', payload); + patchRuntimeProperty('timer', payload); updateDevTools({ timer: payload }); break; } case 'ontime-onAir': { - patchRuntime('onAir', payload); + patchRuntimeProperty('onAir', payload); updateDevTools({ onAir: payload }); break; } case 'ontime-message': { - patchRuntime('message', payload); + patchRuntimeProperty('message', payload); updateDevTools({ message: payload }); break; } case 'ontime-runtime': { - patchRuntime('runtime', payload); + patchRuntimeProperty('runtime', payload); updateDevTools({ runtime: payload }); break; } case 'ontime-eventNow': { - patchRuntime('eventNow', payload); + patchRuntimeProperty('eventNow', payload); updateDevTools({ eventNow: payload }); break; } case 'ontime-currentBlock': { - patchRuntime('currentBlock', payload); + patchRuntimeProperty('currentBlock', payload); updateDevTools({ currentBlock: payload }); break; } case 'ontime-publicEventNow': { - patchRuntime('publicEventNow', payload); + patchRuntimeProperty('publicEventNow', payload); updateDevTools({ publicEventNow: payload }); break; } case 'ontime-eventNext': { - patchRuntime('eventNext', payload); + patchRuntimeProperty('eventNext', payload); updateDevTools({ eventNext: payload }); break; } case 'ontime-publicEventNext': { - patchRuntime('publicEventNext', payload); + patchRuntimeProperty('publicEventNext', payload); updateDevTools({ publicEventNext: payload }); break; } case 'ontime-auxtimer1': { - patchRuntime('auxtimer1', payload); + patchRuntimeProperty('auxtimer1', payload); updateDevTools({ auxtimer1: payload }); break; } @@ -232,11 +234,23 @@ export const socketSendJson = (type: string, payload?: unknown) => { ); }; -function updateDevTools(newData: Partial, store = RUNTIME) { +function updateDevTools(newData: Partial) { if (!isProduction) { - ontimeQueryClient.setQueryData(store, (oldData: RuntimeStore) => ({ + ontimeQueryClient.setQueryData(RUNTIME, (oldData: RuntimeStore) => ({ ...oldData, ...newData, })); } } + +/** + * Allows setting the status of the client + * We leverage the ping as an indication of the client's online status + * @example ping < 0 - client is offline + * @example ping > 0 -> client is online + */ +function setOnlineStatus(status: boolean) { + const derivedPing = status ? 1 : -1; + patchRuntimeProperty('ping', derivedPing); + updateDevTools({ ping: derivedPing }); +} diff --git a/apps/client/src/externals.ts b/apps/client/src/externals.ts index ec771ba29..963ae663c 100644 --- a/apps/client/src/externals.ts +++ b/apps/client/src/externals.ts @@ -1,8 +1,9 @@ -import { version } from '../../../package.json'; /** * This file contains a list of constants that may need to be resolved at runtime */ +import { version } from '../../../package.json'; + export const githubUrl = 'https://www.github.com/cpvalente/ontime'; export const apiRepoLatest = 'https://api.github.com/repos/cpvalente/ontime/releases/latest'; export const websiteUrl = 'https://www.getontime.no'; @@ -16,13 +17,59 @@ export const appVersion = version; export const isProduction = import.meta.env.MODE === 'production'; export const isDev = !isProduction; export const isLocalhost = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'; -export const isOntimeCloud = Boolean(import.meta.env.VITE_IS_CLOUD); +export const isDockerImage = Boolean(import.meta.env.VITE_IS_DOCKER); +export const isOntimeCloud = window.location.hostname.includes('cloud.getontime.no'); -// resolve protocol -const socketProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws'; +// resolve entrypoint URLs -// resolve port -const STATIC_PORT = 4001; -export const serverPort = isProduction ? window.location.port : STATIC_PORT; -export const serverURL = `${window.location.protocol}//${location.hostname}:${serverPort}`; -export const websocketUrl = `${socketProtocol}://${location.hostname}:${serverPort}/ws`; +/** + * Resolve base + * @example '' for electron + * @example '/client-hash' for cloud + */ +export const baseURI = resolveBaseURI(); +export const serverURL = resolveUrl('http', ''); +export const websocketUrl = resolveUrl('ws', 'ws'); + +function resolveUrl(protocol: 'http' | 'ws', path: string) { + const url = new URL(window.location.origin); + + // generate ws url + if (protocol === 'ws') { + // ensure we remain in a secure context + const isSecure = window.location.protocol === 'https:'; + url.protocol = isSecure ? 'wss' : 'ws'; + } + + // make path name relative to the base URI + url.pathname = baseURI ? `${baseURI}/${path}` : path; + + // in development mode, we use the React port for UI, but need the requests to target the server + if (isDev) { + // this is used as a fallback port for development + url.port = '4001'; + } + + const result = url.toString(); + + // prevent trailing slash + return result.endsWith('/') ? result.slice(0, -1) : result; +} + +/** + * Resolves a base URI for a client that is not at the root segment + * ie: https://cloud.getontime.com/client-hash/timer + * This is necessary for ontime cloud and should otherwise not affect the client + */ +function resolveBaseURI(): string { + // in ontime cloud, the base tag is set by the server + const baseHref = document.querySelector('base')?.getAttribute('href'); + const base = baseHref ?? ''; + + // prevent a trailing slash from either an empty base or a base with a trailing slash + if (base.endsWith('/')) { + return base.slice(0, -1); + } + + return base; +} diff --git a/apps/client/src/features/app-settings/panel/integrations-panel/OscIntegrations.tsx b/apps/client/src/features/app-settings/panel/integrations-panel/OscIntegrations.tsx index 1c604f31b..d124a2313 100644 --- a/apps/client/src/features/app-settings/panel/integrations-panel/OscIntegrations.tsx +++ b/apps/client/src/features/app-settings/panel/integrations-panel/OscIntegrations.tsx @@ -10,6 +10,7 @@ import { maybeAxiosError } from '../../../../common/api/utils'; import useOscSettings, { useOscSettingsMutation } from '../../../../common/hooks-query/useOscSettings'; import { isKeyEscape } from '../../../../common/utils/keyEvent'; import { isASCII, isASCIIorEmpty, isIPAddress, isOnlyNumbers, startsWithSlash } from '../../../../common/utils/regex'; +import { isOntimeCloud } from '../../../../externals'; import * as Panel from '../../panel-utils/PanelUtils'; import { cycles } from './integrationUtils'; @@ -106,6 +107,9 @@ export default function OscIntegrations() {
+ {isOntimeCloud && ( + For security reasons OSC integrations are not available in the cloud service. + )} diff --git a/apps/client/src/features/app-settings/panel/network-panel/NetworkInterfaces.tsx b/apps/client/src/features/app-settings/panel/network-panel/NetworkInterfaces.tsx index db4c98f98..556531b38 100644 --- a/apps/client/src/features/app-settings/panel/network-panel/NetworkInterfaces.tsx +++ b/apps/client/src/features/app-settings/panel/network-panel/NetworkInterfaces.tsx @@ -2,8 +2,8 @@ import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp'; import CopyTag from '../../../../common/components/copy-tag/CopyTag'; import useInfo from '../../../../common/hooks-query/useInfo'; -import { openLink } from '../../../../common/utils/linkUtils'; -import { isLocalhost, serverPort } from '../../../../externals'; +import { linkToOtherHost, openLink } from '../../../../common/utils/linkUtils'; +import { isLocalhost } from '../../../../externals'; import style from './NetworkInterfaces.module.scss'; @@ -14,11 +14,11 @@ export default function InfoNif() { return (
- {data.networkInterfaces?.map((nif) => { + {data.networkInterfaces.map((nif) => { // interfaces outside localhost wont have access if (nif.name === 'localhost' && !isLocalhost) return null; + const address = linkToOtherHost(nif.address); - const address = `http://${nif.address}:${serverPort}`; return ( Network - {isOntimeCloud && } + {isDockerImage && } Ontime is streaming on the following network interfaces diff --git a/apps/client/src/features/app-settings/panel/project-panel/ProjectCreateForm.tsx b/apps/client/src/features/app-settings/panel/project-panel/ProjectCreateForm.tsx index d88bfd7e1..fa5b902de 100644 --- a/apps/client/src/features/app-settings/panel/project-panel/ProjectCreateForm.tsx +++ b/apps/client/src/features/app-settings/panel/project-panel/ProjectCreateForm.tsx @@ -6,6 +6,7 @@ import { useQueryClient } from '@tanstack/react-query'; import { PROJECT_LIST } from '../../../../common/api/constants'; import { createProject } from '../../../../common/api/db'; import { maybeAxiosError } from '../../../../common/api/utils'; +import { documentationUrl, websiteUrl } from '../../../../externals'; import * as Panel from '../../panel-utils/PanelUtils'; import style from './ProjectPanel.module.scss'; @@ -118,7 +119,7 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) { @@ -140,7 +141,7 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) { diff --git a/apps/client/src/features/app-settings/panel/project-panel/ProjectData.tsx b/apps/client/src/features/app-settings/panel/project-panel/ProjectData.tsx index 960ceb218..7f3094f64 100644 --- a/apps/client/src/features/app-settings/panel/project-panel/ProjectData.tsx +++ b/apps/client/src/features/app-settings/panel/project-panel/ProjectData.tsx @@ -10,6 +10,7 @@ import { postProjectData, uploadProjectLogo } from '../../../../common/api/proje import { maybeAxiosError } from '../../../../common/api/utils'; import useProjectData from '../../../../common/hooks-query/useProjectData'; import { validateLogo } from '../../../../common/utils/uploadUtils'; +import { documentationUrl, websiteUrl } from '../../../../externals'; import * as Panel from '../../panel-utils/PanelUtils'; import style from './ProjectPanel.module.scss'; @@ -203,7 +204,7 @@ export default function ProjectData() { @@ -225,7 +226,7 @@ export default function ProjectData() { diff --git a/apps/client/src/features/app-settings/panel/shutdown-panel/ShutdownPanel.tsx b/apps/client/src/features/app-settings/panel/shutdown-panel/ShutdownPanel.tsx index 21f192df4..e6b203b54 100644 --- a/apps/client/src/features/app-settings/panel/shutdown-panel/ShutdownPanel.tsx +++ b/apps/client/src/features/app-settings/panel/shutdown-panel/ShutdownPanel.tsx @@ -11,7 +11,7 @@ import { } from '@chakra-ui/react'; import { useElectronEvent } from '../../../../common/hooks/useElectronEvent'; -import { isLocalhost } from '../../../../externals'; +import { isLocalhost, isOntimeCloud } from '../../../../externals'; import * as Panel from '../../panel-utils/PanelUtils'; export default function ShutdownPanel() { @@ -24,18 +24,28 @@ export default function ShutdownPanel() { onClose(); }; + const canShutdown = isElectron || isLocalhost; + return ( <> Shutdown Ontime - - This will shutdown the Ontime server.
- The runtime state will be lost, but your project is kept for next time. -
+ {isOntimeCloud ? ( + + For security reasons, shutting down the server must be done from the Ontime Cloud dashboard. + + ) : ( + + This will shutdown the Ontime server.
+ The runtime state will be lost, but your project is kept for next time. +
+ )} - Note: Ontime can only be shutdown from the machine it is running in. + {!canShutdown && ( + Note: Ontime can only be shutdown from the machine it is running in. + )} @@ -49,7 +59,7 @@ export default function ShutdownPanel() { - diff --git a/apps/client/src/features/control/playback/playback-timer/PlaybackTimer.tsx b/apps/client/src/features/control/playback/playback-timer/PlaybackTimer.tsx index e58a3db0f..e168fd588 100644 --- a/apps/client/src/features/control/playback/playback-timer/PlaybackTimer.tsx +++ b/apps/client/src/features/control/playback/playback-timer/PlaybackTimer.tsx @@ -1,9 +1,10 @@ import { PropsWithChildren } from 'react'; import { Tooltip } from '@chakra-ui/react'; import { Playback, TimerPhase } from 'ontime-types'; -import { dayInMs, millisToMinutes, millisToSeconds, millisToString } from 'ontime-utils'; +import { dayInMs, millisToString } from 'ontime-utils'; import { useTimer } from '../../../../common/hooks/useSocket'; +import { formatDuration } from '../../../../common/utils/time'; import TimerDisplay from '../timer-display/TimerDisplay'; import style from './PlaybackTimer.module.scss'; @@ -13,22 +14,12 @@ interface PlaybackTimerProps { } function resolveAddedTimeLabel(addedTime: number) { - function resolveClosestUnit(ms: number) { - if (ms < 6000) { - return `${millisToSeconds(ms)} seconds`; - } else if (ms < 12000) { - return '1 minute'; - } else { - return `${millisToMinutes(ms)} minutes`; - } - } - if (addedTime > 0) { - return `Added ${resolveClosestUnit(addedTime)}`; + return `Added ${formatDuration(addedTime, false)}`; } if (addedTime < 0) { - return `Removed ${resolveClosestUnit(addedTime)}`; + return `Removed ${formatDuration(Math.abs(addedTime), false)}`; } return ''; diff --git a/apps/client/src/features/cuesheet/__tests__/__snapshots__/utils.test.js.snap b/apps/client/src/features/cuesheet/__tests__/__snapshots__/utils.test.js.snap deleted file mode 100644 index 5214b2670..000000000 --- a/apps/client/src/features/cuesheet/__tests__/__snapshots__/utils.test.js.snap +++ /dev/null @@ -1,41 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`makeTable() > returns array of arrays with given fields 1`] = ` -[ - [ - "Ontime · Rundown export", - ], - [ - "Project title: test title", - ], - [ - "Project description: test description", - ], - [ - "Time Start", - "Time End", - "Duration", - "ID", - "Colour", - "Cue", - "Title", - "Note", - "Is Public? (x)", - "Skip?", - "lighting", - ], - [ - "00:00:00", - "00:00:00", - "...", - "", - "", - "", - "test title 1", - "", - "x", - "", - "", - ], -] -`; diff --git a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss b/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss deleted file mode 100644 index b82de38a8..000000000 --- a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.module.scss +++ /dev/null @@ -1,3 +0,0 @@ -.progressOverride { - height: 1rem; -} diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/EditableCell.tsx b/apps/client/src/features/cuesheet/cuesheet-table-elements/EditableCell.tsx deleted file mode 100644 index 301b60f4b..000000000 --- a/apps/client/src/features/cuesheet/cuesheet-table-elements/EditableCell.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { ChangeEvent, memo, useCallback, useEffect, useRef, useState } from 'react'; -import { getHotkeyHandler } from '@mantine/hooks'; - -import { AutoTextArea } from '../../../common/components/input/auto-text-area/AutoTextArea'; - -interface EditableCellProps { - value: string; - handleUpdate: (newValue: string) => void; -} - -const EditableCell = (props: EditableCellProps) => { - const { value: initialValue, handleUpdate } = props; - - // We need to keep and update the state of the cell normally - const [value, setValue] = useState(initialValue); - const ref = useRef(); - const onChange = useCallback((event: ChangeEvent) => setValue(event.target.value), []); - - // We'll only update the external data when the input is blurred - const onBlur = useCallback(() => handleUpdate(value), [handleUpdate, value]); - - //TODO: maybe we can unify this with `useReactiveTextInput` - const onKeyDown = getHotkeyHandler([ - ['mod + Enter', () => ref.current?.blur()], - [ - 'Escape', - () => { - setValue(initialValue); - setTimeout(() => ref.current?.blur()); - }, - ], - ]); - - // If the initialValue is changed external, sync it up with our state - useEffect(() => { - setValue(initialValue); - }, [initialValue]); - - return ( - - ); -}; - -export default memo(EditableCell); diff --git a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.module.scss b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.module.scss deleted file mode 100644 index 944dbcc3b..000000000 --- a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.module.scss +++ /dev/null @@ -1,120 +0,0 @@ -$label-colour: $gray-700; -$active-colour: $gray-500; - -@mixin label { - font-size: $inner-section-text-size; - color: $label-colour; - text-align: center; - align-self: end; -} - -@mixin time { - font-family: 'Open Sans Light', $ontime-font-family; - font-size: 2rem; - text-align: center; -} - -.header { - grid-area: header; - display: grid; - width: 100%; - padding: 0.25rem 1rem; - height: max-content; - column-gap: 2rem; - - grid-template-areas: 'event playback timer clock actions'; - grid-template-columns: 1fr auto auto auto auto; - align-items: center; - justify-items: center; -} - -.event { - grid-area: event; - justify-self: start; - - .title { - font-size: 1.75rem; - } - - .eventNow { - justify-self: start; - font-size: 1.5rem; - } -} - -.playback { - grid-area: playback; - display: flex; - flex-direction: column; - justify-items: center; - align-items: center; - - .playbackLabel { - @include label; - } - - svg { - font-size: 2rem; - height: 3rem; - color: $label-colour; - } -} - -.timer { - grid-area: timer; - - .timerLabel { - @include label; - } - - .value { - @include time; - } -} - -.clock { - grid-area: clock; - - .clockLabel { - @include label; - } - - .value { - grid-area: clock; - @include time; - } -} - -.headerActions { - grid-area: actions; - display: flex; - align-items: center; - gap: 0.5rem; - color: $label-colour; - height: 100%; - font-size: 1rem; - - .actionIcon, - .actionText { - cursor: pointer; - - &.enabled { - color: $active-indicator; - } - - &:hover { - color: $active-colour; - } - } - - .actionIcon { - font-size: 1.25rem; - } -} - -@media (min-width: 1200px) { - // in large screens we want to space the buttons - .headerActions { - padding-left: 10vw; - } -} diff --git a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.tsx b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.tsx deleted file mode 100644 index 1966a0e52..000000000 --- a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import { Tooltip } from '@chakra-ui/react'; -import { IoLocate } from '@react-icons/all-files/io5/IoLocate'; -import { Playback, ProjectData } from 'ontime-types'; - -import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon'; -import useProjectData from '../../../common/hooks-query/useProjectData'; -import { cx, enDash } from '../../../common/utils/styleUtils'; -import { tooltipDelayFast } from '../../../ontimeConfig'; -import { useCuesheetSettings } from '../store/CuesheetSettings'; - -import CuesheetTableHeaderTimers from './CuesheetTableHeaderTimers'; - -import style from './CuesheetTableHeader.module.scss'; - -interface CuesheetTableHeaderProps { - handleExport: (headerData: ProjectData) => void; - featureData: { - playback: Playback; - selectedEventIndex: number | null; - numEvents: number; - titleNow: string | null; - }; -} - -export default function CuesheetTableHeader({ handleExport, featureData }: CuesheetTableHeaderProps) { - const followSelected = useCuesheetSettings((state) => state.followSelected); - const toggleFollow = useCuesheetSettings((state) => state.toggleFollow); - const { data: project } = useProjectData(); - - const exportProject = () => { - if (project) { - handleExport(project); - } - }; - - const selected = !featureData.numEvents - ? 'No events' - : `Event ${featureData.selectedEventIndex != null ? featureData.selectedEventIndex + 1 : enDash}/${ - featureData.numEvents ? featureData.numEvents : enDash - }`; - - return ( -
-
-
{project?.title || enDash}
-
{featureData?.titleNow || enDash}
-
-
-
{selected}
- -
- -
- - toggleFollow()} - className={cx([style.actionIcon, followSelected ? style.enabled : null])} - > - - - - - - Export CSV - - -
-
- ); -} diff --git a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx deleted file mode 100644 index 147e15a47..000000000 --- a/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeaderTimers.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { useClock, useTimer } from '../../../common/hooks/useSocket'; -import ClockTime from '../../viewers/common/clock-time/ClockTime'; -import RunningTime from '../../viewers/common/running-time/RunningTime'; - -import style from './CuesheetTableHeader.module.scss'; - -export default function CuesheetTableHeaderTimers() { - const { current } = useTimer(); - const { clock } = useClock(); - - return ( - <> -
-
Running Timer
- -
-
-
Time Now
- -
- - ); -} diff --git a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx deleted file mode 100644 index a5990accb..000000000 --- a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { memo, ReactNode } from 'react'; -import { Button, Checkbox, Switch } from '@chakra-ui/react'; -import { Column } from '@tanstack/react-table'; -import { OntimeRundownEntry } from 'ontime-types'; - -import { useCuesheetSettings } from '../store/CuesheetSettings'; - -import style from './CuesheetTableSettings.module.scss'; - -// reusable button styles -const buttonProps = { - size: 'sm', - variant: 'ontime-subtle', -}; - -interface CuesheetTableSettingsProps { - columns: Column[]; - handleResetResizing: () => void; - handleResetReordering: () => void; - handleClearToggles: () => void; -} - -function CuesheetTableSettings(props: CuesheetTableSettingsProps) { - const { columns, handleResetResizing, handleResetReordering, handleClearToggles } = props; - const { - followSelected, - showIndexColumn, - toggleFollow, - showPrevious, - togglePreviousVisibility, - showDelayBlock, - hideSeconds, - showDelayedTimes, - toggleIndexColumn, - toggleDelayedTimes, - toggleDelayVisibility, - toggleSecondsVisibility, - } = useCuesheetSettings(); - - return ( -
-
-
Toggle column visibility
-
- {columns.map((column) => { - const columnHeader = column.columnDef.header; - const visible = column.getIsVisible(); - return ( - - ); - })} -
-
Table Options
-
- - - -
-
Delay Flow
-
- - - -
-
-
- - - -
-
- ); -} - -export default memo(CuesheetTableSettings); diff --git a/apps/client/src/features/cuesheet/cuesheetCols.tsx b/apps/client/src/features/cuesheet/cuesheetCols.tsx deleted file mode 100644 index 29d8a3c06..000000000 --- a/apps/client/src/features/cuesheet/cuesheetCols.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { useCallback } from 'react'; -import { IoCheckmark } from '@react-icons/all-files/io5/IoCheckmark'; -import { CellContext, ColumnDef } from '@tanstack/react-table'; -import { CustomFields, isOntimeEvent, OntimeEvent, OntimeRundownEntry } from 'ontime-types'; - -import DelayIndicator from '../../common/components/delay-indicator/DelayIndicator'; -import RunningTime from '../viewers/common/running-time/RunningTime'; - -import EditableCell from './cuesheet-table-elements/EditableCell'; -import { useCuesheetSettings } from './store/CuesheetSettings'; - -import style from './Cuesheet.module.scss'; - -function makePublic(row: CellContext) { - const cellValue = row.getValue(); - return cellValue ? : ''; -} - -function MakeTimer({ getValue, row: { original } }: CellContext) { - const showDelayedTimes = useCuesheetSettings((state) => state.showDelayedTimes); - const hideSeconds = useCuesheetSettings((state) => state.hideSeconds); - const cellValue = (getValue() as number | null) ?? 0; - const delayValue = (original as OntimeEvent)?.delay ?? 0; - - return ( - - - - {delayValue !== 0 && showDelayedTimes && ( - - )} - - ); -} - -function MakeDuration({ getValue }: CellContext) { - const hideSeconds = useCuesheetSettings((state) => state.hideSeconds); - const cellValue = (getValue() as number | null) ?? 0; - - return ; -} - -function MakeCustomField({ row, column, table }: CellContext) { - const update = useCallback( - (newValue: string) => { - // @ts-expect-error -- we inject this into react-table - table.options.meta?.handleUpdate(row.index, column.id, newValue); - }, - // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable - [column.id, row.index], - ); - - const event = row.original; - if (!isOntimeEvent(event)) { - return null; - } - - // events dont necessarily contain all custom fields - const initialValue = event.custom[column.id] ?? ''; - - return ; -} - -export function makeCuesheetColumns(customFields: CustomFields): ColumnDef[] { - const dynamicCustomFields = Object.keys(customFields).map((key) => ({ - accessorKey: key, - id: key, - header: customFields[key].label, - meta: { colour: customFields[key].colour }, - cell: MakeCustomField, - size: 250, - })); - - return [ - { - accessorKey: 'cue', - id: 'cue', - header: 'Cue', - cell: (row) => row.getValue(), - size: 75, - }, - { - accessorKey: 'isPublic', - id: 'isPublic', - header: 'Public', - cell: makePublic, - size: 45, - }, - { - accessorKey: 'timeStart', - id: 'timeStart', - header: 'Start', - cell: MakeTimer, - size: 75, - }, - { - accessorKey: 'timeEnd', - id: 'timeEnd', - header: 'End', - cell: MakeTimer, - size: 75, - }, - { - accessorKey: 'duration', - id: 'duration', - header: 'Duration', - cell: MakeDuration, - size: 75, - }, - { - accessorKey: 'title', - id: 'title', - header: 'Title', - cell: (row) => row.getValue(), - size: 250, - }, - { - accessorKey: 'note', - id: 'note', - header: 'Note', - cell: (row) => row.getValue(), - size: 250, - }, - ...dynamicCustomFields, - ]; -} diff --git a/apps/client/src/features/cuesheet/defaults.ts b/apps/client/src/features/cuesheet/defaults.ts deleted file mode 100644 index c57bc6e2a..000000000 --- a/apps/client/src/features/cuesheet/defaults.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { OntimeEntryCommonKeys, OntimeEvent } from 'ontime-types'; - -/** - * @description set default column order - */ -export const defaultColumnOrder: OntimeEntryCommonKeys[] = [ - 'isPublic', - 'cue', - 'timeStart', - 'timeEnd', - 'duration', - 'title', - 'note', -]; - -/** - * @description set default hidden columns - */ -export const defaultHiddenColumns: (keyof OntimeEvent)[] = []; diff --git a/apps/client/src/features/cuesheet/store/CuesheetSettings.tsx b/apps/client/src/features/cuesheet/store/CuesheetSettings.tsx deleted file mode 100644 index aacf0e256..000000000 --- a/apps/client/src/features/cuesheet/store/CuesheetSettings.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { create } from 'zustand'; - -import { booleanFromLocalStorage } from '../../../common/utils/localStorage'; - -interface CuesheetSettings { - showSettings: boolean; - showIndexColumn: boolean; - followSelected: boolean; - showPrevious: boolean; - showDelayBlock: boolean; - showDelayedTimes: boolean; - hideSeconds: boolean; - - toggleSettings: (newValue?: boolean) => void; - toggleFollow: (newValue?: boolean) => void; - togglePreviousVisibility: (newValue?: boolean) => void; - toggleIndexColumn: (newValue?: boolean) => void; - toggleDelayVisibility: (newValue?: boolean) => void; - toggleDelayedTimes: (newValue?: boolean) => void; - toggleSecondsVisibility: (newValue?: boolean) => void; -} - -function toggle(oldValue: boolean, value?: boolean) { - if (typeof value === 'undefined') { - return !oldValue; - } - return value; -} - -enum CuesheetKeys { - Follow = 'ontime-cuesheet-follow-selected', - DelayVisibility = 'ontime-cuesheet-show-delay', - PreviousVisibility = 'ontime-cuesheet-show-previous', - ColumnIndex = 'ontime-cuesheet-show-index-column', - DelayedTimes = 'ontime-cuesheet-show-delayed', - Seconds = 'ontime-cuesheet-hide-sceconds', -} - -export const useCuesheetSettings = create()((set) => ({ - showSettings: false, - showIndexColumn: booleanFromLocalStorage(CuesheetKeys.ColumnIndex, true), - followSelected: booleanFromLocalStorage(CuesheetKeys.Follow, false), - showPrevious: booleanFromLocalStorage(CuesheetKeys.PreviousVisibility, true), - showDelayBlock: booleanFromLocalStorage(CuesheetKeys.DelayVisibility, true), - showDelayedTimes: booleanFromLocalStorage(CuesheetKeys.DelayedTimes, false), - hideSeconds: booleanFromLocalStorage(CuesheetKeys.Seconds, false), - - toggleSettings: (newValue?: boolean) => set((state) => ({ showSettings: toggle(state.showSettings, newValue) })), - toggleFollow: (newValue?: boolean) => - set((state) => { - const followSelected = toggle(state.followSelected, newValue); - localStorage.setItem(CuesheetKeys.Follow, String(followSelected)); - return { followSelected }; - }), - toggleIndexColumn: (newValue?: boolean) => - set((state) => { - const showIndexColumn = toggle(state.showIndexColumn, newValue); - localStorage.setItem(CuesheetKeys.ColumnIndex, String(showIndexColumn)); - return { showIndexColumn }; - }), - togglePreviousVisibility: (newValue?: boolean) => - set((state) => { - const showPrevious = toggle(state.showPrevious, newValue); - localStorage.setItem(CuesheetKeys.PreviousVisibility, String(showPrevious)); - return { showPrevious }; - }), - toggleDelayVisibility: (newValue?: boolean) => - set((state) => { - const showDelayBlock = toggle(state.showDelayBlock, newValue); - localStorage.setItem(CuesheetKeys.DelayVisibility, String(showDelayBlock)); - return { showDelayBlock }; - }), - toggleDelayedTimes: (newValue?: boolean) => - set((state) => { - const showDelayedTimes = toggle(state.showDelayedTimes, newValue); - localStorage.setItem(CuesheetKeys.DelayedTimes, String(showDelayedTimes)); - return { showDelayedTimes }; - }), - toggleSecondsVisibility: (newValue?: boolean) => - set((state) => { - const hideSeconds = toggle(state.hideSeconds, newValue); - localStorage.setItem(CuesheetKeys.Seconds, String(hideSeconds)); - return { hideSeconds }; - }), -})); diff --git a/apps/client/src/features/editors/finder/Finder.tsx b/apps/client/src/features/editors/finder/Finder.tsx index 2a8f9e3fb..379554cb9 100644 --- a/apps/client/src/features/editors/finder/Finder.tsx +++ b/apps/client/src/features/editors/finder/Finder.tsx @@ -1,7 +1,7 @@ import { KeyboardEvent, useState } from 'react'; import { Input, Modal, ModalBody, ModalContent, ModalFooter, ModalOverlay } from '@chakra-ui/react'; import { useDebouncedCallback } from '@mantine/hooks'; -import { isOntimeEvent, SupportedEvent } from 'ontime-types'; +import { SupportedEvent } from 'ontime-types'; import { useEventSelection } from '../../rundown/useEventSelection'; @@ -65,14 +65,15 @@ export default function Finder(props: FinderProps) { {error &&
  • {error}
  • } {results.length === 0 &&
  • No results
  • } {results.length > 0 && - results.map((event, index) => { + results.map((entry, index) => { const isSelected = selected === index; - const displayIndex = event.type === SupportedEvent.Block ? '-' : event.index; - const colour = event.type === SupportedEvent.Event ? event.colour : ''; + const displayIndex = entry.type === SupportedEvent.Event ? entry.eventIndex : '-'; + const displayCue = entry.type === SupportedEvent.Event ? entry.cue : ''; + const colour = entry.type === SupportedEvent.Event ? entry.colour : ''; return (
  • {displayIndex}
  • - {isOntimeEvent(event) &&
    {event.cue}
    } -
    {event.title}
    +
    {displayCue}
    +
    {entry.title}
    {isSelected && Go ⏎} diff --git a/apps/client/src/features/editors/finder/useFinder.tsx b/apps/client/src/features/editors/finder/useFinder.tsx index 9ec95fa1a..d5b2468c6 100644 --- a/apps/client/src/features/editors/finder/useFinder.tsx +++ b/apps/client/src/features/editors/finder/useFinder.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useState } from 'react'; +import { ChangeEvent, useEffect, useRef, useState } from 'react'; import { isOntimeBlock, isOntimeEvent, MaybeString, SupportedEvent } from 'ontime-types'; import { useFlatRundown } from '../../../common/hooks-query/useRundown'; @@ -28,6 +28,17 @@ export default function useFinder() { const { data } = useFlatRundown(); const [results, setResults] = useState([]); const [error, setError] = useState(null); + const lastSearchString = useRef(''); + + /** clear results when source data changes */ + useEffect(() => { + setResults([]); + setError(null); + // fake a submit event to re-run the search + if (lastSearchString.current) { + find({ target: { value: lastSearchString.current } } as ChangeEvent); + } + }, [data]); /** Returns a single item with a matching index */ const searchByIndex = (searchString: string) => { @@ -155,6 +166,7 @@ export default function useFinder() { } const searchValue = event.target.value.toLowerCase(); + lastSearchString.current = searchValue; if (searchValue.startsWith('index ')) { const searchString = searchValue.replace('index ', '').trim(); diff --git a/apps/client/src/features/editors/welcome/Welcome.module.scss b/apps/client/src/features/editors/welcome/Welcome.module.scss index 3965310ea..22b3249f6 100644 --- a/apps/client/src/features/editors/welcome/Welcome.module.scss +++ b/apps/client/src/features/editors/welcome/Welcome.module.scss @@ -27,6 +27,7 @@ } .tableContainer { + height: 350px; max-height: 350px; overflow-y: auto; } diff --git a/apps/client/src/features/editors/welcome/Welcome.tsx b/apps/client/src/features/editors/welcome/Welcome.tsx index 1090b67da..ca8299f4f 100644 --- a/apps/client/src/features/editors/welcome/Welcome.tsx +++ b/apps/client/src/features/editors/welcome/Welcome.tsx @@ -53,7 +53,7 @@ export default function Welcome(props: WelcomeProps) { }; return ( - + diff --git a/apps/client/src/features/operator/Operator.tsx b/apps/client/src/features/operator/Operator.tsx index ea9c07a96..ef13e3652 100644 --- a/apps/client/src/features/operator/Operator.tsx +++ b/apps/client/src/features/operator/Operator.tsx @@ -3,7 +3,7 @@ import { useSearchParams } from 'react-router-dom'; import { isOntimeEvent, OntimeEvent, SupportedEvent } from 'ontime-types'; import { getFirstEventNormal, getLastEventNormal } from 'ontime-utils'; -import Empty from '../../common/components/state/Empty'; +import EmptyPage from '../../common/components/state/EmptyPage'; import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import useFollowComponent from '../../common/hooks/useFollowComponent'; import { useOperator } from '../../common/hooks/useSocket'; @@ -108,7 +108,7 @@ export default function Operator() { const isLoading = status === 'pending' || customFieldStatus === 'pending' || projectDataStatus === 'pending'; if (missingData || isLoading) { - return ; + return ; } // get fields which the user subscribed to diff --git a/apps/client/src/features/overview/Overview.tsx b/apps/client/src/features/overview/Overview.tsx index 162e3e75a..3a86e8764 100644 --- a/apps/client/src/features/overview/Overview.tsx +++ b/apps/client/src/features/overview/Overview.tsx @@ -87,6 +87,10 @@ function _CuesheetOverview({ children }: { children: React.ReactNode }) { function TitlesOverview() { const { data } = useProjectData(); + if (!data.title && !data.description) { + return null; + } + return (
    {data.title}
    diff --git a/apps/client/src/features/rundown/Rundown.tsx b/apps/client/src/features/rundown/Rundown.tsx index 27aa0ce22..ce77e8361 100644 --- a/apps/client/src/features/rundown/Rundown.tsx +++ b/apps/client/src/features/rundown/Rundown.tsx @@ -256,10 +256,12 @@ export default function Rundown({ data }: RundownProps) { return insertAtId(SupportedEvent.Event, cursor)} />; } - let lastEntry: PlayableEvent | undefined; // used by indicators - let thisEntry: PlayableEvent | undefined; - let previousEventId: string | undefined; - let thisId = previousEventId; + // last event is used to calculate relative timings + let lastEvent: PlayableEvent | undefined; // used by indicators + let thisEvent: PlayableEvent | undefined; + // previous entry is used to infer position in the rundown for new events + let previousEntryId: string | undefined; + let thisId = previousEntryId; let eventIndex = 0; // all events before the current selected are in the past @@ -272,63 +274,64 @@ export default function Rundown({ data }: RundownProps) {
    - {statefulEntries.map((eventId, index) => { + {statefulEntries.map((entryId, index) => { // we iterate through a stateful copy of order to make the operations smoother // this means that this can be out of sync with order until the useEffect runs // instead of writing all the logic guards, we simply short circuit rendering here - const event = rundown[eventId]; - if (!event) { + const entry = rundown[entryId]; + if (!entry) { return null; } if (index === 0) { eventIndex = 0; } - if (isOntimeEvent(event)) { + previousEntryId = thisId; + thisId = entryId; + if (isOntimeEvent(entry)) { // event indexes are 1 based in frontend eventIndex++; - previousEventId = thisId; - lastEntry = thisEntry; + lastEvent = thisEvent; - if (isPlayableEvent(event)) { + if (isPlayableEvent(entry)) { // populate previous entry - if (isNewLatest(event.timeStart, event.timeEnd, lastEntry?.timeStart, lastEntry?.timeEnd)) { - thisEntry = event; + if (isNewLatest(entry.timeStart, entry.timeEnd, lastEvent?.timeStart, lastEvent?.timeEnd)) { + thisEvent = entry; } - thisId = eventId; } } const isFirst = index === 0; const isLast = index === order.length - 1; - const isLoaded = featureData?.selectedEventId === event.id; - const isNext = featureData?.nextEventId === event.id; - const hasCursor = event.id === cursor; + const isLoaded = featureData?.selectedEventId === entry.id; + const isNext = featureData?.nextEventId === entry.id; + const hasCursor = entry.id === cursor; if (isLoaded) { isPast = false; } return ( - - {isEditMode && (hasCursor || isFirst) && } + + {isEditMode && (hasCursor || isFirst) && }
    - {isOntimeEvent(event) &&
    {eventIndex}
    } -
    + {isOntimeEvent(entry) &&
    {eventIndex}
    } +
    - {isEditMode && (hasCursor || isLast) && } + {isEditMode && (hasCursor || isLast) && } ); })} diff --git a/apps/client/src/features/rundown/RundownEntry.tsx b/apps/client/src/features/rundown/RundownEntry.tsx index 163129916..5df99938c 100644 --- a/apps/client/src/features/rundown/RundownEntry.tsx +++ b/apps/client/src/features/rundown/RundownEntry.tsx @@ -34,6 +34,7 @@ interface RundownEntryProps { isNext: boolean; previousStart?: number; previousEnd?: number; + previousEntryId?: string; previousEventId?: string; playback?: Playback; // we only care about this if this event is playing isRolling: boolean; // we need to know even if not related to this event @@ -48,6 +49,7 @@ export default function RundownEntry(props: RundownEntryProps) { isNext, previousStart, previousEnd, + previousEntryId, previousEventId, playback, isRolling, @@ -84,7 +86,7 @@ export default function RundownEntry(props: RundownEntryProps) { case 'event-before': { const newEvent = { type: SupportedEvent.Event }; const options = { - after: previousEventId, + after: previousEntryId, }; return addEvent(newEvent, options); } @@ -92,13 +94,13 @@ export default function RundownEntry(props: RundownEntryProps) { return addEvent({ type: SupportedEvent.Delay }, { after: data.id }); } case 'delay-before': { - return addEvent({ type: SupportedEvent.Delay }, { after: previousEventId }); + return addEvent({ type: SupportedEvent.Delay }, { after: previousEntryId }); } case 'block': { return addEvent({ type: SupportedEvent.Block }, { after: data.id }); } case 'block-before': { - return addEvent({ type: SupportedEvent.Block }, { after: previousEventId }); + return addEvent({ type: SupportedEvent.Block }, { after: previousEntryId }); } case 'swap': { const { value } = payload as FieldValue; diff --git a/apps/client/src/features/viewers/common/viewUtils.ts b/apps/client/src/features/viewers/common/viewUtils.ts index 32158f01c..bfc2e3046 100644 --- a/apps/client/src/features/viewers/common/viewUtils.ts +++ b/apps/client/src/features/viewers/common/viewUtils.ts @@ -32,6 +32,10 @@ export function getTimerByType(freezeEnd: boolean, timerObject?: TimerTypeParams } } +/** + * Parses a string to semantically verify if it represents a true value + * Used in the context of parsing search params and local storage items which can be strings or null + */ export function isStringBoolean(text: string | null) { if (text === null) { return false; diff --git a/apps/client/src/features/viewers/studio/StudioClock.scss b/apps/client/src/features/viewers/studio/StudioClock.scss index 7ebe263e9..66fd12b5f 100644 --- a/apps/client/src/features/viewers/studio/StudioClock.scss +++ b/apps/client/src/features/viewers/studio/StudioClock.scss @@ -149,6 +149,7 @@ $orange-active: #f60; grid-area: schedule; font-family: monospace; margin-right: 2vw; + overflow: hidden; } .onAir { diff --git a/apps/client/src/theme/_ontimeStyles.scss b/apps/client/src/theme/_ontimeStyles.scss index 5877af849..46cb5962b 100644 --- a/apps/client/src/theme/_ontimeStyles.scss +++ b/apps/client/src/theme/_ontimeStyles.scss @@ -12,6 +12,7 @@ $action-text-color: $blue-400; $ontime-color: #ff7597; $error-red: $red-500; $warning-orange: $orange-500; +$info-blue: $blue-500; $opacity-disabled: 0.4; $active-red: $red-700; @@ -57,6 +58,7 @@ $text-body-size: calc(1rem - 1px); // media queries $min-tablet: 500px; +$small-screen: 800px; .blink { animation: blink 1s step-start infinite; diff --git a/apps/client/src/theme/ontimeTextInputs.ts b/apps/client/src/theme/ontimeTextInputs.ts index 0547b58db..ec96e7fba 100644 --- a/apps/client/src/theme/ontimeTextInputs.ts +++ b/apps/client/src/theme/ontimeTextInputs.ts @@ -38,6 +38,16 @@ export const ontimeInputGhosted = { }, }; +export const ontimeInputTransparent = { + field: { + ...commonStyles, + backgroundColor: 'transparent', + _hover: { + backgroundColor: 'rgba(255, 255, 255, 0.10)', // $white-10 + }, + }, +}; + export const ontimeTextAreaFilled = { ...commonStyles, }; diff --git a/apps/client/src/theme/theme.ts b/apps/client/src/theme/theme.ts index ee6b00e46..ff68d5177 100644 --- a/apps/client/src/theme/theme.ts +++ b/apps/client/src/theme/theme.ts @@ -21,6 +21,7 @@ import { ontimeTab } from './ontimeTab'; import { ontimeInputFilled, ontimeInputGhosted, + ontimeInputTransparent, ontimeTextAreaFilled, ontimeTextAreaTransparent, } from './ontimeTextInputs'; @@ -78,6 +79,7 @@ const theme = extendTheme({ variants: { 'ontime-filled': { ...ontimeInputFilled }, 'ontime-ghosted': { ...ontimeInputGhosted }, + 'ontime-transparent': { ...ontimeInputTransparent }, }, }, Kbd: { diff --git a/apps/client/src/translation/TranslationProvider.tsx b/apps/client/src/translation/TranslationProvider.tsx index fd3b70abf..7aa37cb01 100644 --- a/apps/client/src/translation/TranslationProvider.tsx +++ b/apps/client/src/translation/TranslationProvider.tsx @@ -39,8 +39,6 @@ export const TranslationContext = createContext({ export const TranslationProvider = ({ children }: PropsWithChildren) => { const { data } = useSettings(); - console.log('....', data.language) - const getLocalizedString = useCallback( (key: keyof typeof langEn, lang = data?.language || 'en'): string => { if (lang in translationsList) { diff --git a/apps/client/src/features/cuesheet/Cuesheet.module.scss b/apps/client/src/views/cuesheet/Cuesheet.module.scss similarity index 97% rename from apps/client/src/features/cuesheet/Cuesheet.module.scss rename to apps/client/src/views/cuesheet/Cuesheet.module.scss index 1fccb55a6..a5064eb87 100644 --- a/apps/client/src/features/cuesheet/Cuesheet.module.scss +++ b/apps/client/src/views/cuesheet/Cuesheet.module.scss @@ -45,12 +45,11 @@ $table-header-font-size: calc(1rem - 3px); .tableHeader { position: sticky; - top: -1px; + top: 0px; z-index: 10; - + background-color: $ui-black; font-size: $table-header-font-size; - color: $gray-700; -} + color: $label-gray;} th { background-color: $gray-1300; diff --git a/apps/client/src/features/cuesheet/Cuesheet.tsx b/apps/client/src/views/cuesheet/Cuesheet.tsx similarity index 82% rename from apps/client/src/features/cuesheet/Cuesheet.tsx rename to apps/client/src/views/cuesheet/Cuesheet.tsx index deaf586e5..d205da34b 100644 --- a/apps/client/src/features/cuesheet/Cuesheet.tsx +++ b/apps/client/src/views/cuesheet/Cuesheet.tsx @@ -1,7 +1,14 @@ import { useCallback, useRef } from 'react'; import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; import Color from 'color'; -import { isOntimeBlock, isOntimeDelay, isOntimeEvent, OntimeRundown, OntimeRundownEntry } from 'ontime-types'; +import { + CustomFieldLabel, + isOntimeBlock, + isOntimeDelay, + isOntimeEvent, + OntimeRundown, + OntimeRundownEntry, +} from 'ontime-types'; import useFollowComponent from '../../common/hooks/useFollowComponent'; import { getAccessibleColour } from '../../common/utils/styleUtils'; @@ -11,7 +18,7 @@ import CuesheetHeader from './cuesheet-table-elements/CuesheetHeader'; import DelayRow from './cuesheet-table-elements/DelayRow'; import EventRow from './cuesheet-table-elements/EventRow'; import CuesheetTableSettings from './cuesheet-table-settings/CuesheetTableSettings'; -import { useCuesheetSettings } from './store/CuesheetSettings'; +import { useCuesheetOptions } from './cuesheet.options'; import useColumnManager from './useColumnManager'; import style from './Cuesheet.module.scss'; @@ -19,13 +26,21 @@ import style from './Cuesheet.module.scss'; interface CuesheetProps { data: OntimeRundown; columns: ColumnDef[]; - handleUpdate: (rowIndex: number, accessor: keyof OntimeRundownEntry, payload: unknown) => void; + handleUpdate: (rowIndex: number, accessor: keyof OntimeRundownEntry, payload: string) => void; + handleUpdateCustom: (rowIndex: number, accessor: CustomFieldLabel, payload: string) => void; selectedId: string | null; currentBlockId: string | null; } -export default function Cuesheet({ data, columns, handleUpdate, selectedId, currentBlockId }: CuesheetProps) { - const { followSelected, showSettings, showDelayBlock, showPrevious, showIndexColumn } = useCuesheetSettings(); +export default function Cuesheet({ + data, + columns, + handleUpdate, + handleUpdateCustom, + selectedId, + currentBlockId, +}: CuesheetProps) { + const { followSelected, hideDelays, hidePast, hideIndexColumn } = useCuesheetOptions(); const { columnVisibility, columnOrder, @@ -51,6 +66,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId, curr }, meta: { handleUpdate, + handleUpdateCustom, }, onColumnVisibilityChange: setColumnVisibility, onColumnSizingChange: setColumnSizing, @@ -94,17 +110,15 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId, curr return ( <> - {showSettings && ( - - )} +
    - + {rowModel.rows.map((row) => { const key = row.original.id; @@ -114,17 +128,17 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId, curr } if (isOntimeBlock(row.original)) { - if (isPast && !showPrevious && key !== currentBlockId) { + if (isPast && hidePast && key !== currentBlockId) { return null; } return ; } if (isOntimeDelay(row.original)) { - if (isPast && !showPrevious) { + if (isPast && hidePast) { return null; } const delayVal = row.original.duration; - if (!showDelayBlock || delayVal === 0) { + if (hideDelays || delayVal === 0) { return null; } @@ -134,7 +148,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId, curr eventIndex++; const isSelected = key === selectedId; - if (isPast && !showPrevious) { + if (isPast && hidePast) { return null; } @@ -159,7 +173,7 @@ export default function Cuesheet({ data, columns, handleUpdate, selectedId, curr selectedRef={isSelected ? selectedRef : undefined} skip={row.original.skip} colour={row.original.colour} - showIndexColumn={showIndexColumn} + showIndexColumn={!hideIndexColumn} > {row.getVisibleCells().map((cell) => { return ( diff --git a/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss b/apps/client/src/views/cuesheet/CuesheetPage.module.scss similarity index 79% rename from apps/client/src/features/cuesheet/CuesheetWrapper.module.scss rename to apps/client/src/views/cuesheet/CuesheetPage.module.scss index b179cf178..80ef47ba7 100644 --- a/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss +++ b/apps/client/src/views/cuesheet/CuesheetPage.module.scss @@ -5,9 +5,10 @@ padding: 1rem 0.5rem; display: grid; - grid-template-rows: 3rem auto 1fr; + grid-template-rows: 3rem auto auto 1fr; grid-template-areas: 'overview' + 'progress' 'settings' 'table'; gap: 1rem; diff --git a/apps/client/src/features/cuesheet/CuesheetWrapper.tsx b/apps/client/src/views/cuesheet/CuesheetPage.tsx similarity index 58% rename from apps/client/src/features/cuesheet/CuesheetWrapper.tsx rename to apps/client/src/views/cuesheet/CuesheetPage.tsx index efb71ae81..e59184527 100644 --- a/apps/client/src/features/cuesheet/CuesheetWrapper.tsx +++ b/apps/client/src/views/cuesheet/CuesheetPage.tsx @@ -1,44 +1,51 @@ import { useCallback, useMemo } from 'react'; +import { useSearchParams } from 'react-router-dom'; import { IconButton, useDisclosure } from '@chakra-ui/react'; import { IoApps } from '@react-icons/all-files/io5/IoApps'; import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline'; -import { CustomFieldLabel, isOntimeEvent } from 'ontime-types'; +import { CustomFieldLabel, isOntimeEvent, OntimeEvent } from 'ontime-types'; import ProductionNavigationMenu from '../../common/components/navigation-menu/ProductionNavigationMenu'; -import Empty from '../../common/components/state/Empty'; +import EmptyPage from '../../common/components/state/EmptyPage'; +import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import { useEventAction } from '../../common/hooks/useEventAction'; import { useCuesheet } from '../../common/hooks/useSocket'; import { useWindowTitle } from '../../common/hooks/useWindowTitle'; import useCustomFields from '../../common/hooks-query/useCustomFields'; import { useFlatRundown } from '../../common/hooks-query/useRundown'; -import { CuesheetOverview } from '../overview/Overview'; +import { CuesheetOverview } from '../../features/overview/Overview'; import CuesheetProgress from './cuesheet-progress/CuesheetProgress'; -import { useCuesheetSettings } from './store/CuesheetSettings'; import Cuesheet from './Cuesheet'; +import { cuesheetOptions } from './cuesheet.options'; import { makeCuesheetColumns } from './cuesheetCols'; -import styles from './CuesheetWrapper.module.scss'; +import styles from './CuesheetPage.module.scss'; -export default function CuesheetWrapper() { +export default function CuesheetPage() { // TODO: can we use the normalised rundown for the table? const { data: flatRundown, status: rundownStatus } = useFlatRundown(); const { data: customFields } = useCustomFields(); + const [searchParams, setSearchParams] = useSearchParams(); const { isOpen: isMenuOpen, onOpen, onClose } = useDisclosure(); - const { updateCustomField } = useEventAction(); + const { updateCustomField, updateEvent } = useEventAction(); const featureData = useCuesheet(); const columns = useMemo(() => makeCuesheetColumns(customFields), [customFields]); - const toggleSettings = useCuesheetSettings((state) => state.toggleSettings); useWindowTitle('Cuesheet'); + /** Handles showing the view params edit drawer */ + const showEditFormDrawer = useCallback(() => { + searchParams.set('edit', 'true'); + setSearchParams(searchParams); + }, [searchParams, setSearchParams]); + /** - * Handles updating a field - * Currently, only custom fields can be updated from the cuesheet + * Handles updating a custom field */ - const handleUpdate = useCallback( - async (rowIndex: number, accessor: CustomFieldLabel, payload: unknown) => { + const handleUpdateCustom = useCallback( + async (rowIndex: number, accessor: CustomFieldLabel, payload: string) => { if (!flatRundown || rundownStatus !== 'success') { return; } @@ -53,52 +60,68 @@ export default function CuesheetWrapper() { return; } + // skip if there is no value change const previousValue = event.custom[accessor]; - if (previousValue === payload) { return; } - - // check if value is valid - // in anticipation to different types of event here - if (typeof payload !== 'string') { - return; - } - - // cleanup - const cleanVal = payload.trim(); - - // submit - try { - await updateCustomField(event.id, accessor, cleanVal); - } catch (error) { - console.error(error); - } + updateCustomField(event.id, accessor, payload); }, [flatRundown, rundownStatus, updateCustomField], ); + /** + * Handles updating all other string fields + */ + const handleUpdate = useCallback( + async (rowIndex: number, accessor: keyof OntimeEvent, payload: string) => { + if (!flatRundown || rundownStatus !== 'success') { + return; + } + + if (rowIndex == null || accessor == null || payload == null) { + return; + } + + // check if value is the same + const event = flatRundown[rowIndex]; + if (!event || !isOntimeEvent(event)) { + return; + } + + // skip if there is no value change + const previousValue = event[accessor]; + if (previousValue === payload) { + return; + } + + updateEvent({ id: event.id, [accessor]: payload }); + }, + [flatRundown, rundownStatus, updateEvent], + ); + if (!customFields || !flatRundown || rundownStatus !== 'success') { - return ; + return ; } return (
    + } onClick={onOpen} /> } - onClick={() => toggleSettings()} + onClick={showEditFormDrawer} /> @@ -106,6 +129,8 @@ export default function CuesheetWrapper() { data={flatRundown} columns={columns} handleUpdate={handleUpdate} + handleUpdateCustom={handleUpdateCustom} + //TODO: stabilizer selectedEventId and currentBlockId selectedId={featureData.selectedEventId} currentBlockId={featureData.currentBlockId} /> diff --git a/apps/client/src/features/cuesheet/ProtectedCuesheet.tsx b/apps/client/src/views/cuesheet/ProtectedCuesheet.tsx similarity index 73% rename from apps/client/src/features/cuesheet/ProtectedCuesheet.tsx rename to apps/client/src/views/cuesheet/ProtectedCuesheet.tsx index 6a31c1742..af8c4aee7 100644 --- a/apps/client/src/features/cuesheet/ProtectedCuesheet.tsx +++ b/apps/client/src/views/cuesheet/ProtectedCuesheet.tsx @@ -1,11 +1,11 @@ import ProtectRoute from '../../common/components/protect-route/ProtectRoute'; -import CuesheetWrapper from './CuesheetWrapper'; +import CuesheetPage from './CuesheetPage'; export default function ProtectedCuesheet() { return ( - + ); } diff --git a/apps/client/src/features/cuesheet/__tests__/utils.test.js b/apps/client/src/views/cuesheet/__tests__/cuesheet.utils.test.ts similarity index 67% rename from apps/client/src/features/cuesheet/__tests__/utils.test.js rename to apps/client/src/views/cuesheet/__tests__/cuesheet.utils.test.ts index d62ed12e7..a7b162246 100644 --- a/apps/client/src/features/cuesheet/__tests__/utils.test.js +++ b/apps/client/src/views/cuesheet/__tests__/cuesheet.utils.test.ts @@ -1,4 +1,6 @@ -import { makeCSV, makeTable, parseField } from '../cuesheetUtils'; +import { ProjectData } from 'ontime-types'; + +import { makeCSV, makeTable, parseField } from '../cuesheet.utils'; describe('parseField()', () => { it('returns a string from given millis on timeStart, TimeEnd and duration', () => { @@ -27,6 +29,7 @@ describe('parseField()', () => { }); it('returns an empty string on undefined fields', () => { + // @ts-expect-error -- testing user data with missing fields expect(parseField('title')).toBe(''); }); @@ -51,6 +54,7 @@ describe('makeTable()', () => { const headerData = { title: 'test title', description: 'test description', + projectLogo: 'test logo', }; const tableData = [ { @@ -66,8 +70,48 @@ describe('makeTable()', () => { lighting: { label: 'test' }, }; - const table = makeTable(headerData, tableData, customFields); - expect(table).toMatchSnapshot(); + // @ts-expect-error -- testing user data with missing fields + const table = makeTable(headerData as ProjectData, tableData, customFields); + expect(table).not.toContain('test logo'); + expect(table).toMatchInlineSnapshot(` + [ + [ + "Ontime · Rundown export", + ], + [ + "Project title: test title", + ], + [ + "Project description: test description", + ], + [ + "Time Start", + "Time End", + "Duration", + "ID", + "Colour", + "Cue", + "Title", + "Note", + "Is Public? (x)", + "Skip?", + "lighting", + ], + [ + "00:00:00", + "00:00:00", + "...", + "", + "", + "", + "test title 1", + "", + "x", + "", + "", + ], + ] + `); }); }); diff --git a/apps/client/src/views/cuesheet/cuesheet-progress/CuesheetProgress.module.scss b/apps/client/src/views/cuesheet/cuesheet-progress/CuesheetProgress.module.scss new file mode 100644 index 000000000..44a571809 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheet-progress/CuesheetProgress.module.scss @@ -0,0 +1,4 @@ +.progressOverride { + height: 1rem; + grid-area: progress; +} diff --git a/apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx b/apps/client/src/views/cuesheet/cuesheet-progress/CuesheetProgress.tsx similarity index 100% rename from apps/client/src/features/cuesheet/cuesheet-progress/CuesheetProgress.tsx rename to apps/client/src/views/cuesheet/cuesheet-progress/CuesheetProgress.tsx diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/BlockRow.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/BlockRow.tsx similarity index 100% rename from apps/client/src/features/cuesheet/cuesheet-table-elements/BlockRow.tsx rename to apps/client/src/views/cuesheet/cuesheet-table-elements/BlockRow.tsx diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx similarity index 100% rename from apps/client/src/features/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx rename to apps/client/src/views/cuesheet/cuesheet-table-elements/CuesheetHeader.tsx diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/DelayRow.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/DelayRow.tsx similarity index 100% rename from apps/client/src/features/cuesheet/cuesheet-table-elements/DelayRow.tsx rename to apps/client/src/views/cuesheet/cuesheet-table-elements/DelayRow.tsx diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/EventRow.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/EventRow.tsx similarity index 100% rename from apps/client/src/features/cuesheet/cuesheet-table-elements/EventRow.tsx rename to apps/client/src/views/cuesheet/cuesheet-table-elements/EventRow.tsx diff --git a/apps/client/src/views/cuesheet/cuesheet-table-elements/MultiLineCell.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/MultiLineCell.tsx new file mode 100644 index 000000000..9fa159863 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheet-table-elements/MultiLineCell.tsx @@ -0,0 +1,37 @@ +import { memo, useCallback, useRef } from 'react'; + +import { AutoTextArea } from '../../../common/components/input/auto-text-area/AutoTextArea'; +import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput'; + +interface MultiLineCellProps { + initialValue: string; + handleUpdate: (newValue: string) => void; +} + +const MultiLineCell = (props: MultiLineCellProps) => { + const { initialValue, handleUpdate } = props; + const ref = useRef(null); + const submitCallback = useCallback((newValue: string) => handleUpdate(newValue), [handleUpdate]); + + const { value, onChange, onBlur, onKeyDown } = useReactiveTextInput(initialValue, submitCallback, ref, { + submitOnCtrlEnter: true, + }); + + return ( + + ); +}; + +export default memo(MultiLineCell); diff --git a/apps/client/src/views/cuesheet/cuesheet-table-elements/SingleLineCell.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/SingleLineCell.tsx new file mode 100644 index 000000000..329d8a4f9 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheet-table-elements/SingleLineCell.tsx @@ -0,0 +1,35 @@ +import { memo, useCallback, useRef } from 'react'; +import { Input } from '@chakra-ui/react'; + +import useReactiveTextInput from '../../../common/components/input/text-input/useReactiveTextInput'; + +interface SingleLineCellProps { + initialValue: string; + handleUpdate: (newValue: string) => void; +} + +const SingleLineCell = (props: SingleLineCellProps) => { + const { initialValue, handleUpdate } = props; + const ref = useRef(null); + const submitCallback = useCallback((newValue: string) => handleUpdate(newValue), [handleUpdate]); + + const { value, onChange, onBlur, onKeyDown } = useReactiveTextInput(initialValue, submitCallback, ref, { + submitOnCtrlEnter: true, + }); + + return ( + + ); +}; + +export default memo(SingleLineCell); diff --git a/apps/client/src/features/cuesheet/cuesheet-table-elements/SortableCell.tsx b/apps/client/src/views/cuesheet/cuesheet-table-elements/SortableCell.tsx similarity index 100% rename from apps/client/src/features/cuesheet/cuesheet-table-elements/SortableCell.tsx rename to apps/client/src/views/cuesheet/cuesheet-table-elements/SortableCell.tsx diff --git a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss b/apps/client/src/views/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss similarity index 53% rename from apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss rename to apps/client/src/views/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss index 1946fd5a4..a036cda7c 100644 --- a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss +++ b/apps/client/src/views/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss @@ -1,24 +1,20 @@ .tableSettings { grid-area: settings; - padding: 1rem; - background-color: $ui-black; + padding-inline: 0.5rem; display: flex; + gap: 5rem; font-size: $inner-section-text-size; -} -.leftPanel { - display: flex; - flex-direction: column; - width: 100%; - gap: 0.5rem; + @media (max-width: $small-screen) { + gap: 1rem; + } } .sectionTitle { - color: $gray-700; text-transform: uppercase; } -.options { +.row { display: flex; flex-wrap: wrap; column-gap: 1rem; @@ -30,12 +26,4 @@ display: flex; align-items: center; gap: 0.5rem; -} - -.rightPanel { - display: flex; - flex-direction: column; - gap: 0.5rem; - - padding-left: 2rem; -} +} \ No newline at end of file diff --git a/apps/client/src/views/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx b/apps/client/src/views/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx new file mode 100644 index 000000000..fd7c04424 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx @@ -0,0 +1,65 @@ +import { memo, ReactNode } from 'react'; +import { Button, Checkbox } from '@chakra-ui/react'; +import { Column } from '@tanstack/react-table'; +import { OntimeRundownEntry } from 'ontime-types'; + +import * as Editor from '../../../features/editors/editor-utils/EditorUtils'; + +import style from './CuesheetTableSettings.module.scss'; + +// reusable button styles +const buttonProps = { + size: 'xs', + variant: 'ontime-subtle', +}; + +interface CuesheetTableSettingsProps { + columns: Column[]; + handleResetResizing: () => void; + handleResetReordering: () => void; + handleClearToggles: () => void; +} + +function CuesheetTableSettings(props: CuesheetTableSettingsProps) { + const { columns, handleResetResizing, handleResetReordering, handleClearToggles } = props; + + return ( +
    +
    + Toggle column visibility +
    + {columns.map((column) => { + const columnHeader = column.columnDef.header; + const visible = column.getIsVisible(); + return ( + + ); + })} +
    +
    +
    + Reset Options +
    + + + +
    +
    +
    + ); +} + +export default memo(CuesheetTableSettings); diff --git a/apps/client/src/views/cuesheet/cuesheet.options.ts b/apps/client/src/views/cuesheet/cuesheet.options.ts new file mode 100644 index 000000000..82568c5d4 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheet.options.ts @@ -0,0 +1,90 @@ +import { useMemo } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +import { ViewOption } from '../../common/components/view-params-editor/types'; +import { isStringBoolean } from '../../features/viewers/common/viewUtils'; + +/** + * In the specific case of the cuesheet options + * we save the user preferences in the local storage + */ +export const cuesheetOptions: ViewOption[] = [ + { section: 'Table options' }, + { + id: 'hideTableSeconds', + title: 'Hide seconds in table', + description: 'Whether to hide seconds in the time fields displayed in the table', + type: 'boolean', + defaultValue: false, + }, + { + id: 'followSelected', + title: 'Follow selected event', + description: 'Whether the view should automatically scroll to the selected event', + type: 'boolean', + defaultValue: false, + }, + { + id: 'hidePast', + title: 'Hide Past Events', + description: 'Whether to hide events that have passed', + type: 'boolean', + defaultValue: false, + }, + { + id: 'hideIndexColumn', + title: 'Hide index column', + description: 'Whether the hide the event indexes in the table', + type: 'boolean', + defaultValue: false, + }, + { section: 'Delay flow' }, + { + id: 'showDelayedTimes', + title: 'Show delayed times', + description: 'Whether the time fields should include delays', + type: 'boolean', + defaultValue: false, + }, + { + id: 'hideDelays', + title: 'Hide delays', + description: 'Whether to hide the rows containing scheduled delays', + type: 'boolean', + defaultValue: false, + }, +]; + +type CuesheetOptions = { + hideTableSeconds: boolean; + followSelected: boolean; + hidePast: boolean; + hideIndexColumn: boolean; + showDelayedTimes: boolean; + hideDelays: boolean; +}; + +/** + * Utility extract the view options from URL Params + * the names and fallbacks are manually matched with cuesheetOptions + */ +export function getOptionsFromParams(searchParams: URLSearchParams): CuesheetOptions { + // we manually make an object that matches the key above + return { + hideTableSeconds: isStringBoolean(searchParams.get('hideTableSeconds')), + followSelected: isStringBoolean(searchParams.get('followSelected')), + hidePast: isStringBoolean(searchParams.get('hidePast')), + hideIndexColumn: isStringBoolean(searchParams.get('hideIndexColumn')), + showDelayedTimes: isStringBoolean(searchParams.get('showDelayedTimes')), + hideDelays: isStringBoolean(searchParams.get('hideDelays')), + }; +} + +/** + * Hook exposes the cuesheet view options + */ +export function useCuesheetOptions(): CuesheetOptions { + const [searchParams] = useSearchParams(); + const options = useMemo(() => getOptionsFromParams(searchParams), [searchParams]); + return options; +} diff --git a/apps/client/src/features/cuesheet/cuesheetUtils.ts b/apps/client/src/views/cuesheet/cuesheet.utils.ts similarity index 100% rename from apps/client/src/features/cuesheet/cuesheetUtils.ts rename to apps/client/src/views/cuesheet/cuesheet.utils.ts diff --git a/apps/client/src/views/cuesheet/cuesheetCols.tsx b/apps/client/src/views/cuesheet/cuesheetCols.tsx new file mode 100644 index 000000000..f82a55561 --- /dev/null +++ b/apps/client/src/views/cuesheet/cuesheetCols.tsx @@ -0,0 +1,182 @@ +import { useCallback } from 'react'; +import { Checkbox } from '@chakra-ui/react'; +import { CellContext, ColumnDef } from '@tanstack/react-table'; +import { CustomFields, isOntimeEvent, OntimeEvent, OntimeRundownEntry } from 'ontime-types'; + +import DelayIndicator from '../../common/components/delay-indicator/DelayIndicator'; +import RunningTime from '../../features/viewers/common/running-time/RunningTime'; + +import MultiLineCell from './cuesheet-table-elements/MultiLineCell'; +import SingleLineCell from './cuesheet-table-elements/SingleLineCell'; +import { useCuesheetOptions } from './cuesheet.options'; + +import style from './Cuesheet.module.scss'; + +function MakePublic({ row, column, table }: CellContext) { + const update = useCallback( + (event: React.ChangeEvent) => { + // @ts-expect-error -- we inject this into react-table + table.options.meta?.handleUpdate(row.index, column.id, event.target.checked); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable + [column.id, row.index], + ); + + const event = row.original; + if (!isOntimeEvent(event)) { + return null; + } + + const isChecked = event.isPublic; + + return ( + + ); +} + +function MakeTimer({ getValue, row: { original } }: CellContext) { + const { showDelayedTimes, hideTableSeconds } = useCuesheetOptions(); + const cellValue = (getValue() as number | null) ?? 0; + const delayValue = (original as OntimeEvent)?.delay ?? 0; + + return ( + + + + {delayValue !== 0 && showDelayedTimes && ( + + )} + + ); +} + +function MakeDuration({ getValue }: CellContext) { + const { hideTableSeconds } = useCuesheetOptions(); + const cellValue = (getValue() as number | null) ?? 0; + + return ; +} + +function MakeMultiLineField({ row, column, table }: CellContext) { + const update = useCallback( + (newValue: string) => { + // @ts-expect-error -- we inject this into react-table + table.options.meta?.handleUpdate(row.index, column.id, newValue); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable + [column.id, row.index], + ); + + const event = row.original; + if (!isOntimeEvent(event)) { + return null; + } + + const initialValue = event[column.id as keyof OntimeRundownEntry] ?? ''; + + return ; +} + +function MakeSingleLineField({ row, column, table }: CellContext) { + const update = useCallback( + (newValue: string) => { + // @ts-expect-error -- we inject this into react-table + table.options.meta?.handleUpdate(row.index, column.id, newValue); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable + [column.id, row.index], + ); + + const event = row.original; + if (!isOntimeEvent(event)) { + return null; + } + + const initialValue = event[column.id as keyof OntimeRundownEntry] ?? ''; + + return ; +} + +function MakeCustomField({ row, column, table }: CellContext) { + const update = useCallback( + (newValue: string) => { + // @ts-expect-error -- we inject this into react-table + table.options.meta?.handleUpdateCustom(row.index, column.id, newValue); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable + [column.id, row.index], + ); + + const event = row.original; + if (!isOntimeEvent(event)) { + return null; + } + + const initialValue = event.custom[column.id] ?? ''; + + return ; +} + +export function makeCuesheetColumns(customFields: CustomFields): ColumnDef[] { + const dynamicCustomFields = Object.keys(customFields).map((key) => ({ + accessorKey: key, + id: key, + header: customFields[key].label, + meta: { colour: customFields[key].colour }, + cell: MakeCustomField, + size: 250, + })); + + return [ + { + accessorKey: 'cue', + id: 'cue', + header: 'Cue', + cell: (row) => row.getValue(), + size: 75, + }, + { + accessorKey: 'isPublic', + id: 'isPublic', + header: 'Public', + cell: MakePublic, + size: 45, + }, + { + accessorKey: 'timeStart', + id: 'timeStart', + header: 'Start', + cell: MakeTimer, + size: 75, + }, + { + accessorKey: 'timeEnd', + id: 'timeEnd', + header: 'End', + cell: MakeTimer, + size: 75, + }, + { + accessorKey: 'duration', + id: 'duration', + header: 'Duration', + cell: MakeDuration, + size: 75, + }, + { + accessorKey: 'title', + id: 'title', + header: 'Title', + cell: MakeSingleLineField, + size: 250, + }, + { + accessorKey: 'note', + id: 'note', + header: 'Note', + cell: MakeMultiLineField, + size: 250, + }, + ...dynamicCustomFields, + ]; +} diff --git a/apps/client/src/features/cuesheet/useColumnManager.tsx b/apps/client/src/views/cuesheet/useColumnManager.tsx similarity index 100% rename from apps/client/src/features/cuesheet/useColumnManager.tsx rename to apps/client/src/views/cuesheet/useColumnManager.tsx diff --git a/apps/client/src/views/project-info/ProjectInfo.tsx b/apps/client/src/views/project-info/ProjectInfo.tsx index b31b6c3ce..5f8f9e0cd 100644 --- a/apps/client/src/views/project-info/ProjectInfo.tsx +++ b/apps/client/src/views/project-info/ProjectInfo.tsx @@ -1,6 +1,7 @@ import { ProjectData } from 'ontime-types'; import Empty from '../../common/components/state/Empty'; +import EmptyPage from '../../common/components/state/EmptyPage'; import ViewLogo from '../../common/components/view-logo/ViewLogo'; import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; import { useWindowTitle } from '../../common/hooks/useWindowTitle'; @@ -16,7 +17,7 @@ interface ProjectInfoProps { isMirrored: boolean; } -export default function ProjectInfoProps(props: ProjectInfoProps) { +export default function ProjectInfo(props: ProjectInfoProps) { const { general, isMirrored } = props; useWindowTitle('Project info'); @@ -25,6 +26,25 @@ export default function ProjectInfoProps(props: ProjectInfoProps) { return ; } + if (!general) { + return ( + <> + + return ; + + ); + } + + const isEmpty = Object.values(general).every((value) => !value); + if (isEmpty) { + return ( + <> + + ; + + ); + } + return (
    diff --git a/apps/client/vite.config.js b/apps/client/vite.config.js index d8e53fa85..25b2d5dc4 100644 --- a/apps/client/vite.config.js +++ b/apps/client/vite.config.js @@ -11,6 +11,7 @@ const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN; const isDev = process.env.NODE_ENV === 'local' || process.env.NODE_ENV === 'development'; export default defineConfig({ + base: './', // Ontime cloud: we use relative paths to allow them to reference a dynamic base set at runtime plugins: [ react(), svgrPlugin(), @@ -33,7 +34,7 @@ export default defineConfig({ }), compression({ algorithm: 'brotliCompress', - exclude: /\.(html)$/, // Exclude HTML files from compression so we can change the base property at runtime + exclude: /\.(html)$/, // Ontime cloud: Exclude HTML files from compression so we can change the base property at runtime }), ], server: { diff --git a/apps/electron/package.json b/apps/electron/package.json index f09544585..7b11aff71 100644 --- a/apps/electron/package.json +++ b/apps/electron/package.json @@ -1,6 +1,6 @@ { "name": "ontime", - "version": "3.8.0", + "version": "3.9.5", "author": "Carlos Valente", "description": "Time keeping for live events", "repository": "https://github.com/cpvalente/ontime", diff --git a/apps/electron/src/menu/applicationMenu.js b/apps/electron/src/menu/applicationMenu.js index a5d9f2682..85d04754f 100644 --- a/apps/electron/src/menu/applicationMenu.js +++ b/apps/electron/src/menu/applicationMenu.js @@ -29,6 +29,7 @@ function getApplicationMenu(askToQuit, clientUrl, serverUrl, redirectWindow, sho const template = [ ...(isMac ? [makeMacMenu(askToQuit)] : []), makeFileMenu(serverUrl, redirectWindow, showDialog, download), + makeEditMenu(), makeViewMenu(clientUrl), makeSettingsMenu(redirectWindow), makeHelpMenu(redirectWindow), @@ -61,6 +62,22 @@ function makeMacMenu(askToQuit) { }; } +/** + * Utility function generates the edit menu + * @returns {Object} + */ +function makeEditMenu() { + return { + label: 'Edit', + submenu: [ + { label: 'Cut', accelerator: 'CmdOrCtrl+X', role: 'cut' }, + { label: 'Copy', accelerator: 'CmdOrCtrl+C', role: 'copy' }, + { label: 'Paste', accelerator: 'CmdOrCtrl+V', role: 'paste' }, + { label: 'Select All', accelerator: 'CmdOrCtrl+A', role: 'selectAll' }, + ], + }; +} + /** * Utility function generates the file menu * @param {string} serverUrl - base url for the application @@ -253,7 +270,7 @@ function makeSettingsMenu(redirectWindow) { click: () => redirectWindow('/editor?settings=network__log'), }, { - label: 'Manage cleints', + label: 'Manage clients', click: () => redirectWindow('/editor?settings=network__clients'), }, ], diff --git a/apps/server/package.json b/apps/server/package.json index 914594ea0..325cb79c8 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -2,7 +2,7 @@ "name": "ontime-server", "type": "module", "main": "src/index.ts", - "version": "3.8.0", + "version": "3.9.5", "exports": "./src/index.js", "dependencies": { "@googleapis/sheets": "^5.0.5", diff --git a/apps/server/src/adapters/WebsocketAdapter.ts b/apps/server/src/adapters/WebsocketAdapter.ts index 6fd5e7274..cc87be0de 100644 --- a/apps/server/src/adapters/WebsocketAdapter.ts +++ b/apps/server/src/adapters/WebsocketAdapter.ts @@ -47,8 +47,8 @@ export class SocketServer implements IAdapter { this.wss = null; } - init(server: Server) { - this.wss = new WebSocketServer({ path: '/ws', server, maxPayload: this.MAX_PAYLOAD }); + init(server: Server, prefix?: string) { + this.wss = new WebSocketServer({ path: `${prefix}/ws`, server, maxPayload: this.MAX_PAYLOAD }); this.wss.on('connection', (ws) => { const clientId = generateId(); diff --git a/apps/server/src/api-data/rundown/rundown.controller.ts b/apps/server/src/api-data/rundown/rundown.controller.ts index d5c77d4cb..0c196fc55 100644 --- a/apps/server/src/api-data/rundown/rundown.controller.ts +++ b/apps/server/src/api-data/rundown/rundown.controller.ts @@ -19,7 +19,6 @@ import { deleteEvent, editEvent, reorderEvent, - setFrozenState, swapEvents, } from '../../services/rundown-service/RundownService.js'; import { @@ -127,17 +126,6 @@ export async function rundownBatchPut(req: Request, res: Response) { - try { - const { frozen } = req.body; - setFrozenState(frozen); - res.status(200).send({ message: 'Rundown frozen state updated.' }); - } catch (error) { - const message = getErrorMessage(error); - res.status(400).send({ message }); - } -} - export async function rundownReorder(req: Request, res: Response) { if (failEmptyObjects(req.body, res)) { return; diff --git a/apps/server/src/api-data/rundown/rundown.middleware.ts b/apps/server/src/api-data/rundown/rundown.middleware.ts deleted file mode 100644 index 41f2d852e..000000000 --- a/apps/server/src/api-data/rundown/rundown.middleware.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { eventStore } from '../../stores/EventStore.js'; - -export const preventIfFrozen = function (req, res, next) { - if (eventStore.get('frozen')) { - res.status(403).send({ message: 'Rundown is frozen' }); - } else { - next(); - } -}; diff --git a/apps/server/src/api-data/rundown/rundown.router.ts b/apps/server/src/api-data/rundown/rundown.router.ts index 5cefd66d6..ebac101cb 100644 --- a/apps/server/src/api-data/rundown/rundown.router.ts +++ b/apps/server/src/api-data/rundown/rundown.router.ts @@ -5,7 +5,6 @@ import { rundownApplyDelay, rundownBatchPut, rundownDelete, - rundownFrozenPost, rundownGetAll, rundownGetById, rundownGetNormalised, @@ -19,14 +18,12 @@ import { paramsMustHaveEventId, rundownArrayOfIds, rundownBatchPutValidator, - rundownFrozenPostValidator, rundownGetPaginatedQueryParams, rundownPostValidator, rundownPutValidator, rundownReorderValidator, rundownSwapValidator, } from './rundown.validation.js'; -import { preventIfFrozen } from './rundown.middleware.js'; export const router = express.Router(); @@ -36,14 +33,13 @@ router.get('/normalised', rundownGetNormalised); router.get('/:eventId', paramsMustHaveEventId, rundownGetById); // not used in Ontime frontend router.post('/', rundownPostValidator, rundownPost); -router.post('/frozen', rundownFrozenPostValidator, rundownFrozenPost); router.put('/', rundownPutValidator, rundownPut); router.put('/batch', rundownBatchPutValidator, rundownBatchPut); -router.patch('/reorder/', rundownReorderValidator, preventIfFrozen, rundownReorder); -router.patch('/swap', rundownSwapValidator, preventIfFrozen, rundownSwap); +router.patch('/reorder/', rundownReorderValidator, rundownReorder); +router.patch('/swap', rundownSwapValidator, rundownSwap); router.patch('/applydelay/:eventId', paramsMustHaveEventId, rundownApplyDelay); -router.delete('/', rundownArrayOfIds, preventIfFrozen, deletesEventById); -router.delete('/all', preventIfFrozen, rundownDelete); +router.delete('/', rundownArrayOfIds, deletesEventById); +router.delete('/all', rundownDelete); diff --git a/apps/server/src/api-data/rundown/rundown.validation.ts b/apps/server/src/api-data/rundown/rundown.validation.ts index 117fb6fdc..2de741ff9 100644 --- a/apps/server/src/api-data/rundown/rundown.validation.ts +++ b/apps/server/src/api-data/rundown/rundown.validation.ts @@ -21,15 +21,6 @@ export const rundownPutValidator = [ }, ]; -export const rundownFrozenPostValidator = [ - body('frozen').isBoolean().exists(), - (req: Request, res: Response, next: NextFunction) => { - const errors = validationResult(req); - if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() }); - next(); - }, -] - export const rundownBatchPutValidator = [ body('data').isObject().exists(), body('ids').isArray().exists(), diff --git a/apps/server/src/api-integration/integration.controller.ts b/apps/server/src/api-integration/integration.controller.ts index a9e83e035..ded76835e 100644 --- a/apps/server/src/api-integration/integration.controller.ts +++ b/apps/server/src/api-integration/integration.controller.ts @@ -241,6 +241,11 @@ const actionHandlers: Record = { const timeInMs = numberOrError(command.duration) * 1000; reply.payload = auxTimerService.setTime(timeInMs); } + if ('addtime' in command) { + // convert addTime in seconds to ms + const timeInMs = numberOrError(command.addtime) * 1000; + reply.payload = auxTimerService.addTime(timeInMs); + } if ('direction' in command) { if (command.direction === SimpleDirection.CountUp || command.direction === SimpleDirection.CountDown) { reply.payload = auxTimerService.setDirection(command.direction); diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index 3be2fb6de..b084ec13e 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -6,11 +6,11 @@ import expressStaticGzip from 'express-static-gzip'; import http, { Server } from 'http'; import cors from 'cors'; import serverTiming from 'server-timing'; -import { extname, resolve } from 'path'; +import { extname } from 'node:path'; // import utils -import { publicDir, srcDir } from './setup/index.js'; -import { environment, isProduction, updateRouterPrefix } from './externals.js'; +import { publicDir, srcDir, srcFiles } from './setup/index.js'; +import { environment, isOntimeCloud, isProduction, updateRouterPrefix } from './externals.js'; import { ONTIME_VERSION } from './ONTIME_VERSION.js'; import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js'; @@ -53,8 +53,14 @@ if (!canLog) { console.log(`Ontime public directory at ${publicDir.root} `); } -// calls an update to the client router prefix -updateRouterPrefix(); +/** + * When running in Ontime cloud, the client is not at the root segment + * ie: https://cloud.getontime.com/client-hash/timer + * This means: + * - changing the base path in the index.html file + * - prepending all express routes with the given prefix + */ +const prefix = updateRouterPrefix(); // Create express APP const app = express(); @@ -75,20 +81,20 @@ app.use(express.urlencoded({ extended: true })); app.use(express.json({ limit: '1mb' })); // Implement route endpoints -app.use('/data', appRouter); // router for application data -app.use('/api', integrationRouter); // router for integrations +app.use(`${prefix}/data`, appRouter); // router for application data +app.use(`${prefix}/api`, integrationRouter); // router for integrations // serve static external files -app.use('/external', express.static(publicDir.externalDir)); -app.use('/user', express.static(publicDir.userDir)); - -// if the user reaches to the root, we show a 404 -app.use('/external', (req, res) => { +app.use(`${prefix}/external`, express.static(publicDir.externalDir)); +app.use(`${prefix}/external`, (req, res) => { + // if the user reaches to the root, we show a 404 res.status(404).send(`${req.originalUrl} not found`); }); +app.use(`${prefix}/user`, express.static(publicDir.userDir)); // serve static - react, in dev/test mode we fetch the React app from module app.use( + prefix, expressStaticGzip(srcDir.clientDir, { enableBrotli: true, orderPreference: ['br'], @@ -111,8 +117,8 @@ app.use( }), ); -app.get('*', (_req, res) => { - res.sendFile(resolve(srcDir.clientDir, 'index.html')); +app.get(`${prefix}/*`, (_req, res) => { + res.sendFile(srcFiles.clientIndexHtml); }); // Implement catch all @@ -183,7 +189,7 @@ export const startServer = async ( const portError = resultPort !== desiredPort; await getDataProvider().setSettings({ ...settings, serverPort: resultPort }); - socket.init(expressServer); + socket.init(expressServer, prefix); /** * Module initialises the services and provides initial payload for the store @@ -209,7 +215,6 @@ export const startServer = async ( playback: SimplePlayback.Stop, direction: SimpleDirection.CountDown, }, - frozen: false, ping: -1, }); @@ -228,10 +233,10 @@ export const startServer = async ( runtimeService.init(maybeRestorePoint); const nif = getNetworkInterfaces(); - consoleSuccess(`Local: http://localhost:${resultPort}/editor`); + consoleSuccess(`Local: http://localhost:${resultPort}${prefix}/editor`); for (const key in nif) { const address = nif[key].address; - consoleSuccess(`Network: http://${address}:${resultPort}/editor`); + consoleSuccess(`Network: http://${address}:${resultPort}${prefix}/editor`); } const returnMessage = `Ontime is listening on port ${resultPort}`; @@ -253,16 +258,6 @@ export const startIntegrations = async () => { // if a config is not provided, we use the persisted one const { osc, http } = getDataProvider().getData(); - if (osc) { - logger.info(LogOrigin.Tx, 'Initialising OSC Integration...'); - try { - oscIntegration.init(osc); - integrationService.register(oscIntegration); - } catch (error) { - logger.error(LogOrigin.Tx, 'OSC Integration initialisation failed'); - } - } - if (http) { logger.info(LogOrigin.Tx, 'Initialising HTTP Integration...'); try { @@ -272,6 +267,21 @@ export const startIntegrations = async () => { logger.error(LogOrigin.Tx, `HTTP Integration initialisation failed: ${error}`); } } + + if (isOntimeCloud) { + logger.info(LogOrigin.Tx, 'Skipping OSC in Cloud environment...'); + return; + } + + if (osc) { + logger.info(LogOrigin.Tx, 'Initialising OSC Integration...'); + try { + oscIntegration.init(osc); + integrationService.register(oscIntegration); + } catch (error) { + logger.error(LogOrigin.Tx, 'OSC Integration initialisation failed'); + } + } }; /** diff --git a/apps/server/src/classes/simple-timer/SimpleTimer.ts b/apps/server/src/classes/simple-timer/SimpleTimer.ts index cc3e03fde..8ae23cb46 100644 --- a/apps/server/src/classes/simple-timer/SimpleTimer.ts +++ b/apps/server/src/classes/simple-timer/SimpleTimer.ts @@ -37,6 +37,14 @@ export class SimpleTimer { return this.state; } + public addTime(millis: number): SimpleTimerState { + this.state.duration += millis; + // the value of current will be overridden when update is called, + // but if we are in pause or stop state it will not be changed so we do it here + this.state.current += millis; + return this.state; + } + public setDirection(direction: SimpleDirection, timeNow: number): SimpleTimerState { // if we are playing, we need to reset the targets if (this.state.playback === SimplePlayback.Start) { diff --git a/apps/server/src/classes/simple-timer/__tests__/SimpleTimer.test.ts b/apps/server/src/classes/simple-timer/__tests__/SimpleTimer.test.ts index c516eb6fa..cc6c924c8 100644 --- a/apps/server/src/classes/simple-timer/__tests__/SimpleTimer.test.ts +++ b/apps/server/src/classes/simple-timer/__tests__/SimpleTimer.test.ts @@ -177,5 +177,60 @@ describe('SimpleTimer count-down', () => { playback: SimplePlayback.Start, }); }); + + test('adding time affects final result', () => { + timer.reset(); + + timer.setTime(1000); + timer.start(0); + timer.update(100); + expect(timer.state).toMatchObject({ current: 900, duration: 1000 }); + + timer.addTime(1000); + timer.update(200); + expect(timer.state).toMatchObject({ current: 1800, duration: 2000 }); + + timer.update(300); + expect(timer.state).toMatchObject({ current: 1700, duration: 2000 }); + + timer.stop(); + expect(timer.state).toMatchObject({ current: 1000, duration: 1000 }); + }); + + test('adding time affects paused timer', () => { + timer.reset(); + + timer.setTime(1000); + timer.start(0); + timer.update(100); + expect(timer.state).toMatchObject({ current: 900, duration: 1000 }); + + timer.pause(200); + expect(timer.state).toMatchObject({ current: 900, duration: 1000 }); + + timer.addTime(1000); + timer.update(200); + expect(timer.state).toMatchObject({ current: 1900, duration: 2000 }); + + timer.start(300); + expect(timer.state).toMatchObject({ current: 1800, duration: 2000 }); + }); + + test('adding time affects stopped timer, but returns to initial valuses when stopped again', () => { + timer.reset(); + + timer.setTime(1000); + expect(timer.state).toMatchObject({ current: 1000, duration: 1000 }); + + timer.addTime(1000); + expect(timer.state).toMatchObject({ current: 2000, duration: 2000 }); + + timer.start(0); + timer.update(100); + expect(timer.state).toMatchObject({ current: 1900, duration: 2000 }); + + timer.stop(); + expect(timer.state).toMatchObject({ current: 1000, duration: 1000 }); + }); }); }); diff --git a/apps/server/src/config/config.ts b/apps/server/src/config/config.ts index 67af650e8..80778d468 100644 --- a/apps/server/src/config/config.ts +++ b/apps/server/src/config/config.ts @@ -1,7 +1,7 @@ import { MILLIS_PER_MINUTE } from 'ontime-utils'; export const timerConfig = { - skipLimit: 1000, // threshold of skip for recalculating + skipLimit: 1000, // threshold of skip for recalculating, values lower than updateRate can cause issues with rolling over midnight updateRate: 32, // how often do we update the timer notificationRate: 1000, // how often do we notify clients and integrations triggerAhead: 10, // how far ahead do we trigger the end event diff --git a/apps/server/src/externals.ts b/apps/server/src/externals.ts index 7593833ff..13e886e41 100644 --- a/apps/server/src/externals.ts +++ b/apps/server/src/externals.ts @@ -3,7 +3,8 @@ */ import { readFileSync, writeFileSync } from 'node:fs'; -import { resolve } from 'node:path'; + +import { srcFiles } from './setup/index.js'; // ================================================= // resolve running environment @@ -13,25 +14,26 @@ export const isTest = Boolean(process.env.IS_TEST); export const environment = isTest ? 'test' : env; export const isDocker = env === 'docker'; export const isProduction = isDocker || (env === 'production' && !isTest); - +export const isOntimeCloud = Boolean(process.env.IS_CLOUD); /** * Updates the router prefix in the index.html file * This is only needed in the cloud environment where the client is not at the root segment * ie: https://cloud.getontime.com/client-hash/timer */ -export function updateRouterPrefix(prefix: string | undefined = process.env.ROUTER_PREFIX) { +export function updateRouterPrefix(prefix: string | undefined = process.env.ROUTER_PREFIX): string { if (!prefix) { - return; + return ''; } - const indexFile = resolve('.', 'client', 'index.html'); try { - const data = readFileSync(indexFile, { encoding: 'utf-8', flag: 'r' }).replace( - //g, - `', + ``, ); - writeFileSync(indexFile, data, { encoding: 'utf-8', flag: 'w' }); + writeFileSync(srcFiles.clientIndexHtml, data, { encoding: 'utf-8', flag: 'w' }); } catch (_error) { /** unhandled */ } + + return `/${prefix}`; } diff --git a/apps/server/src/services/aux-timer-service/AuxTimerService.ts b/apps/server/src/services/aux-timer-service/AuxTimerService.ts index 48d9157f2..e0b4c59e5 100644 --- a/apps/server/src/services/aux-timer-service/AuxTimerService.ts +++ b/apps/server/src/services/aux-timer-service/AuxTimerService.ts @@ -1,4 +1,4 @@ -import { SimpleDirection, SimpleTimerState } from 'ontime-types'; +import { SimpleDirection, SimplePlayback, SimpleTimerState } from 'ontime-types'; import { SimpleTimer } from '../../classes/simple-timer/SimpleTimer.js'; import { eventStore } from '../../stores/EventStore.js'; @@ -57,6 +57,15 @@ export class AuxTimerService { return this.timer.setTime(duration); } + @broadcastReturn + addTime(millis: number) { + if (this.timer.state.playback === SimplePlayback.Start) { + this.timer.addTime(millis); + return this.timer.update(this.getTime()); + } + return this.timer.addTime(millis); + } + @broadcastReturn private update() { return this.timer.update(this.getTime()); diff --git a/apps/server/src/services/integration-service/OscIntegration.ts b/apps/server/src/services/integration-service/OscIntegration.ts index ac03398cb..8e5ae5c67 100644 --- a/apps/server/src/services/integration-service/OscIntegration.ts +++ b/apps/server/src/services/integration-service/OscIntegration.ts @@ -128,18 +128,18 @@ export class OscIntegration implements IIntegration { ensureDirectory(publicDir.logoDir); const newFilePath = join(publicDir.logoDir, name); - await rename(filePath, newFilePath); + await dockerSafeRename(filePath, newFilePath); return name; } @@ -86,7 +91,7 @@ export async function copyCorruptFile(filePath: string, name: string): Promise { const newPath = join(publicDir.corruptDir, name); - return rename(filePath, newPath); + return dockerSafeRename(filePath, newPath); } /** diff --git a/apps/server/src/services/rundown-service/RundownService.ts b/apps/server/src/services/rundown-service/RundownService.ts index bd008d7a8..3fa8c3ef7 100644 --- a/apps/server/src/services/rundown-service/RundownService.ts +++ b/apps/server/src/services/rundown-service/RundownService.ts @@ -21,7 +21,6 @@ import { runtimeService } from '../runtime-service/RuntimeService.js'; import * as cache from './rundownCache.js'; import { getPlayableEvents, getTimedEvents } from './rundownUtils.js'; -import { eventStore } from '../../stores/EventStore.js'; type PatchWithId = (Partial | Partial | Partial) & { id: string }; @@ -275,7 +274,3 @@ export async function initRundown(rundown: Readonly, customFields // notify timer of change notifyChanges({ timer: true, external: true, reload: true }); } - -export async function setFrozenState(state: boolean) { - eventStore.set('frozen', state); -} diff --git a/apps/server/src/services/runtime-service/RuntimeService.ts b/apps/server/src/services/runtime-service/RuntimeService.ts index 43c50e888..1d8be1d4f 100644 --- a/apps/server/src/services/runtime-service/RuntimeService.ts +++ b/apps/server/src/services/runtime-service/RuntimeService.ts @@ -99,10 +99,15 @@ class RuntimeService { }); this.handleLoadNext(); this.rollLoaded(keepOffset); - } else if (skippedOutOfEvent(newState, this.lastIntegrationClockUpdate, timerConfig.skipLimit)) { + } else if ( + // if there is no previous clock, we could not have skipped + RuntimeService.previousState?.clock && + skippedOutOfEvent(newState, RuntimeService.previousState.clock, timerConfig.skipLimit) + ) { // if we have skipped out of the event, we will recall roll // to push the playback to the right place // this comes with the caveat that we will lose our runtime data + logger.warning(LogOrigin.Playback, 'Time skip detected, reloading roll'); this.roll(true); } } diff --git a/apps/server/src/setup/index.ts b/apps/server/src/setup/index.ts index 3a1c7c96e..0990d69c0 100644 --- a/apps/server/src/setup/index.ts +++ b/apps/server/src/setup/index.ts @@ -77,6 +77,8 @@ export const srcDir = { } as const; export const srcFiles = { + /** Path to start index.html */ + clientIndexHtml: join(srcDir.clientDir, 'index.html'), /** Path to bundled CSS */ cssOverride: join(srcDir.root, config.user, config.styles.directory, config.styles.filename), /** Path to bundled external readme */ diff --git a/apps/server/src/utils/__tests__/url.test.ts b/apps/server/src/utils/__tests__/url.test.ts deleted file mode 100644 index 326a33357..000000000 --- a/apps/server/src/utils/__tests__/url.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { cleanURL } from '../url.js'; - -describe('url is correctly formatted', () => { - it('has no leading spaces', () => { - const test = ' http://testing'; - const expected = 'http://testing'; - expect(cleanURL(test)).toBe(expected); - }); - - it('has no trailing spaces', () => { - const test = 'http://testing '; - const expected = 'http://testing'; - expect(cleanURL(test)).toBe(expected); - }); - - it('doesnt contain spaces', () => { - const test = 'http://t e s t i n g'; - const expected = 'http://t%20e%20s%20t%20i%20n%20g'; - expect(cleanURL(test)).toBe(expected); - }); - - it('only contains allowed characters', () => { - const test = 'http://<>[]{}|^'; - const expected = 'http://'; - expect(cleanURL(test)).toBe(expected); - }); - - it('begins with http://', () => { - const test = 'ontime.com'; - const expected = 'http://ontime.com'; - expect(cleanURL(test)).toBe(expected); - }); -}); diff --git a/apps/server/src/utils/fileManagement.ts b/apps/server/src/utils/fileManagement.ts index 5a2d361dd..759a6ee29 100644 --- a/apps/server/src/utils/fileManagement.ts +++ b/apps/server/src/utils/fileManagement.ts @@ -1,5 +1,5 @@ -import { existsSync, mkdirSync } from 'fs'; -import { readdir, copyFile } from 'fs/promises'; +import { existsSync, mkdirSync, PathLike } from 'fs'; +import { readdir, copyFile, unlink } from 'fs/promises'; import { basename, extname, join, parse } from 'path'; /** @@ -105,3 +105,12 @@ export async function copyDirectory(src: string, dest: string) { } } } + +/** + * workaround avoids origin errors in docker deployments + * EXDEV cross-device link not permitted + */ +export async function dockerSafeRename(oldPath: PathLike, newPath: PathLike) { + await copyFile(oldPath, newPath); + await unlink(oldPath); +} diff --git a/apps/server/src/utils/url.ts b/apps/server/src/utils/url.ts deleted file mode 100644 index 9f8fd7bba..000000000 --- a/apps/server/src/utils/url.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * @description Cleans given url - * @param {string} url - URL to be checked - * @returns {string} Sanitized url - */ -export const cleanURL = (url: string): string => { - // trim whitespaces - let sanitised = url.trim(); - - // clear any whitespaces - sanitised = sanitised.split(' ').join('%20'); - - // contain only allowed characters - sanitised = sanitised.replace(/([@\s<>[\]{}|\\^])+/g, ''); - - // starts with http:// - if (!sanitised.startsWith('http://')) sanitised = `http://${sanitised}`; - - return sanitised; -}; diff --git a/docker-compose.yml b/docker-compose.yml index 88ca8b333..6720dfbff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3" - services: ontime: container_name: ontime diff --git a/e2e/tests/000-upload-showfile.spec.ts b/e2e/tests/000-upload-showfile.spec.ts index 5517b83ab..f1245ac82 100644 --- a/e2e/tests/000-upload-showfile.spec.ts +++ b/e2e/tests/000-upload-showfile.spec.ts @@ -4,6 +4,10 @@ const fileToUpload = 'e2e/tests/fixtures/test-db.json'; test('project file upload', async ({ page }) => { await page.goto('http://localhost:4001/editor'); + + // close the welcome modal if it is open + await page.keyboard.down('Escape'); + await page.getByRole('button', { name: 'Edit' }).click(); await page.getByRole('button', { name: 'Clear rundown' }).click(); await page.getByRole('button', { name: 'Delete all' }).click(); diff --git a/e2e/tests/features/202-cuesheet.spec.ts b/e2e/tests/features/202-cuesheet.spec.ts index c4f23e4f9..df98815e7 100644 --- a/e2e/tests/features/202-cuesheet.spec.ts +++ b/e2e/tests/features/202-cuesheet.spec.ts @@ -1,11 +1,18 @@ -import { test } from '@playwright/test'; +import { expect, test } from '@playwright/test'; -test('cuesheet displays events and exports csv', async ({ page }) => { +test('cuesheet displays events', async ({ page }) => { // same elements in cuesheet await page.goto('http://localhost:4001/cuesheet'); - await page.getByText('Eurovision Song Contest').click(); - await page.getByRole('cell', { name: 'Lunch break' }).click(); - await page.getByRole('cell', { name: 'Albania' }).click(); - await page.getByRole('cell', { name: 'Latvia' }).click(); - await page.getByRole('cell', { name: 'Lithuania' }).click(); + await expect(page.getByText('Eurovision Song Contest')).toBeVisible(); + await expect(page.getByRole('row', { name: 'Lunch break' })).toBeVisible(); + + await expect(page.locator('tr:nth-child(1) > td:nth-child(7)').first().getByRole('textbox').first()).toHaveValue( + 'Albania', + ); + await expect(page.locator('tr:nth-child(2) > td:nth-child(7)').first().getByRole('textbox').first()).toHaveValue( + 'Latvia', + ); + await expect(page.locator('tr:nth-child(3) > td:nth-child(7)').first().getByRole('textbox').first()).toHaveValue( + 'Lithuania', + ); }); diff --git a/e2e/tests/features/302-client-remote.spec.ts b/e2e/tests/features/210-client-remote.spec.ts similarity index 100% rename from e2e/tests/features/302-client-remote.spec.ts rename to e2e/tests/features/210-client-remote.spec.ts diff --git a/package.json b/package.json index f3a6c3cab..c5dd11bc4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ontime", - "version": "3.8.0", + "version": "3.9.5", "description": "Time keeping for live events", "keywords": [ "ontime", diff --git a/packages/types/src/definitions/runtime/RuntimeStore.ts b/packages/types/src/definitions/runtime/RuntimeStore.ts index 13dec8b30..bed1a8229 100644 --- a/packages/types/src/definitions/runtime/RuntimeStore.ts +++ b/packages/types/src/definitions/runtime/RuntimeStore.ts @@ -51,6 +51,5 @@ export const runtimeStorePlaceholder: RuntimeStore = { duration: 0, playback: SimplePlayback.Stop, }, - frozen: false, ping: -1, }; diff --git a/packages/types/src/definitions/runtime/RuntimeStore.type.ts b/packages/types/src/definitions/runtime/RuntimeStore.type.ts index 074b04c9f..b2886441c 100644 --- a/packages/types/src/definitions/runtime/RuntimeStore.type.ts +++ b/packages/types/src/definitions/runtime/RuntimeStore.type.ts @@ -25,7 +25,6 @@ export type RuntimeStore = { // extra timers auxtimer1: SimpleTimerState; - // flags - frozen: boolean; + // utils ping: number; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c02c776de..4d5abd9b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -114,23 +114,23 @@ importers: specifier: ^4.1.0 version: 4.1.0(react@18.3.1) '@sentry/react': - specifier: ^8.19.0 - version: 8.19.0(react@18.3.1) + specifier: ^8.43.0 + version: 8.45.0(react@18.3.1) '@tanstack/react-query': - specifier: ^5.17.9 - version: 5.17.9(react@18.3.1) + specifier: ^5.62.7 + version: 5.62.7(react@18.3.1) '@tanstack/react-query-devtools': - specifier: ^5.17.9 - version: 5.17.9(@tanstack/react-query@5.17.9(react@18.3.1))(react@18.3.1) + specifier: ^5.62.7 + version: 5.62.7(@tanstack/react-query@5.62.7(react@18.3.1))(react@18.3.1) '@tanstack/react-table': - specifier: ^8.11.3 - version: 8.11.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^8.20.5 + version: 8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) autosize: specifier: ^6.0.1 version: 6.0.1 axios: specifier: ^1.2.0 - version: 1.2.2 + version: 1.7.2 color: specifier: ^4.2.3 version: 4.2.3 @@ -293,7 +293,7 @@ importers: version: 2.8.5 dotenv: specifier: ^16.0.1 - version: 16.0.3 + version: 16.3.1 express: specifier: ^4.18.2 version: 4.18.2 @@ -469,14 +469,6 @@ packages: resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} engines: {node: '>=6.0.0'} - '@babel/code-frame@7.18.6': - resolution: {integrity: sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==} - engines: {node: '>=6.9.0'} - - '@babel/code-frame@7.23.5': - resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} - engines: {node: '>=6.9.0'} - '@babel/code-frame@7.24.2': resolution: {integrity: sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==} engines: {node: '>=6.9.0'} @@ -509,10 +501,6 @@ packages: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.18.6': - resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.22.15': resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} engines: {node: '>=6.9.0'} @@ -539,10 +527,6 @@ packages: resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.22.20': - resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.24.5': resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} engines: {node: '>=6.9.0'} @@ -555,10 +539,6 @@ packages: resolution: {integrity: sha512-wCfsbN4nBidDRhpDhvcKlzHWCTlgJYUUdSJfzXb2NuBssDSIjc3xcb+znA7l+zYsFljAcGM0aFkN40cR3lXiGA==} engines: {node: '>=6.9.0'} - '@babel/highlight@7.23.4': - resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} - engines: {node: '>=6.9.0'} - '@babel/highlight@7.24.5': resolution: {integrity: sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==} engines: {node: '>=6.9.0'} @@ -580,18 +560,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.20.7': - resolution: {integrity: sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.21.0': - resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==} - engines: {node: '>=6.9.0'} - - '@babel/runtime@7.22.5': - resolution: {integrity: sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==} - engines: {node: '>=6.9.0'} - '@babel/runtime@7.24.5': resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} engines: {node: '>=6.9.0'} @@ -1671,9 +1639,6 @@ packages: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.4.15': - resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} @@ -1815,28 +1780,28 @@ packages: cpu: [x64] os: [win32] - '@sentry-internal/browser-utils@8.19.0': - resolution: {integrity: sha512-kM/2KlikKuBR63nFi2q7MGS3V9K9hakjvUknhr/jHZqDVfEuBKmp1ZlHFAdJtglKHHJy07gPj/XqDH7BbYh5yg==} + '@sentry-internal/browser-utils@8.45.0': + resolution: {integrity: sha512-MX/E/C+W5I9jkGD1PsbZ2hpCc7YuizNKmEbuGPxQPfUSIPrdE2wpo6ZfIhEbxq9m/trl1oRCN4PXi3BB7dlYYg==} engines: {node: '>=14.18'} - '@sentry-internal/feedback@8.19.0': - resolution: {integrity: sha512-Jc77H8fEaGcBhERc2U/o7Q8CZHvlZLT9vAlzq0ZZR20v/1vwYcJW1ysKfTuvmw22hCR6ukhFNl6pqJocXFVhvA==} + '@sentry-internal/feedback@8.45.0': + resolution: {integrity: sha512-WerpfkKrKPAlnQuqjEgKXZtrx68cla7GyOkNOeL40JQbY4/By4Qjx1atUOmgk/FdjrCLPw+jQQY9pXRpMRqqRw==} engines: {node: '>=14.18'} - '@sentry-internal/replay-canvas@8.19.0': - resolution: {integrity: sha512-l4pKJDHrXEctxrK7Xme/+fKToXpGwr/G2t77BzeE1WEw9LwSwADz/hi8HoMdZzuKWriM2BNbz20tpVS84sODxA==} + '@sentry-internal/replay-canvas@8.45.0': + resolution: {integrity: sha512-LZ8kBuzO5gutDiWnCyYEzBMDLq9PIllcsWsXRpKoau0Zqs3DbyRolI11dNnxmUSh7UW21FksxBpqn5yPmUMbag==} engines: {node: '>=14.18'} - '@sentry-internal/replay@8.19.0': - resolution: {integrity: sha512-EW9e1J6XbqXUXQST1AfSIzT9O8OwPyeFOkhkn9/gqOQv08TJvQEIBtWJEoJS+XFMEUuB8IqIzVWNVko/DnGt9A==} + '@sentry-internal/replay@8.45.0': + resolution: {integrity: sha512-SOFwFpzx0B6lxhLl2hBnxvybo7gdB5TMY8dOHMwXgk5A2+BXvSpvWXnr33yqUlBmC8R3LeFTB3C0plzM5lhkJg==} engines: {node: '>=14.18'} '@sentry/babel-plugin-component-annotate@2.16.1': resolution: {integrity: sha512-pJka66URsqQbk6hTs9H1XFpUeI0xxuqLYf9Dy5pRGNHSJMtfv91U+CaYSWt03aRRMGDXMduh62zAAY7Wf0HO+A==} engines: {node: '>= 14'} - '@sentry/browser@8.19.0': - resolution: {integrity: sha512-ZC1HxIFm4TIGONyy9MkPG6Dw8IAhzq43t5mq9PqrB1ehuWj8GX6Vk3E26kuc2sydAm4AXbj0562OmvZHsAJpUA==} + '@sentry/browser@8.45.0': + resolution: {integrity: sha512-Y+BcfpXY1eEkOYOzgLGkx1YH940uMAymYOxfSZSvC+Vx6xHuaGT05mIFef/aeZbyu2AUs6JjdvD1BRBZlHg78w==} engines: {node: '>=14.18'} '@sentry/bundler-plugin-core@2.16.1': @@ -1889,24 +1854,16 @@ packages: engines: {node: '>= 10'} hasBin: true - '@sentry/core@8.19.0': - resolution: {integrity: sha512-MrgjsZCEjOJgQjIznnDSrLEy7qL+4LVpNieAvr49cV1rzBNSwGmWRnt/puVaPsLyCUgupVx/43BPUHB/HtKNUw==} + '@sentry/core@8.45.0': + resolution: {integrity: sha512-4YTuBipWSh4JrtSYS5GxUQBAcAgOIkEoFfFbwVcr3ivijOacJLRXTBn3rpcy1CKjBq0PHDGR+2RGRYC+bNAMxg==} engines: {node: '>=14.18'} - '@sentry/react@8.19.0': - resolution: {integrity: sha512-MzuMy4AEdSuIrBEyp3W7c4+v215+2MiU9ba7Y0KBKcC/Nrf1cGfRFRbjl9OYm/JIuxkaop7kgYs6sPMrVJVlrQ==} + '@sentry/react@8.45.0': + resolution: {integrity: sha512-xuJBDATJKAHOxpR5IBfGFWJxXb05GMPGGpk8UoWai1Mh50laAQ0/WW+5sDAKrCjXoA+JZ6fb3DP8EE2X93n1nw==} engines: {node: '>=14.18'} peerDependencies: react: ^16.14.0 || 17.x || 18.x || 19.x - '@sentry/types@8.19.0': - resolution: {integrity: sha512-52C8X5V7mK2KIxMJt8MV5TxXAFHqrQR1RKm1oPTwKVWm8hKr1ZYJXINymNrWvpAc3oVIKLC/sa9WFYgXQh+YlA==} - engines: {node: '>=14.18'} - - '@sentry/utils@8.19.0': - resolution: {integrity: sha512-8dWJJKaUN6Hf92Oxw2TBmHchGua2W3ZmonrZTTwLvl06jcAigbiQD0MGuF5ytZP8PHx860orV+SbTGKFzfU3Pg==} - engines: {node: '>=14.18'} - '@sentry/vite-plugin@2.16.1': resolution: {integrity: sha512-RSIyeqFG3PR5iJsZnagQxzOhM22z1Kh9DG+HQQsfVrxokzrWKRu/G17O2MIDh2I5iYEaL0Fkd/9RAXE4/b0aVg==} engines: {node: '>= 14'} @@ -2012,32 +1969,32 @@ packages: peerDependencies: eslint: ^8.0.0 - '@tanstack/query-core@5.17.9': - resolution: {integrity: sha512-8xcvpWIPaRMDNLMvG9ugcUJMgFK316ZsqkPPbsI+TMZsb10N9jk0B6XgPk4/kgWC2ziHyWR7n7wUhxmD0pChQw==} + '@tanstack/query-core@5.62.7': + resolution: {integrity: sha512-fgpfmwatsrUal6V+8EC2cxZIQVl9xvL7qYa03gsdsCy985UTUlS4N+/3hCzwR0PclYDqisca2AqR1BVgJGpUDA==} - '@tanstack/query-devtools@5.17.7': - resolution: {integrity: sha512-TfgvOqza5K7Sk6slxqkRIvXlEJoUoPSsGGwpuYSrpqgSwLSSvPPpZhq7hv7hcY5IvRoTNGoq6+MT01C/jILqoQ==} + '@tanstack/query-devtools@5.61.4': + resolution: {integrity: sha512-21Tw+u8E3IJJj4A/Bct4H0uBaDTEu7zBrR79FeSyY+mS2gx5/m316oDtJiKkILc819VSTYt+sFzODoJNcpPqZQ==} - '@tanstack/react-query-devtools@5.17.9': - resolution: {integrity: sha512-1viWP/jlO0LaeCdtTFqtF1k2RfM3KVpvwVffWv+PMNkS2u4s8YGUM17r3p82udbF9BY1mE7aHqQ3MM1errF5lQ==} + '@tanstack/react-query-devtools@5.62.7': + resolution: {integrity: sha512-wxXsdTZJRs//hMtJMU5aNlUaTclRFPqLvDNeWbRj8YpGD3aoo4zyu53W55W2DY16+ycg3fti21uCW4N9oyj91w==} peerDependencies: - '@tanstack/react-query': ^5.17.9 - react: ^18.0.0 + '@tanstack/react-query': ^5.62.7 + react: ^18 || ^19 - '@tanstack/react-query@5.17.9': - resolution: {integrity: sha512-M5E9gwUq1Stby/pdlYjBlL24euIVuGbWKIFCbtnQxSdXI4PgzjTSdXdV3QE6fc+itF+TUvX/JPTKIwq8yuBXcg==} + '@tanstack/react-query@5.62.7': + resolution: {integrity: sha512-+xCtP4UAFDTlRTYyEjLx0sRtWyr5GIk7TZjZwBu4YaNahi3Rt2oMyRqfpfVrtwsqY2sayP4iXVCwmC+ZqqFmuw==} peerDependencies: - react: ^18.0.0 + react: ^18 || ^19 - '@tanstack/react-table@8.11.3': - resolution: {integrity: sha512-Gwwm7po1MaObBguw69L+UiACkaj+eOtThQEArj/3fmUwMPiWaJcXvNG2X5Te5z2hg0HMx8h0T0Q7p5YmQlTUfw==} + '@tanstack/react-table@8.20.6': + resolution: {integrity: sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ==} engines: {node: '>=12'} peerDependencies: - react: '>=16' - react-dom: '>=16' + react: '>=16.8' + react-dom: '>=16.8' - '@tanstack/table-core@8.11.3': - resolution: {integrity: sha512-nkcFIL696wTf1QMvhGR7dEg60OIRwEZm1OqFTYYDTRc4JOWspgrsJO3IennsOJ7ptumHWLDjV8e5BjPkZcSZAQ==} + '@tanstack/table-core@8.20.5': + resolution: {integrity: sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==} engines: {node: '>=12'} '@testing-library/dom@10.1.0': @@ -2069,9 +2026,6 @@ packages: resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} engines: {node: '>= 10'} - '@types/aria-query@5.0.1': - resolution: {integrity: sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==} - '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -2243,18 +2197,10 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@5.48.1': - resolution: {integrity: sha512-S035ueRrbxRMKvSTv9vJKIWgr86BD8s3RqoRZmsSh/s8HhIs90g6UlK8ZabUSjUZQkhVxt7nmZ63VJ9dcZhtDQ==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/scope-manager@5.62.0': resolution: {integrity: sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/scope-manager@6.21.0': - resolution: {integrity: sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==} - engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/scope-manager@7.16.1': resolution: {integrity: sha512-nYpyv6ALte18gbMz323RM+vpFpTjfNdyakbf3nsLvF43uF9KeNC289SUEW3QLZ1xPtyINJ1dIsZOuWuSRIWygw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2269,31 +2215,14 @@ packages: typescript: optional: true - '@typescript-eslint/types@5.48.1': - resolution: {integrity: sha512-xHyDLU6MSuEEdIlzrrAerCGS3T7AA/L8Hggd0RCYBi0w3JMvGYxlLlXHeg50JI9Tfg5MrtsfuNxbS/3zF1/ATg==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/types@5.62.0': resolution: {integrity: sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/types@6.21.0': - resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==} - engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/types@7.16.1': resolution: {integrity: sha512-AQn9XqCzUXd4bAVEsAXM/Izk11Wx2u4H3BAfQVhSfzfDOm/wAON9nP7J5rpkCxts7E5TELmN845xTUCQrD1xIQ==} engines: {node: ^18.18.0 || >=20.0.0} - '@typescript-eslint/typescript-estree@5.48.1': - resolution: {integrity: sha512-Hut+Osk5FYr+sgFh8J/FHjqX6HFcDzTlWLrFqGoK5kVUN3VBHF/QzZmAsIXCQ8T/W9nQNBTqalxi1P3LSqWnRA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/typescript-estree@5.62.0': resolution: {integrity: sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2303,15 +2232,6 @@ packages: typescript: optional: true - '@typescript-eslint/typescript-estree@6.21.0': - resolution: {integrity: sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - '@typescript-eslint/typescript-estree@7.16.1': resolution: {integrity: sha512-0vFPk8tMjj6apaAZ1HlwM8w7jbghC8jc1aRNJG5vN8Ym5miyhTQGMqU++kuBFDNKe9NcPeZ6x0zfSzV8xC1UlQ==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2321,42 +2241,22 @@ packages: typescript: optional: true - '@typescript-eslint/utils@5.48.1': - resolution: {integrity: sha512-SmQuSrCGUOdmGMwivW14Z0Lj8dxG1mOFZ7soeJ0TQZEJcs3n5Ndgkg0A4bcMFzBELqLJ6GTHnEU+iIoaD6hFGA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@typescript-eslint/utils@5.62.0': resolution: {integrity: sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - '@typescript-eslint/utils@6.21.0': - resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==} - engines: {node: ^16.0.0 || >=18.0.0} - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - '@typescript-eslint/utils@7.16.1': resolution: {integrity: sha512-WrFM8nzCowV0he0RlkotGDujx78xudsxnGMBHI88l5J8wEhED6yBwaSLP99ygfrzAjsQvcYQ94quDwI0d7E1fA==} engines: {node: ^18.18.0 || >=20.0.0} peerDependencies: eslint: ^8.56.0 - '@typescript-eslint/visitor-keys@5.48.1': - resolution: {integrity: sha512-Ns0XBwmfuX7ZknznfXozgnydyR8F6ev/KEGePP4i74uL3ArsKbEhJ7raeKr1JSa997DBDwol/4a0Y+At82c9dA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/visitor-keys@5.62.0': resolution: {integrity: sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@typescript-eslint/visitor-keys@6.21.0': - resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==} - engines: {node: ^16.0.0 || >=18.0.0} - '@typescript-eslint/visitor-keys@7.16.1': resolution: {integrity: sha512-Qlzzx4sE4u3FsHTPQAAQFJFNOuqtuY0LFrZHwQ8IHK705XxBiWOFkfKRWu6niB7hwfgnwIpO4jTC75ozW1PHWg==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2506,9 +2406,6 @@ packages: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} engines: {node: '>=10'} - aria-query@5.1.3: - resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} - aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} @@ -2563,9 +2460,6 @@ packages: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} - axios@1.2.2: - resolution: {integrity: sha512-bz/J4gS2S3I7mpN/YZfGFTqhXTYzRho8Ay38w2otuuDR322KzFIWm/4W2K6gIwvWaws5n+mnb7D1lN9uD+QH6Q==} - axios@1.7.2: resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} @@ -2604,6 +2498,7 @@ packages: boolean@3.2.0: resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -2720,10 +2615,6 @@ packages: chromium-pickle-js@0.2.0: resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} - ci-info@3.7.1: - resolution: {integrity: sha512-4jYS4MOAaCIStSRwiuxc4B8MYhIe676yO1sYGzARnjXkWpmzZMMYxY6zu8WYWDhSuth5zhrQ1rhNSibyyvv4/w==} - engines: {node: '>=8'} - ci-info@3.9.0: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} @@ -2799,10 +2690,6 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - content-type@1.0.4: - resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} - engines: {node: '>= 0.6'} - content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} @@ -2883,9 +2770,6 @@ packages: resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==} engines: {node: '>=8'} - csstype@3.1.1: - resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} - csstype@3.1.2: resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} @@ -2904,15 +2788,6 @@ packages: supports-color: optional: true - debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -2933,9 +2808,6 @@ packages: resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} engines: {node: '>=6'} - deep-equal@2.2.0: - resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2951,10 +2823,6 @@ packages: resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==} engines: {node: '>= 0.4'} - define-properties@1.1.4: - resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} - engines: {node: '>= 0.4'} - define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} @@ -3009,9 +2877,6 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} - dom-accessibility-api@0.5.15: - resolution: {integrity: sha512-8o+oVqLQZoruQPYy3uAAQtc6YbtSiRq5aPJBhJ82YTJRHvI6ofhYAkC81WmjFTnfUbqg6T3aCglIpU9p/5e7Cw==} - dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} @@ -3026,10 +2891,6 @@ packages: dotenv-expand@5.1.0: resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - dotenv@16.0.3: - resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} - engines: {node: '>=12'} - dotenv@16.3.1: resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} engines: {node: '>=12'} @@ -3096,9 +2957,6 @@ packages: resolution: {integrity: sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==} engines: {node: '>= 0.4'} - es-get-iterator@1.1.3: - resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} - es-module-lexer@1.5.4: resolution: {integrity: sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==} @@ -3229,16 +3087,6 @@ packages: resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - eslint-utils@3.0.0: - resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} - engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} - peerDependencies: - eslint: '>=5' - - eslint-visitor-keys@2.1.0: - resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} - engines: {node: '>=10'} - eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -3246,6 +3094,7 @@ packages: eslint@8.56.0: resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. hasBin: true espree@9.6.1: @@ -3377,15 +3226,6 @@ packages: resolution: {integrity: sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==} engines: {node: '>=10'} - follow-redirects@1.15.2: - resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - follow-redirects@1.15.6: resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} engines: {node: '>=4.0'} @@ -3490,9 +3330,6 @@ packages: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} - get-intrinsic@1.1.3: - resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==} - get-intrinsic@1.2.2: resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==} @@ -3696,6 +3533,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -3715,10 +3553,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - is-arguments@1.1.1: - resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} - engines: {node: '>= 0.4'} - is-array-buffer@3.0.1: resolution: {integrity: sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==} @@ -3766,9 +3600,6 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} - is-map@2.0.2: - resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==} - is-negative-zero@2.0.2: resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} engines: {node: '>= 0.4'} @@ -3792,9 +3623,6 @@ packages: resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} engines: {node: '>= 0.4'} - is-set@2.0.2: - resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==} - is-shared-array-buffer@1.0.2: resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} @@ -3814,21 +3642,12 @@ packages: resolution: {integrity: sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==} engines: {node: '>= 0.4'} - is-weakmap@2.0.1: - resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==} - is-weakref@1.0.2: resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} - is-weakset@2.0.2: - resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==} - isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: - resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} - isbinaryfile@4.0.10: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} @@ -4010,10 +3829,6 @@ packages: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} - lz-string@1.4.4: - resolution: {integrity: sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ==} - hasBin: true - lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true @@ -4093,17 +3908,10 @@ packages: resolution: {integrity: sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==} engines: {node: '>=10'} - minimatch@9.0.3: - resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} - engines: {node: '>=16 || 14 >=14.17'} - minimatch@9.0.4: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.7: - resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -4139,9 +3947,6 @@ packages: ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -4210,10 +4015,6 @@ packages: object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} - object-is@1.1.5: - resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} - engines: {node: '>= 0.4'} - object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} @@ -4327,9 +4128,6 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - picocolors@1.0.0: - resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} - picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} @@ -4376,10 +4174,6 @@ packages: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} - pretty-format@29.3.1: - resolution: {integrity: sha512-FyLnmb1cYJV8biEIiRyzRFvs2lry7PPIvOqKVe1GCUEYg4YGmlx1qG9EJNMxArYm7piII4qb8UV1Pncq5dxmcg==} - engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - pretty-format@29.7.0: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -4550,9 +4344,6 @@ packages: resolution: {integrity: sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==} engines: {node: '>=12.0.0'} - readable-stream@2.3.7: - resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} - readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} @@ -4575,9 +4366,6 @@ packages: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} - regenerator-runtime@0.13.11: - resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} - regenerator-runtime@0.14.1: resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} @@ -4627,6 +4415,7 @@ packages: rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true roarr@2.15.4: @@ -4677,19 +4466,10 @@ packages: semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - semver@6.3.0: - resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==} - hasBin: true - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.5.4: - resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} - engines: {node: '>=10'} - hasBin: true - semver@7.6.2: resolution: {integrity: sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==} engines: {node: '>=10'} @@ -4803,10 +4583,6 @@ packages: resolution: {integrity: sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==} engines: {node: '>=18'} - stop-iteration-iterator@1.0.0: - resolution: {integrity: sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==} - engines: {node: '>= 0.4'} - streamsearch@1.1.0: resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} engines: {node: '>=10.0.0'} @@ -4972,9 +4748,6 @@ packages: tslib@2.4.0: resolution: {integrity: sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==} - tslib@2.5.0: - resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} - tslib@2.6.2: resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} @@ -5270,9 +5043,6 @@ packages: which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} - which-collection@1.0.1: - resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==} - which-typed-array@1.1.9: resolution: {integrity: sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==} engines: {node: '>= 0.4'} @@ -5399,15 +5169,6 @@ snapshots: '@jridgewell/gen-mapping': 0.3.3 '@jridgewell/trace-mapping': 0.3.20 - '@babel/code-frame@7.18.6': - dependencies: - '@babel/highlight': 7.23.4 - - '@babel/code-frame@7.23.5': - dependencies: - '@babel/highlight': 7.23.4 - chalk: 2.4.2 - '@babel/code-frame@7.24.2': dependencies: '@babel/highlight': 7.24.5 @@ -5418,7 +5179,7 @@ snapshots: '@babel/core@7.23.6': dependencies: '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.2 '@babel/generator': 7.23.6 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.6) @@ -5428,7 +5189,7 @@ snapshots: '@babel/traverse': 7.23.6 '@babel/types': 7.23.6 convert-source-map: 2.0.0 - debug: 4.3.4 + debug: 4.3.7 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -5461,10 +5222,6 @@ snapshots: dependencies: '@babel/types': 7.23.6 - '@babel/helper-module-imports@7.18.6': - dependencies: - '@babel/types': 7.23.6 - '@babel/helper-module-imports@7.22.15': dependencies: '@babel/types': 7.23.6 @@ -5476,7 +5233,7 @@ snapshots: '@babel/helper-module-imports': 7.22.15 '@babel/helper-simple-access': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-validator-identifier': 7.24.5 '@babel/helper-plugin-utils@7.22.5': {} @@ -5490,8 +5247,6 @@ snapshots: '@babel/helper-string-parser@7.23.4': {} - '@babel/helper-validator-identifier@7.22.20': {} - '@babel/helper-validator-identifier@7.24.5': {} '@babel/helper-validator-option@7.23.5': {} @@ -5504,12 +5259,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/highlight@7.23.4': - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 - '@babel/highlight@7.24.5': dependencies: '@babel/helper-validator-identifier': 7.24.5 @@ -5531,31 +5280,19 @@ snapshots: '@babel/core': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 - '@babel/runtime@7.20.7': - dependencies: - regenerator-runtime: 0.13.11 - - '@babel/runtime@7.21.0': - dependencies: - regenerator-runtime: 0.13.11 - - '@babel/runtime@7.22.5': - dependencies: - regenerator-runtime: 0.13.11 - '@babel/runtime@7.24.5': dependencies: regenerator-runtime: 0.14.1 '@babel/template@7.22.15': dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.2 '@babel/parser': 7.23.6 '@babel/types': 7.23.6 '@babel/traverse@7.23.6': dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.2 '@babel/generator': 7.23.6 '@babel/helper-environment-visitor': 7.22.20 '@babel/helper-function-name': 7.23.0 @@ -5563,7 +5300,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.6 '@babel/types': 7.23.6 - debug: 4.3.4 + debug: 4.3.7 globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -5571,7 +5308,7 @@ snapshots: '@babel/types@7.23.6': dependencies: '@babel/helper-string-parser': 7.23.4 - '@babel/helper-validator-identifier': 7.22.20 + '@babel/helper-validator-identifier': 7.24.5 to-fast-properties: 2.0.0 '@chakra-ui/accordion@2.2.0(@chakra-ui/system@2.5.8(@emotion/react@11.10.6(@types/react@18.0.26)(react@18.3.1))(@emotion/styled@11.10.6(@emotion/react@11.10.6(@types/react@18.0.26)(react@18.3.1))(@types/react@18.0.26)(react@18.3.1))(react@18.3.1))(framer-motion@10.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': @@ -6307,7 +6044,7 @@ snapshots: '@electron/get@2.0.3': dependencies: - debug: 4.3.4 + debug: 4.3.7 env-paths: 2.2.1 fs-extra: 8.1.0 got: 11.8.6 @@ -6321,7 +6058,7 @@ snapshots: '@electron/notarize@2.2.1': dependencies: - debug: 4.3.4 + debug: 4.3.7 fs-extra: 9.1.0 promise-retry: 2.0.1 transitivePeerDependencies: @@ -6330,7 +6067,7 @@ snapshots: '@electron/osx-sign@1.0.5': dependencies: compare-version: 0.1.2 - debug: 4.3.4 + debug: 4.3.7 fs-extra: 10.1.0 isbinaryfile: 4.0.10 minimist: 1.2.8 @@ -6342,7 +6079,7 @@ snapshots: dependencies: '@electron/asar': 3.2.8 '@malept/cross-spawn-promise': 1.1.1 - debug: 4.3.4 + debug: 4.3.7 dir-compare: 3.3.0 fs-extra: 9.1.0 minimatch: 3.1.2 @@ -6352,8 +6089,8 @@ snapshots: '@emotion/babel-plugin@11.10.6': dependencies: - '@babel/helper-module-imports': 7.18.6 - '@babel/runtime': 7.21.0 + '@babel/helper-module-imports': 7.22.15 + '@babel/runtime': 7.24.5 '@emotion/hash': 0.9.0 '@emotion/memoize': 0.8.0 '@emotion/serialize': 1.1.1 @@ -6390,7 +6127,7 @@ snapshots: '@emotion/react@11.10.6(@types/react@18.0.26)(react@18.3.1)': dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.24.5 '@emotion/babel-plugin': 11.10.6 '@emotion/cache': 11.10.5 '@emotion/serialize': 1.1.1 @@ -6408,13 +6145,13 @@ snapshots: '@emotion/memoize': 0.8.0 '@emotion/unitless': 0.8.0 '@emotion/utils': 1.2.0 - csstype: 3.1.1 + csstype: 3.1.2 '@emotion/sheet@1.2.1': {} '@emotion/styled@11.10.6(@emotion/react@11.10.6(@types/react@18.0.26)(react@18.3.1))(@types/react@18.0.26)(react@18.3.1)': dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.24.5 '@emotion/babel-plugin': 11.10.6 '@emotion/is-prop-valid': 1.2.0 '@emotion/react': 11.10.6(@types/react@18.0.26)(react@18.3.1) @@ -6655,7 +6392,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.7 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -6686,7 +6423,7 @@ snapshots: '@humanwhocodes/config-array@0.11.13': dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4 + debug: 4.3.7 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -6715,21 +6452,19 @@ snapshots: '@jridgewell/gen-mapping@0.3.3': dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@jridgewell/trace-mapping': 0.3.20 '@jridgewell/resolve-uri@3.1.1': {} '@jridgewell/set-array@1.1.2': {} - '@jridgewell/sourcemap-codec@1.4.15': {} - '@jridgewell/sourcemap-codec@1.5.0': {} '@jridgewell/trace-mapping@0.3.20': dependencies: '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 '@malept/cross-spawn-promise@1.1.1': dependencies: @@ -6737,7 +6472,7 @@ snapshots: '@malept/flatpak-bundler@0.4.0': dependencies: - debug: 4.3.4 + debug: 4.3.7 fs-extra: 9.1.0 lodash: 4.17.21 tmp-promise: 3.0.3 @@ -6830,43 +6565,33 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.17.2': optional: true - '@sentry-internal/browser-utils@8.19.0': + '@sentry-internal/browser-utils@8.45.0': dependencies: - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core': 8.45.0 - '@sentry-internal/feedback@8.19.0': + '@sentry-internal/feedback@8.45.0': dependencies: - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core': 8.45.0 - '@sentry-internal/replay-canvas@8.19.0': + '@sentry-internal/replay-canvas@8.45.0': dependencies: - '@sentry-internal/replay': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry-internal/replay': 8.45.0 + '@sentry/core': 8.45.0 - '@sentry-internal/replay@8.19.0': + '@sentry-internal/replay@8.45.0': dependencies: - '@sentry-internal/browser-utils': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry-internal/browser-utils': 8.45.0 + '@sentry/core': 8.45.0 '@sentry/babel-plugin-component-annotate@2.16.1': {} - '@sentry/browser@8.19.0': + '@sentry/browser@8.45.0': dependencies: - '@sentry-internal/browser-utils': 8.19.0 - '@sentry-internal/feedback': 8.19.0 - '@sentry-internal/replay': 8.19.0 - '@sentry-internal/replay-canvas': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry-internal/browser-utils': 8.45.0 + '@sentry-internal/feedback': 8.45.0 + '@sentry-internal/replay': 8.45.0 + '@sentry-internal/replay-canvas': 8.45.0 + '@sentry/core': 8.45.0 '@sentry/bundler-plugin-core@2.16.1': dependencies: @@ -6922,26 +6647,15 @@ snapshots: - encoding - supports-color - '@sentry/core@8.19.0': - dependencies: - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/core@8.45.0': {} - '@sentry/react@8.19.0(react@18.3.1)': + '@sentry/react@8.45.0(react@18.3.1)': dependencies: - '@sentry/browser': 8.19.0 - '@sentry/core': 8.19.0 - '@sentry/types': 8.19.0 - '@sentry/utils': 8.19.0 + '@sentry/browser': 8.45.0 + '@sentry/core': 8.45.0 hoist-non-react-statics: 3.3.2 react: 18.3.1 - '@sentry/types@8.19.0': {} - - '@sentry/utils@8.19.0': - dependencies: - '@sentry/types': 8.19.0 - '@sentry/vite-plugin@2.16.1': dependencies: '@sentry/bundler-plugin-core': 2.16.1 @@ -7050,28 +6764,28 @@ snapshots: - supports-color - typescript - '@tanstack/query-core@5.17.9': {} + '@tanstack/query-core@5.62.7': {} - '@tanstack/query-devtools@5.17.7': {} + '@tanstack/query-devtools@5.61.4': {} - '@tanstack/react-query-devtools@5.17.9(@tanstack/react-query@5.17.9(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-devtools@5.62.7(@tanstack/react-query@5.62.7(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/query-devtools': 5.17.7 - '@tanstack/react-query': 5.17.9(react@18.3.1) + '@tanstack/query-devtools': 5.61.4 + '@tanstack/react-query': 5.62.7(react@18.3.1) react: 18.3.1 - '@tanstack/react-query@5.17.9(react@18.3.1)': + '@tanstack/react-query@5.62.7(react@18.3.1)': dependencies: - '@tanstack/query-core': 5.17.9 + '@tanstack/query-core': 5.62.7 react: 18.3.1 - '@tanstack/react-table@8.11.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@tanstack/react-table@8.20.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/table-core': 8.11.3 + '@tanstack/table-core': 8.20.5 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@tanstack/table-core@8.11.3': {} + '@tanstack/table-core@8.20.5': {} '@testing-library/dom@10.1.0': dependencies: @@ -7086,30 +6800,30 @@ snapshots: '@testing-library/dom@8.19.1': dependencies: - '@babel/code-frame': 7.18.6 - '@babel/runtime': 7.20.7 - '@types/aria-query': 5.0.1 - aria-query: 5.1.3 + '@babel/code-frame': 7.24.2 + '@babel/runtime': 7.24.5 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 chalk: 4.1.2 - dom-accessibility-api: 0.5.15 - lz-string: 1.4.4 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 pretty-format: 27.5.1 '@testing-library/jest-dom@5.16.5': dependencies: '@adobe/css-tools': 4.0.2 - '@babel/runtime': 7.20.7 + '@babel/runtime': 7.24.5 '@types/testing-library__jest-dom': 5.14.5 - aria-query: 5.1.3 + aria-query: 5.3.0 chalk: 3.0.0 css.escape: 1.5.1 - dom-accessibility-api: 0.5.15 + dom-accessibility-api: 0.5.16 lodash: 4.17.21 redent: 3.0.0 '@testing-library/react@13.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@babel/runtime': 7.20.7 + '@babel/runtime': 7.24.5 '@testing-library/dom': 8.19.1 '@types/react-dom': 18.0.10 react: 18.3.1 @@ -7121,8 +6835,6 @@ snapshots: '@tootallnate/once@2.0.0': {} - '@types/aria-query@5.0.1': {} - '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -7214,7 +6926,7 @@ snapshots: '@types/jest@29.2.5': dependencies: expect: 29.3.1 - pretty-format: 29.3.1 + pretty-format: 29.7.0 '@types/json-schema@7.0.15': {} @@ -7266,7 +6978,7 @@ snapshots: dependencies: '@types/prop-types': 15.7.5 '@types/scheduler': 0.16.2 - csstype: 3.1.1 + csstype: 3.1.2 '@types/responselike@1.0.3': dependencies: @@ -7333,28 +7045,18 @@ snapshots: '@typescript-eslint/types': 7.16.1 '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) '@typescript-eslint/visitor-keys': 7.16.1 - debug: 4.3.4 + debug: 4.3.7 eslint: 8.56.0 optionalDependencies: typescript: 5.5.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@5.48.1': - dependencies: - '@typescript-eslint/types': 5.48.1 - '@typescript-eslint/visitor-keys': 5.48.1 - '@typescript-eslint/scope-manager@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - '@typescript-eslint/scope-manager@6.21.0': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - '@typescript-eslint/scope-manager@7.16.1': dependencies: '@typescript-eslint/types': 7.16.1 @@ -7364,7 +7066,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.16.1(typescript@5.5.3) '@typescript-eslint/utils': 7.16.1(eslint@8.56.0)(typescript@5.5.3) - debug: 4.3.4 + debug: 4.3.7 eslint: 8.56.0 ts-api-utils: 1.3.0(typescript@5.5.3) optionalDependencies: @@ -7372,52 +7074,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@5.48.1': {} - '@typescript-eslint/types@5.62.0': {} - '@typescript-eslint/types@6.21.0': {} - '@typescript-eslint/types@7.16.1': {} - '@typescript-eslint/typescript-estree@5.48.1(typescript@5.5.3)': - dependencies: - '@typescript-eslint/types': 5.48.1 - '@typescript-eslint/visitor-keys': 5.48.1 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - semver: 7.5.4 - tsutils: 3.21.0(typescript@5.5.3) - optionalDependencies: - typescript: 5.5.3 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.3)': dependencies: '@typescript-eslint/types': 5.62.0 '@typescript-eslint/visitor-keys': 5.62.0 - debug: 4.3.4 + debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 - tsutils: 3.21.0(typescript@5.5.3) - optionalDependencies: - typescript: 5.5.3 - transitivePeerDependencies: - - supports-color - - '@typescript-eslint/typescript-estree@6.21.0(typescript@5.5.3)': - dependencies: - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.4 - globby: 11.1.0 - is-glob: 4.0.3 - minimatch: 9.0.3 semver: 7.6.2 - ts-api-utils: 1.3.0(typescript@5.5.3) + tsutils: 3.21.0(typescript@5.5.3) optionalDependencies: typescript: 5.5.3 transitivePeerDependencies: @@ -7427,7 +7096,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.16.1 '@typescript-eslint/visitor-keys': 7.16.1 - debug: 4.3.4 + debug: 4.3.7 globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 @@ -7438,21 +7107,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@5.48.1(eslint@8.56.0)(typescript@5.5.3)': - dependencies: - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.5 - '@typescript-eslint/scope-manager': 5.48.1 - '@typescript-eslint/types': 5.48.1 - '@typescript-eslint/typescript-estree': 5.48.1(typescript@5.5.3) - eslint: 8.56.0 - eslint-scope: 5.1.1 - eslint-utils: 3.0.0(eslint@8.56.0) - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - - typescript - '@typescript-eslint/utils@5.62.0(eslint@8.56.0)(typescript@5.5.3)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) @@ -7463,20 +7117,6 @@ snapshots: '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.3) eslint: 8.56.0 eslint-scope: 5.1.1 - semver: 7.5.4 - transitivePeerDependencies: - - supports-color - - typescript - - '@typescript-eslint/utils@6.21.0(eslint@8.56.0)(typescript@5.5.3)': - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0) - '@types/json-schema': 7.0.15 - '@types/semver': 7.5.5 - '@typescript-eslint/scope-manager': 6.21.0 - '@typescript-eslint/types': 6.21.0 - '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.3) - eslint: 8.56.0 semver: 7.6.2 transitivePeerDependencies: - supports-color @@ -7493,21 +7133,11 @@ snapshots: - supports-color - typescript - '@typescript-eslint/visitor-keys@5.48.1': - dependencies: - '@typescript-eslint/types': 5.48.1 - eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@5.62.0': dependencies: '@typescript-eslint/types': 5.62.0 eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@6.21.0': - dependencies: - '@typescript-eslint/types': 6.21.0 - eslint-visitor-keys: 3.4.3 - '@typescript-eslint/visitor-keys@7.16.1': dependencies: '@typescript-eslint/types': 7.16.1 @@ -7596,13 +7226,13 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4 + debug: 4.3.7 transitivePeerDependencies: - supports-color agent-base@7.1.0: dependencies: - debug: 4.3.4 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -7649,7 +7279,7 @@ snapshots: builder-util: 24.13.1 builder-util-runtime: 9.2.4 chromium-pickle-js: 0.2.0 - debug: 4.3.4 + debug: 4.3.7 dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) ejs: 3.1.9 electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3) @@ -7664,7 +7294,7 @@ snapshots: minimatch: 5.1.6 read-config-file: 6.3.2 sanitize-filename: 1.6.3 - semver: 7.5.4 + semver: 7.6.2 tar: 6.2.0 temp-file: 3.4.0 transitivePeerDependencies: @@ -7714,10 +7344,6 @@ snapshots: dependencies: tslib: 2.6.2 - aria-query@5.1.3: - dependencies: - deep-equal: 2.2.0 - aria-query@5.3.0: dependencies: dequal: 2.0.3 @@ -7727,9 +7353,9 @@ snapshots: array-includes@3.1.6: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.2 is-string: 1.0.7 array-union@2.1.0: {} @@ -7737,17 +7363,17 @@ snapshots: array.prototype.flatmap@1.3.1: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 array.prototype.tosorted@1.1.1: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 es-shim-unscopables: 1.0.0 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.2 assert-plus@1.0.0: optional: true @@ -7769,14 +7395,6 @@ snapshots: available-typed-arrays@1.0.5: {} - axios@1.2.2: - dependencies: - follow-redirects: 1.15.2 - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - axios@1.7.2: dependencies: follow-redirects: 1.15.6 @@ -7787,7 +7405,7 @@ snapshots: babel-plugin-macros@3.1.0: dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.24.5 cosmiconfig: 7.1.0 resolve: 1.22.1 @@ -7868,7 +7486,7 @@ snapshots: builder-util-runtime@9.2.4: dependencies: - debug: 4.3.4 + debug: 4.3.7 sax: 1.3.0 transitivePeerDependencies: - supports-color @@ -7882,7 +7500,7 @@ snapshots: builder-util-runtime: 9.2.4 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.7 fs-extra: 10.1.0 http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 @@ -7984,8 +7602,6 @@ snapshots: chromium-pickle-js@0.2.0: {} - ci-info@3.7.1: {} - ci-info@3.9.0: {} cli-truncate@2.1.0: @@ -8053,7 +7669,7 @@ snapshots: dependencies: buffer-from: 1.1.2 inherits: 2.0.4 - readable-stream: 2.3.7 + readable-stream: 2.3.8 typedarray: 0.0.6 config-file-ts@0.2.4: @@ -8065,8 +7681,6 @@ snapshots: dependencies: safe-buffer: 5.2.1 - content-type@1.0.4: {} - content-type@1.0.5: {} convert-source-map@1.9.0: {} @@ -8144,8 +7758,6 @@ snapshots: dependencies: cssom: 0.3.8 - csstype@3.1.1: {} - csstype@3.1.2: {} csv-stringify@6.4.5: {} @@ -8160,10 +7772,6 @@ snapshots: dependencies: ms: 2.0.0 - debug@4.3.4: - dependencies: - ms: 2.1.2 - debug@4.3.7: dependencies: ms: 2.1.3 @@ -8176,26 +7784,6 @@ snapshots: deep-eql@5.0.2: {} - deep-equal@2.2.0: - dependencies: - call-bind: 1.0.2 - es-get-iterator: 1.1.3 - get-intrinsic: 1.2.2 - is-arguments: 1.1.1 - is-array-buffer: 3.0.1 - is-date-object: 1.0.5 - is-regex: 1.1.4 - is-shared-array-buffer: 1.0.2 - isarray: 2.0.5 - object-is: 1.1.5 - object-keys: 1.1.1 - object.assign: 4.1.4 - regexp.prototype.flags: 1.4.3 - side-channel: 1.0.4 - which-boxed-primitive: 1.0.2 - which-collection: 1.0.1 - which-typed-array: 1.1.9 - deep-is@0.1.4: {} deepmerge-ts@7.0.3: {} @@ -8208,11 +7796,6 @@ snapshots: gopd: 1.0.1 has-property-descriptors: 1.0.1 - define-properties@1.1.4: - dependencies: - has-property-descriptors: 1.0.1 - object-keys: 1.1.1 - define-properties@1.2.1: dependencies: define-data-property: 1.1.1 @@ -8277,8 +7860,6 @@ snapshots: dependencies: esutils: 2.0.3 - dom-accessibility-api@0.5.15: {} - dom-accessibility-api@0.5.16: {} domexception@4.0.0: @@ -8292,8 +7873,6 @@ snapshots: dotenv-expand@5.1.0: {} - dotenv@16.0.3: {} - dotenv@16.3.1: {} dotenv@9.0.2: {} @@ -8411,18 +7990,6 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.9 - es-get-iterator@1.1.3: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.2 - has-symbols: 1.0.3 - is-arguments: 1.1.1 - is-map: 2.0.2 - is-set: 2.0.2 - is-string: 1.0.7 - isarray: 2.0.5 - stop-iteration-iterator: 1.0.0 - es-module-lexer@1.5.4: {} es-set-tostringtag@2.0.1: @@ -8548,7 +8115,7 @@ snapshots: eslint-plugin-jest@28.6.0(@typescript-eslint/eslint-plugin@7.16.1(@typescript-eslint/parser@7.16.1(eslint@8.56.0)(typescript@5.5.3))(eslint@8.56.0)(typescript@5.5.3))(eslint@8.56.0)(typescript@5.5.3): dependencies: - '@typescript-eslint/utils': 6.21.0(eslint@8.56.0)(typescript@5.5.3) + '@typescript-eslint/utils': 7.16.1(eslint@8.56.0)(typescript@5.5.3) eslint: 8.56.0 optionalDependencies: '@typescript-eslint/eslint-plugin': 7.16.1(@typescript-eslint/parser@7.16.1(eslint@8.56.0)(typescript@5.5.3))(eslint@8.56.0)(typescript@5.5.3) @@ -8592,7 +8159,7 @@ snapshots: object.values: 1.1.6 prop-types: 15.8.1 resolve: 2.0.0-next.4 - semver: 6.3.0 + semver: 6.3.1 string.prototype.matchall: 4.0.8 eslint-plugin-simple-import-sort@8.0.0(eslint@8.56.0): @@ -8601,7 +8168,7 @@ snapshots: eslint-plugin-testing-library@5.9.1(eslint@8.56.0)(typescript@5.5.3): dependencies: - '@typescript-eslint/utils': 5.48.1(eslint@8.56.0)(typescript@5.5.3) + '@typescript-eslint/utils': 5.62.0(eslint@8.56.0)(typescript@5.5.3) eslint: 8.56.0 transitivePeerDependencies: - supports-color @@ -8617,13 +8184,6 @@ snapshots: esrecurse: 4.3.0 estraverse: 5.3.0 - eslint-utils@3.0.0(eslint@8.56.0): - dependencies: - eslint: 8.56.0 - eslint-visitor-keys: 2.1.0 - - eslint-visitor-keys@2.1.0: {} - eslint-visitor-keys@3.4.3: {} eslint@8.56.0: @@ -8639,7 +8199,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.7 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -8726,7 +8286,7 @@ snapshots: array-flatten: 1.1.1 body-parser: 1.20.1 content-disposition: 0.5.4 - content-type: 1.0.4 + content-type: 1.0.5 cookie: 0.5.0 cookie-signature: 1.0.6 debug: 2.6.9 @@ -8760,7 +8320,7 @@ snapshots: extract-zip@2.0.1: dependencies: - debug: 4.3.4 + debug: 4.3.7 get-stream: 5.2.0 yauzl: 2.10.0 optionalDependencies: @@ -8840,8 +8400,6 @@ snapshots: dependencies: tslib: 2.6.2 - follow-redirects@1.15.2: {} - follow-redirects@1.15.6: {} for-each@0.3.3: @@ -8862,7 +8420,7 @@ snapshots: framer-motion@10.11.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - tslib: 2.5.0 + tslib: 2.6.2 optionalDependencies: '@emotion/is-prop-valid': 0.8.8 react: 18.3.1 @@ -8940,12 +8498,6 @@ snapshots: get-caller-file@2.0.5: {} - get-intrinsic@1.1.3: - dependencies: - function-bind: 1.1.2 - has: 1.0.3 - has-symbols: 1.0.3 - get-intrinsic@1.2.2: dependencies: function-bind: 1.1.2 @@ -9147,7 +8699,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -9164,14 +8716,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.3.7 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -9225,11 +8777,6 @@ snapshots: ipaddr.js@1.9.1: {} - is-arguments@1.1.1: - dependencies: - call-bind: 1.0.2 - has-tostringtag: 1.0.0 - is-array-buffer@3.0.1: dependencies: call-bind: 1.0.2 @@ -9275,8 +8822,6 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-map@2.0.2: {} - is-negative-zero@2.0.2: {} is-number-object@1.0.7: @@ -9294,8 +8839,6 @@ snapshots: call-bind: 1.0.2 has-tostringtag: 1.0.0 - is-set@2.0.2: {} - is-shared-array-buffer@1.0.2: dependencies: call-bind: 1.0.2 @@ -9318,21 +8861,12 @@ snapshots: gopd: 1.0.1 has-tostringtag: 1.0.0 - is-weakmap@2.0.1: {} - is-weakref@1.0.2: dependencies: call-bind: 1.0.2 - is-weakset@2.0.2: - dependencies: - call-bind: 1.0.2 - get-intrinsic: 1.2.2 - isarray@1.0.0: {} - isarray@2.0.5: {} - isbinaryfile@4.0.10: {} isbinaryfile@5.0.0: {} @@ -9364,7 +8898,7 @@ snapshots: jest-message-util@29.3.1: dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.2 '@jest/types': 29.3.1 '@types/stack-utils': 2.0.1 chalk: 4.1.2 @@ -9379,7 +8913,7 @@ snapshots: '@jest/types': 29.3.1 '@types/node': 20.14.10 chalk: 4.1.2 - ci-info: 3.7.1 + ci-info: 3.9.0 graceful-fs: 4.2.11 picomatch: 2.3.1 @@ -9545,8 +9079,6 @@ snapshots: dependencies: yallist: 4.0.0 - lz-string@1.4.4: {} - lz-string@1.5.0: {} magic-string@0.30.14: @@ -9555,7 +9087,7 @@ snapshots: magic-string@0.30.8: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 matcher@3.0.0: dependencies: @@ -9605,16 +9137,10 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimatch@9.0.3: - dependencies: - brace-expansion: 2.0.1 - minimatch@9.0.4: dependencies: brace-expansion: 2.0.1 - minimist@1.2.7: {} - minimist@1.2.8: {} minipass@3.3.6: @@ -9640,8 +9166,6 @@ snapshots: ms@2.0.0: {} - ms@2.1.2: {} - ms@2.1.3: {} multer@1.4.5-lts.1: @@ -9692,11 +9216,6 @@ snapshots: object-inspect@1.12.3: {} - object-is@1.1.5: - dependencies: - call-bind: 1.0.2 - define-properties: 1.2.1 - object-keys@1.1.1: {} object.assign@4.1.4: @@ -9709,24 +9228,24 @@ snapshots: object.entries@1.1.6: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 object.fromentries@2.0.6: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 object.hasown@1.1.2: dependencies: - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 object.values@1.1.6: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 on-finished@2.4.1: @@ -9779,7 +9298,7 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.23.5 + '@babel/code-frame': 7.24.2 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 @@ -9813,8 +9332,6 @@ snapshots: pend@1.2.0: {} - picocolors@1.0.0: {} - picocolors@1.0.1: {} picomatch@2.3.1: {} @@ -9836,7 +9353,7 @@ snapshots: postcss@8.4.38: dependencies: nanoid: 3.3.7 - picocolors: 1.0.0 + picocolors: 1.0.1 source-map-js: 1.2.0 prelude-ls@1.1.2: {} @@ -9855,12 +9372,6 @@ snapshots: ansi-styles: 5.2.0 react-is: 17.0.2 - pretty-format@29.3.1: - dependencies: - '@jest/schemas': 29.6.3 - ansi-styles: 5.2.0 - react-is: 18.2.0 - pretty-format@29.7.0: dependencies: '@jest/schemas': 29.6.3 @@ -9941,7 +9452,7 @@ snapshots: react-focus-lock@2.9.4(@types/react@18.0.26)(react@18.3.1): dependencies: - '@babel/runtime': 7.22.5 + '@babel/runtime': 7.24.5 focus-lock: 0.11.6 prop-types: 15.8.1 react: 18.3.1 @@ -10022,16 +9533,6 @@ snapshots: json5: 2.2.3 lazy-val: 1.0.5 - readable-stream@2.3.7: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 @@ -10065,8 +9566,6 @@ snapshots: indent-string: 4.0.0 strip-indent: 3.0.0 - regenerator-runtime@0.13.11: {} - regenerator-runtime@0.14.1: {} regexp.prototype.flags@1.4.3: @@ -10188,14 +9687,8 @@ snapshots: semver-compare@1.0.0: optional: true - semver@6.3.0: {} - semver@6.3.1: {} - semver@7.5.4: - dependencies: - lru-cache: 6.0.0 - semver@7.6.2: {} send@0.18.0: @@ -10251,7 +9744,7 @@ snapshots: shx@0.3.4: dependencies: - minimist: 1.2.7 + minimist: 1.2.8 shelljs: 0.8.5 side-channel@1.0.4: @@ -10268,7 +9761,7 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.5.4 + semver: 7.6.2 slash@3.0.0: {} @@ -10319,10 +9812,6 @@ snapshots: steno@4.0.2: {} - stop-iteration-iterator@1.0.0: - dependencies: - internal-slot: 1.0.4 - streamsearch@1.1.0: {} string-width@4.2.3: @@ -10334,9 +9823,9 @@ snapshots: string.prototype.matchall@4.0.8: dependencies: call-bind: 1.0.2 - define-properties: 1.1.4 + define-properties: 1.2.1 es-abstract: 1.21.1 - get-intrinsic: 1.1.3 + get-intrinsic: 1.2.2 has-symbols: 1.0.3 internal-slot: 1.0.4 regexp.prototype.flags: 1.4.3 @@ -10372,7 +9861,7 @@ snapshots: sumchecker@3.0.1: dependencies: - debug: 4.3.4 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -10484,8 +9973,6 @@ snapshots: tslib@2.4.0: {} - tslib@2.5.0: {} - tslib@2.6.2: {} tsutils@3.21.0(typescript@5.5.3): @@ -10585,7 +10072,7 @@ snapshots: dependencies: browserslist: 4.22.2 escalade: 3.1.1 - picocolors: 1.0.0 + picocolors: 1.0.1 uri-js@4.4.1: dependencies: @@ -10674,7 +10161,7 @@ snapshots: vite-tsconfig-paths@4.3.1(typescript@5.5.3)(vite@5.2.11(@types/node@20.14.10)(sass@1.57.1)): dependencies: - debug: 4.3.4 + debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.0.2(typescript@5.5.3) optionalDependencies: @@ -10776,13 +10263,6 @@ snapshots: is-string: 1.0.7 is-symbol: 1.0.4 - which-collection@1.0.1: - dependencies: - is-map: 2.0.2 - is-set: 2.0.2 - is-weakmap: 2.0.1 - is-weakset: 2.0.2 - which-typed-array@1.1.9: dependencies: available-typed-arrays: 1.0.5