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.
@@ -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: 48,
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: 23,
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("main", { style: {
59
- position: 'absolute',
60
- left: 0,
61
- top: 49,
62
- right: 640,
63
- bottom: 93,
64
- }, children: [_jsx(BroadcastMap, { camera: state.camera, countries: countries, regions: regions, provinces: provinces, events: mapEvents, frame: state.contentFrame }), legend?.type === 'legend' && (_jsx("div", { style: {
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
- left: 24,
67
- bottom: 24,
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
- gap: 10,
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
- right: 20,
85
- bottom: 20,
86
- width: 420,
87
- height: 290,
88
- border: '1px solid #78858e',
89
- opacity: changeProgress(state.contentFrame, inset.start),
90
- transform: `translateY(${10 * (1 - changeProgress(state.contentFrame, inset.start))}px)`,
91
- }, children: [_jsx(BroadcastMap, { camera: inset.camera, countries: countries, regions: regions, provinces: provinces, events: mapEvents, frame: state.contentFrame, inset: true }), _jsx("div", { style: {
92
- position: 'absolute',
93
- top: 0,
94
- left: 0,
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: timeline.scenario.title.length > 40 ? 30 : 36,
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: 24,
169
+ fontSize: 36,
160
170
  color: '#bac3c9',
161
171
  marginTop: 22,
162
172
  maxWidth: 1200,
@@ -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, frame, progress, }: {
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;
@@ -1,15 +1,10 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { pages } from '../scenario.js';
3
- import { numberFont } from './fonts.js';
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: 49,
12
- bottom: 93,
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, color: '#9ba5ac', marginBottom: 32 }, children: source }), _jsx("h2", { style: {
23
- fontSize: 46,
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: 34,
30
- lineHeight: 1.9,
24
+ fontSize: 44,
25
+ lineHeight: 1.6,
31
26
  whiteSpace: 'pre-wrap',
32
- wordBreak: 'keep-all',
33
- }, children: _jsx(PagedText, { chunks: chunks, frame: frame, start: event.start, seconds: event.pageSeconds }) }), chunks.length > 1 && (_jsxs("div", { style: {
34
- fontSize: 18,
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, pageAt, transitionAt } from './transitions.js';
6
- export function PagedText({ chunks, frame, start, seconds, }) {
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) => news && (_jsxs("div", { "aria-hidden": i === 0 || undefined, style: {
26
- position: 'absolute',
27
- inset: 0,
28
- display: 'flex',
29
- alignItems: 'center',
30
- gap: 24,
31
- opacity: i === 0 ? 1 - progress : progress,
32
- transform: `translateY(${i === 0 ? -92 * progress : 92 * (1 - progress)}px)`,
33
- }, children: [_jsxs("div", { style: { minWidth: 135, flexShrink: 0 }, children: [_jsx("div", { style: { fontSize: 18, color: '#b3c0c9', fontWeight: 600 }, children: timeline.scenario.agencies[news.source] }), _jsx("div", { style: {
34
- fontSize: 16,
35
- color: '#85959f',
36
- marginTop: 5,
37
- fontFamily: numberFont,
38
- fontVariantNumeric: 'tabular-nums',
39
- }, children: clock(news.publishedAt
40
- ? Date.parse(news.publishedAt)
41
- : stateAt(timeline, news.start).epoch, timeline.scenario.timezone) })] }), _jsx("div", { style: {
42
- fontSize: news.headline.length > 120 ? 23 : news.headline.length > 85 ? 25 : 30,
43
- lineHeight: 1.3,
44
- wordBreak: 'keep-all',
45
- overflowWrap: 'anywhere',
46
- }, children: news.headline })] }, news.id))), _jsx("div", { style: {
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: 18,
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: 18,
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: 22,
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
+ }
@@ -0,0 +1,5 @@
1
+ export declare const layout: {
2
+ readonly header: 64;
3
+ readonly footer: 128;
4
+ readonly sidebar: 768;
5
+ };
@@ -0,0 +1,2 @@
1
+ // Composition pixels. At 854px landscape width, 40px text is approximately 18px.
2
+ export const layout = { header: 64, footer: 128, sidebar: 768 };
@@ -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);