mirror of
https://github.com/cpvalente/ontime.git
synced 2026-07-26 10:38:55 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1025347c08 | |||
| 43988ea12b | |||
| 722e045b20 | |||
| ad6804019b | |||
| cd40a6e55e | |||
| 08d9e24871 | |||
| 0649678dca | |||
| a00ec2d02a | |||
| 54a74ccc2a | |||
| 6f3ab274bd | |||
| 90870ecfb6 | |||
| b640e0e181 | |||
| c41fe824cf | |||
| 62c8319d70 | |||
| b1d23467a2 | |||
| 9a62daf047 | |||
| b9ab1c6fd7 | |||
| c1054711b0 | |||
| a4d4f29a37 | |||
| 2793aadea0 | |||
| e7cfb7d9d9 | |||
| 256a851c9b | |||
| 4ab9c81cb8 | |||
| 2f13d6c89e | |||
| f1f7bad25e | |||
| a8b52a48f7 | |||
| cd0999b2ab | |||
| b4c60f3f04 | |||
| 5cefad3666 | |||
| 7a6ecd8c34 | |||
| ba96ecfd91 | |||
| 7bed3757f2 | |||
| e2e755b1d2 | |||
| a77d23109d | |||
| 0eb3b8d382 | |||
| 0021185288 | |||
| 94c72ff4f6 | |||
| e4c08dc9b2 | |||
| ec74af0d62 | |||
| b6d72dd082 | |||
| 31c311daf0 | |||
| e9b3cc6090 | |||
| 543b04a097 | |||
| d39b85b6e6 | |||
| bbe107bb2b | |||
| c616240db1 | |||
| 166be66ce3 | |||
| 4180d0a337 | |||
| 78108e316c | |||
| 3bb8b70915 | |||
| 1c4f13a0ed | |||
| 3ca0abad53 | |||
| 030c8f897f | |||
| 68175cfa3b | |||
| fd8f757851 | |||
| 876d111c61 | |||
| 730cb95c04 | |||
| 3c388d4fb5 | |||
| 2e23718d73 | |||
| 89ea8c470b | |||
| 178640bfc4 | |||
| 4ed38340e0 | |||
| 69eb9a5eff | |||
| 351425127a | |||
| 95f2ba37cc | |||
| fe8bbd5003 | |||
| c6e38a2b4a | |||
| 56f18fe71c | |||
| 0b9306de82 | |||
| 3b1e6cf33d | |||
| dda219ebbe | |||
| 8bb771e5c6 | |||
| 8723bcbd33 | |||
| 0dcea4f2d7 | |||
| 696c016c90 | |||
| eed6373dbf |
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+2
-2
@@ -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
@@ -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,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",
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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 *;
|
||||
`,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ export const dbModel: DatabaseModel = {
|
||||
backstageUrl: '',
|
||||
backstageInfo: '',
|
||||
projectLogo: null,
|
||||
custom: [],
|
||||
},
|
||||
settings: {
|
||||
app: 'ontime',
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
@@ -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,4 +1,4 @@
|
||||
import { isProduction } from '../externals.js';
|
||||
import { isProduction } from '../setup/environment.js';
|
||||
|
||||
import { consoleError } from './console.js';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+3
-2
@@ -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
@@ -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 }[];
|
||||
};
|
||||
|
||||
Generated
+1824
-628
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user