Documentation and formatting

This commit is contained in:
Kim Ravn Hansen
2025-10-13 15:57:03 +02:00
parent 1631143647
commit 2fe8f7149c

View File

@@ -4,134 +4,131 @@
* @enum {string} * @enum {string}
*/ */
export const FrameType = { export const FrameType = {
/** /**
* ╔════════════╗ * ╔════════════╗
* ║ Hello, TUI ║ * ║ Hello, TUI ║
* ╚════════════╝ * ╚════════════╝
* *
* @type {string} Double-lined frame * @type {string} Double-lined frame
*/ */
Double: "Double", Double: "Double",
/** /**
* ┌────────────┐ * ┌────────────┐
* │ Hello, TUI │ * │ Hello, TUI │
* └────────────┘ * └────────────┘
* *
* @type {string} Single-lined frame * @type {string} Single-lined frame
*/ */
Single: "Single", Single: "Single",
/** /**
* *
* Hello, TUI * Hello, TUI
* *
* *
* @type {string} Double-lined frame * @type {string} Double-lined frame
*/ */
Invisible: "Invisible", Invisible: "Invisible",
/** /**
* ( ) * ( )
* ( Hello, TUI ) * ( Hello, TUI )
* ( ) * ( )
* *
* @type {string} Double-lined frame * @type {string} Double-lined frame
*/ */
Parentheses: "Parentheses", Parentheses: "Parentheses",
/** /**
* +------------+ * +------------+
* | Hello, TUI | * | Hello, TUI |
* +------------+ * +------------+
* *
* @type {string} Double-lined frame * @type {string} Double-lined frame
*/ */
Basic: "Basic", Basic: "Basic",
/** /**
* @protected * @protected
* Default values for the common frame types. * Default values for the common frame types.
* *
* [north, south, east, west, northwest, northeast, southwest, southeast] * [north, south, east, west, northwest, northeast, southwest, southeast]
*/ */
values: { values: {
Basic: "--||++++", Basic: "--||++++",
Double: "══║║╔╗╚╝", Double: "══║║╔╗╚╝",
Invisible: " ", Invisible: " ",
Parentheses: " () ", Parentheses: " () ",
Single: "──││┌┐└┘", Single: "──││┌┐└┘",
}, },
}; };
export class FramingOptions { export class FramingOptions {
/** @type {number=0} Vertical Padding; number of vertical whitespace (newlines) between the text and the frame. */ /** @type {number=0} Vertical Padding; number of vertical whitespace (newlines) between the text and the frame. */
vPadding = 0; vPadding = 0;
/** @type {number=0} Margin ; number of newlines to to insert before and after the framed text */ /** @type {number=0} Margin ; number of newlines to to insert before and after the framed text */
vMargin = 0; vMargin = 0;
/** @type {number=0} Horizontal Padding; number of whitespace characters to insert between the text and the sides of the frame. */ /** @type {number=0} Horizontal Padding; number of whitespace characters to insert between the text and the sides of the frame. */
hPadding = 0; hPadding = 0;
/** @type {number=0} Margin ; number of newlines to to insert before and after the text, but inside the frame */ /** @type {number=0} Margin ; number of newlines to to insert before and after the text, but inside the frame */
hMargin = 0; hMargin = 0;
/** @type {FrameType=FrameType.Double} Type of frame to put around the text */ /** @type {FrameType=FrameType.Double} Type of frame to put around the text */
frameType = FrameType.Double; frameType = FrameType.Double;
/** @type {number=0} Pad each line to become at least this long */ /** @type {number=0} Pad each line to become at least this long */
minLineWidth = 0; minLineWidth = 0;
// Light block: ░ (U+2591) // Light block: ░ (U+2591)
// Medium block: ▒ (U+2592) // Medium block: ▒ (U+2592)
// Dark block: ▓ (U+2593) // Dark block: ▓ (U+2593)
// Solid block: █ (U+2588) // Solid block: █ (U+2588)
/** @type {string} Single character to use as filler inside the frame. */ /** @type {string} Single character to use as filler inside the frame. */
paddingChar = " "; // character used for padding inside the frame. paddingChar = " ";
/** @type {string} Single character to use as filler outside the frame. */ /** @type {string} Single character to use as filler outside the frame. */
marginChar = " "; marginChar = " ";
/** @type {string} The 8 characters that make up the frame elements */ /** @type {string} The 8 characters that make up the frame elements */
frameChars = FrameType.values.Double; frameChars = FrameType.values.Double;
/** /**
* @param {object} o * @param {object} o
* @returns {FramingOptions} * @returns {FramingOptions}
*/ */
static fromObject(o) { static fromObject(o) {
const result = new FramingOptions(); const result = new FramingOptions();
result.vPadding = Math.max(0, Number.parseInt(o.vPadding) || 0); result.vPadding = Math.max(0, Number.parseInt(o.vPadding) || 0);
result.hPadding = Math.max(0, Number.parseInt(o.hPadding) || 0); result.hPadding = Math.max(0, Number.parseInt(o.hPadding) || 0);
result.vMargin = Math.max(0, Number.parseInt(o.vMargin) || 0); result.vMargin = Math.max(0, Number.parseInt(o.vMargin) || 0);
result.hMargin = Math.max(0, Number.parseInt(o.hMargin) || 0); result.hMargin = Math.max(0, Number.parseInt(o.hMargin) || 0);
result.minLineWidth = Math.max(0, Number.parseInt(o.hMargin) || 0); result.minLineWidth = Math.max(0, Number.parseInt(o.hMargin) || 0);
result.paddingChar = String(o.paddingChar || " ")[0] || " "; result.paddingChar = String(o.paddingChar || " ")[0] || " ";
result.marginChar = String(o.marginChar || " ")[0] || " "; result.marginChar = String(o.marginChar || " ")[0] || " ";
// //
// Do we have custom and valid frame chars? // Do we have custom and valid frame chars?
if ( if (typeof o.frameChars === "string" && o.frameChars.length === FrameType.values.Double.length) {
typeof o.frameChars === "string" && result.frameChars = o.frameChars;
o.frameChars.length === FrameType.values.Double.length
) {
result.frameChars = o.frameChars;
// //
// do we have document frame type instead ? // do we have document frame type instead ?
} else if (o.frameType && FrameType.hasOwnProperty(o.frameType)) { } else if (o.frameType && FrameType.hasOwnProperty(o.frameType)) {
result.frameChars = FrameType.values[o.frameType]; result.frameChars = FrameType.values[o.frameType];
// Fall back to using "Double" frame // Fall back to using "Double" frame
} else { } else {
result.frameChars = FrameType.values.Double; result.frameChars = FrameType.values.Double;
}
return result;
} }
return result;
}
} }
/** /**
@@ -139,198 +136,187 @@ export class FramingOptions {
* @param {FramingOptions} options * @param {FramingOptions} options
*/ */
export function frameText(text, options) { export function frameText(text, options) {
if (!options) { if (!options) {
options = new FramingOptions(); options = new FramingOptions();
} }
if (!(options instanceof FramingOptions)) { if (!(options instanceof FramingOptions)) {
options = FramingOptions.fromObject(options); options = FramingOptions.fromObject(options);
} }
// There is a point to this; each element in the array may contain newlines, // There is a point to this; each element in the array may contain newlines,
// so we have to combine everything into a long text and then split into // so we have to combine everything into a long text and then split into
// individual lines afterwards. // individual lines afterwards.
if (Array.isArray(text)) { if (Array.isArray(text)) {
text = text.join("\n"); text = text.join("\n");
} }
if (typeof text !== "string") { if (typeof text !== "string") {
console.debug(text); console.debug(text);
throw new Error( throw new Error(`text argument was neither an array or a string, it was a ${typeof text}`);
`text argument was neither an array or a string, it was a ${typeof text}`, }
/** @type {string[]} */
const lines = text.split("\n");
const innerLineLength = Math.max(
lines.reduce((accumulator, currentLine) => {
if (currentLine.length > accumulator) {
return currentLine.length;
}
return accumulator;
}, 0),
options.minLineWidth,
); );
}
/** @type {string[]} */ const frameThickness = 1; // always 1 for now.
const lines = text.split("\n");
const innerLineLength = Math.max( const outerLineLength = 0 + innerLineLength + frameThickness * 2 + options.hPadding * 2 + options.hMargin * 2;
lines.reduce((accumulator, currentLine) => {
if (currentLine.length > accumulator) {
return currentLine.length;
}
return accumulator;
}, 0),
options.minLineWidth,
);
const frameThickness = 1; // always 1 for now. // get the frame characters from the frameType.
let [
fNorth, // horizontal frame top lines
fSouth, // horizontal frame bottom lines
fWest, // vertical frame lines on the left side
fEast, // vertical frame lines on the right side
fNorthWest, // upper left frame corner
fNorthEast, // upper right frame corner
fSouthWest, // lower left frame corner
fSouthEast, // lower right frame corner
] = options.frameChars.split("");
if (fNorth === "§") {
fNorth = "";
}
if (fSouth === "§") {
fSouth = "";
}
if (fEast === "§") {
fEast = "";
}
if (fWest === "§") {
fWest = "";
}
if (fNorthEast === "§") {
fNorthEast = "";
}
if (fSouthEast === "§") {
fSouthEast = "";
}
if (fNorthWest === "§") {
fNorthWest = "";
}
if (fSouthWest === "§") {
fSouthWest = "";
}
const outerLineLength = let output = "";
0 +
innerLineLength +
frameThickness * 2 +
options.hPadding * 2 +
options.hMargin * 2;
// get the frame characters from the frameType. //
let [ // GENERATE THE MARGIN SPACE ABOVE THE FRAMED MsgType.TEXT
fNorth, // horizontal frame top lines //
fSouth, // horizontal frame bottom lines // ( we insert space characters even though )
fWest, // vertical frame lines on the left side // ( they wouldn't normally be visible. But )
fEast, // vertical frame lines on the right side // ( Some fonts might allow us to see blank )
fNorthWest, // upper left frame corner // ( space, and what if we want to nest many )
fNorthEast, // upper right frame corner // ( frames inside each other? )
fSouthWest, // lower left frame corner //
fSouthEast, // lower right frame corner output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(options.vMargin);
] = options.frameChars.split("");
if (fNorth === "§") {
fNorth = "";
}
if (fSouth === "§") {
fSouth = "";
}
if (fEast === "§") {
fEast = "";
}
if (fWest === "§") {
fWest = "";
}
if (fNorthEast === "§") {
fNorthEast = "";
}
if (fSouthEast === "§") {
fSouthEast = "";
}
if (fNorthWest === "§") {
fNorthWest = "";
}
if (fSouthWest === "§") {
fSouthWest = "";
}
let output = ""; //
// GENERATE THE TOP PART OF THE FRAME
// // ╔════════════╗
// GENERATE THE MARGIN SPACE ABOVE THE FRAMED MsgType.TEXT //
// //
// ( we insert space characters even though )
// ( they wouldn't normally be visible. But )
// ( Some fonts might allow us to see blank )
// ( space, and what if we want to nest many )
// ( frames inside each other? )
//
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(
options.vMargin,
);
//
// GENERATE THE TOP PART OF THE FRAME
// ╔════════════╗
//
//
output +=
"" + // Make sure JS knows we're adding a string.
options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
fNorthWest + // northwest frame corner
fNorth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
fNorthEast + // northeast frame corner
options.marginChar.repeat(options.hMargin) + // the margin after the frame ends
"\n";
//
// GENERATE UPPER PADDING
//
// ║ ║
//
// (the blank lines within the frame and above the text)
output += (
options.marginChar.repeat(options.hMargin) +
fWest +
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
fEast +
options.marginChar.repeat(options.hMargin) +
"\n"
).repeat(options.vPadding);
//
// GENERATE FRAMED MsgType.TEXT SEGMENT
//
// ║ My pretty ║
// ║ text here ║
//
// ( this could be done with a reduce() )
//
for (const line of lines) {
output += output +=
"" + // Make sure JS knows we're adding a string. "" + // Make sure JS knows we're adding a string.
options.marginChar.repeat(options.hMargin) + // margin before frame options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
fWest + // vertical frame char fNorthWest + // northwest frame corner
options.paddingChar.repeat(options.hPadding) + // padding before text fNorth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
line.padEnd(innerLineLength, " ") + // The actual text. Pad it with normal space character, NOT custom space. fNorthEast + // northeast frame corner
options.paddingChar.repeat(options.hPadding) + // padding after text options.marginChar.repeat(options.hMargin) + // the margin after the frame ends
fEast + // vertical frame bar "\n";
options.marginChar.repeat(options.hMargin) + // margin after frame //
"\n"; // GENERATE UPPER PADDING
} //
// ║ ║
//
// (the blank lines within the frame and above the text)
output += (
options.marginChar.repeat(options.hMargin) +
fWest +
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
fEast +
options.marginChar.repeat(options.hMargin) +
"\n"
).repeat(options.vPadding);
// //
// GENERATE LOWER PADDING // GENERATE FRAMED MsgType.TEXT SEGMENT
// //
// ║ // ║ My pretty
// // ║ text here ║
// ( the blank lines within the ) //
// ( frame and below the text ) // ( this could be done with a reduce() )
// //
// ( this code is a direct ) for (const line of lines) {
// ( repeat of the code that ) output +=
// ( generates top padding ) "" + // Make sure JS knows we're adding a string.
output += ( options.marginChar.repeat(options.hMargin) + // margin before frame
options.marginChar.repeat(options.hMargin) + fWest + // vertical frame char
fWest + options.paddingChar.repeat(options.hPadding) + // padding before text
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) + line.padEnd(innerLineLength, " ") + // The actual text. Pad it with normal space character, NOT custom space.
fEast + options.paddingChar.repeat(options.hPadding) + // padding after text
options.marginChar.repeat(options.hMargin) + fEast + // vertical frame bar
"\n" options.marginChar.repeat(options.hMargin) + // margin after frame
).repeat(options.vPadding); "\n";
}
// //
// GENERATE THE BOTTOM PART OF THE FRAME // GENERATE LOWER PADDING
// //
// ╚════════════╝ //
// //
output += // ( the blank lines within the )
"" + // Make sure JS knows we're adding a string. // ( frame and below the text )
options.marginChar.repeat(options.hMargin) + // the margin before the frame starts //
fSouthWest + // northwest frame corner // ( this code is a direct )
fSouth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar // ( repeat of the code that )
fSouthEast + // northeast frame corner // ( generates top padding )
options.marginChar.repeat(options.hMargin) + // the margin after the frame starts output += (
"\n"; options.marginChar.repeat(options.hMargin) +
fWest +
options.paddingChar.repeat(innerLineLength + options.hPadding * 2) +
fEast +
options.marginChar.repeat(options.hMargin) +
"\n"
).repeat(options.vPadding);
// //
// GENERATE THE MARGIN SPACE BELOW THE FRAMED MsgType.TEXT // GENERATE THE BOTTOM PART OF THE FRAME
// //
// ( we insert space characters even though ) // ╚════════════╝
// ( they wouldn't normally be visible. But ) //
// ( Some fonts might allow us to see blank ) output +=
// ( space, and what if we want to nest many ) "" + // Make sure JS knows we're adding a string.
// ( frames inside each other? ) options.marginChar.repeat(options.hMargin) + // the margin before the frame starts
// fSouthWest + // northwest frame corner
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat( fSouth.repeat(innerLineLength + options.hPadding * 2) + // the long horizontal frame top bar
options.vMargin, fSouthEast + // northeast frame corner
); options.marginChar.repeat(options.hMargin) + // the margin after the frame starts
"\n";
return output; //
// GENERATE THE MARGIN SPACE BELOW THE FRAMED MsgType.TEXT
//
// ( we insert space characters even though )
// ( they wouldn't normally be visible. But )
// ( Some fonts might allow us to see blank )
// ( space, and what if we want to nest many )
// ( frames inside each other? )
//
output += (options.marginChar.repeat(outerLineLength) + "\n").repeat(options.vMargin);
return output;
} }
// Allow this script to be run directly from node as well as being included! // Allow this script to be run directly from node as well as being included!