2025-05-04 14:29:13 +00:00
|
|
|
|
import { MessageFlags } from 'discord-api-types/v10';
|
2025-05-08 01:52:12 +00:00
|
|
|
|
import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js';
|
|
|
|
|
|
|
2025-05-04 14:29:13 +00:00
|
|
|
|
import { CODES } from '../_src/ansiColors.js';
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Combined ANSI utilities module
|
|
|
|
|
|
* - /ansi: preview nested [tag]…[/] ANSI coloring
|
|
|
|
|
|
* - /ansitheme: display full BG×FG theme chart
|
|
|
|
|
|
* Both commands are Admin-only.
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const commands = [
|
2025-05-08 01:52:12 +00:00
|
|
|
|
// Preview arbitrary ANSI tags
|
|
|
|
|
|
{
|
|
|
|
|
|
data: new SlashCommandBuilder()
|
|
|
|
|
|
.setName('ansi')
|
|
|
|
|
|
.setDescription('Preview an ANSI-colored code block')
|
|
|
|
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
|
|
|
|
|
.setDMPermission(false)
|
|
|
|
|
|
.addStringOption(opt =>
|
|
|
|
|
|
opt
|
|
|
|
|
|
.setName('text')
|
|
|
|
|
|
.setDescription('Use [red]…[/], [bold,blue]…[/], escape \\[/]')
|
|
|
|
|
|
.setRequired(true)
|
|
|
|
|
|
)
|
|
|
|
|
|
.addBooleanOption(opt =>
|
|
|
|
|
|
opt
|
|
|
|
|
|
.setName('ephemeral')
|
|
|
|
|
|
.setDescription('Reply ephemerally?')
|
|
|
|
|
|
.setRequired(false)
|
|
|
|
|
|
),
|
|
|
|
|
|
async execute(interaction, client) {
|
|
|
|
|
|
const raw = interaction.options.getString('text', true);
|
|
|
|
|
|
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
|
|
|
|
|
const colored = client.ansi`${raw}`;
|
|
|
|
|
|
const block = client.wrapAnsi(colored);
|
|
|
|
|
|
const opts = { content: block };
|
|
|
|
|
|
if (ephemeral) opts.flags = MessageFlags.Ephemeral;
|
|
|
|
|
|
await interaction.reply(opts);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
2025-05-04 14:29:13 +00:00
|
|
|
|
|
2025-05-08 01:52:12 +00:00
|
|
|
|
// Show complete ANSI theme chart
|
|
|
|
|
|
{
|
|
|
|
|
|
data: new SlashCommandBuilder()
|
|
|
|
|
|
.setName('ansitheme')
|
|
|
|
|
|
.setDescription('Show ANSI color theme chart')
|
|
|
|
|
|
.setDefaultMemberPermissions(PermissionFlagsBits.Administrator)
|
|
|
|
|
|
.setDMPermission(false)
|
|
|
|
|
|
.addBooleanOption(opt =>
|
|
|
|
|
|
opt
|
|
|
|
|
|
.setName('ephemeral')
|
|
|
|
|
|
.setDescription('Reply ephemerally?')
|
|
|
|
|
|
.setRequired(false)
|
|
|
|
|
|
),
|
|
|
|
|
|
async execute(interaction, client) {
|
|
|
|
|
|
const fgs = ['gray', 'red', 'green', 'yellow', 'blue', 'pink', 'cyan', 'white'];
|
|
|
|
|
|
const bgs = ['bgGray', 'bgOrange', 'bgBlue', 'bgTurquoise', 'bgFirefly', 'bgIndigo', 'bgLightGray', 'bgWhite'];
|
|
|
|
|
|
const pad = 8;
|
|
|
|
|
|
// Column header with padded labels (no colors) - shifted right by 1
|
|
|
|
|
|
const header = ' ' + fgs.map(f => f.padEnd(pad, ' ')).join('');
|
|
|
|
|
|
// Sample row with no background (padded cells)
|
|
|
|
|
|
let defaultRow = '';
|
|
|
|
|
|
for (const fg of fgs) {
|
|
|
|
|
|
const fgCode = CODES[fg];
|
|
|
|
|
|
const openNormal = `\u001b[${fgCode}m`;
|
|
|
|
|
|
const openBold = `\u001b[${fgCode};${CODES.bold}m`;
|
|
|
|
|
|
const openUnder = `\u001b[${fgCode};${CODES.underline}m`;
|
|
|
|
|
|
const cell = `${openNormal}sa\u001b[0m${openBold}mp\u001b[0m${openUnder}le\u001b[0m`;
|
|
|
|
|
|
defaultRow += ' ' + cell + ' ';
|
|
|
|
|
|
}
|
|
|
|
|
|
// Append default row label after one pad
|
|
|
|
|
|
defaultRow += 'default';
|
|
|
|
|
|
// Colored rows per background
|
|
|
|
|
|
const rows = [];
|
|
|
|
|
|
for (const bg of bgs) {
|
|
|
|
|
|
let row = '';
|
|
|
|
|
|
const bgCode = CODES[bg];
|
|
|
|
|
|
for (const fg of fgs) {
|
|
|
|
|
|
const fgCode = CODES[fg];
|
|
|
|
|
|
const openNormal = `\u001b[${bgCode};${fgCode}m`;
|
|
|
|
|
|
const openBold = `\u001b[${bgCode};${fgCode};${CODES.bold}m`;
|
|
|
|
|
|
const openUnder = `\u001b[${bgCode};${fgCode};${CODES.underline}m`;
|
|
|
|
|
|
const cell = `${openNormal}sa\u001b[0m${openBold}mp\u001b[0m${openUnder}le\u001b[0m`;
|
|
|
|
|
|
row += ' ' + cell + ' ';
|
|
|
|
|
|
}
|
|
|
|
|
|
// Append uncolored row label immediately after cell padding
|
|
|
|
|
|
row += bg;
|
|
|
|
|
|
rows.push(row);
|
|
|
|
|
|
}
|
|
|
|
|
|
// Determine ephemeral setting
|
|
|
|
|
|
const ephemeral = interaction.options.getBoolean('ephemeral') ?? true;
|
|
|
|
|
|
// Initial sample table (header + default row)
|
|
|
|
|
|
const sampleContent = [header, defaultRow].join('\n');
|
|
|
|
|
|
const optsSample = { content: client.wrapAnsi(sampleContent) };
|
|
|
|
|
|
if (ephemeral) optsSample.flags = MessageFlags.Ephemeral;
|
|
|
|
|
|
await interaction.reply(optsSample);
|
|
|
|
|
|
// Split colored rows into two tables
|
|
|
|
|
|
const half = Math.ceil(rows.length / 2);
|
|
|
|
|
|
const firstRows = rows.slice(0, half);
|
|
|
|
|
|
const secondRows = rows.slice(half);
|
|
|
|
|
|
// First colored table
|
|
|
|
|
|
const table1 = [header, ...firstRows].join('\n');
|
|
|
|
|
|
const opts1 = { content: client.wrapAnsi(table1) };
|
|
|
|
|
|
if (ephemeral) opts1.flags = MessageFlags.Ephemeral;
|
|
|
|
|
|
await interaction.followUp(opts1);
|
|
|
|
|
|
// Second colored table
|
|
|
|
|
|
if (secondRows.length > 0) {
|
|
|
|
|
|
const table2 = [header, ...secondRows].join('\n');
|
|
|
|
|
|
const opts2 = { content: client.wrapAnsi(table2) };
|
|
|
|
|
|
if (ephemeral) opts2.flags = MessageFlags.Ephemeral;
|
|
|
|
|
|
await interaction.followUp(opts2);
|
|
|
|
|
|
}
|
2025-05-04 14:29:13 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
export async function init(client) {
|
2025-05-08 01:52:12 +00:00
|
|
|
|
client.logger.info('[module:ansi] Loaded ANSI utilities');
|
|
|
|
|
|
}
|