Skip to content
3 changes: 3 additions & 0 deletions packages/blueprints-integration/src/documents/part.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ export interface IBlueprintMutatablePart<TPrivateData = unknown, TPublicData = u
/** User-facing identifier that can be used by the User to identify the contents of a segment in the Rundown source system */
identifier?: string

/** User-facing notes shown in the segment header */
segmentHeaderNotes?: ITranslatableMessage[]

/** MediaObjects that when created/updated, should cause the blueprint to be rerun for the Segment of this Part */
hackListenToMediaObjectUpdates?: HackPartMediaObjectSubscription[]

Expand Down
11 changes: 10 additions & 1 deletion packages/job-worker/src/blueprints/context/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import {
IBlueprintShowStyleVariant,
IOutputLayer,
ISourceLayer,
ITranslatableMessage,
PieceAbSessionInfo,
RundownPlaylistTiming,
} from '@sofie-automation/blueprints-integration'
Expand Down Expand Up @@ -138,6 +139,7 @@ export const PlayoutMutatablePartSampleKeys = allKeysOfObject<PlayoutMutatablePa
displayDurationGroup: true,
displayDuration: true,
identifier: true,
segmentHeaderNotes: true,
hackListenToMediaObjectUpdates: true,
userEditOperations: true,
userEditProperties: true,
Expand Down Expand Up @@ -326,6 +328,7 @@ export function convertPartToBlueprints(part: ReadonlyDeep<DBPart>): IBlueprintP
displayDurationGroup: part.displayDurationGroup,
displayDuration: part.displayDuration,
identifier: part.identifier,
segmentHeaderNotes: clone<ITranslatableMessage[] | undefined>(part.segmentHeaderNotes),
hackListenToMediaObjectUpdates: clone<HackPartMediaObjectSubscription[] | undefined>(
part.hackListenToMediaObjectUpdates
),
Expand Down Expand Up @@ -705,7 +708,13 @@ export function convertPartialBlueprintMutablePartToCore(
blueprintId,
])
} else {
delete playoutUpdatePart.userEditOperations
delete playoutUpdatePart.userEditProperties
}

if ('segmentHeaderNotes' in updatePart) {
playoutUpdatePart.segmentHeaderNotes = updatePart.segmentHeaderNotes?.map((note) =>
wrapTranslatableMessageFromBlueprints(note, [blueprintId])
)
}

return playoutUpdatePart
Expand Down
3 changes: 3 additions & 0 deletions packages/job-worker/src/ingest/generationSegment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ function updateModelWithGeneratedPart(
]),
}
: undefined,
segmentHeaderNotes: blueprintPart.part.segmentHeaderNotes?.map((note) =>
wrapTranslatableMessageFromBlueprints(note, [blueprintId])
),
userEditOperations: translateUserEditsFromBlueprint(blueprintPart.part.userEditOperations, [blueprintId]),
userEditProperties: translateUserEditPropertiesFromBlueprint(blueprintPart.part.userEditProperties, [
blueprintId,
Expand Down
3 changes: 3 additions & 0 deletions packages/webui/src/client/styles/_colorScheme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ $general-timecode-color: var(--general-timecode-color);
$part-identifier: var(--part-identifier);
$part-identifier-text: var(--part-identifier-text);

$segment-header-note-identifier: var(--segment-header-note-identifier);
$segment-header-note-identifier-text: var(--segment-header-note-identifier-text);

$ui-button-primary: var(--ui-button-primary);
$ui-button-primary--hover: var(--ui-button-primary--hover);
$ui-button-primary--translucent: var(--ui-button-primary--translucent);
Expand Down
4 changes: 4 additions & 0 deletions packages/webui/src/client/styles/defaultColors.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@

--part-identifier: #363636;
--part-identifier-text: #eee;

--segment-header-note-identifier: #363636;
--segment-header-note-identifier-text: #fff;

--general-timecode-color: #ffff00;

--ui-button-primary: #1769ff;
Expand Down
122 changes: 102 additions & 20 deletions packages/webui/src/client/styles/rundownView.scss
Original file line number Diff line number Diff line change
Expand Up @@ -548,9 +548,8 @@ svg.icon {
}

.segment-timeline__title {
display: grid;
grid-template-columns: auto;
grid-template-rows: [title-title] min-content [title-notifications] 1fr [title-identifiers] min-content [end];
display: flex;
flex-direction: column;

margin: 0;
padding: 0;
Expand All @@ -571,6 +570,12 @@ svg.icon {
grid-row: title / timeline-header;
}

.segment-timeline__title__content {
display: flex;
flex-direction: column;
flex-grow: 1;
}

h2 {
margin: 0;
padding: 0.2em;
Expand All @@ -580,7 +585,6 @@ svg.icon {
hyphens: auto;
-webkit-hyphens: auto;
-ms-hyphens: auto;
grid-row: title-title / title-notifications;
}

.segment-timeline__title__notes {
Expand All @@ -589,28 +593,80 @@ svg.icon {
line-height: 1.4em;
font-size: 0.75em;
font-weight: 400;
grid-row: title-notifications / title-identifiers;
}

.segment-timeline__part-identifiers {
grid-row: title-identifiers / end;
margin-top: auto;

.segment-timeline__part-identifiers__identifier {
font-weight: 300;
text-shadow: none;
margin: 2.5px;
padding: 1px 4px;
// The pill-shaped labels that appear in the segment header, showing and linking to the part name or number
margin: 0.1rem;
padding: 0.1rem 0.35rem 0.1rem 0.35rem;
background-color: $part-identifier;
color: $part-identifier-text;
border-radius: 10px;
font-size: 0.85rem;
border-radius: 99px;
font-family: 'Roboto Flex', 'Roboto', sans-serif;
font-style: normal;
font-size: 0.9rem;
letter-spacing: 0.03em;
line-height: 100%;
text-shadow: none;
font-variant-numeric: proportional-nums;
font-variation-settings:
'wdth' 90,
'wght' 500,
'opsz' 120,
'slnt' 0,
'GRAD' 0,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 514,
'YTUC' 712;

&:hover {
background-color: #8c8c8c;
background-color: #6f6f6f;
color: #fff;
font-weight: 700;
box-shadow: 0px 0px 4px 0px #fff;
}
}
}
.segment-timeline__segment-header-note-identifier {
// The pill-shaped labels that appear in the segment header, showing extra information about the segment.
align-self: flex-start;
display: inline-block;
margin-top: auto;
margin: 0.25rem;
padding: 0.1rem 0.35rem 0.1rem 0.35rem;
background-color: $segment-header-note-identifier;
color: $segment-header-note-identifier-text;
border-radius: 99px;
font-family: 'Roboto Flex', 'Roboto', sans-serif;
font-style: normal;
font-size: 0.9rem;
letter-spacing: 0.03em;
line-height: 100%;
text-shadow: none;
font-variant-numeric: proportional-nums;
font-variation-settings:
'wdth' 70,
'wght' 500,
'opsz' 120,
'slnt' 0,
'GRAD' 0,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 514,
'YTUC' 712;
}
}

&.has-identifiers {
Expand Down Expand Up @@ -1113,7 +1169,8 @@ svg.icon {
}
.segment-timeline__part {
.segment-timeline__part__invalid-cover {
background-image: repeating-linear-gradient(
background-image:
repeating-linear-gradient(
45deg,
var(--invalid-reason-color-transparent) 0%,
var(--invalid-reason-color-transparent) 4px,
Expand Down Expand Up @@ -1395,7 +1452,8 @@ svg.icon {
left: 2px;
right: 2px;
z-index: 3;
background: repeating-linear-gradient(
background:
repeating-linear-gradient(
45deg,
var(--invalid-reason-color-opaque) 0,
var(--invalid-reason-color-opaque) 5px,
Expand Down Expand Up @@ -1576,8 +1634,9 @@ svg.icon {
bottom: 0;
right: 1px;
z-index: 10;
pointer-events: none;
background-image: repeating-linear-gradient(
pointer-events: all;
background-image:
repeating-linear-gradient(
45deg,
var(--invalid-reason-color-transparent) 0%,
var(--invalid-reason-color-transparent) 5px,
Expand Down Expand Up @@ -2500,13 +2559,36 @@ svg.icon {
}

.segment-timeline__identifier {
// The Parts identifier label on the right side of the part, showing the Part name or number
z-index: -1;
padding: 0 4px 0 10px;
padding: 0.1rem 0.35rem 0.1rem 0.6rem;
box-sizing: border-box;
border-radius: 0 99px 99px 0;
margin: 0rem;

background-color: $part-identifier;
border-radius: 0 8px 8px 0;
color: $part-identifier-text;
font-size: 0.85rem;
font-family: 'Roboto Flex', 'Roboto', sans-serif;
font-style: normal;
font-size: 0.9rem;
letter-spacing: 0.03em;
line-height: 100%;
text-shadow: none;
font-variant-numeric: proportional-nums;
font-variation-settings:
'wdth' 90,
'wght' 500,
'opsz' 120,
'slnt' 0,
'GRAD' 0,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 514,
'YTUC' 712;
}

&.gap {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
import { NoteSeverity } from '@sofie-automation/blueprints-integration'
import { IContextMenuContext } from '../RundownView.js'
import { IOutputLayerUi, PartUi, SegmentNoteCounts, SegmentUi } from '../SegmentContainer/withResolvedSegment.js'
import { IOutputLayerUi, PartUi, SegmentUi } from '../SegmentContainer/withResolvedSegment.js'
import { ContextMenuTrigger } from '@jstarpl/react-contextmenu'
import { CriticalIconSmall, WarningIconSmall } from '../../lib/ui/icons/notifications.js'
import { contextMenuHoldToDisplayTime, useCombinedRefs, useRundownViewEventBusListener } from '../../lib/lib.js'
import { useTranslation } from 'react-i18next'
import { literal } from '@sofie-automation/corelib/dist/lib'
Expand All @@ -28,6 +27,7 @@ import { isPartPlayable } from '@sofie-automation/corelib/dist/dataModel/Part'
import { UIStudio } from '@sofie-automation/corelib/src/dataModel/Studio.js'
import { PieceUi } from '@sofie-automation/corelib/src/dataModel/Piece.js'
import { isLoopRunning } from '@sofie-automation/corelib/src/playout/stateCacheResolver.js'
import { SegmentHeaderNotes } from '../SegmentHeader/SegmentHeaderNotes.js'

interface IProps {
id: string
Expand All @@ -36,7 +36,6 @@ interface IProps {
playlist: DBRundownPlaylist
studio: UIStudio
parts: Array<PartUi>
segmentNoteCounts: SegmentNoteCounts
hasAlreadyPlayed: boolean
hasGuestItems: boolean
hasRemoteItems: boolean
Expand Down Expand Up @@ -79,9 +78,6 @@ export const SegmentAdlibTesting = React.memo(
const [squishedHover, setSquishedHover] = useState<null | number>(null)
const squishedHoverTimeout = useRef<number | null>(null)

const criticalNotes = props.segmentNoteCounts.criticial
const warningNotes = props.segmentNoteCounts.warning

const getSegmentContext = () => {
const ctx = literal<IContextMenuContext>({
segment: props.segment,
Expand Down Expand Up @@ -460,30 +456,7 @@ export const SegmentAdlibTesting = React.memo(
>
{t('Adlib Testing')}
</h2>
{(criticalNotes > 0 || warningNotes > 0) && (
<div className="segment-timeline__title__notes">
{criticalNotes > 0 && (
<div
className="segment-timeline__title__notes__note segment-timeline__title__notes__note--critical"
onClick={() => props.onHeaderNoteClick?.(props.segment._id, NoteSeverity.ERROR)}
aria-label={t('Critical problems')}
>
<CriticalIconSmall />
<div className="segment-timeline__title__notes__count">{criticalNotes}</div>
</div>
)}
{warningNotes > 0 && (
<div
className="segment-timeline__title__notes__note segment-timeline__title__notes__note--warning"
onClick={() => props.onHeaderNoteClick?.(props.segment._id, NoteSeverity.WARNING)}
aria-label={t('Warnings')}
>
<WarningIconSmall />
<div className="segment-timeline__title__notes__count">{warningNotes}</div>
</div>
)}
</div>
)}
<SegmentHeaderNotes segmentId={props.segment._id} onHeaderNoteClick={props.onHeaderNoteClick} />
</ContextMenuTrigger>
<div className="segment-timeline__source-layers" role="tree" aria-label={t('Sources')}>
{Object.values<IOutputLayerUi>(props.segment.outputLayers)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,6 @@ export const SegmentAdlibTestingContainer = withResolvedSegment<IProps>(function
segment={props.segmentui}
studio={props.studio}
parts={props.parts}
segmentNoteCounts={props.segmentNoteCounts}
onItemClick={props.onPieceClick}
onItemDoubleClick={props.onPieceDoubleClick}
playlist={props.playlist}
Expand Down
Loading
Loading