1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
9#![warn(unused_extern_crates)]
10#![warn(missing_docs)]
11
12zng_wgt::enable_widget_macros!();
13
14use zng_ext_font::*;
15use zng_wgt::{prelude::*, *};
16use zng_wgt_fill::*;
17use zng_wgt_filter::*;
18use zng_wgt_scroll::{LazyMode, lazy};
19use zng_wgt_stack::{Stack, StackDirection};
20use zng_wgt_text::*;
21
22#[doc(hidden)]
23pub use zng_wgt_text::__formatx;
24
25#[widget($crate::AnsiText {
29 ($txt:literal) => {
30 txt = $crate::__formatx!($txt);
31 };
32 ($txt:expr) => {
33 txt = $txt;
34 };
35 ($txt:tt, $($format:tt)*) => {
36 txt = $crate::__formatx!($txt, $($format)*);
37 };
38})]
39#[rustfmt::skip]
40pub struct AnsiText(
41 FontMix<
42 TextSpacingMix<
43 ParagraphMix<
44 LangMix<
45 WidgetBase
46 >>>>
47);
48impl AnsiText {
49 fn widget_intrinsic(&mut self) {
50 widget_set! {
51 self;
52 font_family = ["JetBrains Mono", "Consolas", "monospace"];
53 };
54
55 self.widget_builder().push_build_action(|wgt| {
56 let txt = wgt.capture_var_or_default(property_id!(txt));
57 let child = ansi_node(txt);
58 wgt.set_child(child.boxed());
59 });
60 }
61
62 widget_impl! {
63 pub txt(text: impl IntoVar<Txt>);
65 }
66}
67
68pub use ansi_parse::*;
69mod ansi_parse {
70
71 use super::*;
72
73 #[derive(Debug)]
75 pub struct AnsiTxt<'a> {
76 pub txt: &'a str,
78 pub style: AnsiStyle,
80 }
81
82 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
86 pub struct AnsiStyle {
87 pub background_color: AnsiColor,
89 pub color: AnsiColor,
91 pub weight: AnsiWeight,
93 pub italic: bool,
95 pub underline: bool,
97 pub strikethrough: bool,
99 pub invert_color: bool,
101 pub hidden: bool,
103 pub blink: bool,
105 }
106 impl Default for AnsiStyle {
107 fn default() -> Self {
108 Self {
109 background_color: AnsiColor::Black,
110 color: AnsiColor::White,
111 weight: Default::default(),
112 italic: false,
113 underline: false,
114 strikethrough: false,
115 invert_color: false,
116 hidden: false,
117 blink: false,
118 }
119 }
120 }
121
122 #[allow(missing_docs)]
126 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
127 pub enum AnsiColor {
128 Black,
129 Red,
130 Green,
131 Yellow,
132 Blue,
133 Magenta,
134 Cyan,
135 White,
136 BrightBlack,
138 BrightRed,
139 BrightGreen,
140 BrightYellow,
141 BrightBlue,
142 BrightMagenta,
143 BrightCyan,
144 BrightWhite,
145 Ansi256(u8),
147 TrueColor(u8, u8, u8),
149 }
150 impl_from_and_into_var! {
151 fn from(color: AnsiColor) -> Rgba {
152 match color {
153 AnsiColor::Black => rgb(0, 0, 0),
154 AnsiColor::Red => rgb(205, 49, 49),
155 AnsiColor::Green => rgb(13, 188, 121),
156 AnsiColor::Yellow => rgb(229, 229, 16),
157 AnsiColor::Blue => rgb(36, 114, 200),
158 AnsiColor::Magenta => rgb(188, 63, 188),
159 AnsiColor::Cyan => rgb(17, 168, 205),
160 AnsiColor::White => rgb(229, 229, 229),
161 AnsiColor::BrightBlack => rgb(102, 102, 102),
162 AnsiColor::BrightRed => rgb(241, 76, 76),
163 AnsiColor::BrightGreen => rgb(35, 209, 139),
164 AnsiColor::BrightYellow => rgb(245, 245, 67),
165 AnsiColor::BrightBlue => rgb(59, 142, 234),
166 AnsiColor::BrightMagenta => rgb(214, 112, 214),
167 AnsiColor::BrightCyan => rgb(41, 184, 219),
168 AnsiColor::BrightWhite => rgb(229, 229, 229),
169 AnsiColor::Ansi256(c) => {
170 let (r, g, b) = X_TERM_256[c as usize];
171 rgb(r, g, b)
172 }
173 AnsiColor::TrueColor(r, g, b) => rgb(r, g, b),
174 }
175 }
176 }
177
178 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
182 pub enum AnsiWeight {
183 Normal,
185 Bold,
187 Faint,
189 }
190 impl Default for AnsiWeight {
191 fn default() -> Self {
192 Self::Normal
193 }
194 }
195 impl_from_and_into_var! {
196 fn from(weight: AnsiWeight) -> FontWeight {
197 match weight {
198 AnsiWeight::Normal => FontWeight::NORMAL,
199 AnsiWeight::Bold => FontWeight::BOLD,
200 AnsiWeight::Faint => FontWeight::LIGHT,
201 }
202 }
203 }
204
205 pub struct AnsiTextParser<'a> {
211 source: &'a str,
212 pub style: AnsiStyle,
214 }
215 impl<'a> AnsiTextParser<'a> {
216 pub fn new(source: &'a str) -> Self {
218 Self {
219 source,
220 style: AnsiStyle::default(),
221 }
222 }
223 }
224 impl<'a> Iterator for AnsiTextParser<'a> {
225 type Item = AnsiTxt<'a>;
226
227 fn next(&mut self) -> Option<Self::Item> {
228 const CSI: &str = "\x1b[";
229
230 fn is_esc_end(byte: u8) -> bool {
231 (0x40..=0x7e).contains(&byte)
232 }
233
234 loop {
235 if self.source.is_empty() {
236 return None;
237 } else if let Some(source) = self.source.strip_prefix(CSI) {
238 let mut esc_end = 0;
239 while esc_end < source.len() && !is_esc_end(source.as_bytes()[esc_end]) {
240 esc_end += 1;
241 }
242 esc_end += 1;
243
244 let (esc, source) = source.split_at(esc_end);
245
246 let esc = &esc[..(esc.len() - 1)];
247 self.style.set(esc);
248
249 self.source = source;
250 continue;
251 } else if let Some(i) = self.source.find(CSI) {
252 let (txt, source) = self.source.split_at(i);
253 self.source = source;
254 return Some(AnsiTxt {
255 txt,
256 style: self.style.clone(),
257 });
258 } else {
259 return Some(AnsiTxt {
260 txt: std::mem::take(&mut self.source),
261 style: self.style.clone(),
262 });
263 }
264 }
265 }
266 }
267
268 impl AnsiStyle {
269 fn set(&mut self, esc_codes: &str) {
270 let mut esc_codes = esc_codes.split(';');
271 while let Some(code) = esc_codes.next() {
272 match code {
273 "0" => *self = Self::default(),
274 "1" => self.weight = AnsiWeight::Bold,
275 "2" => self.weight = AnsiWeight::Faint,
276 "3" => self.italic = true,
277 "4" => self.underline = true,
278 "5" => self.blink = true,
279 "7" => self.invert_color = true,
280 "8" => self.hidden = true,
281 "9" => self.strikethrough = true,
282 "22" => self.weight = AnsiWeight::Normal,
283 "23" => self.italic = false,
284 "24" => self.underline = false,
285 "25" => self.blink = false,
286 "27" => self.invert_color = false,
287 "28" => self.hidden = false,
288 "29" => self.strikethrough = false,
289 "30" => self.color = AnsiColor::Black,
290 "31" => self.color = AnsiColor::Red,
291 "32" => self.color = AnsiColor::Green,
292 "33" => self.color = AnsiColor::Yellow,
293 "34" => self.color = AnsiColor::Blue,
294 "35" => self.color = AnsiColor::Magenta,
295 "36" => self.color = AnsiColor::Cyan,
296 "37" => self.color = AnsiColor::White,
297 "40" => self.color = AnsiColor::Black,
298 "41" => self.color = AnsiColor::Red,
299 "42" => self.color = AnsiColor::Green,
300 "43" => self.color = AnsiColor::Yellow,
301 "44" => self.color = AnsiColor::Blue,
302 "45" => self.color = AnsiColor::Magenta,
303 "46" => self.color = AnsiColor::Cyan,
304 "47" => self.color = AnsiColor::White,
305 "90" => self.color = AnsiColor::BrightBlack,
306 "91" => self.color = AnsiColor::BrightRed,
307 "92" => self.color = AnsiColor::BrightGreen,
308 "93" => self.color = AnsiColor::BrightYellow,
309 "94" => self.color = AnsiColor::BrightBlue,
310 "95" => self.color = AnsiColor::BrightMagenta,
311 "96" => self.color = AnsiColor::BrightCyan,
312 "97" => self.color = AnsiColor::BrightWhite,
313 "100" => self.background_color = AnsiColor::BrightBlack,
314 "101" => self.background_color = AnsiColor::BrightRed,
315 "102" => self.background_color = AnsiColor::BrightGreen,
316 "103" => self.background_color = AnsiColor::BrightYellow,
317 "104" => self.background_color = AnsiColor::BrightBlue,
318 "105" => self.background_color = AnsiColor::BrightMagenta,
319 "106" => self.background_color = AnsiColor::BrightCyan,
320 "107" => self.background_color = AnsiColor::BrightWhite,
321 "38" | "48" => {
322 let target = if code == "38" {
323 &mut self.color
324 } else {
325 &mut self.background_color
326 };
327 match esc_codes.next() {
328 Some("5") => {
329 let c = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
330 *target = AnsiColor::Ansi256(c)
331 }
332 Some("2") => {
333 let r = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
334 let g = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
335 let b = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
336
337 *target = AnsiColor::TrueColor(r, g, b);
338 }
339 _ => {}
340 }
341 }
342 _ => (),
343 }
344 }
345 }
346 }
347}
348
349pub use ansi_fn::*;
350mod ansi_fn {
351 use std::time::Duration;
352
353 use super::{AnsiColor, AnsiStyle, AnsiWeight};
354
355 use super::*;
356
357 pub struct TextFnArgs {
361 pub txt: Txt,
363 pub style: AnsiStyle,
365 }
366
367 pub struct LineFnArgs {
371 pub index: u32,
373 pub page_index: u32,
375 pub text: UiVec,
377 }
378
379 pub struct PageFnArgs {
383 pub index: u32,
385
386 pub lines: UiVec,
388 }
389
390 pub struct PanelFnArgs {
394 pub pages: UiVec,
396 }
397
398 context_var! {
399 pub static TEXT_FN_VAR: WidgetFn<TextFnArgs> = wgt_fn!(|args: TextFnArgs| { default_text_fn(args) });
403
404 pub static LINE_FN_VAR: WidgetFn<LineFnArgs> = wgt_fn!(|args: LineFnArgs| { default_line_fn(args) });
408
409 pub static PAGE_FN_VAR: WidgetFn<PageFnArgs> = wgt_fn!(|args: PageFnArgs| { default_page_fn(args) });
413
414 pub static PANEL_FN_VAR: WidgetFn<PanelFnArgs> = wgt_fn!(|args: PanelFnArgs| { default_panel_fn(args) });
420
421 pub static BLINK_INTERVAL_VAR: Duration = Duration::ZERO;
425
426 pub static LINES_PER_PAGE_VAR: u32 = 200;
430 }
431
432 pub fn default_text_fn(args: TextFnArgs) -> impl UiNode {
439 let mut text = Text::widget_new();
440
441 widget_set! {
442 &mut text;
443 txt = args.txt;
444 }
445
446 if args.style.background_color != AnsiColor::Black {
447 widget_set! {
448 &mut text;
449 background_color = args.style.background_color;
450 }
451 }
452 if args.style.color != AnsiColor::White {
453 widget_set! {
454 &mut text;
455 font_color = args.style.color;
456 }
457 }
458
459 if args.style.weight != AnsiWeight::Normal {
460 widget_set! {
461 &mut text;
462 font_weight = args.style.weight;
463 }
464 }
465 if args.style.italic {
466 widget_set! {
467 &mut text;
468 font_style = FontStyle::Italic;
469 }
470 }
471
472 if args.style.underline {
473 widget_set! {
474 &mut text;
475 underline = 1, LineStyle::Solid;
476 }
477 }
478 if args.style.strikethrough {
479 widget_set! {
480 &mut text;
481 strikethrough = 1, LineStyle::Solid;
482 }
483 }
484
485 if args.style.invert_color {
486 widget_set! {
487 &mut text;
488 invert_color = true;
489 }
490 }
491
492 if args.style.hidden {
493 widget_set! {
494 &mut text;
495 visibility = Visibility::Hidden;
496 }
497 }
498 if args.style.blink && !args.style.hidden {
499 let opacity = var(1.fct());
500
501 let interval = BLINK_INTERVAL_VAR.get();
502 if interval != Duration::ZERO && interval != Duration::MAX {
503 opacity.step_oci(0.fct(), interval).perm();
504
505 widget_set! {
506 &mut text;
507 opacity;
508 }
509 }
510 }
511
512 text.widget_build()
513 }
514
515 pub fn default_line_fn(mut args: LineFnArgs) -> impl UiNode {
519 use crate::prelude::*;
520
521 if args.text.is_empty() {
522 Text!("").boxed()
523 } else if args.text.len() == 1 {
524 args.text.remove(0)
525 } else {
526 Stack! {
527 direction = StackDirection::start_to_end();
528 children = args.text;
529 }
530 .boxed()
531 }
532 }
533
534 pub fn default_page_fn(mut args: PageFnArgs) -> impl UiNode {
538 use crate::prelude::*;
539
540 if args.lines.is_empty() {
541 NilUiNode.boxed()
542 } else if args.lines.len() == 1 {
543 args.lines.remove(0)
544 } else {
545 let len = args.lines.len();
546 Stack! {
547 direction = StackDirection::top_to_bottom();
548 children = args.lines;
549 lazy = LazyMode::lazy_vertical(wgt_fn!(|_| {
550 let height_sample = zng_wgt_text::node::line_placeholder(50);
551 zng_wgt_stack::lazy_sample(len, StackDirection::top_to_bottom(), 0, height_sample)
552 }));
553 }
554 .boxed()
555 }
556 }
557
558 pub fn default_panel_fn(mut args: PanelFnArgs) -> impl UiNode {
562 use crate::prelude::*;
563
564 if args.pages.is_empty() {
565 NilUiNode.boxed()
566 } else if args.pages.len() == 1 {
567 args.pages.remove(0)
568 } else {
569 Stack! {
570 direction = StackDirection::top_to_bottom();
571 children = args.pages;
572 }
573 .boxed()
574 }
575 }
576
577 #[property(CONTEXT, default(BLINK_INTERVAL_VAR), widget_impl(AnsiText))]
583 pub fn blink_interval(child: impl UiNode, interval: impl IntoVar<Duration>) -> impl UiNode {
584 with_context_var(child, BLINK_INTERVAL_VAR, interval)
585 }
586
587 #[property(CONTEXT, default(TEXT_FN_VAR), widget_impl(AnsiText))]
591 pub fn text_fn(child: impl UiNode, wgt_fn: impl IntoVar<WidgetFn<TextFnArgs>>) -> impl UiNode {
592 with_context_var(child, TEXT_FN_VAR, wgt_fn)
593 }
594
595 #[property(CONTEXT, default(LINE_FN_VAR), widget_impl(AnsiText))]
599 pub fn line_fn(child: impl UiNode, wgt_fn: impl IntoVar<WidgetFn<LineFnArgs>>) -> impl UiNode {
600 with_context_var(child, LINE_FN_VAR, wgt_fn)
601 }
602
603 #[property(CONTEXT, default(PAGE_FN_VAR), widget_impl(AnsiText))]
611 pub fn page_fn(child: impl UiNode, wgt_fn: impl IntoVar<WidgetFn<PageFnArgs>>) -> impl UiNode {
612 with_context_var(child, PAGE_FN_VAR, wgt_fn)
613 }
614
615 #[property(CONTEXT, default(PANEL_FN_VAR), widget_impl(AnsiText))]
617 pub fn panel_fn(child: impl UiNode, wgt_fn: impl IntoVar<WidgetFn<PanelFnArgs>>) -> impl UiNode {
618 with_context_var(child, PANEL_FN_VAR, wgt_fn)
619 }
620
621 #[property(CONTEXT, default(LINES_PER_PAGE_VAR), widget_impl(AnsiText))]
625 pub fn lines_per_page(child: impl UiNode, count: impl IntoVar<u32>) -> impl UiNode {
626 with_context_var(child, LINES_PER_PAGE_VAR, count)
627 }
628}
629
630fn generate_ansi(txt: &impl Var<Txt>) -> BoxedUiNode {
631 use ansi_fn::*;
632 use std::mem;
633
634 txt.with(|txt| {
635 let text_fn = TEXT_FN_VAR.get();
636 let line_fn = LINE_FN_VAR.get();
637 let page_fn = PAGE_FN_VAR.get();
638 let panel_fn = PANEL_FN_VAR.get();
639 let lines_per_page = LINES_PER_PAGE_VAR.get() as usize;
640
641 let mut pages = Vec::with_capacity(4);
642 let mut lines = Vec::with_capacity(50);
643
644 for (i, line) in txt.lines().enumerate() {
645 let text = ansi_parse::AnsiTextParser::new(line)
646 .filter_map(|txt| {
647 text_fn.call_checked(TextFnArgs {
648 txt: txt.txt.to_txt(),
649 style: txt.style,
650 })
651 })
652 .collect();
653
654 lines.push(line_fn(LineFnArgs {
655 index: i as u32,
656 page_index: lines.len() as u32,
657 text,
658 }));
659
660 if lines.len() == lines_per_page {
661 let lines = mem::replace(&mut lines, Vec::with_capacity(50));
662 pages.push(page_fn(PageFnArgs {
663 index: pages.len() as u32,
664 lines: lines.into(),
665 }));
666 }
667 }
668
669 if !lines.is_empty() {
670 pages.push(page_fn(PageFnArgs {
671 index: pages.len() as u32,
672 lines: lines.into(),
673 }));
674 }
675
676 panel_fn(PanelFnArgs { pages: pages.into() })
677 })
678}
679
680pub fn ansi_node(txt: impl IntoVar<Txt>) -> impl UiNode {
682 let txt = txt.into_var();
683 match_node(NilUiNode.boxed(), move |c, op| match op {
684 UiNodeOp::Init => {
685 WIDGET
686 .sub_var(&txt)
687 .sub_var(&TEXT_FN_VAR)
688 .sub_var(&LINE_FN_VAR)
689 .sub_var(&PAGE_FN_VAR)
690 .sub_var(&PANEL_FN_VAR)
691 .sub_var(&LINES_PER_PAGE_VAR)
692 .sub_var(&BLINK_INTERVAL_VAR);
693
694 *c.child() = generate_ansi(&txt);
695 }
696 UiNodeOp::Deinit => {
697 c.deinit();
698 *c.child() = NilUiNode.boxed();
699 }
700 UiNodeOp::Update { .. } => {
701 use ansi_fn::*;
702
703 if txt.is_new()
704 || TEXT_FN_VAR.is_new()
705 || LINE_FN_VAR.is_new()
706 || PAGE_FN_VAR.is_new()
707 || PANEL_FN_VAR.is_new()
708 || LINES_PER_PAGE_VAR.is_new()
709 || BLINK_INTERVAL_VAR.is_new()
710 {
711 c.child().deinit();
712 *c.child() = generate_ansi(&txt);
713 c.child().init();
714 WIDGET.update_info().layout().render();
715 }
716 }
717 _ => {}
718 })
719}
720
721static X_TERM_256: [(u8, u8, u8); 256] = [
722 (0, 0, 0),
723 (128, 0, 0),
724 (0, 128, 0),
725 (128, 128, 0),
726 (0, 0, 128),
727 (128, 0, 128),
728 (0, 128, 128),
729 (192, 192, 192),
730 (128, 128, 128),
731 (255, 0, 0),
732 (0, 255, 0),
733 (255, 255, 0),
734 (0, 0, 255),
735 (255, 0, 255),
736 (0, 255, 255),
737 (255, 255, 255),
738 (0, 0, 0),
739 (0, 0, 95),
740 (0, 0, 135),
741 (0, 0, 175),
742 (0, 0, 215),
743 (0, 0, 255),
744 (0, 95, 0),
745 (0, 95, 95),
746 (0, 95, 135),
747 (0, 95, 175),
748 (0, 95, 215),
749 (0, 95, 255),
750 (0, 135, 0),
751 (0, 135, 95),
752 (0, 135, 135),
753 (0, 135, 175),
754 (0, 135, 215),
755 (0, 135, 255),
756 (0, 175, 0),
757 (0, 175, 95),
758 (0, 175, 135),
759 (0, 175, 175),
760 (0, 175, 215),
761 (0, 175, 255),
762 (0, 215, 0),
763 (0, 215, 95),
764 (0, 215, 135),
765 (0, 215, 175),
766 (0, 215, 215),
767 (0, 215, 255),
768 (0, 255, 0),
769 (0, 255, 95),
770 (0, 255, 135),
771 (0, 255, 175),
772 (0, 255, 215),
773 (0, 255, 255),
774 (95, 0, 0),
775 (95, 0, 95),
776 (95, 0, 135),
777 (95, 0, 175),
778 (95, 0, 215),
779 (95, 0, 255),
780 (95, 95, 0),
781 (95, 95, 95),
782 (95, 95, 135),
783 (95, 95, 175),
784 (95, 95, 215),
785 (95, 95, 255),
786 (95, 135, 0),
787 (95, 135, 95),
788 (95, 135, 135),
789 (95, 135, 175),
790 (95, 135, 215),
791 (95, 135, 255),
792 (95, 175, 0),
793 (95, 175, 95),
794 (95, 175, 135),
795 (95, 175, 175),
796 (95, 175, 215),
797 (95, 175, 255),
798 (95, 215, 0),
799 (95, 215, 95),
800 (95, 215, 135),
801 (95, 215, 175),
802 (95, 215, 215),
803 (95, 215, 255),
804 (95, 255, 0),
805 (95, 255, 95),
806 (95, 255, 135),
807 (95, 255, 175),
808 (95, 255, 215),
809 (95, 255, 255),
810 (135, 0, 0),
811 (135, 0, 95),
812 (135, 0, 135),
813 (135, 0, 175),
814 (135, 0, 215),
815 (135, 0, 255),
816 (135, 95, 0),
817 (135, 95, 95),
818 (135, 95, 135),
819 (135, 95, 175),
820 (135, 95, 215),
821 (135, 95, 255),
822 (135, 135, 0),
823 (135, 135, 95),
824 (135, 135, 135),
825 (135, 135, 175),
826 (135, 135, 215),
827 (135, 135, 255),
828 (135, 175, 0),
829 (135, 175, 95),
830 (135, 175, 135),
831 (135, 175, 175),
832 (135, 175, 215),
833 (135, 175, 255),
834 (135, 215, 0),
835 (135, 215, 95),
836 (135, 215, 135),
837 (135, 215, 175),
838 (135, 215, 215),
839 (135, 215, 255),
840 (135, 255, 0),
841 (135, 255, 95),
842 (135, 255, 135),
843 (135, 255, 175),
844 (135, 255, 215),
845 (135, 255, 255),
846 (175, 0, 0),
847 (175, 0, 95),
848 (175, 0, 135),
849 (175, 0, 175),
850 (175, 0, 215),
851 (175, 0, 255),
852 (175, 95, 0),
853 (175, 95, 95),
854 (175, 95, 135),
855 (175, 95, 175),
856 (175, 95, 215),
857 (175, 95, 255),
858 (175, 135, 0),
859 (175, 135, 95),
860 (175, 135, 135),
861 (175, 135, 175),
862 (175, 135, 215),
863 (175, 135, 255),
864 (175, 175, 0),
865 (175, 175, 95),
866 (175, 175, 135),
867 (175, 175, 175),
868 (175, 175, 215),
869 (175, 175, 255),
870 (175, 215, 0),
871 (175, 215, 95),
872 (175, 215, 135),
873 (175, 215, 175),
874 (175, 215, 215),
875 (175, 215, 255),
876 (175, 255, 0),
877 (175, 255, 95),
878 (175, 255, 135),
879 (175, 255, 175),
880 (175, 255, 215),
881 (175, 255, 255),
882 (215, 0, 0),
883 (215, 0, 95),
884 (215, 0, 135),
885 (215, 0, 175),
886 (215, 0, 215),
887 (215, 0, 255),
888 (215, 95, 0),
889 (215, 95, 95),
890 (215, 95, 135),
891 (215, 95, 175),
892 (215, 95, 215),
893 (215, 95, 255),
894 (215, 135, 0),
895 (215, 135, 95),
896 (215, 135, 135),
897 (215, 135, 175),
898 (215, 135, 215),
899 (215, 135, 255),
900 (215, 175, 0),
901 (215, 175, 95),
902 (215, 175, 135),
903 (215, 175, 175),
904 (215, 175, 215),
905 (215, 175, 255),
906 (215, 215, 0),
907 (215, 215, 95),
908 (215, 215, 135),
909 (215, 215, 175),
910 (215, 215, 215),
911 (215, 215, 255),
912 (215, 255, 0),
913 (215, 255, 95),
914 (215, 255, 135),
915 (215, 255, 175),
916 (215, 255, 215),
917 (215, 255, 255),
918 (255, 0, 0),
919 (255, 0, 95),
920 (255, 0, 135),
921 (255, 0, 175),
922 (255, 0, 215),
923 (255, 0, 255),
924 (255, 95, 0),
925 (255, 95, 95),
926 (255, 95, 135),
927 (255, 95, 175),
928 (255, 95, 215),
929 (255, 95, 255),
930 (255, 135, 0),
931 (255, 135, 95),
932 (255, 135, 135),
933 (255, 135, 175),
934 (255, 135, 215),
935 (255, 135, 255),
936 (255, 175, 0),
937 (255, 175, 95),
938 (255, 175, 135),
939 (255, 175, 175),
940 (255, 175, 215),
941 (255, 175, 255),
942 (255, 215, 0),
943 (255, 215, 95),
944 (255, 215, 135),
945 (255, 215, 175),
946 (255, 215, 215),
947 (255, 215, 255),
948 (255, 255, 0),
949 (255, 255, 95),
950 (255, 255, 135),
951 (255, 255, 175),
952 (255, 255, 215),
953 (255, 255, 255),
954 (8, 8, 8),
955 (18, 18, 18),
956 (28, 28, 28),
957 (38, 38, 38),
958 (48, 48, 48),
959 (58, 58, 58),
960 (68, 68, 68),
961 (78, 78, 78),
962 (88, 88, 88),
963 (98, 98, 98),
964 (108, 108, 108),
965 (118, 118, 118),
966 (128, 128, 128),
967 (138, 138, 138),
968 (148, 148, 148),
969 (158, 158, 158),
970 (168, 168, 168),
971 (178, 178, 178),
972 (188, 188, 188),
973 (198, 198, 198),
974 (208, 208, 208),
975 (218, 218, 218),
976 (228, 228, 228),
977 (238, 238, 238),
978];