mirror of
https://github.com/cpvalente/ontime.git
synced 2026-09-03 05:19:08 +00:00
feat: show progress in op and cuesheet (#605)
This commit is contained in:
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
. "$(dirname -- "$0")/_/husky.sh"
|
||||||
|
|
||||||
pnpm lint
|
pnpm lint-staged
|
||||||
|
|||||||
@@ -41,6 +41,7 @@
|
|||||||
"build:electron": "cross-env NODE_ENV=local vite build",
|
"build:electron": "cross-env NODE_ENV=local vite build",
|
||||||
"build:docker": "vite build",
|
"build:docker": "vite build",
|
||||||
"lint": "eslint . --quiet",
|
"lint": "eslint . --quiet",
|
||||||
|
"lint-staged": "eslint",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:pipeline": "vitest run",
|
"test:pipeline": "vitest run",
|
||||||
"cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf build"
|
"cleanup": "rm -rf .turbo && rm -rf node_modules && rm -rf build"
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ $progress-bar-br: 3px;
|
|||||||
border-radius: $progress-bar-br;
|
border-radius: $progress-bar-br;
|
||||||
background-color: var(--timer-progress-bg-override, $viewer-card-bg-color);
|
background-color: var(--timer-progress-bg-override, $viewer-card-bg-color);
|
||||||
display: flex;
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&--hidden {
|
&--hidden {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -31,7 +32,6 @@ $progress-bar-br: 3px;
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
right: 0;
|
right: 0;
|
||||||
border-radius: $progress-bar-br;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,12 +39,10 @@ $progress-bar-br: 3px;
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
right: 0;
|
right: 0;
|
||||||
border-radius: 0 $progress-bar-br $progress-bar-br 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiprogress-bar__bg-danger {
|
.multiprogress-bar__bg-danger {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height: inherit;
|
height: inherit;
|
||||||
right: 0;
|
right: 0;
|
||||||
border-radius: 0 $progress-bar-br $progress-bar-br 0;
|
|
||||||
}
|
}
|
||||||
+16
-12
@@ -3,7 +3,7 @@ import { clamp } from '../../utils/math';
|
|||||||
import './MultiPartProgressBar.scss';
|
import './MultiPartProgressBar.scss';
|
||||||
|
|
||||||
interface MultiPartProgressBar {
|
interface MultiPartProgressBar {
|
||||||
now: number;
|
now: number | null;
|
||||||
complete: number;
|
complete: number;
|
||||||
normalColor: string;
|
normalColor: string;
|
||||||
warning: number;
|
warning: number;
|
||||||
@@ -17,23 +17,27 @@ interface MultiPartProgressBar {
|
|||||||
export default function MultiPartProgressBar(props: MultiPartProgressBar) {
|
export default function MultiPartProgressBar(props: MultiPartProgressBar) {
|
||||||
const { now, complete, normalColor, warning, warningColor, danger, dangerColor, hidden, className = '' } = props;
|
const { now, complete, normalColor, warning, warningColor, danger, dangerColor, hidden, className = '' } = props;
|
||||||
|
|
||||||
const percentComplete = 100 - clamp(100 - (Math.max(now, 0) * 100) / complete, 0, 100);
|
const percentComplete = 100 - clamp(100 - (Math.max(now ?? 0, 0) * 100) / complete, 0, 100);
|
||||||
|
|
||||||
const dangerWidth = clamp((danger / complete) * 100, 0, 100);
|
const dangerWidth = clamp((danger / complete) * 100, 0, 100);
|
||||||
const warningWidth = clamp((warning / complete) * 100, 0, 100);
|
const warningWidth = clamp((warning / complete) * 100, 0, 100);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`multiprogress-bar ${hidden ? 'multiprogress-bar--hidden' : ''} ${className}`}>
|
<div className={`multiprogress-bar ${hidden ? 'multiprogress-bar--hidden' : ''} ${className}`}>
|
||||||
<div className='multiprogress-bar__bg-normal' style={{ backgroundColor: normalColor }} />
|
{now !== null && (
|
||||||
<div
|
<>
|
||||||
className='multiprogress-bar__bg-warning'
|
<div className='multiprogress-bar__bg-normal' style={{ backgroundColor: normalColor }} />
|
||||||
style={{ width: `${warningWidth}%`, backgroundColor: warningColor }}
|
<div
|
||||||
/>
|
className='multiprogress-bar__bg-warning'
|
||||||
<div
|
style={{ width: `${warningWidth}%`, backgroundColor: warningColor }}
|
||||||
className='multiprogress-bar__bg-danger'
|
/>
|
||||||
style={{ width: `${dangerWidth}%`, backgroundColor: dangerColor }}
|
<div
|
||||||
/>
|
className='multiprogress-bar__bg-danger'
|
||||||
<div className='multiprogress-bar__indicator' style={{ width: `${percentComplete}%` }} />
|
style={{ width: `${dangerWidth}%`, backgroundColor: dangerColor }}
|
||||||
|
/>
|
||||||
|
<div className='multiprogress-bar__indicator' style={{ width: `${percentComplete}%` }} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,6 @@
|
|||||||
|
|
||||||
& > * {
|
& > * {
|
||||||
border: 1px solid $white-10;
|
border: 1px solid $white-10;
|
||||||
border-radius: 4px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import useRundown from '../../common/hooks-query/useRundown';
|
|||||||
import useUserFields from '../../common/hooks-query/useUserFields';
|
import useUserFields from '../../common/hooks-query/useUserFields';
|
||||||
import ExportModal, { ExportType } from '../modals/export-modal/ExportModal';
|
import ExportModal, { ExportType } from '../modals/export-modal/ExportModal';
|
||||||
|
|
||||||
|
import CuesheetProgress from './cuesheet-progress/CuesheetProgress';
|
||||||
import CuesheetTableHeader from './cuesheet-table-header/CuesheetTableHeader';
|
import CuesheetTableHeader from './cuesheet-table-header/CuesheetTableHeader';
|
||||||
import Cuesheet from './Cuesheet';
|
import Cuesheet from './Cuesheet';
|
||||||
import { makeCuesheetColumns } from './cuesheetCols';
|
import { makeCuesheetColumns } from './cuesheetCols';
|
||||||
@@ -140,6 +141,7 @@ export default function CuesheetWrapper() {
|
|||||||
return (
|
return (
|
||||||
<div className={styles.tableWrapper} data-testid='cuesheet'>
|
<div className={styles.tableWrapper} data-testid='cuesheet'>
|
||||||
<CuesheetTableHeader handleExport={handleOpenModal} featureData={featureData} />
|
<CuesheetTableHeader handleExport={handleOpenModal} featureData={featureData} />
|
||||||
|
<CuesheetProgress />
|
||||||
<Cuesheet data={rundown} columns={columns} handleUpdate={handleUpdate} selectedId={featureData.selectedEventId} />
|
<Cuesheet data={rundown} columns={columns} handleUpdate={handleUpdate} selectedId={featureData.selectedEventId} />
|
||||||
<ExportModal isOpen={isModalOpen} onClose={onModalClose} />
|
<ExportModal isOpen={isModalOpen} onClose={onModalClose} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
.progressOverride {
|
||||||
|
height: 1rem;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
|
||||||
|
import { useTimer } from '../../../common/hooks/useSocket';
|
||||||
|
import useViewSettings from '../../../common/hooks-query/useViewSettings';
|
||||||
|
|
||||||
|
import styles from "./CuesheetProgress.module.scss"
|
||||||
|
|
||||||
|
export default function CuesheetProgress() {
|
||||||
|
const { data } = useViewSettings();
|
||||||
|
const timer = useTimer();
|
||||||
|
const totalTime = (timer.duration ?? 0) + (timer.addedTime ?? 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MultiPartProgressBar
|
||||||
|
now={timer.current}
|
||||||
|
complete={totalTime}
|
||||||
|
normalColor={data!.normalColor}
|
||||||
|
warning={data!.warningThreshold}
|
||||||
|
warningColor={data!.warningColor}
|
||||||
|
danger={data!.dangerThreshold}
|
||||||
|
dangerColor={data!.dangerColor}
|
||||||
|
className={styles.progressOverride}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,9 +2,11 @@
|
|||||||
@use '../../../../src/theme/v2Styles' as *;
|
@use '../../../../src/theme/v2Styles' as *;
|
||||||
|
|
||||||
.followButton {
|
.followButton {
|
||||||
position: relative;
|
position: fixed;
|
||||||
bottom: 12rem;
|
bottom: 1.5rem;
|
||||||
margin: 0 auto;
|
bottom: calc(1.5rem + env(safe-area-inset-bottom));
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -15,16 +15,19 @@
|
|||||||
background-color: $gray-1350;
|
background-color: $gray-1350;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
|
|
||||||
padding: 0.5rem 1rem;
|
|
||||||
border-bottom: 1px solid $white-10;
|
border-bottom: 1px solid $white-10;
|
||||||
box-shadow: $large-top-drawer-shadow;
|
box-shadow: $large-top-drawer-shadow;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timers {
|
||||||
display: grid;
|
display: grid;
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"playback timer1B timer2B timer3B";
|
"playback timer1B timer2B timer3B";
|
||||||
grid-template-columns: 1fr auto auto auto;
|
grid-template-columns: 1fr auto auto auto;
|
||||||
column-gap: 1.5rem;
|
column-gap: 1.5rem;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.playbackIcon {
|
.playbackIcon {
|
||||||
@@ -83,7 +86,7 @@
|
|||||||
|
|
||||||
// tablet
|
// tablet
|
||||||
@media (min-width: $min-tablet) {
|
@media (min-width: $min-tablet) {
|
||||||
.statusBar {
|
.timers {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
"playback timer1B timer2A timer3A"
|
"playback timer1B timer2A timer3A"
|
||||||
"title title timer2B timer3B";
|
"title title timer2B timer3B";
|
||||||
@@ -103,3 +106,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.progressOverride {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
import { useMemo } from 'react';
|
|
||||||
import { Playback } from 'ontime-types';
|
import { Playback } from 'ontime-types';
|
||||||
import { millisToString } from 'ontime-utils';
|
|
||||||
|
|
||||||
import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon';
|
import useViewSettings from '../../../common/hooks-query/useViewSettings';
|
||||||
import { useTimer } from '../../../common/hooks/useSocket';
|
|
||||||
import { cx } from '../../../common/utils/styleUtils';
|
import StatusBarProgress from './StatusBarProgress';
|
||||||
import { formatTime } from '../../../common/utils/time';
|
import StatusBarTimers from './StatusBarTimers';
|
||||||
|
|
||||||
import styles from './StatusBar.module.scss';
|
import styles from './StatusBar.module.scss';
|
||||||
|
|
||||||
@@ -22,73 +20,20 @@ interface StatusBarProps {
|
|||||||
export default function StatusBar(props: StatusBarProps) {
|
export default function StatusBar(props: StatusBarProps) {
|
||||||
const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
|
const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
|
||||||
|
|
||||||
const timer = useTimer();
|
const { data } = useViewSettings();
|
||||||
|
|
||||||
const getTimeStart = () => {
|
|
||||||
if (firstStart === undefined) {
|
|
||||||
return '...';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedEventId) {
|
|
||||||
if (firstId === selectedEventId) {
|
|
||||||
return millisToString(timer.expectedFinish);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return millisToString(firstStart);
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTimeEnd = () => {
|
|
||||||
if (lastEnd === undefined) {
|
|
||||||
return '...';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedEventId) {
|
|
||||||
if (lastId === selectedEventId) {
|
|
||||||
return millisToString(timer.expectedFinish);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return millisToString(lastEnd);
|
|
||||||
};
|
|
||||||
|
|
||||||
// use user defined format
|
|
||||||
const timeNow = formatTime(timer.clock, {
|
|
||||||
showSeconds: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const runningTime = millisToString(timer.current);
|
|
||||||
const elapsedTime = millisToString(timer.elapsed);
|
|
||||||
|
|
||||||
const PlaybackIconComponent = useMemo(() => {
|
|
||||||
const isPlaying = playback === Playback.Play || playback === Playback.Roll;
|
|
||||||
const classes = cx([styles.playbackIcon, isPlaying ? styles.active : null]);
|
|
||||||
return <PlaybackIcon state={playback} skipTooltip className={classes} />;
|
|
||||||
}, [playback]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.statusBar}>
|
<div className={styles.statusBar}>
|
||||||
{PlaybackIconComponent}
|
<StatusBarTimers
|
||||||
<div className={styles.timeNow}>
|
projectTitle={projectTitle}
|
||||||
<span className={styles.label}>Time now</span>
|
playback={playback}
|
||||||
<span className={styles.timer}>{timeNow}</span>
|
selectedEventId={selectedEventId}
|
||||||
</div>
|
firstStart={firstStart}
|
||||||
<div className={styles.elapsedTime}>
|
firstId={firstId}
|
||||||
<span className={styles.label}>Elapsed time</span>
|
lastEnd={lastEnd}
|
||||||
<span className={styles.timer}>{elapsedTime}</span>
|
lastId={lastId}
|
||||||
</div>
|
/>
|
||||||
<div className={styles.runningTime}>
|
{data && <StatusBarProgress viewSettings={data} />}
|
||||||
<span className={styles.label}>Running timer</span>
|
|
||||||
<span className={styles.timer}>{runningTime}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<span className={styles.title}>{projectTitle}</span>
|
|
||||||
<div className={styles.startTime}>
|
|
||||||
<span className={styles.label}>Scheduled start</span>
|
|
||||||
<span className={styles.timer}>{getTimeStart()}</span>
|
|
||||||
</div>
|
|
||||||
<div className={styles.endTime}>
|
|
||||||
<span className={styles.label}>Scheduled end</span>
|
|
||||||
<span className={styles.timer}>{getTimeEnd()}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { ViewSettings } from 'ontime-types';
|
||||||
|
|
||||||
|
import MultiPartProgressBar from '../../../common/components/multi-part-progress-bar/MultiPartProgressBar';
|
||||||
|
import { useTimer } from '../../../common/hooks/useSocket';
|
||||||
|
|
||||||
|
import styles from './StatusBar.module.scss';
|
||||||
|
|
||||||
|
interface StatusBarProgressProps {
|
||||||
|
viewSettings: ViewSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function StatusBarProgress(props: StatusBarProgressProps) {
|
||||||
|
const { viewSettings } = props;
|
||||||
|
|
||||||
|
const timer = useTimer();
|
||||||
|
const totalTime = (timer.duration ?? 0) + (timer.addedTime ?? 0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MultiPartProgressBar
|
||||||
|
now={timer.current}
|
||||||
|
complete={totalTime}
|
||||||
|
normalColor={viewSettings.normalColor}
|
||||||
|
warning={viewSettings.warningThreshold}
|
||||||
|
warningColor={viewSettings.warningColor}
|
||||||
|
danger={viewSettings.dangerThreshold}
|
||||||
|
dangerColor={viewSettings.dangerColor}
|
||||||
|
className={styles.progressOverride}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { useMemo } from 'react';
|
||||||
|
import { Playback } from 'ontime-types';
|
||||||
|
import { millisToString } from 'ontime-utils';
|
||||||
|
|
||||||
|
import PlaybackIcon from '../../../common/components/playback-icon/PlaybackIcon';
|
||||||
|
import { useTimer } from '../../../common/hooks/useSocket';
|
||||||
|
import { cx } from '../../../common/utils/styleUtils';
|
||||||
|
import { formatTime } from '../../../common/utils/time';
|
||||||
|
|
||||||
|
import styles from './StatusBar.module.scss';
|
||||||
|
|
||||||
|
interface StatusBarTimersProps {
|
||||||
|
projectTitle: string;
|
||||||
|
playback: Playback;
|
||||||
|
selectedEventId: string | null;
|
||||||
|
firstStart?: number;
|
||||||
|
firstId?: string;
|
||||||
|
lastEnd?: number;
|
||||||
|
lastId?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function StatusBarTimers(props: StatusBarTimersProps) {
|
||||||
|
const { projectTitle, playback, selectedEventId, firstStart, firstId, lastEnd, lastId } = props;
|
||||||
|
|
||||||
|
const timer = useTimer();
|
||||||
|
|
||||||
|
const getTimeStart = () => {
|
||||||
|
if (firstStart === undefined) {
|
||||||
|
return '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedEventId) {
|
||||||
|
if (firstId === selectedEventId) {
|
||||||
|
return millisToString(timer.expectedFinish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return millisToString(firstStart);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTimeEnd = () => {
|
||||||
|
if (lastEnd === undefined) {
|
||||||
|
return '...';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedEventId) {
|
||||||
|
if (lastId === selectedEventId) {
|
||||||
|
return millisToString(timer.expectedFinish);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return millisToString(lastEnd);
|
||||||
|
};
|
||||||
|
|
||||||
|
const PlaybackIconComponent = useMemo(() => {
|
||||||
|
const isPlaying = playback === Playback.Play || playback === Playback.Roll;
|
||||||
|
const classes = cx([styles.playbackIcon, isPlaying ? styles.active : null]);
|
||||||
|
return <PlaybackIcon state={playback} skipTooltip className={classes} />;
|
||||||
|
}, [playback]);
|
||||||
|
|
||||||
|
// use user defined format
|
||||||
|
const timeNow = formatTime(timer.clock, {
|
||||||
|
showSeconds: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const runningTime = millisToString(timer.current);
|
||||||
|
const elapsedTime = millisToString(timer.elapsed);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.timers}>
|
||||||
|
{PlaybackIconComponent}
|
||||||
|
<div className={styles.timeNow}>
|
||||||
|
<span className={styles.label}>Time now</span>
|
||||||
|
<span className={styles.timer}>{timeNow}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.elapsedTime}>
|
||||||
|
<span className={styles.label}>Elapsed time</span>
|
||||||
|
<span className={styles.timer}>{elapsedTime}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.runningTime}>
|
||||||
|
<span className={styles.label}>Running timer</span>
|
||||||
|
<span className={styles.timer}>{runningTime}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className={styles.title}>{projectTitle}</span>
|
||||||
|
<div className={styles.startTime}>
|
||||||
|
<span className={styles.label}>Scheduled start</span>
|
||||||
|
<span className={styles.timer}>{getTimeStart()}</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.endTime}>
|
||||||
|
<span className={styles.label}>Scheduled end</span>
|
||||||
|
<span className={styles.timer}>{getTimeEnd()}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -124,7 +124,7 @@ export default function Timer(props: TimerProps) {
|
|||||||
if (showExternal) {
|
if (showExternal) {
|
||||||
timerFontSize *= 0.8;
|
timerFontSize *= 0.8;
|
||||||
}
|
}
|
||||||
const externalFontSize = timerFontSize * 0.4
|
const externalFontSize = timerFontSize * 0.4;
|
||||||
const timerContainerClasses = `timer-container ${showBlinking ? (showOverlay ? '' : 'blink') : ''}`;
|
const timerContainerClasses = `timer-container ${showBlinking ? (showOverlay ? '' : 'blink') : ''}`;
|
||||||
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
|
const timerClasses = `timer ${!isPlaying ? 'timer--paused' : ''} ${showFinished ? 'timer--finished' : ''}`;
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ export default function Timer(props: TimerProps) {
|
|||||||
{!userOptions.hideProgress && (
|
{!userOptions.hideProgress && (
|
||||||
<MultiPartProgressBar
|
<MultiPartProgressBar
|
||||||
className={isPlaying ? 'progress-container' : 'progress-container progress-container--paused'}
|
className={isPlaying ? 'progress-container' : 'progress-container progress-container--paused'}
|
||||||
now={time.current ?? 0}
|
now={time.current}
|
||||||
complete={totalTime}
|
complete={totalTime}
|
||||||
normalColor={viewSettings.normalColor}
|
normalColor={viewSettings.normalColor}
|
||||||
warning={viewSettings.warningThreshold}
|
warning={viewSettings.warningThreshold}
|
||||||
|
|||||||
@@ -2,23 +2,6 @@
|
|||||||
|
|
||||||
//////////////////////////////////// general app elements
|
//////////////////////////////////// general app elements
|
||||||
|
|
||||||
@mixin main-container {
|
|
||||||
background-color: $bg-container-l1;
|
|
||||||
border: 1px solid $bg-container-l1;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin second-container {
|
|
||||||
background-color: $bg-container-l2;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin third-container {
|
|
||||||
background-color: $bg-container-l3;
|
|
||||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
|
||||||
border-radius: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@mixin action-link {
|
@mixin action-link {
|
||||||
color: $action-text-color;
|
color: $action-text-color;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "",
|
"postinstall": "",
|
||||||
"lint": "eslint . --quiet",
|
"lint": "eslint . --quiet",
|
||||||
|
"lint-staged": "eslint",
|
||||||
"dev:electron": "cross-env NODE_ENV=development electron .",
|
"dev:electron": "cross-env NODE_ENV=development electron .",
|
||||||
"dist-win": "electron-builder --publish=never --x64 --win",
|
"dist-win": "electron-builder --publish=never --x64 --win",
|
||||||
"dist-mac": "electron-builder --publish=never --mac",
|
"dist-mac": "electron-builder --publish=never --mac",
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
"build:docker": "pnpm prebuild && esbuild src/index.ts --log-level=error --platform=node --format=cjs --minify --bundle --outfile=dist/docker.cjs",
|
"build:docker": "pnpm prebuild && esbuild src/index.ts --log-level=error --platform=node --format=cjs --minify --bundle --outfile=dist/docker.cjs",
|
||||||
"build:debug": "pnpm prebuild && esbuild src/app.ts --platform=node --format=cjs --bundle --outfile=dist/index.cjs",
|
"build:debug": "pnpm prebuild && esbuild src/app.ts --platform=node --format=cjs --bundle --outfile=dist/index.cjs",
|
||||||
"lint": "eslint . --quiet",
|
"lint": "eslint . --quiet",
|
||||||
|
"lint-staged": "eslint",
|
||||||
"test": "cross-env IS_TEST=true vitest",
|
"test": "cross-env IS_TEST=true vitest",
|
||||||
"test:pipeline": "cross-env IS_TEST=true vitest run",
|
"test:pipeline": "cross-env IS_TEST=true vitest run",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
"description": "shared typings for ontime",
|
"description": "shared typings for ontime",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"cleanup": "rm -rf .turbo && rm -rf node_modules",
|
"cleanup": "rm -rf .turbo && rm -rf node_modules",
|
||||||
"lint": "eslint . --quiet"
|
"lint": "eslint . --quiet",
|
||||||
|
"lint-staged": "eslint"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"description": "shared logic for ontime",
|
"description": "shared logic for ontime",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint . --quiet",
|
"lint": "eslint . --quiet",
|
||||||
|
"lint-staged": "eslint",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"test:pipeline": "vitest run",
|
"test:pipeline": "vitest run",
|
||||||
"cleanup": "rm -rf .turbo && rm -rf node_modules"
|
"cleanup": "rm -rf .turbo && rm -rf node_modules"
|
||||||
|
|||||||
Reference in New Issue
Block a user