Compare commits

...

76 Commits

Author SHA1 Message Date
Alex Christoffer Rasmussen 1025347c08 refactor: add version to session endpoint (#1659) 2025-06-23 20:42:53 +02:00
Carlos Valente 43988ea12b fix: stale ids in cuesheet (#1657) 2025-06-23 19:56:53 +02:00
Alex Christoffer Rasmussen 722e045b20 Revert "Session version" (#1649)
* Revert "refactor: add version to session endpoint"

This reverts commit cd40a6e55e.

* Revert "Remove public event feature (#1645)"

This reverts commit 08d9e24871.

* Revert "Fix: rearrange playing event (#1640)"

This reverts commit 0649678dca.

* Revert "refactor: style tweaks to edit css modal"

This reverts commit a00ec2d02a.

* Revert "refactor: remove usages of framer-motion"

This reverts commit 54a74ccc2a.

* Revert "Upgrade expressjs (#1633)"

This reverts commit 6f3ab274bd.

* Revert "Refactor: require trigger in all events objects (#1636)"

This reverts commit 90870ecfb6.

* Revert "Fix: Correct boundary condition in applyDelay"

This reverts commit b640e0e181.

* Revert "let vite be the proxy to the dev server (#1630)"

This reverts commit c41fe824cf.

* Revert "refactor: migrate custom fields to transactions"

This reverts commit 62c8319d70.

* Revert "refactor: simplify validations"

This reverts commit b1d23467a2.

* Revert "refactor: create transaction system and apply to adding entry (#1620)"

This reverts commit 9a62daf047.

* Revert "Refactor: better rounding (#1594)"

This reverts commit b9ab1c6fd7.

* Revert "refactor: improve reorder logic"

This reverts commit c1054711b0.

* Revert "chore: simplify URLs"

This reverts commit a4d4f29a37.

* Revert "refactor: order is single source of truth"

This reverts commit 2793aadea0.

* Revert "refactor: refetch targets is enum"

This reverts commit e7cfb7d9d9.

* Revert "refactor: small ux improvements"

This reverts commit 256a851c9b.

* Revert "refactor: remove trivially inferred numEvents"

This reverts commit 4ab9c81cb8.

* Revert "feat: duplicate groups"

This reverts commit 2f13d6c89e.

* Revert "feat: create group from entry selection"

This reverts commit f1f7bad25e.

* Revert "feat: create block from rundown empty"

This reverts commit a8b52a48f7.

* Revert "fix: collapsed blocks dont render children"

This reverts commit cd0999b2ab.

* Revert "refactor: type cleanup and test improvements"

This reverts commit b4c60f3f04.

* Revert "feat: allow dissolving a block"

This reverts commit 5cefad3666.

* Revert "fix: uncontrolled prop on controlled component"

This reverts commit 7a6ecd8c34.

* Revert "refactor: improve return of reorder"

This reverts commit ba96ecfd91.

* Revert "refactor: mutations on batch elements must have IDs"

This reverts commit 7bed3757f2.

* Revert "chore: upgrade dependencies"

This reverts commit e2e755b1d2.

* Revert "refactor: extract utility to merge two arrays"

This reverts commit a77d23109d.

* Revert "refactor: change network mode defaults"

This reverts commit 0eb3b8d382.

* Revert "fix: delete nested events"

This reverts commit 0021185288.

* Revert "fix: add event at end of block"

This reverts commit 94c72ff4f6.

* Revert "refactor: make finder available in exported rundown"

This reverts commit e4c08dc9b2.

* Revert "assert non null and update test (#1604)"

This reverts commit ec74af0d62.

* Revert "Fix project renumber (#1597)"

This reverts commit b6d72dd082.

* Revert "Refactor: WebSocket from flush queue to one patch (#1595)"

This reverts commit 31c311daf0.

* Revert "Refactor: ms for api calls (#1593)"

This reverts commit e9b3cc6090.

* Revert "fix test (#1601)"

This reverts commit 543b04a097.

* Revert "fix: rebase master"

This reverts commit d39b85b6e6.

* Revert "chore: correct test path"

This reverts commit bbe107bb2b.

* Revert "refactor: extract rundown parsing"

This reverts commit c616240db1.

* Revert "chore: improve convention entry <> event"

This reverts commit 166be66ce3.

* Revert "refactor: maintain flat orders"

This reverts commit 4180d0a337.

* Revert "refactor: implement operations on nested events"

This reverts commit 78108e316c.

* Revert "refactor: process events in rundown"

This reverts commit 3bb8b70915.

* Revert "chore: improve convention entry <> event"

This reverts commit 1c4f13a0ed.

* Revert "chore: rename currentBlock > parent"

This reverts commit 3ca0abad53.

* Revert "refactor: fix delay positioning in gaps"

This reverts commit 030c8f897f.

* Revert "refactor(e2e): skip flaky test"

This reverts commit 68175cfa3b.

* Revert "refactor: improve project loading"

This reverts commit fd8f757851.

* Revert "refactor: gather group metadata"

This reverts commit 876d111c61.

* Revert "refactor: swap maintains schedule"

This reverts commit 730cb95c04.

* Revert "chore: rename files"

This reverts commit 3c388d4fb5.

* Revert "refactor: restructure model to contain an object of rundowns"

This reverts commit 2e23718d73.

* Revert "refactor: clearer relationship on rundown elements"

This reverts commit 89ea8c470b.

* Revert "refactor: use strict typing"

This reverts commit 178640bfc4.

* Revert "refactor: remove stop as a possible end action"

This reverts commit 4ed38340e0.

* Revert "refactor: restructure model to contain an object of rundowns"

This reverts commit 69eb9a5eff.

* Revert "chore: remove IDE files"

This reverts commit 351425127a.

* Revert "refactor: remove unused and legacy code"

This reverts commit 95f2ba37cc.
2025-06-20 12:28:20 +02:00
Carlos Valente ad6804019b bump version to 3.16.0 2025-06-20 07:41:39 +02:00
Carlos Valente cd40a6e55e refactor: add version to session endpoint 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 08d9e24871 Remove public event feature (#1645) 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 0649678dca Fix: rearrange playing event (#1640)
* creat failing test

* find and pass index of eventNow in case it moved

* fix cache invalidate edits should also recalculate timer

* use coluor as test indicator

* fixed empty cue guard

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-19 18:53:28 +02:00
google-labs-jules[bot] a00ec2d02a refactor: style tweaks to edit css modal 2025-06-19 18:53:28 +02:00
google-labs-jules[bot] 54a74ccc2a refactor: remove usages of framer-motion 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 6f3ab274bd Upgrade expressjs (#1633)
* upgrade expressjs

* migration

* reenable test

* extend timeout on download test

* fixup! migration

* move empty body test from controller to validator

* enusre not empty

* extract validation function

* fixup! reenable test

* remove thin controllers

* disable e2e test of project file download
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 90870ecfb6 Refactor: require trigger in all events objects (#1636)
* reqire trigger in all events

* use structuredClone when cloning an event
2025-06-19 18:53:28 +02:00
google-labs-jules[bot] b640e0e181 Fix: Correct boundary condition in applyDelay
The `applyDelay` function had a condition that incorrectly used `rundown.order.length` instead of `rundownMetadata.flatEntryOrder.length` to check if a delay entry was the last in the sequence. `rundown.order` only contains top-level entries, while `flatEntryOrder` contains all entries, including those within blocks, which is the relevant list for this check.

This commit corrects the condition to use `rundownMetadata.flatEntryOrder.length`.

Existing tests in `rundown.dao.test.ts` (specifically the test `removes delays in last position without applying changes`) already cover this scenario and pass with the correction, ensuring the fix behaves as expected.
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen c41fe824cf let vite be the proxy to the dev server (#1630) 2025-06-19 18:53:28 +02:00
Carlos Valente 62c8319d70 refactor: migrate custom fields to transactions
refactor: extract functions to api domain

refactor: strict custom field parsing

refactor: remove rundown cache utilities

refactor: directory restructure
2025-06-19 18:53:28 +02:00
Carlos Valente b1d23467a2 refactor: simplify validations 2025-06-19 18:53:28 +02:00
Carlos Valente 9a62daf047 refactor: create transaction system and apply to adding entry (#1620)
* refactor: create transaction system and apply to adding entry

* refactor: migrate edit mutations to transaction

* refactor: migrate delete mutation to transaction

* refactor: migrate reorder mutation to transaction

* refactor: migrate apply delay to transaction

* refactor: migrate swapEvents to transaction

* refactor: migrate clone to transaction

* refactor: migrate group/ungroup transactions

* refactor: simplify mutations

* refactor: migrate getters

* chore: add tests to processRundown()
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen b9ab1c6fd7 Refactor: better rounding (#1594) 2025-06-19 18:53:28 +02:00
Carlos Valente c1054711b0 refactor: improve reorder logic 2025-06-19 18:53:28 +02:00
Carlos Valente a4d4f29a37 chore: simplify URLs 2025-06-19 18:53:28 +02:00
Carlos Valente 2793aadea0 refactor: order is single source of truth 2025-06-19 18:53:28 +02:00
Carlos Valente e7cfb7d9d9 refactor: refetch targets is enum 2025-06-19 18:53:28 +02:00
Carlos Valente 256a851c9b refactor: small ux improvements
- rename dissolve > ungroup
- prevent ondrag when clicking
- add untitled as block title fallback
- move block action to context menu
2025-06-19 18:53:28 +02:00
Carlos Valente 4ab9c81cb8 refactor: remove trivially inferred numEvents 2025-06-19 18:53:28 +02:00
Carlos Valente 2f13d6c89e feat: duplicate groups 2025-06-19 18:53:28 +02:00
Carlos Valente f1f7bad25e feat: create group from entry selection 2025-06-19 18:53:28 +02:00
Carlos Valente a8b52a48f7 feat: create block from rundown empty 2025-06-19 18:53:28 +02:00
Carlos Valente cd0999b2ab fix: collapsed blocks dont render children 2025-06-19 18:53:28 +02:00
Carlos Valente b4c60f3f04 refactor: type cleanup and test improvements 2025-06-19 18:53:28 +02:00
Carlos Valente 5cefad3666 feat: allow dissolving a block 2025-06-19 18:53:28 +02:00
Carlos Valente 7a6ecd8c34 fix: uncontrolled prop on controlled component 2025-06-19 18:53:28 +02:00
Carlos Valente ba96ecfd91 refactor: improve return of reorder 2025-06-19 18:53:28 +02:00
Carlos Valente 7bed3757f2 refactor: mutations on batch elements must have IDs 2025-06-19 18:53:28 +02:00
Carlos Valente e2e755b1d2 chore: upgrade dependencies 2025-06-19 18:53:28 +02:00
Carlos Valente a77d23109d refactor: extract utility to merge two arrays 2025-06-19 18:53:28 +02:00
Carlos Valente 0eb3b8d382 refactor: change network mode defaults 2025-06-19 18:53:28 +02:00
Carlos Valente 0021185288 fix: delete nested events 2025-06-19 18:53:28 +02:00
Carlos Valente 94c72ff4f6 fix: add event at end of block 2025-06-19 18:53:28 +02:00
Carlos Valente e4c08dc9b2 refactor: make finder available in exported rundown 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen ec74af0d62 assert non null and update test (#1604) 2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen b6d72dd082 Fix project renumber (#1597)
* fix: generateUniqueFileName

* loadProject should not generate new names

* update comments

* extract and test getProjectNumber

* spell

* finish jsdoc

* use getProjectNumber

* cleanup loadProject

* create a `incrementProjectNumber` function

* spelling

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>

---------

Co-authored-by: Carlos Valente <34649812+cpvalente@users.noreply.github.com>
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 31c311daf0 Refactor: WebSocket from flush queue to one patch (#1595)
* change flush to one patch

* remove unused types

* create batch

* merge patch into eventStore

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen e9b3cc6090 Refactor: ms for api calls (#1593)
* use ms for api calls

* change it in UI

* update comment

* rename variables

* refactor: addtime api logic
2025-06-19 18:53:28 +02:00
Alex Christoffer Rasmussen 543b04a097 fix test (#1601)
* fix test

* add comment
2025-06-19 18:53:28 +02:00
Carlos Valente d39b85b6e6 fix: rebase master 2025-06-19 18:53:28 +02:00
Carlos Valente bbe107bb2b chore: correct test path 2025-06-19 18:53:28 +02:00
Carlos Valente c616240db1 refactor: extract rundown parsing
refactor: implement groups in editor
2025-06-19 18:53:28 +02:00
Carlos Valente 166be66ce3 chore: improve convention entry <> event 2025-06-19 18:53:28 +02:00
Carlos Valente 4180d0a337 refactor: maintain flat orders 2025-06-19 18:53:28 +02:00
Carlos Valente 78108e316c refactor: implement operations on nested events 2025-06-19 18:53:28 +02:00
Carlos Valente 3bb8b70915 refactor: process events in rundown 2025-06-19 18:53:28 +02:00
Carlos Valente 1c4f13a0ed chore: improve convention entry <> event 2025-06-19 18:53:28 +02:00
Carlos Valente 3ca0abad53 chore: rename currentBlock > parent 2025-06-19 18:53:28 +02:00
Carlos Valente 030c8f897f refactor: fix delay positioning in gaps 2025-06-19 18:53:28 +02:00
Carlos Valente 68175cfa3b refactor(e2e): skip flaky test 2025-06-19 18:53:28 +02:00
Carlos Valente fd8f757851 refactor: improve project loading 2025-06-19 18:53:28 +02:00
Carlos Valente 876d111c61 refactor: gather group metadata 2025-06-19 18:53:28 +02:00
Carlos Valente 730cb95c04 refactor: swap maintains schedule 2025-06-19 18:53:28 +02:00
Carlos Valente 3c388d4fb5 chore: rename files 2025-06-19 18:53:28 +02:00
Carlos Valente 2e23718d73 refactor: restructure model to contain an object of rundowns 2025-06-19 18:53:28 +02:00
Carlos Valente 89ea8c470b refactor: clearer relationship on rundown elements 2025-06-19 18:53:28 +02:00
Carlos Valente 178640bfc4 refactor: use strict typing 2025-06-19 18:53:28 +02:00
Carlos Valente 4ed38340e0 refactor: remove stop as a possible end action 2025-06-19 18:53:28 +02:00
Carlos Valente 69eb9a5eff refactor: restructure model to contain an object of rundowns 2025-06-19 18:53:28 +02:00
Carlos Valente 351425127a chore: remove IDE files 2025-06-19 18:53:28 +02:00
Carlos Valente 95f2ba37cc refactor: remove unused and legacy code
- remove legacy migrations
- remove unused server code
- remove unused UI code
2025-06-19 18:53:28 +02:00
jwetzell fe8bbd5003 add electron and esbuild to allow list for postinstall builds 2025-06-06 20:25:46 +02:00
jwetzell c6e38a2b4a upgrade vite to v6 (#1626) 2025-06-05 06:27:59 +02:00
jwetzell 56f18fe71c update node and pnpm versions in README 2025-06-05 06:13:04 +02:00
jwetzell 0b9306de82 update pnpm to latest v10 2025-06-05 06:13:04 +02:00
jwetzell 3b1e6cf33d update node version in workflows 2025-06-05 06:13:04 +02:00
jwetzell dda219ebbe update NodeJS to v22 2025-06-05 06:13:04 +02:00
jwetzell 8bb771e5c6 upgrade electron and electron-builder 2025-06-05 06:13:04 +02:00
Shobhit Nagpal 8723bcbd33 refactor: block event xlsx import (#1623) 2025-06-02 06:56:46 +02:00
Shobhit Nagpal 0dcea4f2d7 fix: pass empty string fallback when parsing fields for export (#1616) 2025-05-25 19:58:41 +02:00
Shobhit Nagpal 696c016c90 feat: custom data for projects (#1571) 2025-05-23 09:04:57 +02:00
Shobhit Nagpal eed6373dbf refactor: change offset classes depending on playback state (#1611)
* refactor: assign offsetClasses based on playback state
2025-05-23 09:04:57 +02:00
59 changed files with 2173 additions and 732 deletions
+6 -6
View File
@@ -14,12 +14,12 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
version: 10
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -56,12 +56,12 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
version: 10
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -89,12 +89,12 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
version: 10
- name: Install dependencies
run: pnpm install --frozen-lockfile
+2 -2
View File
@@ -20,13 +20,13 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
registry-url: 'https://registry.npmjs.org'
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
version: 10
- name: Install dependencies
run: pnpm install --frozen-lockfile
+4 -4
View File
@@ -17,12 +17,12 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
version: 10
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -73,12 +73,12 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9
version: 10
- name: Install dependencies
run: pnpm install --frozen-lockfile
+1 -1
View File
@@ -1 +1 @@
v20.15.1
v22.15.1
+2 -2
View File
@@ -9,8 +9,8 @@ Ontime consists of 3 distinct parts
The steps below will assume you have locally installed the necessary dependencies.
Other dependencies will be installed as part of the setup
- __node__ (~20)
- __pnpm__ (~9)
- __node__ (~22)
- __pnpm__ (~10)
- __docker__ (only necessary to run and build docker images)
## LOCAL DEVELOPMENT
+3 -3
View File
@@ -1,13 +1,13 @@
FROM node:20-bullseye AS builder
FROM node:22-bullseye AS builder
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN npm install -g pnpm@9.5.0
RUN npm install -g pnpm@10.11.0
COPY . /app
WORKDIR /app
RUN pnpm --filter=ontime-ui --filter=ontime-server --filter=ontime-utils install --config.dedupe-peer-dependents=false --frozen-lockfile
RUN pnpm --filter=ontime-ui --filter=ontime-server run build:docker
FROM node:20-alpine
FROM node:22-alpine
# Set environment variables
# Environment Variable to signal that we are running production
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@getontime/cli",
"version": "3.15.2",
"version": "3.16.0",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "ontime-ui",
"version": "3.15.2",
"version": "3.16.0",
"private": true,
"type": "module",
"dependencies": {
@@ -72,7 +72,7 @@
"@types/react-dom": "^18.0.10",
"@typescript-eslint/eslint-plugin": "catalog:",
"@typescript-eslint/parser": "catalog:",
"@vitejs/plugin-react": "^4.2.1",
"@vitejs/plugin-react": "4.5.1",
"eslint": "catalog:",
"eslint-config-prettier": "catalog:",
"eslint-plugin-jest": "^28.6.0",
@@ -86,10 +86,10 @@
"prettier": "catalog:",
"sass": "^1.57.1",
"typescript": "catalog:",
"vite": "^5.2.11",
"vite-plugin-compression2": "^1.3.3",
"vite-plugin-svgr": "^4.2.0",
"vite-tsconfig-paths": "^4.3.1",
"vite": "6.3.1",
"vite-plugin-compression2": "1.4.0",
"vite-plugin-svgr": "4.3.0",
"vite-tsconfig-paths": "5.1.4",
"vitest": "catalog:"
}
}
@@ -41,7 +41,7 @@ export function useFlatRundown() {
// update data whenever the revision changes
useEffect(() => {
if (data.revision !== -1 && data.revision !== prevRevision) {
if (data.revision !== -1 || data.revision !== prevRevision) {
const flatRundown = data.order.map((id) => data.rundown[id]);
setFlatRunDown(flatRundown);
setPrevRevision(data.revision);
@@ -8,4 +8,5 @@ export const projectDataPlaceholder: ProjectData = {
backstageUrl: '',
backstageInfo: '',
projectLogo: null,
custom: [],
};
@@ -2,4 +2,4 @@
exports[`cx() > ignores falsy values 1`] = `""`;
exports[`cx() > merges styles 1`] = `"_test_98a1e0 _another_98a1e0"`;
exports[`cx() > merges styles 1`] = `"_test_d5d33f _another_d5d33f"`;
@@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { useFieldArray, useForm } from 'react-hook-form';
import { IoTrash } from 'react-icons/io5';
import { Button, Input, Textarea } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';
@@ -23,6 +24,7 @@ type ProjectCreateFormValues = {
publicUrl?: string;
backstageInfo?: string;
backstageUrl?: string;
custom?: { title: string; value: string }[];
};
export default function ProjectCreateForm(props: ProjectCreateFromProps) {
@@ -34,6 +36,7 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) {
const {
handleSubmit,
register,
control,
formState: { isSubmitting, isValid },
setFocus,
} = useForm<ProjectCreateFormValues>({
@@ -44,6 +47,11 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) {
},
});
const { fields, append, remove } = useFieldArray({
control,
name: 'custom',
});
// set focus to first field
useEffect(() => {
setFocus('title');
@@ -59,6 +67,7 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) {
...values,
filename,
});
await queryClient.invalidateQueries({ queryKey: PROJECT_LIST });
onClose();
} catch (error) {
@@ -66,6 +75,10 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) {
}
};
const handleAddCustom = () => {
append({ title: '', value: '' });
};
return (
<Panel.Section
as='form'
@@ -151,6 +164,42 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) {
{...register('backstageUrl')}
/>
</label>
<Panel.Section>
<Panel.ListItem>
<Panel.Field title='Custom data' description='Add custom data for your project' />
<Button variant='ontime-subtle' onClick={handleAddCustom}>
+
</Button>
</Panel.ListItem>
{fields.map((field, idx) => (
<div key={field.id} className={style.customDataItem}>
<Panel.Paragraph>{idx + 1}.</Panel.Paragraph>
<label>
Title
<Input
variant='ontime-filled'
size='sm'
placeholder={field.title}
autoComplete='off'
{...register(`custom.${idx}.title` as const)}
/>
</label>
<label>
Value
<Input
variant='ontime-filled'
size='sm'
placeholder={field.value}
autoComplete='off'
{...register(`custom.${idx}.value` as const)}
/>
</label>
<Button variant='ontime-ghosted' onClick={() => remove(idx)}>
<IoTrash />
</Button>
</div>
))}
</Panel.Section>
</Panel.Section>
</Panel.Section>
);
@@ -1,6 +1,6 @@
import { ChangeEvent, useEffect, useRef } from 'react';
import { useForm } from 'react-hook-form';
import { IoDownloadOutline, IoTrash } from 'react-icons/io5';
import { useFieldArray, useForm } from 'react-hook-form';
import { IoAdd, IoDownloadOutline, IoTrash } from 'react-icons/io5';
import { Button, Input, Textarea } from '@chakra-ui/react';
import { type ProjectData } from 'ontime-types';
@@ -25,6 +25,7 @@ export default function ProjectData() {
formState: { isSubmitting, isValid, isDirty, errors },
setError,
watch,
control,
setValue,
} = useForm({
defaultValues: data,
@@ -32,6 +33,12 @@ export default function ProjectData() {
resetOptions: {
keepDirtyValues: true,
},
mode: 'onChange',
});
const { fields, append, remove } = useFieldArray({
control,
name: 'custom',
});
// reset form values if data changes
@@ -77,6 +84,10 @@ export default function ProjectData() {
});
};
const handleAddCustom = () => {
append({ title: '', value: '' });
};
const onSubmit = async (formData: ProjectData) => {
try {
await postProjectData(formData);
@@ -231,6 +242,69 @@ export default function ProjectData() {
{...register('backstageUrl')}
/>
</label>
<Panel.Section style={{ marginTop: 0 }}>
<Panel.ListItem>
<Panel.Field title='Custom data' description='' />
<Button leftIcon={<IoAdd />} size='sm' variant='ontime-subtle' onClick={handleAddCustom}>
Add
</Button>
</Panel.ListItem>
{fields.length > 0 &&
fields.map((field, idx) => {
const rowErrors = errors.custom?.[idx] as
| {
title?: { message?: string };
value?: { message?: string };
}
| undefined;
return (
<div key={field.id} className={style.customDataItem}>
<div>
<div className={style.titleRow}>
<label>
Title
<Input
variant='ontime-filled'
size='sm'
defaultValue={field.title}
placeholder='Title of your custom data'
autoComplete='off'
{...register(`custom.${idx}.title`, {
required: { value: true, message: 'Field cannot be empty' },
})}
/>
</label>
<Button
size='sm'
variant='ontime-subtle'
color='#FA5656' // $red-500
onClick={() => remove(idx)}
leftIcon={<IoTrash />}
>
Delete Entry
</Button>
</div>
{rowErrors?.title?.message && <Panel.Error>{rowErrors.title.message}</Panel.Error>}
</div>
<label>
Value
<Textarea
variant='ontime-filled'
resize='none'
size='sm'
defaultValue={field.value}
autoComplete='off'
placeholder='Text of your custom data'
{...register(`custom.${idx}.value`, {
required: { value: true, message: 'Field cannot be empty' },
})}
/>
{rowErrors?.value?.message && <Panel.Error>{rowErrors.value.message}</Panel.Error>}
</label>
</div>
);
})}
</Panel.Section>
</Panel.Section>
</Panel.Card>
</Panel.Section>
@@ -57,3 +57,18 @@
height: auto;
}
}
.customDataItem {
display: contents;
width: 100%;
.titleRow{
display: flex;
gap: 1rem;
align-items: end;
label {
flex: 1;
}
}
}
@@ -45,6 +45,10 @@
@include ellipsis-overflow;
}
.offset {
color: $muted-gray;
}
.ahead {
color: $playback-ahead;
}
@@ -1,4 +1,5 @@
import { memo, PropsWithChildren, ReactNode, useMemo } from 'react';
import { Playback } from 'ontime-types';
import { millisToString } from 'ontime-utils';
import ErrorBoundary from '../../common/components/error-boundary/ErrorBoundary';
@@ -163,10 +164,10 @@ function ProgressOverview() {
}
function RuntimeOverview() {
const { clock, offset } = useRuntimePlaybackOverview();
const { clock, offset, playback } = useRuntimePlaybackOverview();
const offsetText = getOffsetText(offset);
const offsetClasses = offset === null ? undefined : offset <= 0 ? style.behind : style.ahead;
const offsetClasses = cx([style.offset, playback !== Playback.Stop && (offset < 0 ? style.behind : style.ahead)]);
return (
<>
@@ -96,11 +96,12 @@ describe('makeTable()', () => {
"Is Public? (x)",
"Skip?",
"lighting",
"Type",
],
[
"00:00:00",
"00:00:00",
"...",
"",
"",
"",
"",
@@ -109,6 +110,7 @@ describe('makeTable()', () => {
"x",
"",
"",
"",
],
]
`);
@@ -29,6 +29,7 @@ interface EventRowProps {
export default memo(EventRow, (prevProps, nextProps) => {
return (
prevProps.rowId === nextProps.rowId &&
prevProps.event.id === nextProps.event.id &&
prevProps.event.revision === nextProps.event.revision &&
prevProps.eventIndex === nextProps.eventIndex &&
prevProps.rowIndex === nextProps.rowIndex &&
@@ -20,7 +20,7 @@ type CsvHeaderKey = OntimeEntryCommonKeys | keyof CustomFields;
export const parseField = (field: CsvHeaderKey, data: unknown): string => {
if (field === 'timeStart' || field === 'timeEnd' || field === 'duration') {
return millisToString(data as MaybeNumber);
return millisToString(data as MaybeNumber, { fallback: '' });
}
if (field === 'isPublic' || field === 'skip') {
@@ -59,6 +59,7 @@ export const makeTable = (headerData: ProjectData, rundown: OntimeRundown, custo
'isPublic',
'skip',
...customFieldKeys,
'type',
];
const fieldTitles = [
@@ -73,6 +74,7 @@ export const makeTable = (headerData: ProjectData, rundown: OntimeRundown, custo
'Is Public? (x)',
'Skip?',
...customFieldLabels,
'Type',
];
// add header row to data
@@ -25,7 +25,9 @@
flex: 1;
max-height: 100%;
overflow-y: auto;
width: min(calc(100vw - 4rem), 800px);
width: min(calc(100vw - 4rem), 960px);
padding-bottom: 10vh;
}
.info__label {
@@ -33,11 +35,15 @@
color: var(--label-color-override, $viewer-label-color);
text-transform: uppercase;
margin-top: $view-element-gap;
white-space: pre;
}
.info__value {
white-space: break-spaces;
}
a.info__value {
color: $action-text-color;
&:hover {
color: $ontime-color;
}
@@ -8,6 +8,7 @@ import { useWindowTitle } from '../../common/hooks/useWindowTitle';
import { useTranslation } from '../../translation/TranslationProvider';
import BackstageInfo from './backstage-info/BackstageInfo';
import CustomInfo from './custom-info/CustomInfo';
import PublicInfo from './public-info/PublicInfo';
import { projectInfoOptions } from './projectInfo.options';
@@ -66,6 +67,7 @@ export default function ProjectInfo(props: ProjectInfoProps) {
)}
<BackstageInfo general={general} />
<PublicInfo general={general} />
<CustomInfo general={general} />
</div>
</div>
);
@@ -0,0 +1,36 @@
import { Fragment } from 'react';
import { useSearchParams } from 'react-router-dom';
import { ProjectData } from 'ontime-types';
import { isStringBoolean } from '../../../features/viewers/common/viewUtils';
interface CustomInfoProps {
general: ProjectData;
}
export default function CustomInfo(props: CustomInfoProps) {
const { general } = props;
const [searchParams] = useSearchParams();
const showCustom = isStringBoolean(searchParams.get('showCustom'));
if (!showCustom || general.custom === undefined || general.custom.length === 0) {
return null;
}
return (
<>
{general.custom.map((info, idx) => {
if (!info.title || !info.value) {
return null;
}
return (
<Fragment key={`${info.title}-${idx}`}>
<div className='info__label'>{info.title}</div>
<div className='info__value'>{info.value}</div>
</Fragment>
);
})}
</>
);
}
@@ -9,14 +9,21 @@ export const projectInfoOptions: ViewOption[] = [
{
id: 'showBackstage',
title: 'Show backstage Data',
description: 'Weather to show fields related to the backstage views',
description: 'Whether to show fields related to the backstage views',
type: 'boolean',
defaultValue: false,
},
{
id: 'showPublic',
title: 'Show Public Data',
description: 'Weather to show fields related to the public views',
description: 'Whether to show fields related to the public views',
type: 'boolean',
defaultValue: false,
},
{
id: 'showCustom',
title: 'Show Custom Data',
description: 'Whether to show fields related to the custom data',
type: 'boolean',
defaultValue: false,
},
+3 -3
View File
@@ -70,9 +70,9 @@ export default defineConfig({
preprocessorOptions: {
scss: {
additionalData: `
@use './src/theme/ontimeColours' as *;
@use './src/theme/ontimeStyles' as *;
@use './src/theme/mixins' as *;
@use '@/theme/ontimeColours' as *;
@use '@/theme/ontimeStyles' as *;
@use '@/theme/mixins' as *;
`,
},
},
+4 -6
View File
@@ -1,6 +1,6 @@
{
"name": "ontime-electron",
"version": "3.15.2",
"version": "3.16.0",
"author": "Carlos Valente",
"description": "Time keeping for live events",
"repository": "https://github.com/cpvalente/ontime",
@@ -12,8 +12,8 @@
"license": "AGPL-3.0-only",
"main": "src/main.js",
"devDependencies": {
"electron": "^31.2.0",
"electron-builder": "^24.13.3",
"electron": "36.3.1",
"electron-builder": "26.0.12",
"eslint": "catalog:",
"eslint-config-prettier": "catalog:",
"prettier": "catalog:",
@@ -38,9 +38,7 @@
"icon": "icon.icns"
},
"mac": {
"notarize": {
"teamId": "MDAU6QK6R4"
},
"notarize": true,
"hardenedRuntime": true,
"gatekeeperAssess": false,
"entitlements": "./entitlements.plist",
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "ontime-server",
"type": "module",
"main": "src/index.ts",
"version": "3.15.2",
"version": "3.16.0",
"exports": "./src/index.js",
"dependencies": {
"@googleapis/sheets": "^5.0.5",
+1 -1
View File
@@ -5,7 +5,7 @@ import * as dgram from 'node:dgram';
import { logger } from '../classes/Logger.js';
import { dispatchFromAdapter } from '../api-integration/integration.controller.js';
import { isOntimeCloud } from '../externals.js';
import { isOntimeCloud } from '../setup/environment.js';
import { integrationPayloadFromPath } from './utils/parse.js';
import type { IAdapter } from './IAdapter.js';
@@ -12,7 +12,7 @@ import { getPropertyFromPath } from 'ontime-utils';
import { logger } from '../../classes/Logger.js';
import { getState, type RuntimeState } from '../../stores/runtimeState.js';
import { isOntimeCloud } from '../../externals.js';
import { isOntimeCloud } from '../../setup/environment.js';
import { emitOSC } from './clients/osc.client.js';
import { emitHTTP } from './clients/http.client.js';
+2 -1
View File
@@ -58,6 +58,7 @@ export async function createProjectFile(req: Request, res: Response<{ filename:
backstageUrl: req.body?.backstageUrl ?? '',
backstageInfo: req.body?.backstageInfo ?? '',
projectLogo: req.body?.projectLogo ?? null,
custom: req.body?.custom ?? [],
},
});
@@ -203,7 +204,7 @@ export async function loadProject(req: Request, res: Response<MessageResponse |
/**
* Loads the demo project
*/
export async function loadDemo(req: Request, res: Response<MessageResponse | ErrorResponse>) {
export async function loadDemo(_req: Request, res: Response<MessageResponse | ErrorResponse>) {
try {
const projectName = await projectService.loadDemoProject();
@@ -16,6 +16,7 @@ export const validateNewProject = [
body('backstageInfo').optional().isString().trim(),
body('projectLogo').optional().isString().trim(),
body('endMessage').optional().isString().trim(),
body('custom').optional().isArray(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
@@ -5,11 +5,11 @@ import type { Request, Response } from 'express';
import { removeUndefined } from '../../utils/parserUtils.js';
import { failEmptyObjects } from '../../utils/routerUtils.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import { editCurrentProjectData } from '../../services/project-service/ProjectService.js';
import * as projectDao from './project.dao.js';
export function getProjectData(_req: Request, res: Response<ProjectData>) {
res.json(getDataProvider().getProjectData());
res.json(projectDao.getProjectData());
}
export async function postProjectData(req: Request, res: Response<ProjectData | ErrorResponse>) {
@@ -27,6 +27,7 @@ export async function postProjectData(req: Request, res: Response<ProjectData |
backstageInfo: req.body?.backstageInfo,
endMessage: req.body?.endMessage,
projectLogo: req.body?.projectLogo,
custom: req.body?.custom,
});
const updatedData = await editCurrentProjectData(newData);
@@ -0,0 +1,9 @@
import { ProjectData } from 'ontime-types';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
/**
* Gets a copy of the stored project data
*/
export function getProjectData(): ProjectData {
return structuredClone(getDataProvider().getProjectData());
}
@@ -10,6 +10,9 @@ export const projectSanitiser = [
body('backstageInfo').optional().isString().trim(),
body('endMessage').optional().isString().trim(),
body('projectLogo').optional({ nullable: true }).isString().trim(),
body('custom').optional().isArray(),
body('custom.*.title').optional().isString().trim().notEmpty(),
body('custom.*.value').optional().isString().trim().notEmpty(),
(req: Request, res: Response, next: NextFunction) => {
const errors = validationResult(req);
@@ -10,6 +10,7 @@ import { getNetworkInterfaces } from '../../utils/network.js';
import { getTimezoneLabel } from '../../utils/time.js';
import { password, routerPrefix } from '../../externals.js';
import { hashPassword } from '../../utils/hash.js';
import { ONTIME_VERSION } from '../../ONTIME_VERSION.js';
const startedAt = new Date();
@@ -28,6 +29,7 @@ export async function getSessionStats(): Promise<SessionStats> {
projectName,
playback,
timezone: getTimezoneLabel(startedAt),
version: ONTIME_VERSION,
};
}
@@ -3,7 +3,7 @@ import { getErrorMessage, obfuscate } from 'ontime-utils';
import type { Request, Response } from 'express';
import { isDocker } from '../../externals.js';
import { isDocker } from '../../setup/environment.js';
import { failEmptyObjects } from '../../utils/routerUtils.js';
import { getDataProvider } from '../../classes/data-provider/DataProvider.js';
import * as appState from '../../services/app-state-service/AppStateService.js';
+2 -1
View File
@@ -9,7 +9,8 @@ import cookieParser from 'cookie-parser';
// import utils
import { publicDir, srcDir } from './setup/index.js';
import { environment, isProduction, updateRouterPrefix } from './externals.js';
import { environment, isProduction } from './setup/environment.js';
import { updateRouterPrefix } from './externals.js';
import { ONTIME_VERSION } from './ONTIME_VERSION.js';
import { consoleSuccess, consoleHighlight, consoleError } from './utils/console.js';
+1 -1
View File
@@ -4,7 +4,7 @@ import { generateId, millisToString } from 'ontime-utils';
import { clock } from '../services/Clock.js';
import { socket } from '../adapters/WebsocketAdapter.js';
import { consoleSubdued, consoleError } from '../utils/console.js';
import { isProduction } from '../externals.js';
import { isProduction } from '../setup/environment.js';
class Logger {
private queue: Log[];
@@ -14,7 +14,7 @@ import { JSONFilePreset } from 'lowdb/node';
import { isPath } from '../../utils/fileManagement.js';
import { shouldCrashDev } from '../../utils/development.js';
import { isTest } from '../../externals.js';
import { isTest } from '../../setup/environment.js';
import { safeMerge } from './DataProvider.utils.js';
@@ -63,7 +63,7 @@ function getData(): Readonly<DatabaseModel> {
}
async function setProjectData(newData: Partial<ProjectData>): ReadonlyPromise<ProjectData> {
db.data.project = { ...db.data.project, ...newData };
db.data.project = { ...structuredClone(db.data.project), ...structuredClone(newData) }; // Performing deep copy as we're updating / merging data
await persist();
return db.data.project;
}
@@ -4,24 +4,27 @@ import { DatabaseModel } from 'ontime-types';
* Merges a partial ontime project into a given ontime project
*/
export function safeMerge(existing: DatabaseModel, newData: Partial<DatabaseModel>): DatabaseModel {
const deepExisting = structuredClone(existing);
const deepNewData = structuredClone(newData);
const {
rundown = existing.rundown,
rundown = deepExisting.rundown,
project = {},
settings = {},
viewSettings = {},
urlPresets = existing.urlPresets,
customFields = existing.customFields,
automation = existing.automation,
} = newData;
urlPresets = deepExisting.urlPresets,
customFields = deepExisting.customFields,
automation = deepExisting.automation,
} = deepNewData;
return {
...existing,
...deepExisting,
rundown,
project: { ...existing.project, ...project },
settings: { ...existing.settings, ...settings },
viewSettings: { ...existing.viewSettings, ...viewSettings },
urlPresets: urlPresets ?? existing.urlPresets,
customFields: customFields ?? existing.customFields,
automation: { ...existing.automation, ...automation },
project: { ...deepExisting.project, ...project },
settings: { ...deepExisting.settings, ...settings },
viewSettings: { ...deepExisting.viewSettings, ...viewSettings },
urlPresets: urlPresets ?? deepExisting.urlPresets,
customFields: customFields ?? deepExisting.customFields,
automation: { ...deepExisting.automation, ...automation },
};
}
@@ -12,6 +12,12 @@ describe('safeMerge', () => {
publicInfo: 'existing backstageInfo',
backstageInfo: 'existing backstageInfo',
projectLogo: null,
custom: [
{
title: 'existing custom title',
value: 'existing custom value',
},
],
},
settings: {
app: 'ontime',
@@ -62,6 +68,12 @@ describe('safeMerge', () => {
project: {
title: 'new title',
publicInfo: 'new public info',
custom: [
{
title: 'new custom title',
value: 'new custom value',
},
],
},
};
// @ts-expect-error -- just testing
@@ -74,6 +86,12 @@ describe('safeMerge', () => {
backstageUrl: 'existing backstageUrl',
backstageInfo: 'existing backstageInfo',
projectLogo: null,
custom: [
{
title: 'new custom title',
value: 'new custom value',
},
],
});
});
@@ -107,6 +125,7 @@ describe('safeMerge', () => {
backstageUrl: '',
backstageInfo: '',
projectLogo: null,
custom: [],
},
settings: {
app: 'ontime',
-8
View File
@@ -7,14 +7,6 @@ import { readFileSync, writeFileSync } from 'node:fs';
import { srcFiles } from './setup/index.js';
// =================================================
// resolve running environment
const env = process.env.NODE_ENV || 'production';
export const isTest = Boolean(process.env.IS_TEST);
export const environment = isTest ? 'test' : env;
export const isDocker = env === 'docker';
export const isProduction = isDocker || (env === 'production' && !isTest);
export const isOntimeCloud = Boolean(process.env.IS_CLOUD);
export const password = process.env.SESSION_PASSWORD;
export const routerPrefix = process.env.ROUTER_PREFIX;
+1
View File
@@ -11,6 +11,7 @@ export const dbModel: DatabaseModel = {
backstageUrl: '',
backstageInfo: '',
projectLogo: null,
custom: [],
},
settings: {
app: 'ontime',
+1
View File
@@ -413,6 +413,7 @@ export const demoDb: DatabaseModel = {
backstageUrl: 'www.github.com/cpvalente/ontime',
backstageInfo: 'Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal',
projectLogo: null,
custom: [],
},
settings: {
app: 'ontime',
@@ -2,7 +2,7 @@ import { Low } from 'lowdb';
import { JSONFile } from 'lowdb/node';
import { publicFiles } from '../../setup/index.js';
import { isTest } from '../../externals.js';
import { isTest } from '../../setup/environment.js';
import { isPath } from '../../utils/fileManagement.js';
import { shouldCrashDev } from '../../utils/development.js';
@@ -314,17 +314,13 @@ export async function patchCurrentProject(data: Partial<DatabaseModel>) {
}
/**
* Changes the title of a project
* it handles invalidating the necessary data
* Patches the current project data
* Handles deleting the local logo if the logo has been removed
*/
export async function editCurrentProjectData(newData: Partial<ProjectData>) {
const currentProjectData = getDataProvider().getProjectData();
const updatedProjectData = await getDataProvider().setProjectData(newData);
if (currentProjectData.title !== updatedProjectData.title) {
// something
}
// Delete the old logo if the logo has been removed
if (!updatedProjectData.projectLogo && currentProjectData.projectLogo) {
const filePath = join(publicDir.logoDir, currentProjectData.projectLogo);
+8
View File
@@ -0,0 +1,8 @@
// resolve running environment
const env = process.env.NODE_ENV || 'production';
export const isTest = Boolean(process.env.IS_TEST);
export const environment = isTest ? 'test' : env;
export const isDocker = env === 'docker';
export const isProduction = isDocker || (env === 'production' && !isTest);
export const isOntimeCloud = Boolean(process.env.IS_CLOUD);
+1 -1
View File
@@ -10,7 +10,7 @@ import { dirname, join } from 'path';
import { config } from './config.js';
import { ensureDirectory } from '../utils/fileManagement.js';
import { isProduction } from '../externals.js';
import { isProduction } from './environment.js';
/**
* Returns public path depending on OS
@@ -65,6 +65,7 @@ describe('parseProject()', () => {
publicInfo: 'publicInfo',
backstageUrl: 'backstageUrl',
backstageInfo: 'backstageInfo',
custom: [],
},
},
errorEmitter,
@@ -77,6 +78,7 @@ describe('parseProject()', () => {
backstageUrl: 'backstageUrl',
backstageInfo: 'backstageInfo',
projectLogo: null,
custom: [],
});
expect(errorEmitter).not.toHaveBeenCalled();
});
+1 -1
View File
@@ -1,4 +1,4 @@
import { isProduction } from '../externals.js';
import { isProduction } from '../setup/environment.js';
import { consoleError } from './console.js';
+1 -1
View File
@@ -4,7 +4,7 @@ import type { Server } from 'http';
import { networkInterfaces } from 'os';
import type { AddressInfo } from 'net';
import { isDocker, isOntimeCloud, isProduction } from '../externals.js';
import { isDocker, isOntimeCloud, isProduction } from '../setup/environment.js';
import { logger } from '../classes/Logger.js';
/**
+1 -1
View File
@@ -215,7 +215,7 @@ export const parseExcel = (
const maybeTimeType = makeString(column, '');
if (maybeTimeType === 'block') {
event.type = SupportedEvent.Block;
} else if (maybeTimeType === '' || isKnownTimerType(maybeTimeType)) {
} else if (maybeTimeType === '' || maybeTimeType === 'event' || isKnownTimerType(maybeTimeType)) {
event.type = SupportedEvent.Event;
event.timerType = validateTimerType(maybeTimeType);
} else {
+1
View File
@@ -114,6 +114,7 @@ export function parseProject(data: Partial<DatabaseModel>, emitError?: ErrorEmit
backstageUrl: data.project.backstageUrl ?? dbModel.project.backstageUrl,
backstageInfo: data.project.backstageInfo ?? dbModel.project.backstageInfo,
projectLogo: data.project.projectLogo ?? dbModel.project.projectLogo,
custom: data.project.custom ?? dbModel.project.custom,
};
}
+3 -2
View File
@@ -410,7 +410,8 @@
"publicInfo": "Rehearsal Schedule - Turin 2022",
"backstageUrl": "www.github.com/cpvalente/ontime",
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
"projectLogo": null
"projectLogo": null,
"custom": []
},
"settings": {
"app": "ontime",
@@ -455,4 +456,4 @@
"label": "artist"
}
}
}
}
+3 -2
View File
@@ -410,7 +410,8 @@
"publicInfo": "Rehearsal Schedule - Turin 2022",
"backstageUrl": "www.github.com/cpvalente/ontime",
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
"projectLogo": null
"projectLogo": null,
"custom": []
},
"settings": {
"app": "ontime",
@@ -455,4 +456,4 @@
"label": "artist"
}
}
}
}
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "ontime",
"version": "3.15.2",
"version": "3.16.0",
"description": "Time keeping for live events",
"keywords": [
"ontime",
@@ -16,8 +16,8 @@
},
"license": "AGPL-3.0-only",
"engines": {
"node": "~20",
"pnpm": "~9"
"node": "~22",
"pnpm": "~10"
},
"type": "module",
"scripts": {
@@ -50,5 +50,5 @@
"turbo": "^2.3.3",
"typescript": "catalog:"
},
"packageManager": "pnpm@9.15.0"
"packageManager": "pnpm@10.11.0+sha512.6540583f41cc5f628eb3d9773ecee802f4f9ef9923cc45b69890fb47991d4b092964694ec3a4f738a420c918a333062c8b925d312f42e4f0c263eb603551f977"
}
@@ -15,6 +15,7 @@ export interface SessionStats {
projectName: string;
playback: Playback;
timezone: string;
version: string;
}
export interface GetInfo {
@@ -6,4 +6,5 @@ export type ProjectData = {
backstageUrl: string;
backstageInfo: string;
projectLogo: string | null;
custom: { title: string; value: string }[];
};
+1824 -628
View File
File diff suppressed because it is too large Load Diff
+9 -7
View File
@@ -1,14 +1,16 @@
# https://pnpm.io/pnpm-workspace_yaml
packages:
- "apps/*"
- "packages/*"
- apps/*
- packages/*
catalog:
typescript: 5.5.3
"@typescript-eslint/eslint-plugin": 7.16.1
"@typescript-eslint/parser": 7.16.1
"@types/node": 20.17.16
'@typescript-eslint/eslint-plugin': 7.16.1
'@typescript-eslint/parser': 7.16.1
'@types/node': 22.15.26
prettier: 3.3.1
eslint: 8.56.0
eslint-config-prettier: 9.1.0
eslint-plugin-prettier: 5.1.3
vitest: 2.1.6
vitest: 3.2.1
onlyBuiltDependencies:
- electron
- esbuild