Skip to content

Conversation

@bresilla
Copy link

@bresilla bresilla commented Jan 20, 2026

Summary

Adds full support for terminal indexed colors (0-255) and OSC-based terminal palette control, enabling the use of the standard 256-color palette and dynamic color modification via ANSI/OSC escape sequences.

Makes this possible (with tools like pywal et al):
ezgif-54dccc5a972ae0ee

Changes

TypeScript (RGBA.ts)

  • Added COLOR_TYPE_RGB, COLOR_TYPE_INDEXED, COLOR_TYPE_DEFAULT constants
  • Extended RGBA class with colorType, index properties
  • Added RGBA.fromIndex(index), RGBA.defaultForeground(), RGBA.defaultBackground() static methods
  • Added isIndexed(), isDefault(), isRgb() instance methods
  • Added indexToApproximateRgb() function with standard 256-color palette mapping
  • Extended ColorInput type to accept string | RGBA | IndexedColor | number
  • Updated parseColor() to handle "ansi:N" format, numeric indices, and {index: N} objects

TypeScript (terminal-palette.ts) - OSC Palette Control

  • Added setPaletteColor(index, hex) - dynamically set any of the 256 palette colors (OSC 4)
  • Added setForeground(hex) - set terminal default foreground color (OSC 10)
  • Added setBackground(hex) - set terminal default background color (OSC 11)
  • Added setCursorColor(hex) - set cursor color (OSC 12)
  • Added resetPaletteColor(index) - reset palette color to terminal default (OSC 104)
  • Added resetForeground() - reset foreground to default (OSC 110)
  • Added resetBackground() - reset background to default (OSC 111)
  • Added resetCursorColor() - reset cursor color to default (OSC 112)

Zig (ansi.zig)

  • Added ColorType enum: rgb, indexed, default
  • Added Color struct combining RGBA with type and index
  • Added indexToApproximateRgba(), fgIndexedColorOutput(), bgIndexedColorOutput()
  • Added fgDefaultOutput(), bgDefaultOutput() for default color reset

Zig (buffer.zig)

  • Extended Cell struct with fg_color_type, bg_color_type, fg_index, bg_index
  • Added corresponding arrays to OptimizedBuffer
  • Updated all buffer operations to handle color types

Zig (renderer.zig)

  • Updated color output to check cell color type
  • Outputs \x1b[38;5;Nm / \x1b[48;5;Nm for indexed colors
  • Outputs \x1b[39m / \x1b[49m for default colors
  • Falls back to RGB for rgb color type

Usage Examples

Indexed Colors

import { parseColor, RGBA } from "@opentui/core"

// Various ways to specify indexed colors
parseColor("ansi:196")      // String format (bright red)
parseColor(46)              // Numeric (green)
parseColor({ index: 226 })  // Object (yellow)
RGBA.fromIndex(21)          // Direct method (blue)

// Check color type
const color = parseColor("ansi:196")
color.isIndexed()  // true
color.index        // 196

OSC Palette Control

// Dynamically modify terminal colors
paletteDetector.setPaletteColor(1, "#ff5555")  // Change color 1 to custom red
paletteDetector.setBackground("#1a1a2e")       // Set terminal background
paletteDetector.setForeground("#eaeaea")       // Set terminal foreground
paletteDetector.setCursorColor("#ff79c6")      // Set cursor color

// Reset to terminal defaults
paletteDetector.resetPaletteColor(1)           // Reset color 1
paletteDetector.resetBackground()              // Reset background
paletteDetector.resetForeground()              // Reset foreground

Testing

  • All 3057 existing tests pass
  • Build completes successfully

- Extend RGBA class with colorType, index properties and fromIndex() method
- Add parseColor() support for 'ansi:N' format, numeric indices, and {index: N} objects
- Add indexToApproximateRgb() for converting indexed colors to RGB approximations
- Extend Zig Cell structure with fg/bg color type and index tracking
- Add indexed color ANSI escape sequence output (ESC[38;5;Nm / ESC[48;5;Nm)
- Add default color support (ESC[39m / ESC[49m)
- Add terminal palette set/reset methods for OSC 4/10/11/12 color control
Copilot AI review requested due to automatic review settings January 20, 2026 14:41
@bresilla bresilla requested a review from Hona as a code owner January 20, 2026 14:41
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for terminal indexed colors (0-255) and OSC-based palette control to enable dynamic terminal color modification. However, the implementation has a critical architectural issue: the color type metadata (indexed vs RGB vs default) is not transmitted across the FFI boundary between TypeScript and Zig, rendering the indexed color feature non-functional in practice.

Changes:

  • Extended RGBA class with color type tracking (indexed, RGB, default) and added parsing support for numeric indices and "ansi:N" format
  • Added OSC palette control methods for dynamically setting and resetting terminal colors
  • Extended Zig buffer Cell struct with color type fields and updated renderer to output appropriate ANSI sequences based on color type

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/core/src/lib/RGBA.ts Added color type tracking, indexed color support, and extended ColorInput type
packages/core/src/lib/terminal-palette.ts Added OSC sequence methods for setting/resetting palette colors
packages/core/src/renderables/EditBufferRenderable.ts Updated type annotations to use expanded ColorInput type
packages/core/src/zig/ansi.zig Added ColorType enum, Color struct, and indexed color output functions
packages/core/src/zig/buffer.zig Extended Cell and OptimizedBuffer with color type fields and added setCellWithColorType method
packages/core/src/zig/renderer.zig Updated rendering logic to output indexed/default color ANSI codes based on cell color type
Comments suppressed due to low confidence (1)

packages/core/src/zig/buffer.zig:925

  • When creating Cell structs in drawChar and setCellWithAlphaBlending, the color type fields are not specified, causing them to default to .rgb with index 0. This means indexed and default colors passed from TypeScript will be converted to RGB during rendering. These methods need to either receive color type parameters or extract them from an extended FFI interface.
    pub fn drawChar(
        self: *OptimizedBuffer,
        char: u32,
        x: u32,
        y: u32,
        fg: RGBA,
        bg: RGBA,
        attributes: u32,
    ) !void {
        if (!self.isPointInScissor(@intCast(x), @intCast(y))) return;

        if (isRGBAWithAlpha(bg) or isRGBAWithAlpha(fg)) {
            try self.setCellWithAlphaBlending(x, y, char, fg, bg, attributes);
        } else {
            self.set(x, y, Cell{
                .char = char,
                .fg = fg,
                .bg = bg,
                .attributes = attributes,
            });
        }
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

… add tests

- Add FFI functions to pass color type + index metadata to Zig
- Update buffer.ts to use new FFI when colors have type info
- Add parseColor context param for correct default fg/bg handling
- Add hex validation in normalizeHex() with clear error messages
- Remove unused Zig Color struct
- Add tests for OSC palette methods and indexed color parsing
Keep colorType/index through styled text, box borders/fills, and renderer background so ANSI palette theming (e.g. pywal) works consistently.
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 12 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Normalize colorType/index before passing to Zig and add RGBA coverage for default/indexed inputs.
@msmps msmps added the core This relates to the core package label Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core This relates to the core package

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants