import { SlashCommandBuilder, PermissionFlagsBits } from 'discord.js'; import { MessageFlags } from 'discord-api-types/v10'; 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 = [ // 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); } }, // 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); } } } ]; export async function init(client) { client.logger.info('[module:ansi] Loaded ANSI utilities'); }