From e00f93edb6c90cac72eb7d8659a86ee2a630288c Mon Sep 17 00:00:00 2001 From: Aura Date: Sat, 2 Nov 2024 09:06:28 +0100 Subject: [PATCH] feat(unicode): add pagination (#271) --- package.json | 3 +- src/commands/unicode.ts | 194 ++-------------- src/interaction-handlers/unicode.ts | 23 ++ src/lib/i18n/LanguageKeys/Commands/Unicode.ts | 2 + src/lib/utilities/discord-utilities.ts | 24 +- .../{unicode.ts => unicode/backend.ts} | 72 +++--- src/lib/utilities/unicode/index.ts | 2 + src/lib/utilities/unicode/response.ts | 215 ++++++++++++++++++ src/locales/en-US/commands/unicode.json | 3 +- src/tsconfig.json | 5 +- 10 files changed, 325 insertions(+), 218 deletions(-) create mode 100644 src/interaction-handlers/unicode.ts rename src/lib/utilities/{unicode.ts => unicode/backend.ts} (92%) create mode 100644 src/lib/utilities/unicode/index.ts create mode 100644 src/lib/utilities/unicode/response.ts diff --git a/package.json b/package.json index 0d88e3d15..cb2a01644 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,9 @@ "main": "dist/main.js", "type": "module", "imports": { - "#lib/*": "./dist/lib/*.js", "#lib/structures": "./dist/lib/structures/index.js", + "#lib/unicode": "./dist/lib/utilities/unicode/index.js", + "#lib/*": "./dist/lib/*.js", "#generated/*": "./src/generated/*" }, "scripts": { diff --git a/src/commands/unicode.ts b/src/commands/unicode.ts index bf1b1db50..232007ae5 100644 --- a/src/commands/unicode.ts +++ b/src/commands/unicode.ts @@ -1,16 +1,16 @@ -import { EmbedColors } from '#lib/common/constants'; import { LanguageKeys } from '#lib/i18n/LanguageKeys'; -import { BidirectionalCategory, Category, Class, SearchCategory, getUnicode, searchUnicode } from '#lib/utilities/unicode'; -import { EmbedBuilder, bold, inlineCode, italic } from '@discordjs/builders'; -import { isNullish } from '@sapphire/utilities'; -import { Command, RegisterCommand, RegisterSubcommand } from '@skyra/http-framework'; import { - applyLocalizedBuilder, - createSelectMenuChoiceName, - getSupportedUserLanguageT, - type TFunction, - type TypedT -} from '@skyra/http-framework-i18n'; + BidirectionalCategory, + Class, + SearchCategory, + UnicodeBidirectionalCategoryKeyMapper, + UnicodeClassKeyMapper, + getSelectMenuComponents, + getUnicodeInformationEmbeds, + searchUnicode +} from '#lib/unicode'; +import { Command, RegisterCommand, RegisterSubcommand } from '@skyra/http-framework'; +import { applyLocalizedBuilder, createSelectMenuChoiceName, getSupportedUserLanguageT } from '@skyra/http-framework-i18n'; import { ApplicationIntegrationType, InteractionContextType, MessageFlags } from 'discord-api-types/v10'; const Root = LanguageKeys.Commands.Unicode; @@ -28,9 +28,12 @@ export class UserCommand extends Command { public async inspect(interaction: Command.ChatInputInteraction, options: InspectOptions) { const ids = [...options.character]; const t = getSupportedUserLanguageT(interaction); - const content = ids.length > 10 ? t(Root.TooManyCharacters) : ''; - const embeds = ids.slice(0, 10).map((id) => this.getInformation(t, id)); - return interaction.reply({ content, embeds, flags: MessageFlags.Ephemeral }); + const content = ids.length > 250 ? t(Root.TooManyCharacters) : ''; + const characters = ids.slice(0, 250); + + const components = characters.length > 10 ? getSelectMenuComponents(t, characters) : undefined; + const embeds = getUnicodeInformationEmbeds(t, characters.slice(0, 10)); + return interaction.reply({ content, embeds, components, flags: MessageFlags.Ephemeral }); } @RegisterSubcommand((builder) => @@ -61,13 +64,13 @@ export class UserCommand extends Command { ) .addNumberOption((builder) => applyLocalizedBuilder(builder, Root.OptionsBidirectionalCategory).setChoices( - ...Object.entries(UserCommand.UnicodeBidirectionalCategoryKeyMapper) // + ...Object.entries(UnicodeBidirectionalCategoryKeyMapper) // .map(([key, value]) => createSelectMenuChoiceName(value, { value: Number(key) })) ) ) .addNumberOption((builder) => applyLocalizedBuilder(builder, Root.OptionsClass).setChoices( - ...Object.entries(UserCommand.UnicodeClassKeyMapper) // + ...Object.entries(UnicodeClassKeyMapper) // .map(([key, value]) => createSelectMenuChoiceName(value, { value: Number(key) })) ) ) @@ -91,165 +94,6 @@ export class UserCommand extends Command { })) }); } - - private getInformation(t: TFunction, character: string) { - const id = character.codePointAt(0)!; - const unicode = getUnicode(id); - const embed = new EmbedBuilder(); - const lines = [bold(`${inlineCode(character)} — ${id.toString(16).toUpperCase().padStart(4, '0')}`)] as string[]; - - if (isNullish(unicode)) { - embed.setColor(EmbedColors.Error); - lines.push(t(Root.UnknownCharacter)); - } else { - embed.setColor(UserCommand.UnicodeCategoryColorMapper[unicode.category]); - lines.push( - t(Root.InformationBasic, { - name: unicode.unicodeName || unicode.name, - category: t(UserCommand.UnicodeCategoryKeyMapper[unicode.category]), - bidirectionalCategory: t(UserCommand.UnicodeBidirectionalCategoryKeyMapper[unicode.bidirectionalCategory]), - class: - unicode.class >= Class.CCC10 && unicode.class <= Class.CCC132 - ? Class[unicode.class] - : t(UserCommand.UnicodeClassKeyMapper[unicode.class as keyof typeof UserCommand.UnicodeClassKeyMapper]) - }) - ); - if (unicode.value) lines.push(t(Root.InformationValue, { value: unicode.value })); - if (unicode.mirrored) lines.push(t(Root.InformationMirrored)); - if (unicode.mapping.base) { - lines.push(t(Root.InformationMappingBase, { value: this.parseMapping(unicode.mapping.base) })); - } - if (unicode.mapping.lowercase) { - lines.push(t(Root.InformationMappingLowercase, { value: this.parseMapping(unicode.mapping.lowercase) })); - } - if (unicode.mapping.uppercase) { - lines.push(t(Root.InformationMappingUppercase, { value: this.parseMapping(unicode.mapping.uppercase) })); - } - if (unicode.comment) lines.push(t(Root.InformationComment, { value: unicode.comment })); - } - - return embed.setDescription(lines.join('\n')).toJSON(); - } - - private parseMapping(mapping: string) { - return mapping - .split(' ') - .map((part) => (part.startsWith('<') ? italic(part) : `${inlineCode(String.fromCodePoint(parseInt(part, 16)))} ${italic(part)}`)) - .join(' + '); - } - - private static readonly UnicodeCategoryColorMapper = { - [Category.Control]: 0x607d8b, - [Category.Format]: 0x9e9e9e, - [Category.PrivateUse]: 0x9e9e9e, - [Category.Surrogate]: 0x795548, - [Category.LowercaseLetter]: 0x03a9f4, - [Category.ModifierLetter]: 0x03a9f4, - [Category.OtherLetter]: 0x03a9f4, - [Category.TitlecaseLetter]: 0x03a9f4, - [Category.UppercaseLetter]: 0x03a9f4, - [Category.SpacingMark]: 0x009688, - [Category.EnclosingMark]: 0x009688, - [Category.NonspacingMark]: 0x009688, - [Category.DecimalNumber]: 0x3f51b5, - [Category.LetterNumber]: 0x3f51b5, - [Category.OtherNumber]: 0x3f51b5, - [Category.ConnectorPunctuation]: 0x4caf50, - [Category.DashPunctuation]: 0x4caf50, - [Category.ClosePunctuation]: 0x4caf50, - [Category.FinalPunctuation]: 0x4caf50, - [Category.InitialPunctuation]: 0x4caf50, - [Category.OtherPunctuation]: 0x4caf50, - [Category.OpenPunctuation]: 0x4caf50, - [Category.CurrencySymbol]: 0xffeb3b, - [Category.ModifierSymbol]: 0xffeb3b, - [Category.MathSymbol]: 0xffeb3b, - [Category.OtherSymbol]: 0xffeb3b, - [Category.LineSeparator]: 0xff9800, - [Category.ParagraphSeparator]: 0xff9800, - [Category.SpaceSeparator]: 0xff9800 - } satisfies Record; - - private static readonly UnicodeCategoryKeyMapper = { - [Category.Control]: Root.CategoryControl, - [Category.Format]: Root.CategoryFormat, - [Category.PrivateUse]: Root.CategoryPrivateUse, - [Category.Surrogate]: Root.CategorySurrogate, - [Category.LowercaseLetter]: Root.CategoryLowercaseLetter, - [Category.ModifierLetter]: Root.CategoryModifierLetter, - [Category.OtherLetter]: Root.CategoryOtherLetter, - [Category.TitlecaseLetter]: Root.CategoryTitlecaseLetter, - [Category.UppercaseLetter]: Root.CategoryUppercaseLetter, - [Category.SpacingMark]: Root.CategorySpacingMark, - [Category.EnclosingMark]: Root.CategoryEnclosingMark, - [Category.NonspacingMark]: Root.CategoryNonspacingMark, - [Category.DecimalNumber]: Root.CategoryDecimalNumber, - [Category.LetterNumber]: Root.CategoryLetterNumber, - [Category.OtherNumber]: Root.CategoryOtherNumber, - [Category.ConnectorPunctuation]: Root.CategoryConnectorPunctuation, - [Category.DashPunctuation]: Root.CategoryDashPunctuation, - [Category.ClosePunctuation]: Root.CategoryClosePunctuation, - [Category.FinalPunctuation]: Root.CategoryFinalPunctuation, - [Category.InitialPunctuation]: Root.CategoryInitialPunctuation, - [Category.OtherPunctuation]: Root.CategoryOtherPunctuation, - [Category.OpenPunctuation]: Root.CategoryOpenPunctuation, - [Category.CurrencySymbol]: Root.CategoryCurrencySymbol, - [Category.ModifierSymbol]: Root.CategoryModifierSymbol, - [Category.MathSymbol]: Root.CategoryMathSymbol, - [Category.OtherSymbol]: Root.CategoryOtherSymbol, - [Category.LineSeparator]: Root.CategoryLineSeparator, - [Category.ParagraphSeparator]: Root.CategoryParagraphSeparator, - [Category.SpaceSeparator]: Root.CategorySpaceSeparator - } satisfies Record; - - private static readonly UnicodeBidirectionalCategoryKeyMapper = { - [BidirectionalCategory.ArabicLetter]: Root.CategoryBidirectionalArabicLetter, - [BidirectionalCategory.ArabicNumber]: Root.CategoryBidirectionalArabicNumber, - [BidirectionalCategory.ParagraphSeparator]: Root.CategoryBidirectionalParagraphSeparator, - [BidirectionalCategory.BoundaryNeutral]: Root.CategoryBidirectionalBoundaryNeutral, - [BidirectionalCategory.CommonSeparator]: Root.CategoryBidirectionalCommonSeparator, - [BidirectionalCategory.EuropeanNumber]: Root.CategoryBidirectionalEuropeanNumber, - [BidirectionalCategory.EuropeanSeparator]: Root.CategoryBidirectionalEuropeanSeparator, - [BidirectionalCategory.EuropeanTerminator]: Root.CategoryBidirectionalEuropeanTerminator, - [BidirectionalCategory.FirstStrongIsolate]: Root.CategoryBidirectionalFirstStrongIsolate, - [BidirectionalCategory.LeftToRight]: Root.CategoryBidirectionalLeftToRight, - [BidirectionalCategory.LeftToRightEmbedding]: Root.CategoryBidirectionalLeftToRightEmbedding, - [BidirectionalCategory.LeftToRightIsolate]: Root.CategoryBidirectionalLeftToRightIsolate, - [BidirectionalCategory.LeftToRightOverride]: Root.CategoryBidirectionalLeftToRightOverride, - [BidirectionalCategory.NonSpacingMark]: Root.CategoryBidirectionalNonSpacingMark, - [BidirectionalCategory.OtherNeutral]: Root.CategoryBidirectionalOtherNeutral, - [BidirectionalCategory.PopDirectionalFormat]: Root.CategoryBidirectionalPopDirectionalFormat, - [BidirectionalCategory.PopDirectionalIsolate]: Root.CategoryBidirectionalPopDirectionalIsolate, - [BidirectionalCategory.RightToLeft]: Root.CategoryBidirectionalRightToLeft, - [BidirectionalCategory.RightToLeftEmbedding]: Root.CategoryBidirectionalRightToLeftEmbedding, - [BidirectionalCategory.RightToLeftIsolate]: Root.CategoryBidirectionalRightToLeftIsolate, - [BidirectionalCategory.RightToLeftOverride]: Root.CategoryBidirectionalRightToLeftOverride, - [BidirectionalCategory.SegmentSeparator]: Root.CategoryBidirectionalSegmentSeparator, - [BidirectionalCategory.WhiteSpace]: Root.CategoryBidirectionalWhiteSpace - } satisfies Record; - - private static readonly UnicodeClassKeyMapper = { - [Class.NotReordered]: Root.ClassNotReordered, - [Class.Overlay]: Root.ClassOverlay, - [Class.Unnamed]: Root.ClassUnnamed, - [Class.Nukta]: Root.ClassNukta, - [Class.KanaVoicing]: Root.ClassKanaVoicing, - [Class.Virama]: Root.ClassVirama, - [Class.AttachedBelow]: Root.ClassAttachedBelow, - [Class.AttachedAbove]: Root.ClassAttachedAbove, - [Class.AttachedAboveRight]: Root.ClassAttachedAboveRight, - [Class.BelowLeft]: Root.ClassBelowLeft, - [Class.Below]: Root.ClassBelow, - [Class.BelowRight]: Root.ClassBelowRight, - [Class.Left]: Root.ClassLeft, - [Class.Right]: Root.ClassRight, - [Class.AboveLeft]: Root.ClassAboveLeft, - [Class.Above]: Root.ClassAbove, - [Class.AboveRight]: Root.ClassAboveRight, - [Class.DoubleBelow]: Root.ClassDoubleBelow, - [Class.DoubleAbove]: Root.ClassDoubleAbove, - [Class.IotaSubscript]: Root.ClassIotaSubscript - }; } interface InspectOptions { diff --git a/src/interaction-handlers/unicode.ts b/src/interaction-handlers/unicode.ts new file mode 100644 index 000000000..4eecb7900 --- /dev/null +++ b/src/interaction-handlers/unicode.ts @@ -0,0 +1,23 @@ +import { getUnicodeInformationEmbeds } from '#lib/unicode'; +import { displaySelectMenuIndex, makeActionRow } from '#lib/utilities/discord-utilities'; +import { InteractionHandler, type Interactions } from '@skyra/http-framework'; +import { getSupportedLanguageT } from '@skyra/http-framework-i18n'; +import type { APIActionRowComponent, APIStringSelectComponent } from 'discord-api-types/v10'; + +export class UserHandler extends InteractionHandler { + public async run(interaction: Interactions.MessageComponentStringSelect) { + const parameters = interaction.values[0].split('.') as Parameters; + const pageIndex = Number(parameters[0]); + const characters = [...Buffer.from(parameters[1], 'base64').toString('utf8')]; + + const { content } = interaction.message; + const row = interaction.message.components![0] as APIActionRowComponent; + + const t = getSupportedLanguageT(interaction); + const component = displaySelectMenuIndex(row.components[0], pageIndex); + const embeds = getUnicodeInformationEmbeds(t, characters); + return interaction.update({ content, embeds, components: [makeActionRow([component])] }); + } +} + +type Parameters = [pageIndex: `${number}`, characters: string]; diff --git a/src/lib/i18n/LanguageKeys/Commands/Unicode.ts b/src/lib/i18n/LanguageKeys/Commands/Unicode.ts index e7b7c718d..1878be086 100644 --- a/src/lib/i18n/LanguageKeys/Commands/Unicode.ts +++ b/src/lib/i18n/LanguageKeys/Commands/Unicode.ts @@ -24,6 +24,8 @@ export const InformationComment = FT<{ value: string }>('commands/unicode:inform export const TooManyCharacters = T('commands/unicode:tooManyCharacters'); export const UnknownCharacter = T('commands/unicode:unknownCharacter'); +export const SelectMenuOptionLabel = FT<{ page: number; characters: string }>('commands/unicode:selectMenuOptionLabel'); + export const CategoryControl = T('commands/unicode:categoryControl'); export const CategoryFormat = T('commands/unicode:categoryFormat'); export const CategoryPrivateUse = T('commands/unicode:categoryPrivateUse'); diff --git a/src/lib/utilities/discord-utilities.ts b/src/lib/utilities/discord-utilities.ts index 9e1a896f6..ec29508bf 100644 --- a/src/lib/utilities/discord-utilities.ts +++ b/src/lib/utilities/discord-utilities.ts @@ -1,5 +1,27 @@ -import type { APIChannel } from 'discord-api-types/v10'; +import { + ComponentType, + type APIActionRowComponent, + type APIChannel, + type APIMessageActionRowComponent, + type APIStringSelectComponent +} from 'discord-api-types/v10'; export function isNsfwChannel(channel: Partial): boolean { return 'nsfw' in channel ? (channel.nsfw ?? false) : false; } + +export function makeActionRow( + components: Component[] +): APIActionRowComponent { + return { type: ComponentType.ActionRow, components }; +} + +export function displaySelectMenuIndex(component: APIStringSelectComponent, index: number): APIStringSelectComponent { + return { + ...component, + options: component.options.map((option, optionIndex) => ({ + ...option, + default: optionIndex === index + })) + }; +} diff --git a/src/lib/utilities/unicode.ts b/src/lib/utilities/unicode/backend.ts similarity index 92% rename from src/lib/utilities/unicode.ts rename to src/lib/utilities/unicode/backend.ts index 99d4e45f7..34dcfa8a2 100644 --- a/src/lib/utilities/unicode.ts +++ b/src/lib/utilities/unicode/backend.ts @@ -1,6 +1,6 @@ import { PathSrc } from '#lib/common/constants'; import { isNullish, isNullishOrEmpty } from '@sapphire/utilities'; -import { readFile } from 'fs/promises'; +import { readFile } from 'node:fs/promises'; const PathUnicode = new URL('./generated/data/unicode.json', PathSrc); const unicode = new Map((JSON.parse(await readFile(PathUnicode, 'utf8')) as Unicode[]).map((data) => [data.id, data] as const)); @@ -234,39 +234,39 @@ export enum Category { * @see {@link https://www.compart.com/en/unicode/combining} */ export enum Class { - NotReordered, - Overlay, + NotReordered = 0, + Overlay = 1, Unnamed = 6, - Nukta, - KanaVoicing, - Virama, - CCC10, - CCC11, - CCC12, - CCC13, - CCC14, - CCC15, - CCC16, - CCC17, - CCC18, - CCC19, - CCC20, - CCC21, - CCC22, - CCC23, - CCC24, - CCC25, - CCC26, - CCC27, - CCC28, - CCC29, - CCC30, - CCC31, - CCC32, - CCC33, - CCC34, - CCC35, - CCC36, + Nukta = 7, + KanaVoicing = 8, + Virama = 9, + CCC10 = 10, + CCC11 = 11, + CCC12 = 12, + CCC13 = 13, + CCC14 = 14, + CCC15 = 15, + CCC16 = 16, + CCC17 = 17, + CCC18 = 18, + CCC19 = 19, + CCC20 = 20, + CCC21 = 21, + CCC22 = 22, + CCC23 = 23, + CCC24 = 24, + CCC25 = 25, + CCC26 = 26, + CCC27 = 27, + CCC28 = 28, + CCC29 = 29, + CCC30 = 30, + CCC31 = 31, + CCC32 = 32, + CCC33 = 33, + CCC34 = 34, + CCC35 = 35, + CCC36 = 36, CCC84 = 84, CCC91 = 91, CCC103 = 103, @@ -274,7 +274,7 @@ export enum Class { CCC118 = 118, CCC122 = 122, CCC129 = 129, - CCC130, + CCC130 = 130, CCC132 = 132, AttachedBelow = 202, AttachedAbove = 214, @@ -287,8 +287,8 @@ export enum Class { AboveLeft = 228, Above = 230, AboveRight = 232, - DoubleBelow, - DoubleAbove, + DoubleBelow = 233, + DoubleAbove = 234, IotaSubscript = 240 } diff --git a/src/lib/utilities/unicode/index.ts b/src/lib/utilities/unicode/index.ts new file mode 100644 index 000000000..d1926eaa8 --- /dev/null +++ b/src/lib/utilities/unicode/index.ts @@ -0,0 +1,2 @@ +export * from '#lib/utilities/unicode/backend'; +export * from '#lib/utilities/unicode/response'; diff --git a/src/lib/utilities/unicode/response.ts b/src/lib/utilities/unicode/response.ts new file mode 100644 index 000000000..15fa22aaa --- /dev/null +++ b/src/lib/utilities/unicode/response.ts @@ -0,0 +1,215 @@ +import { EmbedColors } from '#lib/common/constants'; +import { LanguageKeys } from '#lib/i18n/LanguageKeys'; +import { makeActionRow } from '#lib/utilities/discord-utilities'; +import { BidirectionalCategory, Category, Class, getUnicode, type Unicode } from '#lib/utilities/unicode/backend'; +import { bold, EmbedBuilder, inlineCode, italic } from '@discordjs/builders'; +import { chunk, isNullish } from '@sapphire/utilities'; +import type { TFunction, TypedT } from '@skyra/http-framework-i18n'; +import { ComponentType, type APIEmbed, type APISelectMenuOption, type APIStringSelectComponent } from 'discord-api-types/v10'; + +const Root = LanguageKeys.Commands.Unicode; + +export function getUnicodeInformationEmbeds(t: TFunction, characters: readonly string[]): APIEmbed[] { + return characters.map((character) => getUnicodeInformationEmbed(t, character)); +} + +export function getUnicodeInformationEmbed(t: TFunction, character: string): APIEmbed { + const id = character.codePointAt(0)!; + const unicode = getUnicode(id); + const embed = new EmbedBuilder(); + const lines = [bold(`${inlineCode(character)} — ${id.toString(16).toUpperCase().padStart(4, '0')}`)] as string[]; + + if (isNullish(unicode)) { + embed.setColor(EmbedColors.Error); + lines.push(t(Root.UnknownCharacter)); + } else { + embed.setColor(UnicodeCategoryColorMapper[unicode.category]); + addUnicodeInformationEmbedDescription(lines, t, unicode); + } + + return embed.setDescription(lines.join('\n')).toJSON(); +} + +export function getSelectMenuComponents(t: TFunction, characters: readonly string[]) { + const component = getSelectMenuOptions(t, characters); + return [makeActionRow([component])]; +} + +export function getSelectMenuOptions(t: TFunction, characters: readonly string[]): APIStringSelectComponent { + const pages = chunk(characters, 10); + const totalPages = pages.length; + return { + type: ComponentType.StringSelect, + custom_id: 'unicode', + options: pages.map((page, index) => getSelectMenuOption(t, page, index, totalPages)) + }; +} + +function getSelectMenuOption(t: TFunction, characters: readonly string[], pageIndex: number, totalPages: number): APISelectMenuOption { + const prefix = pageIndex === 0 ? '' : '…'; + const suffix = pageIndex + 1 === totalPages ? '' : '…'; + const string = characters.join(''); + return { + default: pageIndex === 0, + label: t(Root.SelectMenuOptionLabel, { page: pageIndex + 1, characters: `${prefix}“${string}”${suffix}` }), + value: `${pageIndex}.${Buffer.from(string, 'utf8').toString('base64')}` + }; +} + +function addUnicodeInformationEmbedDescription(output: string[], t: TFunction, unicode: Unicode) { + output.push( + t(Root.InformationBasic, { + name: unicode.unicodeName || unicode.name, + category: t(UnicodeCategoryKeyMapper[unicode.category]), + bidirectionalCategory: t(UnicodeBidirectionalCategoryKeyMapper[unicode.bidirectionalCategory]), + class: + unicode.class >= Class.CCC10 && unicode.class <= Class.CCC132 + ? Class[unicode.class] + : t(UnicodeClassKeyMapper[unicode.class as keyof typeof UnicodeClassKeyMapper]) + }) + ); + + if (unicode.value) { + output.push(t(Root.InformationValue, { value: unicode.value })); + } + + if (unicode.mirrored) { + output.push(t(Root.InformationMirrored)); + } + + if (unicode.mapping.base) { + output.push(t(Root.InformationMappingBase, { value: parseMapping(unicode.mapping.base) })); + } + + if (unicode.mapping.lowercase) { + output.push(t(Root.InformationMappingLowercase, { value: parseMapping(unicode.mapping.lowercase) })); + } + + if (unicode.mapping.uppercase) { + output.push(t(Root.InformationMappingUppercase, { value: parseMapping(unicode.mapping.uppercase) })); + } + + if (unicode.comment) { + output.push(t(Root.InformationComment, { value: unicode.comment })); + } +} + +function parseMapping(mapping: string) { + return mapping + .split(' ') + .map((part) => (part.startsWith('<') ? italic(part) : `${inlineCode(String.fromCodePoint(parseInt(part, 16)))} ${italic(part)}`)) + .join(' + '); +} + +export const UnicodeCategoryColorMapper = { + [Category.Control]: 0x607d8b, + [Category.Format]: 0x9e9e9e, + [Category.PrivateUse]: 0x9e9e9e, + [Category.Surrogate]: 0x795548, + [Category.LowercaseLetter]: 0x03a9f4, + [Category.ModifierLetter]: 0x03a9f4, + [Category.OtherLetter]: 0x03a9f4, + [Category.TitlecaseLetter]: 0x03a9f4, + [Category.UppercaseLetter]: 0x03a9f4, + [Category.SpacingMark]: 0x009688, + [Category.EnclosingMark]: 0x009688, + [Category.NonspacingMark]: 0x009688, + [Category.DecimalNumber]: 0x3f51b5, + [Category.LetterNumber]: 0x3f51b5, + [Category.OtherNumber]: 0x3f51b5, + [Category.ConnectorPunctuation]: 0x4caf50, + [Category.DashPunctuation]: 0x4caf50, + [Category.ClosePunctuation]: 0x4caf50, + [Category.FinalPunctuation]: 0x4caf50, + [Category.InitialPunctuation]: 0x4caf50, + [Category.OtherPunctuation]: 0x4caf50, + [Category.OpenPunctuation]: 0x4caf50, + [Category.CurrencySymbol]: 0xffeb3b, + [Category.ModifierSymbol]: 0xffeb3b, + [Category.MathSymbol]: 0xffeb3b, + [Category.OtherSymbol]: 0xffeb3b, + [Category.LineSeparator]: 0xff9800, + [Category.ParagraphSeparator]: 0xff9800, + [Category.SpaceSeparator]: 0xff9800 +} satisfies Record; + +export const UnicodeCategoryKeyMapper = { + [Category.Control]: Root.CategoryControl, + [Category.Format]: Root.CategoryFormat, + [Category.PrivateUse]: Root.CategoryPrivateUse, + [Category.Surrogate]: Root.CategorySurrogate, + [Category.LowercaseLetter]: Root.CategoryLowercaseLetter, + [Category.ModifierLetter]: Root.CategoryModifierLetter, + [Category.OtherLetter]: Root.CategoryOtherLetter, + [Category.TitlecaseLetter]: Root.CategoryTitlecaseLetter, + [Category.UppercaseLetter]: Root.CategoryUppercaseLetter, + [Category.SpacingMark]: Root.CategorySpacingMark, + [Category.EnclosingMark]: Root.CategoryEnclosingMark, + [Category.NonspacingMark]: Root.CategoryNonspacingMark, + [Category.DecimalNumber]: Root.CategoryDecimalNumber, + [Category.LetterNumber]: Root.CategoryLetterNumber, + [Category.OtherNumber]: Root.CategoryOtherNumber, + [Category.ConnectorPunctuation]: Root.CategoryConnectorPunctuation, + [Category.DashPunctuation]: Root.CategoryDashPunctuation, + [Category.ClosePunctuation]: Root.CategoryClosePunctuation, + [Category.FinalPunctuation]: Root.CategoryFinalPunctuation, + [Category.InitialPunctuation]: Root.CategoryInitialPunctuation, + [Category.OtherPunctuation]: Root.CategoryOtherPunctuation, + [Category.OpenPunctuation]: Root.CategoryOpenPunctuation, + [Category.CurrencySymbol]: Root.CategoryCurrencySymbol, + [Category.ModifierSymbol]: Root.CategoryModifierSymbol, + [Category.MathSymbol]: Root.CategoryMathSymbol, + [Category.OtherSymbol]: Root.CategoryOtherSymbol, + [Category.LineSeparator]: Root.CategoryLineSeparator, + [Category.ParagraphSeparator]: Root.CategoryParagraphSeparator, + [Category.SpaceSeparator]: Root.CategorySpaceSeparator +} satisfies Record; + +export const UnicodeBidirectionalCategoryKeyMapper = { + [BidirectionalCategory.ArabicLetter]: Root.CategoryBidirectionalArabicLetter, + [BidirectionalCategory.ArabicNumber]: Root.CategoryBidirectionalArabicNumber, + [BidirectionalCategory.ParagraphSeparator]: Root.CategoryBidirectionalParagraphSeparator, + [BidirectionalCategory.BoundaryNeutral]: Root.CategoryBidirectionalBoundaryNeutral, + [BidirectionalCategory.CommonSeparator]: Root.CategoryBidirectionalCommonSeparator, + [BidirectionalCategory.EuropeanNumber]: Root.CategoryBidirectionalEuropeanNumber, + [BidirectionalCategory.EuropeanSeparator]: Root.CategoryBidirectionalEuropeanSeparator, + [BidirectionalCategory.EuropeanTerminator]: Root.CategoryBidirectionalEuropeanTerminator, + [BidirectionalCategory.FirstStrongIsolate]: Root.CategoryBidirectionalFirstStrongIsolate, + [BidirectionalCategory.LeftToRight]: Root.CategoryBidirectionalLeftToRight, + [BidirectionalCategory.LeftToRightEmbedding]: Root.CategoryBidirectionalLeftToRightEmbedding, + [BidirectionalCategory.LeftToRightIsolate]: Root.CategoryBidirectionalLeftToRightIsolate, + [BidirectionalCategory.LeftToRightOverride]: Root.CategoryBidirectionalLeftToRightOverride, + [BidirectionalCategory.NonSpacingMark]: Root.CategoryBidirectionalNonSpacingMark, + [BidirectionalCategory.OtherNeutral]: Root.CategoryBidirectionalOtherNeutral, + [BidirectionalCategory.PopDirectionalFormat]: Root.CategoryBidirectionalPopDirectionalFormat, + [BidirectionalCategory.PopDirectionalIsolate]: Root.CategoryBidirectionalPopDirectionalIsolate, + [BidirectionalCategory.RightToLeft]: Root.CategoryBidirectionalRightToLeft, + [BidirectionalCategory.RightToLeftEmbedding]: Root.CategoryBidirectionalRightToLeftEmbedding, + [BidirectionalCategory.RightToLeftIsolate]: Root.CategoryBidirectionalRightToLeftIsolate, + [BidirectionalCategory.RightToLeftOverride]: Root.CategoryBidirectionalRightToLeftOverride, + [BidirectionalCategory.SegmentSeparator]: Root.CategoryBidirectionalSegmentSeparator, + [BidirectionalCategory.WhiteSpace]: Root.CategoryBidirectionalWhiteSpace +} satisfies Record; + +export const UnicodeClassKeyMapper = { + [Class.NotReordered]: Root.ClassNotReordered, + [Class.Overlay]: Root.ClassOverlay, + [Class.Unnamed]: Root.ClassUnnamed, + [Class.Nukta]: Root.ClassNukta, + [Class.KanaVoicing]: Root.ClassKanaVoicing, + [Class.Virama]: Root.ClassVirama, + [Class.AttachedBelow]: Root.ClassAttachedBelow, + [Class.AttachedAbove]: Root.ClassAttachedAbove, + [Class.AttachedAboveRight]: Root.ClassAttachedAboveRight, + [Class.BelowLeft]: Root.ClassBelowLeft, + [Class.Below]: Root.ClassBelow, + [Class.BelowRight]: Root.ClassBelowRight, + [Class.Left]: Root.ClassLeft, + [Class.Right]: Root.ClassRight, + [Class.AboveLeft]: Root.ClassAboveLeft, + [Class.Above]: Root.ClassAbove, + [Class.AboveRight]: Root.ClassAboveRight, + [Class.DoubleBelow]: Root.ClassDoubleBelow, + [Class.DoubleAbove]: Root.ClassDoubleAbove, + [Class.IotaSubscript]: Root.ClassIotaSubscript +}; diff --git a/src/locales/en-US/commands/unicode.json b/src/locales/en-US/commands/unicode.json index 04a6eb59f..f9f817345 100644 --- a/src/locales/en-US/commands/unicode.json +++ b/src/locales/en-US/commands/unicode.json @@ -13,8 +13,9 @@ "optionsBidirectionalCategoryDescription": "The unicode bidirectional category to filter by, if any", "optionsClassName": "class", "optionsClassDescription": "The unicode class to filter by, if any", - "tooManyCharacters": "There are too many characters, only the first 10 will be displayed.", + "tooManyCharacters": "There are too many characters, only the first 10 pages (250 characters) will be displayed.", "unknownCharacter": "I could not retrieve the unicode information for this character.", + "selectMenuOptionLabel": "Page {{page, number}} ({{characters}})", "informationBasic": "**Name**: `{{name}}` ({{category}})\n**Bidirectional**: {{bidirectionalCategory}}\n**Class**: {{class}}", "informationValue": "**Value**: `{{value, number}}`", "informationMirrored": "**Mirrored**", diff --git a/src/tsconfig.json b/src/tsconfig.json index dfdcb4225..9cd73ebec 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -6,10 +6,7 @@ "outDir": "../dist", "composite": true, "tsBuildInfoFile": "../dist/tsconfig.tsbuildinfo", - "paths": { - "#lib/*": ["lib/*"], - "#generated/": ["generated/*"] - } + "resolvePackageJsonImports": true }, "include": ["."] }