@lincyaw/host 1.0.0-rc.12 → 1.0.0-rc.14
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/package.json +6 -3
- package/src/contracts.ts +10 -0
- package/src/design/components.ts +6 -20
- package/src/design/menu.ts +18 -0
- package/src/design/segmented.ts +11 -0
- package/src/index.ts +5 -5
- package/src/registries.ts +10 -1
- package/src/runtime.ts +3 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lincyaw/host",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.14",
|
|
4
4
|
"description": "Presmium's host interface for plugin components in every form: step state, services, interface text, and the design system (tokens, components, fonts)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"design-system"
|
|
17
17
|
],
|
|
18
18
|
"type": "module",
|
|
19
|
+
"sideEffects": [
|
|
20
|
+
"*.css"
|
|
21
|
+
],
|
|
19
22
|
"engines": {
|
|
20
23
|
"node": ">=22.19"
|
|
21
24
|
},
|
|
@@ -41,8 +44,8 @@
|
|
|
41
44
|
"@radix-ui/react-tooltip": "^1.2.8",
|
|
42
45
|
"react": "18.3.1",
|
|
43
46
|
"react-dom": "18.3.1",
|
|
44
|
-
"@lincyaw/design": "1.0.0-rc.
|
|
45
|
-
"@lincyaw/kernel": "1.0.0-rc.
|
|
47
|
+
"@lincyaw/design": "1.0.0-rc.14",
|
|
48
|
+
"@lincyaw/kernel": "1.0.0-rc.14"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
51
|
"@types/react": "18.3.28",
|
package/src/contracts.ts
CHANGED
|
@@ -143,6 +143,16 @@ export interface TimedCaptions {
|
|
|
143
143
|
}
|
|
144
144
|
export const TIMED_CAPTIONS = defineService<TimedCaptions>('presmium.timedCaptions')
|
|
145
145
|
|
|
146
|
+
/**
|
|
147
|
+
* A component that draws asynchronously (a scene loading its code) holds the page as not yet drawn until it has: the
|
|
148
|
+
* viewer tells an embedding page when the current slide is drawn (`notifyEmbedder`).
|
|
149
|
+
*/
|
|
150
|
+
export interface PageReadiness {
|
|
151
|
+
readonly pending: Store<number>
|
|
152
|
+
hold(): Unsubscribe
|
|
153
|
+
}
|
|
154
|
+
export const PAGE_READINESS = defineService<PageReadiness>('presmium.pageReadiness')
|
|
155
|
+
|
|
146
156
|
export interface StepClockDriver {
|
|
147
157
|
readonly elapsed: Store<number | undefined>
|
|
148
158
|
readonly recording?: boolean
|
package/src/design/components.ts
CHANGED
|
@@ -2,10 +2,8 @@ import { createContext, createElement as h, forwardRef, useContext, useEffect, u
|
|
|
2
2
|
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react'
|
|
3
3
|
import * as Dialog from '@radix-ui/react-dialog'
|
|
4
4
|
import * as Popover from '@radix-ui/react-popover'
|
|
5
|
-
import * as Dropdown from '@radix-ui/react-dropdown-menu'
|
|
6
5
|
import * as Tabs from '@radix-ui/react-tabs'
|
|
7
6
|
import * as Tooltip from '@radix-ui/react-tooltip'
|
|
8
|
-
import * as RadioGroup from '@radix-ui/react-radio-group'
|
|
9
7
|
import { useLocale } from '../locale.ts'
|
|
10
8
|
import { HOST_MESSAGES } from '../messages.ts'
|
|
11
9
|
import { ICONS } from '@lincyaw/design'
|
|
@@ -13,11 +11,13 @@ import type { IconName } from '@lincyaw/design'
|
|
|
13
11
|
import type { ToolbarPlacement } from '../contracts.ts'
|
|
14
12
|
export type ButtonVariant = 'default' | 'primary' | 'accent' | 'ghost' | 'danger'
|
|
15
13
|
export type ControlDensity = 'compact' | 'regular' | 'touch'
|
|
14
|
+
/** `overlay`: one large round control laid over media, such as the Play that starts a lecture the browser would not autoplay. */
|
|
15
|
+
export type IconButtonVariant = 'default' | 'overlay'
|
|
16
16
|
export type Tone = 'neutral' | 'accent' | 'ok' | 'warn' | 'danger' | 'outline'
|
|
17
17
|
export type PopoverPlacement = 'below-end' | 'below-start' | 'above-end' | 'above-start' | 'above' | 'below'
|
|
18
18
|
export interface Choice { value: string, label: string, icon?: IconName, disabled?: boolean }
|
|
19
19
|
export interface MenuItem { id: string, label: string, icon?: IconName, danger?: boolean, disabled?: boolean, shortcut?: string, checked?: boolean, separator?: boolean }
|
|
20
|
-
const cls = (...values: (string | false | undefined)[]) => values.filter(Boolean).join(' ')
|
|
20
|
+
export const cls = (...values: (string | false | undefined)[]) => values.filter(Boolean).join(' ')
|
|
21
21
|
export function PmIcon({ name, size: _size, strokeWidth = 1.7, ...attrs }: { name: IconName, size?: number | string, strokeWidth?: number } & HTMLAttributes<SVGElement>) {
|
|
22
22
|
return h('svg', { ...attrs, className: cls('pm-ui-icon', attrs.className), viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth, strokeLinecap: 'round', strokeLinejoin: 'round', 'aria-hidden': true, focusable: false }, ICONS[name]?.map(([tag, attributes], key) => h(tag, { ...attributes, key })))
|
|
23
23
|
}
|
|
@@ -33,8 +33,8 @@ export function PmTooltip({ label, shortcut, children }: { label: string, shortc
|
|
|
33
33
|
h(Tooltip.Root, {}, h(Tooltip.Trigger, { asChild: true }, children), h(Tooltip.Portal, {},
|
|
34
34
|
h(Tooltip.Content, { className: 'pm-ui-tooltip pm-ui', side: 'top', sideOffset: 6, collisionPadding: 8 }, label, shortcut ? h('kbd', { className: 'pm-ui-shortcut' }, shortcut) : null))) })
|
|
35
35
|
}
|
|
36
|
-
export const PmIconButton = forwardRef<HTMLButtonElement, ButtonProps & { icon: IconName, label: string, hint?: string, shortcut?: string }>(function PmIconButton({ icon, label, hint, shortcut, small, density = small ? 'compact' : 'regular', loading, className, disabled, ...rest }, ref) {
|
|
37
|
-
const button = h('button', { ...rest, ref, type: 'button', 'data-density': density, 'aria-label': label, 'aria-busy': loading || undefined, disabled: disabled || loading || rest['aria-disabled'] === true || rest['aria-disabled'] === 'true', className: cls('pm-ui-icon-button', className) }, h(PmIcon, { name: loading ? 'loading' : icon, className: loading ? 'pm-ui-loading' : undefined }))
|
|
36
|
+
export const PmIconButton = forwardRef<HTMLButtonElement, Omit<ButtonProps, 'variant'> & { variant?: IconButtonVariant, icon: IconName, label: string, hint?: string, shortcut?: string }>(function PmIconButton({ variant = 'default', icon, label, hint, shortcut, small, density = small ? 'compact' : 'regular', loading, className, disabled, ...rest }, ref) {
|
|
37
|
+
const button = h('button', { ...rest, ref, type: 'button', 'data-density': density, 'aria-label': label, 'aria-busy': loading || undefined, disabled: disabled || loading || rest['aria-disabled'] === true || rest['aria-disabled'] === 'true', className: cls('pm-ui-icon-button', variant !== 'default' && `pm-ui-icon-button--${variant}`, className) }, h(PmIcon, { name: loading ? 'loading' : icon, className: loading ? 'pm-ui-loading' : undefined }))
|
|
38
38
|
return h(PmTooltip, { label: hint ?? label, shortcut, children: button })
|
|
39
39
|
})
|
|
40
40
|
export function PmBadge({ tone = 'neutral', pill, children, className, ...rest }: HTMLAttributes<HTMLSpanElement> & { tone?: Tone, pill?: boolean }) { return h('span', { ...rest, className: cls('pm-ui-badge', tone !== 'neutral' && `pm-ui-badge--${tone}`, pill && 'pm-ui-badge--pill', className) }, children) }
|
|
@@ -102,18 +102,8 @@ function DialogSurface({ open, label, closeLabel, name, children, actions, trigg
|
|
|
102
102
|
}
|
|
103
103
|
export function PmDialog(props: OverlayProps) { return h(DialogSurface, props) }
|
|
104
104
|
export function PmDrawer(props: OverlayProps) { return h(DialogSurface, { ...props, drawer: true }) }
|
|
105
|
-
const placement = (value: PopoverPlacement) => ({ side: value.startsWith('above') ? 'top' as const : 'bottom' as const, align: value.endsWith('start') ? 'start' as const : 'end' as const })
|
|
105
|
+
export const placement = (value: PopoverPlacement) => ({ side: value.startsWith('above') ? 'top' as const : 'bottom' as const, align: value.endsWith('start') ? 'start' as const : 'end' as const })
|
|
106
106
|
export function PmPopover({ open, label, name, children, trigger, placement: place = 'below-end', onOpenChange }: OverlayProps & { placement?: PopoverPlacement }) { return h(Popover.Root, { open, onOpenChange }, trigger ? h(Popover.Trigger, { asChild: true }, trigger) : null, h(Popover.Portal, {}, h(Popover.Content, { ...placement(place), className: cls('pm-ui-popover pm-ui-floating pm-ui', name), 'aria-label': label, onEscapeKeyDown: event => event.stopPropagation() }, children))) }
|
|
107
|
-
export function PmMenu({ label, items, icon = 'more', placement: place = 'below-end', onSelect }: { label: string, items: MenuItem[], icon?: IconName, placement?: PopoverPlacement, onSelect?(id: string): void }) {
|
|
108
|
-
return h(Dropdown.Root, {},
|
|
109
|
-
h(Dropdown.Trigger, { asChild: true }, h(PmIconButton, { icon, label, small: true })),
|
|
110
|
-
h(Dropdown.Portal, {}, h(Dropdown.Content, { ...placement(place), className: 'pm-ui-menu pm-ui-floating pm-ui', ...{ 'data-density': 'compact' }, 'aria-label': label, onEscapeKeyDown: event => event.stopPropagation() },
|
|
111
|
-
items.map(item => h(Dropdown.Group, { key: item.id }, item.separator ? h(Dropdown.Separator, { className: 'pm-ui-menu__separator' }) : null,
|
|
112
|
-
item.checked === undefined ? h(Dropdown.Item, { disabled: item.disabled, textValue: item.label, className: cls('pm-ui-menu__item', item.danger && 'pm-ui-menu__item--danger'), onSelect: () => onSelect?.(item.id) },
|
|
113
|
-
item.icon ? h(PmIcon, { name: item.icon }) : null, item.label, item.shortcut ? h('kbd', { className: 'pm-ui-shortcut' }, item.shortcut) : null)
|
|
114
|
-
: h(Dropdown.CheckboxItem, { checked: item.checked, disabled: item.disabled, textValue: item.label, className: cls('pm-ui-menu__item', item.danger && 'pm-ui-menu__item--danger'), onSelect: () => onSelect?.(item.id) },
|
|
115
|
-
h(Dropdown.ItemIndicator, {}, h(PmIcon, { name: 'check' })), item.label))))))
|
|
116
|
-
}
|
|
117
107
|
export function PmTabs({ value, modelValue, tabs, label, children, onValueChange, iconOnly, density = 'compact' }: { value?: string, modelValue?: string, tabs: Choice[], label: string, children?: ReactNode, iconOnly?: boolean, density?: ControlDensity, onValueChange?(value: string): void }) {
|
|
118
108
|
return h(Tabs.Root, { value: value ?? modelValue, onValueChange, activationMode: 'manual', className: 'pm-ui-tabs-root', ...{ 'data-density': density } },
|
|
119
109
|
h(Tabs.List, { className: 'pm-ui-tabs', 'aria-label': label }, tabs.map(tab => {
|
|
@@ -122,10 +112,6 @@ export function PmTabs({ value, modelValue, tabs, label, children, onValueChange
|
|
|
122
112
|
})), children)
|
|
123
113
|
}
|
|
124
114
|
export function PmTabPanel({ value, children, forceMount, hidden }: { value: string, children?: ReactNode, forceMount?: true, hidden?: boolean }) { return h(Tabs.Content, { value, forceMount, hidden, className: 'pm-ui-tab-panel' }, children) }
|
|
125
|
-
export function PmSegmented({ value, modelValue, choices, items, label, onValueChange, density = 'compact', disabled }: { value?: string, modelValue?: string, choices?: Choice[], items?: Choice[], label: string, density?: ControlDensity, disabled?: boolean, onValueChange?(value: string): void }) {
|
|
126
|
-
return h(RadioGroup.Root, { className: 'pm-ui-segmented', 'aria-label': label, value: value ?? modelValue, onValueChange, orientation: 'horizontal', ...{ 'data-density': density }, disabled },
|
|
127
|
-
(choices ?? items ?? []).map(item => h(RadioGroup.Item, { key: item.value, value: item.value, className: 'pm-ui-segmented__option', disabled: item.disabled }, item.icon ? h(PmIcon, { name: item.icon }) : null, item.label)))
|
|
128
|
-
}
|
|
129
115
|
export function PmNotice({ tone = 'neutral', children, className, ...rest }: HTMLAttributes<HTMLDivElement> & { tone?: Tone }) { return h('div', { ...rest, className: cls('pm-ui-notice', tone !== 'neutral' && `pm-ui-notice--${tone}`, className), role: tone === 'danger' ? 'alert' : 'status' }, children) }
|
|
130
116
|
export function PmEmpty({ title, icon, action, children }: { title?: string, icon?: IconName, action?: ReactNode, children?: ReactNode }) { return h('div', { className: 'pm-ui-empty' }, icon ? h(PmIcon, { name: icon }) : null, title ? h('p', { className: 'pm-ui-empty__title' }, title) : null, children, action) }
|
|
131
117
|
/** Loading in the shape of the content: `document` headings and paragraphs, `list` rows, `preview` a slide-shaped area. */
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createElement as h } from 'react'
|
|
2
|
+
import * as Dropdown from '@radix-ui/react-dropdown-menu'
|
|
3
|
+
import type { IconName } from '@lincyaw/design'
|
|
4
|
+
import { cls, placement, PmIcon, PmIconButton } from './components.ts'
|
|
5
|
+
import type { MenuItem, PopoverPlacement } from './components.ts'
|
|
6
|
+
|
|
7
|
+
// A module of its own, as `segmented.ts` is: a page whose code shows no menu loads none of Radix's menus.
|
|
8
|
+
|
|
9
|
+
export function PmMenu({ label, items, icon = 'more', placement: place = 'below-end', onSelect }: { label: string, items: MenuItem[], icon?: IconName, placement?: PopoverPlacement, onSelect?(id: string): void }) {
|
|
10
|
+
return h(Dropdown.Root, {},
|
|
11
|
+
h(Dropdown.Trigger, { asChild: true }, h(PmIconButton, { icon, label, small: true })),
|
|
12
|
+
h(Dropdown.Portal, {}, h(Dropdown.Content, { ...placement(place), className: 'pm-ui-menu pm-ui-floating pm-ui', ...{ 'data-density': 'compact' }, 'aria-label': label, onEscapeKeyDown: event => event.stopPropagation() },
|
|
13
|
+
items.map(item => h(Dropdown.Group, { key: item.id }, item.separator ? h(Dropdown.Separator, { className: 'pm-ui-menu__separator' }) : null,
|
|
14
|
+
item.checked === undefined ? h(Dropdown.Item, { disabled: item.disabled, textValue: item.label, className: cls('pm-ui-menu__item', item.danger && 'pm-ui-menu__item--danger'), onSelect: () => onSelect?.(item.id) },
|
|
15
|
+
item.icon ? h(PmIcon, { name: item.icon }) : null, item.label, item.shortcut ? h('kbd', { className: 'pm-ui-shortcut' }, item.shortcut) : null)
|
|
16
|
+
: h(Dropdown.CheckboxItem, { checked: item.checked, disabled: item.disabled, textValue: item.label, className: cls('pm-ui-menu__item', item.danger && 'pm-ui-menu__item--danger'), onSelect: () => onSelect?.(item.id) },
|
|
17
|
+
h(Dropdown.ItemIndicator, {}, h(PmIcon, { name: 'check' })), item.label))))))
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createElement as h } from 'react'
|
|
2
|
+
import * as RadioGroup from '@radix-ui/react-radio-group'
|
|
3
|
+
import { PmIcon } from './components.ts'
|
|
4
|
+
import type { Choice, ControlDensity } from './components.ts'
|
|
5
|
+
|
|
6
|
+
// A module of its own, as `menu.ts` is: a page whose code shows no segmented control loads none of Radix's radio group.
|
|
7
|
+
|
|
8
|
+
export function PmSegmented({ value, modelValue, choices, items, label, onValueChange, density = 'compact', disabled }: { value?: string, modelValue?: string, choices?: Choice[], items?: Choice[], label: string, density?: ControlDensity, disabled?: boolean, onValueChange?(value: string): void }) {
|
|
9
|
+
return h(RadioGroup.Root, { className: 'pm-ui-segmented', 'aria-label': label, value: value ?? modelValue, onValueChange, orientation: 'horizontal', ...{ 'data-density': density }, disabled },
|
|
10
|
+
(choices ?? items ?? []).map(item => h(RadioGroup.Item, { key: item.value, value: item.value, className: 'pm-ui-segmented__option', disabled: item.disabled }, item.icon ? h(PmIcon, { name: item.icon }) : null, item.label)))
|
|
11
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -18,10 +18,8 @@ export {
|
|
|
18
18
|
PmField,
|
|
19
19
|
PmIcon,
|
|
20
20
|
PmIconButton,
|
|
21
|
-
PmMenu,
|
|
22
21
|
PmNotice,
|
|
23
22
|
PmPopover,
|
|
24
|
-
PmSegmented,
|
|
25
23
|
PmTabs,
|
|
26
24
|
PmTabPanel,
|
|
27
25
|
PmTooltip,
|
|
@@ -29,7 +27,9 @@ export {
|
|
|
29
27
|
provideToolbarPlacement,
|
|
30
28
|
useToolbarPlacement,
|
|
31
29
|
} from './design/components.ts'
|
|
32
|
-
export type { ButtonVariant, Choice, ControlDensity, MenuItem, PopoverPlacement, Tone } from './design/components.ts'
|
|
30
|
+
export type { ButtonVariant, Choice, ControlDensity, IconButtonVariant, MenuItem, PopoverPlacement, Tone } from './design/components.ts'
|
|
31
|
+
export { PmMenu } from './design/menu.ts'
|
|
32
|
+
export { PmSegmented } from './design/segmented.ts'
|
|
33
33
|
export { PmColumns, PmStack, PmTabGroup } from './design/layout.ts'
|
|
34
34
|
export { PmCaption, PmCaptionProvider } from './design/caption.ts'
|
|
35
35
|
export type { CaptionProps } from './design/caption.ts'
|
|
@@ -73,8 +73,8 @@ export type { StepScope as StepScopeValue } from './step-scope.ts'
|
|
|
73
73
|
export { createSharedStates } from './shared-state.ts'
|
|
74
74
|
export type { SharedStates } from './shared-state.ts'
|
|
75
75
|
export { PmToolbar } from './panels.ts'
|
|
76
|
-
export { NAVIGATION, OPERATIONS, PLAYBACK_CONTROL, PRODUCTION_PLAYBACK, STATUS, STEP_CLOCK, SYNC_TRANSPORTS, TIMED_CAPTIONS, TOOLBAR } from './contracts.ts'
|
|
77
|
-
export type { TimedCaptions } from './contracts.ts'
|
|
76
|
+
export { NAVIGATION, OPERATIONS, PAGE_READINESS, PLAYBACK_CONTROL, PRODUCTION_PLAYBACK, STATUS, STEP_CLOCK, SYNC_TRANSPORTS, TIMED_CAPTIONS, TOOLBAR } from './contracts.ts'
|
|
77
|
+
export type { PageReadiness, TimedCaptions } from './contracts.ts'
|
|
78
78
|
export { joinWindows } from './windows.ts'
|
|
79
79
|
export { setSiteBase, siteBase } from './site.ts'
|
|
80
80
|
export type { WindowChannel } from './windows.ts'
|
package/src/registries.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { PresenterPanel, PresenterPanels, Unsubscribe } from '@lincyaw/kern
|
|
|
2
2
|
import { currentLocale } from './locale.ts'
|
|
3
3
|
import { HOST_MESSAGES } from './messages.ts'
|
|
4
4
|
import StartReportPanel from './start-report-panel.ts'
|
|
5
|
-
import type { StatusProvider, StatusRegistry, TimedCaptions, Toolbar, ToolbarItem } from './contracts.ts'
|
|
5
|
+
import type { PageReadiness, StatusProvider, StatusRegistry, TimedCaptions, Toolbar, ToolbarItem } from './contracts.ts'
|
|
6
6
|
import { createStore } from './store.ts'
|
|
7
7
|
|
|
8
8
|
export function createTimedCaptions(): TimedCaptions {
|
|
@@ -14,6 +14,15 @@ export function createTimedCaptions(): TimedCaptions {
|
|
|
14
14
|
} }
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
export function createPageReadiness(): PageReadiness {
|
|
18
|
+
const holders = new Set<symbol>(), pending = createStore(0)
|
|
19
|
+
return { pending, hold() {
|
|
20
|
+
const holder = Symbol()
|
|
21
|
+
holders.add(holder); pending.set(holders.size)
|
|
22
|
+
return () => { if (holders.delete(holder)) pending.set(holders.size) }
|
|
23
|
+
} }
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
interface Item {
|
|
18
27
|
id: string
|
|
19
28
|
order?: number
|
package/src/runtime.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { EnvironmentSpec, Logger, PluginDefinition, PluginReport, Presentat
|
|
|
3
3
|
import { createStore } from './store.ts'
|
|
4
4
|
import { connectActions } from './actions.ts'
|
|
5
5
|
import { PmApprovals } from './approvals.ts'
|
|
6
|
-
import { NAVIGATION, OPERATIONS, PLAYBACK_CONTROL, STATUS, STEP_CLOCK, SYNC_TRANSPORTS, TIMED_CAPTIONS, TOOLBAR } from './contracts.ts'
|
|
6
|
+
import { NAVIGATION, OPERATIONS, PAGE_READINESS, PLAYBACK_CONTROL, STATUS, STEP_CLOCK, SYNC_TRANSPORTS, TIMED_CAPTIONS, TOOLBAR } from './contracts.ts'
|
|
7
7
|
import type { Navigation, ServerClient, SyncTransports } from './contracts.ts'
|
|
8
8
|
import { connectControl } from './control.ts'
|
|
9
9
|
import { componentErrors } from './error-boundary.ts'
|
|
@@ -11,7 +11,7 @@ import { currentLocale } from './locale.ts'
|
|
|
11
11
|
import { HOST_MESSAGES } from './messages.ts'
|
|
12
12
|
import { createOperations } from './operations.ts'
|
|
13
13
|
import { createPlaybackControl } from './playback-control.ts'
|
|
14
|
-
import { createPresenterPanels, createStatusRegistry, createTimedCaptions, createToolbar } from './registries.ts'
|
|
14
|
+
import { createPageReadiness, createPresenterPanels, createStatusRegistry, createTimedCaptions, createToolbar } from './registries.ts'
|
|
15
15
|
import { provideServer } from './server.ts'
|
|
16
16
|
import { createStepClock } from './step-clock.ts'
|
|
17
17
|
|
|
@@ -156,6 +156,7 @@ export function startHost(options: HostStart): Promise<StartReport> {
|
|
|
156
156
|
kernel.provide(OPERATIONS, operations)
|
|
157
157
|
kernel.provide(PLAYBACK_CONTROL, createPlaybackControl())
|
|
158
158
|
kernel.provide(TIMED_CAPTIONS, createTimedCaptions())
|
|
159
|
+
kernel.provide(PAGE_READINESS, createPageReadiness())
|
|
159
160
|
const stepClock = createStepClock(options.navigation)
|
|
160
161
|
release(stepClock.stop)
|
|
161
162
|
kernel.provide(STEP_CLOCK, stepClock.clock)
|