1#![doc(html_favicon_url = "https://zng-ui.github.io/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://zng-ui.github.io/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_input::{CursorIcon, cursor};
19use zng_wgt_layer::popup;
20use zng_wgt_scroll::{LazyMode, lazy};
21use zng_wgt_stack::{Stack, StackDirection};
22use zng_wgt_text::*;
23
24#[doc(hidden)]
25pub use zng_wgt_text::__formatx;
26
27#[widget($crate::AnsiText {
31 ($txt:literal) => {
32 txt = $crate::__formatx!($txt);
33 };
34 ($txt:expr) => {
35 txt = $txt;
36 };
37 ($txt:tt, $($format:tt)*) => {
38 txt = $crate::__formatx!($txt, $($format)*);
39 };
40})]
41#[rustfmt::skip]
42pub struct AnsiText(
43 FontMix<
44 TextSpacingMix<
45 ParagraphMix<
46 LangMix<
47 WidgetBase
48 >>>>
49);
50impl AnsiText {
51 fn widget_intrinsic(&mut self) {
52 widget_set! {
53 self;
54 font_family = ["JetBrains Mono", "Consolas", "monospace"];
55 rich_text = true;
56
57 popup::context_capture = default_popup_context_capture();
58 when #txt_selectable {
59 cursor = CursorIcon::Text;
60 zng_wgt_menu::context::context_menu_fn = WidgetFn::new(default_context_menu);
61 }
62 };
63
64 self.widget_builder().push_build_action(|wgt| {
65 let txt = wgt.capture_var_or_default(property_id!(txt));
66 let child = ansi_node(txt);
67 wgt.set_child(child);
68 });
69 }
70
71 widget_impl! {
72 pub txt(text: impl IntoVar<Txt>);
74
75 pub zng_wgt_text::txt_selectable(enabled: impl IntoVar<bool>);
79 }
80}
81
82pub use ansi_parse::*;
83mod ansi_parse {
84
85 use super::*;
86
87 #[derive(Debug)]
89 #[non_exhaustive]
90 pub struct AnsiTxt<'a> {
91 pub txt: &'a str,
93 pub style: AnsiStyle,
95 }
96
97 #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
101 #[non_exhaustive]
102 pub struct AnsiStyle {
103 pub background_color: AnsiColor,
105 pub color: AnsiColor,
107 pub weight: AnsiWeight,
109 pub italic: bool,
111 pub underline: bool,
113 pub strikethrough: bool,
115 pub invert_color: bool,
117 pub hidden: bool,
119 pub blink: bool,
121 }
122 impl Default for AnsiStyle {
123 fn default() -> Self {
124 Self {
125 background_color: AnsiColor::Black,
126 color: AnsiColor::White,
127 weight: Default::default(),
128 italic: false,
129 underline: false,
130 strikethrough: false,
131 invert_color: false,
132 hidden: false,
133 blink: false,
134 }
135 }
136 }
137
138 #[allow(missing_docs)]
142 #[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
143 pub enum AnsiColor {
144 Black,
145 Red,
146 Green,
147 Yellow,
148 Blue,
149 Magenta,
150 Cyan,
151 White,
152 BrightBlack,
154 BrightRed,
155 BrightGreen,
156 BrightYellow,
157 BrightBlue,
158 BrightMagenta,
159 BrightCyan,
160 BrightWhite,
161 Ansi256(u8),
163 TrueColor(u8, u8, u8),
165 }
166 impl_from_and_into_var! {
167 fn from(color: AnsiColor) -> Rgba {
168 match color {
169 AnsiColor::Black => rgb(0, 0, 0),
170 AnsiColor::Red => rgb(205, 49, 49),
171 AnsiColor::Green => rgb(13, 188, 121),
172 AnsiColor::Yellow => rgb(229, 229, 16),
173 AnsiColor::Blue => rgb(36, 114, 200),
174 AnsiColor::Magenta => rgb(188, 63, 188),
175 AnsiColor::Cyan => rgb(17, 168, 205),
176 AnsiColor::White => rgb(229, 229, 229),
177 AnsiColor::BrightBlack => rgb(102, 102, 102),
178 AnsiColor::BrightRed => rgb(241, 76, 76),
179 AnsiColor::BrightGreen => rgb(35, 209, 139),
180 AnsiColor::BrightYellow => rgb(245, 245, 67),
181 AnsiColor::BrightBlue => rgb(59, 142, 234),
182 AnsiColor::BrightMagenta => rgb(214, 112, 214),
183 AnsiColor::BrightCyan => rgb(41, 184, 219),
184 AnsiColor::BrightWhite => rgb(229, 229, 229),
185 AnsiColor::Ansi256(c) => {
186 let (r, g, b) = X_TERM_256[c as usize];
187 rgb(r, g, b)
188 }
189 AnsiColor::TrueColor(r, g, b) => rgb(r, g, b),
190 }
191 }
192 }
193
194 #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
198 pub enum AnsiWeight {
199 #[default]
201 Normal,
202 Bold,
204 Faint,
206 }
207 impl_from_and_into_var! {
208 fn from(weight: AnsiWeight) -> FontWeight {
209 match weight {
210 AnsiWeight::Normal => FontWeight::NORMAL,
211 AnsiWeight::Bold => FontWeight::BOLD,
212 AnsiWeight::Faint => FontWeight::LIGHT,
213 }
214 }
215 }
216
217 pub struct AnsiTextParser<'a> {
223 source: &'a str,
224 pub style: AnsiStyle,
226 }
227 impl<'a> AnsiTextParser<'a> {
228 pub fn new(source: &'a str) -> Self {
230 Self {
231 source,
232 style: AnsiStyle::default(),
233 }
234 }
235 }
236 impl<'a> Iterator for AnsiTextParser<'a> {
237 type Item = AnsiTxt<'a>;
238
239 fn next(&mut self) -> Option<Self::Item> {
240 const CSI: &str = "\x1b[";
241
242 fn is_esc_end(byte: u8) -> bool {
243 (0x40..=0x7e).contains(&byte)
244 }
245
246 loop {
247 if self.source.is_empty() {
248 return None;
249 } else if let Some(source) = self.source.strip_prefix(CSI) {
250 let mut esc_end = 0;
251 while esc_end < source.len() && !is_esc_end(source.as_bytes()[esc_end]) {
252 esc_end += 1;
253 }
254 esc_end += 1;
255
256 let (esc, source) = source.split_at(esc_end);
257
258 let esc = &esc[..(esc.len() - 1)];
259 self.style.set(esc);
260
261 self.source = source;
262 continue;
263 } else if let Some(i) = self.source.find(CSI) {
264 let (txt, source) = self.source.split_at(i);
265 self.source = source;
266 return Some(AnsiTxt {
267 txt,
268 style: self.style.clone(),
269 });
270 } else {
271 return Some(AnsiTxt {
272 txt: std::mem::take(&mut self.source),
273 style: self.style.clone(),
274 });
275 }
276 }
277 }
278 }
279
280 impl AnsiStyle {
281 fn set(&mut self, esc_codes: &str) {
282 let mut esc_codes = esc_codes.split(';');
283 while let Some(code) = esc_codes.next() {
284 match code {
285 "0" => *self = Self::default(),
286 "1" => self.weight = AnsiWeight::Bold,
287 "2" => self.weight = AnsiWeight::Faint,
288 "3" => self.italic = true,
289 "4" => self.underline = true,
290 "5" => self.blink = true,
291 "7" => self.invert_color = true,
292 "8" => self.hidden = true,
293 "9" => self.strikethrough = true,
294 "22" => self.weight = AnsiWeight::Normal,
295 "23" => self.italic = false,
296 "24" => self.underline = false,
297 "25" => self.blink = false,
298 "27" => self.invert_color = false,
299 "28" => self.hidden = false,
300 "29" => self.strikethrough = false,
301 "30" => self.color = AnsiColor::Black,
302 "31" => self.color = AnsiColor::Red,
303 "32" => self.color = AnsiColor::Green,
304 "33" => self.color = AnsiColor::Yellow,
305 "34" => self.color = AnsiColor::Blue,
306 "35" => self.color = AnsiColor::Magenta,
307 "36" => self.color = AnsiColor::Cyan,
308 "37" => self.color = AnsiColor::White,
309 "40" => self.color = AnsiColor::Black,
310 "41" => self.color = AnsiColor::Red,
311 "42" => self.color = AnsiColor::Green,
312 "43" => self.color = AnsiColor::Yellow,
313 "44" => self.color = AnsiColor::Blue,
314 "45" => self.color = AnsiColor::Magenta,
315 "46" => self.color = AnsiColor::Cyan,
316 "47" => self.color = AnsiColor::White,
317 "90" => self.color = AnsiColor::BrightBlack,
318 "91" => self.color = AnsiColor::BrightRed,
319 "92" => self.color = AnsiColor::BrightGreen,
320 "93" => self.color = AnsiColor::BrightYellow,
321 "94" => self.color = AnsiColor::BrightBlue,
322 "95" => self.color = AnsiColor::BrightMagenta,
323 "96" => self.color = AnsiColor::BrightCyan,
324 "97" => self.color = AnsiColor::BrightWhite,
325 "100" => self.background_color = AnsiColor::BrightBlack,
326 "101" => self.background_color = AnsiColor::BrightRed,
327 "102" => self.background_color = AnsiColor::BrightGreen,
328 "103" => self.background_color = AnsiColor::BrightYellow,
329 "104" => self.background_color = AnsiColor::BrightBlue,
330 "105" => self.background_color = AnsiColor::BrightMagenta,
331 "106" => self.background_color = AnsiColor::BrightCyan,
332 "107" => self.background_color = AnsiColor::BrightWhite,
333 "38" | "48" => {
334 let target = if code == "38" {
335 &mut self.color
336 } else {
337 &mut self.background_color
338 };
339 match esc_codes.next() {
340 Some("5") => {
341 let c = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
342 *target = AnsiColor::Ansi256(c)
343 }
344 Some("2") => {
345 let r = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
346 let g = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
347 let b = esc_codes.next().and_then(|c| c.parse().ok()).unwrap_or(0);
348
349 *target = AnsiColor::TrueColor(r, g, b);
350 }
351 _ => {}
352 }
353 }
354 _ => (),
355 }
356 }
357 }
358 }
359}
360
361pub use ansi_fn::*;
362mod ansi_fn {
363 use std::time::Duration;
364
365 use super::{AnsiColor, AnsiStyle, AnsiWeight};
366
367 use super::*;
368
369 #[non_exhaustive]
373 pub struct TextFnArgs {
374 pub txt: Txt,
376 pub style: AnsiStyle,
378 }
379 impl TextFnArgs {
380 pub fn new(txt: impl Into<Txt>, style: AnsiStyle) -> Self {
382 Self { txt: txt.into(), style }
383 }
384 }
385
386 #[non_exhaustive]
390 pub struct LineFnArgs {
391 pub index: u32,
393 pub page_index: u32,
395 pub text: UiVec,
397 }
398
399 impl LineFnArgs {
400 pub fn new(index: u32, page_index: u32, text: UiVec) -> Self {
402 Self { index, page_index, text }
403 }
404 }
405
406 #[non_exhaustive]
410 pub struct PageFnArgs {
411 pub index: u32,
413
414 pub lines: UiVec,
416 }
417
418 impl PageFnArgs {
419 pub fn new(index: u32, lines: UiVec) -> Self {
421 Self { index, lines }
422 }
423 }
424
425 #[non_exhaustive]
429 pub struct PanelFnArgs {
430 pub pages: UiVec,
432 }
433
434 impl PanelFnArgs {
435 pub fn new(pages: UiVec) -> Self {
437 Self { pages }
438 }
439 }
440
441 context_var! {
442 pub static TEXT_FN_VAR: WidgetFn<TextFnArgs> = wgt_fn!(|args: TextFnArgs| { default_text_fn(args) });
446
447 pub static LINE_FN_VAR: WidgetFn<LineFnArgs> = wgt_fn!(|args: LineFnArgs| { default_line_fn(args) });
451
452 pub static PAGE_FN_VAR: WidgetFn<PageFnArgs> = wgt_fn!(|args: PageFnArgs| { default_page_fn(args) });
456
457 pub static PANEL_FN_VAR: WidgetFn<PanelFnArgs> = wgt_fn!(|args: PanelFnArgs| { default_panel_fn(args) });
463
464 pub static BLINK_INTERVAL_VAR: Duration = Duration::ZERO;
468
469 pub static LINES_PER_PAGE_VAR: u32 = 200;
473 }
474
475 pub fn default_text_fn(args: TextFnArgs) -> UiNode {
482 let mut text = Text::widget_new();
483
484 widget_set! {
485 &mut text;
486 txt = args.txt;
487 }
488
489 if args.style.background_color != AnsiColor::Black {
490 widget_set! {
491 &mut text;
492 background_color = args.style.background_color;
493 }
494 }
495 if args.style.color != AnsiColor::White {
496 widget_set! {
497 &mut text;
498 font_color = args.style.color;
499 }
500 }
501
502 if args.style.weight != AnsiWeight::Normal {
503 widget_set! {
504 &mut text;
505 font_weight = args.style.weight;
506 }
507 }
508 if args.style.italic {
509 widget_set! {
510 &mut text;
511 font_style = FontStyle::Italic;
512 }
513 }
514
515 if args.style.underline {
516 widget_set! {
517 &mut text;
518 underline = 1, LineStyle::Solid;
519 }
520 }
521 if args.style.strikethrough {
522 widget_set! {
523 &mut text;
524 strikethrough = 1, LineStyle::Solid;
525 }
526 }
527
528 if args.style.invert_color {
529 widget_set! {
530 &mut text;
531 invert_color = true;
532 }
533 }
534
535 if args.style.hidden {
536 widget_set! {
537 &mut text;
538 visibility = Visibility::Hidden;
539 }
540 }
541 if args.style.blink && !args.style.hidden {
542 let opacity = var(1.fct());
543
544 let interval = BLINK_INTERVAL_VAR.get();
545 if interval != Duration::ZERO && interval != Duration::MAX {
546 opacity.step_oci(0.fct(), interval).perm();
547
548 widget_set! {
549 &mut text;
550 opacity;
551 }
552 }
553 }
554
555 text.widget_build()
556 }
557
558 pub fn default_line_fn(mut args: LineFnArgs) -> UiNode {
562 if args.text.is_empty() {
563 Text!("")
564 } else if args.text.len() == 1 {
565 args.text.remove(0)
566 } else {
567 Stack! {
568 rich_text = true;
569 direction = StackDirection::start_to_end();
570 children = args.text;
571 }
572 }
573 }
574
575 pub fn default_page_fn(mut args: PageFnArgs) -> UiNode {
579 use crate::prelude::*;
580
581 if args.lines.is_empty() {
582 UiNode::nil()
583 } else if args.lines.len() == 1 {
584 args.lines.remove(0)
585 } else {
586 let len = args.lines.len();
587 Stack! {
588 rich_text = true;
589 direction = StackDirection::top_to_bottom();
590 children = args.lines;
591 lazy = LazyMode::lazy_vertical(wgt_fn!(|_| {
592 let height_sample = zng_wgt_text::node::line_placeholder(50);
593 zng_wgt_stack::lazy_sample(len, StackDirection::top_to_bottom(), 0, height_sample)
594 }));
595 }
596 }
597 }
598
599 pub fn default_panel_fn(mut args: PanelFnArgs) -> UiNode {
603 use crate::prelude::*;
604
605 if args.pages.is_empty() {
606 UiNode::nil()
607 } else if args.pages.len() == 1 {
608 args.pages.remove(0)
609 } else {
610 Stack! {
611 rich_text = true;
612 direction = StackDirection::top_to_bottom();
613 children = args.pages;
614 }
615 }
616 }
617
618 #[property(CONTEXT, default(BLINK_INTERVAL_VAR), widget_impl(AnsiText))]
624 pub fn blink_interval(child: impl IntoUiNode, interval: impl IntoVar<Duration>) -> UiNode {
625 with_context_var(child, BLINK_INTERVAL_VAR, interval)
626 }
627
628 #[property(CONTEXT, default(TEXT_FN_VAR), widget_impl(AnsiText))]
632 pub fn text_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<TextFnArgs>>) -> UiNode {
633 with_context_var(child, TEXT_FN_VAR, wgt_fn)
634 }
635
636 #[property(CONTEXT, default(LINE_FN_VAR), widget_impl(AnsiText))]
640 pub fn line_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<LineFnArgs>>) -> UiNode {
641 with_context_var(child, LINE_FN_VAR, wgt_fn)
642 }
643
644 #[property(CONTEXT, default(PAGE_FN_VAR), widget_impl(AnsiText))]
652 pub fn page_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<PageFnArgs>>) -> UiNode {
653 with_context_var(child, PAGE_FN_VAR, wgt_fn)
654 }
655
656 #[property(CONTEXT, default(PANEL_FN_VAR), widget_impl(AnsiText))]
658 pub fn panel_fn(child: impl IntoUiNode, wgt_fn: impl IntoVar<WidgetFn<PanelFnArgs>>) -> UiNode {
659 with_context_var(child, PANEL_FN_VAR, wgt_fn)
660 }
661
662 #[property(CONTEXT, default(LINES_PER_PAGE_VAR), widget_impl(AnsiText))]
666 pub fn lines_per_page(child: impl IntoUiNode, count: impl IntoVar<u32>) -> UiNode {
667 with_context_var(child, LINES_PER_PAGE_VAR, count)
668 }
669}
670
671fn generate_ansi(txt: &Var<Txt>) -> UiNode {
672 use ansi_fn::*;
673 use std::mem;
674
675 txt.with(|txt| {
676 let text_fn = TEXT_FN_VAR.get();
677 let line_fn = LINE_FN_VAR.get();
678 let page_fn = PAGE_FN_VAR.get();
679 let panel_fn = PANEL_FN_VAR.get();
680 let lines_per_page = LINES_PER_PAGE_VAR.get() as usize;
681
682 let mut pages = Vec::with_capacity(4);
683 let mut lines = Vec::with_capacity(50);
684
685 for (i, line) in txt.lines().enumerate() {
686 let text = ansi_parse::AnsiTextParser::new(line)
687 .filter_map(|txt| {
688 text_fn.call_checked(TextFnArgs {
689 txt: txt.txt.to_txt(),
690 style: txt.style,
691 })
692 })
693 .collect();
694
695 lines.push(line_fn(LineFnArgs {
696 index: i as u32,
697 page_index: lines.len() as u32,
698 text,
699 }));
700
701 if lines.len() == lines_per_page {
702 let lines = mem::replace(&mut lines, Vec::with_capacity(50));
703 pages.push(page_fn(PageFnArgs {
704 index: pages.len() as u32,
705 lines: lines.into(),
706 }));
707 }
708 }
709
710 if !lines.is_empty() {
711 pages.push(page_fn(PageFnArgs {
712 index: pages.len() as u32,
713 lines: lines.into(),
714 }));
715 }
716
717 panel_fn(PanelFnArgs { pages: pages.into() })
718 })
719}
720
721pub fn ansi_node(txt: impl IntoVar<Txt>) -> UiNode {
723 let txt = txt.into_var();
724 match_node(UiNode::nil(), move |c, op| match op {
725 UiNodeOp::Init => {
726 WIDGET
727 .sub_var(&txt)
728 .sub_var(&TEXT_FN_VAR)
729 .sub_var(&LINE_FN_VAR)
730 .sub_var(&PAGE_FN_VAR)
731 .sub_var(&PANEL_FN_VAR)
732 .sub_var(&LINES_PER_PAGE_VAR)
733 .sub_var(&BLINK_INTERVAL_VAR);
734
735 *c.node() = generate_ansi(&txt);
736 }
737 UiNodeOp::Deinit => {
738 c.deinit();
739 *c.node() = UiNode::nil();
740 }
741 UiNodeOp::Update { .. } => {
742 use ansi_fn::*;
743
744 if txt.is_new()
745 || TEXT_FN_VAR.is_new()
746 || LINE_FN_VAR.is_new()
747 || PAGE_FN_VAR.is_new()
748 || PANEL_FN_VAR.is_new()
749 || LINES_PER_PAGE_VAR.is_new()
750 || BLINK_INTERVAL_VAR.is_new()
751 {
752 c.node().deinit();
753 *c.node() = generate_ansi(&txt);
754 c.node().init();
755 WIDGET.update_info().layout().render();
756 }
757 }
758 _ => {}
759 })
760}
761
762pub fn default_context_menu(args: zng_wgt_menu::context::ContextMenuArgs) -> UiNode {
767 use zng_wgt_button::Button;
768 let id = args.anchor_id;
769 zng_wgt_menu::context::ContextMenu!(ui_vec![
770 Button!(zng_ext_clipboard::COPY_CMD.scoped(id)),
771 Button!(zng_wgt_text::cmd::SELECT_ALL_CMD.scoped(id)),
772 ])
773}
774
775pub fn default_popup_context_capture() -> popup::ContextCapture {
781 popup::ContextCapture::context_vars_except(Text::context_vars_set_except_lang)
782}
783
784static X_TERM_256: [(u8, u8, u8); 256] = [
785 (0, 0, 0),
786 (128, 0, 0),
787 (0, 128, 0),
788 (128, 128, 0),
789 (0, 0, 128),
790 (128, 0, 128),
791 (0, 128, 128),
792 (192, 192, 192),
793 (128, 128, 128),
794 (255, 0, 0),
795 (0, 255, 0),
796 (255, 255, 0),
797 (0, 0, 255),
798 (255, 0, 255),
799 (0, 255, 255),
800 (255, 255, 255),
801 (0, 0, 0),
802 (0, 0, 95),
803 (0, 0, 135),
804 (0, 0, 175),
805 (0, 0, 215),
806 (0, 0, 255),
807 (0, 95, 0),
808 (0, 95, 95),
809 (0, 95, 135),
810 (0, 95, 175),
811 (0, 95, 215),
812 (0, 95, 255),
813 (0, 135, 0),
814 (0, 135, 95),
815 (0, 135, 135),
816 (0, 135, 175),
817 (0, 135, 215),
818 (0, 135, 255),
819 (0, 175, 0),
820 (0, 175, 95),
821 (0, 175, 135),
822 (0, 175, 175),
823 (0, 175, 215),
824 (0, 175, 255),
825 (0, 215, 0),
826 (0, 215, 95),
827 (0, 215, 135),
828 (0, 215, 175),
829 (0, 215, 215),
830 (0, 215, 255),
831 (0, 255, 0),
832 (0, 255, 95),
833 (0, 255, 135),
834 (0, 255, 175),
835 (0, 255, 215),
836 (0, 255, 255),
837 (95, 0, 0),
838 (95, 0, 95),
839 (95, 0, 135),
840 (95, 0, 175),
841 (95, 0, 215),
842 (95, 0, 255),
843 (95, 95, 0),
844 (95, 95, 95),
845 (95, 95, 135),
846 (95, 95, 175),
847 (95, 95, 215),
848 (95, 95, 255),
849 (95, 135, 0),
850 (95, 135, 95),
851 (95, 135, 135),
852 (95, 135, 175),
853 (95, 135, 215),
854 (95, 135, 255),
855 (95, 175, 0),
856 (95, 175, 95),
857 (95, 175, 135),
858 (95, 175, 175),
859 (95, 175, 215),
860 (95, 175, 255),
861 (95, 215, 0),
862 (95, 215, 95),
863 (95, 215, 135),
864 (95, 215, 175),
865 (95, 215, 215),
866 (95, 215, 255),
867 (95, 255, 0),
868 (95, 255, 95),
869 (95, 255, 135),
870 (95, 255, 175),
871 (95, 255, 215),
872 (95, 255, 255),
873 (135, 0, 0),
874 (135, 0, 95),
875 (135, 0, 135),
876 (135, 0, 175),
877 (135, 0, 215),
878 (135, 0, 255),
879 (135, 95, 0),
880 (135, 95, 95),
881 (135, 95, 135),
882 (135, 95, 175),
883 (135, 95, 215),
884 (135, 95, 255),
885 (135, 135, 0),
886 (135, 135, 95),
887 (135, 135, 135),
888 (135, 135, 175),
889 (135, 135, 215),
890 (135, 135, 255),
891 (135, 175, 0),
892 (135, 175, 95),
893 (135, 175, 135),
894 (135, 175, 175),
895 (135, 175, 215),
896 (135, 175, 255),
897 (135, 215, 0),
898 (135, 215, 95),
899 (135, 215, 135),
900 (135, 215, 175),
901 (135, 215, 215),
902 (135, 215, 255),
903 (135, 255, 0),
904 (135, 255, 95),
905 (135, 255, 135),
906 (135, 255, 175),
907 (135, 255, 215),
908 (135, 255, 255),
909 (175, 0, 0),
910 (175, 0, 95),
911 (175, 0, 135),
912 (175, 0, 175),
913 (175, 0, 215),
914 (175, 0, 255),
915 (175, 95, 0),
916 (175, 95, 95),
917 (175, 95, 135),
918 (175, 95, 175),
919 (175, 95, 215),
920 (175, 95, 255),
921 (175, 135, 0),
922 (175, 135, 95),
923 (175, 135, 135),
924 (175, 135, 175),
925 (175, 135, 215),
926 (175, 135, 255),
927 (175, 175, 0),
928 (175, 175, 95),
929 (175, 175, 135),
930 (175, 175, 175),
931 (175, 175, 215),
932 (175, 175, 255),
933 (175, 215, 0),
934 (175, 215, 95),
935 (175, 215, 135),
936 (175, 215, 175),
937 (175, 215, 215),
938 (175, 215, 255),
939 (175, 255, 0),
940 (175, 255, 95),
941 (175, 255, 135),
942 (175, 255, 175),
943 (175, 255, 215),
944 (175, 255, 255),
945 (215, 0, 0),
946 (215, 0, 95),
947 (215, 0, 135),
948 (215, 0, 175),
949 (215, 0, 215),
950 (215, 0, 255),
951 (215, 95, 0),
952 (215, 95, 95),
953 (215, 95, 135),
954 (215, 95, 175),
955 (215, 95, 215),
956 (215, 95, 255),
957 (215, 135, 0),
958 (215, 135, 95),
959 (215, 135, 135),
960 (215, 135, 175),
961 (215, 135, 215),
962 (215, 135, 255),
963 (215, 175, 0),
964 (215, 175, 95),
965 (215, 175, 135),
966 (215, 175, 175),
967 (215, 175, 215),
968 (215, 175, 255),
969 (215, 215, 0),
970 (215, 215, 95),
971 (215, 215, 135),
972 (215, 215, 175),
973 (215, 215, 215),
974 (215, 215, 255),
975 (215, 255, 0),
976 (215, 255, 95),
977 (215, 255, 135),
978 (215, 255, 175),
979 (215, 255, 215),
980 (215, 255, 255),
981 (255, 0, 0),
982 (255, 0, 95),
983 (255, 0, 135),
984 (255, 0, 175),
985 (255, 0, 215),
986 (255, 0, 255),
987 (255, 95, 0),
988 (255, 95, 95),
989 (255, 95, 135),
990 (255, 95, 175),
991 (255, 95, 215),
992 (255, 95, 255),
993 (255, 135, 0),
994 (255, 135, 95),
995 (255, 135, 135),
996 (255, 135, 175),
997 (255, 135, 215),
998 (255, 135, 255),
999 (255, 175, 0),
1000 (255, 175, 95),
1001 (255, 175, 135),
1002 (255, 175, 175),
1003 (255, 175, 215),
1004 (255, 175, 255),
1005 (255, 215, 0),
1006 (255, 215, 95),
1007 (255, 215, 135),
1008 (255, 215, 175),
1009 (255, 215, 215),
1010 (255, 215, 255),
1011 (255, 255, 0),
1012 (255, 255, 95),
1013 (255, 255, 135),
1014 (255, 255, 175),
1015 (255, 255, 215),
1016 (255, 255, 255),
1017 (8, 8, 8),
1018 (18, 18, 18),
1019 (28, 28, 28),
1020 (38, 38, 38),
1021 (48, 48, 48),
1022 (58, 58, 58),
1023 (68, 68, 68),
1024 (78, 78, 78),
1025 (88, 88, 88),
1026 (98, 98, 98),
1027 (108, 108, 108),
1028 (118, 118, 118),
1029 (128, 128, 128),
1030 (138, 138, 138),
1031 (148, 148, 148),
1032 (158, 158, 158),
1033 (168, 168, 168),
1034 (178, 178, 178),
1035 (188, 188, 188),
1036 (198, 198, 198),
1037 (208, 208, 208),
1038 (218, 218, 218),
1039 (228, 228, 228),
1040 (238, 238, 238),
1041];