Skip to content

fix(post): render ANSI Rich table without leaking escape codes - #66

Merged
saforem2 merged 6 commits into
mainfrom
fix/ansi-table-render-2026-11-11
Nov 12, 2026
Merged

saforem2 merged 6 commits into
mainfrom
fix/ansi-table-render-2026-11-11

Conversation

@saforem2

@saforem2 saforem2 commented Nov 11, 2026 •

Copy link
Copy Markdown
Owner

Captures the current dirty working tree onto a branch (previously on main with unstaged changes and no PR).

What changed

  • web/astro.config.mjs — wire rehypeAnsi (both markdown + MDX pipelines)
  • web/src/utils/rehype-ansi.mjs + web/src/assets/ezpz-status.ansi — SGR → themed spans
  • web/src/components/RichTable.astro — accessible Rich table component
  • web/src/pages/posts/2026/10/16/index.mdx — Rich table content update + doc fix
  • web/src/styles/global.css + widgets — ANSI classes + UI tweaks

Verification

  • bun run build passes (157 pages, 37s)
  • Built dist/posts/2026/10/16/index.html contains ansi-block / ansi-bold etc., no literal [1m residue (checked via grep)

Fixes the screenshot-reported ANSI leak (raw [1m/[0m showing in the Rich table).

Closes n/a

Summary by Sourcery

Render ANSI terminal output as themed, accessible HTML while improving the related article presentation and responsive widget layout.

New Features:

  • Add an accessible, theme-aware Rich terminal table component for rendering job status data in posts.

Bug Fixes:

  • Decode ANSI SGR escape sequences in Markdown and MDX code blocks so terminal output renders with styled colors instead of exposing raw escape codes.
  • Preserve code-block theming and language framing while applying ANSI-specific styling.

Enhancements:

  • Update the HPC status article with the accessible Rich table and endpoint warning examples.
  • Improve responsive widget sizing, scrolling, and visual framing across landing-page widgets.
  • Refine focus-marker behavior and code-block spacing and styling.

Documentation:

  • Document Rich table output and its plain-text fallback in the HPC integration article.

Copilot AI balanced review requested due to automatic review settings November 11, 2026 20:14
@sourcery-ai

sourcery-ai Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Implementation

Decode SGR escapes into styled spans while preserving Shiki’s code-block frame and terminal label.

  • web/src/utils/rehype-ansi.mjs L1-192

UI

Add an accessible, theme-aware HTML table for the scheduler output and update the post to use it.

  • web/src/components/RichTable.astro L1-253
  • web/src/pages/posts/2026/10/16/index.mdx L10-11, L192-214

Style decoded ANSI spans across themes and refine code-block framing, badges, and focus markers.

  • web/src/layouts/Doc.css L1283-1288
  • web/src/styles/global.css L1466-1478, L1692-1825 (+4 small edits)

Adjust responsive widget sizing and scrolling, and reformat related widget styles.

  • web/src/components/landing/widgets/Widgets.astro L265-280, L290-336, L347-365 (+6 small edits)
  • web/src/components/landing/widgets/WorkWidget.astro L197-231, L248-258
  • web/src/components/landing/widgets/widget-frame.css L19-32, L139-162, L200-227 (+6 small edits)

Configuration

Register the ANSI rehype plugin in both Markdown and MDX processing pipelines.

  • web/astro.config.mjs L11, L502, L537

Add the captured ANSI scheduler output asset used by the post.

  • web/src/assets/ezpz-status.ansi L1-37

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota. Try requesting a review at a lower effort level.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Deploying sam-onl with  Cloudflare Pages  Cloudflare Pages

Latest commit: 0c959ab
Status:⚡️  Build in progress...

View logs

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 8 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="web/src/components/RichTable.astro" line_range="33" />
<code_context>
+} = Astro.props as Props
+
+/** Short Rich IDs shown alongside full IDs, if any */
+const ids = fullIds ?? (rows.length ? rows.map((r) => `${r[0]}.aurora-pbs-0001.example.alcf.anl.gov`) : [])
+
+const stateClass = (s: string) => {
</code_context>
<issue_to_address>
**Copied job IDs use a placeholder host**

When a caller omits `fullIds`, `ids` appends a fixed Aurora hostname to each row’s first value, so the component presents a fabricated full job ID that users copy instead of the actual identifier.

Do not generate full IDs by appending a fixed hostname; show only the short IDs unless callers provide the full IDs.

Also at `web/src/components/RichTable.astro:34`.
</issue_to_address>

### Comment 2
<location path="web/src/pages/posts/2026/10/16/index.mdx" line_range="193" />
<code_context>
-  8936286.aurora-pbs-0001.example.alcf.anl.gov
-  8936102.aurora-pbs-0001.example.alcf.anl.gov
-  8935991.aurora-pbs-0001.example.alcf.anl.gov
+```bash
+ezpz status --remote aurora
+╭─────────┬─────────┬───┬────────┬───────┬───────┬──────┬───────────┬──────────╮
</code_context>
<issue_to_address>
**Status example lacks accessible table cells**

When a reader views the updated status example, the post puts the status data in a `bash` fence instead of rendering `RichTable`, so readers and assistive technology get terminal text rather than semantic table cells.

Render the status example with `RichTable` instead of a `bash` code fence.
</issue_to_address>

### Comment 3
<location path="web/src/styles/global.css" line_range="1679" />
<code_context>
+.ansi-bg-black{background:color-mix(in srgb,var(--foreground0) 12%,transparent)} .ansi-bg-red{background:color-mix(in srgb,var(--red) 14%,transparent)} .ansi-bg-green{background:color-mix(in srgb,var(--green) 14%,transparent)} .ansi-bg-yellow{background:color-mix(in srgb,var(--yellow) 14%,transparent)} .ansi-bg-blue{background:color-mix(in srgb,var(--blue) 14%,transparent)} .ansi-bg-magenta{background:color-mix(in srgb,var(--mauve,var(--pink)) 14%,transparent)} .ansi-bg-cyan{background:color-mix(in srgb,var(--teal,var(--blue)) 14%,transparent)} .ansi-bg-white{background:color-mix(in srgb,var(--foreground0) 8%,transparent)}
</code_context>
<issue_to_address>
**Bright ANSI backgrounds render as default**

When a code block contains an SGR bright-background sequence (100–107), `BG_CLASS` emits `ansi-bg-bright-*` classes, but the stylesheet defines no matching background rules, so the requested background color is not rendered.

Add stylesheet background rules for the `ansi-bg-bright-*` classes.

Also at `web/src/utils/rehype-ansi.mjs:27-29`.
</issue_to_address>

### Comment 4
<location path="web/src/components/landing/widgets/Widgets.astro" line_range="307" />
<code_context>
+            )
+            :global(.talks-widget) {
+            max-height: min(55dvh, 42lh) !important;
+            max-height: min(55vh, 42lh) !important;
+            overflow: hidden !important;
+            display: flex !important;
</code_context>
<issue_to_address>
**Mobile panels exceed visible viewport**

When a mobile browser’s visible viewport is shorter than its layout viewport and the viewport-based cap is limiting, the later `55vh` declaration overrides `55dvh`, so `.talks-widget` panels use the larger layout viewport cap and can extend below the visible screen, crowding sibling widgets.

Make `55vh` the fallback and place the `55dvh` declaration after it so supported browsers use the dynamic viewport.

Also at `web/src/components/landing/widgets/WorkWidget.astro:204`.
</issue_to_address>

### Comment 5
<location path="web/src/utils/rehype-ansi.mjs" line_range="31-32" />
<code_context>
+    104: 'ansi-bg-bright-blue', 105: 'ansi-bg-bright-magenta', 106: 'ansi-bg-bright-cyan', 107: 'ansi-bg-bright-white',
+}
+
+const ANSI_RE = /\x1b\[([0-9;]*)m/g
+const ANSI_STRIP = /\x1b\[[0-9;]*m/g
+
+function color256(n) {
</code_context>
<issue_to_address>
**Colon-form SGR leaks visibly**

When a terminal output uses colon-separated SGR parameters, such as `38:2::r:g:b`, `ANSI_RE` and `ANSI_STRIP` do not recognize colon-separated SGR parameters, so the plugin returns without transforming a block that uses only that valid SGR form and the escape sequences remain visible in the rendered code.

Recognize and decode colon-separated SGR parameters, or strip unsupported SGR sequences before rendering.
</issue_to_address>

### Comment 6
<location path="web/src/components/RichTable.astro" line_range="65" />
<code_context>
+                rows.map((row, i) => (
+                    <tr>
+                        {row.map((cell, j) => (
+                            <td class={j === 1 ? stateClass(cell) : undefined}>{cell}</td>
+                        ))}
+                    </tr>
</code_context>
<issue_to_address>
**Custom columns color the wrong cell**

When a caller places the state column anywhere other than the second position, the cell renderer applies `stateClass` only to index 1, so reordering the configurable headers colors a non-state cell and leaves the state cell uncolored.

Make the state column explicit or define a fixed, semantically typed column schema instead of accepting arbitrary headers and rows.
</issue_to_address>

### Comment 7
<location path="web/src/pages/posts/2026/10/16/index.mdx" line_range="195" />
<code_context>
+╭─────────┬─────────┬───┬────────┬───────┬───────┬──────┬───────────┬──────────╮
+│�[1m �[0m�[1mHost   �[0m�[1m �[0m│�[1m �[0m�[1mJOBID  �[0m�[1m �[0m│�[1m �[0m�[1mS�[0m�[1m �[0m│�[1m �[0m�[1mQUEUE �[0m�[1m �[0m│�[1m �[0m�[1mNODES�[0m�[1m �[0m│�[1m �[0m�[1m WALL�[0m�[1m �[0m│�[1m �[0m�[1mELAP�[0m�[1m �[0m│�[1m �[0m�[1mJOBNAME  �[0m�[1m �[0m│�[1m �[0m�[1mNOTE    �[0m�[1m �[0m│
+├─────────┼─────────┼───┼────────┼───────┼───────┼──────┼───────────┼──────────┤
</code_context>
<issue_to_address>
**Terminal docs contradict sample**

When a reader compares the Rich-table description with the updated example, the new terminal sample shows box borders and does not show full IDs dimmed below the rows, while the accompanying prose describes a borderless table with short IDs and dimmed full IDs; readers get an inaccurate description of the displayed output.

Update the prose to match the output shown in the sample.
</issue_to_address>

### Comment 8
<location path="web/src/utils/rehype-ansi.mjs" line_range="136" />
<code_context>
+                    } else cur.push(n)
+                }
+            }
+            flush()
+            code.children = out
+            // Tag the pre so CSS can suppress the numbered gutter on ANSI-colored blocks
</code_context>
<issue_to_address>
**ANSI blocks gain a blank line**

When an ANSI code block ends with the newline commonly present in fenced Markdown, the split on a trailing newline already flushes the preceding line, then the unconditional final `flush()` emits another newline-only block; because `.ansi-line` is displayed as a block, the rendered code gains an extra blank row.

Avoid the final flush when the input ended at a line boundary and the current line is empty.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread web/src/components/RichTable.astro Resolved
Comment thread web/src/styles/global.css Resolved
Comment thread web/src/utils/rehype-ansi.mjs Resolved
Comment thread web/src/components/RichTable.astro Resolved
Comment thread web/src/utils/rehype-ansi.mjs Resolved
rehype-ansi overwrote the pre class with 'ansi-block' alone, dropping
astro-code which carries the border and language chip. Append instead
and ensure astro-code is present so the block renders like other code
fences.

Co-authored-by: internal-model
Bash green competed with the table's own cyan/yellow cells.
Orange terminal label reads as "dump" not "snippet".

Co-authored-by: internal-model
Dark themes use `color: var(--shiki-*) !important` on every
code span, which flattened the ANSI palette to a single hue.
Add !important to the ANSI palette so it wins the cascade
in all themes; the tokens already adapt via the site palette.

Co-authored-by: internal-model
@saforem2
saforem2 force-pushed the fix/ansi-table-render-2026-11-11 branch from b9da098 to 3760835 Compare November 12, 2026 15:44
@saforem2
saforem2 merged commit 8a3b4bb into main Nov 12, 2026
1 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants