commit 1bbe1ffd8172cc3678ddf14355f77a0a6aaee42d
parent 53f9dc3a46af138fc2f302cf0bb6ee87de7a60ff
Author: Luxferre <lux@ferre>
Date: Mon, 3 Apr 2023 12:37:10 +0300
Added basic terminal coloring to the infolines
Diffstat:
4 files changed, 130 insertions(+), 4 deletions(-)
diff --git a/README.md b/README.md
@@ -65,6 +65,7 @@ Note that currently, Kopher and hi01379.js assume that the search request from a
- Unlimited navigation history (doesn't persist between sessions)
- Up to 10 numbered bookmarks plus customizable homepage
- Binary blob downloads (see "Downloads")
+- Limited support of ANSI terminal escape codes in Gophermap infolines (see "Does it have terminal emulation support for character styling?")
## Controls
@@ -105,9 +106,17 @@ For your convenience, only the current hole hostname is always displayed in the
Yes. In fact, UTF-8 is the only supported encoding. As ASCII is a subset of UTF-8, old ASCII-only resources will be displayed just fine as well.
-## Does it have terminal emulation support for character coloring purposes?
+## Does it have terminal emulation support for character styling?
-No, but that feature is considered to be implemented if this client gains more traction and if there still is enough amount of the resources that actually use VT escape characters.
+As of April 2023, Kopher implements a subset of ANSI terminal escape codes for text styling purposes with the following limitations:
+
+- they are applied to the Gophermap information lines **only** (and detected and deleted everywhere else);
+- they only work within the single infoline or a block of sequential infolines and all styling gets reset when another entry type is encountered, even if the reset sequence isn't there;
+- only the basic 8/16-color palette is implemented (because these codes physically translate into corresponding data attributes);
+- beside the colors, the following text attributes are implemented: bold, italic, underline, blinking, inverse, hidden, strikethrough;
+- any unsupported escape sequences are automatically deleted from the rendered output.
+
+All these limitations are intended to keep Kopher's codebase simple, and not likely to change in the foreseeable future.
## Can I use the hi01379.js library in non-KaiOS environments?
diff --git a/app.css b/app.css
@@ -64,3 +64,54 @@ footer .status {width: 100%; text-overflow: ellipsis; overflow: hidden; white-sp
}
.content a[data-focused="1"] {text-decoration: underline}
+/* Terminal infoline styling */
+
+/* modes = ['', 'BB', 'FN', 'IT', 'UL', 'BL', '', 'IN', 'HI', 'ST'] */
+
+/* colors (FC/BC): 0 black, 1 red, 2 green, 3 yellow, 4 blue, 5 magenta, 6 cyan, 7 white */
+
+.content span[data-styling] {
+ --ds-fg-color: var(--fg-color);
+ --ds-bg-color: var(--bg-color);
+ background: var(--ds-bg-color);
+ color: var(--ds-fg-color);
+}
+
+@keyframes strict_blink {
+ 0% {
+ visibility: hidden
+ }
+ 50% {
+ visibility: hidden
+ }
+ 100% {
+ visibility: visible
+ }
+}
+
+.content span[data-styling~="BB"] {font-weight: bolder}
+.content span[data-styling~="IT"] {font-style: italic}
+.content span[data-styling~="UL"] {text-decoration: underline}
+.content span[data-styling~="BL"] {animation: 2s linear infinite strict_blink}
+.content span[data-styling~="IN"] {background:var(--ds-fg-color);color:var(--ds-bg-color)}
+.content span[data-styling~="HI"] {visibility: hidden}
+.content span[data-styling~="ST"] {text-decoration: line-through}
+
+.content span[data-styling~="FC0"] {--ds-fg-color: black}
+.content span[data-styling~="FC1"] {--ds-fg-color: red}
+.content span[data-styling~="FC2"] {--ds-fg-color: green}
+.content span[data-styling~="FC3"] {--ds-fg-color: yellow}
+.content span[data-styling~="FC4"] {--ds-fg-color: blue}
+.content span[data-styling~="FC5"] {--ds-fg-color: magenta}
+.content span[data-styling~="FC6"] {--ds-fg-color: cyan}
+.content span[data-styling~="FC7"] {--ds-fg-color: white}
+
+.content span[data-styling~="BC0"] {--ds-bg-color: black}
+.content span[data-styling~="BC1"] {--ds-bg-color: red}
+.content span[data-styling~="BC2"] {--ds-bg-color: green}
+.content span[data-styling~="BC3"] {--ds-bg-color: yellow}
+.content span[data-styling~="BC4"] {--ds-bg-color: blue}
+.content span[data-styling~="BC5"] {--ds-bg-color: magenta}
+.content span[data-styling~="BC6"] {--ds-bg-color: cyan}
+.content span[data-styling~="BC7"] {--ds-bg-color: white}
+
diff --git a/js/hi01379.js b/js/hi01379.js
@@ -28,11 +28,74 @@ Hi01379 = (function(psGopherRequest) {
}).join('\n')
}
+ function terminalize(str, attrs) { // partial terminal styling support with ANSI codes
+ if(!attrs) attrs = [] // accept previous attributes array if present
+ var prevattrs = attrs.join(' ') // preserve stringified copy
+ var out = '', c, styleseq, sl, l = str.length, i, j, att, attindex, strattrs,
+ visualOpenTag='<span data-styling="%s">', visualCloseTag='</span>',
+ modes = ['', 'BB', 'FN', 'IT', 'UL', 'BL', '', 'IN', 'HI', 'ST']
+ for(i=0;i<l;i++) {
+ c = str[i]
+ if(c === '\x1b') { // escape character encountered
+ c = str[i+1] // get the next character, don't increase the counter yet
+ if(c === '[') { // ANSI sequence starting
+ i++ // increase the counter, now it's pointing to [
+ styleseq='' // init style sequence buffer
+ do { // read the style sequence until m character or any whitespace
+ c = str[++i] // increase the counter once more, [ is ignored
+ styleseq += c
+ } while(c !== 'm' && c !== ' ' && c !== '\t' && c !== '\r' && c !== '\n')
+ styleseq = styleseq.slice(0, -1).split(';') // attributes are delimited by ;
+ for(sl=styleseq.length,j=0;j<sl;j++) { // iterate over attributes in their order
+ att = parseInt(styleseq[j]) // in this case parseInt is more reliable than |0
+ if(att === 0) attrs = [] // reset all styling
+ else if(att >= 30 && att <= 37) attrs.push('FC'+(att-30)) // foreground color
+ else if(att >= 40 && att <= 47) attrs.push('BC'+(att-40)) // background color
+ else if(att < 10) attrs.push(modes[att]) // activate special modes
+ else if(att > 20 && att < 30) { // deactivate special modes
+ if(att === 22) { // also deactivate bold mode 1
+ attindex = attrs.indexOf('BB')
+ if(attindex > -1)
+ attrs.splice(attindex, 1)
+ }
+ attindex = attrs.indexOf(modes[att - 20])
+ if(attindex > -1)
+ attrs.splice(attindex, 1)
+ }
+ else if(att === 39) { // reset only the foreground color
+ attrs = attrs.map(function(v) {
+ return v.startsWith('FC') ? '' : v
+ }).filter(String)
+ }
+ else if(att === 49) { // reset only the background color
+ attrs = attrs.map(function(v) {
+ return v.startsWith('BC') ? '' : v
+ }).filter(String)
+ }
+ }
+ // now we have our attrs array with all styling applied, output the tags
+ strattrs = attrs.join(' ')
+ if(strattrs != prevattrs) { // there are some changes
+ if(prevattrs != '') // previous attributes were not empty
+ out += visualCloseTag
+ if(strattrs) // current attributes are not empty
+ out += visualOpenTag.replace('%s', strattrs)
+ prevattrs = strattrs // update the previous attribute cache
+ }
+ } // don't add any processed characters "as is" to the output
+ else continue // skip the escape and proceed with the loop
+ }
+ else out += c // just append it to the output
+ }
+ return {output: out, attrs: attrs} // return both output and remaining attributes
+ }
+
function gophermapToHTML(gmap, chost, cport) {
if(!cport) cport = 70
if(!chost) chost = 'localhost'
var lines = gmap.split(/\r?\n/), line, l = lines.length, i, lbr = '<br>',
type, rest, desc, sel, host, port,
+ iattrs = [], istyled, // attribute cache and styled infomessage placeholder
output = '', pretag = 'pre', preflag = false
for(i=0;i<l;i++) {
line = lines[i]
@@ -55,11 +118,14 @@ Hi01379 = (function(psGopherRequest) {
preflag = true
output += '<' + pretag + '>'
}
- output += desc + '\n'
+ istyled = terminalize(desc, iattrs)
+ iattrs = istyled.attrs
+ output += istyled.output + '\n'
}
else { //information messages ended, stop preformatting
if(preflag) {
preflag = false
+ iattrs = [] // reset info styling attributes
output += '</' + pretag + '>'
}
if(desc = desc.trim()) { // ignore empty descriptions
diff --git a/manifest.webapp b/manifest.webapp
@@ -1,5 +1,5 @@
{
- "version": "0.0.4",
+ "version": "0.0.5",
"name": "Kopher",
"description": "A Gopher client for KaiOS",
"launch_path": "/index.html",