diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 2c19cae2d..6cf8f2165 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -20,6 +20,12 @@ From the project root, run the following commands - __Install the project dependencies__ by running `pnpm i` - __Run dev mode__ by running `turbo dev` +### Debugging backend +To debug backend code in Node.js: +- Open two separate terminals and navigate to the `apps/client` and `apps/server` directories. +- In each terminal, run the command `pnpm dev` to start the development servers for both the client and server applications. +- If you need to set breakpoints and inspect the code execution, enable Node.js inspect mode by running `pnpm dev:inspect`. + ## TESTING Generally we have 2 types of tests. diff --git a/apps/client/.stylelintrc b/apps/client/.stylelintrc deleted file mode 100644 index 5a9002904..000000000 --- a/apps/client/.stylelintrc +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": [ - "stylelint-config-standard-scss", - "stylelint-config-prettier" - ] -} diff --git a/apps/client/package.json b/apps/client/package.json index 29e25934e..d71fd4ab9 100644 --- a/apps/client/package.json +++ b/apps/client/package.json @@ -3,7 +3,7 @@ "version": "2.0.9", "private": true, "dependencies": { - "@chakra-ui/react": "^2.5.5", + "@chakra-ui/react": "^2.7.0", "@dnd-kit/core": "^6.0.8", "@dnd-kit/sortable": "^7.0.2", "@dnd-kit/utilities": "^3.2.1", @@ -14,7 +14,8 @@ "@sentry/tracing": "^7.46.0", "@tanstack/react-query": "^4.28.0", "@tanstack/react-query-devtools": "^4.29.0", - "autosize": "^5.0.2", + "@tanstack/react-table": "^8.9.2", + "autosize": "^6.0.1", "axios": "^1.2.0", "color": "^4.2.3", "csv-stringify": "^6.2.3", @@ -27,7 +28,6 @@ "react-hook-form": "^7.43.5", "react-qr-code": "^2.0.11", "react-router-dom": "^6.3.0", - "react-table": "^7.7.0", "typeface-open-sans": "^1.1.13", "web-vitals": "^3.1.1", "zustand": "^4.3.6" @@ -40,7 +40,6 @@ "build:local": "cross-env NODE_ENV=local vite build", "build:docker": "vite build", "lint": "eslint .", - "stylelint": "npx stylelint \"**/*.scss\"\n", "test": "vitest", "test:pipeline": "vitest run", "cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf build" @@ -65,7 +64,6 @@ "@testing-library/user-event": "^14.1.1", "@types/color": "^3.0.3", "@types/luxon": "^3.2.0", - "@types/prop-types": "^15.7.5", "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "@types/testing-library__jest-dom": "^5.14.5", @@ -84,11 +82,7 @@ "ontime-types": "workspace:*", "ontime-utils": "workspace:*", "prettier": "^2.8.3", - "prop-types": "^15.8.1", "sass": "^1.57.1", - "stylelint": "^14.16.1", - "stylelint-config-prettier": "^9.0.4", - "stylelint-config-standard-scss": "^6.1.0", "typescript": "^4.9.4", "vite": "^4.3.1", "vite-plugin-compression2": "^0.9.0", diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index a71c3e945..b8e42c73a 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -5,7 +5,7 @@ import useAliases from './common/hooks-query/useAliases'; import withData from './features/viewers/ViewWrapper'; const Editor = lazy(() => import('./features/editors/ProtectedEditor')); -const Table = lazy(() => import('./features/table/ProtectedTable')); +const Cuesheet = lazy(() => import('./features/cuesheet/ProtectedCuesheet')); const TimerView = lazy(() => import('./features/viewers/timer/Timer')); const MinimalTimerView = lazy(() => import('./features/viewers/minimal-timer/MinimalTimer')); @@ -76,9 +76,9 @@ export default function AppRouter() { {/*/!* Protected Routes *!/*/} } /> - } /> - } /> - } /> + } /> + } /> + } /> {/*/!* Protected Routes - Elements *!/*/} { - const { isDark, ...rest } = props; +export const AutoTextArea = (props: TextareaProps) => { const ref = useRef(null); useEffect(() => { @@ -26,8 +21,8 @@ export const AutoTextArea = (props: AutoTextAreaProps) => { resize='none' ref={ref} transition='height none' - variant={isDark ? 'ontime-filled' : 'ontime-filled-on-light'} - {...rest} + variant='ontime-transparent' + {...props} /> ); }; diff --git a/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx b/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx index 461cc62ce..23dfbcf79 100644 --- a/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx +++ b/apps/client/src/common/components/navigation-menu/NavigationMenu.tsx @@ -112,6 +112,11 @@ function NavigationMenu() {
+ + Cuesheet + + +
{navigatorConstants.map((route) => ( undefined, - toggleTheme: () => undefined, - toggleFollow: () => undefined, -}); - -export const TableSettingsProvider = ({ children }) => { - const [theme, setTheme] = useLocalStorage('table-color-theme', 'dark'); - const [followSelected, setFollowSelected] = useLocalStorage('table-follow-selected', false); - const [showSettings, setShowSettings] = useState(false); - - /** - * @description Toggles the current value of dark mode - * @param {string} val - 'light' or 'dark' - */ - const toggleTheme = useCallback( - (val) => { - if (val === undefined) { - setTheme((prev) => (prev === 'light' ? 'dark' : 'light')); - } else { - setTheme(val); - } - }, - [setTheme] - ); - - /** - * @description Toggles visibility state for settings - * @param {boolean} val - whether the settings window is visible - */ - const toggleSettings = useCallback( - (val) => { - if (val === undefined) { - setShowSettings((prev) => !prev); - } else { - setShowSettings(val); - } - }, - [setShowSettings] - ); - - /** - * @description Toggles follow option - * @param {boolean} val - whether the window follows selected event - */ - const toggleFollow = useCallback( - (val) => { - if (val === undefined) { - setFollowSelected((prev) => !prev); - } else { - setFollowSelected(val); - } - }, - [setFollowSelected] - ); - - return ( - - {children} - - ); -}; diff --git a/apps/client/src/common/stores/logger.ts b/apps/client/src/common/stores/logger.ts index 9587ce7c8..fb60d07c4 100644 --- a/apps/client/src/common/stores/logger.ts +++ b/apps/client/src/common/stores/logger.ts @@ -1,5 +1,5 @@ import { useCallback } from 'react'; -import { Log, LogLevel } from 'ontime-types'; +import { Log, LogLevel, LogOrigin } from 'ontime-types'; import { generateId, millisToString } from 'ontime-utils'; import { useStore } from 'zustand'; import { createStore } from 'zustand/vanilla'; @@ -34,7 +34,7 @@ export function useEmitLog() { const _emit = useCallback((text: string, level: LogLevel) => { const log = { id: generateId(), - origin: 'CLIENT', + origin: LogOrigin.Client, time: millisToString(nowInMillis()), level, text, diff --git a/apps/client/src/common/utils/__tests__/dateConfig.test.js b/apps/client/src/common/utils/__tests__/dateConfig.test.js index eba30d62f..34b889021 100644 --- a/apps/client/src/common/utils/__tests__/dateConfig.test.js +++ b/apps/client/src/common/utils/__tests__/dateConfig.test.js @@ -1,4 +1,21 @@ -import { forgivingStringToMillis, millisToDelayString, millisToMinutes, millisToSeconds } from '../dateConfig'; +import { + forgivingStringToMillis, + millisToDelayString, + millisToMinutes, + millisToSeconds, + secondsInMillis, +} from '../dateConfig'; + +describe('test secondsInMillis function', () => { + it('return 0 if value is null', () => { + expect(secondsInMillis(null)).toBe(0); + }); + it('returns the seconds value of a millis date', () => { + const date = 1686255053619; // Thu Jun 08 2023 20:10:53 + const seconds = secondsInMillis(date); + expect(seconds).toBe(53); + }); +}); describe('test millisToSeconds function', () => { it('test with null values', () => { diff --git a/apps/client/src/common/utils/dateConfig.ts b/apps/client/src/common/utils/dateConfig.ts index e001d9836..b976c8664 100644 --- a/apps/client/src/common/utils/dateConfig.ts +++ b/apps/client/src/common/utils/dateConfig.ts @@ -5,6 +5,13 @@ import { mth, mtm, mts } from './timeConstants'; export const timeFormat = 'HH:mm'; export const timeFormatSeconds = 'HH:mm:ss'; +export function secondsInMillis(millis: number | null) { + if (!millis) { + return 0; + } + return Math.floor((millis % mtm) / mts); +} + /** * @description Converts milliseconds to seconds * @param {number | null} millis - time in seconds diff --git a/apps/client/src/common/utils/eventsManager.ts b/apps/client/src/common/utils/eventsManager.ts index 9f8fbbc27..1edcf9172 100644 --- a/apps/client/src/common/utils/eventsManager.ts +++ b/apps/client/src/common/utils/eventsManager.ts @@ -41,7 +41,7 @@ export const getEventsWithDelay = (rundown: OntimeRundownEntry[]): OntimeEvent[] * @param {number} limit - max number of events to return * @returns {Object[]} Event list with maximum objects */ -export const trimRundown = (rundown: OntimeRundownEntry[], selectedId: string, limit: number) => { +export const trimRundown = (rundown: OntimeEvent[], selectedId: string, limit: number): OntimeEvent[] => { if (rundown == null) return []; const BEFORE = 2; @@ -78,7 +78,7 @@ export const formatEventList = ( selectedId: string, nextId: string, options: FormatEventListOptionsProp, -) => { +): ScheduleEvent[] => { if (rundown == null) return []; const { showEnd = false } = options; @@ -103,6 +103,15 @@ export const formatEventList = ( return formattedEvents; }; +export type ScheduleEvent = { + id: string; + time: string; + title: string; + isNow: boolean; + isNext: boolean; + colour: string; +}; + /** * @description Creates a safe duplicate of an event * @param {object} event diff --git a/apps/client/src/features/cuesheet/Cuesheet.module.scss b/apps/client/src/features/cuesheet/Cuesheet.module.scss new file mode 100644 index 000000000..de94398b9 --- /dev/null +++ b/apps/client/src/features/cuesheet/Cuesheet.module.scss @@ -0,0 +1,157 @@ +@use '../../theme/ontimeColours' as *; +@use '../../theme/v2Styles' as *; + +$table-font-size: calc(1rem - 2px); +$table-header-font-size: calc(1rem - 3px); + +@mixin ellipsis-overflow() { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.cuesheetContainer { + grid-area: table; + display: flex; + flex-direction: column; + width: 100%; + height: 100%; + overflow: auto; + padding-bottom: 640px; // allow focus to reach last elements +} + +.cuesheet { + font-size: $table-font-size; + font-weight: 400; + + tr { + display: flex; + } + + th, td { + margin: 1px; + font-weight: inherit; + font-size: inherit; + text-align: left; + position: relative; + @include ellipsis-overflow; + } +} + +.tableHeader, +.eventRow { + .indexColumn { + min-width: 2rem; + text-align: right; + font-weight: 400; + } +} + +.tableHeader { + position: sticky; + top: 0; + z-index: 10; + background-color: $gray-1300; + + font-size: $table-header-font-size; + color: $gray-700; + font-weight: 400; +} + +.eventRow { + vertical-align: top; + + td { + background-color: $gray-1250; + border-radius: 2px; + padding: 0.25rem; + } + + &.skip { + text-decoration: line-through; + opacity: $opacity-disabled !important; // fighting inline styles + } +} + +.blockRow { + width: 100%; + background-color: $gray-1350; + font-size: 1rem; + height: 2.5rem; + + td { + align-self: flex-end; + position: sticky; + left: 1rem; + padding: 0.25rem 0; + } +} + +.delayRow { + width: 100%; + color: $ontime-delay-text; + + td { + position: sticky; + left: 47.5%; // center of the screen, ish + padding: 0.5rem 0; + } +} + +.check { + font-size: 1.5rem; + margin: 0 auto; +} + +.time { + display: flex; + gap: 0.5rem; + align-items: center; + + > * { + @include ellipsis-overflow; + } +} + +.delaySymbol { + svg { + font-size: 1.5rem; + color: $ontime-delay; + margin: 0 auto; + } +} + +.delayedTime { + color: $ontime-delay-text; + font-size: calc(1rem - 2px); +} + +.resizer { + cursor: col-resize; + opacity: 0; + display: inline-block; + width: 3px; + height: 100%; + position: absolute; + right: 0; + top: 0; + transform: translateX(50%); + background-color: $action-blue; + + user-select: none; + touch-action: none; + + transition-duration: $transition-time-action; + transition-property: width, background-color; + + &:hover { + opacity: $opacity-disabled; + width: 6px; + } + + &.isResizing { + opacity: 1; + width: 6px; + background-color: $action-blue; + } +} diff --git a/apps/client/src/features/cuesheet/Cuesheet.tsx b/apps/client/src/features/cuesheet/Cuesheet.tsx new file mode 100644 index 000000000..bd863aedc --- /dev/null +++ b/apps/client/src/features/cuesheet/Cuesheet.tsx @@ -0,0 +1,278 @@ +import { MutableRefObject, useEffect, useRef } from 'react'; +import { Tooltip } from '@chakra-ui/react'; +import { + closestCenter, + DndContext, + DragEndEvent, + KeyboardSensor, + PointerSensor, + TouchSensor, + useSensor, + useSensors, +} from '@dnd-kit/core'; +import { horizontalListSortingStrategy, SortableContext, sortableKeyboardCoordinates } from '@dnd-kit/sortable'; +import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from '@tanstack/react-table'; +import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, OntimeRundownEntry, SupportedEvent } from 'ontime-types'; + +import { useLocalStorage } from '../../common/hooks/useLocalStorage'; +import { millisToDelayString } from '../../common/utils/dateConfig'; +import { getAccessibleColour } from '../../common/utils/styleUtils'; +import { tooltipDelayFast } from '../../ontimeConfig'; + +import CuesheetTableSettings from './cuesheet-table-settings/CuesheetTableSettings'; +import { useCuesheetSettings } from './store/CuesheetSettings'; +import { SortableCell } from './tableElements/SortableCell'; +import { initialColumnOrder } from './cuesheetCols'; + +import style from './Cuesheet.module.scss'; + +const pastOpacity = '0.2'; + +interface CuesheetProps { + data: OntimeRundown; + columns: ColumnDef[]; + handleUpdate: (rowIndex: number, accessor: keyof OntimeRundownEntry, payload: unknown) => void; + selectedId: string | null; +} + +export default function Cuesheet({ data, columns, handleUpdate, selectedId }: CuesheetProps) { + const followSelected = useCuesheetSettings((state) => state.followSelected); + const showSettings = useCuesheetSettings((state) => state.showSettings); + const showDelayBlock = useCuesheetSettings((state) => state.showDelayBlock); + const showPrevious = useCuesheetSettings((state) => state.showPrevious); + + const [columnVisibility, setColumnVisibility] = useLocalStorage('table-hidden', {}); + const [columnOrder, saveColumnOrder] = useLocalStorage('table-order', initialColumnOrder); + const [columnSizing, setColumnSizing] = useLocalStorage('table-sizes', {}); + + const selectedRef = useRef(null); + + const table = useReactTable({ + data, + columns, + columnResizeMode: 'onChange', + state: { + columnOrder, + columnVisibility, + columnSizing, + }, + meta: { + handleUpdate, + }, + onColumnVisibilityChange: setColumnVisibility, + onColumnSizingChange: setColumnSizing, + getCoreRowModel: getCoreRowModel(), + }); + const tableContainerRef = useRef(null); + + const sensors = useSensors( + useSensor(PointerSensor, { + activationConstraint: { + delay: 100, + tolerance: 50, + }, + }), + useSensor(TouchSensor, { + activationConstraint: { + delay: 100, + tolerance: 50, + }, + }), + useSensor(KeyboardSensor, { + coordinateGetter: sortableKeyboardCoordinates, + }), + ); + + // when selection moves, view should follow + useEffect(() => { + function scrollToComponent( + componentRef: MutableRefObject, + scrollRef: MutableRefObject, + ) { + const componentRect = componentRef.current.getBoundingClientRect(); + const scrollRect = scrollRef.current.getBoundingClientRect(); + const top = componentRect.top - scrollRect.top + scrollRef.current.scrollTop - 100; + scrollRef.current.scrollTo({ top, behavior: 'smooth' }); + } + + if (!followSelected) { + return; + } + + if (selectedRef.current && tableContainerRef.current) { + // Use requestAnimationFrame to ensure the component is fully loaded + window.requestAnimationFrame(() => { + scrollToComponent( + selectedRef as MutableRefObject, + tableContainerRef as MutableRefObject, + ); + }); + } + // eslint-disable-next-line -- the prompt seems incorrect, we need the refs + }, [selectedRef.current, tableContainerRef.current, followSelected]); + + const handleOnDragEnd = (event: DragEndEvent) => { + const { delta, active, over } = event; + + // cancel if delta y is greater than 200 + if (delta.y > 200) return; + // cancel if we do not have an over id + if (over?.id == null) return; + + // get index of from + const fromIndex = columnOrder.indexOf(active.id as string); + + // get index of to + const toIndex = columnOrder.indexOf(over.id as string); + + if (toIndex === -1) { + return; + } + + const reorderedCols = [...columnOrder]; + const reorderedItem = reorderedCols.splice(fromIndex, 1); + reorderedCols.splice(toIndex, 0, reorderedItem[0]); + + saveColumnOrder(reorderedCols); + }; + + const resetColumnOrder = () => { + saveColumnOrder(initialColumnOrder); + }; + + const setAllVisible = () => { + table.toggleAllColumnsVisible(true); + }; + + const resetColumnResizing = () => { + setColumnSizing({}); + }; + + let eventIndex = 0; + let isPast = Boolean(selectedId); + + return ( + <> + {showSettings && ( + + )} +
+ + + {table.getHeaderGroups().map((headerGroup) => { + const key = headerGroup.id; + + return ( + + + + + {headerGroup.headers.map((header) => { + const width = header.getSize(); + + return ( + + {header.isPlaceholder + ? null + : flexRender(header.column.columnDef.header, header.getContext())} + + ); + })} + + + + ); + })} + + + + {table.getRowModel().rows.map((row) => { + const entryType = row.original.type as SupportedEvent; + const key = row.original.id; + const isSelected = selectedId === key; + if (isSelected) { + isPast = false; + } + + if (entryType === SupportedEvent.Block) { + const title = (row.original as OntimeBlock).title; + + return ( + + + + ); + } + if (entryType === SupportedEvent.Delay) { + const delayVal = (row.original as OntimeDelay).duration; + + if (!showDelayBlock || delayVal === 0) { + return null; + } + + const delayTime = millisToDelayString(delayVal); + return ( + + + + ); + } + if (entryType === SupportedEvent.Event) { + eventIndex++; + const isSelected = key === selectedId; + if (isSelected) { + isPast = false; + } + + if (isPast && !showPrevious) { + return null; + } + + const bgFallback = 'transparent'; + const bgColour = (row.original as OntimeEvent).colour || bgFallback; + const textColour = bgColour === bgFallback ? undefined : getAccessibleColour(bgColour); + const isSkipped = (row.original as OntimeEvent).skip; + + let rowBgColour: string | undefined; + if (row.original.id === selectedId) { + rowBgColour = '#D20300'; // $red-700 + } + return ( + + + {row.getVisibleCells().map((cell) => { + return ( + + ); + })} + + ); + } + + // currently there is no scenario where entryType is not handled above, either way... + return null; + })} + +
+ + # + +
{title}
{delayTime}
+ {eventIndex} + + {flexRender(cell.column.columnDef.cell, cell.getContext())} +
+
+ + ); +} diff --git a/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss b/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss new file mode 100644 index 000000000..851ddadef --- /dev/null +++ b/apps/client/src/features/cuesheet/CuesheetWrapper.module.scss @@ -0,0 +1,24 @@ +@use '../../theme/v2Styles' as *; +@use '../../theme/ontimeColours' as *; + +.tableWrapper { + width: 100%; + height: 100vh; + padding: 1rem; + + display: grid; + grid-template-rows: auto auto 1fr; + grid-template-areas: + 'header' + 'settings' + 'table'; + gap: 1rem; + + background-color: $gray-1300; + color: white; + + & > * { + border: 1px solid $white-10; + border-radius: 4px; + } +} diff --git a/apps/client/src/features/table/TableWrapper.jsx b/apps/client/src/features/cuesheet/CuesheetWrapper.tsx similarity index 62% rename from apps/client/src/features/table/TableWrapper.jsx rename to apps/client/src/features/cuesheet/CuesheetWrapper.tsx index ddfed6a10..1f1330df3 100644 --- a/apps/client/src/features/table/TableWrapper.jsx +++ b/apps/client/src/features/cuesheet/CuesheetWrapper.tsx @@ -1,23 +1,25 @@ -import { useCallback, useContext, useEffect } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; +import { EventData, OntimeRundownEntry } from 'ontime-types'; -import { TableSettingsContext } from '../../common/context/TableSettingsContext'; +import Empty from '../../common/components/state/Empty'; import { useEventAction } from '../../common/hooks/useEventAction'; import { useCuesheet } from '../../common/hooks/useSocket'; import useRundown from '../../common/hooks-query/useRundown'; import useUserFields from '../../common/hooks-query/useUserFields'; -import OntimeTable from './OntimeTable'; -import TableHeader from './TableHeader'; -import { makeCSV, makeTable } from './tableUtils'; +import CuesheetTableHeader from './cuesheet-table-header/CuesheetTableHeader'; +import Cuesheet from './Cuesheet'; +import { makeCuesheetColumns } from './cuesheetCols'; +import { makeCSV, makeTable } from './cuesheetUtils'; -import style from './Table.module.scss'; +import styles from './CuesheetWrapper.module.scss'; -export default function TableWrapper() { +export default function CuesheetWrapper() { const { data: rundown } = useRundown(); const { data: userFields } = useUserFields(); const { updateEvent } = useEventAction(); const featureData = useCuesheet(); - const { theme } = useContext(TableSettingsContext); + const columns = useMemo(() => makeCuesheetColumns(userFields), [userFields]); // Set window title useEffect(() => { @@ -25,14 +27,18 @@ export default function TableWrapper() { }, []); const handleUpdate = useCallback( - async (rowIndex, accessor, payload) => { + async (rowIndex: number, accessor: keyof OntimeRundownEntry, payload: unknown) => { + if (!rundown) { + return; + } + if (rowIndex == null || accessor == null || payload == null) { return; } // check if value is the same const event = rundown[rowIndex]; - if (event == null) { + if (!event) { return; } @@ -63,7 +69,7 @@ export default function TableWrapper() { ); const exportHandler = useCallback( - (headerData) => { + (headerData: EventData) => { if (!headerData || !rundown || !userFields) { return; } @@ -80,18 +86,14 @@ export default function TableWrapper() { [rundown, userFields], ); - if (typeof rundown === 'undefined' || typeof userFields === 'undefined') { - return loading...; + if (!rundown || !userFields) { + return ; } + return ( -
- - +
+ +
); } diff --git a/apps/client/src/features/cuesheet/ProtectedCuesheet.tsx b/apps/client/src/features/cuesheet/ProtectedCuesheet.tsx new file mode 100644 index 000000000..6a31c1742 --- /dev/null +++ b/apps/client/src/features/cuesheet/ProtectedCuesheet.tsx @@ -0,0 +1,11 @@ +import ProtectRoute from '../../common/components/protect-route/ProtectRoute'; + +import CuesheetWrapper from './CuesheetWrapper'; + +export default function ProtectedCuesheet() { + return ( + + + + ); +} diff --git a/apps/client/src/features/table/__tests__/__snapshots__/utils.test.js.snap b/apps/client/src/features/cuesheet/__tests__/__snapshots__/utils.test.js.snap similarity index 88% rename from apps/client/src/features/table/__tests__/__snapshots__/utils.test.js.snap rename to apps/client/src/features/cuesheet/__tests__/__snapshots__/utils.test.js.snap index b0c7aefe6..399b5867d 100644 --- a/apps/client/src/features/table/__tests__/__snapshots__/utils.test.js.snap +++ b/apps/client/src/features/cuesheet/__tests__/__snapshots__/utils.test.js.snap @@ -25,8 +25,11 @@ exports[`makeTable() > returns array of arrays with given fields 1`] = ` "Presenter Name", "Event Subtitle", "Is Public? (x)", - "Notes", + "Note", "Colour", + "End Action", + "Timer Type", + "Skip?", "user0:test", ], [ @@ -38,6 +41,9 @@ exports[`makeTable() > returns array of arrays with given fields 1`] = ` "x", "", "", + "", + "", + "", "test", "test", "", diff --git a/apps/client/src/features/table/__tests__/utils.test.js b/apps/client/src/features/cuesheet/__tests__/utils.test.js similarity index 97% rename from apps/client/src/features/table/__tests__/utils.test.js rename to apps/client/src/features/cuesheet/__tests__/utils.test.js index e7341053b..70c68c0b9 100644 --- a/apps/client/src/features/table/__tests__/utils.test.js +++ b/apps/client/src/features/cuesheet/__tests__/utils.test.js @@ -1,4 +1,4 @@ -import { makeCSV, makeTable, parseField } from '../tableUtils'; +import { makeCSV, makeTable, parseField } from '../cuesheetUtils'; describe('parseField()', () => { it('returns a string from given millis on timeStart and TimeEnd', () => { 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 new file mode 100644 index 000000000..f5b988dee --- /dev/null +++ b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.module.scss @@ -0,0 +1,119 @@ +@use '../../../theme/ontimeColours' as *; +@use '../../../theme/v2Styles' as *; + +$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; + font-size: 1.125rem; + color: $label-colour; + height: 100%; + + .actionIcon { + cursor: pointer; + + &:hover { + color: $active-colour; + } + + &.enabled { + color: $active-indicator; + } + } +} + +@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 new file mode 100644 index 000000000..52e3cda2b --- /dev/null +++ b/apps/client/src/features/cuesheet/cuesheet-table-header/CuesheetTableHeader.tsx @@ -0,0 +1,100 @@ +import { Tooltip } from '@chakra-ui/react'; +import { IoContract } from '@react-icons/all-files/io5/IoContract'; +import { IoExpand } from '@react-icons/all-files/io5/IoExpand'; +import { IoLocate } from '@react-icons/all-files/io5/IoLocate'; +import { IoSettingsOutline } from '@react-icons/all-files/io5/IoSettingsOutline'; +import { EventData, Playback } from 'ontime-types'; +import { formatDisplay } from 'ontime-utils'; + +import useFullscreen from '../../../common/hooks/useFullscreen'; +import { useTimer } from '../../../common/hooks/useSocket'; +import useEventData from '../../../common/hooks-query/useEventData'; +import { formatTime } from '../../../common/utils/time'; +import { tooltipDelayFast } from '../../../ontimeConfig'; +import { useCuesheetSettings } from '../store/CuesheetSettings'; +import PlaybackIcon from '../tableElements/PlaybackIcon'; + +import style from './CuesheetTableHeader.module.scss'; + +interface CuesheetTableHeaderProps { + handleCSVExport: (headerData: EventData) => void; + featureData: { + playback: Playback; + selectedEventIndex: number | null; + numEvents: number; + titleNow: string | null; + }; +} + +export default function CuesheetTableHeader({ handleCSVExport, featureData }: CuesheetTableHeaderProps) { + const followSelected = useCuesheetSettings((state) => state.followSelected); + const showSettings = useCuesheetSettings((state) => state.showSettings); + const toggleSettings = useCuesheetSettings((state) => state.toggleSettings); + const toggleFollow = useCuesheetSettings((state) => state.toggleFollow); + const timer = useTimer(); + const { isFullScreen, toggleFullScreen } = useFullscreen(); + const { data: event } = useEventData(); + + const exportCsv = () => { + if (event) { + handleCSVExport(event); + } + }; + + const selected = !featureData.numEvents + ? 'No events' + : `Event ${featureData.selectedEventIndex != null ? featureData.selectedEventIndex + 1 : '-'}/${ + featureData.numEvents ? featureData.numEvents : '-' + }`; + + // prepare presentation variables + const isOvertime = (timer.current ?? 0) < 0; + const timerNow = timer.current == null ? '-' : `${isOvertime ? '-' : ''}${formatDisplay(timer.current)}`; + const timeNow = formatTime(timer.clock, { + showSeconds: true, + format: 'hh:mm:ss a', + }); + + return ( +
+
+
{event?.title || '-'}
+
{featureData?.titleNow || '-'}
+
+
+
{selected}
+ +
+
+
Running Timer
+
{timerNow}
+
+
+
Time Now
+
{timeNow}
+
+
+ + toggleFollow()} className={`${style.actionIcon} ${followSelected ? style.enabled : ''}`}> + + + + + toggleSettings()} className={`${style.actionIcon} ${showSettings ? style.enabled : ''}`}> + + + + + toggleFullScreen()} className={style.actionIcon}> + {isFullScreen ? : } + + + + + CSV + + +
+
+ ); +} diff --git a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss new file mode 100644 index 000000000..8ed07c661 --- /dev/null +++ b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.module.scss @@ -0,0 +1,44 @@ +@use '../../../theme/v2Styles' as *; +@use '../../../theme/ontimeColours' as *; + +.tableSettings { + grid-area: settings; + padding: 1rem; + background-color: $ui-black; + display: flex; + font-size: $inner-section-text-size; +} + +.leftPanel { + display: flex; + flex-direction: column; + width: 100%; + gap: 0.5rem; +} + +.sectionTitle { + color: $gray-700; + text-transform: uppercase; +} + +.options { + display: flex; + flex-wrap: wrap; + column-gap: 1rem; + row-gap: 0.25em; +} + +.option { + cursor: pointer; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.rightPanel { + display: flex; + flex-direction: column; + gap: 0.5rem; + + padding-left: 2rem; +} diff --git a/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx new file mode 100644 index 000000000..accd5ce9c --- /dev/null +++ b/apps/client/src/features/cuesheet/cuesheet-table-settings/CuesheetTableSettings.tsx @@ -0,0 +1,83 @@ +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; +} + +export default function CuesheetTableSettings(props: CuesheetTableSettingsProps) { + const { columns, handleResetResizing, handleResetReordering, handleClearToggles } = props; + const showPrevious = useCuesheetSettings((state) => state.showPrevious); + const togglePreviousVisibility = useCuesheetSettings((state) => state.togglePreviousVisibility); + const showDelayBlock = useCuesheetSettings((state) => state.showDelayBlock); + const toggleDelayVisibility = useCuesheetSettings((state) => state.toggleDelayVisibility); + const showDelayedTimes = useCuesheetSettings((state) => state.showDelayedTimes); + const toggleDelayedTimes = useCuesheetSettings((state) => state.toggleDelayedTimes); + + return ( +
+
+
Toggle column visibility
+
+ {columns.map((column) => { + const columnHeader = column.columnDef.header; + const visible = column.getIsVisible(); + return ( + + ); + })} +
+
Table Options
+
+ +
+
Delay Flow
+
+ + +
+
+
+ + + +
+
+ ); +} diff --git a/apps/client/src/features/cuesheet/cuesheetCols.tsx b/apps/client/src/features/cuesheet/cuesheetCols.tsx new file mode 100644 index 000000000..ae826d329 --- /dev/null +++ b/apps/client/src/features/cuesheet/cuesheetCols.tsx @@ -0,0 +1,195 @@ +import { useCallback } from 'react'; +import { Tooltip } from '@chakra-ui/react'; +import { IoCheckmark } from '@react-icons/all-files/io5/IoCheckmark'; +import { IoChevronDown } from '@react-icons/all-files/io5/IoChevronDown'; +import { IoChevronUp } from '@react-icons/all-files/io5/IoChevronUp'; +import { CellContext, ColumnDef } from '@tanstack/react-table'; +import { OntimeEvent, OntimeRundownEntry, UserFields } from 'ontime-types'; +import { millisToString } from 'ontime-utils'; + +import { millisToDelayString } from '../../common/utils/dateConfig'; +import { tooltipDelayFast } from '../../ontimeConfig'; + +import { useCuesheetSettings } from './store/CuesheetSettings'; +import EditableCell from './tableElements/EditableCell'; + +import style from './Cuesheet.module.scss'; + +function makePublic(row: CellContext) { + const cellValue = row.getValue(); + return cellValue ? : ''; +} + +function DelayIndicator(props: { delayValue: number }) { + const { delayValue } = props; + if (delayValue < 0) { + return ( + + + + + + ); + } + + if (delayValue > 0) { + return ( + + + + + + ); + } + return null; +} + +function MakeTimer({ getValue, row: { original } }: CellContext) { + const showDelayedTimes = useCuesheetSettings((state) => state.showDelayedTimes); + const cellValue = (getValue() as number | null) ?? 0; + const delayValue = (original as OntimeEvent)?.delay ?? 0; + + console.log(delayValue) + + return ( + + + {millisToString(cellValue)} + {(delayValue !== 0 && showDelayedTimes) && {` ${millisToString(cellValue + delayValue)}`}} + + ); +} + +function MakeUserField({ getValue, row: { index }, column: { id }, table }: CellContext) { + const update = useCallback( + (newValue: string) => { + // @ts-expect-error -- we inject this into react-table + table.options.meta?.handleUpdate(index, id, newValue); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps -- we skip table.options.meta since the reference seems unstable + [id, index], + ); + + const initialValue = getValue() as string; + + return ; +} + +export function makeCuesheetColumns(userFields?: UserFields): ColumnDef[] { + return [ + { + 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: (row) => millisToString(row.getValue() as number | null), + size: 75, + }, + { + accessorKey: 'title', + id: 'title', + header: 'Title', + cell: (row) => row.getValue(), + }, + { + accessorKey: 'subtitle', + id: 'subtitle', + header: 'Subtitle', + cell: (row) => row.getValue(), + }, + { + accessorKey: 'presenter', + id: 'presenter', + header: 'Presenter', + cell: (row) => row.getValue(), + }, + { + accessorKey: 'note', + id: 'note', + header: 'Note', + cell: (row) => row.getValue(), + }, + { + accessorKey: 'user0', + id: 'user0', + header: userFields?.user0 || 'User 0', + cell: MakeUserField, + }, + { + accessorKey: 'user1', + id: 'user1', + header: userFields?.user1 || 'User 1', + cell: MakeUserField, + }, + { + accessorKey: 'user2', + id: 'user2', + header: userFields?.user2 || 'User 2', + cell: MakeUserField, + }, + { + accessorKey: 'user3', + id: 'user3', + header: userFields?.user3 || 'User 3', + cell: MakeUserField, + }, + { + accessorKey: 'user4', + id: 'user4', + header: userFields?.user4 || 'User 4', + cell: MakeUserField, + }, + { + accessorKey: 'user5', + id: 'user5', + header: userFields?.user5 || 'User 5', + cell: MakeUserField, + }, + { + accessorKey: 'user6', + id: 'user6', + header: userFields?.user6 || 'User 6', + cell: MakeUserField, + }, + { + accessorKey: 'user7', + id: 'user7', + header: userFields?.user7 || 'User 7', + cell: MakeUserField, + }, + { + accessorKey: 'user8', + id: 'user8', + header: userFields?.user8 || 'User 8', + cell: MakeUserField, + }, + { + accessorKey: 'user9', + id: 'user9', + header: userFields?.user9 || 'User 9', + cell: MakeUserField, + }, + ]; +} + +export const initialColumnOrder: string[] = makeCuesheetColumns().map((column) => column.id as string); diff --git a/apps/client/src/features/table/tableUtils.js b/apps/client/src/features/cuesheet/cuesheetUtils.ts similarity index 68% rename from apps/client/src/features/table/tableUtils.js rename to apps/client/src/features/cuesheet/cuesheetUtils.ts index 862dc5fb1..0b253ad2e 100644 --- a/apps/client/src/features/table/tableUtils.js +++ b/apps/client/src/features/cuesheet/cuesheetUtils.ts @@ -1,4 +1,5 @@ import { stringify } from 'csv-stringify/browser/esm/sync'; +import { EventData, OntimeEntryCommonKeys, OntimeRundown, UserFields } from 'ontime-types'; import { millisToString } from 'ontime-utils'; /** @@ -8,14 +9,15 @@ import { millisToString } from 'ontime-utils'; * @return {string} */ -export const parseField = (field, data) => { +export const parseField = (field: keyof OntimeRundown, data: unknown): string => { let val; switch (field) { case 'timeStart': case 'timeEnd': - val = millisToString(data); + val = millisToString(data as number | null); break; case 'isPublic': + case 'skip': val = data ? 'x' : ''; break; default: @@ -25,17 +27,18 @@ export const parseField = (field, data) => { if (typeof data === 'undefined') { return ''; } - return val; + // all other values are strings + return val as string; }; /** * @description Creates an array of arrays usable by xlsx for export * @param {object} headerData - * @param {array} tableData + * @param {array} rundown * @param {object} userFields * @return {(string[])[]} */ -export const makeTable = (headerData, tableData, userFields) => { +export const makeTable = (headerData: EventData, rundown: OntimeRundown, userFields: UserFields): string[][] => { const data = [ ['Ontime ยท Schedule Template'], ['Event Name', headerData?.title || ''], @@ -44,15 +47,18 @@ export const makeTable = (headerData, tableData, userFields) => { [], ]; - const fieldOrder = [ + const fieldOrder: OntimeEntryCommonKeys[] = [ 'timeStart', 'timeEnd', 'title', 'presenter', 'subtitle', 'isPublic', - 'notes', + 'note', 'colour', + 'endAction', + 'timerType', + 'skip', 'user0', 'user1', 'user2', @@ -72,20 +78,23 @@ export const makeTable = (headerData, tableData, userFields) => { 'Presenter Name', 'Event Subtitle', 'Is Public? (x)', - 'Notes', + 'Note', 'Colour', + 'End Action', + 'Timer Type', + 'Skip?', ]; for (const field in userFields) { - const fieldValue = userFields[field]; + const fieldValue = userFields[field as keyof UserFields]; const displayName = `${field}${fieldValue !== field && fieldValue !== '' ? `:${fieldValue}` : ''}`; fieldTitles.push(displayName); } data.push(fieldTitles); - tableData.forEach((entry) => { - const row = []; + rundown.forEach((entry) => { + const row: string[] = []; fieldOrder.forEach((field) => row.push(parseField(field, entry[field]))); data.push(row); }); @@ -98,8 +107,8 @@ export const makeTable = (headerData, tableData, userFields) => { * @param {array[]} arrayOfArrays * @return {string} */ -export const makeCSV = (arrayOfArrays) => { - let csvData = 'data:text/csv;charset=utf-8,'; +export const makeCSV = (arrayOfArrays: string[][]) => { + const csvData = 'data:text/csv;charset=utf-8,'; const stringifiedData = stringify(arrayOfArrays); return csvData + stringifiedData; }; diff --git a/apps/client/src/features/table/defaults.js b/apps/client/src/features/cuesheet/defaults.ts similarity index 69% rename from apps/client/src/features/table/defaults.js rename to apps/client/src/features/cuesheet/defaults.ts index 89735ecf3..842592b7a 100644 --- a/apps/client/src/features/table/defaults.js +++ b/apps/client/src/features/cuesheet/defaults.ts @@ -1,7 +1,9 @@ +import { OntimeEntryCommonKeys, OntimeEvent } from 'ontime-types'; + /** * @description set default column order */ -export const defaultColumnOrder = [ +export const defaultColumnOrder: OntimeEntryCommonKeys[] = [ 'isPublic', 'timeStart', 'timeEnd', @@ -25,7 +27,7 @@ export const defaultColumnOrder = [ /** * @description set default hidden columns */ -export const defaultHiddenColumns = [ +export const defaultHiddenColumns: (keyof OntimeEvent)[] = [ 'user0', 'user1', 'user2', diff --git a/apps/client/src/features/cuesheet/store/CuesheetSettings.tsx b/apps/client/src/features/cuesheet/store/CuesheetSettings.tsx new file mode 100644 index 000000000..0b840f5a5 --- /dev/null +++ b/apps/client/src/features/cuesheet/store/CuesheetSettings.tsx @@ -0,0 +1,65 @@ +import { create } from 'zustand'; + +import { booleanFromLocalStorage } from '../../../common/utils/localStorage'; + +interface CuesheetSettings { + showSettings: boolean; + followSelected: boolean; + showPrevious: boolean; + showDelayBlock: boolean; + showDelayedTimes: boolean; + + toggleSettings: (newValue?: boolean) => void; + toggleFollow: (newValue?: boolean) => void; + togglePreviousVisibility: (newValue?: boolean) => void; + toggleDelayVisibility: (newValue?: boolean) => void; + toggleDelayedTimes: (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', + DelayedTimes = 'ontime-cuesheet-show-delayed', +} + +export const useCuesheetSettings = create()((set) => ({ + showSettings: false, + followSelected: booleanFromLocalStorage(CuesheetKeys.Follow, false), + showPrevious: booleanFromLocalStorage(CuesheetKeys.PreviousVisibility, true), + showDelayBlock: booleanFromLocalStorage(CuesheetKeys.DelayVisibility, true), + showDelayedTimes: booleanFromLocalStorage(CuesheetKeys.DelayedTimes, 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 }; + }), + 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 }; + }), +})); diff --git a/apps/client/src/features/cuesheet/tableElements/EditableCell.tsx b/apps/client/src/features/cuesheet/tableElements/EditableCell.tsx new file mode 100644 index 000000000..85fa55f0e --- /dev/null +++ b/apps/client/src/features/cuesheet/tableElements/EditableCell.tsx @@ -0,0 +1,40 @@ +import { ChangeEvent, memo, useCallback, useEffect, useState } from 'react'; + +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 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]); + + // 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/tableElements/PlaybackIcon.tsx b/apps/client/src/features/cuesheet/tableElements/PlaybackIcon.tsx new file mode 100644 index 000000000..7813e94f4 --- /dev/null +++ b/apps/client/src/features/cuesheet/tableElements/PlaybackIcon.tsx @@ -0,0 +1,36 @@ +import { Tooltip } from '@chakra-ui/react'; +import { IoPause } from '@react-icons/all-files/io5/IoPause'; +import { IoPlay } from '@react-icons/all-files/io5/IoPlay'; +import { IoStop } from '@react-icons/all-files/io5/IoStop'; +import { Playback } from 'ontime-types'; + +import { tooltipDelayFast } from '../../../ontimeConfig'; + +interface PlaybackIconProps { + state: Playback; +} + +export default function PlaybackIcon(props: PlaybackIconProps) { + const { state } = props; + + // if timer is Pause or Armed + let label = 'Timer Paused'; + let Icon = IoPause; + + if (state === Playback.Roll) { + label = 'Timer Rolling'; + Icon = IoPlay; + } else if (state === Playback.Play) { + label = 'Timer Playing'; + Icon = IoPlay; + } else if (state === Playback.Stop) { + label = 'Timer Stopped'; + Icon = IoStop; + } + + return ( + + + + ); +} diff --git a/apps/client/src/features/cuesheet/tableElements/SortableCell.tsx b/apps/client/src/features/cuesheet/tableElements/SortableCell.tsx new file mode 100644 index 000000000..f20535c21 --- /dev/null +++ b/apps/client/src/features/cuesheet/tableElements/SortableCell.tsx @@ -0,0 +1,48 @@ +import { CSSProperties, ReactNode } from 'react'; +import { useSortable } from '@dnd-kit/sortable'; +import { CSS } from '@dnd-kit/utilities'; +import { Header } from '@tanstack/react-table'; +import { OntimeRundownEntry } from 'ontime-types'; + +import { cx } from '../../../common/utils/styleUtils'; + +import styles from '../Cuesheet.module.scss'; + +interface SortableCellProps { + header: Header; + style: CSSProperties; + children: ReactNode; +} + +export function SortableCell({ header, style, children }: SortableCellProps) { + const { column, colSpan } = header; + + const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ + id: column.id, + }); + + // build drag styles + const dragStyle = { + ...style, + opacity: isDragging ? 0.5 : 1, + transform: CSS.Translate.toString(transform), + transition, + }; + + const resizerClasses = cx([styles.resizer, header.column.getIsResizing() ? styles.isResizing : null]); + + return ( + +
+ {children} +
+
+ + ); +} diff --git a/apps/client/src/features/info/InfoLogger.tsx b/apps/client/src/features/info/InfoLogger.tsx index 493eff34d..285717c5c 100644 --- a/apps/client/src/features/info/InfoLogger.tsx +++ b/apps/client/src/features/info/InfoLogger.tsx @@ -1,19 +1,11 @@ import { useCallback, useState } from 'react'; import { Button } from '@chakra-ui/react'; +import { LogOrigin } from 'ontime-types'; import { clearLogs, useLogData } from '../../common/stores/logger'; import style from './InfoLogger.module.scss'; -enum LogFilter { - User = 'USER', - Client = 'CLIENT', - Server = 'SERVER', - RX = 'RX', - TX = 'TX', - Playback = 'PLAYBACK', -} - export default function InfoLogger() { const { logs: logData } = useLogData(); @@ -24,35 +16,35 @@ export default function InfoLogger() { const [showPlayback, setShowPlayback] = useState(true); const [showUser, setShowUser] = useState(true); - const matchers: LogFilter[] = []; + const matchers: LogOrigin[] = []; if (showUser) { - matchers.push(LogFilter.User); + matchers.push(LogOrigin.User); } if (showClient) { - matchers.push(LogFilter.Client); + matchers.push(LogOrigin.Client); } if (showServer) { - matchers.push(LogFilter.Server); + matchers.push(LogOrigin.Server); } if (showRx) { - matchers.push(LogFilter.RX); + matchers.push(LogOrigin.Rx); } if (showTx) { - matchers.push(LogFilter.TX); + matchers.push(LogOrigin.Tx); } if (showPlayback) { - matchers.push(LogFilter.Playback); + matchers.push(LogOrigin.Playback); } const filteredData = logData.filter((entry) => matchers.some((match) => entry.origin === match)); - const disableOthers = useCallback((toEnable: LogFilter) => { - toEnable === LogFilter.User ? setShowUser(true) : setShowUser(false); - toEnable === LogFilter.Client ? setShowClient(true) : setShowClient(false); - toEnable === LogFilter.Server ? setShowServer(true) : setShowServer(false); - toEnable === LogFilter.RX ? setShowRx(true) : setShowRx(false); - toEnable === LogFilter.TX ? setShowTx(true) : setShowTx(false); - toEnable === LogFilter.Playback ? setShowPlayback(true) : setShowPlayback(false); + const disableOthers = useCallback((toEnable: LogOrigin) => { + toEnable === LogOrigin.User ? setShowUser(true) : setShowUser(false); + toEnable === LogOrigin.Client ? setShowClient(true) : setShowClient(false); + toEnable === LogOrigin.Server ? setShowServer(true) : setShowServer(false); + toEnable === LogOrigin.Rx ? setShowRx(true) : setShowRx(false); + toEnable === LogOrigin.Tx ? setShowTx(true) : setShowTx(false); + toEnable === LogOrigin.Playback ? setShowPlayback(true) : setShowPlayback(false); }, []); return ( @@ -62,55 +54,55 @@ export default function InfoLogger() { variant={showUser ? 'ontime-filled' : 'ontime-subtle'} size='xs' onClick={() => setShowUser((s) => !s)} - onAuxClick={() => disableOthers(LogFilter.User)} + onAuxClick={() => disableOthers(LogOrigin.User)} onContextMenu={(e) => e.preventDefault()} > - {LogFilter.User} + {LogOrigin.User} - - - -
-
- ); -} - -TableSettings.propTypes = { - columns: PropTypes.array, - handleResetResizing: PropTypes.func.isRequired, - handleResetReordering: PropTypes.func.isRequired, - handleResetToggles: PropTypes.func.isRequired, - handleClearToggles: PropTypes.func.isRequired, -}; diff --git a/apps/client/src/features/table/tableRows/BlockRow.jsx b/apps/client/src/features/table/tableRows/BlockRow.jsx deleted file mode 100644 index 1bdb089ac..000000000 --- a/apps/client/src/features/table/tableRows/BlockRow.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import PropTypes from 'prop-types'; - -import style from '../Table.module.scss'; - -export default function BlockRow(props) { - const { row } = props; - return ( - - {row.original?.title || 'Block'} - - ); -} - -BlockRow.propTypes = { - row: PropTypes.object.isRequired, -}; diff --git a/apps/client/src/features/table/tableRows/DelayRow.jsx b/apps/client/src/features/table/tableRows/DelayRow.jsx deleted file mode 100644 index fb24298dd..000000000 --- a/apps/client/src/features/table/tableRows/DelayRow.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import PropTypes from 'prop-types'; - -import { millisToDelayString } from '../../../common/utils/dateConfig'; - -import style from '../Table.module.scss'; - -export default function DelayRow(props) { - const { row } = props; - const delayVal = row.original.duration; - const delayTime = delayVal !== 0 ? millisToDelayString(delayVal) : null; - - return ( - - {delayTime} - - ); -} - -DelayRow.propTypes = { - row: PropTypes.object.isRequired, -}; diff --git a/apps/client/src/features/table/tableRows/EventRow.jsx b/apps/client/src/features/table/tableRows/EventRow.jsx deleted file mode 100644 index 0cb1079ac..000000000 --- a/apps/client/src/features/table/tableRows/EventRow.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import PropTypes from 'prop-types'; - -import { getAccessibleColour } from '../../../common/utils/styleUtils'; - -import style from '../Table.module.scss'; - -export default function EventRow(props) { - const { row, index, selectedId, delay } = props; - const selected = row.original.id === selectedId; - - const colours = row.original.colour - ? getAccessibleColour(row.original.colour) - : {}; - - return ( - - {index} - {row.cells.map((cell) => { - const { key, style, ...restCellProps } = cell.getCellProps(); - const dynamicStyles = { ...style, ...colours }; - - - // Inject delay value if exits - if (delay !== 0 && delay != null) { - const col = cell.column.Header; - if (col === 'End' || col === 'Start') { - cell.delayed = cell.value + delay; - } - } - - return ( - - {cell.render('Cell')} - - ); - })} - - ); -} - -EventRow.propTypes = { - row: PropTypes.object.isRequired, - index: PropTypes.number.isRequired, - selectedId: PropTypes.string, - delay: PropTypes.number, -}; diff --git a/apps/client/src/features/viewers/studio/StudioClock.jsx b/apps/client/src/features/viewers/studio/StudioClock.tsx similarity index 79% rename from apps/client/src/features/viewers/studio/StudioClock.jsx rename to apps/client/src/features/viewers/studio/StudioClock.tsx index 433b21270..59f698bb7 100644 --- a/apps/client/src/features/viewers/studio/StudioClock.jsx +++ b/apps/client/src/features/viewers/studio/StudioClock.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; import { useSearchParams } from 'react-router-dom'; -import { formatDisplay, millisToString } from 'ontime-utils'; -import PropTypes from 'prop-types'; +import type { OntimeRundown, ViewSettings } from 'ontime-types'; +import { formatDisplay } from 'ontime-utils'; import { overrideStylesURL } from '../../../common/api/apiConstants'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; @@ -9,8 +9,16 @@ import { STUDIO_CLOCK_OPTIONS } from '../../../common/components/view-params-edi import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor'; import useFitText from '../../../common/hooks/useFitText'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; -import { formatEventList, getEventsWithDelay, trimRundown } from '../../../common/utils/eventsManager'; +import { TimeManagerType } from '../../../common/models/TimeManager.type'; +import { secondsInMillis } from '../../../common/utils/dateConfig'; +import { + formatEventList, + getEventsWithDelay, + type ScheduleEvent, + trimRundown, +} from '../../../common/utils/eventsManager'; import { formatTime } from '../../../common/utils/time'; +import { TitleManager } from '../ViewWrapper'; import './StudioClock.scss'; @@ -19,25 +27,25 @@ const formatOptions = { format: 'hh:mm', }; -StudioClock.propTypes = { - isMirrored: PropTypes.bool, - title: PropTypes.object, - time: PropTypes.object, - backstageEvents: PropTypes.array, - selectedId: PropTypes.string, - nextId: PropTypes.string, - onAir: PropTypes.bool, - viewSettings: PropTypes.object, -}; +interface StudioClockProps { + isMirrored: boolean; + title: TitleManager; + time: TimeManagerType; + backstageEvents: OntimeRundown; + selectedId: string | null; + nextId: string | null; + onAir: boolean; + viewSettings: ViewSettings; +} -export default function StudioClock(props) { +export default function StudioClock(props: StudioClockProps) { const { isMirrored, title, time, backstageEvents, selectedId, nextId, onAir, viewSettings } = props; // deferring rendering seems to affect styling (font and useFitText) useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL); const { fontSize: titleFontSize, ref: titleRef } = useFitText({ maxFontSize: 500 }); - const [schedule, setSchedule] = useState([]); + const [schedule, setSchedule] = useState([]); const activeIndicators = [...Array(12).keys()]; const secondsIndicators = [...Array(60).keys()]; @@ -59,16 +67,16 @@ export default function StudioClock(props) { } const delayed = getEventsWithDelay(backstageEvents); - const events = delayed.filter((e) => e.type === 'event'); - const trimmed = trimRundown(events, selectedId, MAX_TITLES); - const formatted = formatEventList(trimmed, selectedId, nextId, { + const trimmed = trimRundown(delayed, selectedId || '', MAX_TITLES); + + const formatted = formatEventList(trimmed, selectedId || '', nextId || '', { showEnd: false, }); setSchedule(formatted); }, [backstageEvents, nextId, selectedId]); const clock = formatTime(time.clock, formatOptions); - const [, , secondsNow] = millisToString(time.clock).split(':'); + const secondsNow = secondsInMillis(time.clock); const isNegative = (time.current ?? 0) < 0; return ( diff --git a/apps/client/src/index.scss b/apps/client/src/index.scss index 66aab9aa0..c7299401e 100644 --- a/apps/client/src/index.scss +++ b/apps/client/src/index.scss @@ -30,7 +30,6 @@ html, } @media (min-width: 1450px) and (max-width: 1666px) { - body, html, .App { diff --git a/apps/client/src/theme/_main.scss b/apps/client/src/theme/_main.scss deleted file mode 100644 index ab5f09f7c..000000000 --- a/apps/client/src/theme/_main.scss +++ /dev/null @@ -1,132 +0,0 @@ -@use "./ontimeColours" as *; - -$transition-time-action: 0.1s; -$transition-time-feedback: 0.3s; - -//////////////////////////////////// general app colours - -$bg-black-gradient: #202020; -$bg-black: #121212; -$bg-black-100: #070707; -$bg-black-200: #1a1a1a; // container borders -$bg-black-300: #1f1f1f; // container text -$bg-gray-1100: #232323; // container borders -$bg-gray-1050: #242424; // container borders -$bg-gray-1000: #262626; // container borders -$bg-gray-950: #292929; -$bg-gray-900: #303030; -$bg-gray-800: #404040; -$bg-gray-700: #505050; -$bg-gray-500: #666666; -$bg-gray-100: #c0c0c0; // borders and whatnot - -$bg-overlay: rgba(0, 0, 0, 0.85); - -// $ontime-accent: #4bffab); -$ontime-accent: #58A151; -$ontime-accent-text: mix($bg-black, $ontime-accent, 10%); -$ontime-pink: #ff7597; -$ontime-pink-variant: #ff6969; -$ontime-roll: #0274B6; -$ontime-delay: #F57C13; -$action-blue: #3182ce; -$ontime-paused: #c05621; -$opacity-disabled: 0.4; -$ontime-red: #E4281E; - -//rgba(255, 255, 255, 0.39); - $bg-gray-700 -//rgba(255, 255, 255, 0.13); - $bg-gray-900 -//rgba(255, 255, 255, 0.05); - $bg-gray-1000 -//rgba(255, 255, 255, 0.07) - $bg-gray-1000 -// text input bg - -// was rgba(255, 255, 255, 0.03) -// container level 1 - $bg-gray-1000 -// container level 2 - $bg-gray-1100 -// container level 2 border - rgba(0, 0, 0, 0.05) -// indent in level 2 - $bg-black-300 -// outdent in level2 - $bg-gray-950 -//////////////////////////////////// editor - -$notes-color: #f6f6f6; - -$text-white: #fffffa; -$text-gray-disabled: #505050; -$label-gray: #aaa; -$header-gray: $label-gray; -$clocks: #ddd; -$bg-gray: #f4f4f8; -$text-delay: #F57C13; - -$light-bg: #2b6cb0; -$light-bg-transparent: #2b6cb055; -$light-text: #2b6cb022; - -$info-gray: #aaa; -$info-gray-hover: #ddd; -$warning-orange: #dd6b20; -$error-red: #e53e3e; - -//////////////////////////////////// viewers -$title-white: #fffd; - -//////////////////////////////////// block elements -$bg-container-over: #0b1521; -$bg-container-over-l1: #132337; -$bg-container-l1: #202020; -$bg-container-l2: #232323; -$bg-container-l3: #2b2b2b; -$border-l1: 1px solid $bg-gray-1000; -$border-l3: 1px solid $bg-gray-900; - -$block-delay-color: #E2720D; -$delay-text: #d69e2e; -$block-delay-border: #d69e2e55; -$block-block-color: #7347AD; -$block-border: 1px solid $bg-gray-1100; - -//////////////////////////////////// utils - -@mixin ellipsis { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -//////////////////////////////////// custom inputs -$input-bg: rgba(255, 255, 255, 0.03); -$input-hover-bg: rgba(255, 255, 255, 0.13); -$input-bg-delayed: rgba(214, 158, 46, 0.07); -$input-hover-bg-delayed: rgba(214, 158, 46, 0.17); -$input-border: 1px solid transparent; -$input-delayed-border: 1px solid $block-delay-border; - -//////////////////////////////////// general app element overriders - -// no decoration on lists -ul { - list-style-type: none; -} - -// no resizing on text areas -textarea { - resize: none !important; -} - -// Define style for a link -a:hover { - color: $ontime-pink; -} - -// horizontal separator -.hSeparator { - width: 100%; - border-bottom: 1px solid $light-bg; - margin: 1em auto; - display: flex; - align-items: center; -} - -// inline vertical separator -.vSpan { - margin: 0 0.5em; -} diff --git a/apps/client/src/theme/_v2Styles.scss b/apps/client/src/theme/_v2Styles.scss index 0f64a0c5b..c299fb8e8 100644 --- a/apps/client/src/theme/_v2Styles.scss +++ b/apps/client/src/theme/_v2Styles.scss @@ -51,8 +51,8 @@ $ontime-font-family: "Open Sans", "Segoe UI", sans-serif; $label-gray: $gray-400; $secondary-text-gray: $gray-400; $section-white: $ui-white; -$inner-section-text-size: 14px; -$text-body-size: 15px; +$inner-section-text-size: calc(1rem - 2px); +$text-body-size: calc(1rem - 1px); .blink { animation: blink $blinking-time linear infinite; diff --git a/apps/client/src/theme/ontimeTextInputs.ts b/apps/client/src/theme/ontimeTextInputs.ts index e185e5a5a..c135d589e 100644 --- a/apps/client/src/theme/ontimeTextInputs.ts +++ b/apps/client/src/theme/ontimeTextInputs.ts @@ -36,6 +36,13 @@ export const ontimeInputFilledOnLight = { export const ontimeTextAreaFilled = { ...commonStyles, }; +export const ontimeTextAreaTransparent = { + ...commonStyles, + backgroundColor: 'transparent', + _hover: { + backgroundColor: 'rgba(255, 255, 255, 0.10)', // $white-10 + }, +}; export const ontimeTextAreaFilledOnLight = { borderRadius: '3px', diff --git a/apps/client/src/theme/theme.ts b/apps/client/src/theme/theme.ts index 44d2dcfe3..d1b89a58b 100644 --- a/apps/client/src/theme/theme.ts +++ b/apps/client/src/theme/theme.ts @@ -23,6 +23,7 @@ import { ontimeInputFilledOnLight, ontimeTextAreaFilled, ontimeTextAreaFilledOnLight, + ontimeTextAreaTransparent, } from './ontimeTextInputs'; import { ontimeTooltip } from './ontimeTooltip'; @@ -96,6 +97,7 @@ const theme = extendTheme({ }, variants: { 'ontime-filled': { ...ontimeTextAreaFilled }, + 'ontime-transparent': { ...ontimeTextAreaTransparent }, 'ontime-filled-on-light': { ...ontimeTextAreaFilledOnLight }, }, }, diff --git a/apps/server/package.json b/apps/server/package.json index cb80ae90a..29503f694 100644 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -44,6 +44,7 @@ "setdb": "shx cp ../../demo-db/db.json src/preloaded-db/db.json", "postinstall": "pnpm addversion && pnpm setdb", "dev": "cross-env NODE_ENV=development nodemon --exec \"ts-node-esm\" ./src/index.ts", + "dev:inspect": "cross-env NODE_ENV=development nodemon --exec \"node --inspect --loader ts-node/esm\" ./src/index.ts", "dev:test": "cross-env IS_TEST=true nodemon --exec \"ts-node-esm\" ./src/index.ts", "prebuild": "pnpm setdb", "build": "pnpm prebuild && esbuild src/app.ts --log-level=error --platform=node --format=cjs --bundle --minify --outfile=dist/index.cjs", diff --git a/apps/server/src/adapters/OscAdapter.ts b/apps/server/src/adapters/OscAdapter.ts index 9a61dfbbe..d1a3a3ead 100644 --- a/apps/server/src/adapters/OscAdapter.ts +++ b/apps/server/src/adapters/OscAdapter.ts @@ -1,5 +1,6 @@ +import { LogOrigin, OSCSettings } from 'ontime-types'; + import { Server } from 'node-osc'; -import { OSCSettings } from 'ontime-types'; import { IAdapter } from './IAdapter.js'; import { dispatchFromAdapter } from '../controllers/integrationController.js'; @@ -25,13 +26,13 @@ export class OscServer implements IAdapter { // get first part before (ontime) if (address !== 'ontime') { - logger.error('RX', `OSC IN: OSC messages to ontime must start with /ontime/, received: ${msg}`); + logger.error(LogOrigin.Rx, `OSC IN: OSC messages to ontime must start with /ontime/, received: ${msg}`); return; } // get second part (command) if (!path) { - logger.error('RX', 'OSC IN: No path found'); + logger.error(LogOrigin.Rx, 'OSC IN: No path found'); return; } @@ -42,7 +43,7 @@ export class OscServer implements IAdapter { this.osc.emit(topic, payload); } } catch (error) { - logger.error('RX', `OSC IN: ${error}`); + logger.error(LogOrigin.Rx, `OSC IN: ${error}`); } }); } diff --git a/apps/server/src/adapters/WebsocketAdapter.ts b/apps/server/src/adapters/WebsocketAdapter.ts index d092c7cca..b5668c784 100644 --- a/apps/server/src/adapters/WebsocketAdapter.ts +++ b/apps/server/src/adapters/WebsocketAdapter.ts @@ -14,6 +14,8 @@ * Payload: adds necessary payload for the request to be completed */ +import { LogOrigin } from 'ontime-types'; + import { WebSocket, WebSocketServer } from 'ws'; import getRandomName from '../utils/getRandomName.js'; @@ -47,7 +49,7 @@ export class SocketServer implements IAdapter { this.wss.on('connection', (ws) => { let clientId = getRandomName(); this.clientIds.add(clientId); - logger.info('CLIENT', `${this.wss.clients.size} Connections with new: ${clientId}`); + logger.info(LogOrigin.Client, `${this.wss.clients.size} Connections with new: ${clientId}`); // send store payload on connect ws.send( @@ -67,7 +69,7 @@ export class SocketServer implements IAdapter { ws.on('error', console.error); ws.on('close', () => { - logger.info('CLIENT', `${this.wss.clients.size} Connections with disconnected: ${clientId}`); + logger.info(LogOrigin.Client, `${this.wss.clients.size} Connections with disconnected: ${clientId}`); this.clientIds.delete(clientId); }); @@ -96,7 +98,7 @@ export class SocketServer implements IAdapter { clientId = payload; this.clientIds.delete(previousName); this.clientIds.add(clientId); - logger.info('CLIENT', `Client ${previousName} renamed to ${clientId}`); + logger.info(LogOrigin.Client, `Client ${previousName} renamed to ${clientId}`); } ws.send( JSON.stringify({ @@ -127,7 +129,7 @@ export class SocketServer implements IAdapter { ws.send(topic, payload); } } catch (error) { - logger.error('RX', `WS IN: ${error}`); + logger.error(LogOrigin.Rx, `WS IN: ${error}`); } } catch (_) { // we ignore unknown diff --git a/apps/server/src/app.ts b/apps/server/src/app.ts index 86a639750..f27f49e54 100644 --- a/apps/server/src/app.ts +++ b/apps/server/src/app.ts @@ -9,7 +9,7 @@ import { join, resolve } from 'path'; import { currentDirectory, environment, externalsStartDirectory, isProduction, resolvedPath } from './setup.js'; import { ONTIME_VERSION } from './ONTIME_VERSION.js'; -import { OSCSettings } from 'ontime-types'; +import { LogOrigin, OSCSettings } from 'ontime-types'; // Import Routes import { router as rundownRouter } from './routes/rundownRouter.js'; @@ -159,7 +159,7 @@ export const startOSCServer = async (overrideConfig = null) => { const { osc } = DataProvider.getData(); if (!osc.enabledIn) { - logger.info('RX', 'OSC Input Disabled'); + logger.info(LogOrigin.Rx, 'OSC Input Disabled'); return; } @@ -170,7 +170,7 @@ export const startOSCServer = async (overrideConfig = null) => { }; // Start OSC Server - logger.info('RX', `Starting OSC Server on port: ${oscSettings.portIn}`); + logger.info(LogOrigin.Rx, `Starting OSC Server on port: ${oscSettings.portIn}`); oscServer = new OscServer(oscSettings); }; @@ -187,7 +187,7 @@ export const startIntegrations = async (config?: { osc: OSCSettings }) => { } const { success, message } = oscIntegration.init(osc); - logger.info('RX', message); + logger.info(LogOrigin.Rx, message); if (success) { integrationService.register(oscIntegration); @@ -214,12 +214,12 @@ export const shutdown = async (exitCode = 0) => { process.on('exit', (code) => console.log(`Ontime exited with code: ${code}`)); process.on('unhandledRejection', async (error) => { - logger.error('SERVER', `Error: unhandled rejection ${error}`); + logger.error(LogOrigin.Server, `Error: unhandled rejection ${error}`); await shutdown(1); }); process.on('uncaughtException', async (error) => { - logger.error('SERVER', `Error: uncaught exception ${error}`); + logger.error(LogOrigin.Server, `Error: uncaught exception ${error}`); await shutdown(1); }); diff --git a/apps/server/src/classes/data-provider/DataProvider.ts b/apps/server/src/classes/data-provider/DataProvider.ts index b727d8d81..66f705ac2 100644 --- a/apps/server/src/classes/data-provider/DataProvider.ts +++ b/apps/server/src/classes/data-provider/DataProvider.ts @@ -2,7 +2,7 @@ * Class Event Provider is a mediator for handling the local db * and adds logic specific to ontime data */ -import { EventData, SupportedEvent, ViewSettings } from 'ontime-types'; +import { EventData, ViewSettings } from 'ontime-types'; import { data, db } from '../../modules/loadDb.js'; import { safeMerge } from './DataProvider.utils.js'; @@ -27,31 +27,14 @@ export class DataProvider { await this.persist(); } + static getIndexOf(eventId) { + return data.rundown.findIndex((e) => e.id === eventId); + } + static getEventById(eventId) { return data.rundown.find((e) => e.id === eventId); } - static async updateEventById(eventId, newData) { - const eventIndex = data.rundown.findIndex((e) => e.id === eventId); - const persistedEvent = data.rundown[eventIndex]; - const newEvent = { ...persistedEvent, ...newData }; - if (newEvent.type === SupportedEvent.Event) { - newEvent.revision++; - } - data.rundown[eventIndex] = newEvent; - await this.persist(); - return data.rundown[eventIndex]; - } - - static async deleteEvent(eventId) { - const eventIndex = data.rundown.findIndex((e) => e.id === eventId); - - if (eventIndex !== -1) { - data.rundown.splice(eventIndex, 1); - await this.persist(); - } - } - static getRundownLength() { return data.rundown.length; } @@ -62,53 +45,6 @@ export class DataProvider { await db.write(); } - /** - * Insets an event after a given index - * @param entry - * @param index - * @return {Promise} - */ - static async insertEventAt(entry, index) { - // get events - const events = DataProvider.getRundown(); - const count = events.length; - const order = entry.order; - - // Remove order field from object - delete entry.order; - - // Insert at beginning - if (order === 0) { - events.unshift(entry); - } - - // insert at end - else if (order >= count) { - events.push(entry); - } - - // insert in the middle - else { - events.splice(index, 0, entry); - } - - // save events - await DataProvider.setRundown(events); - } - - /** - * @description Inserts an entry after an element with given ID - * @param entry - * @param id - * @return {Promise} - */ - static async insertEventAfterId(entry, id) { - const index = [...data.rundown].findIndex((event) => event.id === id); - // eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars -- we are just getting rid of after parameter - const { after, ...sanitisedEvent } = entry; - await DataProvider.insertEventAt(sanitisedEvent, index + 1); - } - static getSettings() { return data.settings; } diff --git a/apps/server/src/classes/event-loader/EventLoader.ts b/apps/server/src/classes/event-loader/EventLoader.ts index 0074bb429..e2821d443 100644 --- a/apps/server/src/classes/event-loader/EventLoader.ts +++ b/apps/server/src/classes/event-loader/EventLoader.ts @@ -1,4 +1,4 @@ -import { Loaded, OntimeEvent, TitleBlock } from 'ontime-types'; +import { Loaded, OntimeEvent, SupportedEvent, TitleBlock } from 'ontime-types'; import { DataProvider } from '../data-provider/DataProvider.js'; import { getRollTimers } from '../../services/rollUtils.js'; @@ -34,8 +34,7 @@ export class EventLoader { * @return {array} */ static getTimedEvents(): OntimeEvent[] { - // return mockLoaderData.filter((event) => event.type === 'event'); - return DataProvider.getRundown().filter((event) => event.type === 'event'); + return DataProvider.getRundown().filter((event) => event.type === SupportedEvent.Event) as OntimeEvent[]; } /** @@ -43,8 +42,9 @@ export class EventLoader { * @return {array} */ static getPlayableEvents(): OntimeEvent[] { - // return mockLoaderData.filter((event) => event.type === 'event' && !event.skip); - return DataProvider.getRundown().filter((event) => event.type === 'event' && !event.skip); + return DataProvider.getRundown().filter( + (event) => event.type === SupportedEvent.Event && !event.skip, + ) as OntimeEvent[]; } /** diff --git a/apps/server/src/controllers/ontimeController.ts b/apps/server/src/controllers/ontimeController.ts index 0e2db0821..c5cd3c4dc 100644 --- a/apps/server/src/controllers/ontimeController.ts +++ b/apps/server/src/controllers/ontimeController.ts @@ -1,6 +1,8 @@ +import { Alias, EventData, LogOrigin } from 'ontime-types'; + import fs from 'fs'; -import type { Alias, EventData } from 'ontime-types'; import { networkInterfaces } from 'os'; + import { fileHandler } from '../utils/parser.js'; import { DataProvider } from '../classes/data-provider/DataProvider.js'; import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js'; @@ -10,7 +12,7 @@ import { eventStore } from '../stores/EventStore.js'; import { resolveDbPath } from '../setup.js'; import { oscIntegration } from '../services/integration-service/OscIntegration.js'; import { logger } from '../classes/Logger.js'; -import { deleteAllEvents, forceReset } from '../services/RundownService.js'; +import { deleteAllEvents, forceReset } from '../services/rundown-service/RundownService.js'; // Create controller for GET request to '/ontime/poll' // Returns data for current state @@ -281,7 +283,7 @@ export const postOscSubscriptions = async (req, res) => { // TODO: this update could be more granular, checking that relevant data was changed const { message } = oscIntegration.init(oscSettings); - logger.info('RX', message); + logger.info(LogOrigin.Rx, message); res.send(oscSettings).status(200); } catch (error) { @@ -302,7 +304,7 @@ export const postOSC = async (req, res) => { // TODO: this update could be more granular, checking that relevant data was changed const { message } = oscIntegration.init(oscSettings); - logger.info('RX', message); + logger.info(LogOrigin.Rx, message); res.send(oscSettings).status(200); } catch (error) { diff --git a/apps/server/src/controllers/playbackController.js b/apps/server/src/controllers/playbackController.ts similarity index 100% rename from apps/server/src/controllers/playbackController.js rename to apps/server/src/controllers/playbackController.ts diff --git a/apps/server/src/controllers/rundownController.js b/apps/server/src/controllers/rundownController.ts similarity index 85% rename from apps/server/src/controllers/rundownController.js rename to apps/server/src/controllers/rundownController.ts index 77f746367..46b3346ab 100644 --- a/apps/server/src/controllers/rundownController.js +++ b/apps/server/src/controllers/rundownController.ts @@ -1,4 +1,4 @@ -import { DataProvider } from '../classes/data-provider/DataProvider.ts'; +import { OntimeEvent } from 'ontime-types'; import { failEmptyObjects } from '../utils/routerUtils.js'; import { addEvent, @@ -7,18 +7,14 @@ import { deleteEvent, editEvent, reorderEvent, -} from '../services/RundownService.ts'; +} from '../services/rundown-service/RundownService.js'; +import { getDelayedRundown } from '../services/rundown-service/delayedRundown.utils.js'; // Create controller for GET request to '/events' // Returns - export const rundownGetAll = async (req, res) => { - res.json(DataProvider.getRundown()); -}; - -// Create controller for GET request to '/events/:eventId' -// Returns - -export const getEventById = async (req, res) => { - res.json(DataProvider.getEventById(req.params?.eventId)); + const delayedRundown = getDelayedRundown(); + res.json(delayedRundown); }; // Create controller for POST request to '/events/' diff --git a/apps/server/src/controllers/rundownController.validate.js b/apps/server/src/controllers/rundownController.validate.ts similarity index 100% rename from apps/server/src/controllers/rundownController.validate.js rename to apps/server/src/controllers/rundownController.validate.ts diff --git a/apps/server/src/models/eventsDefinition.ts b/apps/server/src/models/eventsDefinition.ts index 82d21a119..b8b8adf0f 100644 --- a/apps/server/src/models/eventsDefinition.ts +++ b/apps/server/src/models/eventsDefinition.ts @@ -1,6 +1,6 @@ import { EndAction, OntimeBlock, OntimeDelay, OntimeEvent, SupportedEvent, TimerType } from 'ontime-types'; -export const event: Omit = { +export const event: Omit = { title: '', subtitle: '', presenter: '', diff --git a/apps/server/src/routes/rundownRouter.ts b/apps/server/src/routes/rundownRouter.ts index 4191d88ea..e4957f53f 100644 --- a/apps/server/src/routes/rundownRouter.ts +++ b/apps/server/src/routes/rundownRouter.ts @@ -1,7 +1,6 @@ import express from 'express'; import { deleteEventById, - getEventById, rundownApplyDelay, rundownDelete, rundownGetAll, @@ -21,9 +20,6 @@ export const router = express.Router(); // create route between controller and '/events/' endpoint router.get('/', rundownGetAll); -// create route between controller and '/events/:eventId' endpoint -router.get('/:eventId', paramsMustHaveEventId, getEventById); - // create route between controller and '/events/' endpoint router.post('/', rundownPostValidator, rundownPost); diff --git a/apps/server/src/services/PlaybackService.ts b/apps/server/src/services/PlaybackService.ts index 813906f4f..c463e5cce 100644 --- a/apps/server/src/services/PlaybackService.ts +++ b/apps/server/src/services/PlaybackService.ts @@ -1,4 +1,4 @@ -import { OntimeEvent } from 'ontime-types'; +import { LogOrigin, OntimeEvent, Playback } from 'ontime-types'; import { validatePlayback } from 'ontime-utils'; import { eventLoader, EventLoader } from '../classes/event-loader/EventLoader.js'; @@ -20,9 +20,9 @@ export class PlaybackService { static loadEvent(event: OntimeEvent): boolean { let success = false; if (!event) { - logger.error('PLAYBACK', 'No event found'); + logger.error(LogOrigin.Playback, 'No event found'); } else if (event.skip) { - logger.warning('PLAYBACK', `Refused playback of skipped event ID ${event.id}`); + logger.warning(LogOrigin.Playback, `Refused playback of skipped event ID ${event.id}`); } else { eventLoader.loadEvent(event); eventTimer.load(event); @@ -41,7 +41,7 @@ export class PlaybackService { const event = EventLoader.getEventWithId(eventId); const success = PlaybackService.loadEvent(event); if (success) { - logger.info('PLAYBACK', `Loaded event with ID ${event.id}`); + logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`); PlaybackService.start(); } return success; @@ -56,7 +56,7 @@ export class PlaybackService { const event = EventLoader.getEventAtIndex(eventIndex); const success = PlaybackService.loadEvent(event); if (success) { - logger.info('PLAYBACK', `Loaded event with ID ${event.id}`); + logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`); PlaybackService.start(); } return success; @@ -71,7 +71,7 @@ export class PlaybackService { const event = EventLoader.getEventWithId(eventId); const success = PlaybackService.loadEvent(event); if (success) { - logger.info('PLAYBACK', `Loaded event with ID ${event.id}`); + logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`); } return success; } @@ -85,7 +85,7 @@ export class PlaybackService { const event = EventLoader.getEventAtIndex(eventIndex); const success = PlaybackService.loadEvent(event); if (success) { - logger.info('PLAYBACK', `Loaded event with ID ${event.id}`); + logger.info(LogOrigin.Playback, `Loaded event with ID ${event.id}`); } return success; } @@ -98,7 +98,7 @@ export class PlaybackService { if (previousEvent) { const success = PlaybackService.loadEvent(previousEvent); if (success) { - logger.info('PLAYBACK', `Loaded event with ID ${previousEvent.id}`); + logger.info(LogOrigin.Playback, `Loaded event with ID ${previousEvent.id}`); } } } @@ -113,19 +113,19 @@ export class PlaybackService { if (nextEvent) { const success = PlaybackService.loadEvent(nextEvent); if (success) { - logger.info('PLAYBACK', `Loaded event with ID ${nextEvent.id}`); + logger.info(LogOrigin.Playback, `Loaded event with ID ${nextEvent.id}`); return true; } } else if (fallbackAction === 'stop') { - logger.info('PLAYBACK', 'No next event found! Stopping playback'); + logger.info(LogOrigin.Playback, 'No next event found! Stopping playback'); PlaybackService.stop(); return false; } else if (fallbackAction === 'pause') { - logger.info('PLAYBACK', 'No next event found! Pausing playback'); + logger.info(LogOrigin.Playback, 'No next event found! Pausing playback'); PlaybackService.pause(); return false; } else { - logger.info('PLAYBACK', 'No next event found! Continuing playback'); + logger.info(LogOrigin.Playback, 'No next event found! Continuing playback'); return false; } } @@ -137,7 +137,7 @@ export class PlaybackService { if (validatePlayback(eventTimer.playback).start) { eventTimer.start(); const newState = eventTimer.playback; - logger.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); + logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } } @@ -159,7 +159,7 @@ export class PlaybackService { if (validatePlayback(eventTimer.playback).pause) { eventTimer.pause(); const newState = eventTimer.playback; - logger.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); + logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } } @@ -171,7 +171,7 @@ export class PlaybackService { eventLoader.reset(); eventTimer.stop(); const newState = eventTimer.playback; - logger.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); + logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } } @@ -193,14 +193,14 @@ export class PlaybackService { // nothing to play if (rollTimers === null) { - logger.warning('SERVER', 'Roll: no events found'); + logger.warning(LogOrigin.Server, 'Roll: no events found'); PlaybackService.stop(); return; } const { currentEvent, nextEvent } = rollTimers; if (!currentEvent && !nextEvent) { - logger.warning('SERVER', 'Roll: no events found'); + logger.warning(LogOrigin.Server, 'Roll: no events found'); PlaybackService.stop(); return; } @@ -208,7 +208,7 @@ export class PlaybackService { eventTimer.roll(currentEvent, nextEvent); const newState = eventTimer.playback; - logger.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); + logger.info(LogOrigin.Playback, `Play Mode ${newState.toUpperCase()}`); } } @@ -221,8 +221,8 @@ export class PlaybackService { const delayInMs = delayTime * 1000 * 60; eventTimer.delay(delayInMs); delayInMs > 0 - ? logger.info('PLAYBACK', `Added ${delayTime} min delay`) - : logger.info('PLAYBACK', `Removed ${delayTime} min delay`); + ? logger.info(LogOrigin.Playback, `Added ${delayTime} min delay`) + : logger.info(LogOrigin.Playback, `Removed ${delayTime} min delay`); } } } diff --git a/apps/server/src/services/RundownService.ts b/apps/server/src/services/rundown-service/RundownService.ts similarity index 54% rename from apps/server/src/services/RundownService.ts rename to apps/server/src/services/rundown-service/RundownService.ts index 0dc8aeb94..f1db79314 100644 --- a/apps/server/src/services/RundownService.ts +++ b/apps/server/src/services/rundown-service/RundownService.ts @@ -1,11 +1,30 @@ -import { OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent, SupportedEvent } from 'ontime-types'; +import { + LogOrigin, + OntimeBaseEvent, + OntimeBlock, + OntimeDelay, + OntimeEvent, + OntimeRundown, + SupportedEvent, +} from 'ontime-types'; import { generateId } from 'ontime-utils'; -import { DataProvider } from '../classes/data-provider/DataProvider.js'; -import { block as blockDef, delay as delayDef, event as eventDef } from '../models/eventsDefinition.js'; -import { MAX_EVENTS } from '../settings.js'; -import { EventLoader, eventLoader } from '../classes/event-loader/EventLoader.js'; -import { eventTimer } from './TimerService.js'; -import { sendRefetch } from '../adapters/websocketAux.js'; +import { DataProvider } from '../../classes/data-provider/DataProvider.js'; +import { block as blockDef, delay, delay as delayDef, event as eventDef } from '../../models/eventsDefinition.js'; +import { MAX_EVENTS } from '../../settings.js'; +import { EventLoader, eventLoader } from '../../classes/event-loader/EventLoader.js'; +import { eventTimer } from '../TimerService.js'; +import { sendRefetch } from '../../adapters/websocketAux.js'; +import { runtimeCacheStore } from '../../stores/cachingStore.js'; +import { + cachedAdd, + cachedDelete, + cachedEdit, + cachedReorder, + calculateRuntimeDelaysFrom, + delayedRundownCacheKey, + getDelayedRundown, +} from './delayedRundown.utils.js'; +import { logger } from '../../classes/Logger.js'; /** * Forces rundown to be recalculated @@ -14,6 +33,7 @@ import { sendRefetch } from '../adapters/websocketAux.js'; export function forceReset() { eventLoader.reset(); sendRefetch(); + runtimeCacheStore.invalidate(delayedRundownCacheKey); } /** @@ -73,7 +93,7 @@ const isNewNext = () => { }; /** - * Updates timer object + * Updates timer service when a relevant piece of data changes */ export function updateTimer(affectedIds?: string[]) { const runningEventId = eventLoader.loaded.selectedEventId; @@ -130,44 +150,54 @@ export async function addEvent(eventData: Partial | Partial = {}; const id = generateId(); + // TODO: filter the parameters that exist in the event, use the parserUtils switch (eventData.type) { - case 'event': - newEvent = { ...eventDef, ...eventData, id } as Partial; + case SupportedEvent.Event: + newEvent = { ...eventDef, ...eventData, id }; break; - case 'delay': - newEvent = { ...delayDef, ...eventData, id } as Partial; + case SupportedEvent.Delay: + newEvent = { ...delayDef, ...eventData, id }; break; - case 'block': - newEvent = { ...blockDef, ...eventData, id } as Partial; + case SupportedEvent.Block: + newEvent = { ...blockDef, ...eventData, id }; break; } - try { - const afterId = newEvent?.after; - if (typeof afterId === 'undefined') { - await DataProvider.insertEventAt(newEvent, 0); + let insertIndex = 0; + if (typeof newEvent?.after !== 'undefined') { + const index = DataProvider.getIndexOf(newEvent.after); + if (index < 0) { + logger.warning(LogOrigin.Server, `Could not find event with id ${newEvent.after}`); } else { - delete newEvent.after; - await DataProvider.insertEventAfterId(newEvent, afterId); + insertIndex = index + 1; } - } catch (error) { - throw new Error(error); + delete newEvent.after; } + + // modify rundown + await cachedAdd(insertIndex, newEvent as OntimeEvent | OntimeDelay | OntimeBlock); + + // notify timer service of changed events updateTimer([id]); + + // notify event loader that rundown size has changed updateChangeNumEvents(); + + // advice socket subscribers of change sendRefetch(); + return newEvent; } -export async function editEvent(eventData) { - const eventId = eventData.id; - const eventInMemory = DataProvider.getEventById(eventId); - if (typeof eventInMemory === 'undefined') { - throw new Error('No event with ID found'); - } - const newEvent = await DataProvider.updateEventById(eventId, eventData); - updateTimer([eventId]); +export async function editEvent(eventData: Partial | Partial | Partial) { + const newEvent = await cachedEdit(eventData.id, eventData); + + // notify timer service of changed events + updateTimer([newEvent.id]); + + // advice socket subscribers of change sendRefetch(); + return newEvent; } @@ -177,9 +207,18 @@ export async function editEvent(eventData) { * @returns {Promise} */ export async function deleteEvent(eventId) { - await DataProvider.deleteEvent(eventId); + await cachedDelete(eventId); + + // notify timer service of changed events updateTimer([eventId]); + + // notify event loader that rundown size has changed updateChangeNumEvents(); + + // invalidate cache + runtimeCacheStore.invalidate(delayedRundownCacheKey); + + // advice socket subscribers of change sendRefetch(); } @@ -190,78 +229,83 @@ export async function deleteEvent(eventId) { export async function deleteAllEvents() { await DataProvider.clearRundown(); updateTimer(); - updateChangeNumEvents(); - sendRefetch(); + forceReset(); } /** * reorders a given event - * @param {string} eventId - * @param {number} from - * @param {number} to + * @param {string} eventId - ID of event from, for sanity check + * @param {number} from - index of event from + * @param {number} to - index of event to * @returns {Promise} */ -export async function reorderEvent(eventId, from, to) { - const rundown = DataProvider.getRundown(); - const index = rundown.findIndex((event) => event.id === eventId); +export async function reorderEvent(eventId: string, from: number, to: number) { + const reorderedItem = await cachedReorder(eventId, from, to); - if (index !== from) { - throw new Error('ID not found at index'); - } - const [reorderedItem] = rundown.splice(from, 1); - - // reinsert item at to - rundown.splice(to, 0, reorderedItem); - - // save rundown - await DataProvider.setRundown(rundown); + // notify timer service of changed events updateTimer(); + + // advice socket subscribers of change sendRefetch(); return reorderedItem; } +export function _applyDelay( + eventId: string, + rundown: OntimeRundown, +): { + delayIndex: number | null; + updatedRundown: OntimeRundown; +} { + const updatedRundown = [...rundown]; + let delayIndex = null; + let delayValue = 0; + + for (const [index, event] of updatedRundown.entries()) { + // look for delay + if (delayIndex === null) { + if (event.type === SupportedEvent.Delay && event.id === eventId) { + delayValue = event.duration; + delayIndex = index; + + if (delayValue === 0) { + // nothing to apply + break; + } + } + continue; + } + + // once delay is found, apply delay value to all items until block or end + if (event.type === SupportedEvent.Event) { + updatedRundown[index] = { + ...event, + timeStart: Math.max(0, event.timeStart + delayValue), + timeEnd: Math.max(event.duration, event.timeEnd + delayValue), + revision: event.revision + 1, + }; + } else if (event.type === SupportedEvent.Block) { + break; + } + } + + return { delayIndex, updatedRundown }; +} + /** * applies delay value for given event * @param eventId * @returns {Promise} */ export async function applyDelay(eventId: string) { - const rundown = DataProvider.getRundown(); - let delayIndex = null; - let delayValue = 0; - - for (const [index, event] of rundown.entries()) { - // look for delay - if (delayIndex === null) { - if (event.id === eventId && event.type === SupportedEvent.Delay) { - delayValue = event.duration; - delayIndex = index; - } - } - - // apply delay value to all items until block or end - else { - if (event.type === SupportedEvent.Event) { - event.timeStart = Math.max(0, event.timeStart + delayValue); - event.timeEnd = Math.max(event.duration, event.timeStart + delayValue); - event.revision += 1; - } else if (event.type === SupportedEvent.Block) { - break; - } - } - } - + const rundown: OntimeRundown = DataProvider.getRundown(); + const { delayIndex, updatedRundown } = _applyDelay(eventId, rundown); if (delayIndex === null) { throw new Error(`Delay event with ID ${eventId} not found`); } - // delete delay - rundown.splice(delayIndex, 1); - - // update rundown - await DataProvider.setRundown(rundown); - updateTimer(); - sendRefetch(); + await DataProvider.setRundown(updatedRundown); + await deleteEvent(eventId); } /** diff --git a/apps/server/src/services/rundown-service/__tests__/RundownService.test.ts b/apps/server/src/services/rundown-service/__tests__/RundownService.test.ts new file mode 100644 index 000000000..a4b9e928f --- /dev/null +++ b/apps/server/src/services/rundown-service/__tests__/RundownService.test.ts @@ -0,0 +1,319 @@ +import { EndAction, OntimeRundown, SupportedEvent, TimerType } from 'ontime-types'; +import { _applyDelay } from '../RundownService.js'; + +describe('applyDelay()', () => { + it('applies its duration to following events', () => { + const rundown: OntimeRundown = [ + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 4, + id: '659e1', + }, + { + duration: 600000, + type: SupportedEvent.Delay, + revision: 0, + id: '07986', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 4, + id: 'd48c2', + }, + ]; + + const eventId = rundown[1].id; + const { delayIndex, updatedRundown } = _applyDelay(eventId, rundown); + + expect(delayIndex).toBe(1); + // we do not delay delays anymore + expect(updatedRundown.length).toBe(3); + expect(rundown.length).toBe(3); + expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart); + expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart); + expect(updatedRundown[2].timeEnd).toBe(rundown[1].duration + rundown[2].timeEnd); + }); + it('stops propagating on blocks', () => { + const rundown: OntimeRundown = [ + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '659e1', + }, + { + duration: 600000, + type: SupportedEvent.Delay, + revision: 0, + id: '07986', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: 'd48c2', + }, + { + title: '', + type: SupportedEvent.Block, + id: '9870d', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1800000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 2, + id: '2f185', + }, + ]; + + const eventId = rundown[1].id; + const { updatedRundown } = _applyDelay(eventId, rundown); + + expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart); + expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart); + expect(updatedRundown[4].timeStart).toBe(rundown[4].timeStart); + }); + it('only applies given delay', () => { + const rundown: OntimeRundown = [ + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '659e1', + }, + { + duration: 600000, + type: SupportedEvent.Delay, + revision: 0, + id: '07986', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1200000, + duration: 0, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '1c48f', + }, + { + duration: 1200000, + type: SupportedEvent.Delay, + revision: 0, + id: '7db42', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: 'd48c2', + }, + { + title: '', + type: SupportedEvent.Block, + id: '9870d', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1800000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '2f185', + }, + ]; + + const eventId = rundown[1].id; + const { updatedRundown } = _applyDelay(eventId, rundown); + + expect(updatedRundown[0].timeStart).toBe(rundown[0].timeStart); + expect(updatedRundown[2].timeStart).toBe(rundown[1].duration + rundown[2].timeStart); + expect(updatedRundown[4].timeStart).toBe(rundown[1].duration + rundown[4].timeStart); + }); +}); diff --git a/apps/server/src/services/rundown-service/__tests__/delayedRundown.utils.test.ts b/apps/server/src/services/rundown-service/__tests__/delayedRundown.utils.test.ts new file mode 100644 index 000000000..48d9740c9 --- /dev/null +++ b/apps/server/src/services/rundown-service/__tests__/delayedRundown.utils.test.ts @@ -0,0 +1,445 @@ +import { EndAction, OntimeRundown, SupportedEvent, TimerType } from 'ontime-types'; + +import { calculateRuntimeDelays, calculateRuntimeDelaysFrom, getDelayAt } from '../delayedRundown.utils.js'; + +describe('calculateRuntimeDelays', () => { + it('calculates all delays in a given rundown', () => { + const rundown: OntimeRundown = [ + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '659e1', + }, + { + duration: 600000, + type: SupportedEvent.Delay, + revision: 0, + id: '07986', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1200000, + duration: 0, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '1c48f', + }, + { + duration: 1200000, + type: SupportedEvent.Delay, + revision: 0, + id: '7db42', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: 'd48c2', + }, + { + title: '', + type: SupportedEvent.Block, + id: '9870d', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1800000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '2f185', + }, + ]; + + const updatedRundown = calculateRuntimeDelays(rundown); + + expect(rundown.length).toBe(updatedRundown.length); + expect(updatedRundown[0].delay).toBe(0); + expect(updatedRundown[2].delay).toBe(600000); + expect(updatedRundown[4].delay).toBe(600000 + 1200000); + expect(updatedRundown[6].delay).toBe(0); + }); +}); + +describe('getDelayAt()', () => { + const delayedRundown: OntimeRundown = [ + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '659e1', + delay: 0, + }, + { + duration: 600000, + type: SupportedEvent.Delay, + revision: 0, + id: '07986', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1200000, + duration: 0, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '1c48f', + delay: 600000, + }, + { + duration: 1200000, + type: SupportedEvent.Delay, + revision: 0, + id: '7db42', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: 'd48c2', + delay: 1800000, + }, + { + title: '', + type: SupportedEvent.Block, + id: '9870d', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1800000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '2f185', + delay: 0, + }, + ]; + + it('calculates delay in a rundown', () => { + const delayAtStart = getDelayAt(0, delayedRundown); + const delayOnFirstEvent = getDelayAt(2, delayedRundown); + const delayOnSecondEvent = getDelayAt(4, delayedRundown); + const delayOnBlockedEvent = getDelayAt(0, delayedRundown); + + expect(delayAtStart).toBe(0); + expect(delayOnFirstEvent).toBe(600000); + expect(delayOnSecondEvent).toBe(600000 + 1200000); + expect(delayOnBlockedEvent).toBe(0); + }); + it('finds delay before a delay block', () => { + const valueOnFirstDelayBlock = getDelayAt(1, delayedRundown); + const valueOnSecondDelayBlock = getDelayAt(3, delayedRundown); + const valueAfterSecondDelayBlock = getDelayAt(4, delayedRundown); + + expect(valueOnFirstDelayBlock).toBe(0); + expect(valueOnSecondDelayBlock).toBe(600000); + expect(valueAfterSecondDelayBlock).toBe(600000 + 1200000); + }); + it('returns 0 after blocks', () => { + const valueOnBlock = getDelayAt(6, delayedRundown); + expect(valueOnBlock).toBe(0); + }); +}); + +describe('calculateRuntimeDelaysFrom()', () => { + it('updates delays from given id', () => { + const delayedRundown: OntimeRundown = [ + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '659e1', + delay: 0, + }, + { + duration: 600000, + type: SupportedEvent.Delay, + revision: 0, + id: '07986', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1200000, + duration: 0, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '1c48f', + delay: 0, + }, + { + duration: 1200000, + type: SupportedEvent.Delay, + revision: 0, + id: '7db42', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 600000, + timeEnd: 1200000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: 'd48c2', + delay: 1800000, + }, + { + title: '', + type: SupportedEvent.Block, + id: '9870d', + }, + { + title: '', + subtitle: '', + presenter: '', + note: '', + endAction: EndAction.None, + timerType: TimerType.CountDown, + timeStart: 1200000, + timeEnd: 1800000, + duration: 600000, + isPublic: true, + skip: false, + colour: '', + user0: '', + user1: '', + user2: '', + user3: '', + user4: '', + user5: '', + user6: '', + user7: '', + user8: '', + user9: '', + type: SupportedEvent.Event, + revision: 0, + id: '2f185', + delay: 0, + }, + ]; + + const updatedRundown = calculateRuntimeDelaysFrom('07986', delayedRundown); + + // we only update from the 4th on + expect(updatedRundown[0].delay).toBe(0); + // 1 + 3 + expect(updatedRundown[4].delay).toBe(600000 + 1200000); + }); +}); diff --git a/apps/server/src/services/rundown-service/delayedRundown.utils.ts b/apps/server/src/services/rundown-service/delayedRundown.utils.ts new file mode 100644 index 000000000..1d1b2cc6d --- /dev/null +++ b/apps/server/src/services/rundown-service/delayedRundown.utils.ts @@ -0,0 +1,261 @@ +import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types'; +import { DataProvider } from '../../classes/data-provider/DataProvider.js'; +import { getCached, runtimeCacheStore } from '../../stores/cachingStore.js'; +import { isProduction } from '../../setup.js'; +import { deleteAtIndex, insertAtIndex, reorderArray } from '../../utils/arrayUtils.js'; + +/** + * Key of rundown in cache + */ +export const delayedRundownCacheKey = 'delayed-rundown'; + +/** + * Invalidates the cached rundown when an inconsistency is found + * will throw when not in production + * @param errorMessage + */ +export function invalidateFromError(errorMessage = 'Found mismatch between store and cache') { + if (isProduction) { + runtimeCacheStore.invalidate(delayedRundownCacheKey); + } else { + throw new Error(errorMessage); + } +} + +/** + * Returns rundown with calculated delays + * Ensures request goes through the caching layer + */ +export function getDelayedRundown(): OntimeRundown { + function calculateRundown() { + const rundown = DataProvider.getRundown(); + return calculateRuntimeDelays(rundown); + } + + return getCached(delayedRundownCacheKey, calculateRundown); +} + +/** + * Adds an event in the rundown at given index, ensuring replication to delayed rundown cache + * @param eventIndex + * @param event + */ +export async function cachedAdd(eventIndex: number, event: OntimeEvent | OntimeDelay | OntimeBlock) { + // TODO: create wrapper function + const rundown = DataProvider.getRundown(); + const newRundown = insertAtIndex(eventIndex, event, rundown); + + const delayedRundown = getDelayedRundown(); + let newDelayedRundown = insertAtIndex(eventIndex, event, delayedRundown); + + // update delay cache + if (event.type === SupportedEvent.Event) { + // if it is an event, we need its delay + (newDelayedRundown[eventIndex] as OntimeEvent).delay = getDelayAt(eventIndex, newDelayedRundown); + } else { + // if it is a block or delay, we invalidate from here + newDelayedRundown = calculateRuntimeDelaysFromIndex(eventIndex, newDelayedRundown); + } + + runtimeCacheStore.setCached(delayedRundownCacheKey, newDelayedRundown); + // we need to delay updating this to ensure add operation happens on same dataset + await DataProvider.setRundown(newRundown); +} + +/** + * Edits an event in rundown, ensuring replication to delayed rundown cache + * @param eventId + * @param patchObject + */ +export async function cachedEdit( + eventId: string, + patchObject: Partial | Partial | Partial, +) { + const indexInMemory = DataProvider.getIndexOf(eventId); + if (indexInMemory < 0) { + throw new Error('No event with ID found'); + } + + const updatedRundown = DataProvider.getRundown(); + const newEvent = { ...updatedRundown[indexInMemory], ...patchObject }; + if (newEvent.type === SupportedEvent.Event) { + newEvent.revision++; + } + // @ts-expect-error -- this merge is safe + updatedRundown[indexInMemory] = newEvent; + + let newDelayedRundown = getDelayedRundown(); + if (newDelayedRundown?.[indexInMemory].id !== newEvent.id) { + invalidateFromError(); + } else { + // @ts-expect-error -- this merge is safe + newDelayedRundown[indexInMemory] = newEvent; + if (newEvent.type === SupportedEvent.Event) { + (newDelayedRundown[indexInMemory] as OntimeEvent).delay = getDelayAt(indexInMemory, newDelayedRundown); + } else if (newEvent.type === SupportedEvent.Delay) { + // blocks have no reason to change the rundown, from delays we need to recalculate + newDelayedRundown = calculateRuntimeDelaysFromIndex(indexInMemory, newDelayedRundown); + } + + runtimeCacheStore.setCached(delayedRundownCacheKey, newDelayedRundown); + } + + // we need to delay updating this to ensure edit operation happens on same dataset + await DataProvider.setRundown(updatedRundown); + + return newEvent; +} + +/** + * Deletes an event with given id from rundown, ensuring replication to delayed rundown cache + * @param eventId + */ +export async function cachedDelete(eventId: string) { + const eventIndex = DataProvider.getIndexOf(eventId); + let delayedRundown = getDelayedRundown(); + + if (eventIndex < 0) { + if (delayedRundown.findIndex((event) => event.id === eventId) >= 0) { + invalidateFromError(); + } + return; + } + + let updatedRundown = DataProvider.getRundown(); + const eventType = updatedRundown[eventIndex].type; + updatedRundown = deleteAtIndex(eventIndex, updatedRundown); + if (eventId !== delayedRundown[eventIndex].id) { + invalidateFromError(); + } else { + delayedRundown = deleteAtIndex(eventIndex, delayedRundown); + if (eventType === SupportedEvent.Delay || eventType === SupportedEvent.Block) { + // for events, we do not have to worry + // the following event, would have taken the place of the deleted event by now + delayedRundown = calculateRuntimeDelaysFromIndex(eventIndex, delayedRundown); + } + runtimeCacheStore.setCached(delayedRundownCacheKey, delayedRundown); + } + // we need to delay updating this to ensure edit operation happens on same dataset + await DataProvider.setRundown(updatedRundown); +} + +/** + * Reorders an event in the rundown, ensuring replication to delayed rundown cache + * @param eventId + * @param from + * @param to + */ +export async function cachedReorder(eventId: string, from: number, to: number) { + const indexCheck = DataProvider.getIndexOf(eventId); + if (indexCheck !== from) { + invalidateFromError(); + throw new Error('ID not found at index'); + } + + let updatedRundown = DataProvider.getRundown(); + const reorderedEvent = updatedRundown[from]; + updatedRundown = reorderArray(updatedRundown, from, to); + + const delayedRundown = getDelayedRundown(); + if (eventId !== delayedRundown[from].id) { + invalidateFromError(); + } else { + // TODO: could we be more granular about updates + // I fear we need to update both from and to, which could signify more iterations + runtimeCacheStore.invalidate(delayedRundownCacheKey); + } + + // we need to delay updating this to ensure edit operation happens on same dataset + await DataProvider.setRundown(updatedRundown); + + return reorderedEvent; +} + +/** + * Calculates all delays in a given rundown + * @param rundown + */ +export function calculateRuntimeDelays(rundown: OntimeRundown) { + let accumulatedDelay = 0; + const updatedRundown = [...rundown]; + + for (const [index, event] of updatedRundown.entries()) { + if (event.type === SupportedEvent.Delay) { + accumulatedDelay += event.duration; + } else if (event.type === SupportedEvent.Block) { + accumulatedDelay = 0; + } else if (event.type === SupportedEvent.Event) { + updatedRundown[index] = { + ...event, + delay: accumulatedDelay, + }; + } + } + return updatedRundown; +} + +/** + * Calculate delays in rundown from a given index + * @param eventIndex + * @param rundown + */ +export function calculateRuntimeDelaysFromIndex(eventIndex: number, rundown: OntimeRundown) { + if (eventIndex === -1) { + throw new Error('ID not found at index'); + } + + let accumulatedDelay = getDelayAt(eventIndex, rundown); + const updatedRundown = [...rundown]; + + for (let i = eventIndex; i < rundown.length; i++) { + const event = rundown[i]; + if (event.type === SupportedEvent.Delay) { + accumulatedDelay += event.duration; + } else if (event.type === SupportedEvent.Block) { + if (i === eventIndex) { + accumulatedDelay = 0; + } else { + break; + } + } else if (event.type === SupportedEvent.Event) { + updatedRundown[i] = { + ...event, + delay: accumulatedDelay, + }; + } + } + return updatedRundown; +} + +/** + * Calculate delays in rundown from an event with given id + * @param eventId + * @param rundown + */ +export function calculateRuntimeDelaysFrom(eventId: string, rundown: OntimeRundown) { + const index = rundown.findIndex((event) => event.id === eventId); + return calculateRuntimeDelaysFromIndex(index, rundown); +} + +/** + * Calculates delay to an event at a given index + * @param eventIndex + * @param rundown + */ +export function getDelayAt(eventIndex: number, rundown: OntimeRundown): number { + if (eventIndex < 1) { + return 0; + } + + // we need to check the event before + const event = rundown[eventIndex - 1]; + + if (event.type === SupportedEvent.Delay) { + return event.duration + getDelayAt(eventIndex - 1, rundown); + } else if (event.type === SupportedEvent.Block) { + return 0; + } else if (event.type === SupportedEvent.Event) { + return event.delay ?? 0; + } + return 0; +} diff --git a/apps/server/src/stores/__tests__/cachingStore.test.ts b/apps/server/src/stores/__tests__/cachingStore.test.ts new file mode 100644 index 000000000..6f91878a0 --- /dev/null +++ b/apps/server/src/stores/__tests__/cachingStore.test.ts @@ -0,0 +1,67 @@ +import { runtimeCacheStore } from '../cachingStore.js'; + +describe('cachingStore()', () => { + beforeEach(() => { + runtimeCacheStore.clear(); // Clear the cache before each test + }); + + it('should check if an item is cached', () => { + // Add an item to the cache + runtimeCacheStore.setCached('key', 'value'); + + // Check if the item is cached + expect(runtimeCacheStore.checkCached('key')).toBe(true); + expect(runtimeCacheStore.checkCached('non-existent-key')).toBe(false); + }); + + it('should get an item from the cache', () => { + // Add an item to the cache + runtimeCacheStore.setCached('key', 'value'); + + // Get the item from the cache + const result = runtimeCacheStore.getCached('key', () => 'default-value'); + + // Check the returned value + expect(result).toBe('value'); + }); + + it('should retrieve default value when item is not cached', () => { + // Get an item that is not in the cache + const result = runtimeCacheStore.getCached('non-existent-key', () => 'default-value'); + + // Check the returned value + expect(result).toBe('default-value'); + }); + + it('should set an item in the cache', () => { + // Set an item in the cache + runtimeCacheStore.setCached('key', 'value'); + + // Check if the item is cached + expect(runtimeCacheStore.checkCached('key')).toBe(true); + }); + + it('should invalidate an item in the cache', () => { + // Add an item to the cache + runtimeCacheStore.setCached('key', 'value'); + + // Invalidate the item + runtimeCacheStore.invalidate('key'); + + // Check if the item is no longer cached + expect(runtimeCacheStore.checkCached('key')).toBe(false); + }); + + it('should clear the cache', () => { + // Add items to the cache + runtimeCacheStore.setCached('key1', 'value1'); + runtimeCacheStore.setCached('key2', 'value2'); + + // Clear the cache + runtimeCacheStore.clear(); + + // Check if the cache is empty + expect(runtimeCacheStore.checkCached('key1')).toBe(false); + expect(runtimeCacheStore.checkCached('key2')).toBe(false); + }); +}); diff --git a/apps/server/src/stores/cachingStore.ts b/apps/server/src/stores/cachingStore.ts new file mode 100644 index 000000000..a5ac8d05d --- /dev/null +++ b/apps/server/src/stores/cachingStore.ts @@ -0,0 +1,47 @@ +interface CacheData { + data: unknown; +} + +const runtimeCache: Map = new Map(); + +export function checkCached(key: string): boolean { + return runtimeCache.has(key); +} + +export function getCached(key: string, callback: () => T): T { + if (!runtimeCache.has(key)) { + try { + const data = callback(); + runtimeCache.set(key, { data }); + } catch (error) { + console.log(`Failed retrieving data from callback: ${error}`); + } + } + + return runtimeCache.get(key).data as T; +} + +export function setCached(key: string, value: T): T { + runtimeCache.set(key, { data: value }); + return runtimeCache.get(key).data as T; +} + +export function invalidate(key) { + runtimeCache.delete(key); +} + +export function clear() { + runtimeCache.clear(); +} + +function createCacheStore() { + return { + checkCached, + getCached, + setCached, + invalidate, + clear, + }; +} + +export const runtimeCacheStore = createCacheStore(); diff --git a/apps/server/src/utils/__tests__/arrayUtils.tests.ts b/apps/server/src/utils/__tests__/arrayUtils.tests.ts new file mode 100644 index 000000000..947fc3a21 --- /dev/null +++ b/apps/server/src/utils/__tests__/arrayUtils.tests.ts @@ -0,0 +1,54 @@ +import { insertAtIndex, reorderArray } from '../arrayUtils.js'; + +describe('insertAtIndex', () => { + it('should insert an item at the beginning of the array', () => { + const array = [2, 3, 4]; + const result = insertAtIndex(0, 1, array); + expect(result).toEqual([1, 2, 3, 4]); + }); + + it('should insert an item at the end of the array', () => { + const array = [1, 2, 3]; + const result = insertAtIndex(3, 4, array); + expect(result).toEqual([1, 2, 3, 4]); + }); + + it('should insert an item in the middle of the array', () => { + const array = [1, 2, 4]; + const result = insertAtIndex(2, 3, array); + expect(result).toEqual([1, 2, 3, 4]); + }); + + it('should return a new array and not modify the original array', () => { + const array = [1, 2, 3]; + const result = insertAtIndex(1, 5, array); + expect(result).toEqual([1, 5, 2, 3]); + expect(array).toEqual([1, 2, 3]); // Original array should remain unchanged + }); +}); + +describe('reorderArray', () => { + it('should reorder an item in the array', () => { + const array = ['a', 'b', 'c', 'd']; + const result = reorderArray(array, 1, 3); + expect(result).toEqual(['a', 'c', 'd', 'b']); + }); + + it('should return the original array if fromIndex and toIndex are the same', () => { + const array = ['a', 'b', 'c']; + const result = reorderArray(array, 1, 1); + expect(result).toEqual(array); + }); + + it('should handle reordering to the beginning of the array', () => { + const array = ['a', 'b', 'c']; + const result = reorderArray(array, 2, 0); + expect(result).toEqual(['c', 'a', 'b']); + }); + + it('should handle reordering to the end of the array', () => { + const array = ['a', 'b', 'c']; + const result = reorderArray(array, 0, 2); + expect(result).toEqual(['b', 'c', 'a']); + }); +}); diff --git a/apps/server/src/utils/arrayUtils.ts b/apps/server/src/utils/arrayUtils.ts new file mode 100644 index 000000000..49cb8780a --- /dev/null +++ b/apps/server/src/utils/arrayUtils.ts @@ -0,0 +1,50 @@ +/** + * Inserts an item in an array at a given index + * @param index + * @param item + * @param array + */ +export function insertAtIndex(index: number, item: T, array: T[]): T[] { + const modifiedArray = [...array]; + + // Insert at beginning + if (index === 0) { + modifiedArray.unshift(item); + } + + // insert at end + else if (index >= modifiedArray.length) { + modifiedArray.push(item); + } + + // insert in the middle + else { + modifiedArray.splice(index, 0, item); + } + + return modifiedArray; +} + +/** + * Deletes array element at a given index + * @param index + * @param array + */ +export function deleteAtIndex(index: number, array: T[]) { + return array.filter((_, i) => i !== index); +} + +export function reorderArray(array: T[], fromIndex: number, toIndex: number) { + if (fromIndex === toIndex) { + return array; // No change needed, return the original array + } + + const modifiedArray = [...array]; + + // delete in from + const [reorderedItem] = modifiedArray.splice(fromIndex, 1); + + // reinsert item at to + modifiedArray.splice(toIndex, 0, reorderedItem); + return modifiedArray; +} diff --git a/e2e/tests/features/202-cuesheet.spec.ts b/e2e/tests/features/202-cuesheet.spec.ts new file mode 100644 index 000000000..1c9547252 --- /dev/null +++ b/e2e/tests/features/202-cuesheet.spec.ts @@ -0,0 +1,62 @@ +import { expect, test } from '@playwright/test'; +import fs from 'fs'; + +test('cuesheet displays events and exports csv', async ({ page }) => { + // ensure elements exist in editor + await page.goto('http://localhost:4001/editor'); + await page.getByText('First test event').click(); + await page.getByText('Second test event').click(); + await page.getByText('Third test event').click(); + await page.getByText('Add timeSubtract timeApplyCancel').click(); + await page.getByText('Lunch').click(); + + // same elements in cuesheet + await page.goto('http://localhost:4001/cuesheet'); + await page.getByText('All about Carlos demo event').click(); + await page.getByRole('cell', { name: 'First test event' }).click(); + await page.getByRole('cell', { name: 'Second test event' }).click(); + await page.getByRole('cell', { name: 'Third test event' }).click(); + await page.getByRole('cell', { name: '+10 min' }).click(); + await page.getByRole('cell', { name: 'Lunch' }).click(); + const downloadPromise = page.waitForEvent('download'); + await page.getByTestId('cuesheet').getByText('CSV').click(); + + // From here we test the CSV download feature + + function validateCSV(contents) { + // We should try to keep this in sync with the implementation over at cuesheetUtils.ts + const expectedHeader = ['All about Carlos demo event', 'www.getontime.no']; + const expectedColumns = [ + 'Time Start', + 'Time End', + 'Event Title', + 'Presenter Name', + 'Event Subtitle', + 'Public', + 'Note', + 'Colour', + 'End Action', + 'Timer Type', + 'Skip', + 'user0', + 'user1', + 'user2', + 'user3', + 'user4', + 'user5', + 'user6', + 'user7', + 'user8', + 'user9', + ]; + const expectedValues = ['First test event', 'Second test event', 'Third test event', 'Lunch']; + + const allExpected = [...expectedHeader, ...expectedColumns, ...expectedValues]; + return allExpected.every((value) => contents.includes(value)); + } + + const download = await downloadPromise; + const contents = await fs.promises.readFile(await download.path(), 'utf-8'); + expect(contents).toContain('All about Carlos demo event'); + expect(validateCSV(contents)).toBe(true); +}); diff --git a/packages/types/src/definitions/core/OntimeEvent.type.ts b/packages/types/src/definitions/core/OntimeEvent.type.ts index 8b0342b1a..a5f4e8b79 100644 --- a/packages/types/src/definitions/core/OntimeEvent.type.ts +++ b/packages/types/src/definitions/core/OntimeEvent.type.ts @@ -30,8 +30,8 @@ export type OntimeEvent = OntimeBaseEvent & { subtitle: string; presenter: string; note: string; - endAction: EndAction, - timerType: TimerType, + endAction: EndAction; + timerType: TimerType; timeStart: number; timeEnd: number; duration: number; @@ -49,4 +49,5 @@ export type OntimeEvent = OntimeBaseEvent & { user8: string; user9: string; revision: number; + delay?: number; // calculated at runtime }; diff --git a/packages/types/src/definitions/core/Rundown.type.ts b/packages/types/src/definitions/core/Rundown.type.ts index 8f018b2d2..9a4890e50 100644 --- a/packages/types/src/definitions/core/Rundown.type.ts +++ b/packages/types/src/definitions/core/Rundown.type.ts @@ -1,4 +1,7 @@ -import { OntimeBlock, OntimeDelay, OntimeEvent } from './OntimeEvent.type'; +import { OntimeBlock, OntimeDelay, OntimeEvent } from './OntimeEvent.type.js'; export type OntimeRundownEntry = OntimeDelay | OntimeBlock | OntimeEvent; export type OntimeRundown = OntimeRundownEntry[]; + +// we need to create a manual union type since keys cannot be used in type unions +export type OntimeEntryCommonKeys = keyof OntimeEvent | keyof OntimeDelay | keyof OntimeBlock; diff --git a/packages/types/src/definitions/runtime/Logger.type.ts b/packages/types/src/definitions/runtime/Logger.type.ts index 1ff25e82f..0a903f97f 100644 --- a/packages/types/src/definitions/runtime/Logger.type.ts +++ b/packages/types/src/definitions/runtime/Logger.type.ts @@ -16,3 +16,12 @@ export type LogMessage = { type: 'ontime-log'; payload: Log; }; + +export enum LogOrigin { + Client = 'CLIENT', + Playback = 'PLAYBACK', + Rx = 'RX', + Server = 'SERVER', + Tx = 'TX', + User = 'USER' +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 302a3fe92..8dd8526c4 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -10,11 +10,11 @@ import { OntimeEvent, SupportedEvent, } from './definitions/core/OntimeEvent.type.js'; -import { OntimeRundown, OntimeRundownEntry } from './definitions/core/Rundown.type.js'; +import { OntimeEntryCommonKeys, OntimeRundown, OntimeRundownEntry } from './definitions/core/Rundown.type.js'; import { OSCSettings, OscSubscription, OscSubscriptionOptions } from './definitions/core/OscSettings.type.js'; import { Playback } from './definitions/runtime/Playback.type.js'; import { Loaded } from './definitions/runtime/Playlist.type.js'; -import { Log, LogLevel, LogMessage } from './definitions/runtime/Logger.type.js'; +import { Log, LogLevel, LogMessage, LogOrigin } from './definitions/runtime/Logger.type.js'; import { RuntimeStore } from './definitions/runtime/RuntimeStore.type.js'; import { Settings } from './definitions/core/Settings.type.js'; import { TimerLifeCycle } from './definitions/core/TimerLifecycle.type.js'; @@ -32,7 +32,7 @@ export { TimerType }; export { EndAction }; export { SupportedEvent }; export type { OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent }; -export type { OntimeRundown, OntimeRundownEntry }; +export type { OntimeEntryCommonKeys, OntimeRundown, OntimeRundownEntry }; // ---> Event export type { EventData }; @@ -57,6 +57,7 @@ export type { OscSubscription, OSCSettings, OscSubscriptionOptions }; // SERVER RUNTIME export { LogLevel }; export type { Log, LogMessage }; +export { LogOrigin }; export { Playback }; export { TimerLifeCycle }; diff --git a/packages/utils/src/date-utils/millisToString.ts b/packages/utils/src/date-utils/millisToString.ts index 7820eba17..0a35a0dd8 100644 --- a/packages/utils/src/date-utils/millisToString.ts +++ b/packages/utils/src/date-utils/millisToString.ts @@ -8,7 +8,7 @@ import { DateTime } from 'luxon'; * @returns {string} String representing time 00:12:02 */ export function millisToString(millis: number | null, showSeconds = true, fallback = '...') { - if (millis === null) { + if (millis == null) { return fallback; } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e3da0fb6..fff533d3c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -30,7 +30,7 @@ importers: apps/client: specifiers: - '@chakra-ui/react': ^2.5.5 + '@chakra-ui/react': ^2.7.0 '@dnd-kit/core': ^6.0.8 '@dnd-kit/sortable': ^7.0.2 '@dnd-kit/utilities': ^3.2.1 @@ -43,19 +43,19 @@ importers: '@tanstack/eslint-plugin-query': ^4.26.2 '@tanstack/react-query': ^4.28.0 '@tanstack/react-query-devtools': ^4.29.0 + '@tanstack/react-table': ^8.9.2 '@testing-library/jest-dom': ^5.16.5 '@testing-library/react': ^13.1.1 '@testing-library/user-event': ^14.1.1 '@types/color': ^3.0.3 '@types/luxon': ^3.2.0 - '@types/prop-types': ^15.7.5 '@types/react': ^18.0.26 '@types/react-dom': ^18.0.10 '@types/testing-library__jest-dom': ^5.14.5 '@typescript-eslint/eslint-plugin': ^5.48.1 '@typescript-eslint/parser': ^5.48.1 '@vitejs/plugin-react': ^3.0.1 - autosize: ^5.0.2 + autosize: ^6.0.1 axios: ^1.2.0 color: ^4.2.3 csv-stringify: ^6.2.3 @@ -73,7 +73,6 @@ importers: ontime-types: workspace:* ontime-utils: workspace:* prettier: ^2.8.3 - prop-types: ^15.8.1 react: ^18.2.0 react-colorful: ^5.6.1 react-dom: ^18.2.0 @@ -81,11 +80,7 @@ importers: react-hook-form: ^7.43.5 react-qr-code: ^2.0.11 react-router-dom: ^6.3.0 - react-table: ^7.7.0 sass: ^1.57.1 - stylelint: ^14.16.1 - stylelint-config-prettier: ^9.0.4 - stylelint-config-standard-scss: ^6.1.0 typeface-open-sans: ^1.1.13 typescript: ^4.9.4 vite: ^4.3.1 @@ -96,7 +91,7 @@ importers: web-vitals: ^3.1.1 zustand: ^4.3.6 dependencies: - '@chakra-ui/react': 2.5.5_tlyz7qwuzzubgapow55lw5vriq + '@chakra-ui/react': 2.7.0_tlyz7qwuzzubgapow55lw5vriq '@dnd-kit/core': 6.0.8_biqbaboplfbrettd7655fr4n2y '@dnd-kit/sortable': 7.0.2_52scne4zmdeyjh2otzkgz2xfvu '@dnd-kit/utilities': 3.2.1_react@18.2.0 @@ -107,7 +102,8 @@ importers: '@sentry/tracing': 7.46.0 '@tanstack/react-query': 4.28.0_biqbaboplfbrettd7655fr4n2y '@tanstack/react-query-devtools': 4.29.0_q4teel2yjbizrm4naiaqcdpjum - autosize: 5.0.2 + '@tanstack/react-table': 8.9.2_biqbaboplfbrettd7655fr4n2y + autosize: 6.0.1 axios: 1.2.2 color: 4.2.3 csv-stringify: 6.2.3 @@ -120,7 +116,6 @@ importers: react-hook-form: 7.43.5_react@18.2.0 react-qr-code: 2.0.11_react@18.2.0 react-router-dom: 6.6.2_biqbaboplfbrettd7655fr4n2y - react-table: 7.8.0_react@18.2.0 typeface-open-sans: 1.1.13 web-vitals: 3.1.1 zustand: 4.3.6_react@18.2.0 @@ -132,7 +127,6 @@ importers: '@testing-library/user-event': 14.4.3 '@types/color': 3.0.3 '@types/luxon': 3.2.0 - '@types/prop-types': 15.7.5 '@types/react': 18.0.26 '@types/react-dom': 18.0.10 '@types/testing-library__jest-dom': 5.14.5 @@ -151,11 +145,7 @@ importers: ontime-types: link:../../packages/types ontime-utils: link:../../packages/utils prettier: 2.8.3 - prop-types: 15.8.1 sass: 1.57.1 - stylelint: 14.16.1 - stylelint-config-prettier: 9.0.4_stylelint@14.16.1 - stylelint-config-standard-scss: 6.1.0_stylelint@14.16.1 typescript: 4.9.4 vite: 4.3.1_sass@1.57.1 vite-plugin-compression2: 0.9.0 @@ -497,6 +487,13 @@ packages: regenerator-runtime: 0.13.11 dev: false + /@babel/runtime/7.22.5: + resolution: {integrity: sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.13.11 + dev: false + /@babel/template/7.20.7: resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==} engines: {node: '>=6.9.0'} @@ -532,36 +529,36 @@ packages: '@babel/helper-validator-identifier': 7.19.1 to-fast-properties: 2.0.0 - /@chakra-ui/accordion/2.1.11_i6fhfa2wvtxv5b2jykryjj4lam: - resolution: {integrity: sha512-mfVPmqETp9pyRDHJ33AdF19oHv/LyxVzQJtlxUByuvs8Cj9QQZ2LQLg5kejm+b3mj03A7A6yfbuo3RNaI4Bhsg==} + /@chakra-ui/accordion/2.2.0_xdwvxhu5ub5hflmtgd6mbsauaa: + resolution: {integrity: sha512-2IK1iLzTZ22u8GKPPPn65mqJdZidn4AvkgAbv17ISdKA07VHJ8jSd4QF1T5iCXjKfZ0XaXozmhP4kDhjwF2IbQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: '@chakra-ui/descendant': 3.0.14_react@18.2.0 - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-use-controllable-state': 2.0.8_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba '@chakra-ui/transition': 2.0.16_bdsblprzt47ebwyq2agncnzwpe framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 dev: false - /@chakra-ui/alert/2.1.0_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/alert/2.1.0_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-OcfHwoXI5VrmM+tHJTHT62Bx6TfyfCxSa0PWUOueJzSyhlUOKBND5we6UtrOB7D0jwX45qKKEDJOLG5yCG21jQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/spinner': 2.0.13_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/spinner': 2.0.13_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -569,21 +566,21 @@ packages: resolution: {integrity: sha512-pKfOS/mztc4sUXHNc8ypJ1gPWSolWT770jrgVRfolVbYlki8y5Y+As996zMF6k5lewTu6j9DQequ7Cc9a69IVQ==} dev: false - /@chakra-ui/avatar/2.2.8_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-uBs9PMrqyK111tPIYIKnOM4n3mwgKqGpvYmtwBnnbQLTNLg4gtiWWVbpTuNMpyu1av0xQYomjUt8Doed8w6p8g==} + /@chakra-ui/avatar/2.2.11_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-CJFkoWvlCTDJTUBrKA/aVyG5Zz6TBEIVmmsJtqC6VcQuVDTxkWod8ruXnjb0LT2DUveL7xR5qZM9a5IXcsH3zg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/image': 2.0.15_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/image': 2.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-children-utils': 2.0.6_react@18.2.0 '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/breadcrumb/2.1.5_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/breadcrumb/2.1.5_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-p3eQQrHQBkRB69xOmNyBJqEdfCrMt+e0eOH+Pm/DjFWfIVIbnIaFbmDCeWClqlLa21Ypc6h1hR9jEmvg8kmOog==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -592,7 +589,7 @@ packages: '@chakra-ui/react-children-utils': 2.0.6_react@18.2.0 '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -602,7 +599,7 @@ packages: '@chakra-ui/shared-utils': 2.0.5 dev: false - /@chakra-ui/button/2.0.18_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/button/2.0.18_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-E3c99+lOm6ou4nQVOTLkG+IdOPMjsQK+Qe7VyP8A/xeAMFONuibrWPRPpprr4ZkB4kEoLMfNuyH2+aEza3ScUA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -611,29 +608,29 @@ packages: '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/spinner': 2.0.13_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/spinner': 2.0.13_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/card/2.1.6_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/card/2.1.6_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-fFd/WAdRNVY/WOSQv4skpy0WeVhhI0f7dTY1Sm0jVl0KLmuP/GnpsWtKtqWjNcV00K963EXDyhlk6+9oxbP4gw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/checkbox/2.2.14_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-uqo6lFWLqYBujPglrvRhTAErtuIXpmdpc5w0W4bjK7kyvLhxOpUh1hlDb2WoqlNpfRn/OaNeF6VinPnf9BJL8w==} + /@chakra-ui/checkbox/2.2.15_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-Ju2yQjX8azgFa5f6VLPuwdGYobZ+rdbcYqjiks848JvPc75UsPhpS05cb4XlrKT7M16I8txDA5rPJdqqFicHCA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-callback-ref': 2.0.7_react@18.2.0 @@ -642,8 +639,8 @@ packages: '@chakra-ui/react-use-safe-layout-effect': 2.0.5_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba - '@chakra-ui/visually-hidden': 2.0.15_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/visually-hidden': 2.0.15_62ez5scglruzijw4rniqq4y54y '@zag-js/focus-visible': 0.2.2 react: 18.2.0 dev: false @@ -658,14 +655,14 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/close-button/2.0.17_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/close-button/2.0.17_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-05YPXk456t1Xa3KpqTrvm+7smx+95dmaPiwjiBN3p7LHUQVHJd8ZXSDB0V+WKi419k3cVQeJUdU/azDO2f40sw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -678,13 +675,13 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/control-box/2.0.13_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/control-box/2.0.13_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-FEyrU4crxati80KUF/+1Z1CU3eZK6Sa0Yv7Z/ydtz9/tvGblXW9NFanoomXAOvcIFLbaLQPPATm9Gmpr7VG05A==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -699,8 +696,8 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/css-reset/2.1.1_3og6jmu6wvzuytygvdoxepq3x4: - resolution: {integrity: sha512-jwEOfIAWmQsnChHQTW/eRE+dfE4MjmhvSvoUug5nkV1pI7veC/20noFlIZxzi82EbiQI8Fs0+Jnusgxr2yaOHA==} + /@chakra-ui/css-reset/2.1.2_3og6jmu6wvzuytygvdoxepq3x4: + resolution: {integrity: sha512-4ySTLd+3iRpp4lX0yI9Yo2uQm2f+qwYGNOZF0cNcfN+4UJCd3IsaWxYRR/Anz+M51NVldZbYzC+TEYC/kpJc4A==} peerDependencies: '@emotion/react': '>=10.0.35' react: '>=18' @@ -719,12 +716,12 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/dom-utils/2.0.6: - resolution: {integrity: sha512-PVtDkPrDD5b8aoL6Atg7SLjkwhWb7BwMcLOF1L449L3nZN+DAO3nyAh6iUhZVJyunELj9d0r65CDlnMREyJZmA==} + /@chakra-ui/dom-utils/2.1.0: + resolution: {integrity: sha512-ZmF2qRa1QZ0CMLU8M1zCfmw29DmPNtfjR9iTo74U5FPr3i1aoAh7fbJ4qAlZ197Xw9eAW28tvzQuoVWeL5C7fQ==} dev: false - /@chakra-ui/editable/2.0.21_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-oYuXbHnggxSYJN7P9Pn0Scs9tPC91no4z1y58Oe+ILoJKZ+bFAEHtL7FEISDNJxw++MEukeFu7GU1hVqmdLsKQ==} + /@chakra-ui/editable/3.0.0_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-q/7C/TM3iLaoQKlEiM8AY565i9NoaXtS6N6N4HWIEL5mZJPbMeHKxrCHUZlHxYuQJqFOGc09ZPD9fAFx1GkYwQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' @@ -738,7 +735,7 @@ packages: '@chakra-ui/react-use-safe-layout-effect': 2.0.5_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -746,35 +743,35 @@ packages: resolution: {integrity: sha512-IGM/yGUHS+8TOQrZGpAKOJl/xGBrmRYJrmbHfUE7zrG3PpQyXvbLDP1M+RggkCFVgHlJi2wpYIf0QtQlU0XZfw==} dev: false - /@chakra-ui/focus-lock/2.0.16_kzbn2opkn2327fwg5yzwzya5o4: - resolution: {integrity: sha512-UuAdGCPVrCa1lecoAvpOQD7JFT7a9RdmhKWhFt5ioIcekSLJcerdLHuuL3w0qz//8kd1/SOt7oP0aJqdAJQrCw==} + /@chakra-ui/focus-lock/2.0.17_kzbn2opkn2327fwg5yzwzya5o4: + resolution: {integrity: sha512-V+m4Ml9E8QY66DUpHX/imInVvz5XJ5zx59Tl0aNancXgeVY1Rt/ZdxuZdPLCAmPC/MF3GUOgnEA+WU8i+VL6Gw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/dom-utils': 2.0.6 + '@chakra-ui/dom-utils': 2.1.0 react: 18.2.0 react-focus-lock: 2.9.4_kzbn2opkn2327fwg5yzwzya5o4 transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/form-control/2.0.18_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/form-control/2.0.18_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-I0a0jG01IAtRPccOXSNugyRdUAe8Dy40ctqedZvznMweOXzbMCF1m+sHPLdWeWC/VI13VoAispdPY0/zHOdjsQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/hooks/2.1.6_react@18.2.0: - resolution: {integrity: sha512-oMSOeoOF6/UpwTVlDFHSROAA4hPY8WgJ0erdHs1ZkuwAwHv7UzjDkvrb6xYzAAH9qHoFzc5RIBm6jVoh3LCc+Q==} + /@chakra-ui/hooks/2.2.0_react@18.2.0: + resolution: {integrity: sha512-GZE64mcr20w+3KbCUPqQJHHmiFnX5Rcp8jS3YntGA4D5X2qU85jka7QkjfBwv/iduZ5Ei0YpCMYGCpi91dhD1Q==} peerDependencies: react: '>=18' dependencies: @@ -785,57 +782,57 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/icon/3.0.16_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/icon/3.0.16_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-RpA1X5Ptz8Mt39HSyEIW1wxAz2AXyf9H0JJ5HVx/dBdMZaGMDJ0HyyPBVci0m4RCoJuyG1HHG/DXJaVfUTVAeg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/image/2.0.15_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-w2rElXtI3FHXuGpMCsSklus+pO1Pl2LWDwsCGdpBQUvGFbnHfl7MftQgTlaGHeD5OS95Pxva39hKrA2VklKHiQ==} + /@chakra-ui/image/2.0.16_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-iFypk1slgP3OK7VIPOtkB0UuiqVxNalgA59yoRM43xLIeZAEZpKngUVno4A2kFS61yKN0eIY4hXD3Xjm+25EJA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/react-use-safe-layout-effect': 2.0.5_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/input/2.0.21_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-AIWjjg6MgcOtlvKmVoZfPPfgF+sBSWL3Zq2HSCAMvS6h7jfxz/Xv0UTFGPk5F4Wt0YHT7qMySg0Jsm0b78HZJg==} + /@chakra-ui/input/2.0.22_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-dCIC0/Q7mjZf17YqgoQsnXn0bus6vgriTRn8VmxOc+WcVl+KBSTBWujGrS5yu85WIFQ0aeqQvziDnDQybPqAbA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/object-utils': 2.0.8 + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/object-utils': 2.1.0 '@chakra-ui/react-children-utils': 2.0.6_react@18.2.0 '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/layout/2.1.18_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-F4Gh2e+DGdaWdWT5NZduIFD9NM7Bnuh8sXARFHWPvIu7yvAwZ3ddqC9GK4F3qUngdmkJxDLWQqRSwSh96Lxbhw==} + /@chakra-ui/layout/2.2.0_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-WvfsWQjqzbCxv7pbpPGVKxj9eQr7MC2i37ag4Wn7ClIG7uPuwHYTUWOnjnu27O3H/zA4cRVZ4Hs3GpSPbojZFQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/breakpoint-utils': 2.0.8 - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/object-utils': 2.0.8 + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/object-utils': 2.1.0 '@chakra-ui/react-children-utils': 2.0.6_react@18.2.0 '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -851,7 +848,7 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/media-query/3.2.12_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/media-query/3.2.12_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-8pSLDf3oxxhFrhd40rs7vSeIBfvOmIKHA7DJlGUC/y+9irD24ZwgmCtFnn+y3gI47hTJsopbSX+wb8nr7XPswA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -860,12 +857,12 @@ packages: '@chakra-ui/breakpoint-utils': 2.0.8 '@chakra-ui/react-env': 3.0.0_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/menu/2.1.12_i6fhfa2wvtxv5b2jykryjj4lam: - resolution: {integrity: sha512-ylNK1VJlr/3/EGg9dLPZ87cBJJjeiYXeU/gOAphsKXMnByrXWhbp4YVnyyyha2KZ0zEw0aPU4nCZ+A69aT9wrg==} + /@chakra-ui/menu/2.1.15_xdwvxhu5ub5hflmtgd6mbsauaa: + resolution: {integrity: sha512-+1fh7KBKZyhy8wi7Q6nQAzrvjM6xggyhGMnSna0rt6FJVA2jlfkjb5FozyIVPnkfJKjkKd8THVhrs9E7pHNV/w==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' @@ -874,58 +871,58 @@ packages: '@chakra-ui/clickable': 2.0.14_react@18.2.0 '@chakra-ui/descendant': 3.0.14_react@18.2.0 '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/popper': 3.0.13_react@18.2.0 + '@chakra-ui/popper': 3.0.14_react@18.2.0 '@chakra-ui/react-children-utils': 2.0.6_react@18.2.0 '@chakra-ui/react-context': 2.0.8_react@18.2.0 - '@chakra-ui/react-use-animation-state': 2.0.8_react@18.2.0 + '@chakra-ui/react-use-animation-state': 2.0.9_react@18.2.0 '@chakra-ui/react-use-controllable-state': 2.0.8_react@18.2.0 '@chakra-ui/react-use-disclosure': 2.0.8_react@18.2.0 - '@chakra-ui/react-use-focus-effect': 2.0.9_react@18.2.0 + '@chakra-ui/react-use-focus-effect': 2.0.11_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 - '@chakra-ui/react-use-outside-click': 2.0.7_react@18.2.0 + '@chakra-ui/react-use-outside-click': 2.1.0_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba '@chakra-ui/transition': 2.0.16_bdsblprzt47ebwyq2agncnzwpe framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 dev: false - /@chakra-ui/modal/2.2.11_tmy6ru2c6di5zbchaoptqbokzi: - resolution: {integrity: sha512-2J0ZUV5tEzkPiawdkgPz6bmex7NXAde1VXooMwdvK+vuT8PV3U61yorTJOZVLdw7TjjI1Yo94mzsp6UwBud43Q==} + /@chakra-ui/modal/2.2.12_mifiypkmkwrvofebk2kkbeehoy: + resolution: {integrity: sha512-F1nNmYGvyqlmxidbwaBM3y57NhZ/Qeyc8BE9tb1FL1v9nxQhkfrPvMQ9miK0O1syPN6aZ5MMj+uD3AsRFE+/tA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/close-button': 2.0.17_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/focus-lock': 2.0.16_kzbn2opkn2327fwg5yzwzya5o4 + '@chakra-ui/close-button': 2.0.17_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/focus-lock': 2.0.17_kzbn2opkn2327fwg5yzwzya5o4 '@chakra-ui/portal': 2.0.16_biqbaboplfbrettd7655fr4n2y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba '@chakra-ui/transition': 2.0.16_bdsblprzt47ebwyq2agncnzwpe aria-hidden: 1.2.3 framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 - react-remove-scroll: 2.5.5_kzbn2opkn2327fwg5yzwzya5o4 + react-remove-scroll: 2.5.6_kzbn2opkn2327fwg5yzwzya5o4 transitivePeerDependencies: - '@types/react' dev: false - /@chakra-ui/number-input/2.0.19_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/number-input/2.0.19_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-HDaITvtMEqOauOrCPsARDxKD9PSHmhWywpcyCSOX0lMe4xx2aaGhU0QQFhsJsykj8Er6pytMv6t0KZksdDv3YA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/counter': 2.0.14_react@18.2.0 - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-callback-ref': 2.0.7_react@18.2.0 @@ -935,7 +932,7 @@ packages: '@chakra-ui/react-use-safe-layout-effect': 2.0.5_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -943,11 +940,11 @@ packages: resolution: {integrity: sha512-yOGxBjXNvLTBvQyhMDqGU0Oj26s91mbAlqKHiuw737AXHt0aPllOthVUqQMeaYLwLCjGMg0jtI7JReRzyi94Dg==} dev: false - /@chakra-ui/object-utils/2.0.8: - resolution: {integrity: sha512-2upjT2JgRuiupdrtBWklKBS6tqeGMA77Nh6Q0JaoQuH/8yq+15CGckqn3IUWkWoGI0Fg3bK9LDlbbD+9DLw95Q==} + /@chakra-ui/object-utils/2.1.0: + resolution: {integrity: sha512-tgIZOgLHaoti5PYGPTwK3t/cqtcycW0owaiOXoZOcpwwX/vlVb+H1jFsQyWiiwQVPt9RkoSLtxzXamx+aHH+bQ==} dev: false - /@chakra-ui/pin-input/2.0.20_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/pin-input/2.0.20_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-IHVmerrtHN8F+jRB3W1HnMir1S1TUCWhI7qDInxqPtoRffHt6mzZgLZ0izx8p1fD4HkW4c1d4/ZLEz9uH9bBRg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -959,41 +956,41 @@ packages: '@chakra-ui/react-use-controllable-state': 2.0.8_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/popover/2.1.9_i6fhfa2wvtxv5b2jykryjj4lam: - resolution: {integrity: sha512-OMJ12VVs9N32tFaZSOqikkKPtwAVwXYsES/D1pff/amBrE3ngCrpxJSIp4uvTdORfIYDojJqrR52ZplDKS9hRQ==} + /@chakra-ui/popover/2.1.12_xdwvxhu5ub5hflmtgd6mbsauaa: + resolution: {integrity: sha512-Corh8trA1f3ydcMQqomgSvYNNhAlpxiBpMY2sglwYazOJcueHA8CI05cJVD0T/wwoTob7BShabhCGFZThn61Ng==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/close-button': 2.0.17_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/close-button': 2.0.17_62ez5scglruzijw4rniqq4y54y '@chakra-ui/lazy-utils': 2.0.5 - '@chakra-ui/popper': 3.0.13_react@18.2.0 + '@chakra-ui/popper': 3.0.14_react@18.2.0 '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-types': 2.0.7_react@18.2.0 - '@chakra-ui/react-use-animation-state': 2.0.8_react@18.2.0 + '@chakra-ui/react-use-animation-state': 2.0.9_react@18.2.0 '@chakra-ui/react-use-disclosure': 2.0.8_react@18.2.0 - '@chakra-ui/react-use-focus-effect': 2.0.9_react@18.2.0 + '@chakra-ui/react-use-focus-effect': 2.0.11_react@18.2.0 '@chakra-ui/react-use-focus-on-pointer-down': 2.0.6_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 dev: false - /@chakra-ui/popper/3.0.13_react@18.2.0: - resolution: {integrity: sha512-FwtmYz80Ju8oK3Z1HQfisUE7JIMmDsCQsRBu6XuJ3TFQnBHit73yjZmxKjuRJ4JgyT4WBnZoTF3ATbRKSagBeg==} + /@chakra-ui/popper/3.0.14_react@18.2.0: + resolution: {integrity: sha512-RDMmmSfjsmHJbVn2agDyoJpTbQK33fxx//njwJdeyM0zTG/3/4xjI/Cxru3acJ2Y+1jFGmPqhO81stFjnbtfIw==} peerDependencies: react: '>=18' dependencies: '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 - '@popperjs/core': 2.11.6 + '@popperjs/core': 2.11.8 react: 18.2.0 dev: false @@ -1009,29 +1006,29 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false - /@chakra-ui/progress/2.1.6_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/progress/2.1.6_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-hHh5Ysv4z6bK+j2GJbi/FT9CVyto2PtNUNwBmr3oNMVsoOUMoRjczfXvvYqp0EHr9PCpxqrq7sRwgQXUzhbDSw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/react-context': 2.0.8_react@18.2.0 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/provider/2.2.2_fbxtuirhogpez7m7qjkm3itwca: - resolution: {integrity: sha512-UVwnIDnAWq1aKroN5AF+OpNpUqLVeIUk7tKvX3z4CY9FsPFFi6LTEhRHdhpwaU1Tau3Tf9agEu5URegpY7S8BA==} + /@chakra-ui/provider/2.3.0_fbxtuirhogpez7m7qjkm3itwca: + resolution: {integrity: sha512-vKgmjoLVS3NnHW8RSYwmhhda2ZTi3fQc1egkYSVwngGky4CsN15I+XDhxJitVd66H41cjah/UNJyoeq7ACseLA==} peerDependencies: '@emotion/react': ^11.0.0 '@emotion/styled': ^11.0.0 react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/css-reset': 2.1.1_3og6jmu6wvzuytygvdoxepq3x4 + '@chakra-ui/css-reset': 2.1.2_3og6jmu6wvzuytygvdoxepq3x4 '@chakra-ui/portal': 2.0.16_biqbaboplfbrettd7655fr4n2y '@chakra-ui/react-env': 3.0.0_react@18.2.0 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba '@chakra-ui/utils': 2.0.15 '@emotion/react': 11.10.6_kzbn2opkn2327fwg5yzwzya5o4 '@emotion/styled': 11.10.6_qry7xzgsc55pr5ngbiorqn3fpa @@ -1039,18 +1036,18 @@ packages: react-dom: 18.2.0_react@18.2.0 dev: false - /@chakra-ui/radio/2.0.22_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/radio/2.0.22_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-GsQ5WAnLwivWl6gPk8P1x+tCcpVakCt5R5T0HumF7DGPXKdJbjS+RaFySrbETmyTJsKY4QrfXn+g8CWVrMjPjw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba '@zag-js/focus-visible': 0.2.2 react: 18.2.0 dev: false @@ -1088,12 +1085,12 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/react-use-animation-state/2.0.8_react@18.2.0: - resolution: {integrity: sha512-xv9zSF2Rd1mHWQ+m5DLBWeh4atF8qrNvsOs3MNrvxKYBS3f79N3pqcQGrWAEvirXWXfiCeje2VAkEggqFRIo+Q==} + /@chakra-ui/react-use-animation-state/2.0.9_react@18.2.0: + resolution: {integrity: sha512-WFoD5OG03PBmzJCoRwM8rVfU442AvKBPPgA0yGGlKioH29OGuX7W78Ml+cYdXxonTiB03YSRZzUwaUnP4wAy1Q==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/dom-utils': 2.0.6 + '@chakra-ui/dom-utils': 2.1.0 '@chakra-ui/react-use-event-listener': 2.0.7_react@18.2.0 react: 18.2.0 dev: false @@ -1133,12 +1130,12 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/react-use-focus-effect/2.0.9_react@18.2.0: - resolution: {integrity: sha512-20nfNkpbVwyb41q9wxp8c4jmVp6TUGAPE3uFTDpiGcIOyPW5aecQtPmTXPMJH+2aa8Nu1wyoT1btxO+UYiQM3g==} + /@chakra-ui/react-use-focus-effect/2.0.11_react@18.2.0: + resolution: {integrity: sha512-/zadgjaCWD50TfuYsO1vDS2zSBs2p/l8P2DPEIA8FuaowbBubKrk9shKQDWmbfDU7KArGxPxrvo+VXvskPPjHw==} peerDependencies: react: '>=18' dependencies: - '@chakra-ui/dom-utils': 2.0.6 + '@chakra-ui/dom-utils': 2.1.0 '@chakra-ui/react-use-event-listener': 2.0.7_react@18.2.0 '@chakra-ui/react-use-safe-layout-effect': 2.0.5_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 @@ -1179,8 +1176,8 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/react-use-outside-click/2.0.7_react@18.2.0: - resolution: {integrity: sha512-MsAuGLkwYNxNJ5rb8lYNvXApXxYMnJ3MzqBpQj1kh5qP/+JSla9XMjE/P94ub4fSEttmNSqs43SmPPrmPuihsQ==} + /@chakra-ui/react-use-outside-click/2.1.0_react@18.2.0: + resolution: {integrity: sha512-JanCo4QtWvMl9ZZUpKJKV62RlMWDFdPCE0Q64a7eWTOQgWWcpyBW7TOYRunQTqrK30FqkYFJCOlAWOtn+6Rw7A==} peerDependencies: react: '>=18' dependencies: @@ -1250,8 +1247,8 @@ packages: react: 18.2.0 dev: false - /@chakra-ui/react/2.5.5_tlyz7qwuzzubgapow55lw5vriq: - resolution: {integrity: sha512-aBVMUtdWv2MrptD/tKSqICPsuJ+I+jvauegffO1qPUDlK3RrXIDeOHkLGWohgXNcjY5bGVWguFEzJm97//0ooQ==} + /@chakra-ui/react/2.7.0_tlyz7qwuzzubgapow55lw5vriq: + resolution: {integrity: sha512-+FcUFQMsPfhWuM9Iu7uqufwwhmHN2IX6FWsBixYGOalO86dpgETsILMZP9PuWfgj7GpWiy2Dum6HXekh0Tk2Mg==} peerDependencies: '@emotion/react': ^11.0.0 '@emotion/styled': ^11.0.0 @@ -1259,57 +1256,58 @@ packages: react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/accordion': 2.1.11_i6fhfa2wvtxv5b2jykryjj4lam - '@chakra-ui/alert': 2.1.0_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/avatar': 2.2.8_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/breadcrumb': 2.1.5_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/button': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/card': 2.1.6_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/checkbox': 2.2.14_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/close-button': 2.0.17_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/control-box': 2.0.13_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/accordion': 2.2.0_xdwvxhu5ub5hflmtgd6mbsauaa + '@chakra-ui/alert': 2.1.0_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/avatar': 2.2.11_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/breadcrumb': 2.1.5_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/button': 2.0.18_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/card': 2.1.6_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/checkbox': 2.2.15_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/close-button': 2.0.17_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/control-box': 2.0.13_62ez5scglruzijw4rniqq4y54y '@chakra-ui/counter': 2.0.14_react@18.2.0 - '@chakra-ui/css-reset': 2.1.1_3og6jmu6wvzuytygvdoxepq3x4 - '@chakra-ui/editable': 2.0.21_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/focus-lock': 2.0.16_kzbn2opkn2327fwg5yzwzya5o4 - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/hooks': 2.1.6_react@18.2.0 - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/image': 2.0.15_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/input': 2.0.21_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/layout': 2.1.18_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/css-reset': 2.1.2_3og6jmu6wvzuytygvdoxepq3x4 + '@chakra-ui/editable': 3.0.0_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/focus-lock': 2.0.17_kzbn2opkn2327fwg5yzwzya5o4 + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/hooks': 2.2.0_react@18.2.0 + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/image': 2.0.16_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/input': 2.0.22_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/layout': 2.2.0_62ez5scglruzijw4rniqq4y54y '@chakra-ui/live-region': 2.0.13_react@18.2.0 - '@chakra-ui/media-query': 3.2.12_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/menu': 2.1.12_i6fhfa2wvtxv5b2jykryjj4lam - '@chakra-ui/modal': 2.2.11_tmy6ru2c6di5zbchaoptqbokzi - '@chakra-ui/number-input': 2.0.19_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/pin-input': 2.0.20_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/popover': 2.1.9_i6fhfa2wvtxv5b2jykryjj4lam - '@chakra-ui/popper': 3.0.13_react@18.2.0 + '@chakra-ui/media-query': 3.2.12_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/menu': 2.1.15_xdwvxhu5ub5hflmtgd6mbsauaa + '@chakra-ui/modal': 2.2.12_mifiypkmkwrvofebk2kkbeehoy + '@chakra-ui/number-input': 2.0.19_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/pin-input': 2.0.20_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/popover': 2.1.12_xdwvxhu5ub5hflmtgd6mbsauaa + '@chakra-ui/popper': 3.0.14_react@18.2.0 '@chakra-ui/portal': 2.0.16_biqbaboplfbrettd7655fr4n2y - '@chakra-ui/progress': 2.1.6_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/provider': 2.2.2_fbxtuirhogpez7m7qjkm3itwca - '@chakra-ui/radio': 2.0.22_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/progress': 2.1.6_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/provider': 2.3.0_fbxtuirhogpez7m7qjkm3itwca + '@chakra-ui/radio': 2.0.22_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-env': 3.0.0_react@18.2.0 - '@chakra-ui/select': 2.0.19_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/skeleton': 2.0.24_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/slider': 2.0.23_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/spinner': 2.0.13_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/stat': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/styled-system': 2.8.0 - '@chakra-ui/switch': 2.0.26_i6fhfa2wvtxv5b2jykryjj4lam - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba - '@chakra-ui/table': 2.0.17_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/tabs': 2.1.9_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/tag': 3.0.0_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/textarea': 2.0.19_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/theme': 3.0.1_wv7sq5bj4kx5i3evdevscgumbi - '@chakra-ui/theme-utils': 2.0.15 - '@chakra-ui/toast': 6.1.1_dsh6aqeljrnpc2ytvot4skb6iy - '@chakra-ui/tooltip': 2.2.7_dsh6aqeljrnpc2ytvot4skb6iy + '@chakra-ui/select': 2.0.19_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/skeleton': 2.0.24_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/slider': 2.0.25_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/spinner': 2.0.13_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/stat': 2.0.18_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/stepper': 2.2.0_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/styled-system': 2.9.1 + '@chakra-ui/switch': 2.0.27_xdwvxhu5ub5hflmtgd6mbsauaa + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/table': 2.0.17_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/tabs': 2.1.9_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/tag': 3.0.0_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/textarea': 2.0.19_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/theme': 3.1.2_hq32mhfiotloi5hecuixt2e454 + '@chakra-ui/theme-utils': 2.0.18 + '@chakra-ui/toast': 6.1.4_mqgjs6i23b3kxqr2rvhzy7z5lq + '@chakra-ui/tooltip': 2.2.9_mqgjs6i23b3kxqr2rvhzy7z5lq '@chakra-ui/transition': 2.0.16_bdsblprzt47ebwyq2agncnzwpe '@chakra-ui/utils': 2.0.15 - '@chakra-ui/visually-hidden': 2.0.15_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/visually-hidden': 2.0.15_62ez5scglruzijw4rniqq4y54y '@emotion/react': 11.10.6_kzbn2opkn2327fwg5yzwzya5o4 '@emotion/styled': 11.10.6_qry7xzgsc55pr5ngbiorqn3fpa framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y @@ -1319,15 +1317,15 @@ packages: - '@types/react' dev: false - /@chakra-ui/select/2.0.19_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/select/2.0.19_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-eAlFh+JhwtJ17OrB6fO6gEAGOMH18ERNrXLqWbYLrs674Le7xuREgtuAYDoxUzvYXYYTTdOJtVbcHGriI3o6rA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -1335,21 +1333,21 @@ packages: resolution: {integrity: sha512-4/Wur0FqDov7Y0nCXl7HbHzCg4aq86h+SXdoUeuCMD3dSj7dpsVnStLYhng1vxvlbUnLpdF4oz5Myt3i/a7N3Q==} dev: false - /@chakra-ui/skeleton/2.0.24_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/skeleton/2.0.24_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-1jXtVKcl/jpbrJlc/TyMsFyI651GTXY5ma30kWyTXoby2E+cxbV6OR8GB/NMZdGxbQBax8/VdtYVjI0n+OBqWA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/media-query': 3.2.12_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/media-query': 3.2.12_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-use-previous': 2.0.5_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/slider/2.0.23_lze4h7kxffpjhokvtqbtrlfkmq: - resolution: {integrity: sha512-/eyRUXLla+ZdBUPXpakE3SAS2JS8mIJR6qcUYiPVKSpRAi6tMyYeQijAXn2QC1AUVd2JrG8Pz+1Jy7Po3uA7cA==} + /@chakra-ui/slider/2.0.25_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-FnWSi0AIXP+9sHMCPboOKGqm902k8dJtsJ7tu3D0AcKkE62WtYLZ2sTqvwJxCfSl4KqVI1i571SrF9WadnnJ8w==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' @@ -1364,68 +1362,81 @@ packages: '@chakra-ui/react-use-pan-event': 2.0.9_react@18.2.0 '@chakra-ui/react-use-size': 2.0.10_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/spinner/2.0.13_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/spinner/2.0.13_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-T1/aSkVpUIuiYyrjfn1+LsQEG7Onbi1UE9ccS/evgf61Dzy4GgTXQUnDuWFSgpV58owqirqOu6jn/9eCwDlzlg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/stat/2.0.18_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/stat/2.0.18_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-wKyfBqhVlIs9bkSerUc6F9KJMw0yTIEKArW7dejWwzToCLPr47u+CtYO6jlJHV6lRvkhi4K4Qc6pyvtJxZ3VpA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/styled-system/2.8.0: - resolution: {integrity: sha512-bmRv/8ACJGGKGx84U1npiUddwdNifJ+/ETklGwooS5APM0ymwUtBYZpFxjYNJrqvVYpg3mVY6HhMyBVptLS7iA==} + /@chakra-ui/stepper/2.2.0_62ez5scglruzijw4rniqq4y54y: + resolution: {integrity: sha512-8ZLxV39oghSVtOUGK8dX8Z6sWVSQiKVmsK4c3OQDa8y2TvxP0VtFD0Z5U1xJlOjQMryZRWhGj9JBc3iQLukuGg==} + peerDependencies: + '@chakra-ui/system': '>=2.0.0' + react: '>=18' + dependencies: + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/react-context': 2.0.8_react@18.2.0 + '@chakra-ui/shared-utils': 2.0.5 + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba + react: 18.2.0 + dev: false + + /@chakra-ui/styled-system/2.9.1: + resolution: {integrity: sha512-jhYKBLxwOPi9/bQt9kqV3ELa/4CjmNNruTyXlPp5M0v0+pDMUngPp48mVLoskm9RKZGE0h1qpvj/jZ3K7c7t8w==} dependencies: '@chakra-ui/shared-utils': 2.0.5 - csstype: 3.1.1 + csstype: 3.1.2 lodash.mergewith: 4.6.2 dev: false - /@chakra-ui/switch/2.0.26_i6fhfa2wvtxv5b2jykryjj4lam: - resolution: {integrity: sha512-x62lF6VazSZJQuVxosChVR6+0lIJe8Pxgkl/C9vxjhp2yVYb3mew5tcX/sDOu0dYZy8ro/9hMfGkdN4r9xEU8A==} + /@chakra-ui/switch/2.0.27_xdwvxhu5ub5hflmtgd6mbsauaa: + resolution: {integrity: sha512-z76y2fxwMlvRBrC5W8xsZvo3gP+zAEbT3Nqy5P8uh/IPd5OvDsGeac90t5cgnQTyxMOpznUNNK+1eUZqtLxWnQ==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' dependencies: - '@chakra-ui/checkbox': 2.2.14_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/checkbox': 2.2.15_62ez5scglruzijw4rniqq4y54y '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 dev: false - /@chakra-ui/system/2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba: - resolution: {integrity: sha512-52BIp/Zyvefgxn5RTByfkTeG4J+y81LWEjWm8jCaRFsLVm8IFgqIrngtcq4I7gD5n/UKbneHlb4eLHo4uc5yDQ==} + /@chakra-ui/system/2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba: + resolution: {integrity: sha512-Vy8UUaCxikOzOGE54IP8tKouvU38rEYU1HCSquU9+oe7Jd70HaiLa4vmUKvHyMUmxkOzDHIkgZLbVQCubSnN5w==} peerDependencies: '@emotion/react': ^11.0.0 '@emotion/styled': ^11.0.0 react: '>=18' dependencies: '@chakra-ui/color-mode': 2.1.12_react@18.2.0 - '@chakra-ui/object-utils': 2.0.8 + '@chakra-ui/object-utils': 2.1.0 '@chakra-ui/react-utils': 2.0.12_react@18.2.0 - '@chakra-ui/styled-system': 2.8.0 - '@chakra-ui/theme-utils': 2.0.15 + '@chakra-ui/styled-system': 2.9.1 + '@chakra-ui/theme-utils': 2.0.18 '@chakra-ui/utils': 2.0.15 '@emotion/react': 11.10.6_kzbn2opkn2327fwg5yzwzya5o4 '@emotion/styled': 11.10.6_qry7xzgsc55pr5ngbiorqn3fpa @@ -1433,7 +1444,7 @@ packages: react-fast-compare: 3.2.1 dev: false - /@chakra-ui/table/2.0.17_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/table/2.0.17_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-OScheTEp1LOYvTki2NFwnAYvac8siAhW9BI5RKm5f5ORL2gVJo4I72RUqE0aKe1oboxgm7CYt5afT5PS5cG61A==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -1441,11 +1452,11 @@ packages: dependencies: '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/tabs/2.1.9_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/tabs/2.1.9_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-Yf8e0kRvaGM6jfkJum0aInQ0U3ZlCafmrYYni2lqjcTtThqu+Yosmo3iYlnullXxCw5MVznfrkb9ySvgQowuYg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' @@ -1460,104 +1471,105 @@ packages: '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/react-use-safe-layout-effect': 2.0.5_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/tag/3.0.0_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/tag/3.0.0_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-YWdMmw/1OWRwNkG9pX+wVtZio+B89odaPj6XeMn5nfNN8+jyhIEpouWv34+CO9G0m1lupJTxPSfgLAd7cqXZMA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/icon': 3.0.16_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/icon': 3.0.16_62ez5scglruzijw4rniqq4y54y '@chakra-ui/react-context': 2.0.8_react@18.2.0 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/textarea/2.0.19_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/textarea/2.0.19_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-adJk+qVGsFeJDvfn56CcJKKse8k7oMGlODrmpnpTdF+xvlsiTM+1GfaJvgNSpHHuQFdz/A0z1uJtfGefk0G2ZA==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/form-control': 2.0.18_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/form-control': 2.0.18_62ez5scglruzijw4rniqq4y54y '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false - /@chakra-ui/theme-tools/2.0.17_wv7sq5bj4kx5i3evdevscgumbi: - resolution: {integrity: sha512-Auu38hnihlJZQcPok6itRDBbwof3TpXGYtDPnOvrq4Xp7jnab36HLt7KEXSDPXbtOk3ZqU99pvI1en5LbDrdjg==} + /@chakra-ui/theme-tools/2.0.18_hq32mhfiotloi5hecuixt2e454: + resolution: {integrity: sha512-MbiRuXb2tb41FbnW41zhsYYAU0znlpfYZnu0mxCf8U2otCwPekJCfESUGYypjq4JnydQ7TDOk+Kz/Wi974l4mw==} peerDependencies: '@chakra-ui/styled-system': '>=2.0.0' dependencies: '@chakra-ui/anatomy': 2.1.2 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/styled-system': 2.8.0 - color2k: 2.0.1 + '@chakra-ui/styled-system': 2.9.1 + color2k: 2.0.2 dev: false - /@chakra-ui/theme-utils/2.0.15: - resolution: {integrity: sha512-UuxtEgE7gwMTGDXtUpTOI7F5X0iHB9ekEOG5PWPn2wWBL7rlk2JtPI7UP5Um5Yg6vvBfXYGK1ySahxqsgf+87g==} + /@chakra-ui/theme-utils/2.0.18: + resolution: {integrity: sha512-aSbkUUiFpc1NHC7lQdA6uYlr6EcZFXz6b4aJ7VRDpqTiywvqYnvfGzhmsB0z94vgtS9qXc6HoIwBp25jYGV2MA==} dependencies: '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/styled-system': 2.8.0 - '@chakra-ui/theme': 3.0.1_wv7sq5bj4kx5i3evdevscgumbi + '@chakra-ui/styled-system': 2.9.1 + '@chakra-ui/theme': 3.1.2_hq32mhfiotloi5hecuixt2e454 lodash.mergewith: 4.6.2 dev: false - /@chakra-ui/theme/3.0.1_wv7sq5bj4kx5i3evdevscgumbi: - resolution: {integrity: sha512-92kDm/Ux/51uJqhRKevQo/O/rdwucDYcpHg2QuwzdAxISCeYvgtl2TtgOOl5EnqEP0j3IEAvZHZUlv8TTbawaw==} + /@chakra-ui/theme/3.1.2_hq32mhfiotloi5hecuixt2e454: + resolution: {integrity: sha512-ebUXMS3LZw2OZxEQNYaFw3/XuA3jpyprhS/frjHMvZKSOaCjMW+c9z25S0jp1NnpQff08VGI8EWbyVZECXU1QA==} peerDependencies: - '@chakra-ui/styled-system': '>=2.0.0' + '@chakra-ui/styled-system': '>=2.8.0' dependencies: '@chakra-ui/anatomy': 2.1.2 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/styled-system': 2.8.0 - '@chakra-ui/theme-tools': 2.0.17_wv7sq5bj4kx5i3evdevscgumbi + '@chakra-ui/styled-system': 2.9.1 + '@chakra-ui/theme-tools': 2.0.18_hq32mhfiotloi5hecuixt2e454 dev: false - /@chakra-ui/toast/6.1.1_dsh6aqeljrnpc2ytvot4skb6iy: - resolution: {integrity: sha512-JtjIKkPVjEu8okGGCipCxNVgK/15h5AicTATZ6RbG2MsHmr4GfKG3fUCvpbuZseArqmLqGLQZQJjVE9vJzaSkQ==} + /@chakra-ui/toast/6.1.4_mqgjs6i23b3kxqr2rvhzy7z5lq: + resolution: {integrity: sha512-wAcPHq/N/ar4jQxkUGhnsbp+lx2eKOpHxn1KaWdHXUkqCNUA1z09fvBsoMyzObSiiwbDuQPZG5RxsOhzfPZX4Q==} peerDependencies: - '@chakra-ui/system': 2.5.5 + '@chakra-ui/system': 2.5.8 framer-motion: '>=4.0.0' react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/alert': 2.1.0_lze4h7kxffpjhokvtqbtrlfkmq - '@chakra-ui/close-button': 2.0.17_lze4h7kxffpjhokvtqbtrlfkmq + '@chakra-ui/alert': 2.1.0_62ez5scglruzijw4rniqq4y54y + '@chakra-ui/close-button': 2.0.17_62ez5scglruzijw4rniqq4y54y '@chakra-ui/portal': 2.0.16_biqbaboplfbrettd7655fr4n2y '@chakra-ui/react-context': 2.0.8_react@18.2.0 '@chakra-ui/react-use-timeout': 2.0.5_react@18.2.0 '@chakra-ui/react-use-update-effect': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/styled-system': 2.8.0 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba - '@chakra-ui/theme': 3.0.1_wv7sq5bj4kx5i3evdevscgumbi + '@chakra-ui/styled-system': 2.9.1 + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/theme': 3.1.2_hq32mhfiotloi5hecuixt2e454 framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 dev: false - /@chakra-ui/tooltip/2.2.7_dsh6aqeljrnpc2ytvot4skb6iy: - resolution: {integrity: sha512-ImUJ6NnVqARaYqpgtO+kzucDRmxo8AF3jMjARw0bx2LxUkKwgRCOEaaRK5p5dHc0Kr6t5/XqjDeUNa19/sLauA==} + /@chakra-ui/tooltip/2.2.9_mqgjs6i23b3kxqr2rvhzy7z5lq: + resolution: {integrity: sha512-ZoksllanqXRUyMDaiogvUVJ+RdFXwZrfrwx3RV22fejYZIQ602hZ3QHtHLB5ZnKFLbvXKMZKM23HxFTSb0Ytqg==} peerDependencies: '@chakra-ui/system': '>=2.0.0' framer-motion: '>=4.0.0' react: '>=18' react-dom: '>=18' dependencies: - '@chakra-ui/popper': 3.0.13_react@18.2.0 + '@chakra-ui/dom-utils': 2.1.0 + '@chakra-ui/popper': 3.0.14_react@18.2.0 '@chakra-ui/portal': 2.0.16_biqbaboplfbrettd7655fr4n2y '@chakra-ui/react-types': 2.0.7_react@18.2.0 '@chakra-ui/react-use-disclosure': 2.0.8_react@18.2.0 '@chakra-ui/react-use-event-listener': 2.0.7_react@18.2.0 '@chakra-ui/react-use-merge-refs': 2.0.7_react@18.2.0 '@chakra-ui/shared-utils': 2.0.5 - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba framer-motion: 10.11.2_biqbaboplfbrettd7655fr4n2y react: 18.2.0 react-dom: 18.2.0_react@18.2.0 @@ -1583,13 +1595,13 @@ packages: lodash.mergewith: 4.6.2 dev: false - /@chakra-ui/visually-hidden/2.0.15_lze4h7kxffpjhokvtqbtrlfkmq: + /@chakra-ui/visually-hidden/2.0.15_62ez5scglruzijw4rniqq4y54y: resolution: {integrity: sha512-WWULIiucYRBIewHKFA7BssQ2ABLHLVd9lrUo3N3SZgR0u4ZRDDVEUNOy+r+9ruDze8+36dGbN9wsN1IdELtdOw==} peerDependencies: '@chakra-ui/system': '>=2.0.0' react: '>=18' dependencies: - '@chakra-ui/system': 2.5.5_xqp3pgpqjlfxxa3zxu4zoc4fba + '@chakra-ui/system': 2.5.8_xqp3pgpqjlfxxa3zxu4zoc4fba react: 18.2.0 dev: false @@ -1600,17 +1612,6 @@ packages: '@jridgewell/trace-mapping': 0.3.9 dev: true - /@csstools/selector-specificity/2.0.2_wajs5nedgkikc5pcuwett7legi: - resolution: {integrity: sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==} - engines: {node: ^12 || ^14 || >=16} - peerDependencies: - postcss: ^8.2 - postcss-selector-parser: ^6.0.10 - dependencies: - postcss: 8.4.21 - postcss-selector-parser: 6.0.11 - dev: true - /@develar/schema-utils/2.6.5: resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} engines: {node: '>= 8.9.0'} @@ -2180,8 +2181,8 @@ packages: fsevents: 2.3.2 dev: true - /@popperjs/core/2.11.6: - resolution: {integrity: sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==} + /@popperjs/core/2.11.8: + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} dev: false /@react-icons/all-files/4.1.0_react@18.2.0: @@ -2553,6 +2554,23 @@ packages: use-sync-external-store: 1.2.0_react@18.2.0 dev: false + /@tanstack/react-table/8.9.2_biqbaboplfbrettd7655fr4n2y: + resolution: {integrity: sha512-Irvw4wqVF9hhuYzmNrlae4IKdlmgSyoRWnApSLebvYzqHoi5tEsYzBj6YPd0hX78aB/L+4w/jgK2eBQVpGfThQ==} + engines: {node: '>=12'} + peerDependencies: + react: '>=16' + react-dom: '>=16' + dependencies: + '@tanstack/table-core': 8.9.2 + react: 18.2.0 + react-dom: 18.2.0_react@18.2.0 + dev: false + + /@tanstack/table-core/8.9.2: + resolution: {integrity: sha512-ajc0OF+karBAdaSz7OK09rCoAHB1XI1+wEhu+tDNMPc+XcO+dTlXXN/Vc0a8vym4kElvEjXEDd9c8Zfgt4bekA==} + engines: {node: '>=12'} + dev: false + /@testing-library/dom/8.19.1: resolution: {integrity: sha512-P6iIPyYQ+qH8CvGauAqanhVnjrnRe0IZFSYCeGkSRW9q3u8bdVn2NPI+lasFyVsEQn1J/IFmp5Aax41+dAP9wg==} engines: {node: '>=12'} @@ -2778,10 +2796,6 @@ packages: dev: true optional: true - /@types/minimist/1.2.2: - resolution: {integrity: sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==} - dev: true - /@types/ms/0.7.31: resolution: {integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==} dev: true @@ -2810,10 +2824,6 @@ packages: resolution: {integrity: sha512-NpaM49IGQQAUlBhHMF82QH80J08os4ZmyF9MkpCzWAGuOHqE4gTEbhzd7L3l5LmWuZ6E0OiC1FweQ4tsiW35+g==} dev: true - /@types/normalize-package-data/2.4.1: - resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==} - dev: true - /@types/parse-json/4.0.0: resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} @@ -3319,15 +3329,6 @@ packages: uri-js: 4.4.1 dev: true - /ajv/8.12.0: - resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} - dependencies: - fast-deep-equal: 3.1.3 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - uri-js: 4.4.1 - dev: true - /ansi-regex/5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3413,7 +3414,7 @@ packages: resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==} engines: {node: '>=10'} dependencies: - tslib: 2.5.0 + tslib: 2.5.3 dev: false /aria-query/5.1.3: @@ -3462,11 +3463,6 @@ packages: get-intrinsic: 1.1.3 dev: true - /arrify/1.0.1: - resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} - engines: {node: '>=0.10.0'} - dev: true - /asar/3.2.0: resolution: {integrity: sha512-COdw2ZQvKdFGFxXwX3oYh2/sOsJWJegrdJCGxnN4MZ7IULgRBp9P6665aqj9z1v9VwP4oP1hRBojRDQ//IGgAg==} engines: {node: '>=10.12.0'} @@ -3496,6 +3492,7 @@ packages: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} dev: true + optional: true /async-exit-hook/2.0.1: resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} @@ -3514,8 +3511,8 @@ packages: engines: {node: '>= 4.0.0'} dev: true - /autosize/5.0.2: - resolution: {integrity: sha512-FPVt5ynkqUAA9gcMZnJHka1XfQgr1WNd/yRfIjmj5WGmjua+u5Hl9hn8M2nU5CNy2bEIcj1ZUwXq7IOHsfZG9w==} + /autosize/6.0.1: + resolution: {integrity: sha512-f86EjiUKE6Xvczc4ioP1JBlWG7FKrE13qe/DxBCpe8GCipCq2nFw73aO8QEBKHfSbYGDN5eB9jXWKen7tspDqQ==} dev: false /available-typed-arrays/1.0.5: @@ -3546,10 +3543,6 @@ packages: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} dev: true - /balanced-match/2.0.0: - resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} - dev: true - /base64-js/1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} requiresBuild: true @@ -3749,20 +3742,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - /camelcase-keys/6.2.2: - resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} - engines: {node: '>=8'} - dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - dev: true - - /camelcase/5.3.1: - resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} - engines: {node: '>=6'} - dev: true - /camelcase/6.3.0: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} @@ -3912,14 +3891,10 @@ packages: color-string: 1.9.1 dev: false - /color2k/2.0.1: - resolution: {integrity: sha512-iCg+xrEqtYISsSJZN1z44fyhv4EfX8lSkcDhodt6VnMf1+iMwZxAtmGXchTCeMUnTbXunGvUVK6E3skkApPnZw==} + /color2k/2.0.2: + resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==} dev: false - /colord/2.9.3: - resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} - dev: true - /colors/1.0.3: resolution: {integrity: sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==} engines: {node: '>=0.1.90'} @@ -4090,21 +4065,10 @@ packages: tiny-invariant: 1.3.1 dev: false - /css-functions-list/3.1.0: - resolution: {integrity: sha512-/9lCvYZaUbBGvYUgYGFJ4dcYiyqdhSjG7IPVluoV8A1ILjkF7ilmhp1OGUz8n+nmBcu0RNrQAzgD8B6FJbrt2w==} - engines: {node: '>=12.22'} - dev: true - /css.escape/1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} dev: true - /cssesc/3.0.0: - resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} - engines: {node: '>=4'} - hasBin: true - dev: true - /cssom/0.3.8: resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==} dev: true @@ -4123,6 +4087,10 @@ packages: /csstype/3.1.1: resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} + /csstype/3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + dev: false + /csv-stringify/6.2.3: resolution: {integrity: sha512-4qGjUMwnlaRc00gc2jrIYh2w/h1fo25B0mTuY9K8fBiIgtmCX3LcgUbrEGViL98Ci4Se/F5LFEtu8k+dItJVZQ==} dev: false @@ -4177,19 +4145,6 @@ packages: ms: 2.1.2 dev: true - /decamelize-keys/1.1.1: - resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} - engines: {node: '>=0.10.0'} - dependencies: - decamelize: 1.2.0 - map-obj: 1.0.1 - dev: true - - /decamelize/1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - dev: true - /decimal.js/10.4.3: resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} dev: true @@ -5040,11 +4995,6 @@ packages: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} dev: true - /fastest-levenshtein/1.0.16: - resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} - engines: {node: '>= 4.9.1'} - dev: true - /fastq/1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} dependencies: @@ -5096,14 +5046,6 @@ packages: resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} dev: false - /find-up/4.1.0: - resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} - engines: {node: '>=8'} - dependencies: - locate-path: 5.0.0 - path-exists: 4.0.0 - dev: true - /find-up/5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -5128,7 +5070,7 @@ packages: resolution: {integrity: sha512-KSuV3ur4gf2KqMNoZx3nXNVhqCkn42GuTYCX4tXPEwf0MjpFQmNMiN6m7dXaUXgIoivL6/65agoUMg4RLS0Vbg==} engines: {node: '>=10'} dependencies: - tslib: 2.5.0 + tslib: 2.5.3 dev: false /follow-redirects/1.15.2: @@ -5346,22 +5288,6 @@ packages: dev: true optional: true - /global-modules/2.0.0: - resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} - engines: {node: '>=6'} - dependencies: - global-prefix: 3.0.0 - dev: true - - /global-prefix/3.0.0: - resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} - engines: {node: '>=6'} - dependencies: - ini: 1.3.8 - kind-of: 6.0.3 - which: 1.3.1 - dev: true - /globals/11.12.0: resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} engines: {node: '>=4'} @@ -5393,10 +5319,6 @@ packages: slash: 3.0.0 dev: true - /globjoin/0.1.4: - resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} - dev: true - /globrex/0.1.2: resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} dev: true @@ -5436,11 +5358,6 @@ packages: resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} dev: true - /hard-rejection/2.1.0: - resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} - engines: {node: '>=6'} - dev: true - /has-bigints/1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} dev: true @@ -5488,10 +5405,6 @@ packages: react-is: 16.13.1 dev: false - /hosted-git-info/2.8.9: - resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} - dev: true - /hosted-git-info/4.1.0: resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} engines: {node: '>=10'} @@ -5506,11 +5419,6 @@ packages: whatwg-encoding: 2.0.0 dev: true - /html-tags/3.2.0: - resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==} - engines: {node: '>=8'} - dev: true - /http-cache-semantics/4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} dev: true @@ -5606,11 +5514,6 @@ packages: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-lazy/4.0.0: - resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} - engines: {node: '>=8'} - dev: true - /imurmurhash/0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -5631,10 +5534,6 @@ packages: /inherits/2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - /ini/1.3.8: - resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: true - /internal-slot/1.0.4: resolution: {integrity: sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==} engines: {node: '>= 0.4'} @@ -5771,16 +5670,6 @@ packages: engines: {node: '>=8'} dev: true - /is-plain-obj/1.1.0: - resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} - engines: {node: '>=0.10.0'} - dev: true - - /is-plain-object/5.0.0: - resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} - engines: {node: '>=0.10.0'} - dev: true - /is-potential-custom-element-name/1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: true @@ -6014,10 +5903,6 @@ packages: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} dev: true - /json-schema-traverse/1.0.0: - resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - dev: true - /json-stable-stringify-without-jsonify/1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} dev: true @@ -6065,15 +5950,6 @@ packages: json-buffer: 3.0.1 dev: true - /kind-of/6.0.3: - resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} - engines: {node: '>=0.10.0'} - dev: true - - /known-css-properties/0.26.0: - resolution: {integrity: sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==} - dev: true - /lazy-val/1.0.5: resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} dev: true @@ -6102,13 +5978,6 @@ packages: engines: {node: '>=14'} dev: true - /locate-path/5.0.0: - resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} - engines: {node: '>=8'} - dependencies: - p-locate: 4.1.0 - dev: true - /locate-path/6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -6124,10 +5993,6 @@ packages: resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} dev: false - /lodash.truncate/4.4.2: - resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} - dev: true - /lodash/4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} @@ -6200,16 +6065,6 @@ packages: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} dev: true - /map-obj/1.0.1: - resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} - engines: {node: '>=0.10.0'} - dev: true - - /map-obj/4.3.0: - resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} - engines: {node: '>=8'} - dev: true - /matcher/3.0.0: resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} engines: {node: '>=10'} @@ -6218,10 +6073,6 @@ packages: dev: true optional: true - /mathml-tag-names/2.1.3: - resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} - dev: true - /md5-hex/3.0.1: resolution: {integrity: sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==} engines: {node: '>=8'} @@ -6234,24 +6085,6 @@ packages: engines: {node: '>= 0.6'} dev: false - /meow/9.0.0: - resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} - engines: {node: '>=10'} - dependencies: - '@types/minimist': 1.2.2 - camelcase-keys: 6.2.2 - decamelize: 1.2.0 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 3.0.3 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.18.1 - yargs-parser: 20.2.9 - dev: true - /merge-descriptors/1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} dev: false @@ -6330,15 +6163,6 @@ packages: brace-expansion: 2.0.1 dev: true - /minimist-options/4.1.0: - resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} - engines: {node: '>= 6'} - dependencies: - arrify: 1.0.1 - is-plain-obj: 1.1.0 - kind-of: 6.0.3 - dev: true - /minimist/1.2.7: resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==} @@ -6495,25 +6319,6 @@ packages: abbrev: 1.1.1 dev: true - /normalize-package-data/2.5.0: - resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} - dependencies: - hosted-git-info: 2.8.9 - resolve: 1.22.1 - semver: 5.7.1 - validate-npm-package-license: 3.0.4 - dev: true - - /normalize-package-data/3.0.3: - resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} - engines: {node: '>=10'} - dependencies: - hosted-git-info: 4.1.0 - is-core-module: 2.11.0 - semver: 7.3.8 - validate-npm-package-license: 3.0.4 - dev: true - /normalize-path/3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -6646,13 +6451,6 @@ packages: engines: {node: '>=8'} dev: true - /p-limit/2.3.0: - resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} - engines: {node: '>=6'} - dependencies: - p-try: 2.2.0 - dev: true - /p-limit/3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -6667,13 +6465,6 @@ packages: yocto-queue: 1.0.0 dev: true - /p-locate/4.1.0: - resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} - engines: {node: '>=8'} - dependencies: - p-limit: 2.3.0 - dev: true - /p-locate/5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -6681,11 +6472,6 @@ packages: p-limit: 3.1.0 dev: true - /p-try/2.2.0: - resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} - engines: {node: '>=6'} - dev: true - /parent-module/1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -6806,42 +6592,6 @@ packages: xmlbuilder: 15.1.1 dev: true - /postcss-media-query-parser/0.2.3: - resolution: {integrity: sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==} - dev: true - - /postcss-resolve-nested-selector/0.1.1: - resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==} - dev: true - - /postcss-safe-parser/6.0.0_postcss@8.4.21: - resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.3.3 - dependencies: - postcss: 8.4.21 - dev: true - - /postcss-scss/4.0.6: - resolution: {integrity: sha512-rLDPhJY4z/i4nVFZ27j9GqLxj1pwxE80eAzUNRMXtcpipFYIeowerzBgG3yJhMtObGEXidtIgbUpQ3eLDsf5OQ==} - engines: {node: '>=12.0'} - peerDependencies: - postcss: ^8.4.19 - dev: true - - /postcss-selector-parser/6.0.11: - resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==} - engines: {node: '>=4'} - dependencies: - cssesc: 3.0.0 - util-deprecate: 1.0.2 - dev: true - - /postcss-value-parser/4.2.0: - resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - dev: true - /postcss/8.4.21: resolution: {integrity: sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==} engines: {node: ^10 || ^12 || >=14} @@ -6970,11 +6720,6 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true - /quick-lru/4.0.1: - resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==} - engines: {node: '>=8'} - dev: true - /quick-lru/5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} @@ -7005,7 +6750,7 @@ packages: peerDependencies: react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.22.5 react: 18.2.0 dev: false @@ -7045,7 +6790,7 @@ packages: '@types/react': optional: true dependencies: - '@babel/runtime': 7.21.0 + '@babel/runtime': 7.22.5 '@types/react': 18.0.26 focus-lock: 0.11.6 prop-types: 15.8.1 @@ -7107,11 +6852,11 @@ packages: '@types/react': 18.0.26 react: 18.2.0 react-style-singleton: 2.2.1_kzbn2opkn2327fwg5yzwzya5o4 - tslib: 2.5.0 + tslib: 2.5.3 dev: false - /react-remove-scroll/2.5.5_kzbn2opkn2327fwg5yzwzya5o4: - resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==} + /react-remove-scroll/2.5.6_kzbn2opkn2327fwg5yzwzya5o4: + resolution: {integrity: sha512-bO856ad1uDYLefgArk559IzUNeQ6SWH4QnrevIUjH+GczV56giDfl3h0Idptf2oIKxQmd1p9BN25jleKodTALg==} engines: {node: '>=10'} peerDependencies: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 @@ -7124,7 +6869,7 @@ packages: react: 18.2.0 react-remove-scroll-bar: 2.3.4_kzbn2opkn2327fwg5yzwzya5o4 react-style-singleton: 2.2.1_kzbn2opkn2327fwg5yzwzya5o4 - tslib: 2.5.0 + tslib: 2.5.3 use-callback-ref: 1.3.0_kzbn2opkn2327fwg5yzwzya5o4 use-sidecar: 1.1.2_kzbn2opkn2327fwg5yzwzya5o4 dev: false @@ -7166,15 +6911,7 @@ packages: get-nonce: 1.0.1 invariant: 2.2.4 react: 18.2.0 - tslib: 2.5.0 - dev: false - - /react-table/7.8.0_react@18.2.0: - resolution: {integrity: sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==} - peerDependencies: - react: ^16.8.3 || ^17.0.0-0 || ^18.0.0 - dependencies: - react: 18.2.0 + tslib: 2.5.3 dev: false /react/18.2.0: @@ -7194,25 +6931,6 @@ packages: lazy-val: 1.0.5 dev: true - /read-pkg-up/7.0.1: - resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} - engines: {node: '>=8'} - dependencies: - find-up: 4.1.0 - read-pkg: 5.2.0 - type-fest: 0.8.1 - dev: true - - /read-pkg/5.2.0: - resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} - engines: {node: '>=8'} - dependencies: - '@types/normalize-package-data': 2.4.1 - normalize-package-data: 2.5.0 - parse-json: 5.2.0 - type-fest: 0.6.0 - dev: true - /readable-stream/2.3.7: resolution: {integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==} dependencies: @@ -7273,11 +6991,6 @@ packages: engines: {node: '>=0.10.0'} dev: true - /require-from-string/2.0.2: - resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} - engines: {node: '>=0.10.0'} - dev: true - /requires-port/1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} dev: true @@ -7290,11 +7003,6 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - /resolve-from/5.0.0: - resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} - engines: {node: '>=8'} - dev: true - /resolve/1.22.1: resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==} hasBin: true @@ -7523,10 +7231,6 @@ packages: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} dev: true - /signal-exit/3.0.7: - resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} - dev: true - /simple-swizzle/0.2.2: resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} dependencies: @@ -7556,15 +7260,6 @@ packages: dev: true optional: true - /slice-ansi/4.0.0: - resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} - engines: {node: '>=10'} - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - dev: true - /smart-buffer/4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -7595,28 +7290,6 @@ packages: requiresBuild: true dev: true - /spdx-correct/3.1.1: - resolution: {integrity: sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==} - dependencies: - spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.12 - dev: true - - /spdx-exceptions/2.3.0: - resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} - dev: true - - /spdx-expression-parse/3.0.1: - resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - dependencies: - spdx-exceptions: 2.3.0 - spdx-license-ids: 3.0.12 - dev: true - - /spdx-license-ids/3.0.12: - resolution: {integrity: sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==} - dev: true - /sprintf-js/1.1.2: resolution: {integrity: sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==} dev: true @@ -7740,126 +7413,6 @@ packages: acorn: 8.8.2 dev: true - /style-search/0.1.0: - resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} - dev: true - - /stylelint-config-prettier/9.0.4_stylelint@14.16.1: - resolution: {integrity: sha512-38nIGTGpFOiK5LjJ8Ma1yUgpKENxoKSOhbDNSemY7Ep0VsJoXIW9Iq/2hSt699oB9tReynfWicTAoIHiq8Rvbg==} - engines: {node: '>= 12'} - hasBin: true - peerDependencies: - stylelint: '>=11.0.0' - dependencies: - stylelint: 14.16.1 - dev: true - - /stylelint-config-recommended-scss/8.0.0_stylelint@14.16.1: - resolution: {integrity: sha512-BxjxEzRaZoQb7Iinc3p92GS6zRdRAkIuEu2ZFLTxJK2e1AIcCb5B5MXY9KOXdGTnYFZ+KKx6R4Fv9zU6CtMYPQ==} - peerDependencies: - postcss: ^8.3.3 - stylelint: ^14.10.0 - peerDependenciesMeta: - postcss: - optional: true - dependencies: - postcss-scss: 4.0.6 - stylelint: 14.16.1 - stylelint-config-recommended: 9.0.0_stylelint@14.16.1 - stylelint-scss: 4.3.0_stylelint@14.16.1 - dev: true - - /stylelint-config-recommended/9.0.0_stylelint@14.16.1: - resolution: {integrity: sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==} - peerDependencies: - stylelint: ^14.10.0 - dependencies: - stylelint: 14.16.1 - dev: true - - /stylelint-config-standard-scss/6.1.0_stylelint@14.16.1: - resolution: {integrity: sha512-iZ2B5kQT2G3rUzx+437cEpdcnFOQkwnwqXuY8Z0QUwIHQVE8mnYChGAquyKFUKZRZ0pRnrciARlPaR1RBtPb0Q==} - peerDependencies: - postcss: ^8.3.3 - stylelint: ^14.14.0 - peerDependenciesMeta: - postcss: - optional: true - dependencies: - stylelint: 14.16.1 - stylelint-config-recommended-scss: 8.0.0_stylelint@14.16.1 - stylelint-config-standard: 29.0.0_stylelint@14.16.1 - dev: true - - /stylelint-config-standard/29.0.0_stylelint@14.16.1: - resolution: {integrity: sha512-uy8tZLbfq6ZrXy4JKu3W+7lYLgRQBxYTUUB88vPgQ+ZzAxdrvcaSUW9hOMNLYBnwH+9Kkj19M2DHdZ4gKwI7tg==} - peerDependencies: - stylelint: ^14.14.0 - dependencies: - stylelint: 14.16.1 - stylelint-config-recommended: 9.0.0_stylelint@14.16.1 - dev: true - - /stylelint-scss/4.3.0_stylelint@14.16.1: - resolution: {integrity: sha512-GvSaKCA3tipzZHoz+nNO7S02ZqOsdBzMiCx9poSmLlb3tdJlGddEX/8QzCOD8O7GQan9bjsvLMsO5xiw6IhhIQ==} - peerDependencies: - stylelint: ^14.5.1 - dependencies: - lodash: 4.17.21 - postcss-media-query-parser: 0.2.3 - postcss-resolve-nested-selector: 0.1.1 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - stylelint: 14.16.1 - dev: true - - /stylelint/14.16.1: - resolution: {integrity: sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - hasBin: true - dependencies: - '@csstools/selector-specificity': 2.0.2_wajs5nedgkikc5pcuwett7legi - balanced-match: 2.0.0 - colord: 2.9.3 - cosmiconfig: 7.1.0 - css-functions-list: 3.1.0 - debug: 4.3.4 - fast-glob: 3.2.12 - fastest-levenshtein: 1.0.16 - file-entry-cache: 6.0.1 - global-modules: 2.0.0 - globby: 11.1.0 - globjoin: 0.1.4 - html-tags: 3.2.0 - ignore: 5.2.4 - import-lazy: 4.0.0 - imurmurhash: 0.1.4 - is-plain-object: 5.0.0 - known-css-properties: 0.26.0 - mathml-tag-names: 2.1.3 - meow: 9.0.0 - micromatch: 4.0.5 - normalize-path: 3.0.0 - picocolors: 1.0.0 - postcss: 8.4.21 - postcss-media-query-parser: 0.2.3 - postcss-resolve-nested-selector: 0.1.1 - postcss-safe-parser: 6.0.0_postcss@8.4.21 - postcss-selector-parser: 6.0.11 - postcss-value-parser: 4.2.0 - resolve-from: 5.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - style-search: 0.1.0 - supports-hyperlinks: 2.3.0 - svg-tags: 1.0.0 - table: 6.8.1 - v8-compile-cache: 2.3.0 - write-file-atomic: 4.0.2 - transitivePeerDependencies: - - supports-color - dev: true - /stylis/4.1.3: resolution: {integrity: sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==} dev: false @@ -7893,14 +7446,6 @@ packages: has-flag: 4.0.0 dev: true - /supports-hyperlinks/2.3.0: - resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==} - engines: {node: '>=8'} - dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 - dev: true - /supports-preserve-symlinks-flag/1.0.0: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} @@ -7909,25 +7454,10 @@ packages: resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} dev: true - /svg-tags/1.0.0: - resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} - dev: true - /symbol-tree/3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} dev: true - /table/6.8.1: - resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} - engines: {node: '>=10.0.0'} - dependencies: - ajv: 8.12.0 - lodash.truncate: 4.4.2 - slice-ansi: 4.0.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - dev: true - /tar/6.1.13: resolution: {integrity: sha512-jdIBIN6LTIe2jqzay/2vtYLlBHa3JF42ot3h1dW8Q0PaAG4v8rm0cvpVePtau5C6OKXGGcgO9q2AMNSWxiLqKw==} engines: {node: '>=10'} @@ -8035,11 +7565,6 @@ packages: punycode: 2.1.1 dev: true - /trim-newlines/3.0.1: - resolution: {integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==} - engines: {node: '>=8'} - dev: true - /truncate-utf8-bytes/1.0.2: resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} dependencies: @@ -8105,6 +7630,10 @@ packages: resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} dev: false + /tslib/2.5.3: + resolution: {integrity: sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==} + dev: false + /tsutils/3.21.0_typescript@4.9.4: resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} engines: {node: '>= 6'} @@ -8201,26 +7730,11 @@ packages: dev: true optional: true - /type-fest/0.18.1: - resolution: {integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==} - engines: {node: '>=10'} - dev: true - /type-fest/0.20.2: resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} engines: {node: '>=10'} dev: true - /type-fest/0.6.0: - resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} - engines: {node: '>=8'} - dev: true - - /type-fest/0.8.1: - resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} - engines: {node: '>=8'} - dev: true - /type-is/1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} @@ -8340,7 +7854,7 @@ packages: dependencies: '@types/react': 18.0.26 react: 18.2.0 - tslib: 2.5.0 + tslib: 2.5.3 dev: false /use-sidecar/1.1.2_kzbn2opkn2327fwg5yzwzya5o4: @@ -8356,7 +7870,7 @@ packages: '@types/react': 18.0.26 detect-node-es: 1.1.0 react: 18.2.0 - tslib: 2.5.0 + tslib: 2.5.3 dev: false /use-sync-external-store/1.2.0_react@18.2.0: @@ -8373,6 +7887,7 @@ packages: /util-deprecate/1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + dev: false /utils-merge/1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} @@ -8383,17 +7898,6 @@ packages: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} dev: true - /v8-compile-cache/2.3.0: - resolution: {integrity: sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==} - dev: true - - /validate-npm-package-license/3.0.4: - resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - dependencies: - spdx-correct: 3.1.1 - spdx-expression-parse: 3.0.1 - dev: true - /validator/13.7.0: resolution: {integrity: sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==} engines: {node: '>= 0.10'} @@ -8820,13 +8324,6 @@ packages: is-typed-array: 1.1.10 dev: true - /which/1.3.1: - resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} - hasBin: true - dependencies: - isexe: 2.0.0 - dev: true - /which/2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -8872,14 +8369,6 @@ packages: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} dev: true - /write-file-atomic/4.0.2: - resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} - engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - /ws/8.12.0: resolution: {integrity: sha512-kU62emKIdKVeEIOIKVegvqpXMSTAMLJozpHZaJNDYqBjzlSYXQGviYwN1osDLJ9av68qHd4a2oSjd7yD4pacig==} engines: {node: '>=10.0.0'} @@ -8957,11 +8446,6 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} - /yargs-parser/20.2.9: - resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} - engines: {node: '>=10'} - dev: true - /yargs-parser/21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'}