|
| 1 | +# Theme Migration Guide: Java <-> Python |
| 2 | + |
| 3 | +This guide covers converting themes between [curses-java](https://github.com/FlossWare/curses-java) (Java) and [curses-themes](https://github.com/FlossWare/curses-themes) (Python). |
| 4 | + |
| 5 | +## Java Theme -> Python |
| 6 | + |
| 7 | +**No manual work required.** Python auto-detects Java JSON format and converts on load: |
| 8 | + |
| 9 | +```python |
| 10 | +from curses_themes import ThemeManager |
| 11 | + |
| 12 | +# Load any Java-exported JSON theme directly |
| 13 | +theme = ThemeManager.load_from_file("themes/borland3d.json") |
| 14 | +theme.apply(stdscr) |
| 15 | +``` |
| 16 | + |
| 17 | +The adapter handles all format differences (color names, border ordering, 3D keys) transparently. |
| 18 | + |
| 19 | +## Python Theme -> Java |
| 20 | + |
| 21 | +Manual conversion is needed. Create a JSON file matching the [schema](../themes/schema.json): |
| 22 | + |
| 23 | +```json |
| 24 | +{ |
| 25 | + "name": "My Theme", |
| 26 | + "version": "1.0", |
| 27 | + "description": "Theme description", |
| 28 | + "colors": { |
| 29 | + "background": { "fg": "WHITE", "bg": "BLACK" }, |
| 30 | + "button": { "fg": "CYAN", "bg": "BLACK" }, |
| 31 | + "button_focused": { "fg": "BLACK", "bg": "CYAN" }, |
| 32 | + "text_input": { "fg": "GREEN", "bg": "BLACK" }, |
| 33 | + "border": { "fg": "WHITE", "bg": "BLACK" }, |
| 34 | + "selection": { "fg": "BLACK", "bg": "WHITE" }, |
| 35 | + "disabled": { "fg": "BLACK", "bg": "BLACK" } |
| 36 | + }, |
| 37 | + "borders": { |
| 38 | + "single": "+-+||+-+" |
| 39 | + } |
| 40 | +} |
| 41 | +``` |
| 42 | + |
| 43 | +Then load in Java: |
| 44 | + |
| 45 | +```java |
| 46 | +ThemeManager.getInstance().loadThemeFromJson("path/to/my-theme.json"); |
| 47 | +``` |
| 48 | + |
| 49 | +### Converting Python RGB to Java Color Names |
| 50 | + |
| 51 | +Map your Python RGB values to the nearest ncurses name: |
| 52 | + |
| 53 | +| ncurses Name | RGB Equivalent | |
| 54 | +|-------------|----------------| |
| 55 | +| `BLACK` | (0, 0, 0) | |
| 56 | +| `RED` | (255, 0, 0) | |
| 57 | +| `GREEN` | (0, 255, 0) | |
| 58 | +| `YELLOW` | (255, 255, 0) | |
| 59 | +| `BLUE` | (0, 0, 255) | |
| 60 | +| `MAGENTA` | (255, 0, 255) | |
| 61 | +| `CYAN` | (0, 255, 255) | |
| 62 | +| `WHITE` | (255, 255, 255)| |
| 63 | + |
| 64 | +## Key Differences |
| 65 | + |
| 66 | +| Aspect | Java (curses-java) | Python (curses-themes) | |
| 67 | +|--------|-------------------|----------------------| |
| 68 | +| **Color values** | ncurses names: `"CYAN"`, `"BLUE"` | RGB tuples: `(0, 255, 255)` | |
| 69 | +| **Border char order** | TL, T, TR, L, **BL, B, BR**, R | TL, T, TR, L, **R, BL, B**, BR | |
| 70 | +| **3D shadow key** | `shadow_color` | `shadow` | |
| 71 | +| **3D highlight key** | `highlight_color` | `highlight` | |
| 72 | +| **3D lowlight key** | `lowlight_color` | `lowlight` | |
| 73 | +| **3D offset** | `shadow_offset: { "x": 2, "y": 1 }` | `shadow_offset_x: 2`, `shadow_offset_y: 1` | |
| 74 | +| **Color structure** | `colors` object with component pairs | `colors` (semantic) + `components` (pairs) | |
| 75 | + |
| 76 | +### Border Character Order Detail |
| 77 | + |
| 78 | +Java uses an 8-character string: `TL T TR L BL B BR R` |
| 79 | + |
| 80 | +``` |
| 81 | +Position: 0 1 2 3 4 5 6 7 |
| 82 | +Java: TL T TR L BL B BR R |
| 83 | +Python: TL T TR L R BL B BR |
| 84 | +``` |
| 85 | + |
| 86 | +Example -- the string `"+-+||+-+"`: |
| 87 | +- **Java reads:** `+` `-` `+` `|` `|` `+` `-` `+` (positions 4-5 = BL, B) |
| 88 | +- **Python reads:** `+` `-` `+` `|` `|` `+` `-` `+` (positions 4-5 = R, BL) |
| 89 | + |
| 90 | +The Python adapter swaps positions 4/7 and 5/6 automatically when loading Java JSON. |
| 91 | + |
| 92 | +## Creating Themes That Work in Both |
| 93 | + |
| 94 | +Write your theme in **Java JSON format** (the source-of-truth format). Both libraries can consume it: |
| 95 | + |
| 96 | +- **Java**: loads it natively via `loadThemeFromJson()` |
| 97 | +- **Python**: auto-detects and converts via `load_from_file()` |
| 98 | + |
| 99 | +Minimal example: |
| 100 | + |
| 101 | +```json |
| 102 | +{ |
| 103 | + "name": "Shared Theme", |
| 104 | + "version": "1.0", |
| 105 | + "colors": { |
| 106 | + "background": { "fg": "WHITE", "bg": "BLACK" }, |
| 107 | + "button": { "fg": "CYAN", "bg": "BLACK" }, |
| 108 | + "button_focused": { "fg": "BLACK", "bg": "CYAN" }, |
| 109 | + "text_input": { "fg": "GREEN", "bg": "BLACK" }, |
| 110 | + "border": { "fg": "WHITE", "bg": "BLACK" }, |
| 111 | + "selection": { "fg": "BLACK", "bg": "WHITE" }, |
| 112 | + "disabled": { "fg": "BLACK", "bg": "BLACK" } |
| 113 | + }, |
| 114 | + "borders": { |
| 115 | + "single": "+-+||+-+" |
| 116 | + } |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +For 3D support, add the `3d` block: |
| 121 | + |
| 122 | +```json |
| 123 | +{ |
| 124 | + "3d": { |
| 125 | + "shadow_color": { "fg": "BLACK", "bg": "BLACK" }, |
| 126 | + "highlight_color": { "fg": "WHITE", "bg": "CYAN" }, |
| 127 | + "lowlight_color": { "fg": "BLACK", "bg": "CYAN" }, |
| 128 | + "shadow_offset": { "x": 2, "y": 1 }, |
| 129 | + "rendering_style": "RAISED" |
| 130 | + } |
| 131 | +} |
| 132 | +``` |
| 133 | + |
| 134 | +## Schema Reference |
| 135 | + |
| 136 | +The shared JSON schema is at [`themes/schema.json`](../themes/schema.json). It defines: |
| 137 | + |
| 138 | +- **Required fields:** `name`, `version`, `colors`, `borders` |
| 139 | +- **Color values:** ncurses name strings or RGB arrays `[r, g, b]` |
| 140 | +- **Border strings:** exactly 8 characters (TL, T, TR, L, BL, B, BR, R) |
| 141 | +- **3D block:** optional, requires `shadow_color`, `highlight_color`, `lowlight_color`, `shadow_offset` |
| 142 | + |
| 143 | +Validate a theme file against the schema: |
| 144 | + |
| 145 | +```bash |
| 146 | +# Using ajv-cli |
| 147 | +npx ajv validate -s themes/schema.json -d themes/my-theme.json |
| 148 | + |
| 149 | +# Using Python jsonschema |
| 150 | +python3 -c " |
| 151 | +import json, jsonschema |
| 152 | +schema = json.load(open('themes/schema.json')) |
| 153 | +theme = json.load(open('themes/my-theme.json')) |
| 154 | +jsonschema.validate(theme, schema) |
| 155 | +print('Valid') |
| 156 | +" |
| 157 | +``` |
0 commit comments