UNPKG

neo-neo-bblessed

Version:

A fork of neo-blessed (which is a fork of blessed) with bug fixes and maintenance.

322 lines (276 loc) 6.95 kB
/** * ansi-viewer * ANSI art viewer for node. * Copyright (c) 2015, Christopher Jeffrey and contributors (MIT License). * https://github.com/chjj/blessed */ import * as blessed from '../../lib/blessed'; import * as fs from 'fs'; import * as singlebyte from './singlebyte'; // Note: request would need to be installed separately const request = require('request'); // $ wget -r -o log --tries=10 'http://artscene.textfiles.com/ansi/' // $ grep 'http.*\.ans$' log | awk '{ print $3 }' > ansi-art.list const urls = fs .readFileSync(__dirname + '/ansi-art.list', 'utf8') .trim() .split('\n'); const map = urls.reduce(function (map: Record<string, string>, url: string) { const match = /([^.\/]+\/[^.\/]+)\.ans$/.exec(url); if (match) { map[match[1]] = url; } return map; }, {}); const max = Object.keys(map).reduce(function (out: number, text: string) { return Math.max(out, text.length); }, 0) + 6; const screen = blessed.screen({ smartCSR: true, dockBorders: true, }); const art = blessed.terminal({ parent: screen, left: 0, top: 0, height: 60, // some are 78/80, some are 80/82 width: 82, border: 'line', tags: true, label: ' {bold}{cyan-fg}ANSI Art{/cyan-fg}{/bold} (Drag Me) ', handler: function () {}, draggable: true, }); const list = blessed.list({ parent: screen, label: ' {bold}{cyan-fg}Art List{/cyan-fg}{/bold} (Drag Me) ', tags: true, draggable: true, top: 0, right: 0, width: max, height: '50%', keys: true, vi: true, mouse: true, border: 'line', scrollbar: { ch: ' ', track: { bg: 'cyan', }, style: { inverse: true, }, }, style: { item: { hover: { bg: 'blue', }, }, selected: { bg: 'blue', bold: true, }, }, search: function (callback: (err: Error | null, value?: string) => void) { prompt.input('Search:', '', function (err: Error | null, value: string) { if (err) return; return callback(null, value); }); }, }); const status = blessed.box({ parent: screen, bottom: 0, right: 0, height: 1, width: 'shrink', style: { bg: 'blue', }, content: 'Select your piece of ANSI art (`/` to search).', }); const loader = blessed.loading({ parent: screen, top: 'center', left: 'center', height: 5, align: 'center', width: '50%', tags: true, hidden: true, border: 'line', }); const msg = blessed.message({ parent: screen, top: 'center', left: 'center', height: 'shrink', width: '50%', align: 'center', tags: true, hidden: true, border: 'line', }); const prompt = blessed.prompt({ parent: screen, top: 'center', left: 'center', height: 'shrink', width: 'shrink', keys: true, vi: true, mouse: true, tags: true, border: 'line', hidden: true, }); list.setItems(Object.keys(map)); list.on('select', function (el: any, selected: number) { if ((list as any)._.rendering) return; const name = el.getText(); const url = map[name]; status.setContent(url); (list as any)._.rendering = true; loader.load('Loading...'); request( { uri: url, encoding: null, }, function (err: Error | null, res: any, body: Buffer) { (list as any)._.rendering = false; loader.stop(); if (err) { return msg.error(err.message); } if (!body) { return msg.error('No body.'); } return cp437ToUtf8(body, function (err: Error | null, bodyText?: string) { if (err) { return msg.error(err.message); } if (!bodyText) { return msg.error('No converted body.'); } if (process.argv[2] === '--debug') { const filename = name.replace(/\//g, '.') + '.ans'; fs.writeFileSync(__dirname + '/' + filename, bodyText); } // Remove text: bodyText = bodyText.replace( 'Downloaded From P-80 International Information Systems 304-744-2253', '' ); // Remove MCI codes: bodyText = bodyText.replace(/%[A-Z0-9]{2}/g, ''); // ^A (SOH) seems to need to produce CRLF in some cases?? // bodyText = bodyText.replace(/\x01/g, '\r\n'); // Reset and write the art: (art as any).term.reset(); (art as any).term.write(bodyText); (art as any).term.cursorHidden = true; screen.render(); if (process.argv[2] === '--debug' || process.argv[2] === '--save') { takeScreenshot(name); } }); } ); }); list.items.forEach(function (item: any, i: number) { const text = item.getText(); item.setHover(map[text]); }); list.focus(); list.enterSelected(0); screen.key('h', function () { list.toggle(); if (list.visible) list.focus(); }); screen.key('r', function () { shuffle(); }); screen.key('S-s', function () { takeScreenshot((list as any).ritems[(list as any).selected]); }); screen.key('s', function () { slideshow(); }); screen.key('q', function () { return process.exit(0); }); screen.render(); /** * Helpers */ // https://github.com/chjj/blessed/issues/127 // https://github.com/Mithgol/node-singlebyte function cp437ToUtf8( buf: Buffer, callback: (err: Error | null, result?: string) => void ): void { try { return callback(null, singlebyte.bufToStr(buf, 'cp437')); } catch (e) { return callback(e as Error); } } // Animating ANSI art doesn't work for screenshots. const ANIMATING = [ 'bbs/void3', 'holiday/xmasfwks', 'unsorted/diver', 'unsorted/mash-chp', 'unsorted/ryans47', 'unsorted/xmasfwks', ]; function takeScreenshot(name: string): void { const filename = name.replace(/\//g, '.') + '.ans.sgr'; let image: string; // Animating art hangs terminal during screenshot as of right now. if (~ANIMATING.indexOf(name)) { image = (blessed.element.prototype as any).screenshot.call( art, 0 - (art as any).ileft, art.width - (art as any).iright, 0 - (art as any).itop, art.height - (art as any).ibottom ); } else { image = (art as any).screenshot(); } fs.writeFileSync(__dirname + '/' + filename, image); msg.display('Screenshot taken.'); } function slideshow(): void { if (!(screen as any)._.slideshow) { (screen as any)._.slideshow = setInterval( (function slide() { if ((screen as any).lockKeys) return; const i = ((list.items.length - 1) * Math.random()) | 0; list.enterSelected(i); return slide; })(), 3000 ); msg.display('Slideshow started.'); } else { clearInterval((screen as any)._.slideshow); delete (screen as any)._.slideshow; msg.display('Slideshow stopped.'); } } function shuffle(): void { const items = Object.keys(map).sort(function (key: string) { return Math.random() > 0.5 ? 1 : -1; }); list.setItems(items); screen.render(); msg.display('Shuffled items.'); }