@agent-webui/ai-desk-harness-gimp 1.0.29-beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +4 -0
  2. package/manifest.json +22 -0
  3. package/package.json +11 -0
  4. package/python/agent-harness/GIMP.md +301 -0
  5. package/python/agent-harness/build/lib/cli_anything/gimp/__init__.py +1 -0
  6. package/python/agent-harness/build/lib/cli_anything/gimp/__main__.py +3 -0
  7. package/python/agent-harness/build/lib/cli_anything/gimp/core/__init__.py +1 -0
  8. package/python/agent-harness/build/lib/cli_anything/gimp/core/canvas.py +193 -0
  9. package/python/agent-harness/build/lib/cli_anything/gimp/core/export.py +479 -0
  10. package/python/agent-harness/build/lib/cli_anything/gimp/core/filters.py +382 -0
  11. package/python/agent-harness/build/lib/cli_anything/gimp/core/layers.py +249 -0
  12. package/python/agent-harness/build/lib/cli_anything/gimp/core/media.py +174 -0
  13. package/python/agent-harness/build/lib/cli_anything/gimp/core/project.py +131 -0
  14. package/python/agent-harness/build/lib/cli_anything/gimp/core/session.py +130 -0
  15. package/python/agent-harness/build/lib/cli_anything/gimp/gimp_cli.py +788 -0
  16. package/python/agent-harness/build/lib/cli_anything/gimp/tests/__init__.py +1 -0
  17. package/python/agent-harness/build/lib/cli_anything/gimp/tests/test_core.py +478 -0
  18. package/python/agent-harness/build/lib/cli_anything/gimp/tests/test_full_e2e.py +578 -0
  19. package/python/agent-harness/build/lib/cli_anything/gimp/utils/__init__.py +1 -0
  20. package/python/agent-harness/build/lib/cli_anything/gimp/utils/gimp_backend.py +208 -0
  21. package/python/agent-harness/build/lib/cli_anything/gimp/utils/repl_skin.py +498 -0
  22. package/python/agent-harness/cli_anything/gimp/README.md +202 -0
  23. package/python/agent-harness/cli_anything/gimp/__init__.py +1 -0
  24. package/python/agent-harness/cli_anything/gimp/__main__.py +3 -0
  25. package/python/agent-harness/cli_anything/gimp/core/__init__.py +1 -0
  26. package/python/agent-harness/cli_anything/gimp/core/canvas.py +193 -0
  27. package/python/agent-harness/cli_anything/gimp/core/export.py +479 -0
  28. package/python/agent-harness/cli_anything/gimp/core/filters.py +382 -0
  29. package/python/agent-harness/cli_anything/gimp/core/layers.py +249 -0
  30. package/python/agent-harness/cli_anything/gimp/core/media.py +174 -0
  31. package/python/agent-harness/cli_anything/gimp/core/project.py +131 -0
  32. package/python/agent-harness/cli_anything/gimp/core/session.py +130 -0
  33. package/python/agent-harness/cli_anything/gimp/gimp_cli.py +788 -0
  34. package/python/agent-harness/cli_anything/gimp/tests/TEST.md +137 -0
  35. package/python/agent-harness/cli_anything/gimp/tests/__init__.py +1 -0
  36. package/python/agent-harness/cli_anything/gimp/tests/test_core.py +478 -0
  37. package/python/agent-harness/cli_anything/gimp/tests/test_full_e2e.py +578 -0
  38. package/python/agent-harness/cli_anything/gimp/utils/__init__.py +1 -0
  39. package/python/agent-harness/cli_anything/gimp/utils/gimp_backend.py +208 -0
  40. package/python/agent-harness/cli_anything/gimp/utils/repl_skin.py +498 -0
  41. package/python/agent-harness/cli_anything_gimp.egg-info/PKG-INFO +236 -0
  42. package/python/agent-harness/cli_anything_gimp.egg-info/SOURCES.txt +25 -0
  43. package/python/agent-harness/cli_anything_gimp.egg-info/dependency_links.txt +1 -0
  44. package/python/agent-harness/cli_anything_gimp.egg-info/entry_points.txt +2 -0
  45. package/python/agent-harness/cli_anything_gimp.egg-info/not-zip-safe +1 -0
  46. package/python/agent-harness/cli_anything_gimp.egg-info/requires.txt +7 -0
  47. package/python/agent-harness/cli_anything_gimp.egg-info/top_level.txt +1 -0
  48. package/python/agent-harness/setup.py +54 -0
@@ -0,0 +1,498 @@
1
+ """cli-anything REPL Skin — Unified terminal interface for all CLI harnesses.
2
+
3
+ Copy this file into your CLI package at:
4
+ cli_anything/<software>/utils/repl_skin.py
5
+
6
+ Usage:
7
+ from cli_anything.<software>.utils.repl_skin import ReplSkin
8
+
9
+ skin = ReplSkin("shotcut", version="1.0.0")
10
+ skin.print_banner()
11
+ prompt_text = skin.prompt(project_name="my_video.mlt", modified=True)
12
+ skin.success("Project saved")
13
+ skin.error("File not found")
14
+ skin.warning("Unsaved changes")
15
+ skin.info("Processing 24 clips...")
16
+ skin.status("Track 1", "3 clips, 00:02:30")
17
+ skin.table(headers, rows)
18
+ skin.print_goodbye()
19
+ """
20
+
21
+ import os
22
+ import sys
23
+
24
+ # ── ANSI color codes (no external deps for core styling) ──────────────
25
+
26
+ _RESET = "\033[0m"
27
+ _BOLD = "\033[1m"
28
+ _DIM = "\033[2m"
29
+ _ITALIC = "\033[3m"
30
+ _UNDERLINE = "\033[4m"
31
+
32
+ # Brand colors
33
+ _CYAN = "\033[38;5;80m" # cli-anything brand cyan
34
+ _CYAN_BG = "\033[48;5;80m"
35
+ _WHITE = "\033[97m"
36
+ _GRAY = "\033[38;5;245m"
37
+ _DARK_GRAY = "\033[38;5;240m"
38
+ _LIGHT_GRAY = "\033[38;5;250m"
39
+
40
+ # Software accent colors — each software gets a unique accent
41
+ _ACCENT_COLORS = {
42
+ "gimp": "\033[38;5;214m", # warm orange
43
+ "blender": "\033[38;5;208m", # deep orange
44
+ "inkscape": "\033[38;5;39m", # bright blue
45
+ "audacity": "\033[38;5;33m", # navy blue
46
+ "libreoffice": "\033[38;5;40m", # green
47
+ "obs_studio": "\033[38;5;55m", # purple
48
+ "kdenlive": "\033[38;5;69m", # slate blue
49
+ "shotcut": "\033[38;5;35m", # teal green
50
+ }
51
+ _DEFAULT_ACCENT = "\033[38;5;75m" # default sky blue
52
+
53
+ # Status colors
54
+ _GREEN = "\033[38;5;78m"
55
+ _YELLOW = "\033[38;5;220m"
56
+ _RED = "\033[38;5;196m"
57
+ _BLUE = "\033[38;5;75m"
58
+ _MAGENTA = "\033[38;5;176m"
59
+
60
+ # ── Brand icon ────────────────────────────────────────────────────────
61
+
62
+ # The cli-anything icon: a small colored diamond/chevron mark
63
+ _ICON = f"{_CYAN}{_BOLD}◆{_RESET}"
64
+ _ICON_SMALL = f"{_CYAN}▸{_RESET}"
65
+
66
+ # ── Box drawing characters ────────────────────────────────────────────
67
+
68
+ _H_LINE = "─"
69
+ _V_LINE = "│"
70
+ _TL = "╭"
71
+ _TR = "╮"
72
+ _BL = "╰"
73
+ _BR = "╯"
74
+ _T_DOWN = "┬"
75
+ _T_UP = "┴"
76
+ _T_RIGHT = "├"
77
+ _T_LEFT = "┤"
78
+ _CROSS = "┼"
79
+
80
+
81
+ def _strip_ansi(text: str) -> str:
82
+ """Remove ANSI escape codes for length calculation."""
83
+ import re
84
+ return re.sub(r"\033\[[^m]*m", "", text)
85
+
86
+
87
+ def _visible_len(text: str) -> int:
88
+ """Get visible length of text (excluding ANSI codes)."""
89
+ return len(_strip_ansi(text))
90
+
91
+
92
+ class ReplSkin:
93
+ """Unified REPL skin for cli-anything CLIs.
94
+
95
+ Provides consistent branding, prompts, and message formatting
96
+ across all CLI harnesses built with the cli-anything methodology.
97
+ """
98
+
99
+ def __init__(self, software: str, version: str = "1.0.0",
100
+ history_file: str | None = None):
101
+ """Initialize the REPL skin.
102
+
103
+ Args:
104
+ software: Software name (e.g., "gimp", "shotcut", "blender").
105
+ version: CLI version string.
106
+ history_file: Path for persistent command history.
107
+ Defaults to ~/.cli-anything-<software>/history
108
+ """
109
+ self.software = software.lower().replace("-", "_")
110
+ self.display_name = software.replace("_", " ").title()
111
+ self.version = version
112
+ self.accent = _ACCENT_COLORS.get(self.software, _DEFAULT_ACCENT)
113
+
114
+ # History file
115
+ if history_file is None:
116
+ from pathlib import Path
117
+ hist_dir = Path.home() / f".cli-anything-{self.software}"
118
+ hist_dir.mkdir(parents=True, exist_ok=True)
119
+ self.history_file = str(hist_dir / "history")
120
+ else:
121
+ self.history_file = history_file
122
+
123
+ # Detect terminal capabilities
124
+ self._color = self._detect_color_support()
125
+
126
+ def _detect_color_support(self) -> bool:
127
+ """Check if terminal supports color."""
128
+ if os.environ.get("NO_COLOR"):
129
+ return False
130
+ if os.environ.get("CLI_ANYTHING_NO_COLOR"):
131
+ return False
132
+ if not hasattr(sys.stdout, "isatty"):
133
+ return False
134
+ return sys.stdout.isatty()
135
+
136
+ def _c(self, code: str, text: str) -> str:
137
+ """Apply color code if colors are supported."""
138
+ if not self._color:
139
+ return text
140
+ return f"{code}{text}{_RESET}"
141
+
142
+ # ── Banner ────────────────────────────────────────────────────────
143
+
144
+ def print_banner(self):
145
+ """Print the startup banner with branding."""
146
+ inner = 54
147
+
148
+ def _box_line(content: str) -> str:
149
+ """Wrap content in box drawing, padding to inner width."""
150
+ pad = inner - _visible_len(content)
151
+ vl = self._c(_DARK_GRAY, _V_LINE)
152
+ return f"{vl}{content}{' ' * max(0, pad)}{vl}"
153
+
154
+ top = self._c(_DARK_GRAY, f"{_TL}{_H_LINE * inner}{_TR}")
155
+ bot = self._c(_DARK_GRAY, f"{_BL}{_H_LINE * inner}{_BR}")
156
+
157
+ # Title: ◆ cli-anything · Shotcut
158
+ icon = self._c(_CYAN + _BOLD, "◆")
159
+ brand = self._c(_CYAN + _BOLD, "cli-anything")
160
+ dot = self._c(_DARK_GRAY, "·")
161
+ name = self._c(self.accent + _BOLD, self.display_name)
162
+ title = f" {icon} {brand} {dot} {name}"
163
+
164
+ ver = f" {self._c(_DARK_GRAY, f' v{self.version}')}"
165
+ tip = f" {self._c(_DARK_GRAY, ' Type help for commands, quit to exit')}"
166
+ empty = ""
167
+
168
+ print(top)
169
+ print(_box_line(title))
170
+ print(_box_line(ver))
171
+ print(_box_line(empty))
172
+ print(_box_line(tip))
173
+ print(bot)
174
+ print()
175
+
176
+ # ── Prompt ────────────────────────────────────────────────────────
177
+
178
+ def prompt(self, project_name: str = "", modified: bool = False,
179
+ context: str = "") -> str:
180
+ """Build a styled prompt string for prompt_toolkit or input().
181
+
182
+ Args:
183
+ project_name: Current project name (empty if none open).
184
+ modified: Whether the project has unsaved changes.
185
+ context: Optional extra context to show in prompt.
186
+
187
+ Returns:
188
+ Formatted prompt string.
189
+ """
190
+ parts = []
191
+
192
+ # Icon
193
+ if self._color:
194
+ parts.append(f"{_CYAN}◆{_RESET} ")
195
+ else:
196
+ parts.append("> ")
197
+
198
+ # Software name
199
+ parts.append(self._c(self.accent + _BOLD, self.software))
200
+
201
+ # Project context
202
+ if project_name or context:
203
+ ctx = context or project_name
204
+ mod = "*" if modified else ""
205
+ parts.append(f" {self._c(_DARK_GRAY, '[')}")
206
+ parts.append(self._c(_LIGHT_GRAY, f"{ctx}{mod}"))
207
+ parts.append(self._c(_DARK_GRAY, ']'))
208
+
209
+ parts.append(self._c(_GRAY, " ❯ "))
210
+
211
+ return "".join(parts)
212
+
213
+ def prompt_tokens(self, project_name: str = "", modified: bool = False,
214
+ context: str = ""):
215
+ """Build prompt_toolkit formatted text tokens for the prompt.
216
+
217
+ Use with prompt_toolkit's FormattedText for proper ANSI handling.
218
+
219
+ Returns:
220
+ list of (style, text) tuples for prompt_toolkit.
221
+ """
222
+ accent_hex = _ANSI_256_TO_HEX.get(self.accent, "#5fafff")
223
+ tokens = []
224
+
225
+ tokens.append(("class:icon", "◆ "))
226
+ tokens.append(("class:software", self.software))
227
+
228
+ if project_name or context:
229
+ ctx = context or project_name
230
+ mod = "*" if modified else ""
231
+ tokens.append(("class:bracket", " ["))
232
+ tokens.append(("class:context", f"{ctx}{mod}"))
233
+ tokens.append(("class:bracket", "]"))
234
+
235
+ tokens.append(("class:arrow", " ❯ "))
236
+
237
+ return tokens
238
+
239
+ def get_prompt_style(self):
240
+ """Get a prompt_toolkit Style object matching the skin.
241
+
242
+ Returns:
243
+ prompt_toolkit.styles.Style
244
+ """
245
+ try:
246
+ from prompt_toolkit.styles import Style
247
+ except ImportError:
248
+ return None
249
+
250
+ accent_hex = _ANSI_256_TO_HEX.get(self.accent, "#5fafff")
251
+
252
+ return Style.from_dict({
253
+ "icon": "#5fdfdf bold", # cyan brand color
254
+ "software": f"{accent_hex} bold",
255
+ "bracket": "#585858",
256
+ "context": "#bcbcbc",
257
+ "arrow": "#808080",
258
+ # Completion menu
259
+ "completion-menu.completion": "bg:#303030 #bcbcbc",
260
+ "completion-menu.completion.current": f"bg:{accent_hex} #000000",
261
+ "completion-menu.meta.completion": "bg:#303030 #808080",
262
+ "completion-menu.meta.completion.current": f"bg:{accent_hex} #000000",
263
+ # Auto-suggest
264
+ "auto-suggest": "#585858",
265
+ # Bottom toolbar
266
+ "bottom-toolbar": "bg:#1c1c1c #808080",
267
+ "bottom-toolbar.text": "#808080",
268
+ })
269
+
270
+ # ── Messages ──────────────────────────────────────────────────────
271
+
272
+ def success(self, message: str):
273
+ """Print a success message with green checkmark."""
274
+ icon = self._c(_GREEN + _BOLD, "✓")
275
+ print(f" {icon} {self._c(_GREEN, message)}")
276
+
277
+ def error(self, message: str):
278
+ """Print an error message with red cross."""
279
+ icon = self._c(_RED + _BOLD, "✗")
280
+ print(f" {icon} {self._c(_RED, message)}", file=sys.stderr)
281
+
282
+ def warning(self, message: str):
283
+ """Print a warning message with yellow triangle."""
284
+ icon = self._c(_YELLOW + _BOLD, "⚠")
285
+ print(f" {icon} {self._c(_YELLOW, message)}")
286
+
287
+ def info(self, message: str):
288
+ """Print an info message with blue dot."""
289
+ icon = self._c(_BLUE, "●")
290
+ print(f" {icon} {self._c(_LIGHT_GRAY, message)}")
291
+
292
+ def hint(self, message: str):
293
+ """Print a subtle hint message."""
294
+ print(f" {self._c(_DARK_GRAY, message)}")
295
+
296
+ def section(self, title: str):
297
+ """Print a section header."""
298
+ print()
299
+ print(f" {self._c(self.accent + _BOLD, title)}")
300
+ print(f" {self._c(_DARK_GRAY, _H_LINE * len(title))}")
301
+
302
+ # ── Status display ────────────────────────────────────────────────
303
+
304
+ def status(self, label: str, value: str):
305
+ """Print a key-value status line."""
306
+ lbl = self._c(_GRAY, f" {label}:")
307
+ val = self._c(_WHITE, f" {value}")
308
+ print(f"{lbl}{val}")
309
+
310
+ def status_block(self, items: dict[str, str], title: str = ""):
311
+ """Print a block of status key-value pairs.
312
+
313
+ Args:
314
+ items: Dict of label -> value pairs.
315
+ title: Optional title for the block.
316
+ """
317
+ if title:
318
+ self.section(title)
319
+
320
+ max_key = max(len(k) for k in items) if items else 0
321
+ for label, value in items.items():
322
+ lbl = self._c(_GRAY, f" {label:<{max_key}}")
323
+ val = self._c(_WHITE, f" {value}")
324
+ print(f"{lbl}{val}")
325
+
326
+ def progress(self, current: int, total: int, label: str = ""):
327
+ """Print a simple progress indicator.
328
+
329
+ Args:
330
+ current: Current step number.
331
+ total: Total number of steps.
332
+ label: Optional label for the progress.
333
+ """
334
+ pct = int(current / total * 100) if total > 0 else 0
335
+ bar_width = 20
336
+ filled = int(bar_width * current / total) if total > 0 else 0
337
+ bar = "█" * filled + "░" * (bar_width - filled)
338
+ text = f" {self._c(_CYAN, bar)} {self._c(_GRAY, f'{pct:3d}%')}"
339
+ if label:
340
+ text += f" {self._c(_LIGHT_GRAY, label)}"
341
+ print(text)
342
+
343
+ # ── Table display ─────────────────────────────────────────────────
344
+
345
+ def table(self, headers: list[str], rows: list[list[str]],
346
+ max_col_width: int = 40):
347
+ """Print a formatted table with box-drawing characters.
348
+
349
+ Args:
350
+ headers: Column header strings.
351
+ rows: List of rows, each a list of cell strings.
352
+ max_col_width: Maximum column width before truncation.
353
+ """
354
+ if not headers:
355
+ return
356
+
357
+ # Calculate column widths
358
+ col_widths = [min(len(h), max_col_width) for h in headers]
359
+ for row in rows:
360
+ for i, cell in enumerate(row):
361
+ if i < len(col_widths):
362
+ col_widths[i] = min(
363
+ max(col_widths[i], len(str(cell))), max_col_width
364
+ )
365
+
366
+ def pad(text: str, width: int) -> str:
367
+ t = str(text)[:width]
368
+ return t + " " * (width - len(t))
369
+
370
+ # Header
371
+ header_cells = [
372
+ self._c(_CYAN + _BOLD, pad(h, col_widths[i]))
373
+ for i, h in enumerate(headers)
374
+ ]
375
+ sep = self._c(_DARK_GRAY, f" {_V_LINE} ")
376
+ header_line = f" {sep.join(header_cells)}"
377
+ print(header_line)
378
+
379
+ # Separator
380
+ sep_parts = [self._c(_DARK_GRAY, _H_LINE * w) for w in col_widths]
381
+ sep_line = self._c(_DARK_GRAY, f" {'───'.join([_H_LINE * w for w in col_widths])}")
382
+ print(sep_line)
383
+
384
+ # Rows
385
+ for row in rows:
386
+ cells = []
387
+ for i, cell in enumerate(row):
388
+ if i < len(col_widths):
389
+ cells.append(self._c(_LIGHT_GRAY, pad(str(cell), col_widths[i])))
390
+ row_sep = self._c(_DARK_GRAY, f" {_V_LINE} ")
391
+ print(f" {row_sep.join(cells)}")
392
+
393
+ # ── Help display ──────────────────────────────────────────────────
394
+
395
+ def help(self, commands: dict[str, str]):
396
+ """Print a formatted help listing.
397
+
398
+ Args:
399
+ commands: Dict of command -> description pairs.
400
+ """
401
+ self.section("Commands")
402
+ max_cmd = max(len(c) for c in commands) if commands else 0
403
+ for cmd, desc in commands.items():
404
+ cmd_styled = self._c(self.accent, f" {cmd:<{max_cmd}}")
405
+ desc_styled = self._c(_GRAY, f" {desc}")
406
+ print(f"{cmd_styled}{desc_styled}")
407
+ print()
408
+
409
+ # ── Goodbye ───────────────────────────────────────────────────────
410
+
411
+ def print_goodbye(self):
412
+ """Print a styled goodbye message."""
413
+ print(f"\n {_ICON_SMALL} {self._c(_GRAY, 'Goodbye!')}\n")
414
+
415
+ # ── Prompt toolkit session factory ────────────────────────────────
416
+
417
+ def create_prompt_session(self):
418
+ """Create a prompt_toolkit PromptSession with skin styling.
419
+
420
+ Returns:
421
+ A configured PromptSession, or None if prompt_toolkit unavailable.
422
+ """
423
+ try:
424
+ from prompt_toolkit import PromptSession
425
+ from prompt_toolkit.history import FileHistory
426
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
427
+ from prompt_toolkit.formatted_text import FormattedText
428
+
429
+ style = self.get_prompt_style()
430
+
431
+ session = PromptSession(
432
+ history=FileHistory(self.history_file),
433
+ auto_suggest=AutoSuggestFromHistory(),
434
+ style=style,
435
+ enable_history_search=True,
436
+ )
437
+ return session
438
+ except ImportError:
439
+ return None
440
+
441
+ def get_input(self, pt_session, project_name: str = "",
442
+ modified: bool = False, context: str = "") -> str:
443
+ """Get input from user using prompt_toolkit or fallback.
444
+
445
+ Args:
446
+ pt_session: A prompt_toolkit PromptSession (or None).
447
+ project_name: Current project name.
448
+ modified: Whether project has unsaved changes.
449
+ context: Optional context string.
450
+
451
+ Returns:
452
+ User input string (stripped).
453
+ """
454
+ if pt_session is not None:
455
+ from prompt_toolkit.formatted_text import FormattedText
456
+ tokens = self.prompt_tokens(project_name, modified, context)
457
+ return pt_session.prompt(FormattedText(tokens)).strip()
458
+ else:
459
+ raw_prompt = self.prompt(project_name, modified, context)
460
+ return input(raw_prompt).strip()
461
+
462
+ # ── Toolbar builder ───────────────────────────────────────────────
463
+
464
+ def bottom_toolbar(self, items: dict[str, str]):
465
+ """Create a bottom toolbar callback for prompt_toolkit.
466
+
467
+ Args:
468
+ items: Dict of label -> value pairs to show in toolbar.
469
+
470
+ Returns:
471
+ A callable that returns FormattedText for the toolbar.
472
+ """
473
+ def toolbar():
474
+ from prompt_toolkit.formatted_text import FormattedText
475
+ parts = []
476
+ for i, (k, v) in enumerate(items.items()):
477
+ if i > 0:
478
+ parts.append(("class:bottom-toolbar.text", " │ "))
479
+ parts.append(("class:bottom-toolbar.text", f" {k}: "))
480
+ parts.append(("class:bottom-toolbar", v))
481
+ return FormattedText(parts)
482
+ return toolbar
483
+
484
+
485
+ # ── ANSI 256-color to hex mapping (for prompt_toolkit styles) ─────────
486
+
487
+ _ANSI_256_TO_HEX = {
488
+ "\033[38;5;33m": "#0087ff", # audacity navy blue
489
+ "\033[38;5;35m": "#00af5f", # shotcut teal
490
+ "\033[38;5;39m": "#00afff", # inkscape bright blue
491
+ "\033[38;5;40m": "#00d700", # libreoffice green
492
+ "\033[38;5;55m": "#5f00af", # obs purple
493
+ "\033[38;5;69m": "#5f87ff", # kdenlive slate blue
494
+ "\033[38;5;75m": "#5fafff", # default sky blue
495
+ "\033[38;5;80m": "#5fd7d7", # brand cyan
496
+ "\033[38;5;208m": "#ff8700", # blender deep orange
497
+ "\033[38;5;214m": "#ffaf00", # gimp warm orange
498
+ }