diff --git a/TODO b/TODO index da864cc..226f3b3 100644 --- a/TODO +++ b/TODO @@ -34,3 +34,4 @@ * Consolidate fpb and fput into just fpb and have it automatically deal with making pastes or not * Language is set both in query string and POST body of fpb cli * Verify behavior around final end-of-lines matches across highlighters +* Auto dark mode diff --git a/cmd/print-styles-css/print.go b/cmd/print-styles-css/print.go index 69153ee..2078a6e 100644 --- a/cmd/print-styles-css/print.go +++ b/cmd/print-styles-css/print.go @@ -56,54 +56,39 @@ func main() { } /* ANSI colors */ - .text .chroma .fg-0 { - color: %s; - } - .text .chroma .fg-0-faint { - color: %s; - } - .text .chroma .fg-1 { - color: %s; - } - .text .chroma .fg-1-faint { - color: %s; - } - .text .chroma .fg-2 { - color: %s; - } - .text .chroma .fg-2-faint { - color: %s; - } - .text .chroma .fg-3 { - color: %s; - } - .text .chroma .fg-3-faint { - color: %s; - } - .text .chroma .fg-4 { - color: %s; - } - .text .chroma .fg-4-faint { - color: %s; - } - .text .chroma .fg-5 { - color: %s; - } - .text .chroma .fg-5-faint { - color: %s; - } - .text .chroma .fg-6 { - color: %s; - } - .text .chroma .fg-6-faint { - color: %s; - } - .text .chroma .fg-7 { - color: %s; - } - .text .chroma .fg-7-faint { - color: %s; - } + .text .chroma .fg-0 { color: %s; } + .text .chroma .fg-1 { color: %s; } + .text .chroma .fg-2 { color: %s; } + .text .chroma .fg-3 { color: %s; } + .text .chroma .fg-4 { color: %s; } + .text .chroma .fg-5 { color: %s; } + .text .chroma .fg-6 { color: %s; } + .text .chroma .fg-7 { color: %s; } + .text .chroma .fg-8 { color: %s; } + .text .chroma .fg-9 { color: %s; } + .text .chroma .fg-10 { color: %s; } + .text .chroma .fg-11 { color: %s; } + .text .chroma .fg-12 { color: %s; } + .text .chroma .fg-13 { color: %s; } + .text .chroma .fg-14 { color: %s; } + .text .chroma .fg-15 { color: %s; } + .text .chroma .bg-0 { background-color: %s; } + .text .chroma .bg-1 { background-color: %s; } + .text .chroma .bg-2 { background-color: %s; } + .text .chroma .bg-3 { background-color: %s; } + .text .chroma .bg-4 { background-color: %s; } + .text .chroma .bg-5 { background-color: %s; } + .text .chroma .bg-6 { background-color: %s; } + .text .chroma .bg-7 { background-color: %s; } + .text .chroma .bg-8 { background-color: %s; } + .text .chroma .bg-9 { background-color: %s; } + .text .chroma .bg-10 { background-color: %s; } + .text .chroma .bg-11 { background-color: %s; } + .text .chroma .bg-12 { background-color: %s; } + .text .chroma .bg-13 { background-color: %s; } + .text .chroma .bg-14 { background-color: %s; } + .text .chroma .bg-15 { background-color: %s; } + } `, style.Name, @@ -122,21 +107,37 @@ func main() { style.FluffyColors.DiffRemoveLineBackground, style.FluffyColors.DiffRemoveSelectedLineBackground, style.ANSIColors.Foreground.Black, - style.ANSIColors.ForegroundFaint.Black, style.ANSIColors.Foreground.Red, - style.ANSIColors.ForegroundFaint.Red, style.ANSIColors.Foreground.Green, - style.ANSIColors.ForegroundFaint.Green, style.ANSIColors.Foreground.Yellow, - style.ANSIColors.ForegroundFaint.Yellow, style.ANSIColors.Foreground.Blue, - style.ANSIColors.ForegroundFaint.Blue, style.ANSIColors.Foreground.Magenta, - style.ANSIColors.ForegroundFaint.Magenta, style.ANSIColors.Foreground.Cyan, - style.ANSIColors.ForegroundFaint.Cyan, style.ANSIColors.Foreground.White, - style.ANSIColors.ForegroundFaint.White, + style.ANSIColors.Background.BrightBlack, + style.ANSIColors.Background.BrightRed, + style.ANSIColors.Background.BrightGreen, + style.ANSIColors.Background.BrightYellow, + style.ANSIColors.Background.BrightBlue, + style.ANSIColors.Background.BrightMagenta, + style.ANSIColors.Background.BrightCyan, + style.ANSIColors.Background.BrightWhite, + style.ANSIColors.Background.Black, + style.ANSIColors.Background.Red, + style.ANSIColors.Background.Green, + style.ANSIColors.Background.Yellow, + style.ANSIColors.Background.Blue, + style.ANSIColors.Background.Magenta, + style.ANSIColors.Background.Cyan, + style.ANSIColors.Background.White, + style.ANSIColors.Background.BrightBlack, + style.ANSIColors.Background.BrightRed, + style.ANSIColors.Background.BrightGreen, + style.ANSIColors.Background.BrightYellow, + style.ANSIColors.Background.BrightBlue, + style.ANSIColors.Background.BrightMagenta, + style.ANSIColors.Background.BrightCyan, + style.ANSIColors.Background.BrightWhite, ) } } diff --git a/server/highlighting/ansi_colors.go b/server/highlighting/ansi_colors.go index d0ddf17..c5a8552 100644 --- a/server/highlighting/ansi_colors.go +++ b/server/highlighting/ansi_colors.go @@ -8,87 +8,104 @@ import ( ) type ANSIColors struct { - Black Color - Red Color - Green Color - Yellow Color - Blue Color - Magenta Color - Cyan Color - White Color + Black Color + Red Color + Green Color + Yellow Color + Blue Color + Magenta Color + Cyan Color + White Color + BrightBlack Color + BrightRed Color + BrightGreen Color + BrightYellow Color + BrightBlue Color + BrightMagenta Color + BrightCyan Color + BrightWhite Color } type ANSIColorSet struct { - Foreground ANSIColors - ForegroundFaint ANSIColors - Background ANSIColors + Foreground ANSIColors + Background ANSIColors } var ansiColorsLight = &ANSIColorSet{ Foreground: ANSIColors{ - Black: "#000000", - Red: "#EF2929", - Green: "#62CA00", - Yellow: "#DAC200", - Blue: "#3465A4", - Magenta: "#CE42BE", - Cyan: "#34E2E2", - White: "#FFFFFF", - }, - // TODO: double-check these colors - ForegroundFaint: ANSIColors{ - Black: "#676767", - Red: "#ff6d67", - Green: "#5ff967", - Yellow: "#fefb67", - Blue: "#6871ff", - Magenta: "#ff76ff", - Cyan: "#5ffdff", - White: "#feffff", + Black: "#000000", + Red: "#EF2929", + Green: "#62CA00", + Yellow: "#DAC200", + Blue: "#3465A4", + Magenta: "#CE42BE", + Cyan: "#34E2E2", + White: "#FFFFFF", + BrightBlack: "#676767", + BrightRed: "#ff6d67", + BrightGreen: "#5ff967", + BrightYellow: "#fefb67", + BrightBlue: "#6871ff", + BrightMagenta: "#ff76ff", + BrightCyan: "#5ffdff", + BrightWhite: "#feffff", }, Background: ANSIColors{ - Black: "#000000", - Red: "#EF2929", - Green: "#8AE234", - Yellow: "#FCE94F", - Blue: "#3465A4", - Magenta: "#C509C5", - Cyan: "#34E2E2", - White: "#FFFFFF", + Black: "#000000", + Red: "#EF2929", + Green: "#8AE234", + Yellow: "#FCE94F", + Blue: "#3465A4", + Magenta: "#C509C5", + Cyan: "#34E2E2", + White: "#FFFFFF", + BrightBlack: "#676767", + BrightRed: "#ff6d67", + BrightGreen: "#5ff967", + BrightYellow: "#fefb67", + BrightBlue: "#6871ff", + BrightMagenta: "#ff76ff", + BrightCyan: "#5ffdff", + BrightWhite: "#feffff", }, } var ansiColorsDark = &ANSIColorSet{ Foreground: ANSIColors{ - Black: "#555753", - Red: "#FF5C5C", - Green: "#8AE234", - Yellow: "#FCE94F", - Blue: "#8FB6E1", - Magenta: "#FF80F1", - Cyan: "#34E2E2", - White: "#EEEEEC", - }, - // TODO: double-check these colors - ForegroundFaint: ANSIColors{ - Black: "#676767", - Red: "#ff6d67", - Green: "#5ff967", - Yellow: "#fefb67", - Blue: "#6871ff", - Magenta: "#ff76ff", - Cyan: "#5ffdff", - White: "#feffff", + Black: "#555753", + Red: "#FF5C5C", + Green: "#8AE234", + Yellow: "#FCE94F", + Blue: "#8FB6E1", + Magenta: "#FF80F1", + Cyan: "#34E2E2", + White: "#EEEEEC", + BrightBlack: "#676767", + BrightRed: "#ff6d67", + BrightGreen: "#5ff967", + BrightYellow: "#fefb67", + BrightBlue: "#6871ff", + BrightMagenta: "#ff76ff", + BrightCyan: "#5ffdff", + BrightWhite: "#feffff", }, Background: ANSIColors{ - Black: "#555753", - Red: "#F03D3D", - Green: "#6ABC1B", - Yellow: "#CEB917", - Blue: "#6392C6", - Magenta: "#FF80F1", - Cyan: "#2FC0C0", - White: "#BFBFBF", + Black: "#555753", + Red: "#F03D3D", + Green: "#6ABC1B", + Yellow: "#CEB917", + Blue: "#6392C6", + Magenta: "#FF80F1", + Cyan: "#2FC0C0", + White: "#BFBFBF", + BrightBlack: "#676767", + BrightRed: "#ff6d67", + BrightGreen: "#5ff967", + BrightYellow: "#fefb67", + BrightBlue: "#6871ff", + BrightMagenta: "#ff76ff", + BrightCyan: "#5ffdff", + BrightWhite: "#feffff", }, } @@ -199,6 +216,9 @@ func (s ansiState) cssStyles() map[string]string { if s.bold { ret["font-weight"] = "bold" } + if s.faint { + ret["opacity"] = "0.5" + } if s.italic { ret["font-style"] = "italic" } @@ -209,19 +229,20 @@ func (s ansiState) cssStyles() map[string]string { ret["text-decoration"] = "line-through" } if s.foreground != nil && s.foreground.rgb != nil { - r := s.foreground.rgb.r - g := s.foreground.rgb.g - b := s.foreground.rgb.b - if s.faint { - // TODO: no idea if this is correct - r = r / 2 - g = g / 2 - b = b / 2 - } - ret["color"] = fmt.Sprintf("rgb(%d, %d, %d)", r, g, b) + ret["color"] = fmt.Sprintf( + "rgb(%d, %d, %d)", + s.foreground.rgb.r, + s.foreground.rgb.g, + s.foreground.rgb.b, + ) } if s.background != nil && s.background.rgb != nil { - ret["background-color"] = fmt.Sprintf("rgb(%d, %d, %d)", s.background.rgb.r, s.background.rgb.g, s.background.rgb.b) + ret["background-color"] = fmt.Sprintf( + "rgb(%d, %d, %d)", + s.background.rgb.r, + s.background.rgb.g, + s.background.rgb.b, + ) } return ret } @@ -242,66 +263,104 @@ func (s ansiState) cssClasses() []string { return ret } -func (s ansiState) update(command string) ansiState { - switch command { - case "0": - return ansiState{} - case "1": - s.bold = true - case "2": - s.faint = true - case "3": - s.italic = true - case "4": - s.underline = true - case "9": - s.strikethrough = true - case "22": - s.bold = false - s.faint = false - case "23": - s.italic = false - case "24": - s.underline = false - case "29": - s.strikethrough = false - case "30": - s.foreground = &ansiColor{index: 0} - case "31": - s.foreground = &ansiColor{index: 1} - case "32": - s.foreground = &ansiColor{index: 2} - case "33": - s.foreground = &ansiColor{index: 3} - case "34": - s.foreground = &ansiColor{index: 4} - case "35": - s.foreground = &ansiColor{index: 5} - case "36": - s.foreground = &ansiColor{index: 6} - case "37": - s.foreground = &ansiColor{index: 7} - case "39": - s.foreground = nil - case "40": - s.background = &ansiColor{index: 0} - case "41": - s.background = &ansiColor{index: 1} - case "42": - s.background = &ansiColor{index: 2} - case "43": - s.background = &ansiColor{index: 3} - case "44": - s.background = &ansiColor{index: 4} - case "45": - s.background = &ansiColor{index: 5} - case "46": - s.background = &ansiColor{index: 6} - case "47": - s.background = &ansiColor{index: 7} - case "49": - s.background = nil +func ansi256Color(index uint8) *ansiColor { + switch { + case index < 16: // Standard colors + return &ansiColor{index: index} + case index >= 16 && index < 232: // 6x6x6 cube (216 colors) + index -= 16 + r := index / 36 + g := (index % 36) / 6 + b := index % 6 + return &ansiColor{rgb: &rgb{ + r: uint8(r*40 + 55), + g: uint8(g*40 + 55), + b: uint8(b*40 + 55), + }} + case index >= 232 && index <= 255: // Grayscale ramp (24 colors) + value := uint8((index-232)*10 + 8) + return &ansiColor{rgb: &rgb{r: value, g: value, b: value}} + } + panic("unreachable") +} + +func (s ansiState) update(commands []string) ansiState { +outer: + for len(commands) > 0 { + command := commands[0] + commands = commands[1:] + switch command { + case "0": + s = ansiState{} + case "1": + s.bold = true + case "2": + s.faint = true + case "3": + s.italic = true + case "4": + s.underline = true + case "9": + s.strikethrough = true + case "22": + s.bold = false + s.faint = false + case "23": + s.italic = false + case "24": + s.underline = false + case "29": + s.strikethrough = false + case "30", "31", "32", "33", "34", "35", "36", "37": + s.foreground = &ansiColor{index: uint8(command[1] - '0')} + case "39": + s.foreground = nil + case "40", "41", "42", "43", "44", "45", "46", "47": + s.background = &ansiColor{index: uint8(command[1] - '0')} + case "49": + s.background = nil + case "90", "91", "92", "93", "94", "95", "96", "97": + s.foreground = &ansiColor{index: uint8(command[1] - '0' + 8)} + case "100", "101", "102", "103", "104", "105", "106", "107": + s.background = &ansiColor{index: uint8(command[2] - '0' + 8)} + case "38", "48": + if len(commands) < 1 { + break outer + } + typeCode := commands[0] + commands = commands[1:] + if typeCode == "5" { + if len(commands) < 1 { + break outer + } + var index uint8 + fmt.Sscanf(commands[0], "%d", &index) + commands = commands[1:] + if command == "38" { + s.foreground = ansi256Color(index) + } else { + s.background = ansi256Color(index) + } + } else if typeCode == "2" { + if len(commands) < 3 { + break outer + } + r := commands[0] + g := commands[1] + b := commands[2] + commands = commands[3:] + rgb := rgb{} + fmt.Sscanf(r, "%d", &rgb.r) + fmt.Sscanf(g, "%d", &rgb.g) + fmt.Sscanf(b, "%d", &rgb.b) + if command == "38" { + s.foreground = &ansiColor{rgb: &rgb} + } else { + s.background = &ansiColor{rgb: &rgb} + } + } + } } return s } @@ -342,9 +401,7 @@ func parseANSI(text string, state ansiState) []parsedANSI { commands := cur.String() cur.Reset() if text[i] == 'm' { - for _, command := range strings.Split(commands, ";") { - state = state.update(command) - } + state = state.update(strings.Split(commands, ";")) } continue } diff --git a/server/highlighting/highlighting.go b/server/highlighting/highlighting.go index 2d6610b..6ef7215 100644 --- a/server/highlighting/highlighting.go +++ b/server/highlighting/highlighting.go @@ -287,8 +287,7 @@ func GuessHighlighterForPaste(text, language, filename string) Highlighter { } if (language == "" || language == "autodetect") && looksLikeAnsiColor(text) { - // TODO: implement - return &PlainTextHighlighter{} + return &ANSIHighlighter{} } diffRequested := (language == "diff" || diff --git a/server/security/csp.go b/server/security/csp.go index a3419e6..af76975 100644 --- a/server/security/csp.go +++ b/server/security/csp.go @@ -67,6 +67,9 @@ func NewCSPMiddleware(conf *config.Config, logger logging.Logger, next http.Hand // style-src fmt.Fprintf(&csp, "; style-src 'self' https://fonts.googleapis.com %s", fileURLBase) + if isDevStaticFileRequest(conf, r) { + fmt.Fprintf(&csp, " 'unsafe-inline'") + } // font-src fmt.Fprintf(&csp, "; font-src https://fonts.gstatic.com %s", fileURLBase) diff --git a/server/security/csp_test.go b/server/security/csp_test.go index c6f114f..3dc1da2 100644 --- a/server/security/csp_test.go +++ b/server/security/csp_test.go @@ -33,7 +33,7 @@ var ( []string{ `default-src 'self' https://fancy-cdn.com \*`, `script-src https://ajax.googleapis.com https://fancy-cdn.com 'unsafe-inline'`, - `style-src 'self' https://fonts.googleapis.com https://fancy-cdn.com`, + `style-src 'self' https://fonts.googleapis.com https://fancy-cdn.com 'unsafe-inline'`, `font-src https://fonts.gstatic.com https://fancy-cdn.com`, }, "; ", diff --git a/server/views/dev-pastes/ansi-color b/server/views/dev-pastes/ansi-color index f7c4981..21a335c 100644 --- a/server/views/dev-pastes/ansi-color +++ b/server/views/dev-pastes/ansi-color @@ -30,11 +30,18 @@ drwxr-xr-x 16 root root 4.0K Aug 3 13:25 var lrwxrwxrwx 1 root root 27 Jun 30 16:40 vmlinuz -> boot/vmlinuz-4.4.0-1022-aws total 1.4G +Standard colors: hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world hello world +Bright colors: +hello world hello world hello world hello world hello world hello world hello world hello world +hello world hello world hello world hello world hello world hello world hello world hello world +hello world hello world hello world hello world hello world hello world hello world hello world +hello world hello world hello world hello world hello world hello world hello world hello world + ### What is fluffy? fluffy is a Flask-based web application that allows you to upload arbitrary @@ -63,3 +70,71 @@ total 1.4G designed so that it is easy to stop accepting uploads while still serving existing files, with the hope being that a "shut down" would involve no longer accepting uploads, but still continuing to serve existing uploads. + + +Standard:  0  1  2  3  4  5  6  7 +Intense:  8  9  10  11  12  13  14  15 + + 16  17  18  19  20  21   34  35  36  37  38  39 + 52  53  54  55  56  57   70  71  72  73  74  75 + 88  89  90  91  92  93   106  107  108  109  110  111 + 124  125  126  127  128  129   142  143  144  145  146  147 + 160  161  162  163  164  165   178  179  180  181  182  183 + 196  197  198  199  200  201   214  215  216  217  218  219 + + 22  23  24  25  26  27   40  41  42  43  44  45 + 58  59  60  61  62  63   76  77  78  79  80  81 + 94  95  96  97  98  99   112  113  114  115  116  117 + 130  131  132  133  134  135   148  149  150  151  152  153 + 166  167  168  169  170  171   184  185  186  187  188  189 + 202  203  204  205  206  207   220  221  222  223  224  225 + + 28  29  30  31  32  33   46  47  48  49  50  51 + 64  65  66  67  68  69   82  83  84  85  86  87 + 100  101  102  103  104  105   118  119  120  121  122  123 + 136  137  138  139  140  141   154  155  156  157  158  159 + 172  173  174  175  176  177   190  191  192  193  194  195 + 208  209  210  211  212  213   226  227  228  229  230  231 + +Grays:  232  233  234  235  236  237  238  239  240  241  242  243 +  244  245  246  247  248  249  250  251  252  253  254  255 + + +--bold Sample --faint Sample +--italic Sample --fraktur Sample +--underline Sample --double-underline [4:2mSample +--blink Sample --rapid-blink Sample +--inverse Sample --invisible Sample +--strike Sample --frame Sample +--encircle Sample --overline [5:3mSample +--ideogram-right Sample --ideogram-right-double Sample +--ideogram-left Sample --ideogram-left-double Sample +--ideogram-stress Sample + + black red green yellow blue magenta cyan white +(none) fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-black fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-red fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-green fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-yellow fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-blue fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-magenta fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-cyan fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB +bg-white fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB ++ intense fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB fnbFNB + +Legend: + Normal color: f = faint, n = normal, b = bold. + Intense color: F = faint, N = normal, B = bold. + + +True color: + +/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/ diff --git a/server/views/uploads_test.go b/server/views/uploads_test.go index 3fc2999..6ce3b16 100644 --- a/server/views/uploads_test.go +++ b/server/views/uploads_test.go @@ -387,7 +387,7 @@ func TestPaste(t *testing.T) { wantLanguage: "Python", wantNumLines: 1, wantPaste: testfunc.ParsedPaste{ - DefaultStyleName: "xcode", + DefaultStyleName: "default", ToolbarInfoLine: "1 line of Python", HasDiffButtons: false, Texts: 1, @@ -413,7 +413,7 @@ func TestPaste(t *testing.T) { wantLanguage: "Python", wantNumLines: 1, wantPaste: testfunc.ParsedPaste{ - DefaultStyleName: "xcode", + DefaultStyleName: "default", ToolbarInfoLine: "1 line of Python", HasDiffButtons: false, Texts: 1, @@ -437,7 +437,7 @@ print("Hello, world!") wantLanguage: "Rendered Markdown", wantNumLines: 12, wantPaste: testfunc.ParsedPaste{ - DefaultStyleName: "xcode", + DefaultStyleName: "default", ToolbarInfoLine: "12 lines of Rendered Markdown", HasDiffButtons: false, Texts: 1,