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