From ef8f8f01583ea57d9f03e195c94c7f451e7a0f56 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 12 May 2026 14:12:45 -0300 Subject: [PATCH 1/5] docs-lint: add empty-table-header-cell + html-row-cell-count-mismatch rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new HTML-table rules in the existing tables rule module: empty-table-header-cell (error) An empty in the header row of an HTML table creates a phantom column when rendered. This is the bug fixed on ArrayGrid by upstream commit 9476417c — the previous version had 8 trailing empty placeholders padding the header out to 13 columns, producing 8 blank columns in the published page. The rule catches this class of bug repo-wide. html-row-cell-count-mismatch (error) Fallback for the same class of bug when the phantom cells are not empty: per top-level table, count / cells in each and flag rows whose count differs from the first row. Both rules have conservative exceptions to avoid false positives: - Row-header tables (tables that use on body row labels) are recognized as comparison/cross-tab tables where the empty top-left corner is semantically correct, not a phantom column. Rule A is skipped on those tables. - Rule B is skipped on tables with nested elements (cell counts would otherwise sweep in nested-table cells). - Rule B is skipped on tables that use rowspan or colspan > 1, since a raw cell-count comparison can't reason about merged cells. --- .github/scripts/docs-lint/rules/tables.mjs | 124 ++++++++++++++++++++- 1 file changed, 119 insertions(+), 5 deletions(-) diff --git a/.github/scripts/docs-lint/rules/tables.mjs b/.github/scripts/docs-lint/rules/tables.mjs index fd72f918..94641cb6 100644 --- a/.github/scripts/docs-lint/rules/tables.mjs +++ b/.github/scripts/docs-lint/rules/tables.mjs @@ -2,11 +2,14 @@ * Table rules for docs-lint. * * Rules and severity: - * error pipe-col-count Pipe row has different column count than header. - * error pipe-no-blank-above Pipe table has no blank line before it (won't render). - * error escaped-html-in-cell Cell content like \ creates a phantom column when rendered. + * error html-row-cell-count-mismatch A row in an HTML table has a different number of cells than the first row. + * warning adjacent-tables Two pipe tables with same column count and no heading between them. + * warning html-blank-between-tags Blank line between tags inside an HTML
. + * warning html-blank-in-cell Blank line inside an HTML cell content block. */ import { visit } from 'unist-util-visit' @@ -16,6 +19,7 @@ export const id = 'tables' export function check({ file, sourceLines, mdast, codeFenceMask, reporter }) { checkPipe(mdast, file, sourceLines, reporter) checkHtml(file, sourceLines, codeFenceMask, reporter) + checkHtmlCells(file, sourceLines, codeFenceMask, reporter) } function checkPipe(mdast, file, sourceLines, reporter) { @@ -176,3 +180,113 @@ function checkHtml(file, sourceLines, codeFenceMask, reporter) { } } } + +function checkHtmlCells(file, sourceLines, codeFenceMask, reporter) { + // Two related HTML-table bugs that aren't caught by the AST-based pipe rules: + // + // empty-table-header-cell (whitespace-only). Even one creates + // a phantom blank column in the rendered table. + // ArrayGrid hit this with 8 trailing empty rows in the same table with different + // cell counts. Fallback for the same class of + // bug when the rogue cells aren't empty. + // + // Row-header table exception: a table that uses (broke GitHub/IDE preview rendering even though ReadMe tolerated it). --- .../brightscript/events/rodeviceinfoevent.md | 110 +++++------------- 1 file changed, 28 insertions(+), 82 deletions(-) diff --git a/docs/REFERENCES/brightscript/events/rodeviceinfoevent.md b/docs/REFERENCES/brightscript/events/rodeviceinfoevent.md index 26d43b64..20c6bbc2 100644 --- a/docs/REFERENCES/brightscript/events/rodeviceinfoevent.md +++ b/docs/REFERENCES/brightscript/events/rodeviceinfoevent.md @@ -23,87 +23,45 @@ Checks if the device status has changed. This method returns true if the device Checks the current status of the device. This method returns an roAssociativeArray containing one of the following members: -
+ // cells (May 11 fix). + // html-row-cell-count-mismatch
in its body + // rows is a comparison/cross-tab table where the top-left corner cell is the + // intersection of column-headers and row-headers. An empty there is the + // semantically correct corner cell, not a phantom column. Skip empty-th flags + // for tables that use scope="row". + // + // Rule B is conservative: skipped on tables that contain nested elements, + // since counting cells per row would otherwise sweep in nested-table cells. + + // Build single string with code fences blanked; preserves line numbers. + const source = sourceLines.map((line, i) => (codeFenceMask[i] ? '' : line)).join('\n') + const lineOf = (idx) => source.slice(0, idx).split('\n').length + + for (const t of findTopLevelTables(source)) { + const inner = source.slice(t.start, t.end) + + // Row-header table heuristic: any as the corner. That works visually but is semantically incorrect: the row labels are headers (they describe their row), not data. Convert the markup to use
inside the table. + const isRowHeaderTable = /]*scope\s*=\s*["']?row["']?/i.test(inner) + + // Rule A: empty . Skip on row-header tables. + if (!isRowHeaderTable) { + const thPattern = /]*)?>([\s\S]*?)<\/th\s*>/gi + let m + while ((m = thPattern.exec(inner)) !== null) { + if (m[1].trim() === '') { + const lineWithinInner = inner.slice(0, m.index).split('\n').length - 1 + reporter.add({ + file, + line: lineOf(t.start) + lineWithinInner, + col: 1, + rule: 'empty-table-header-cell', + severity: 'error', + message: 'empty creates a phantom column when rendered', + }) + } + } + } + + // Rule B: cell count mismatch. Skip if nested tables would confuse the count, + // or if the table uses rowspan/colspan > 1 (legitimate cell merging that a + // raw cell-count comparison can't reason about). + if (t.hasNesting) continue + if (/\b(?:row|col)span\s*=\s*["']?(?!0|1["'\s>])/i.test(inner)) continue + const trPattern = /]*)?>([\s\S]*?)<\/tr\s*>/gi + const rows = [] + let tr + while ((tr = trPattern.exec(inner)) !== null) { + const cellCount = (tr[1].match(/]/gi) || []).length + const lineWithinInner = inner.slice(0, tr.index).split('\n').length - 1 + rows.push({ line: lineOf(t.start) + lineWithinInner, count: cellCount }) + } + if (rows.length < 2) continue + const expected = rows[0].count + for (const row of rows.slice(1)) { + if (row.count !== expected) { + reporter.add({ + file, + line: row.line, + col: 1, + rule: 'html-row-cell-count-mismatch', + severity: 'error', + message: `row has ${row.count} cells; first row of table has ${expected}`, + }) + } + } + } +} + +function findTopLevelTables(source) { + // Walk /
tags tracking depth. Records each top-level + // (depth==1) ...
span and whether nesting was observed inside. + const tables = [] + const tagRegex = /<(\/?)table[\s>]/gi + let depth = 0 + let openStart = -1 + let maxDepth = 0 + let m + while ((m = tagRegex.exec(source)) !== null) { + const isClose = m[1] === '/' + if (!isClose) { + depth++ + if (depth === 1) { + openStart = m.index + maxDepth = 1 + } else if (depth > maxDepth) { + maxDepth = depth + } + } else { + if (depth === 1) { + const endIdx = source.indexOf('>', m.index) + 1 + tables.push({ start: openStart, end: endIdx, hasNesting: maxDepth > 1 }) + openStart = -1 + } + depth-- + } + } + return tables +} From b1e79e8fb984fb52915f725b330a472d585132ed Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 12 May 2026 14:13:07 -0300 Subject: [PATCH 2/5] media/index.md: use scope=row for comparison-table row labels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Adaptive streaming protocols and Supported video codecs tables on media/index.md are both row-label comparison tables: each row labels a feature/spec (Audio Codecs, Aspect Ratio, etc.) and each column compares protocols/codecs. The previous markup used a
for the row-label cell and an empty on each body row's label cell and on the column-header cells. The empty corner stays as the intersection of row-header and column- header axes; it has no row to label. This is the standard pattern recommended by HTML accessibility guidance — assistive tech can now announce "Audio Codecs, DASH, AAC, DTS, DD, DD+" with full row + column context, and ReadMe styles the row-header cells distinctly, which improves visual scannability. Also reformatted the source HTML with 2-space indentation per level for readability (no change to rendered output). --- docs/SPECIFICATIONS/media/index.md | 268 ++++++++++++++--------------- 1 file changed, 134 insertions(+), 134 deletions(-) diff --git a/docs/SPECIFICATIONS/media/index.md b/docs/SPECIFICATIONS/media/index.md index 60938ad9..5f6422d5 100644 --- a/docs/SPECIFICATIONS/media/index.md +++ b/docs/SPECIFICATIONS/media/index.md @@ -51,52 +51,52 @@ Roku supports the following widely-used standard formats for adaptive bit rate s - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DASHHLSSmooth
Audio CodecsAAC, DTS, DD, DD+AAC, MP3, DTS, DD, DD+AAC, MP3, DTS, DD, DD+
Video CodecsAVC, HEVCAVC, HEVC, VP9AVC, HEVC
Subtitle formatsTTML, fragmented ISMT
TTML, unfragmented TTML text
TTML, side-loaded TTML text

WebVTT, fragmented WebVTT text
WebVTT, unfragmented WebVTT text
WebVTT, side-loaded WebVTT text

SRT, side-loaded SRT text
TTML, side-loaded TTML text

WebVTT, fragmented WebVTT text
WebVTT, unfragmented WebVTT text
WebVTT, side-loaded WebVTT text

SRT, side-loaded SRT text
TTML, fragmented ISMT
TTML, side-loaded TTML text

WebVTT, side-loaded WebVTT text

SRT, side-loaded SRT text
Audio/video chunk formatFragmented MP4, CMAF (muxing audio and video not supported for CMAF)video: TS, CMAF (muxing audio and video not supported for CMAF)
audio: aac, ac3, eac3
PIFF
DRMPlayReady, WidevineAES-128, Widevine, VerimatrixPlayReady
HDR supportDolby Vision, HDR10Dolby Vision, HDR10
DASHHLSSmooth
Audio CodecsAAC, DTS, DD, DD+AAC, MP3, DTS, DD, DD+AAC, MP3, DTS, DD, DD+
Video CodecsAVC, HEVCAVC, HEVC, VP9AVC, HEVC
Subtitle formatsTTML, fragmented ISMT
TTML, unfragmented TTML text
TTML, side-loaded TTML text

WebVTT, fragmented WebVTT text
WebVTT, unfragmented WebVTT text
WebVTT, side-loaded WebVTT text

SRT, side-loaded SRT text
TTML, side-loaded TTML text

WebVTT, fragmented WebVTT text
WebVTT, unfragmented WebVTT text
WebVTT, side-loaded WebVTT text

SRT, side-loaded SRT text
TTML, fragmented ISMT
TTML, side-loaded TTML text

WebVTT, side-loaded WebVTT text

SRT, side-loaded SRT text
Audio/video chunk formatFragmented MP4, CMAF (muxing audio and video not supported for CMAF)video: TS, CMAF (muxing audio and video not supported for CMAF)
audio: aac, ac3, eac3
PIFF
DRMPlayReady, WidevineAES-128, Widevine, VerimatrixPlayReady
HDR supportDolby Vision, HDR10Dolby Vision, HDR10
@@ -134,94 +134,94 @@ Videos can be encoded using `H.264`, `HEVC (H.265)`, `VP9`, or `AV1` (DASH only) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
AVC (H.264)HEVC (H.265)[^1]VP9[^2]AV1 (DASH only)
Aspect Ratio 2[^3]VariousVariousVarious
DimensionVarious up to 1920x1080Various up to 3840x2160Various up to 3840x2160Various up to 7680 x 4320
Input Frame Rate[^4]24p, 25p, 30p, 50p, 60p24p, 25p, 30p, 50p, 60p24p, 25p, 30p, 60p[^6]24p,25p,30p,50p,60p
Color SpaceRec.709Rec.709, Rec.2020Rec.709, Rec.2020Rec.709, Rec.2020
Profilemain, highmain, main 10profile 0, profile 2main, main 10
Level4.1, 4.24.1, 5.0, 5.14.1, 5.0, 5.1
Video ModeConstrained VBRConstrained VBRConstrained VBRConstrained VBR
Video BitrateUp to 10MbpsUp to 40MbpsUp to 40MbpsUp to 40Mbps
Peak Video Bit rate1.5x average1.5x average1.5x average1.5x average
Key Frame Interval [^5]< 10s< 10s< 10s< 10s
HDR supportDolby Vision: dvav.09Dolby Vision: dvhe.05
HDR10 (HEVC profile Main 10)
HLG
HDR10Dolby Vision
HDR10
HDR10+
AVC (H.264)HEVC (H.265)[^1]VP9[^2]AV1 (DASH only)
Aspect Ratio 2[^3]VariousVariousVarious
DimensionVarious up to 1920x1080Various up to 3840x2160Various up to 3840x2160Various up to 7680 x 4320
Input Frame Rate[^4]24p, 25p, 30p, 50p, 60p24p, 25p, 30p, 50p, 60p24p, 25p, 30p, 60p[^6]24p,25p,30p,50p,60p
Color SpaceRec.709Rec.709, Rec.2020Rec.709, Rec.2020Rec.709, Rec.2020
Profilemain, highmain, main 10profile 0, profile 2main, main 10
Level4.1, 4.24.1, 5.0, 5.14.1, 5.0, 5.1
Video ModeConstrained VBRConstrained VBRConstrained VBRConstrained VBR
Video BitrateUp to 10MbpsUp to 40MbpsUp to 40MbpsUp to 40Mbps
Peak Video Bit rate1.5x average1.5x average1.5x average1.5x average
Key Frame Interval [^5]< 10s< 10s< 10s< 10s
HDR supportDolby Vision: dvav.09Dolby Vision: dvhe.05
HDR10 (HEVC profile Main 10)
HLG
HDR10Dolby Vision
HDR10
HDR10+
From bd6e77cd2322fa6533230a97fb3283f7376961ef Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 12 May 2026 14:13:22 -0300 Subject: [PATCH 3/5] brs-profiler-file-format.md: label entry-type column, drop empty
The 8 binary-format tables in this file each described a byte-stream entry layout, where the second column showed the entry-type bits (Bits 2..0 of the entry tag, with Literal 0x0..0x5 distinguishing each entry kind). The header for that column was left blank in every table, producing a phantom column for the reader. This change adds an explicit header for the type-marker column: - 7 tables get an "Entry type" header for the Bits 2..0 column. - The Memory operation table additionally gets an "Operation type" header for the Bits 4..3 column (which differentiates alloc / free / free_realloc within a memory entry). Also reformatted each table with 2-space indentation per level, added scope="col" on all header cells for accessibility, and fixed two minor typos in the original body text (a stray ") " and an extra space). --- .../dev-tools/brs-profiler-file-format.md | 293 +++++++++--------- 1 file changed, 147 insertions(+), 146 deletions(-) diff --git a/docs/DEVELOPER/dev-tools/brs-profiler-file-format.md b/docs/DEVELOPER/dev-tools/brs-profiler-file-format.md index 9a4a8a8a..49343586 100644 --- a/docs/DEVELOPER/dev-tools/brs-profiler-file-format.md +++ b/docs/DEVELOPER/dev-tools/brs-profiler-file-format.md @@ -12,7 +12,7 @@ next: --- -Developers can follow this specification to build a custom profiling tool that analyzes app performance metrics such as CPU and memory usage. This specification describes the entries in the header, body, and footer of a BrightScript profiler file (**.bsprof**) file. +Developers can follow this specification to build a custom profiling tool that analyzes app performance metrics such as CPU and memory usage. This specification describes the entries in the header, body, and footer of a BrightScript profiler file (**.bsprof**) file. ## Basic information @@ -30,7 +30,7 @@ The following section details the encoding, data types, and indexes used in the ### Data types -The fields in the file format may be one of the following data types, which can hold any encoded value for a field: +The fields in the file format may be one of the following data types, which can hold any encoded value for a field: | Data type | Definition | | :-------- | :----------------------------------------------------------- | @@ -45,16 +45,16 @@ The fields in the file format may be one of the following data types, which can ### Indexes -All indexes are 1-based (rather than 0-based). A value of 0 indicates a null or invalid ID. +All indexes are 1-based (rather than 0-based). A value of 0 indicates a null or invalid ID. ## Data structure -BrightScript profiling data is provided in a streaming format that contains a series of record types that can appear anywhere in the file. This allows the streaming of data to the network while the app runs. Overall, the file format incudes the following: +BrightScript profiling data is provided in a streaming format that contains a series of record types that can appear anywhere in the file. This allows the streaming of data to the network while the app runs. Overall, the file format incudes the following: - header - body (the body contains the stream of data entries) - end of entries marker -- footer +- footer ### Header @@ -98,20 +98,21 @@ Each entry is a stream of bytes, beginning with a varint-encoded unsigned 64-bit - - - - - - - - - - - - - - + + + + + + + + + + + + + +
Entry tag (variant-encoded uint64)Following bytes
Bits 63..3 (most significant 61 bits)
This is the "entry tag payload". The meaning of this payload is determined by entry tag type.
Bits 2..0 (least significant 3 bits)
This is the entry tag type.
Multiple bytes, determined by entry tag type.
Entry tag (variant-encoded uint64)Entry typeFollowing bytes
Bits 63..3 (most significant 61 bits) +
This is the "entry tag payload". The meaning of this payload is determined by entry tag type.
Bits 2..0 (least significant 3 bits)
This is the entry tag type.
Multiple bytes, determined by entry tag type.
@@ -123,78 +124,78 @@ String IDs are stored in several fields within the profiling data. The indexes a - - - - - - - - - - - - - - + + + + + + + + + + + + + +
Entry tag (variant-encoded uint64)String
Bits 34..3
string ID (uint32)
Bits 2..0 (least significant 3 bits)
Literal 0x0
utf8z string
Entry tag (variant-encoded uint64)Entry typeString
Bits 34..3
string ID (uint32)
Bits 2..0 (least significant 3 bits)
Literal 0x0
utf8z string
### Executable module entry -Each executable module is a stream of bytes that represents a block of code that runs independently of other modules. +Each executable module is a stream of bytes that represents a block of code that runs independently of other modules. The modules listed in a profiler file are run simultaneously within a single profiler target. For example, each SceneGraph component is represented by a separate executable module. - - - - - - - - - - - - - - + + + + + + + + + + + + + +
Entry Tag (variant-encoded uint64)Thread name
Bits 34..3
module ID (uint32)
Bits 2..0 (least significant bits)
Literal 0x1
varint-encoded String ID (strid)
Entry tag (variant-encoded uint64)Entry typeThread name
Bits 34..3
module ID (uint32)
Bits 2..0 (least significant 3 bits)
Literal 0x1
varint-encoded String ID (strid)
-### Path element entry +### Path element entry A path element represents a single entry in a call path that is typically a function name. The path element ID is a uint32, where 0 is explicitly invalid and is used to mark a null value. -#### Root +#### Root - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
Entry tag (varint-encoded uint64)Calling path element IDExecutive module IDFile nameLine numberFunction name
Bits 34..3
The path element ID (uint32).
Bits 0..2 (least significant 3 bits)
Literal 0x2
varint-encoded literal 0 value.

This null calling path element ID specifies that this is a root entry for its executive module.
varint-encoded uint32

The module ID for which this element is a root entry.
varint-encoded String ID

The source file where this function was defined.
Varint-encoded uint32

Line number in the source file, where this function is defined.

This a 1-based value (the first line of source is 1, not 0).
varint-encoded String ID
Entry tag (varint-encoded uint64)Entry typeCalling path element IDExecutive module IDFile nameLine numberFunction name
Bits 34..3
The path element ID (uint32).
Bits 2..0 (least significant 3 bits)
Literal 0x2
varint-encoded literal 0 value.

This null calling path element ID specifies that this is a root entry for its executive module.
varint-encoded uint32

The module ID for which this element is a root entry.
varint-encoded String ID

The source file where this function was defined.
Varint-encoded uint32

Line number in the source file, where this function is defined.

This is a 1-based value (the first line of source is 1, not 0).
varint-encoded String ID
@@ -202,28 +203,28 @@ A path element represents a single entry in a call path that is typically a func - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + +
Entry tag (varint-encoded uint64)Calling path element IDLine offset in callerFile nameLine numberFunction name
Bits 34..3
The path element ID (uint32).
Bits 0..2 (least significant 3 bits)
Literal 0x2
varint-encoded path element ID of callervarint-encoded uint32

A 1-based offset of the calling line of code, into the function at the end of the calling path.

To calculate the actual line number in the source file, a custom tool should use the following formula : pathEntry.lineNumber + memoryEntry.lineOffset - 1.

This value is only present if the file header specifies that line data is included.
varint-encoded String ID

The source file where this function was defined.
Varint-encoded uint32

The line number in the source file, where this function is defined.

This is a 1-based value (the first line of source is 1, not 0).
varint-encoded String ID
Entry tag (varint-encoded uint64)Entry typeCalling path element IDLine offset in callerFile nameLine numberFunction name
Bits 34..3
The path element ID (uint32).
Bits 2..0 (least significant 3 bits)
Literal 0x2
varint-encoded path element ID of callervarint-encoded uint32

A 1-based offset of the calling line of code, into the function at the end of the calling path.

To calculate the actual line number in the source file, a custom tool should use the following formula: pathEntry.lineNumber + memoryEntry.lineOffset - 1.

This value is only present if the file header specifies that line data is included.
varint-encoded String ID

The source file where this function was defined.
Varint-encoded uint32

The line number in the source file, where this function is defined.

This is a 1-based value (the first line of source is 1, not 0).
varint-encoded String ID
@@ -233,26 +234,26 @@ A memory operation is a stream of bytes. - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + +
Entry tag (varint-encoded uint64)Line offsetMemory addressAllocation size
Bits 36..5
The path element ID (the uint32) that generated this operation).
Bits 4..3
Operation type, which may be on the following values:
  • 0: alloc
  • 1: free
  • 2: free_realloc (a free_realloc operation is a free operation that occurs as part of a realloc, and should be immediately followed by an alloc operation).
Bits 2..0 (least significant 3 bits)
Literal 0x3
varint-encoded uint32

A 1-based offset of the line of code, into the function at the end of the call path.

To calculate the actual line number in the source file, a custom tool should use the following formula : pathEntry.lineNumber + memoryEntry.lineOffset - 1.

This value is only present if the file header specifies that line data is included.
varint-encoded uint32Varint-encoded uint32

This value is only present for alloc operations.
Entry tag (varint-encoded uint64)Operation typeEntry typeLine offsetMemory addressAllocation size
Bits 36..5
The path element ID (the uint32) that generated this operation.
Bits 4..3
Operation type, which may be one of the following values:
  • 0: alloc
  • 1: free
  • 2: free_realloc (a free_realloc operation is a free operation that occurs as part of a realloc, and should be immediately followed by an alloc operation).
Bits 2..0 (least significant 3 bits)
Literal 0x3
varint-encoded uint32

A 1-based offset of the line of code, into the function at the end of the call path.

To calculate the actual line number in the source file, a custom tool should use the following formula: pathEntry.lineNumber + memoryEntry.lineOffset - 1.

This value is only present if the file header specifies that line data is included.
varint-encoded uint32Varint-encoded uint32

This value is only present for alloc operations.
@@ -263,24 +264,24 @@ A CPU measurement entry is a stream of bytes. The custom tool should treat CPU e - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + +
Entry tag (varint-encoded uint64)Line offsetCPU selfTime self
Bits 34..3
The path element ID (the uint32) that generated this operation).
Bits 2..0 (least significant 3 bits)
Literal 0x4
varint-encoded uint32

A 1-based offset of the line of code, into the function at the end of the call path.

To calculate the actual line number in the source file, a custom tool should use the following formula : pathEntry.lineNumber + memoryEntry.lineOffset - 1.

This value is only present if the file header specifies that line data is included.
varint-encoded uint32

The incremental CPU time spent on the call path.
varint-encoded uint32

The incremental wall-clock time spent on the call path.
Entry tag (varint-encoded uint64)Entry typeLine offsetCPU selfTime self
Bits 34..3
The path element ID (the uint32) that generated this operation.
Bits 2..0 (least significant 3 bits)
Literal 0x4
varint-encoded uint32

A 1-based offset of the line of code, into the function at the end of the call path.

To calculate the actual line number in the source file, a custom tool should use the following formula: pathEntry.lineNumber + memoryEntry.lineOffset - 1.

This value is only present if the file header specifies that line data is included.
varint-encoded uint32

The incremental CPU time spent on the call path.
varint-encoded uint32

The incremental wall-clock time spent on the call path.
@@ -290,20 +291,20 @@ A count of calls made into a specific call path. The custom tool should treat ea - - - - - - - - - - - - - - + + + + + + + + + + + + + +
Entry tag (varint-encoded uint64)Call count
Bits 34..3
The path element ID (uint32) that was called.
Bits 2..0 (least significant 3 bits)
Literal 0x5
Varint-encoded uint32

The number of times a function was called on the specified call path (incremental value).
Entry tag (varint-encoded uint64)Entry typeCall count
Bits 34..3
The path element ID (uint32) that was called.
Bits 2..0 (least significant 3 bits)
Literal 0x5
Varint-encoded uint32

The number of times a function was called on the specified call path (incremental value).
From 3dfafa1aa1e6689427fd068252ef4b809254ed14 Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 12 May 2026 14:13:38 -0300 Subject: [PATCH 4/5] componentlibrary.md: rebuild Fields table, drop phantom columns The Fields table on this page had three overlapping problems: 1. JSX-style with 10 trailing empty cells padding the header out to 15 columns. The published page rendered with 10 phantom blank columns extending off to the right (same class of bug as the May 11 ArrayGrid fix in 9476417c). 2. The loadStatus row had a legitimate nested
inside its Description cell (Value/Meaning rows for "none"/"loading"/"ready" /"failed"). But the contents of that nested table were also duplicated as 10 flat rows in the outer table. On the published page, the nested-table rows were leaking out of the loadStatus Description cell and rendering as outer-table rows between loadStatus and id. 3. The body cells were marked up with one cell per three lines of source, making the 606-line file mostly whitespace. Rebuilt the table as a clean 5-column, 3-row HTML table (Field / Type / Default / Access Permission / Description) with the nested Value/Meaning table properly contained inside the loadStatus Description cell using / and scope="col" headers. The file goes from 606 lines to 106 lines with no rendered content lost. --- .../control-nodes/componentlibrary.md | 566 +----------------- 1 file changed, 33 insertions(+), 533 deletions(-) diff --git a/docs/REFERENCES/scenegraph/control-nodes/componentlibrary.md b/docs/REFERENCES/scenegraph/control-nodes/componentlibrary.md index 0b194122..90f8f621 100644 --- a/docs/REFERENCES/scenegraph/control-nodes/componentlibrary.md +++ b/docs/REFERENCES/scenegraph/control-nodes/componentlibrary.md @@ -54,553 +54,53 @@ Component libraries do not need to be packaged or signed with the same devid as ## Fields -
cells appended to the loadStatus row, AND duplicated again as 5 separate
+
- - - - - - - - - - - - - - - + + + + + - - - - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + -
- Field - - Type - - Default - - Access Permission - - Description - - - - - - - - - - - FieldTypeDefaultAccess PermissionDescription
- loadStatus - - value string - - "none" - - READ_ONLY - loadStatusvalue string"none"READ_ONLY Indicates the progress of the library download. The possible values are: -

- - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + +
ValueMeaning
noneThe default if the library is not being downloaded
loadingLibrary is downloading
readyLibrary has downloaded successfully
failedDownload of the library has failed
ValueMeaning
noneThe default if the library is not being downloaded
loadingLibrary is downloading
readyLibrary has downloaded successfully
failedDownload of the library has failed
- Value - - Meaning - - none - - The default if the library is not being downloaded - - loading - - Library is downloading - - ready - - Library has downloaded successfully - - failed - - Download of the library has failed -
- Value - - Meaning - - - - - - - - - - - - - - - - - - - - - - - - - - - idstringno defaultREAD_WRITESet to a unique ID for the library for the application
- none - - The default if the library is not being downloaded - - - - - - - - - - - - - - - - - - - - - - - - - - -
- loading - - Library is downloading - - - - - - - - - - - - - - - - - - - - - - - - - - -
- ready - - Library has downloaded successfully - - - - - - - - - - - - - - - - - - - - - - - - - - -
- failed - - Download of the library has failed - - - - - - - - - - - - - - - - - - - - - - - - - - -
- id - - string - - no default - - READ_WRITE - - Set to a unique ID for the library for the application - - - - - - - - - - - - - - - - - - - - -
- uri - - uri - - no default - - READ_WRITE - - The URL of the library to be downloaded - - - - - - - - - - - - - - - - - - - - - uriurino defaultREAD_WRITEThe URL of the library to be downloaded
+
From 579f5fbbfd0e1e43f34c270940ed87720fd03b8f Mon Sep 17 00:00:00 2001 From: Christopher Dwyer-Perkins Date: Tue, 12 May 2026 14:14:03 -0300 Subject: [PATCH 5/5] rodeviceinfoevent.md: convert JSX Table/Anchor to markdown, add method anchors Three cleanup concerns in one table: 1. JSX-flavored /
tags (capital T) converted to plain /
. The capitalized form is a leftover from v1->v2 migration and not used elsewhere in the v2 docs. 2. Four elements converted to markdown link syntax: [text](doc:ifdeviceinfo#anchor). One had a full-URL href pointing at the v1 developer.roku.com site, normalized to the same doc:ifdeviceinfo slug as the others. 3. Three bare method references on the audioCodecCapabilityChanged and videoCodecCapabilityChanged rows had dangling text from partial anchor removal (e.g. `[CanDecodeAudio](doc:ifdeviceinfo) asObject)` with `asObject)` outside the link). Rewrote those as `[CanDecodeAudio(audio_format as Object)](doc:ifdeviceinfo# candecodeaudioaudio_format-as-object-as-object)` and similar, so the link text shows the method signature and the anchor lands on the specific method definition. Also added scope="col" on header cells, collapsed each body cell from three source lines to one (except generalMemoryLevel which keeps its
    ), and removed the stray blank line between rows inside
+
- - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - - + + + - - - + + + -
- Member - - Type - - Description - MemberTypeDescription
- audioGuideEnabled - - Boolean - - True if the screen reader is enabled. The audioGuideEnabled event will only ever get fired if ifDeviceInfo.EnableAudioGuideChangedEvent(true) called before entering the message loop - audioGuideEnabledBooleanTrue if the screen reader is enabled. The audioGuideEnabled event will only ever get fired if [ifDeviceInfo.EnableAudioGuideChangedEvent(true)](doc:ifdeviceinfo#enableaudioguidechangedeventenable-as-boolean-as-dynamic) is called before entering the message loop.
- exitedScreensaver - - Boolean - - True if the screensaver was exited. The exitedScreensaver event will only ever get fired if ifDeviceInfo.EnableScreensaverExitedEvent(true) is called before entering the message loop - exitedScreensaverBooleanTrue if the screensaver was exited. The exitedScreensaver event will only ever get fired if [ifDeviceInfo.EnableScreensaverExitedEvent(true)](doc:ifdeviceinfo#enablescreensaverexitedeventenable-as-boolean-as-dynamic) is called before entering the message loop.
- appFocused - - Boolean - - It is set to False when the System Overlay takes focus and True when the app regains focus - appFocusedBooleanIt is set to False when the System Overlay takes focus and True when the app regains focus.
- linkStatus - - Boolean - - True if the device currently seems to have an active network connection. The linkStatus event will only ever get fired if ifDeviceInfo.EnableLinkStatusEvent(true) is called before entering the message loop - linkStatusBooleanTrue if the device currently seems to have an active network connection. The linkStatus event will only ever get fired if [ifDeviceInfo.EnableLinkStatusEvent(true)](doc:ifdeviceinfo#enablelinkstatuseventenable-as-boolean-as-boolean) is called before entering the message loop.
- internetStatus - - Boolean - - True if the device currently has a valid connection to the external internet. This status is determined by the device's ability to reach Roku's backend services. The internetStatus event will only be fired if ifDeviceInfo.EnableInternetStatusEvent(true) is called. Note that a device may have linkStatus as true (connected to a router) while internetStatus remains false (no ISP connectivity). - internetStatusBooleanTrue if the device currently has a valid connection to the external internet. This status is determined by the device's ability to reach Roku's backend services. The internetStatus event will only be fired if [ifDeviceInfo.EnableInternetStatusEvent(true)](doc:ifdeviceinfo#enableinternetstatuseventenable-as-boolean-as-boolean) is called. Note that a device may have linkStatus as true (connected to a router) while internetStatus remains false (no ISP connectivity).
- generalMemoryLevel - - String - generalMemoryLevelString Fires notifications to the app about memory levels. This event will be sent first when the OS transitions from "normal" to "low" state and will continue to be sent while in "low" or "critical" states.

The events will be throttled so as to not overwhelm the application listening for these events. The application may voluntarily free up memory by invalidating references to objects (e.g. release ContentNodes held in a cache, release offscreen renderable nodes, etc.).

The "low" and "critical" events will be sent to the OS forces the application to exit. -

  • "normal" means that the general memory is within acceptable levels
  • "low" means that the general memory is below acceptable levels but not critical
  • @@ -112,29 +70,17 @@ Checks the current status of the device. This method returns an roAssociativeArr
- audioCodecCapabilityChanged - - Boolean - - The audio codec capability has changed if true. If your application receives this event, you can check the current audio playback capability using the [`roDeviceInfo.CanDecodeAudio`](doc:ifdeviceinfo)asObject) and [`roDeviceInfo.GetAudioDecodeInfo`](doc:ifdeviceinfo)asObject) methods.

This event is only fired if the [`ifDeviceInfo.EnableCodecCapChangedEvent(true)`](doc:ifdeviceinfo)) is called before entering the message loop. -
audioCodecCapabilityChangedBooleanThe audio codec capability has changed if true. If your application receives this event, you can check the current audio playback capability using the [`roDeviceInfo.CanDecodeAudio(audio_format as Object)`](doc:ifdeviceinfo#candecodeaudioaudio_format-as-object-as-object) and [`roDeviceInfo.GetAudioDecodeInfo()`](doc:ifdeviceinfo#getaudiodecodeinfo-as-object) methods.

This event is only fired if [`ifDeviceInfo.EnableCodecCapChangedEvent(true)`](doc:ifdeviceinfo#enablecodeccapchangedeventenable-as-boolean) is called before entering the message loop.
- videoCodecCapabilityChanged - - Boolean - - The video codec capability has changed if true. If your application receives this event, you can check the current video playback capability using the [`roDeviceInfo.CanDecodeVideo`](doc:ifdeviceinfo)asObject) method.

This event is only fired if [`ifDeviceInfo.EnableCodecCapChangedEvent(true)`](doc:ifdeviceinfo)) is called before entering the message loop. -
videoCodecCapabilityChangedBooleanThe video codec capability has changed if true. If your application receives this event, you can check the current video playback capability using the [`roDeviceInfo.CanDecodeVideo(video_format as Object)`](doc:ifdeviceinfo#candecodevideovideo_format-as-object-as-object) method.

This event is only fired if [`ifDeviceInfo.EnableCodecCapChangedEvent(true)`](doc:ifdeviceinfo#enablecodeccapchangedeventenable-as-boolean) is called before entering the message loop.
+ ### isCaptionModeChanged() as Boolean