disaster-broadcast-studio 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -3
- package/dist/cli.js +2 -1
- package/dist/load.js +3 -4
- package/dist/scenario.d.ts +17 -4
- package/dist/scenario.js +40 -35
- package/dist/timeline.d.ts +62 -0
- package/dist/timeline.js +18 -1
- package/dist/video/Alerts.js +15 -31
- package/dist/video/Broadcast.js +74 -64
- package/dist/video/Bulletin.d.ts +1 -2
- package/dist/video/Bulletin.js +11 -20
- package/dist/video/Chat.d.ts +9 -0
- package/dist/video/Chat.js +34 -0
- package/dist/video/Information.d.ts +0 -6
- package/dist/video/Information.js +32 -36
- package/dist/video/Map.js +2 -2
- package/dist/video/TimeJump.d.ts +8 -0
- package/dist/video/TimeJump.js +42 -0
- package/dist/video/layout.d.ts +5 -0
- package/dist/video/layout.js +2 -0
- package/dist/video/transitions.d.ts +58 -11
- package/dist/video/transitions.js +0 -9
- package/dist/video/useBundledFonts.js +1 -1
- package/package.json +1 -1
- package/skills/scenario-writing/SKILL.md +10 -3
- package/skills/scenario-writing/references/README.md +4 -4
- package/skills/scenario-writing/references/audio.md +6 -0
- package/skills/scenario-writing/references/direction.md +31 -7
- package/skills/scenario-writing/references/format.md +52 -10
- package/skills/scenario-writing/references/news-writing.md +10 -0
- package/skills/scenario-writing/references/status-systems.md +4 -2
package/dist/video/Broadcast.js
CHANGED
|
@@ -3,6 +3,7 @@ import { AbsoluteFill, useCurrentFrame } from 'remotion';
|
|
|
3
3
|
import { FPS } from '../scenario.js';
|
|
4
4
|
import { stateAt, clock, envelope, receiptHighlights, shutdownAt } from '../timeline.js';
|
|
5
5
|
import { AlertHistory } from './Alerts.js';
|
|
6
|
+
import { ChatPanel } from './Chat.js';
|
|
6
7
|
import { BroadcastMap } from './Map.js';
|
|
7
8
|
import { SignalEffects, signalLevels } from './SignalEffects.js';
|
|
8
9
|
import { AudioTrack } from './AudioTrack.js';
|
|
@@ -11,6 +12,8 @@ import { useBundledFonts } from './useBundledFonts.js';
|
|
|
11
12
|
import { bodyFont, numberFont } from './fonts.js';
|
|
12
13
|
import { News, Status } from './Information.js';
|
|
13
14
|
import { changeProgress, transitionAt } from './transitions.js';
|
|
15
|
+
import { layout } from './layout.js';
|
|
16
|
+
import { TimeJump, timeJumpStrength } from './TimeJump.js';
|
|
14
17
|
export function Broadcast(props) {
|
|
15
18
|
const { timeline, regions, countries, provinces, audio } = props;
|
|
16
19
|
const openingFrames = 5 * FPS;
|
|
@@ -22,6 +25,7 @@ export function Broadcast(props) {
|
|
|
22
25
|
const signal = signalLevels(state.visible, frame);
|
|
23
26
|
const mapEvents = [...state.visible, ...receiptHighlights(timeline, state.contentFrame)];
|
|
24
27
|
const shutdown = shutdownAt(state.visible, frame);
|
|
28
|
+
const jumpStrength = timeJumpStrength(state.timeJump);
|
|
25
29
|
return (_jsxs(AbsoluteFill, { style: {
|
|
26
30
|
filter: signal.chromatic > 0 ? 'url(#dbs-chromatic)' : undefined,
|
|
27
31
|
background: shutdown ? '#000' : '#080b0d',
|
|
@@ -34,7 +38,7 @@ export function Broadcast(props) {
|
|
|
34
38
|
}, children: [_jsxs("header", { style: {
|
|
35
39
|
position: 'absolute',
|
|
36
40
|
inset: '0 0 auto 0',
|
|
37
|
-
height:
|
|
41
|
+
height: layout.header,
|
|
38
42
|
display: 'flex',
|
|
39
43
|
alignItems: 'center',
|
|
40
44
|
padding: '0 26px',
|
|
@@ -44,7 +48,7 @@ export function Broadcast(props) {
|
|
|
44
48
|
}, children: [_jsx(Status, { timeline: timeline, frame: state.contentFrame }), _jsx("time", { style: {
|
|
45
49
|
marginLeft: 'auto',
|
|
46
50
|
flexShrink: 0,
|
|
47
|
-
fontSize:
|
|
51
|
+
fontSize: 32,
|
|
48
52
|
fontFamily: numberFont,
|
|
49
53
|
fontVariantNumeric: 'tabular-nums',
|
|
50
54
|
color: state.jumping ? '#eef2f4' : '#bdc7ce',
|
|
@@ -55,70 +59,76 @@ export function Broadcast(props) {
|
|
|
55
59
|
padding: '2px 8px',
|
|
56
60
|
fontWeight: state.jumping ? 600 : 400,
|
|
57
61
|
letterSpacing: 0.5,
|
|
58
|
-
}, children: clock(state.displayEpoch, timeline.scenario.timezone, true) })] }), _jsxs(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
}, children: clock(state.displayEpoch, timeline.scenario.timezone, true) })] }), _jsxs(AbsoluteFill, { style: {
|
|
63
|
+
filter: jumpStrength > 0
|
|
64
|
+
? `url(#dbs-time-jump) saturate(${1 - jumpStrength * 0.35})`
|
|
65
|
+
: undefined,
|
|
66
|
+
}, children: [_jsxs("main", { style: {
|
|
67
|
+
position: 'absolute',
|
|
68
|
+
left: 0,
|
|
69
|
+
top: layout.header + 1,
|
|
70
|
+
right: layout.sidebar,
|
|
71
|
+
bottom: layout.footer + 1,
|
|
72
|
+
}, children: [_jsx(BroadcastMap, { camera: state.camera, countries: countries, regions: regions, provinces: provinces, events: mapEvents, frame: state.contentFrame }), legend?.type === 'legend' && (_jsx("div", { style: {
|
|
73
|
+
position: 'absolute',
|
|
74
|
+
left: 24,
|
|
75
|
+
bottom: 24,
|
|
76
|
+
display: 'flex',
|
|
77
|
+
flexDirection: 'column',
|
|
78
|
+
gap: 10,
|
|
79
|
+
background: '#0b0e10ed',
|
|
80
|
+
padding: '14px 18px',
|
|
81
|
+
fontSize: 30,
|
|
82
|
+
color: '#d0d5da',
|
|
83
|
+
opacity: changeProgress(state.contentFrame, legend.start),
|
|
84
|
+
transform: `translateY(${10 * (1 - changeProgress(state.contentFrame, legend.start))}px)`,
|
|
85
|
+
}, children: legend.items.map((item, i) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [_jsx("span", { style: {
|
|
86
|
+
width: 23,
|
|
87
|
+
height: item.symbol === 'line' ? 0 : 16,
|
|
88
|
+
background: item.symbol === 'fill' ? item.color : 'transparent',
|
|
89
|
+
border: item.symbol === 'fill' ? 'none' : `2px solid ${item.color}`,
|
|
90
|
+
} }), _jsx("span", { children: item.text })] }, i))) })), inset?.type === 'inset' && (_jsxs("div", { style: {
|
|
91
|
+
position: 'absolute',
|
|
92
|
+
right: 20,
|
|
93
|
+
bottom: 20,
|
|
94
|
+
width: 420,
|
|
95
|
+
height: 290,
|
|
96
|
+
border: '1px solid #78858e',
|
|
97
|
+
opacity: changeProgress(state.contentFrame, inset.start),
|
|
98
|
+
transform: `translateY(${10 * (1 - changeProgress(state.contentFrame, inset.start))}px)`,
|
|
99
|
+
}, children: [_jsx(BroadcastMap, { camera: inset.camera, countries: countries, regions: regions, provinces: provinces, events: mapEvents, frame: state.contentFrame, inset: true }), _jsx("div", { style: {
|
|
100
|
+
position: 'absolute',
|
|
101
|
+
top: 0,
|
|
102
|
+
left: 0,
|
|
103
|
+
right: 0,
|
|
104
|
+
padding: '7px 12px',
|
|
105
|
+
background: '#0b0e10f2',
|
|
106
|
+
borderBottom: '1px solid #485159',
|
|
107
|
+
fontSize: 28,
|
|
108
|
+
}, children: inset.title })] }))] }), _jsxs("aside", { style: {
|
|
65
109
|
position: 'absolute',
|
|
66
|
-
|
|
67
|
-
|
|
110
|
+
top: layout.header + 1,
|
|
111
|
+
right: 0,
|
|
112
|
+
bottom: layout.footer + 1,
|
|
113
|
+
width: layout.sidebar,
|
|
114
|
+
borderLeft: '1px solid #495159',
|
|
115
|
+
background: '#0c1012',
|
|
116
|
+
overflow: 'hidden',
|
|
68
117
|
display: 'flex',
|
|
69
118
|
flexDirection: 'column',
|
|
70
|
-
|
|
71
|
-
background: '#0b0e10ed',
|
|
72
|
-
padding: '14px 18px',
|
|
73
|
-
fontSize: 21,
|
|
74
|
-
color: '#d0d5da',
|
|
75
|
-
opacity: changeProgress(state.contentFrame, legend.start),
|
|
76
|
-
transform: `translateY(${10 * (1 - changeProgress(state.contentFrame, legend.start))}px)`,
|
|
77
|
-
}, children: legend.items.map((item, i) => (_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [_jsx("span", { style: {
|
|
78
|
-
width: 23,
|
|
79
|
-
height: item.symbol === 'line' ? 0 : 16,
|
|
80
|
-
background: item.symbol === 'fill' ? item.color : 'transparent',
|
|
81
|
-
border: item.symbol === 'fill' ? 'none' : `2px solid ${item.color}`,
|
|
82
|
-
} }), _jsx("span", { children: item.text })] }, i))) })), inset?.type === 'inset' && (_jsxs("div", { style: {
|
|
119
|
+
}, children: [_jsx("div", { style: { flex: 1, minHeight: 0, overflow: 'hidden' }, children: _jsx(AlertHistory, { timeline: timeline, frame: state.contentFrame }) }), state.chat && _jsx(ChatPanel, { timeline: timeline, chat: state.chat, frame: state.contentFrame })] }), [bulletins.previous, bulletins.current].map((bulletin, i) => bulletin && (_jsx(Bulletin, { event: bulletin, source: timeline.scenario.agencies[bulletin.source], progress: i === 0 ? 1 - bulletins.progress : bulletins.progress }, bulletin.id))), _jsx("footer", { style: {
|
|
83
120
|
position: 'absolute',
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
height:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
right: 0,
|
|
96
|
-
padding: '7px 12px',
|
|
97
|
-
background: '#0b0e10f2',
|
|
98
|
-
borderBottom: '1px solid #485159',
|
|
99
|
-
fontSize: 17,
|
|
100
|
-
}, children: inset.title })] }))] }), _jsx("aside", { style: {
|
|
101
|
-
position: 'absolute',
|
|
102
|
-
top: 49,
|
|
103
|
-
right: 0,
|
|
104
|
-
bottom: 93,
|
|
105
|
-
width: 640,
|
|
106
|
-
borderLeft: '1px solid #495159',
|
|
107
|
-
background: '#0c1012',
|
|
108
|
-
overflow: 'hidden',
|
|
109
|
-
}, children: _jsx(AlertHistory, { timeline: timeline, frame: state.contentFrame }) }), [bulletins.previous, bulletins.current].map((bulletin, i) => bulletin && (_jsx(Bulletin, { event: bulletin, source: timeline.scenario.agencies[bulletin.source], frame: i === 0 ? Math.min(state.contentFrame, bulletin.end - 1) : state.contentFrame, progress: i === 0 ? 1 - bulletins.progress : bulletins.progress }, bulletin.id))), _jsx("footer", { style: {
|
|
110
|
-
position: 'absolute',
|
|
111
|
-
bottom: 0,
|
|
112
|
-
left: 0,
|
|
113
|
-
right: 0,
|
|
114
|
-
height: 92,
|
|
115
|
-
borderTop: '1px solid #606a72',
|
|
116
|
-
display: 'flex',
|
|
117
|
-
alignItems: 'center',
|
|
118
|
-
background: '#0b0e10',
|
|
119
|
-
padding: '0 26px',
|
|
120
|
-
gap: 24,
|
|
121
|
-
}, children: _jsx(News, { timeline: timeline, frame: state.contentFrame }) })] }), state.visible
|
|
121
|
+
bottom: 0,
|
|
122
|
+
left: 0,
|
|
123
|
+
right: 0,
|
|
124
|
+
height: layout.footer,
|
|
125
|
+
borderTop: '1px solid #606a72',
|
|
126
|
+
display: 'flex',
|
|
127
|
+
alignItems: 'center',
|
|
128
|
+
background: '#0b0e10',
|
|
129
|
+
padding: '0 26px',
|
|
130
|
+
gap: 24,
|
|
131
|
+
}, children: _jsx(News, { timeline: timeline, frame: state.contentFrame }) })] }), _jsx(TimeJump, { jump: state.timeJump })] }), state.visible
|
|
122
132
|
.filter((e) => e.type === 'screen' && ['blackout', 'flash', 'interference'].includes(e.effect))
|
|
123
133
|
.map((event) => {
|
|
124
134
|
if (event.type !== 'screen')
|
|
@@ -149,14 +159,14 @@ export function Broadcast(props) {
|
|
|
149
159
|
alignItems: 'center',
|
|
150
160
|
opacity: frame < openingFrames - 30 ? 1 : (openingFrames - frame) / 30,
|
|
151
161
|
}, children: [_jsx("div", { style: {
|
|
152
|
-
fontSize:
|
|
162
|
+
fontSize: 54,
|
|
153
163
|
fontWeight: 600,
|
|
154
164
|
maxWidth: 1440,
|
|
155
165
|
textAlign: 'center',
|
|
156
166
|
overflowWrap: 'anywhere',
|
|
157
167
|
lineHeight: 1.5,
|
|
158
168
|
}, children: timeline.scenario.title }), _jsx("div", { style: {
|
|
159
|
-
fontSize:
|
|
169
|
+
fontSize: 36,
|
|
160
170
|
color: '#bac3c9',
|
|
161
171
|
marginTop: 22,
|
|
162
172
|
maxWidth: 1200,
|
package/dist/video/Bulletin.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { CompiledEvent } from '../scenario.js';
|
|
3
|
-
export declare function Bulletin({ event, source,
|
|
3
|
+
export declare function Bulletin({ event, source, progress, }: {
|
|
4
4
|
event: Extract<CompiledEvent, {
|
|
5
5
|
type: 'bulletin';
|
|
6
6
|
}>;
|
|
7
7
|
source: string;
|
|
8
|
-
frame: number;
|
|
9
8
|
progress?: number;
|
|
10
9
|
}): React.JSX.Element;
|
package/dist/video/Bulletin.js
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { PagedText } from './Information.js';
|
|
5
|
-
import { pageAt } from './transitions.js';
|
|
6
|
-
export function Bulletin({ event, source, frame, progress = 1, }) {
|
|
7
|
-
const chunks = pages(event.text);
|
|
8
|
-
const { page } = pageAt(frame, event.start, event.pageSeconds, chunks.length);
|
|
2
|
+
import { layout } from './layout.js';
|
|
3
|
+
export function Bulletin({ event, source, progress = 1, }) {
|
|
9
4
|
return (_jsxs("section", { style: {
|
|
10
5
|
position: 'absolute',
|
|
11
|
-
top:
|
|
12
|
-
bottom:
|
|
6
|
+
top: layout.header + 1,
|
|
7
|
+
bottom: layout.footer + 1,
|
|
13
8
|
left: 0,
|
|
14
9
|
right: 0,
|
|
15
10
|
background: '#0a0d0f',
|
|
@@ -19,21 +14,17 @@ export function Bulletin({ event, source, frame, progress = 1, }) {
|
|
|
19
14
|
flexDirection: 'column',
|
|
20
15
|
justifyContent: 'center',
|
|
21
16
|
padding: '50px 190px',
|
|
22
|
-
}, children: [_jsx("div", { style: { fontSize:
|
|
23
|
-
fontSize:
|
|
17
|
+
}, children: [_jsx("div", { style: { fontSize: 32, color: '#b8c3cb', marginBottom: 24 }, children: source }), _jsx("h2", { style: {
|
|
18
|
+
fontSize: 54,
|
|
24
19
|
fontWeight: 600,
|
|
25
20
|
color: event.color,
|
|
26
21
|
margin: '0 0 32px',
|
|
27
22
|
lineHeight: 1.5,
|
|
28
23
|
}, children: event.heading }), _jsx("div", { style: {
|
|
29
|
-
fontSize:
|
|
30
|
-
lineHeight: 1.
|
|
24
|
+
fontSize: 44,
|
|
25
|
+
lineHeight: 1.6,
|
|
31
26
|
whiteSpace: 'pre-wrap',
|
|
32
|
-
wordBreak: '
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
marginTop: 30,
|
|
36
|
-
color: '#8c969d',
|
|
37
|
-
fontFamily: numberFont,
|
|
38
|
-
}, children: [page + 1, " / ", chunks.length] }))] }));
|
|
27
|
+
wordBreak: 'normal',
|
|
28
|
+
overflowWrap: 'anywhere',
|
|
29
|
+
}, children: event.text })] }));
|
|
39
30
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { Timeline } from '../scenario.js';
|
|
3
|
+
import { stateAt } from '../timeline.js';
|
|
4
|
+
export declare const chatHeight = 260;
|
|
5
|
+
export declare function ChatPanel({ timeline, chat, frame, }: {
|
|
6
|
+
timeline: Timeline;
|
|
7
|
+
chat: NonNullable<ReturnType<typeof stateAt>['chat']>;
|
|
8
|
+
frame: number;
|
|
9
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { clock, stateAt } from '../timeline.js';
|
|
3
|
+
import { numberFont } from './fonts.js';
|
|
4
|
+
import { changeProgress } from './transitions.js';
|
|
5
|
+
export const chatHeight = 260;
|
|
6
|
+
export function ChatPanel({ timeline, chat, frame, }) {
|
|
7
|
+
const progress = changeProgress(frame, chat.panel.start);
|
|
8
|
+
return (_jsxs("section", { "aria-label": chat.panel.title, style: {
|
|
9
|
+
height: chatHeight,
|
|
10
|
+
boxSizing: 'border-box',
|
|
11
|
+
flexShrink: 0,
|
|
12
|
+
display: 'flex',
|
|
13
|
+
flexDirection: 'column',
|
|
14
|
+
borderTop: '1px solid #495159',
|
|
15
|
+
background: '#0b0f12',
|
|
16
|
+
opacity: progress,
|
|
17
|
+
transform: `translateY(${12 * (1 - progress)}px)`,
|
|
18
|
+
}, children: [_jsxs("div", { style: {
|
|
19
|
+
height: 42,
|
|
20
|
+
flexShrink: 0,
|
|
21
|
+
display: 'flex',
|
|
22
|
+
alignItems: 'center',
|
|
23
|
+
gap: 12,
|
|
24
|
+
padding: '0 24px',
|
|
25
|
+
borderBottom: '1px solid #293238',
|
|
26
|
+
}, children: [_jsx("span", { style: { width: 6, height: 6, borderRadius: '50%', background: '#b46e68' } }), _jsx("span", { style: { fontSize: chat.panel.title.length > 20 ? 20 : 28, fontWeight: 600 }, children: chat.panel.title }), _jsx("span", { style: { marginLeft: 'auto', fontSize: 22, color: '#b2bdc5', letterSpacing: 1 }, children: "LIVE" })] }), _jsxs("div", { style: { position: 'relative', flex: 1, minHeight: 0, overflow: 'hidden' }, children: [chat.messages.length === 0 && (_jsx("div", { style: { padding: '16px 24px', color: '#b2bdc5', fontSize: 28 }, children: "\uC544\uC9C1 \uCC44\uD305\uC774 \uC5C6\uC2B5\uB2C8\uB2E4." })), _jsx("div", { style: { position: 'absolute', bottom: 12, left: 24, right: 24 }, children: chat.messages.slice(-12).map((message) => {
|
|
27
|
+
const arrival = changeProgress(frame, message.start, 18);
|
|
28
|
+
return (_jsx("div", { style: { display: 'grid', gridTemplateRows: `${arrival}fr` }, children: _jsx("div", { style: { minHeight: 0, overflow: 'hidden' }, children: _jsxs("article", { style: {
|
|
29
|
+
paddingTop: 8,
|
|
30
|
+
opacity: arrival,
|
|
31
|
+
transform: `translateY(${8 * (1 - arrival)}px)`,
|
|
32
|
+
}, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 12, marginBottom: 3 }, children: [_jsx("span", { style: { fontSize: 24, color: message.color, overflowWrap: 'anywhere' }, children: message.author }), _jsx("time", { style: { fontSize: 22, color: '#abb8c2', fontFamily: numberFont, flexShrink: 0 }, children: clock(stateAt(timeline, message.start).epoch, timeline.scenario.timezone) })] }), _jsx("div", { style: { fontSize: 28, lineHeight: 1.35, overflowWrap: 'anywhere' }, children: message.text })] }) }) }, message.id));
|
|
33
|
+
}) })] })] }));
|
|
34
|
+
}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { type Timeline } from '../scenario.js';
|
|
3
|
-
export declare function PagedText({ chunks, frame, start, seconds, }: {
|
|
4
|
-
chunks: string[];
|
|
5
|
-
frame: number;
|
|
6
|
-
start: number;
|
|
7
|
-
seconds: number;
|
|
8
|
-
}): React.JSX.Element;
|
|
9
3
|
export declare function News({ timeline, frame }: {
|
|
10
4
|
timeline: Timeline;
|
|
11
5
|
frame: number;
|
|
@@ -2,19 +2,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { FPS } from '../scenario.js';
|
|
3
3
|
import { clock, notificationPulse, stateAt } from '../timeline.js';
|
|
4
4
|
import { numberFont } from './fonts.js';
|
|
5
|
-
import { changeFrames, changeProgress,
|
|
6
|
-
|
|
7
|
-
const { page, previous, progress } = pageAt(frame, start, seconds, chunks.length);
|
|
8
|
-
return (_jsxs("div", { style: { display: 'grid', overflow: 'hidden' }, children: [previous !== undefined && progress < 1 && (_jsx("div", { "aria-hidden": true, style: {
|
|
9
|
-
gridArea: '1 / 1',
|
|
10
|
-
opacity: 1 - progress,
|
|
11
|
-
transform: `translateY(${-12 * progress}px)`,
|
|
12
|
-
}, children: chunks[previous] })), _jsx("div", { style: {
|
|
13
|
-
gridArea: '1 / 1',
|
|
14
|
-
opacity: progress,
|
|
15
|
-
transform: `translateY(${12 * (1 - progress)}px)`,
|
|
16
|
-
}, children: chunks[page] })] }));
|
|
17
|
-
}
|
|
5
|
+
import { changeFrames, changeProgress, transitionAt } from './transitions.js';
|
|
6
|
+
import { layout } from './layout.js';
|
|
18
7
|
export function News({ timeline, frame }) {
|
|
19
8
|
const { current, previous, progress } = transitionAt(timeline.events, 'news', frame);
|
|
20
9
|
return (_jsxs("div", { style: {
|
|
@@ -22,28 +11,35 @@ export function News({ timeline, frame }) {
|
|
|
22
11
|
flex: 1,
|
|
23
12
|
alignSelf: 'stretch',
|
|
24
13
|
overflow: 'hidden',
|
|
25
|
-
}, children: [[previous, current].map((news, i) =>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
14
|
+
}, children: [[previous, current].map((news, i) => {
|
|
15
|
+
if (!news)
|
|
16
|
+
return null;
|
|
17
|
+
return (_jsxs("div", { "aria-hidden": i === 0 || undefined, style: {
|
|
18
|
+
position: 'absolute',
|
|
19
|
+
inset: 0,
|
|
20
|
+
display: 'flex',
|
|
21
|
+
alignItems: 'center',
|
|
22
|
+
gap: 24,
|
|
23
|
+
opacity: i === 0 ? 1 - progress : progress,
|
|
24
|
+
transform: `translateY(${i === 0 ? -layout.footer * progress : layout.footer * (1 - progress)}px)`,
|
|
25
|
+
}, children: [_jsxs("div", { style: { width: 280, flexShrink: 0, overflowWrap: 'anywhere' }, children: [_jsx("div", { style: { fontSize: 28, color: '#c6d0d7', fontWeight: 600, lineHeight: 1.3 }, children: timeline.scenario.agencies[news.source] }), _jsx("div", { style: {
|
|
26
|
+
fontSize: 26,
|
|
27
|
+
color: '#b0bec8',
|
|
28
|
+
marginTop: 5,
|
|
29
|
+
fontFamily: numberFont,
|
|
30
|
+
fontVariantNumeric: 'tabular-nums',
|
|
31
|
+
}, children: clock(news.publishedAt
|
|
32
|
+
? Date.parse(news.publishedAt)
|
|
33
|
+
: stateAt(timeline, news.start).epoch, timeline.scenario.timezone) })] }), _jsx("div", { style: {
|
|
34
|
+
fontSize: 40,
|
|
35
|
+
lineHeight: 1.3,
|
|
36
|
+
wordBreak: 'normal',
|
|
37
|
+
whiteSpace: 'pre-wrap',
|
|
38
|
+
minWidth: 0,
|
|
39
|
+
flex: 1,
|
|
40
|
+
overflowWrap: 'anywhere',
|
|
41
|
+
}, children: news.headline })] }, news.id));
|
|
42
|
+
}), _jsx("div", { style: {
|
|
47
43
|
position: 'absolute',
|
|
48
44
|
top: 0,
|
|
49
45
|
left: 0,
|
|
@@ -81,7 +77,7 @@ export function Status({ timeline, frame }) {
|
|
|
81
77
|
border: `1px solid ${badge.color}`,
|
|
82
78
|
color: badge.color,
|
|
83
79
|
padding: '3px 10px',
|
|
84
|
-
fontSize:
|
|
80
|
+
fontSize: 28,
|
|
85
81
|
lineHeight: 1.3,
|
|
86
82
|
whiteSpace: 'nowrap',
|
|
87
83
|
background: `${badge.color}${Math.round((layer === 1 && changed
|
package/dist/video/Map.js
CHANGED
|
@@ -220,7 +220,7 @@ export function BroadcastMap({ camera, countries, regions, provinces, events, fr
|
|
|
220
220
|
background: '#0b0e10f2',
|
|
221
221
|
padding: '6px 12px',
|
|
222
222
|
borderLeft: `3px solid ${e.color}`,
|
|
223
|
-
fontSize:
|
|
223
|
+
fontSize: 28,
|
|
224
224
|
color: e.color,
|
|
225
225
|
whiteSpace: 'nowrap',
|
|
226
226
|
}, children: e.label }, e.id));
|
|
@@ -242,7 +242,7 @@ export function BroadcastMap({ camera, countries, regions, provinces, events, fr
|
|
|
242
242
|
top: '50%',
|
|
243
243
|
left: 'calc(100% + 10px)',
|
|
244
244
|
transform: 'translateY(-50%)',
|
|
245
|
-
fontSize:
|
|
245
|
+
fontSize: 32,
|
|
246
246
|
fontWeight: 600,
|
|
247
247
|
color: e.color,
|
|
248
248
|
whiteSpace: 'nowrap',
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { stateAt } from '../timeline.js';
|
|
3
|
+
type Jump = ReturnType<typeof stateAt>['timeJump'];
|
|
4
|
+
export declare function timeJumpStrength(jump: Jump): number;
|
|
5
|
+
export declare function TimeJump({ jump }: {
|
|
6
|
+
jump: Jump;
|
|
7
|
+
}): React.JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { layout } from './layout.js';
|
|
3
|
+
export function timeJumpStrength(jump) {
|
|
4
|
+
if (!jump)
|
|
5
|
+
return 0;
|
|
6
|
+
const ramp = Math.max(0, Math.min(1, jump.progress / 0.15, (1 - jump.progress) / 0.25));
|
|
7
|
+
return ramp * ramp * (3 - 2 * ramp);
|
|
8
|
+
}
|
|
9
|
+
// All motion comes from the requested frame, including direct seeks and stills.
|
|
10
|
+
export function TimeJump({ jump }) {
|
|
11
|
+
if (!jump)
|
|
12
|
+
return null;
|
|
13
|
+
const strength = timeJumpStrength(jump);
|
|
14
|
+
return (_jsxs(_Fragment, { children: [_jsx("svg", { width: 0, height: 0, "aria-hidden": "true", style: { position: 'absolute' }, children: _jsx("defs", { children: _jsx("filter", { id: "dbs-time-jump", x: "-5%", y: "-5%", width: "110%", height: "110%", children: _jsx("feGaussianBlur", { stdDeviation: `${5 * strength} ${0.3 * strength}` }) }) }) }), _jsxs("div", { "aria-hidden": "true", style: {
|
|
15
|
+
position: 'absolute',
|
|
16
|
+
inset: `${layout.header + 1}px 0 0`,
|
|
17
|
+
overflow: 'hidden',
|
|
18
|
+
pointerEvents: 'none',
|
|
19
|
+
opacity: strength,
|
|
20
|
+
}, children: [_jsx("div", { style: {
|
|
21
|
+
position: 'absolute',
|
|
22
|
+
inset: 0,
|
|
23
|
+
opacity: 0.22,
|
|
24
|
+
background: 'repeating-linear-gradient(to bottom, transparent 0px, transparent 3px, #c6d6df55 3px, #c6d6df55 4px, #0005 4px, #0005 6px)',
|
|
25
|
+
backgroundPositionY: (jump.elapsedFrames * 1.4) % 6,
|
|
26
|
+
} }), [0, 60].map((offset) => (_jsx("div", { style: {
|
|
27
|
+
position: 'absolute',
|
|
28
|
+
left: 0,
|
|
29
|
+
right: 0,
|
|
30
|
+
top: `${((jump.elapsedFrames * 2.8 + offset) % 120) - 10}%`,
|
|
31
|
+
height: '5%',
|
|
32
|
+
background: 'linear-gradient(to bottom, transparent, #dce8ed20 45%, #dce8ed66 50%, #0005 54%, transparent)',
|
|
33
|
+
} }, offset)))] }), _jsx("svg", { viewBox: "0 0 240 140", width: 240, height: 140, "aria-hidden": "true", style: {
|
|
34
|
+
position: 'absolute',
|
|
35
|
+
left: '50%',
|
|
36
|
+
top: '50%',
|
|
37
|
+
transform: 'translate(-50%, -50%)',
|
|
38
|
+
pointerEvents: 'none',
|
|
39
|
+
opacity: strength * 0.27,
|
|
40
|
+
filter: 'blur(2.5px)',
|
|
41
|
+
}, children: _jsx("path", { d: "M20 20 L104 70 L20 120 Z M122 20 L206 70 L122 120 Z", fill: "#e0e8ed" }) })] }));
|
|
42
|
+
}
|
|
@@ -17,11 +17,9 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
17
17
|
duration: number;
|
|
18
18
|
volume: number;
|
|
19
19
|
};
|
|
20
|
-
pageSeconds: number;
|
|
21
20
|
id: string;
|
|
22
21
|
cut: string;
|
|
23
22
|
at: number;
|
|
24
|
-
corrects?: string | undefined;
|
|
25
23
|
occurredAt?: string | undefined;
|
|
26
24
|
publishedAt?: string | undefined;
|
|
27
25
|
} & {
|
|
@@ -84,7 +82,6 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
84
82
|
heading: string;
|
|
85
83
|
text: string;
|
|
86
84
|
color: string;
|
|
87
|
-
pageSeconds: number;
|
|
88
85
|
duration: number;
|
|
89
86
|
carry: boolean;
|
|
90
87
|
id: string;
|
|
@@ -96,6 +93,35 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
96
93
|
order: number;
|
|
97
94
|
}, {
|
|
98
95
|
type: T;
|
|
96
|
+
}> | Extract<{
|
|
97
|
+
type: "chat";
|
|
98
|
+
title: string;
|
|
99
|
+
duration: number;
|
|
100
|
+
carry: boolean;
|
|
101
|
+
id: string;
|
|
102
|
+
cut: string;
|
|
103
|
+
at: number;
|
|
104
|
+
} & {
|
|
105
|
+
start: number;
|
|
106
|
+
end: number;
|
|
107
|
+
order: number;
|
|
108
|
+
}, {
|
|
109
|
+
type: T;
|
|
110
|
+
}> | Extract<{
|
|
111
|
+
type: "chat-message";
|
|
112
|
+
chat: string;
|
|
113
|
+
author: string;
|
|
114
|
+
text: string;
|
|
115
|
+
color: string;
|
|
116
|
+
id: string;
|
|
117
|
+
cut: string;
|
|
118
|
+
at: number;
|
|
119
|
+
} & {
|
|
120
|
+
start: number;
|
|
121
|
+
end: number;
|
|
122
|
+
order: number;
|
|
123
|
+
}, {
|
|
124
|
+
type: T;
|
|
99
125
|
}> | Extract<{
|
|
100
126
|
type: "zone";
|
|
101
127
|
points: [number, number][];
|
|
@@ -292,11 +318,9 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
292
318
|
duration: number;
|
|
293
319
|
volume: number;
|
|
294
320
|
};
|
|
295
|
-
pageSeconds: number;
|
|
296
321
|
id: string;
|
|
297
322
|
cut: string;
|
|
298
323
|
at: number;
|
|
299
|
-
corrects?: string | undefined;
|
|
300
324
|
occurredAt?: string | undefined;
|
|
301
325
|
publishedAt?: string | undefined;
|
|
302
326
|
} & {
|
|
@@ -359,7 +383,6 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
359
383
|
heading: string;
|
|
360
384
|
text: string;
|
|
361
385
|
color: string;
|
|
362
|
-
pageSeconds: number;
|
|
363
386
|
duration: number;
|
|
364
387
|
carry: boolean;
|
|
365
388
|
id: string;
|
|
@@ -371,6 +394,35 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
371
394
|
order: number;
|
|
372
395
|
}, {
|
|
373
396
|
type: T;
|
|
397
|
+
}> | Extract<{
|
|
398
|
+
type: "chat";
|
|
399
|
+
title: string;
|
|
400
|
+
duration: number;
|
|
401
|
+
carry: boolean;
|
|
402
|
+
id: string;
|
|
403
|
+
cut: string;
|
|
404
|
+
at: number;
|
|
405
|
+
} & {
|
|
406
|
+
start: number;
|
|
407
|
+
end: number;
|
|
408
|
+
order: number;
|
|
409
|
+
}, {
|
|
410
|
+
type: T;
|
|
411
|
+
}> | Extract<{
|
|
412
|
+
type: "chat-message";
|
|
413
|
+
chat: string;
|
|
414
|
+
author: string;
|
|
415
|
+
text: string;
|
|
416
|
+
color: string;
|
|
417
|
+
id: string;
|
|
418
|
+
cut: string;
|
|
419
|
+
at: number;
|
|
420
|
+
} & {
|
|
421
|
+
start: number;
|
|
422
|
+
end: number;
|
|
423
|
+
order: number;
|
|
424
|
+
}, {
|
|
425
|
+
type: T;
|
|
374
426
|
}> | Extract<{
|
|
375
427
|
type: "zone";
|
|
376
428
|
points: [number, number][];
|
|
@@ -555,8 +607,3 @@ export declare function transitionAt<T extends CompiledEvent['type']>(events: Co
|
|
|
555
607
|
progress: number;
|
|
556
608
|
start: number;
|
|
557
609
|
};
|
|
558
|
-
export declare function pageAt(frame: number, start: number, seconds: number, count: number): {
|
|
559
|
-
page: number;
|
|
560
|
-
previous: number | undefined;
|
|
561
|
-
progress: number;
|
|
562
|
-
};
|
|
@@ -20,12 +20,3 @@ export function transitionAt(events, type, frame) {
|
|
|
20
20
|
start,
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
|
-
export function pageAt(frame, start, seconds, count) {
|
|
24
|
-
const frames = Math.round(seconds * FPS);
|
|
25
|
-
const turn = Math.floor(Math.max(0, frame - start) / frames);
|
|
26
|
-
return {
|
|
27
|
-
page: turn % count,
|
|
28
|
-
previous: turn > 0 && count > 1 ? (turn - 1) % count : undefined,
|
|
29
|
-
progress: turn > 0 && count > 1 ? changeProgress(frame, start + turn * frames) : 1,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -21,7 +21,7 @@ export function useBundledFonts(text) {
|
|
|
21
21
|
})))
|
|
22
22
|
.then(() => Promise.all(fonts.flatMap((font) => font.weights.map((weight) => document.fonts.load(`${weight} 28px "${font.family}"`, font.id === 'mono'
|
|
23
23
|
? '0123456789:.- /+'
|
|
24
|
-
: text + ' 재난문자 수신 이력 발생 발표
|
|
24
|
+
: text + ' 재난문자 수신 이력 발생 발표 안전안내 긴급재난 위급재난')))))
|
|
25
25
|
.then(() => document.fonts.ready)
|
|
26
26
|
.then(() => { if (!cancelled) {
|
|
27
27
|
continueRender(fontHandle);
|