Added basic terminal coloring to the infolines

This commit is contained in:
Luxferre
2023-04-03 12:37:10 +03:00
parent 53f9dc3a46
commit 1bbe1ffd81
4 changed files with 130 additions and 4 deletions
+11 -2
View File
@@ -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?
+51
View File
@@ -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}
+67 -1
View File
@@ -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
+1 -1
View File
@@ -1,5 +1,5 @@
{
"version": "0.0.4",
"version": "0.0.5",
"name": "Kopher",
"description": "A Gopher client for KaiOS",
"launch_path": "/index.html",