Skip to content

feat (add theme.ts): Centralized Framework-Agnostic Design Token and Animation Subsystem #2

Description

@JustineDevs

Context and Problem Statement

The GitLeap terminal user interface (TUI) and accompanying asset generators require a unified visual identity across multiple target platforms. The user experience depends on distinct branding constraints, including a "one gradient" rule (restricting gradient transitions strictly to wordmark sheens and progress bars) and immediate responsiveness to focus/diagnostic changes. 

Historically, UI styling and animation logic were tightly coupled to the component rendering layer through React/Ink primitives (like ). This design creates structural conflicts when porting core logic to alternative terminal engines, isolates long-running Map-Reduce event pipelines from synchronous React hook execution cycles, and risks unexpected performance drops or memory footprints inside a compiled command-line binary. 

We need a deterministic approach to handle design tokens, color interpolation, cell-based sheen animations, and scroll calculations without introducing framework-specific side effects. Furthermore, we must permanently resolve the framework overlap between React/Ink and Rezi by choosing a single rendering engine. 

Decision Drivers

  • Runtime Execution Performance: Real-time multi-pane interfaces displaying active AST code-slicing steps require high-frequency screen refreshes that must bypass Virtual DOM tree reconciliation.
  • Asynchronous Processing Alignment: UI render states must stream from background queues, cache states, and local terminal sockets cleanly without the risks of infinite re-render loops or out-of-order hook execution.
  • Framework Portability: Style data, color math, and viewport states must exist as raw TypeScript primitives entirely decoupled from any component engine.
  • Visual Fidelity 1:1 Consistency: The animated highlight sweep must behave identically whether computed frame-by-frame inside a live terminal process or pre-baked into static Markdown SVG code.
  • Low-Level Memory Efficiency: Avoid frequent allocations in high-refresh TUI tickers by separating mathematical interpolation steps from terminal canvas paint calls.

Considered Options

  1. Option 1: Inline Component Orchestration (React/Ink) – Maintain a virtual DOM ecosystem using React hook state tracking (useState, useEffect) and color transitions inside individual visual components.
  2. Option 2: Headless Global Subsystem with Native Rezi Rendering (Chosen) – Extract design definitions, hex-math functions, deterministic animation math, and array viewports into a zero-dependency, pure data layer, and commit to Rezi as the exclusive runtime rendering pipeline.

Decision Outcome

Chosen Option: Option 2: Headless Global Subsystem with Native Rezi Rendering

By selecting this architecture, we are explicitly deprecating and purging all instances of React/Ink from the GitLeap codebase. Standardizing on Rezi allows the interface to operate as a direct canvas model. The headless global subsystem isolates layout math from layout processing loops. The presentation layers only need to consume these stateless engines and pass the generated output byte-streams directly down to Rezi cell buffers. 

Architecture Specification & Modules

1. Design Tokens Engine (theme.ts)

Houses the plain-data schema maps and frame-agnostic color math. This module has zero component dependencies. 

typescript

/**
 * GitLeap Design Tokens - Core Visual Language (Plain Data Engine)
 * Framework-Agnostic, Zero-Dependency TUI Configuration
 */

export const COLOR = {
  // Brand Primitives (The "Leap")
  accent: '#00E5A3',       // Transmuter Cyan (The Leap Forward / Success State)
  primary: '#7C3AED',      // Pipeline Purple (Git/AI Integration Space)
  
  // Interface Typography & Canvas States
  textNormal: '#F8FAFC',   // Token Text (Crisp white for long readouts)
  textMuted: '#64748B',    // Diminished text noise (Paths, hashes, metrics)
  bgCanvas: '#0B0F19',     // Deep Terminal Canvas (Background base)
  
  // Diagnostic State Flags
  good: '#00E5A3',         // Success / Greenlit / Pristine Primitive
  warn: '#F59E0B',         // Worker Warning / Cache Miss / AST Self-Correction Loop
  bad: '#EF4444',          // Compile Error / Malicious Injection Blocked
} as const;

export const ICON = {
  success: '✓',
  prompt: '❯',
  pause: '⏸',
  spinner: '⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏', // Braille frame array sequence
  folder: '📂',
  primitive: '🔹',
  manifest: '⚙',
  guide: '📝',
  package: '📦',
} as const;

export const RULE = {
  borderFocused: COLOR.accent,    // Focus-aware active layout panel border
  borderUnfocused: '#1E293B',    // Muted Slicing Gray for layout segmentation
  dividerHorizontal: '#1E293B',  // Trivial rule separator
} as const;

export const GUTTER = {
  paddingLeft: 2,
  paddingRight: 2,
} as const;

export const SOURCE_STYLE = {
  agents: { tag: 'AGENT', hex: COLOR.primary },
  skills: { tag: 'SKILL', hex: COLOR.accent },
  manifests: { tag: 'JSON', hex: '#F59E0B' },
  tests: { tag: 'TEST', hex: '#38BDF8' },
} as const;

function hexToRgb(hex: string): { r: number; g: number; b: number } {
  const sanitized = hex.replace(/^#/, '');
  const num = parseInt(sanitized, 16);
  return {
    r: (num >> 16) & 255,
    g: (num >> 8) & 255,
    b: num & 255,
  };
}

function rgbToHex(r: number, g: number, b: number): string {
  return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
}

/**
 * Pure hex color linear interpolation (lerp) helper.
 * Computes intermediate hexadecimal colors for granular UI gradient ramps.
 */
export function lerpHex(fromHex: string, toHex: string, alpha: number): string {
  const clampedAlpha = Math.max(0, Math.min(1, alpha));
  const start = hexToRgb(fromHex);
  const end = hexToRgb(toHex);

  const r = Math.round(start.r + (end.r - start.r) * clampedAlpha);
  const g = Math.round(start.g + (end.g - start.g) * clampedAlpha);
  const b = Math.round(start.b + (end.b - start.b) * clampedAlpha);

  return rgbToHex(r, g, b);
}

Use code with caution.

### 2. Sheen Animation Mathematical Engine (sheen.ts)

Enforces the "one gradient" constraint via a stateless 1D cosine-bell sweep over cell indices, mapping identically onto active progress components and SVG string outputs. 

typescript

/**
 * GitLeap Brand Animation Primitives - The "One Gradient" Rule Engine
 * Shared deterministic module mapping a 1D cosine-bell sweep over cell matrices.
 */

export const SHEEN_CONFIG = {
  SHEEN_PEAK: 1.0,       // Absolute peak luminescent intensity multiplier
  SHEEN_RADIUS: 6.0,     // Total radius range of the gradient bell sweep across adjacent cells
  SHEEN_TICK_MS: 50,     // Fixed ticker loop processing step timing
  SHEEN_SPEED: 0.4,      // Progression step distance velocity per tick interval
  SHEEN_MAX: 100,        // Total logical period normalization ceiling boundary
} as const;

export interface SheenState {
  sheenPeriod: number;
  sheenCenter: number;
  sheenIntensity: number;
}

/**
 * Computes the spatial location vectors for a single sheen tick iteration loop.
 */
export function calculateSheenStep(tickCounter: number, totalWidth: number): SheenState {
  const normalizedPeriod = SHEEN_CONFIG.SHEEN_MAX;
  const currentStep = (tickCounter * SHEEN_CONFIG.SHEEN_SPEED) % normalizedPeriod;
  const centerPosition = (currentStep / normalizedPeriod) * (totalWidth + SHEEN_CONFIG.SHEEN_RADIUS * 2) - SHEEN_CONFIG.SHEEN_RADIUS;

  return {
    sheenPeriod: normalizedPeriod,
    sheenCenter: centerPosition,
    sheenIntensity: SHEEN_CONFIG.SHEEN_PEAK,
  };
}

/**
 * Evaluates the precise intensity modifier for an individual text-cell position index.
 * Equation: 0.5 * (1 + cos(pi * distance / radius))
 */
export function getCellSheenFactor(cellIndex: number, sheenCenter: number): number {
  const distance = Math.abs(cellIndex - sheenCenter);
  
  if (distance >= SHEEN_CONFIG.SHEEN_RADIUS) {
    return 0;
  }

  const normalizedDistance = distance / SHEEN_CONFIG.SHEEN_RADIUS;
  const bellFactor = 0.5 * (1 + Math.cos(Math.PI * normalizedDistance));

  return bellFactor * SHEEN_CONFIG.SHEEN_PEAK;
}

export function applySheenToColor(baseHex: string, highlightHex: string, sheenFactor: number): string {
  return lerpHex(baseHex, highlightHex, sheenFactor);
}

Use code with caution.

### 3. List Navigation & Window Math (move.ts)

Decoupled layout calculation algorithms that calculate scrolling adjustments independently of screen size or rendering mechanics. 

typescript

/**
 * GitLeap Pure UI Utilities - Scroll Window and Cursor Processing Math
 * Framework-agnostic navigation layers with zero rendering bindings.
 */

/**
 * Circular navigation array cursor calculation.
 */
export function wrapStep(currentIndex: number, stepDirection: -1 | 1, totalLength: number): number {
  if (totalLength <= 0) return 0;
  const nextIndex = currentIndex + stepDirection;
  return (nextIndex + totalLength) % totalLength;
}

/**
 * Computes a sliding window viewport starting index offset point.
 * Ensures the selected list cursor element remains dynamically centered inside a viewport pane.
 */
export function windowStart(
  currentIndex: number,
  currentWindowStart: number,
  viewportHeight: number,
  totalLength: number
): number {
  if (totalLength <= viewportHeight) {
    return 0;
  }

  if (currentIndex < currentWindowStart) {
    return currentIndex;
  }

  if (currentIndex >= currentWindowStart + viewportHeight) {
    return currentIndex - viewportHeight + 1;
  }

  return currentWindowStart;
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions