diff --git a/bin/bcat b/bin/bcat new file mode 100755 index 0000000..bf8ef64 --- /dev/null +++ b/bin/bcat @@ -0,0 +1,8 @@ +#!/usr/bin/env python +import sys +import base64 +from butterfly.escapes import image + +with image(): + with open(sys.argv[1], 'rb') as f: + print(base64.b64encode(f.read()).decode('ascii')) diff --git a/butterfly/__init__.py b/butterfly/__init__.py index 16b1e5c..19e553e 100644 --- a/butterfly/__init__.py +++ b/butterfly/__init__.py @@ -43,12 +43,12 @@ class Route(tornado.web.RequestHandler): def log(self): return log +# Imported from executable +if hasattr(tornado.options.options, 'debug'): + application = tornado.web.Application( + static_path=os.path.join(os.path.dirname(__file__), "static"), + template_path=os.path.join(os.path.dirname(__file__), "templates"), + debug=tornado.options.options.debug + ) -application = tornado.web.Application( - static_path=os.path.join(os.path.dirname(__file__), "static"), - template_path=os.path.join(os.path.dirname(__file__), "templates"), - debug=tornado.options.options.debug -) - - -import butterfly.routes + import butterfly.routes diff --git a/butterfly/escapes.py b/butterfly/escapes.py new file mode 100644 index 0000000..6d7d937 --- /dev/null +++ b/butterfly/escapes.py @@ -0,0 +1,29 @@ +from contextlib import contextmanager + + +@contextmanager +def html(): + print('\x1bP;HTML|') + yield + print('\x1bP') + + +@contextmanager +def image(): + print('\x1bP;IMAGE|') + yield + print('\x1bP') + + +@contextmanager +def prompt(): + print('\x1bP;PROMPT|') + yield + print('\x1bP') + + +@contextmanager +def text(): + print('\x1bP;TEXT|') + yield + print('\x1bP') diff --git a/butterfly/sass/_term_styles.sass b/butterfly/sass/_term_styles.sass index 3374942..cca7f74 100644 --- a/butterfly/sass/_term_styles.sass +++ b/butterfly/sass/_term_styles.sass @@ -44,3 +44,6 @@ $bg: #000 !default .inline-html overflow: hidden + +.inline-image + max-width: 100% diff --git a/butterfly/static/ext.js b/butterfly/static/ext.js index 3684ec8..78ff968 100644 --- a/butterfly/static/ext.js +++ b/butterfly/static/ext.js @@ -114,7 +114,7 @@ if (!next) { next = node.parentNode.parentNode.nextSibling; } - while (next.firstChild) { + while (next != null ? next.firstChild : void 0) { next = next.firstChild; } return next; @@ -301,7 +301,7 @@ })(); - addEventListener('keydown', function(e) { + document.addEventListener('keydown', function(e) { var ref, ref1; if (ref = e.keyCode, indexOf.call([16, 17, 18, 19], ref) >= 0) { return true; @@ -347,7 +347,7 @@ return true; }); - addEventListener('keyup', function(e) { + document.addEventListener('keyup', function(e) { var ref, ref1; if (ref = e.keyCode, indexOf.call([16, 17, 18, 19], ref) >= 0) { return true; @@ -368,7 +368,7 @@ return true; }); - addEventListener('dblclick', function(e) { + document.addEventListener('dblclick', function(e) { var anchorNode, anchorOffset, new_range, range, sel; if (e.ctrlKey || e.altkey) { return; diff --git a/butterfly/static/main.css b/butterfly/static/main.css index 3b119a9..d132bae 100644 --- a/butterfly/static/main.css +++ b/butterfly/static/main.css @@ -3093,3 +3093,6 @@ body[data-native-scroll="yes"] ::-webkit-scrollbar-thumb { .inline-html { overflow: hidden; } + +.inline-image { + max-width: 100%; } diff --git a/butterfly/static/main.js b/butterfly/static/main.js index 2cb7921..fd756ca 100644 --- a/butterfly/static/main.js +++ b/butterfly/static/main.js @@ -179,7 +179,7 @@ this.element.appendChild(div); this.children.push(div); } - this.scrollback = 5000; + this.scrollback = 10000; this.visualBell = 100; this.convertEol = false; this.termName = 'xterm'; @@ -560,7 +560,8 @@ ref3 = this.html; for (l in ref3) { html = ref3[l]; - this.element.insertBefore(html, this.children[l]); + this.children[l].innerHTML = ''; + this.children[l].appendChild(html); } this.html = {}; return this.native_scroll_to(); @@ -1144,6 +1145,15 @@ } } break; + case "IMAGE": + if (this.native_scroll) { + html = document.createElement('img'); + html.classList.add('inline-image'); + html.src = "data:image;base64," + content; + this.html[this.y] = html; + this.updateRange(this.y); + } + break; case "PROMPT": this.send(content); break; diff --git a/coffees/ext/selection.coffee b/coffees/ext/selection.coffee index 4fb6bd6..c057907 100644 --- a/coffees/ext/selection.coffee +++ b/coffees/ext/selection.coffee @@ -38,7 +38,7 @@ next_leaf = (node) -> next = node.parentNode.nextSibling if not next next = node.parentNode.parentNode.nextSibling - while next.firstChild + while next?.firstChild next = next.firstChild next @@ -169,7 +169,7 @@ class Selection return needle -addEventListener 'keydown', (e) -> +document.addEventListener 'keydown', (e) -> return true if e.keyCode in [16..19] # Paste natural selection too if shiftkey @@ -210,7 +210,7 @@ addEventListener 'keydown', (e) -> return cancel e true -addEventListener 'keyup', (e) -> +document.addEventListener 'keyup', (e) -> return true if e.keyCode in [16..19] if selection @@ -225,7 +225,7 @@ addEventListener 'keyup', (e) -> return true true -addEventListener 'dblclick', (e) -> +document.addEventListener 'dblclick', (e) -> return if e.ctrlKey or e.altkey sel = getSelection() return if sel.isCollapsed or sel.toString().match /\s/ diff --git a/coffees/term.coffee b/coffees/term.coffee index d1b0fd6..aa0bf80 100644 --- a/coffees/term.coffee +++ b/coffees/term.coffee @@ -88,7 +88,7 @@ class Terminal @element.appendChild(div) @children.push(div) - @scrollback = 5000 + @scrollback = 10000 @visualBell = 100 @convertEol = false @@ -459,7 +459,8 @@ class Terminal if @native_scroll for l, html of @html - @element.insertBefore(html, @children[l]) + @children[l].innerHTML = '' + @children[l].appendChild(html) @html = {} @native_scroll_to() @@ -540,6 +541,7 @@ class Terminal scroll = @parent.scrollHeight - @parent.getBoundingClientRect().height @parent.scrollTop = scroll + scroll_display: (disp) -> if @native_scroll @native_scroll_to @parent.scrollTop + disp * @char_size.height @@ -1089,6 +1091,14 @@ class Terminal @scroll() line++ + when "IMAGE" + if @native_scroll + html = document.createElement 'img' + html.classList.add 'inline-image' + html.src = "data:image;base64," + content + @html[@y] = html + @updateRange @y + when "PROMPT" @send content