@roku-sdk/components 0.0.0-stage → 2.19.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.
Files changed (212) hide show
  1. package/BREAKING_CHANGES.md +84 -0
  2. package/LICENSE +21 -0
  3. package/README.md +19 -2
  4. package/core-jsx.d.ts +53 -0
  5. package/eslint.config.mjs +3 -0
  6. package/jsx.d.ts +273 -0
  7. package/package.json +85 -3
  8. package/src/__tests__/container.test.tsx +150 -0
  9. package/src/__tests__/effect-gradient-validation.test.tsx +133 -0
  10. package/src/__tests__/glyphs.test.ts +202 -0
  11. package/src/__tests__/modal-boundary-export.test.ts +12 -0
  12. package/src/__tests__/native-modal.test.tsx +224 -0
  13. package/src/__tests__/tween-release.test.tsx +125 -0
  14. package/src/__tests__/tween-stops-shape.test.tsx +165 -0
  15. package/src/__tests__/tween-without-compat.test.tsx +64 -0
  16. package/src/__tests__/tween.test.tsx +195 -0
  17. package/src/blended-busy-spinner.props.ts +37 -0
  18. package/src/blended-busy-spinner.tsx +13 -0
  19. package/src/bounding-tracker.props.ts +18 -0
  20. package/src/bounding-tracker.tsx +13 -0
  21. package/src/button-row.tsx +126 -0
  22. package/src/channel-store.props.ts +425 -0
  23. package/src/channel-store.tsx +76 -0
  24. package/src/container.tsx +130 -0
  25. package/src/core/jsx-runtime.ts +39 -0
  26. package/src/focusable-group.props.ts +8 -0
  27. package/src/focusable-group.tsx +17 -0
  28. package/src/focusable.props.ts +14 -0
  29. package/src/focusable.tsx +17 -0
  30. package/src/glyphs.ts +116 -0
  31. package/src/helpers/index.tsx +4 -0
  32. package/src/helpers/resolution-context.tsx +68 -0
  33. package/src/helpers/scale-for-device-resolution.ts +31 -0
  34. package/src/index.tsx +70 -0
  35. package/src/jsx-props.ts +87 -0
  36. package/src/kdfs.d.ts +14 -0
  37. package/src/kdfs.ts +15 -0
  38. package/src/key-handler.props.ts +33 -0
  39. package/src/key-handler.tsx +24 -0
  40. package/src/modal-dialog.props.ts +38 -0
  41. package/src/modal-dialog.tsx +41 -0
  42. package/src/retain-asset.ts +24 -0
  43. package/src/sg-intrinsics.json +1153 -0
  44. package/src/sg-nodes/__tests__/__snapshots__/font.test.tsx.snap +13 -0
  45. package/src/sg-nodes/__tests__/__snapshots__/prop-forwarding.test.tsx.snap +4409 -0
  46. package/src/sg-nodes/__tests__/core-props.test.ts +301 -0
  47. package/src/sg-nodes/__tests__/font.test.tsx +141 -0
  48. package/src/sg-nodes/__tests__/imperative-methods.test.ts +276 -0
  49. package/src/sg-nodes/__tests__/prop-forwarding.test.tsx +1049 -0
  50. package/src/sg-nodes/__tests__/solid-integration.test.tsx +719 -0
  51. package/src/sg-nodes/animated-image.props.ts +64 -0
  52. package/src/sg-nodes/animated-image.tsx +23 -0
  53. package/src/sg-nodes/animation.props.ts +292 -0
  54. package/src/sg-nodes/animation.tsx +190 -0
  55. package/src/sg-nodes/array-grid.ts +602 -0
  56. package/src/sg-nodes/audio.props.ts +302 -0
  57. package/src/sg-nodes/audio.tsx +57 -0
  58. package/src/sg-nodes/busy-spinner.props.ts +59 -0
  59. package/src/sg-nodes/busy-spinner.tsx +84 -0
  60. package/src/sg-nodes/button-group.props.ts +141 -0
  61. package/src/sg-nodes/button-group.tsx +30 -0
  62. package/src/sg-nodes/button.props.ts +119 -0
  63. package/src/sg-nodes/button.tsx +31 -0
  64. package/src/sg-nodes/check-list.props.ts +71 -0
  65. package/src/sg-nodes/check-list.tsx +55 -0
  66. package/src/sg-nodes/content-node.props.ts +1316 -0
  67. package/src/sg-nodes/content-node.tsx +153 -0
  68. package/src/sg-nodes/core-props.ts +197 -0
  69. package/src/sg-nodes/custom-keyboard.props.ts +108 -0
  70. package/src/sg-nodes/custom-keyboard.tsx +48 -0
  71. package/src/sg-nodes/dialog.props.ts +159 -0
  72. package/src/sg-nodes/dialog.tsx +36 -0
  73. package/src/sg-nodes/dynamic-key-grid.props.ts +22 -0
  74. package/src/sg-nodes/dynamic-key-grid.tsx +16 -0
  75. package/src/sg-nodes/dynamic-keyboard.props.ts +30 -0
  76. package/src/sg-nodes/dynamic-keyboard.tsx +18 -0
  77. package/src/sg-nodes/dynamic-mini-keyboard.props.ts +22 -0
  78. package/src/sg-nodes/dynamic-mini-keyboard.tsx +18 -0
  79. package/src/sg-nodes/dynamic-pinpad.props.ts +57 -0
  80. package/src/sg-nodes/dynamic-pinpad.tsx +18 -0
  81. package/src/sg-nodes/effect.props.ts +119 -0
  82. package/src/sg-nodes/effect.tsx +167 -0
  83. package/src/sg-nodes/font.props.ts +45 -0
  84. package/src/sg-nodes/font.tsx +80 -0
  85. package/src/sg-nodes/group.props.ts +105 -0
  86. package/src/sg-nodes/group.tsx +30 -0
  87. package/src/sg-nodes/label-list.props.ts +86 -0
  88. package/src/sg-nodes/label-list.tsx +64 -0
  89. package/src/sg-nodes/label.props.ts +207 -0
  90. package/src/sg-nodes/label.tsx +56 -0
  91. package/src/sg-nodes/layout-group.props.ts +69 -0
  92. package/src/sg-nodes/layout-group.tsx +75 -0
  93. package/src/sg-nodes/list-panel.props.ts +103 -0
  94. package/src/sg-nodes/list-panel.tsx +49 -0
  95. package/src/sg-nodes/markup-grid.props.ts +78 -0
  96. package/src/sg-nodes/markup-grid.tsx +38 -0
  97. package/src/sg-nodes/markup-list.props.ts +66 -0
  98. package/src/sg-nodes/markup-list.tsx +38 -0
  99. package/src/sg-nodes/mask-group.props.ts +78 -0
  100. package/src/sg-nodes/mask-group.tsx +59 -0
  101. package/src/sg-nodes/media-playback-methods.ts +20 -0
  102. package/src/sg-nodes/node.ts +77 -0
  103. package/src/sg-nodes/overhang.props.ts +169 -0
  104. package/src/sg-nodes/overhang.tsx +29 -0
  105. package/src/sg-nodes/panel-set.props.ts +71 -0
  106. package/src/sg-nodes/panel-set.tsx +53 -0
  107. package/src/sg-nodes/panel.props.ts +176 -0
  108. package/src/sg-nodes/panel.tsx +56 -0
  109. package/src/sg-nodes/poster-grid.props.ts +277 -0
  110. package/src/sg-nodes/poster-grid.tsx +45 -0
  111. package/src/sg-nodes/poster.props.ts +125 -0
  112. package/src/sg-nodes/poster.tsx +44 -0
  113. package/src/sg-nodes/radio-button-list.props.ts +59 -0
  114. package/src/sg-nodes/radio-button-list.tsx +68 -0
  115. package/src/sg-nodes/rectangle.props.ts +29 -0
  116. package/src/sg-nodes/rectangle.tsx +27 -0
  117. package/src/sg-nodes/row-list.props.ts +288 -0
  118. package/src/sg-nodes/row-list.tsx +47 -0
  119. package/src/sg-nodes/scene.props.ts +65 -0
  120. package/src/sg-nodes/scene.tsx +15 -0
  121. package/src/sg-nodes/scrollable-text.props.ts +133 -0
  122. package/src/sg-nodes/scrollable-text.tsx +64 -0
  123. package/src/sg-nodes/scrolling-label.props.ts +106 -0
  124. package/src/sg-nodes/scrolling-label.tsx +42 -0
  125. package/src/sg-nodes/simple-label.props.ts +156 -0
  126. package/src/sg-nodes/simple-label.tsx +59 -0
  127. package/src/sg-nodes/sound-effect.props.ts +130 -0
  128. package/src/sg-nodes/sound-effect.tsx +65 -0
  129. package/src/sg-nodes/standard-dialog.props.ts +520 -0
  130. package/src/sg-nodes/standard-dialog.tsx +428 -0
  131. package/src/sg-nodes/standard-keyboard-dialog.props.ts +84 -0
  132. package/src/sg-nodes/standard-keyboard-dialog.tsx +58 -0
  133. package/src/sg-nodes/standard-message-dialog.props.ts +56 -0
  134. package/src/sg-nodes/standard-message-dialog.tsx +47 -0
  135. package/src/sg-nodes/standard-pinpad-dialog.props.ts +64 -0
  136. package/src/sg-nodes/standard-pinpad-dialog.tsx +44 -0
  137. package/src/sg-nodes/standard-progress-dialog.props.ts +30 -0
  138. package/src/sg-nodes/standard-progress-dialog.tsx +40 -0
  139. package/src/sg-nodes/target-group.props.ts +278 -0
  140. package/src/sg-nodes/target-group.tsx +63 -0
  141. package/src/sg-nodes/target-list.props.ts +128 -0
  142. package/src/sg-nodes/target-list.tsx +66 -0
  143. package/src/sg-nodes/target-set.props.ts +63 -0
  144. package/src/sg-nodes/target-set.tsx +42 -0
  145. package/src/sg-nodes/time-grid.props.ts +646 -0
  146. package/src/sg-nodes/time-grid.tsx +87 -0
  147. package/src/sg-nodes/tween.props.ts +115 -0
  148. package/src/sg-nodes/types.ts +150 -0
  149. package/src/sg-nodes/video.props.ts +642 -0
  150. package/src/sg-nodes/video.tsx +75 -0
  151. package/src/sg-nodes/voice-text-edit-box.props.ts +91 -0
  152. package/src/sg-nodes/voice-text-edit-box.tsx +15 -0
  153. package/src/sg-nodes/zoom-row-list.props.ts +416 -0
  154. package/src/sg-nodes/zoom-row-list.tsx +42 -0
  155. package/src/standard-dialog-next/__tests__/standard-dialog-next.test.tsx +186 -0
  156. package/src/standard-dialog-next/index.ts +5 -0
  157. package/src/standard-dialog-next/standard-dialog-next.props.ts +80 -0
  158. package/src/standard-dialog-next/standard-dialog-next.tsx +326 -0
  159. package/src/test-helpers/tween-test-setup.ts +78 -0
  160. package/src/tween.tsx +173 -0
  161. package/src/types.ts +8 -0
  162. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.brs +13 -0
  163. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml +10 -0
  164. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml.d.ts +42 -0
  165. package/src/xml-components/bounding-tracker/BoundingTracker.brs +26 -0
  166. package/src/xml-components/bounding-tracker/BoundingTracker.xml +14 -0
  167. package/src/xml-components/bounding-tracker/BoundingTracker.xml.d.ts +42 -0
  168. package/src/xml-components/channel-store/channelStoreExt.brs +159 -0
  169. package/src/xml-components/channel-store/channelStoreExt.xml +29 -0
  170. package/src/xml-components/channel-store/channelStoreExt.xml.d.ts +57 -0
  171. package/src/xml-components/focus/focusable-group.brs +251 -0
  172. package/src/xml-components/focus/focusable-group.xml +15 -0
  173. package/src/xml-components/focus/focusable-group.xml.d.ts +42 -0
  174. package/src/xml-components/focus/focusable.xml +18 -0
  175. package/src/xml-components/focus/focusable.xml.d.ts +48 -0
  176. package/src/xml-components/keyboards/CustomAddressKDF.keyboard.json +547 -0
  177. package/src/xml-components/keyboards/CustomKeyboard.brs +322 -0
  178. package/src/xml-components/keyboards/CustomKeyboard.xml +45 -0
  179. package/src/xml-components/keyboards/CustomKeyboard.xml.d.ts +73 -0
  180. package/src/xml-components/keyboards/KeyGridExt.xml +4 -0
  181. package/src/xml-components/keyboards/KeyGridExt.xml.d.ts +37 -0
  182. package/src/xml-components/keyboards/KeyboardExt.xml +4 -0
  183. package/src/xml-components/keyboards/KeyboardExt.xml.d.ts +37 -0
  184. package/src/xml-components/keyboards/MOPPinPadCustomKDF.keyboard.json +151 -0
  185. package/src/xml-components/keyboards/MiniKeyboardExt.xml +4 -0
  186. package/src/xml-components/keyboards/MiniKeyboardExt.xml.d.ts +37 -0
  187. package/src/xml-components/keyboards/PhonePadCustomKDF.keyboard.json +42 -0
  188. package/src/xml-components/keyboards/PinPadExt.brs +102 -0
  189. package/src/xml-components/keyboards/PinPadExt.xml +17 -0
  190. package/src/xml-components/keyboards/PinPadExt.xml.d.ts +49 -0
  191. package/src/xml-components/keyboards/WifiCustomKDF.keyboard.json +1242 -0
  192. package/src/xml-components/keyhandler/KeyHandler.brs +30 -0
  193. package/src/xml-components/keyhandler/KeyHandler.xml +16 -0
  194. package/src/xml-components/keyhandler/KeyHandler.xml.d.ts +45 -0
  195. package/src/xml-components/managed-list-panel/ManagedListPanel.brs +137 -0
  196. package/src/xml-components/managed-list-panel/ManagedListPanel.xml +10 -0
  197. package/src/xml-components/managed-list-panel/ManagedListPanel.xml.d.ts +37 -0
  198. package/src/xml-components/modal/Modal.brs +105 -0
  199. package/src/xml-components/modal/Modal.xml +22 -0
  200. package/src/xml-components/modal/Modal.xml.d.ts +54 -0
  201. package/src/xml-components/modal/StandardDialogExt.xml +10 -0
  202. package/src/xml-components/modal/StandardDialogExt.xml.d.ts +37 -0
  203. package/src/xml-components/modal/StandardKeyboardDialogExt.xml +10 -0
  204. package/src/xml-components/modal/StandardKeyboardDialogExt.xml.d.ts +37 -0
  205. package/src/xml-components/modal/StandardMessageDialogExt.xml +10 -0
  206. package/src/xml-components/modal/StandardMessageDialogExt.xml.d.ts +37 -0
  207. package/src/xml-components/modal/StandardPinPadDialogExt.xml +10 -0
  208. package/src/xml-components/modal/StandardPinPadDialogExt.xml.d.ts +37 -0
  209. package/src/xml-components/modal/StandardProgressDialogExt.xml +10 -0
  210. package/src/xml-components/modal/StandardProgressDialogExt.xml.d.ts +37 -0
  211. package/src/xml-components/modal/tests/Modal.test.brs +195 -0
  212. package/tsconfig.json +10 -0
@@ -0,0 +1,153 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IContentNodeProps, IVideoContentNodeProps } from "./content-node.props";
5
+ import { Component } from "./types";
6
+ export {
7
+ ContentType,
8
+ Rating,
9
+ StreamFormat,
10
+ LiveBoundsPauseBehavior,
11
+ StreamQuality,
12
+ PreferredAudioCodec,
13
+ PlayReadyEncodingType,
14
+ DrmKeySystem,
15
+ DrmTemplateType,
16
+ ContentClassifier
17
+ } from "./content-node.props";
18
+
19
+ export type {
20
+ IDescriptiveAttributes,
21
+ IStreamEntry,
22
+ ISubtitleTrack,
23
+ SubtitleTrackSource,
24
+ SubtitleTrackName,
25
+ IPlaybackConfigurationAttributes,
26
+ IDrmParams,
27
+ IDrmAttributes,
28
+ IContentClassificationAttributes,
29
+ ICdnConfigEntry,
30
+ ICdnSwitchingAttributes,
31
+ ICertificateAttributes,
32
+ IPlaybackControlAttributes,
33
+ ITrackIdAttributes,
34
+ IListScreenAttributes,
35
+ IZoomRowListDataAttributes,
36
+ IContentNodeProps,
37
+ IVideoContentNodeProps,
38
+ IPlaybackConfiguration
39
+ } from "./content-node.props";
40
+
41
+ // ─────────────────────────────────────────────────────────────────────────────
42
+ // Descriptive Attributes
43
+ // ─────────────────────────────────────────────────────────────────────────────
44
+
45
+ // ─────────────────────────────────────────────────────────────────────────────
46
+ // Playback Configuration Attributes
47
+ // ─────────────────────────────────────────────────────────────────────────────
48
+
49
+ // ─────────────────────────────────────────────────────────────────────────────
50
+ // Digital Rights Management (DRM) Attributes
51
+ // ─────────────────────────────────────────────────────────────────────────────
52
+
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+ // Content Classification Attributes
55
+ // ─────────────────────────────────────────────────────────────────────────────
56
+
57
+ // ─────────────────────────────────────────────────────────────────────────────
58
+ // CDN Switching Attributes
59
+ // ─────────────────────────────────────────────────────────────────────────────
60
+
61
+ // ─────────────────────────────────────────────────────────────────────────────
62
+ // SceneGraph Certificate Attributes
63
+ // ─────────────────────────────────────────────────────────────────────────────
64
+
65
+ // ─────────────────────────────────────────────────────────────────────────────
66
+ // Playback Control Attributes
67
+ // ─────────────────────────────────────────────────────────────────────────────
68
+
69
+ // ─────────────────────────────────────────────────────────────────────────────
70
+ // Track ID Attributes
71
+ // ─────────────────────────────────────────────────────────────────────────────
72
+
73
+ // ─────────────────────────────────────────────────────────────────────────────
74
+ // roListScreen Attributes
75
+ // ─────────────────────────────────────────────────────────────────────────────
76
+
77
+ // ─────────────────────────────────────────────────────────────────────────────
78
+ // ZoomRowList Data Attributes
79
+ // ─────────────────────────────────────────────────────────────────────────────
80
+
81
+ // ─────────────────────────────────────────────────────────────────────────────
82
+ // ContentNode
83
+ // ─────────────────────────────────────────────────────────────────────────────
84
+
85
+ /**
86
+ * ContentNode component for specifying data used to configure nodes and components.
87
+ *
88
+ * Many SceneGraph nodes (lists, grids, panels) require a ContentNode as the specification
89
+ * of their `content` field. ContentNodes are structured as a parent node with child
90
+ * ContentNode descendants that carry the actual content data.
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * // Simple content node for a LabelList
95
+ * <labelList>
96
+ * <contentNode role="content">
97
+ * <contentNode title="Movies" description="Browse movies" />
98
+ * <contentNode title="TV Shows" description="Browse TV series" />
99
+ * <contentNode title="Settings" description="App settings" />
100
+ * </contentNode>
101
+ * </labelList>
102
+ * ```
103
+ *
104
+ * @example
105
+ * ```tsx
106
+ * // Content node with playback metadata for Video
107
+ * <contentNode
108
+ * title="My Video"
109
+ * url="https://example.com/video.mp4"
110
+ * streamFormat="mp4"
111
+ * />
112
+ * ```
113
+ *
114
+ * @see {@link https://developer.roku.com/dev/docs/contentnode}
115
+ * @see {@link https://developer.roku.com/dev/docs/content-metadata}
116
+ */
117
+ export const ContentNode: Component<IContentNodeProps> = props => {
118
+ return <contentNode {...props}></contentNode>;
119
+ };
120
+
121
+ // ─────────────────────────────────────────────────────────────────────────────
122
+ // VideoContentNode
123
+ // ─────────────────────────────────────────────────────────────────────────────
124
+
125
+ /**
126
+ * VideoContentNode component for specifying video playback content.
127
+ *
128
+ * A convenience wrapper around ContentNode used specifically for configuring video
129
+ * content with stream URL and format. Typically used as a child of a Video node.
130
+ *
131
+ * @remarks Assigning {@link IVideoContentNodeProps.streamformat} is not supported
132
+ * by the RSG 2.0 SDK yet; use `url` (and other forwarded fields) only.
133
+ *
134
+ * @example
135
+ * ```tsx
136
+ * <video>
137
+ * <VideoContentNode
138
+ * title="Sample Video"
139
+ * url="https://example.com/stream.m3u8"
140
+ * />
141
+ * </video>
142
+ * ```
143
+ *
144
+ * @see {@link https://developer.roku.com/dev/docs/contentnode}
145
+ * @see {@link https://developer.roku.com/dev/docs/video}
146
+ */
147
+ export const VideoContentNode: Component<IVideoContentNodeProps> = props => {
148
+ return <contentNode {...props}></contentNode>;
149
+ };
150
+
151
+ // ─────────────────────────────────────────────────────────────────────────────
152
+ // Backward-compatible type alias
153
+ // ─────────────────────────────────────────────────────────────────────────────
@@ -0,0 +1,197 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * `*CoreProps` — the frozen minimal property subset for the primitive elements.
6
+ *
7
+ * Each `I*CoreProps` is the small, stable core of the corresponding `I*Props`: the properties that
8
+ * are guaranteed to be honoured everywhere the element renders, versioned and frozen so that code
9
+ * written against the core keeps working. The full `I*Props` extends its core base and adds the
10
+ * broader property set (`IGroupProps` is a superset of `IGroupCoreProps`, and so on).
11
+ *
12
+ * ## Why this file is purely additive
13
+ *
14
+ * These interfaces are **derived from**, never carved out of, the existing `I*Props`. Nothing in
15
+ * `group.props.ts`, `rectangle.props.ts`, `label.props.ts`, `poster.props.ts` or `effect.props.ts`
16
+ * changes, and the `jsx-props` barrel is untouched. Every core type is defined here as a `Pick<>`
17
+ * over the existing interface, which has three consequences that matter:
18
+ *
19
+ * 1. **No breaking change.** Existing consumers, the generated `jsx.d.ts` intrinsics map, and the
20
+ * `I*Props` surface are all byte-identical to before.
21
+ * 2. **They cannot drift.** A core member is a projection of the real prop, so its type is
22
+ * whatever the source interface says. If a source prop's type changes, the core changes with
23
+ * it; if a source prop is renamed or removed, this file fails to compile rather than silently
24
+ * describing a prop that no longer exists.
25
+ * 3. **Membership is the only thing declared here.** The list of keys in each `Pick<>` *is* the
26
+ * frozen contract — that is the single decision this file makes, and it is reviewable as a
27
+ * list of names.
28
+ *
29
+ * ## How membership was decided
30
+ *
31
+ * Empirically, from what the in-scope reference applications actually use: `cowboy-tv`,
32
+ * `fantasy-tv`, and `apps/the-roku-channel`. Regenerate the evidence with:
33
+ *
34
+ * ```
35
+ * node scripts/scan-core-props-usage.mjs \
36
+ * ../../examples/flex-grid/cowboy-tv/src \
37
+ * ../../examples/flex-grid/fantasy-tv/src \
38
+ * ../../../../apps/the-roku-channel/src
39
+ * ```
40
+ *
41
+ * A prop earns its place by being used by a real application, not by seeming stable in the
42
+ * abstract. Widening a core interface is additive and safe; narrowing one is a breaking change and
43
+ * follows the major-bump rule in `@roku-sdk/interfaces`'s `FREEZE.md`.
44
+ */
45
+
46
+ import type { IEffectProps } from "./effect.props";
47
+ import type { IGroupProps } from "./group.props";
48
+ import type { ILabelProps } from "./label.props";
49
+ import type { INodeProps } from "./node";
50
+ import type { IPosterProps } from "./poster.props";
51
+ import type { IRectangleProps } from "./rectangle.props";
52
+ import type { ITweenProps } from "./tween.props";
53
+
54
+ /**
55
+ * Frozen minimal subset of `IGroupProps`.
56
+ *
57
+ * `group` is the base of every visual node, so these are inherited by the rectangle, label, and
58
+ * poster core surfaces rather than being redeclared on each.
59
+ *
60
+ * `INodeProps` is intersected in explicitly. A `Pick<>` selects only the named keys, so the
61
+ * framework props (`children`, `ref`, `id`, `focusable`, `gainFocus`) that `IGroupProps` inherits
62
+ * would otherwise be dropped — and every parent element in an application passes `children`. These
63
+ * are not SceneGraph properties; they are how the SolidJS layer itself works.
64
+ *
65
+ * Usage: `translation` (22 files), `opacity` / `visible` (5 each), `clippingRect` (2), and the
66
+ * `scale` / `scaleRotateCenter` pair (1 each — TRC's focus animation).
67
+ *
68
+ * `rotation` is included despite no in-scope app using it yet: it is the third member of the
69
+ * transform triple alongside `scale` and `scaleRotateCenter`, which are already here. Freezing two
70
+ * of the three would be arbitrary — they compose into one transform and `scaleRotateCenter` names
71
+ * the centre of the rotation operation, so it is meaningless without `rotation` being settable.
72
+ */
73
+ export type IGroupCoreProps = INodeProps &
74
+ Pick<
75
+ IGroupProps,
76
+ "translation" | "rotation" | "scale" | "scaleRotateCenter" | "clippingRect" | "opacity" | "visible"
77
+ >;
78
+
79
+ /**
80
+ * Frozen minimal subset of `IRectangleProps`.
81
+ *
82
+ * Usage: `color`, `width`, `height` in 22 files each — the three props that made the cowboy-tv
83
+ * landscape card the worked example for this exercise — plus `blendingEnabled` in 5.
84
+ *
85
+ * `blendingEnabled` is worth a note: it is almost always written in the valueless boolean form
86
+ * (`<rectangle blendingEnabled />`), which is exactly the shape a regex-based usage scan misses.
87
+ * It was absent from the first membership list for that reason. The scanner now uses the
88
+ * TypeScript parser.
89
+ *
90
+ * `effect` carries the styled-surface capability (rounded corners, borders, gradients). It is a
91
+ * child element rather than a scalar, and it degrades by design — where effects are unsupported it
92
+ * is silently ignored — so it is safe to write unconditionally and needs no capability branch in
93
+ * application code.
94
+ */
95
+ export type IRectangleCoreProps = IGroupCoreProps &
96
+ Pick<IRectangleProps, "color" | "width" | "height" | "blendingEnabled" | "effect">;
97
+
98
+ /**
99
+ * Frozen minimal subset of `ILabelProps`.
100
+ *
101
+ * Usage: `text`/`font`/`color`/`width`/`height`/`horizAlign`/`vertAlign`/`maxLines` in all three
102
+ * apps; `wrap` in fantasy-tv.
103
+ */
104
+ export type ILabelCoreProps = IGroupCoreProps &
105
+ Pick<
106
+ ILabelProps,
107
+ "text" | "font" | "color" | "width" | "height" | "horizAlign" | "vertAlign" | "maxLines" | "wrap"
108
+ >;
109
+
110
+ /**
111
+ * Frozen minimal subset of `IPosterProps`.
112
+ *
113
+ * `blendColor` multiplies the bitmap RGBA channels and is animatable by ColorFieldInterpolator.
114
+ *
115
+ * The loading/failed placeholder family is included because `fantasy-tv` and
116
+ * `apps/the-roku-channel` genuinely use it.
117
+ *
118
+ * `effect` is included for the same reason as on rectangle: it is the styled-surface capability,
119
+ * expressed as a child element, and it degrades silently where unsupported.
120
+ *
121
+ * Deliberately excluded: `loadSync`, `loadWidth`, `loadHeight` (load-time bitmap scaling),
122
+ * `onBitmapMargins` (9-patch, out of scope for v1), `audioGuideText`, and `shaderCompatible`.
123
+ */
124
+ export type IPosterCoreProps = IGroupCoreProps &
125
+ Pick<
126
+ IPosterProps,
127
+ | "uri"
128
+ | "width"
129
+ | "height"
130
+ | "blendColor"
131
+ | "loadDisplayMode"
132
+ | "loadingBitmapUri"
133
+ | "loadingBitmapOpacity"
134
+ | "failedBitmapUri"
135
+ | "failedBitmapOpacity"
136
+ | "effect"
137
+ >;
138
+
139
+ /**
140
+ * Frozen minimal subset of `IEffectProps` — which is the whole interface.
141
+ *
142
+ * No in-scope application uses `effect` yet, so unlike the other four this membership is not
143
+ * usage-derived; it is the whole interface by design decision.
144
+ */
145
+ export type IEffectCoreProps = IEffectProps;
146
+
147
+ /**
148
+ * Frozen minimal subset of `ITweenProps` — the portable tween field contract.
149
+ *
150
+ * Unlike the four visual primitives, this membership is not usage-derived. The tween node is still
151
+ * being brought up, and it does not appear in any in-scope application's source yet — the FlexGrid
152
+ * apps reach animation through `@roku-sdk/components`' `<Tween>` wrapper, which renders this element. The
153
+ * membership is instead the node's specified field contract: every field here is portable by
154
+ * construction, honoured by every renderer that implements the element. The freeze applies as soon
155
+ * as that contract is final; widening is an additive minor bump, narrowing is a major break.
156
+ *
157
+ * `INodeProps` is intersected in for the same reason as `IGroupCoreProps`: a `Pick<>` would drop
158
+ * the framework props (`children`, `ref`, `id`) that the SolidJS layer needs.
159
+ */
160
+ export type ITweenCoreProps = INodeProps &
161
+ Pick<
162
+ ITweenProps,
163
+ | "targetNode"
164
+ | "field"
165
+ | "targetValue"
166
+ | "targetVector"
167
+ | "duration"
168
+ | "easeFunction"
169
+ | "axis"
170
+ | "preserveOther"
171
+ | "proposedStops"
172
+ | "control"
173
+ | "onAnimationStart"
174
+ | "onAnimationStop"
175
+ | "onAnimationState"
176
+ >;
177
+
178
+ /**
179
+ * Every primitive's core surface, keyed by JSX tag. Exported so a consumer can refer to the set as
180
+ * a whole rather than restating it.
181
+ *
182
+ * This table also *is* the shipped intrinsic map: `core-jsx.d.ts` at the package root declares
183
+ * `interface IntrinsicElements extends CorePropsByTag {}` against
184
+ * `@roku-sdk/components/core/jsx-runtime`. So the keys here are the tags an application with
185
+ * `jsxImportSource: "@roku-sdk/components/core"` is allowed to write — adding a key exposes a new
186
+ * tag, and a primitive absent from the table is a `Property '<tag>' does not exist on type
187
+ * 'JSX.IntrinsicElements'` error rather than a silent pass. The membership is pinned both ways in
188
+ * `__tests__/core-props.test.ts`.
189
+ */
190
+ export interface CorePropsByTag {
191
+ group: IGroupCoreProps;
192
+ rectangle: IRectangleCoreProps;
193
+ label: ILabelCoreProps;
194
+ poster: IPosterCoreProps;
195
+ effect: IEffectCoreProps;
196
+ tween: ITweenCoreProps;
197
+ }
@@ -0,0 +1,108 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IKeyHandlerData } from "../key-handler.props";
5
+ import type { IGroupProps } from "./group.props";
6
+ import type { Component, EventHandler } from "./types";
7
+ import type { IVoiceTextEditBoxProps } from "./voice-text-edit-box.props";
8
+
9
+ /**
10
+ * Speech-recognition domain for voice-enabled keyboards.
11
+ *
12
+ * Controls the speech-recognition mode on DynamicKeyboardBase and its subclasses.
13
+ *
14
+ * @see {@link https://developer.roku.com/dev/docs/dynamic-keyboard-base}
15
+ */
16
+ export enum KeyboardDomain {
17
+ /** Letter-by-letter dictation for email addresses. */
18
+ EMAIL = "email",
19
+ /** Letter-by-letter dictation for PINs, zip codes, and numeric input. */
20
+ NUMERIC = "numeric",
21
+ /** Letter-by-letter dictation for street addresses and mixed alphanumeric input. */
22
+ ALPHANUMERIC = "alphanumeric",
23
+ /** Full-word input for search queries and general text. */
24
+ GENERIC = "generic",
25
+ /** Letter-by-letter dictation for passwords. */
26
+ PASSWORD = "password",
27
+ /** Address-form keyboard layout (custom KDF). */
28
+ ADDRESS = "address"
29
+ }
30
+
31
+ interface PaletteColors {
32
+ keyboardColor?: string;
33
+ primaryTextColor?: string;
34
+ secondaryItemColor?: string;
35
+ focusColor?: string;
36
+ focusItemColor?: string;
37
+ }
38
+
39
+ /**
40
+ * Color palette for keyboard key grid rendering.
41
+ */
42
+ export interface IKeyboardRSGPaletteNodeProps {
43
+ colors: PaletteColors;
44
+ }
45
+
46
+ /**
47
+ * Properties for the internal DynamicKeyGrid sub-node on {@link CustomKeyboard}.
48
+ *
49
+ * Not for standalone {@link DynamicKeyGrid} — use {@link IDynamicKeyGridProps} instead.
50
+ *
51
+ * @see {@link https://developer.roku.com/dev/docs/dynamic-key-grid}
52
+ */
53
+ export interface IDynamicKeyGridNodeProps extends IGroupProps {
54
+ /**
55
+ * URI of the Key Definition File that defines the keyboard layout.
56
+ * Required for DynamicCustomKeyboard; set to a packaged `.keyboard.json` file.
57
+ */
58
+ keyDefinitionUri?: string;
59
+ /** Jump focus to a specific key grid position `[row, col]`. */
60
+ jumpToKey?: number[];
61
+ /** Active keyboard mode (must match a mode defined in the KDF). */
62
+ mode?: string;
63
+ /** Key label to disable in the current layout. */
64
+ disableKey?: string;
65
+ /** Key label to re-enable in the current layout. */
66
+ enableKey?: string;
67
+ /** When true, focus highlight is visible on the key grid. */
68
+ focusVisible?: boolean;
69
+ /** When true, horizontal navigation wraps at row edges. */
70
+ horizWrap?: boolean;
71
+ /** When true, vertical navigation wraps at column edges. */
72
+ vertWrap?: boolean;
73
+ /** Color palette node for keyboard theming. */
74
+ palette?: Component<IKeyboardRSGPaletteNodeProps>;
75
+ /** Fired when keyboard focus moves to a different key. */
76
+ onKeyFocused?: EventHandler<string>;
77
+ /** Fired when a key is selected, with key name and press state. */
78
+ onKeySelectedValue?: EventHandler<IKeyHandlerData>;
79
+ }
80
+
81
+ /**
82
+ * Properties inherited from DynamicKeyboardBase (key grid + text box behavior).
83
+ *
84
+ * @see {@link https://developer.roku.com/dev/docs/dynamic-keyboard-base}
85
+ */
86
+ export interface IDynamicKeyboardBaseProps extends IDynamicKeyGridNodeProps {
87
+ /** When true, hides the internal VoiceTextEditBox. */
88
+ hideTextBox?: boolean;
89
+ /** Disables voice underscore control in the text edit box. */
90
+ disableVoiceUnderscoreControl?: boolean;
91
+ /** Shows underscore placeholders in the text edit box. */
92
+ showUnderscores?: boolean;
93
+ /** Centers underscore placeholders within the text edit box. */
94
+ centerUnderscores?: boolean;
95
+ /**
96
+ * Speech-recognition domain for voice entry.
97
+ * @see KeyboardDomain
98
+ */
99
+ domain?: KeyboardDomain;
100
+ /** Initial text pre-populated in the edit box on mount. */
101
+ initText?: string;
102
+ }
103
+
104
+ /**
105
+ * Full props for {@link CustomKeyboard}: DynamicKeyboardBase key-grid fields plus
106
+ * VoiceTextEditBox text-entry fields.
107
+ */
108
+ export type IKeyboardPropsWithVoice = IDynamicKeyboardBaseProps & Partial<IVoiceTextEditBoxProps>;
@@ -0,0 +1,48 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import { retainAsset } from "../retain-asset";
5
+ import CustomKeyboardXML from "../xml-components/keyboards/CustomKeyboard.xml";
6
+ import type { IKeyboardPropsWithVoice } from "./custom-keyboard.props";
7
+ import { Component } from "./types";
8
+ export { KeyboardDomain } from "./custom-keyboard.props";
9
+ export type {
10
+ IKeyboardRSGPaletteNodeProps,
11
+ IDynamicKeyGridNodeProps,
12
+ IDynamicKeyboardBaseProps,
13
+ IKeyboardPropsWithVoice
14
+ } from "./custom-keyboard.props";
15
+
16
+ /**
17
+ * Voice-enabled keyboard with a custom layout defined by a Key Definition File.
18
+ *
19
+ * CustomKeyboard extends the native DynamicCustomKeyboard node, combining a
20
+ * DynamicKeyGrid (custom KDF layout) with a VoiceTextEditBox for text and voice entry.
21
+ * Use this for standalone on-screen keyboards outside of StandardDialog.
22
+ *
23
+ * @see {@link https://developer.roku.com/dev/docs/dynamic-custom-keyboard}
24
+ * @see {@link https://developer.roku.com/dev/docs/dynamic-keyboard-base}
25
+ * @see {@link https://developer.roku.com/dev/docs/key-definition-file}
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * import { CustomAddressKDF } from "@roku-sdk/components/kdfs";
30
+ *
31
+ * <CustomKeyboard
32
+ * keyDefinitionUri={CustomAddressKDF}
33
+ * domain={KeyboardDomain.GENERIC}
34
+ * text={query()}
35
+ * hintText="Type here…"
36
+ * maxTextLength={64}
37
+ * showUnderscores={false}
38
+ * onText={setQuery}
39
+ * gainFocus
40
+ * id="keyboard"
41
+ * translation={[80, 140]}
42
+ * />
43
+ * ```
44
+ */
45
+ export const CustomKeyboard: Component<IKeyboardPropsWithVoice> = props => {
46
+ retainAsset(CustomKeyboardXML);
47
+ return <customKeyboard {...props}></customKeyboard>;
48
+ };
@@ -0,0 +1,159 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IGroupProps } from "./group.props";
5
+ import type { EventHandler, Uri } from "./types";
6
+
7
+ /**
8
+ * Properties for the Dialog node.
9
+ *
10
+ * The Dialog node defines a modal pop-up dialog used to present the user with information
11
+ * requiring their immediate attention. The dialog is configured to have up to five regions:
12
+ * title, message, bullet text, graphic, and buttons. All regions are optional except the title.
13
+ *
14
+ * Dialogs are modal and intercept all key events except the Home key. They are closed
15
+ * automatically when the user presses the Home or Back key. If `optionsDialog` is true,
16
+ * pressing the Options key also closes the dialog.
17
+ *
18
+ * > **Note:** Roku OS 10.0 introduced the StandardDialog node with updated graphics and
19
+ * > color palette support. Consider using StandardDialog for new development.
20
+ *
21
+ * @see {@link https://developer.roku.com/dev/docs/dialog}
22
+ */
23
+ export interface IDialogProps extends IGroupProps {
24
+ /**
25
+ * Title of the dialog box, displayed at the top alongside the icon.
26
+ * @default ""
27
+ */
28
+ title?: string;
29
+ /**
30
+ * Color of the title text.
31
+ */
32
+ titleColor?: string;
33
+ /**
34
+ * Font of the title text.
35
+ */
36
+ titleFont?: string;
37
+ /**
38
+ * The string to be displayed in the message region of the dialog. Newline and carriage
39
+ * return characters in the string result in the message being displayed as multiple lines.
40
+ * @default ""
41
+ */
42
+ message?: string;
43
+ /**
44
+ * Color of the message text.
45
+ */
46
+ messageColor?: string;
47
+ /**
48
+ * Font of the message text.
49
+ */
50
+ messageFont?: string;
51
+ /**
52
+ * When true, the bullet text will be displayed with numbers rather than bullets.
53
+ * @default false
54
+ */
55
+ numberedBullets?: boolean;
56
+ /**
57
+ * An array of strings to be displayed as a list of bullet points.
58
+ * @default []
59
+ */
60
+ bulletText?: string[];
61
+ /**
62
+ * Color of the bullet point text.
63
+ *
64
+ * Corresponds to the `bulletTextColor` field in the Roku reference.
65
+ */
66
+ bulletColor?: string;
67
+ /**
68
+ * Font of the bullet point text.
69
+ *
70
+ * Corresponds to the `bulletTextFont` field in the Roku reference.
71
+ */
72
+ bulletFont?: string;
73
+ /**
74
+ * Allows a set of buttons to be created by providing an array of button labels.
75
+ * Each string results in a Button node added to the dialog's internal ButtonGroup.
76
+ * @default []
77
+ */
78
+ buttons?: string[];
79
+ /**
80
+ * Specifies a bitmap to be displayed in the dialog. The bitmap is displayed below
81
+ * the bullet text region and above the buttons.
82
+ * @default ""
83
+ */
84
+ graphicUri?: Uri;
85
+ /**
86
+ * Width of the bitmap graphic. If 0, the width from the image file is used.
87
+ * Must be set together with `graphicHeight` and before `graphicUri`.
88
+ * @default 0
89
+ */
90
+ graphicWidth?: number;
91
+ /**
92
+ * Height of the bitmap graphic. If 0, the height from the image file is used.
93
+ * Must be set together with `graphicWidth` and before `graphicUri`.
94
+ * @default 0
95
+ */
96
+ graphicHeight?: number;
97
+ /**
98
+ * Fired when a button in the dialog is selected. The handler receives the
99
+ * zero-based index of the selected button.
100
+ */
101
+ onButtonSelected?: EventHandler<number>;
102
+ /**
103
+ * Fired when a button in the dialog receives the key focus. The handler receives
104
+ * the zero-based index of the focused button.
105
+ */
106
+ onButtonFocused?: EventHandler<number>;
107
+ /**
108
+ * Causes the button with the specified index to receive focus when the dialog's
109
+ * ButtonGroup has the key focus.
110
+ * @default 0
111
+ */
112
+ focusButton?: number;
113
+ /**
114
+ * When true, the dialog is automatically dismissed when the Options key is pressed.
115
+ * @default false
116
+ */
117
+ optionsDialog?: boolean;
118
+ /**
119
+ * Specifies the bitmap to be displayed as the dialog background. Usually a 9-patch
120
+ * image to support dynamic resizing.
121
+ * @default ""
122
+ */
123
+ backgroundUri?: Uri;
124
+ /**
125
+ * Specifies a bitmap to be displayed as a small icon next to the dialog title.
126
+ * @default ""
127
+ */
128
+ iconUri?: Uri;
129
+ /**
130
+ * Specifies a bitmap to be displayed as the divider between the title region and
131
+ * the remainder of the dialog. Usually a 9-patch image.
132
+ * @default ""
133
+ */
134
+ dividerUri?: Uri;
135
+ /**
136
+ * Causes the dialog to be dismissed. The dialog is dismissed whenever this field
137
+ * is set, regardless of whether it is set to true or false.
138
+ * @default false
139
+ */
140
+ close: boolean;
141
+ /**
142
+ * Fired when the dialog has been closed, whether by setting `close`, pressing
143
+ * Back/Home, pressing Options (if `optionsDialog` is true), or because another
144
+ * dialog was displayed.
145
+ */
146
+ onWasClosed?: EventHandler<void>;
147
+ /**
148
+ * Width of the dialog. By default, this value is pulled from the system theme.
149
+ * @default -1.0
150
+ */
151
+ width?: number;
152
+ /**
153
+ * Maximum height of the dialog. By default, the dialog scales height based on contents
154
+ * but never larger than display resolution. Setting smaller than contents enables a
155
+ * scrollable text region.
156
+ * @default -1.0
157
+ */
158
+ maxHeight?: number;
159
+ }