This release brings 160+ improvements and fixes, it completes the SceneGraph node set — every concrete node documented by Roku is now implemented, including support for Component Libraries. This version also rewrites the CLI on a worker-thread architecture where the app and each SceneGraph Task run on their own thread, adding keyboard control, terminal image rendering (--image), log redirection and screen snapshots. The core engine gains a production mode that removes all debug instrumentation by default, a texture memory registry exposed through the query/r2d2-bitmaps ECP endpoint, full .bpk encryption covering SceneGraph components and the package container, and several Roku OS 15.2/15.3 APIs. Read the full release notes below for more details.
brs-engine-2.3.0-scenegraph-demo.mov
Release Changes
Core Engine
- (brs) Updated
setHttpVersion()signature to accept a version string instead of a boolean by @lvcabral in #920 - (draw2d) Fixed the 9-patch corner insets to be parsed from the marker start and end by @lvcabral in #933
- (draw2d) Fixed
blendColorto apply the RGB tint at full strength regardless of alpha by @lvcabral in #935 - (draw2d) Clamp the 9-patch render size to the fixed corners by @lvcabral in #950
- (brs) Added mocks for
roAudioGuide,roTextToSpeech,roMicrophone,roDSAandroRSAby @lvcabral in #956 - (brs) Made
bs_constlookups case-insensitive in#if/#elseifconditions by @markwpearce in #962 - (brs) Added a global texture memory registry, exposed via the
query/r2d2-bitmapsECP endpoint and the APIrequestBitmaps()method, and introduced the production mode that gates all debug instrumentation behinddebugOnCrashby @lvcabral in #967 - (brs) Propagate the optional state through dot-bracket chaining (
?.[) by @markwpearce in #969 - (brs) Properly escape strings in
FormatJSON()by @lvcabral in #973 - (brs) Matched Roku's
type()results forStringvsroStringby @lvcabral in #977 - (brs) Fixed an edge case for numeric values in
Type()by @lvcabral in #986 - (brs) Added the Roku OS 15.3
roUtilspredicatesisNumber(),isString()andisFloatingPoint()by @lvcabral in #988 - (brs) Added the Roku OS 15.3
roAssociativeArray.values()method by @lvcabral in #989 - (brs) Added the Roku OS 15.3
roTimeSpanmethodstotalMillisecondsLong()andtotalMicrosecondsLong()by @lvcabral in #990 - (brs) Fixed anonymous functions not being resolvable by the
$anon_Nname reported bytoStr(), so observers registered by function name (e.g. by thepromiseslibrary used in Rooibos node suites) work as on a real device by @lvcabral in #993 - (brs) Added the Roku OS 15.2
roUtils.hasComponent()method by @lvcabral in #994 - (brs) Added the
roDeviceInfomethodsisHDMIConnected()andisPassthruCodecActive()by @lvcabral in #995 - (brs) Keep
roAssociativeArrayenumeration stable when replacing an existing key's value by @lvcabral in #997 - (brs) Resolve node methods in call position when a field shadows the method name by @lvcabral in #998
- (brs) Allow any numeric type on the index type check when setting array values by @lvcabral in #1012
- (brs)
roTextureManageronly warns once for a repeatedly-failing local texture by @markwpearce in #1016 - (draw2d) Accept single-axis 9-patch images (stretch marker on only one axis) by @lvcabral in #1024
- (brs) Properly handle boxed
StringinroArray,roAudioPlayerandroVideoPlayerby @lvcabral in #1050 - (brs) Fixed the Micro Debugger to halt all threads while a debug session is active, so threads blocked in a cross-thread rendezvous no longer time out with a spurious "Rendezvous timeout" by @lvcabral in #1054
- (brs) Handle boxed numbers in
forloop bounds andif/whileconditions by @lvcabral in #1058 - (brs) Fixed
roAssociativeArraybehavior when handling keys vs interface methods by @lvcabral in #1071 - (brs) Validate string values in
roRegistrySection.writeMulti()by @lvcabral in #1072 - (brs) Percent-encode reserved URI characters in
roUrlTransferescape()andurlEncode()by @markwpearce in #1073 - (brs) Handle an empty POST body in
roUrlTransfer.postFromStringAsync()by @lvcabral in #1074 - (brs) Follow HTTP redirects in the synchronous download path by @lvcabral in #1082
Browser API
- (api) Enable the SceneGraph extension when running a single
.brsfile that usesroSGNodeorroSGScreenby @lvcabral in #980 - (api) Enforce a minimum video buffering floor before playback starts by @lvcabral in #1033
- (api) Reliable media event and buffering-progress delivery for preview playback by @lvcabral in #1041
- (api) Prevent the space key from triggering page down in browsers by @lvcabral in #1084
- (api) Prevent the browser default action on held keys by @lvcabral in #1085
Node.js Library and CLI
- (cli) Run a folder app from
--rootalone, loadingsource/and serving thecomponents/tree from the mountedpkg:/volume by @lvcabral in #960 - (cli) Encrypt SceneGraph component files (
.brsand.xml, including inline<script>blocks) into.bpkpackages, prune the emptiedcomponents/tree, and wrap the whole package container in AES-256-CTR so plaintext assets are unreadable at rest by @lvcabral in #964 - (cli) Moved to node-canvas v4 with a worker-thread architecture (the app and every SceneGraph
Taskrun on their own worker) and added terminal frame rendering with the--imagemode (iTerm2/Kitty protocols with ANSI fallback), plus--logand--snapshotby @lvcabral in #1083
SceneGraph Extension (brs-scenegraph release v0.3.0)
- (rsg) Added
width/heightfields to theVideonode and suppress its UI when windowed by @lvcabral in #928 - (rsg) Index
RowListper-row arrays by the absolute row index by @lvcabral in #930 - (rsg) Implemented
MaskGroupalpha-mask compositing by @lvcabral in #931 - (rsg) Apply the
uri_resolution_autosubmanifest config more robustly by @lvcabral in #932 - (rsg) Update the global focus pointer before clearing the old focus chain by @lvcabral in #934
- (rsg) Implemented support for Component Libraries by @lvcabral in #936
- (rsg) Added the
allowBackgroundTaskfield toScenewith a warning for unsupported Instant Resume by @lvcabral in #937 - (rsg) Load Component Libraries created at runtime by @lvcabral in #938
- (rsg) Hardened the cross-thread rendezvous (fidelity and reliability) by @lvcabral in #939
- (rsg) Rendezvous Phase 2 —
roRenderThreadQueueand device-accurate timeouts by @lvcabral in #940 - (rsg) Rendezvous Phase 3a — direct render→task responses (feature-flagged) by @lvcabral in #941
- (rsg) Rendezvous Phase 3 — finalized the direct render→task responses by @lvcabral in #942
- (rsg) Restored synchronous (depth-first) observer dispatch with a per-field re-entrancy guard, fixing blank button labels by @lvcabral in #943
- (rsg) Implemented the Standard Dialog Framework nodes by @lvcabral in #944
- (rsg) Implemented the
SimpleLabelnode by @lvcabral in #945 - (rsg) Implemented the
MultiStyleLabelandStdDlgMultiStyleTextItemnodes by @lvcabral in #947 - (rsg) Implemented the
MonospaceLabelnode by @lvcabral in #948 - (rsg) Implemented the
TimeGridnode (EPG) by @lvcabral in #949 - (rsg) Implemented the
TargetSet,TargetGroupandTargetListnodes by @lvcabral in #951 - (rsg) Implemented the Dynamic voice keyboard nodes by @lvcabral in #952
- (rsg) Use the Dynamic keyboards in the Standard dialog framework by @lvcabral in #953
- (rsg) Implemented the
roRenderThreadQueuecomponent (OS 15) by @lvcabral in #954 - (rsg) Prevent a stack overflow from re-entrant render and the
ContentNodeparentFieldcascade by @lvcabral in #959 - (rsg) Trim
script.valingetScripts()to ignore whitespace-only bodies by @markwpearce in #961 - (rsg) Added support for
associativearrayinFieldKindand the value type checks by @lvcabral in #963 - (rsg) Give focus to a custom
StandardDialog's buttons by @lvcabral in #968 - (rsg) Corrected
UserSelectfocus traversal, button centering and grid focus feedback by @lvcabral in #970 - (rsg) Attach a list item component to its parent before
init()by @lvcabral in #971 - (rsg) Implemented the
VideonodebufferingBarandretrievingBarfields by @lvcabral in #972 - (rsg) Hug the
LabelListfocus frame instead of overflowing rows by @lvcabral in #976 - (rsg) Only prevent duplicate fields when those are defined in XML by @lvcabral in #978
- (rsg) Box AA/Array node-field content the way Roku does (literal-aware) by @lvcabral in #979
- (rsg) Fixed issues rendering Task-loaded content in grids by @lvcabral in #982
- (rsg) Cut per-node memory for large content trees with lazy fields and lazy method Callables by @lvcabral in #983
- (rsg) SceneGraph list, animation and format fixes for the hero-grid and living-room samples by @lvcabral in #984
- (rsg)
RowListrendering fixes — item sizing, counter, spacing, labels, wrap and clipping by @lvcabral in #985 - (rsg) Added the Roku OS 15.3
Video.captionRenderAreafield with render-thread caption positioning; captions now only render during full-screen playback, as on a device by @lvcabral in #991 - (rsg) Added regression coverage for
doubleSceneGraph fields (OS 15.3) by @lvcabral in #992 - (rsg) Resolve cross-thread nodes by address via a registry by @lvcabral in #996
- (rsg) Added the
ifSGNodeBoundingRectsub-part methods by @lvcabral in #999 - (rsg) Guard the bounding-rect refresh renders against re-entrancy by @lvcabral in #1000
- (rsg) Mirror a remote field add/remove on the local node copy by @lvcabral in #1001
- (rsg) Added the
scrollingStatusfield to theArrayGridbased nodes by @lvcabral in #1002 - (rsg) Compute bounding rects for nodes under invisible ancestors by @lvcabral in #1003
- (rsg) A failed interface field alias no longer aborts the component's remaining fields by @lvcabral in #1004
- (rsg) Skip grid slots whose item component failed to create by @lvcabral in #1005
- (rsg)
findNode()searches breadth-first per theifSGNodeDictspec by @lvcabral in #1006 - (rsg) The media-node serialization proxy no longer hijacks the singleton player by @lvcabral in #1007
- (rsg)
Videonode prebuffer, configurable autoplay, buffering-step delivery and active-player routing by @lvcabral in #1008 - (rsg)
Postercommits theurifield before its synchronous load and notification by @lvcabral in #1010 - (rsg)
RowListkeepscurrFocusRow/currFocusColumnin sync with the focused item by @lvcabral in #1011 - (rsg) Implemented the
ifSGNodeBoundingRectmethodancestorSubBoundingRect()by @lvcabral in #1013 - (rsg)
DynamicKeyboardpalette support and shared palette resolution by @lvcabral in #1014 - (rsg) Corrected button and list layout — 9-patch width, variable-width rows, icon measure and item focus by @lvcabral in #1015
- (rsg) Reliable
PanelSetnext-panel creation and focusable-based navigation by @lvcabral in #1017 - (rsg) Re-apply the grid item size each frame and fixed WebP images with dropped bottom rows by @lvcabral in #1019
- (rsg)
update()converts a nested{subtype:...}AA to a node on any field, not justchildren[]by @markwpearce in #1020 - (rsg) Include the inter-item spacing in the grid
boundingRect()extent by @lvcabral in #1021 - (rsg) Honor
clippingRectto limit node and children rendering by @lvcabral in #1022 - (rsg) Re-measure a degenerate bounding rect (either dimension zero) mid-render by @lvcabral in #1023
- (rsg) Silence the local mirror of a rendezvous field add so observers fire once by @lvcabral in #1025
- (rsg) Don't apply
LabelvertAlignwhen wrapped text has no explicit height by @lvcabral in #1026 - (rsg) Apply an interface field's default value through its alias targets by @lvcabral in #1027
- (rsg) Emit
vertFocusDirectionwith settle-last focus order and honorVideo.asyncStopSemanticsby @lvcabral in #1028 - (rsg) Resolve the
RowList/ZoomRowListfocused-itemsubBoundingRect()and corrected the vertical outset by @lvcabral in #1029 - (rsg) Span the focus-feedback footprint in a
RowListfocused item'ssubBoundingRect()by @lvcabral in #1030 - (rsg) Defer reentrant field observers and refresh
subBoundingRect()on a pending focus change by @lvcabral in #1032 - (rsg) Honor
vertAligninScrollingLabelby centering against the node height by @lvcabral in #1034 - (rsg) Honor numeric-string writes to
ArrayGridnumColumns/numRowsby @lvcabral in #1035 - (rsg) Honor descending key arrays in the
FieldInterpolatorsegment resolution by @lvcabral in #1036 - (rsg) Fire the initial
itemFocusedwhen list/grid content is populated after assignment by @lvcabral in #1037 - (rsg) Clip
ArrayGrid/PosterGriditems to their cell so overflow is hidden by @lvcabral in #1038 - (rsg) Fire
itemFocusedonly when the list/grid is in the focus chain by @lvcabral in #1039 - (rsg) Fixed the regression caused by #1037 affecting
ArrayGridby @lvcabral in #1040 - (rsg) Only defer reentrant observers for engine-initiated field emissions by @lvcabral in #1042
- (rsg) Honor
Librarystatements in component scripts and completed the RAF mock by @lvcabral in #1043 - (rsg) Detach a node from its previous parent when it is attached elsewhere by @lvcabral in #1044
- (rsg)
ButtonBarsample —RowListfit check,LayoutGroupre-alignment and single-row grid key propagation by @lvcabral in #1045 - (rsg)
ZoomRowListvertical-wrap flag and row-title/poster spacing by @lvcabral in #1046 - (rsg) Only the owning, presenting
Videonode renders the video plane by @lvcabral in #1047 - (rsg) Added the missing
secondaryTitlefield toContentNodeby @lvcabral in #1048 - (rsg) Changed the
ContentNode.subtitleTrackstype fromassocarraytoarrayby @lvcabral in #1051 - (rsg) Properly handle boxed
StringinPosterGrid,ScrollableTextandZoomRowListby @lvcabral in #1052 - (rsg) Position the
PanelSetright panel using the left panel'sleftPositionby @lvcabral in #1053 - (rsg) Guard the cross-thread serialization against circular container references by @lvcabral in #1055
- (rsg) Paint a black plane while a full-screen
Videowith UI enabled is buffering by @lvcabral in #1056 - (rsg) Restore function values across
Taskthreads from the component AST by @lvcabral in #1057 - (rsg) Keep a
Video's internal children off the cross-thread serialization by @lvcabral in #1059 - (rsg) Keep
ButtonGroupfrom managing custom (non-Button)Groupchildren by @lvcabral in #1060 - (rsg) Restore the
mcontext on rebuilt custom nodes and fixed the quit debugger loop by @lvcabral in #1061 - (rsg) Honor the app-managed visibility/opacity of a dynamic keyboard's
textEditBoxby @lvcabral in #1062 - (rsg) Resolve the
fixedvertFocusAnimationStyleto the non-wrappingfixedFocusby @lvcabral in #1063 - (rsg) Return the measured size from a detached
Label'sboundingRect()mid-render by @lvcabral in #1064 - (rsg) Move the live focus when a managed
ButtonGroup'sfocusButtonchanges by @lvcabral in #1065 - (rsg) Measure hidden grids, flush
MarkupGridrects and reorder on re-append by @lvcabral in #1066 - (rsg) Keep the video plane presenting through a mid-stream re-buffer by @lvcabral in #1067
- (rsg)
PanelSetfull-screen preview position, focus handling and back-navigation panel retention by @lvcabral in #1068 - (rsg)
PanelSetsets the attachedPanelheight per the Roku spec by @lvcabral in #1069 - (rsg) Let single-row
MarkupGridvertical keys bubble and honorhorizFocusAnimationStylefixedFocusby @lvcabral in #1070 - (rsg) Decouple the
Timerpolling from the frame-rate-limiting busy-wait by @markwpearce in #1076 - (rsg)
parentSubtype()returnsInvalidinstead of""at the hierarchy root by @markwpearce in #1077 - (rsg) Reset the key buffer when
roSGScreencloses, to prevent a stale key leaking into the next screen by @markwpearce in #1081
Chores, Build and Dependencies
- Bump follow-redirects from 1.15.11 to 1.16.0 by @dependabot[bot] in #917
- Bump uuid from 9.0.1 to 14.0.0 by @dependabot[bot] in #918
- Bump fast-uri from 3.1.0 to 3.1.2 by @dependabot[bot] in #921
- Bump webpack-dev-server from 5.2.3 to 5.2.4 by @dependabot[bot] in #923
- Bump ws from 8.18.3 to 8.20.1 by @dependabot[bot] in #924
- Bump qs and express by @dependabot[bot] in #925
- (chore) Added
CLAUDE.mdguidance for Claude Code by @lvcabral in #927 - (chore) Added the Roku reference docs (
dev-docsubmodule) and thebrs-referenceskill by @lvcabral in #929 - Bump shell-quote from 1.8.3 to 1.8.4 by @dependabot[bot] in #946
- Bump launch-editor from 2.12.0 to 2.14.1 by @dependabot[bot] in #955
- Bump ws by @dependabot[bot] in #957
- Bump webpack-dev-server from 5.2.4 to 5.2.5 by @dependabot[bot] in #958
- (test) Exercise a different case in the negated
bsConsttest by @lvcabral in #965 - (chore) Enforce
no-case-declarationsand brace switch cases that declare bindings by @lvcabral in #966 - (chore) Added new ESLint rules and fixed all instances by @lvcabral in #974
- (chore) Enhanced the ESLint rules and refactored code for consistency by @lvcabral in #975
- (chore) TypeScript 7 readiness — bundler
moduleResolution, explicit types, dropbaseUrlby @lvcabral in #981 - (chore) Updated the
dev-docsubmodule to the latest v2.0 and added thedocs:updatescript by @lvcabral in #987 - (docs) Simplified and shortened
CLAUDE.mdby @lvcabral in #1018 - Bump websocket-driver from 0.7.4 to 0.7.5 by @dependabot[bot] in #1031
- Bump brace-expansion from 5.0.6 to 5.0.7 by @dependabot[bot] in #1075
- Bump shell-quote from 1.8.4 to 1.10.0 by @dependabot[bot] in #1078
- Bump fast-uri from 3.1.2 to 3.1.4 by @dependabot[bot] in #1079
- Bump uuid and webpack-dev-server by @dependabot[bot] in #1080
Full Changelog: v2.2.0...v2.3.0