From cb5de6948f750a6803da965be8f39580a55d36a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20L=C3=B6vqvist?= Date: Sun, 22 Feb 2026 03:46:33 +0100 Subject: [PATCH] Initial commit --- ansi-tunc.mjs | 119 ++++++++++++++++++++++++++++++++++++++++++ grep-mtime-sorter.mjs | 72 +++++++++++++++++++++++++ package.json | 10 ++++ 3 files changed, 201 insertions(+) create mode 100644 ansi-tunc.mjs create mode 100644 grep-mtime-sorter.mjs create mode 100644 package.json diff --git a/ansi-tunc.mjs b/ansi-tunc.mjs new file mode 100644 index 0000000..a332051 --- /dev/null +++ b/ansi-tunc.mjs @@ -0,0 +1,119 @@ +/* VIBE ALERT + +This code was generated using OpenAI’s ChatGPT (GPT-5.2 Thinking, large language model). +The output has not been independently reviewed, audited, or comprehensively validated, +and has only been superficially tested. It should be considered experimental and not fully vetted for production use. + +*/ + +import fs from 'node:fs'; +import readline from 'node:readline'; + +const ansiRegex = /\x1b\[(?:[0-9]{1,3}(?:;[0-9]{1,3})*)?[mK]?/g; + +function tokenize(line) { + const tokens = []; + let lastIndex = 0; + + for (const match of line.matchAll(ansiRegex)) { + const index = match.index; + + if (index > lastIndex) { + tokens.push({ type: 'text', value: line.slice(lastIndex, index) }); + } + + tokens.push({ type: 'ansi', value: match[0] }); + lastIndex = index + match[0].length; + } + + if (lastIndex < line.length) { + tokens.push({ type: 'text', value: line.slice(lastIndex) }); + } + + return tokens; +} + +function truncateLine(line, maxLength, tabSize=4) { + let col = 0; + let result = ''; + + const tokens = tokenize(line); + + for (const token of tokens) { + if (col >= maxLength) { + break; + } + + if (token.type === 'ansi') { + result += token.value; + } else { + let i = 0; + + while (i < token.value.length && col < maxLength) { + const ch = token.value[i]; + + if (ch === '\t') { + const w = tabSize - (col % tabSize); + + if (col + w > maxLength) { + i = token.value.length; + } else { + result += ' '.repeat(w); + col += w; + i++; + } + } else { + result += ch; + col += 1; + i++; + } + } + } + } + + return result.replace(/\s+$/, '') + '\x1b[0m'; +} +function parseArgs(argv) { + let filename = '/dev/stdin'; + let length = 50; + + for (let i = 2; i < argv.length; i++) { + const arg = argv[i]; + + if (arg === '-l' || arg === '--length') { + const value = argv[i + 1]; + if (value) { + length = parseInt(value, 10); + i++; + } + } else { + filename = arg; + } + } + + return { filename, length }; +} + +async function main() { + const { filename, length } = parseArgs(process.argv); + + let inputStream; + + if (filename === '/dev/stdin') { + inputStream = process.stdin; + } else { + inputStream = fs.createReadStream(filename, { encoding: 'utf8' }); + } + + const rl = readline.createInterface({ + input: inputStream, + crlfDelay: Infinity, + }); + + for await (const line of rl) { + const truncated = truncateLine(line, length); + process.stdout.write(truncated + '\n'); + } +} + +await main(); diff --git a/grep-mtime-sorter.mjs b/grep-mtime-sorter.mjs new file mode 100644 index 0000000..1a46ef9 --- /dev/null +++ b/grep-mtime-sorter.mjs @@ -0,0 +1,72 @@ +/* VIBE ALERT + +This code was generated using OpenAI’s ChatGPT (GPT-5.2 Thinking, large language model). +The output has not been independently reviewed, audited, or comprehensively validated, +and has only been superficially tested. It should be considered experimental and not fully vetted for production use. + +*/ + +import fs from 'node:fs'; +import { stat } from 'node:fs/promises'; + +const lineRe = /^\x1b\[35m\x1b\[K(.*?)\x1b/; + + +function isoFormat(timestamp) { + if (timestamp) { + const d = new Date(timestamp * 1000); + const pad = (n) => String(n).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; + } else { + return 'Unknown'; + } +} + +async function getMtime(line) { + const m = lineRe.exec(line); + if (m) { + const filename = m[1]; + try { + const s = await stat(filename); + return s.mtimeMs / 1000; + } catch { + return 0; + } + } else { + throw new Error('Malformed input: ANSI prefix not found'); + } +} + +async function main() { + const input = await new Promise((resolve) => { + let data = ''; + process.stdin.setEncoding('utf8'); + process.stdin.on('data', (chunk) => { data += chunk; }); + process.stdin.on('end', () => { resolve(data); }); + }); + + const lines = input.split(/(?<=\n)/); + + const pairs = await Promise.all( + lines.map(async (line, index) => { + const t = await getMtime(line); + return { t, index }; + }) + ); + + pairs.sort((a, b) => { + if (a.t !== b.t) { + return a.t - b.t; + } else { + return a.index - b.index; + } + }); + + for (const { t, index } of pairs) { + const ts = isoFormat(t).padStart(16, ' '); + process.stdout.write(`\x1b[94m${ts}\x1b[0m `); + process.stdout.write(lines[index]); + } +} + +await main(); diff --git a/package.json b/package.json new file mode 100644 index 0000000..49a06a3 --- /dev/null +++ b/package.json @@ -0,0 +1,10 @@ +{ + "name": "ansi-tools", + "version": "0.1.0", + "type": "module", + "private": true, + "bin": { + "ansi-trunc": "./ansi-trunc.mjs", + "grep-mtime-sorter": "./grep-mtime-sorter.mjs" + } +} \ No newline at end of file