Problem
We currently maintain two parallel implementations of the same theme system:
- curses-java (Java) - 192 source files, Java 21 FFM calling ncurses
- curses-themes (Python) - Pure Python calling ncurses via stdlib
Both have identical Theme3D interfaces and duplicate rendering logic (~500-600 lines):
- 11+ themes (Borland, Borland3D, DBase3, DBase4, DBase4_3D, DOS, TRS-80, TI-99/4A, etc.)
- Color management, 3D border rendering, shadow/highlight/lowlight logic
draw_box_3d() implementation (~125 lines each)
Pain point: Every new theme or rendering feature must be manually duplicated and kept in sync.
Multi-AI Analysis Results
Ran ai-prompt workflow with 3 AI models (Opus, Sonnet, Haiku) to evaluate solutions.
Consensus Level: HIGH (92% confidence)
Models Agreed: 3/3
🏆 Recommended Solutions (Ranked)
#1: Rust + C ABI with UniFFI ⭐⭐⭐⭐⭐
All 3 models ranked this #1
Approach:
- Write theme rendering logic once in Rust
- Compile to shared library (.so/.dylib/.dll)
- Java calls via FFM (same pattern as existing `NcursesBridge.java`)
- Python calls via ctypes (built-in)
- Mozilla's UniFFI auto-generates bindings from .udl interface
Why it wins:
- ✅ TRUE single implementation
- ✅ Already use FFM in Java (familiar pattern)
- ✅ Memory-safe (unlike C)
- ✅ Rust can call ncurses directly (ncurses-rs crate)
- ✅ Production-proven (Mozilla Firefox uses UniFFI)
- ✅ No C code required
Reference: https://github.com/shmuelamar/rs-py-java
Project structure:
```
curses-themes-core/ (Rust crate)
src/lib.rs -- exports extern "C" functions
src/theme.rs -- Theme/Theme3D traits
src/borland3d.rs -- Borland3DTheme
src/render.rs -- draw_box_3d, shadows
Cargo.toml -- depends on ncurses-rs
theme.udl -- UniFFI interface definition
```
Cons:
- ⚠️ Requires learning Rust
- ⚠️ Build complexity (Cargo + Maven + setuptools)
#2: Shared JSON Theme Data + Thin Rendering ⭐⭐⭐⭐
Pragmatic fallback - all 3 models agree viable
Approach:
- Extract all theme DATA (colors, border chars, shadow offsets) to JSON/YAML
- Keep thin rendering engine (~125 lines) in each language
- Both read same JSON files
Example:
```json
{
"name": "Borland 3D",
"border_chars": "+-+||+-+",
"colors": {
"shadow": {"fg": "BLACK", "bg": "BLACK"},
"highlight": {"fg": "WHITE", "bg": "CYAN"}
},
"shadow_offset": {"x": 2, "y": 1}
}
```
Why viable:
- The DATA is what's mostly duplicated (theme definitions)
- The rendering ALGORITHM is small (~125 lines) and stable
Pros:
- ✅ Zero new languages
- ✅ Simple, low risk
- ✅ Human-readable themes
- ✅ Users can create custom themes
Cons:
- ⚠️ Rendering logic still duplicated (~125 lines per language)
#3: GraalVM Native Image ⭐⭐⭐
If keeping Java as source of truth
- Compile Java to .so with `@CEntryPoint`
- Python calls via ctypes
Cons:
- ⚠️ 50-100MB binary (embeds JVM substrate)
- ⚠️ Complex toolchain
#4: Zig with C ABI ⭐⭐
Simpler than Rust, less mature ecosystem
❌ Eliminated Options
- ❌ C library - Rejected (don't want to write/maintain C)
- ❌ WebAssembly/WASI - No termios support, can't call ncurses
- ❌ Go c-shared - Embeds GC runtime (5-10MB overhead, per-call CGo cost)
- ❌ HTTP/IPC service - Too slow for terminal rendering (sub-ms latency needed)
- ❌ GraalVM polyglot - Already considered
- ❌ Jython - Limited to Python 2.7
🎯 Decision Needed
Option A: Learn Rust, build shared library (best long-term, true DRY)
Option B: Use JSON data files (easiest short-term, accept ~125 lines duplication)
Next Steps
- Decide approach: Rust vs JSON vs defer
- If Rust:
- Create `curses-themes-core` Rust crate
- Port Borland3DTheme as proof-of-concept
- Set up UniFFI bindings
- Update Java/Python to call .so
- If JSON:
- Create `curses-theme-specs` repo with JSON definitions
- Write JSON Schema for validation
- Update both projects to load from JSON
References
- Multi-AI analysis: 244K tokens, 92% consensus
- Java FFM already proven in `NcursesBridge.java`
- Both implementations have nearly identical `Theme3D` interfaces
- Reference project: https://github.com/shmuelamar/rs-py-java
Problem
We currently maintain two parallel implementations of the same theme system:
Both have identical Theme3D interfaces and duplicate rendering logic (~500-600 lines):
draw_box_3d()implementation (~125 lines each)Pain point: Every new theme or rendering feature must be manually duplicated and kept in sync.
Multi-AI Analysis Results
Ran ai-prompt workflow with 3 AI models (Opus, Sonnet, Haiku) to evaluate solutions.
Consensus Level: HIGH (92% confidence)
Models Agreed: 3/3
🏆 Recommended Solutions (Ranked)
#1: Rust + C ABI with UniFFI ⭐⭐⭐⭐⭐
All 3 models ranked this #1
Approach:
Why it wins:
Reference: https://github.com/shmuelamar/rs-py-java
Project structure:
```
curses-themes-core/ (Rust crate)
src/lib.rs -- exports extern "C" functions
src/theme.rs -- Theme/Theme3D traits
src/borland3d.rs -- Borland3DTheme
src/render.rs -- draw_box_3d, shadows
Cargo.toml -- depends on ncurses-rs
theme.udl -- UniFFI interface definition
```
Cons:
#2: Shared JSON Theme Data + Thin Rendering ⭐⭐⭐⭐
Pragmatic fallback - all 3 models agree viable
Approach:
Example:
```json
{
"name": "Borland 3D",
"border_chars": "+-+||+-+",
"colors": {
"shadow": {"fg": "BLACK", "bg": "BLACK"},
"highlight": {"fg": "WHITE", "bg": "CYAN"}
},
"shadow_offset": {"x": 2, "y": 1}
}
```
Why viable:
Pros:
Cons:
#3: GraalVM Native Image ⭐⭐⭐
If keeping Java as source of truth
Cons:
#4: Zig with C ABI ⭐⭐
Simpler than Rust, less mature ecosystem
❌ Eliminated Options
🎯 Decision Needed
Option A: Learn Rust, build shared library (best long-term, true DRY)
Option B: Use JSON data files (easiest short-term, accept ~125 lines duplication)
Next Steps
References