Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ export async function checkPieceContentStatusAndDependencies(
blacks: [],
scenes: [],

thumbnailUrl: undefined,
thumbnailUrl: '/dev/fakeThumbnail.png',
previewUrl: '/dev/fakePreview.mp4',

packageName: null,
Expand Down
55 changes: 54 additions & 1 deletion packages/blueprints-integration/src/previews.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import { SplitsContentBoxContent, SplitsContentBoxProperties } from './content.js'
import { SourceLayerType, SplitsContentBoxContent, SplitsContentBoxProperties } from './content.js'
import { NoteSeverity } from './lib.js'
import { ITranslatableMessage } from './translations.js'

export interface PopupPreview<P extends Previews = Previews> {
name?: string
preview?: P
warnings?: InvalidPreview[]
/**
* Add custom content preview content
*/
additionalPreviewContent?: Array<PreviewContent>
}
export type Previews = TablePreview | ScriptPreview | HTMLPreview | SplitPreview | VTPreview | BlueprintImagePreview

Expand All @@ -19,6 +23,55 @@ export enum PreviewType {
BlueprintImage = 'blueprintImage',
}

// The PreviewContent types are a partly replica of the types in PreviewPopUpContext.tsx
export type PreviewContent =
| {
type: 'iframe'
href: string
postMessage?: any
dimensions?: { width: number; height: number }
}
| {
type: 'image'
src: string
}
| {
type: 'video'
src: string
}
| {
type: 'script'
script?: string
firstWords?: string
lastWords?: string
comment?: string
lastModified?: number
}
| {
type: 'title'
content: string
}
| {
type: 'inOutWords'
in?: string
out: string
}
| {
type: 'layerInfo'
Copy link
Contributor

@jstarpl jstarpl Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a layerInfo or more of an "auxiliary Piece info" on a layer of given type?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do think that calling it layerInfo makes sense, based on what it's used for.

layerType: SourceLayerType
text: Array<string>
inTime?: number | string
outTime?: number | string
duration?: number | string
Comment on lines +63 to +65
Copy link
Contributor

@jstarpl jstarpl Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these properties mean? What's the difference between outTime vs inTime + duration? I'm also somewhat unsure about making this interface this loose & bound to a particular implementation of the hoverscrub preview component/any other component using this infromation - say Presenters' Screen. Any change there would effectively require a change to blueprints integration, which we expect to be at least reasonably stable.

I'm also not a big fan of introducing terms inTime and duration - I think they somewhat overlap with expectedStart and expectedDuration? Are they the same? If that's the case, I think it would make sense to use the same names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are optional values for the "in", "out", "duration" labels, and used for the LayerInfo content, not necessary an expectedStart or expectedDuration calculation.
So either we should add a comment in here that explains what it is, or add it to some documentation, what do you prefer?

}
| {
type: 'separationLine'
}
| {
type: 'data'
content: { key: string; value: string }[]
}
Comment on lines +70 to +73
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This interface specifically is very vague for a public-use interface. I think we need to make sure that it's more clear what one can expect/put into here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you clarify what additional specificity you'd like for the data type?
We should definitely dig into what this does and add some documentation, currently I don't know how this feature works, or where it's being used.


Comment on lines +27 to +74
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think with this now becoming a public interface this needs to be described more in terms what the Blueprint Developer should expect to get.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, as this is an add on to the existing popupPreview implementation, are there any place where this is already documented, so I can add it there, or du you prefer it as comments in the interface definition?

interface PreviewBase {
type: PreviewType
}
Expand Down
Binary file added packages/webui/public/dev/fakeThumbnail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
}

.dashboard-panel__panel__button {
margin-top: 10px;
height: 110px;
max-width: 170px !important;
> .dashboard-panel__panel__button__content {
display: grid;
grid-template-columns: 1fr min-content;
Expand All @@ -31,7 +34,7 @@

> .dashboard-panel__panel__button__thumbnail {
position: relative;
height: auto;
height: 85px;
z-index: 1;
overflow: hidden;
grid-column: auto / span 2;
Expand Down
157 changes: 145 additions & 12 deletions packages/webui/src/client/ui/PreviewPopUp/PreviewPopUp.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,16 @@
.preview-popUp {
border: 1px solid var(--sofie-segment-layer-hover-popup-border);
background: var(--sofie-segment-layer-hover-popup-background);
box-shadow: 0 0 4px 0 rgba(0, 0, 0, 0.5);
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.8);
border-radius: 5px;
overflow: hidden;
pointer-events: none;

box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.6);

z-index: 9999;

&--large {
width: 482px;
padding-bottom: 10px;
--preview-max-dimension: 480;
}

Expand All @@ -25,18 +24,65 @@
&--hidden {
visibility: none;
}

font-family: Roboto Flex;

font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 110%;
/* identical to box height, or 15px */
letter-spacing: 0.02em;
font-feature-settings:
'tnum',
'liga' off;
color: #ffffff;
font-variation-settings:
'GRAD' 0,
'opsz' 15,
'slnt' 0,
'wdth' 30,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 548,
'YTUC' 712;
}

.preview-popUp__preview {
width: 100%;
font-family: 'Roboto Condensed';
font-size: 0.9375rem; // 15px;

.preview-popUp__script,
.preview-popUp__script-comment,
.preview-popUp__script-last-modified {
padding: 0.4em 0.4em 0.4em 0.6em;
font-style: italic;
padding: 5px;
padding-left: 2%;
padding-right: 2%;
font-weight: 300;
font-size: 16px;
line-height: 120%;
letter-spacing: 0.03em;
font-feature-settings:
'tnum',
'liga' off;

color: #ffffff;
font-variation-settings:
'GRAD' 0,
'opsz' 16,
'slnt' -10,
'wdth' 75,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 548,
'YTUC' 712;
}

.preview-popUp__script-comment,
Expand All @@ -54,6 +100,72 @@
letter-spacing: 0.02rem;

padding: 5px;
padding-left: 2%;
}

.preview-popUp__element-with-time-info {
width: 100%;
display: flex;

margin-bottom: 7px;

.preview-popUp__element-with-time-info__layer-color {
height: 13px;
aspect-ratio: 1;
margin-left: 2%;
margin-top: 7px;
flex-shrink: 0;
@include item-type-colors();
}

.preview-popUp__element-with-time-info__text {
margin: 5px;
width: calc(100% - 35px);
flex-grow: 1;
}

.preview-popUp__element-with-time-info__timing {
margin-left: 5px;
overflow: none;
white-space: nowrap;
text-overflow: ellipsis;
font-feature-settings: 'liga' off;

font-weight: 500;
line-height: 100%; /* 15px */

.label {
font-weight: 100;
line-height: 100%;
/* identical to box height, or 15px */
letter-spacing: 0.02em;
font-feature-settings:
'tnum',
'liga' off;
color: #b2b2b2;
font-variation-settings:
'GRAD' 0,
'opsz' 30,
'slnt' 0,
'wdth' 25,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 548,
'YTUC' 712;
}
}
}

.preview-popup__separation-line {
width: 96%;
margin-left: 2%;
background-color: #5b5b5b;
margin-top: 0px;
margin-bottom: 0px;
}

.preview-popUp__warning {
Expand Down Expand Up @@ -174,21 +286,42 @@
}

.preview-popUp__in-out-words {
letter-spacing: 0em;
font-weight: 300;
font-size: 16px;
line-height: 100%;
letter-spacing: 0.02em;
font-feature-settings:
'tnum',
'liga' off;
color: #ffffff;
font-variation-settings:
'GRAD' 0,
'opsz' 16,
'slnt' -10,
'wdth' 75,
'XOPQ' 96,
'XTRA' 468,
'YOPQ' 79,
'YTAS' 750,
'YTDE' -203,
'YTFI' 738,
'YTLC' 548,
'YTUC' 712;

width: 100%;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;

margin-top: -25px; //Pull up the in/out words a bit
padding: 7px;
padding: 5px;
padding-left: 2%;
padding-right: 2%;

.separation-line {
width: 100%;
height: 1px;
background-color: #5b5b5b;
margin-bottom: 5px;
margin-bottom: 7px;
}

.in-words,
Expand All @@ -201,7 +334,7 @@
}

.out-words {
direction: rtl;
text-align: right;
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React from 'react'
import { PreviewContent } from './PreviewPopUpContext.js'
import { WarningIconSmall } from '../../lib/ui/icons/notifications.js'
import { translateMessage } from '@sofie-automation/corelib/dist/TranslatableMessage'
import { TFunction, useTranslation } from 'react-i18next'
Expand All @@ -11,9 +10,11 @@ import { RundownUtils } from '../../lib/rundown.js'
import { PieceInstancePiece } from '@sofie-automation/corelib/dist/dataModel/PieceInstance'
import { ReadonlyObjectDeep } from 'type-fest/source/readonly-deep'
import { PieceLifespan } from '@sofie-automation/blueprints-integration'
import { LayerInfoPreview } from './Previews/LayerInfoPreview.js'
import { PreviewContentUI } from './PreviewPopUpContext.js'

interface PreviewPopUpContentProps {
content: PreviewContent
content: PreviewContentUI
time: number | null
}

Expand All @@ -38,7 +39,6 @@ export function PreviewPopUpContent({ content, time }: PreviewPopUpContentProps)
case 'inOutWords':
return (
<div className="preview-popUp__in-out-words">
<hr className="separation-line" />
<div className="in-words">{content.in}</div>
<div className="out-words">{content.out}</div>
</div>
Expand All @@ -59,6 +59,10 @@ export function PreviewPopUpContent({ content, time }: PreviewPopUpContentProps)
</table>
</div>
)
case 'layerInfo':
return <LayerInfoPreview {...content} />
case 'separationLine':
return <hr className="preview-popup__separation-line" />
case 'boxLayout':
return <BoxLayoutPreview content={content} />
case 'warning':
Expand Down Expand Up @@ -108,17 +112,17 @@ function getDurationText(
function getLifeSpanText(t: TFunction, lifespan: PieceLifespan): string {
switch (lifespan) {
case PieceLifespan.WithinPart:
return t('Until next take')
return t('Until Next Take')
case PieceLifespan.OutOnSegmentChange:
return t('Until next segment')
return t('Until Next Segment')
case PieceLifespan.OutOnSegmentEnd:
return t('Until end of segment')
return t('Until End of Segment')
case PieceLifespan.OutOnRundownChange:
return t('Until next rundown')
return t('Until Next Rundown')
case PieceLifespan.OutOnRundownEnd:
return t('Until end of rundown')
return t('Until End of Rundown')
case PieceLifespan.OutOnShowStyleEnd:
return t('Until end of showstyle')
return t('Until End of Showstyle')
default:
return ''
}
Expand Down
Loading
Loading