mirror of
https://github.com/CorentinTh/it-tools.git
synced 2024-09-11 13:37:53 +03:00
Merge 2a088ba7d7
into e876d03608
This commit is contained in:
commit
69d8932f88
11
components.d.ts
vendored
11
components.d.ts
vendored
@ -127,24 +127,18 @@ declare module '@vue/runtime-core' {
|
||||
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
|
||||
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
|
||||
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
|
||||
NCode: typeof import('naive-ui')['NCode']
|
||||
NButton: typeof import('naive-ui')['NButton']
|
||||
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
|
||||
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
|
||||
NDivider: typeof import('naive-ui')['NDivider']
|
||||
NEllipsis: typeof import('naive-ui')['NEllipsis']
|
||||
NFormItem: typeof import('naive-ui')['NFormItem']
|
||||
NGi: typeof import('naive-ui')['NGi']
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NH1: typeof import('naive-ui')['NH1']
|
||||
NH3: typeof import('naive-ui')['NH3']
|
||||
NIcon: typeof import('naive-ui')['NIcon']
|
||||
NInputNumber: typeof import('naive-ui')['NInputNumber']
|
||||
NLabel: typeof import('naive-ui')['NLabel']
|
||||
NLayout: typeof import('naive-ui')['NLayout']
|
||||
NLayoutSider: typeof import('naive-ui')['NLayoutSider']
|
||||
NMenu: typeof import('naive-ui')['NMenu']
|
||||
NScrollbar: typeof import('naive-ui')['NScrollbar']
|
||||
NSpin: typeof import('naive-ui')['NSpin']
|
||||
NSpace: typeof import('naive-ui')['NSpace']
|
||||
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
|
||||
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
|
||||
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
|
||||
@ -179,6 +173,7 @@ declare module '@vue/runtime-core' {
|
||||
'Tool.layout': typeof import('./src/layouts/tool.layout.vue')['default']
|
||||
ToolCard: typeof import('./src/components/ToolCard.vue')['default']
|
||||
UlidGenerator: typeof import('./src/tools/ulid-generator/ulid-generator.vue')['default']
|
||||
UnicodeFormatter: typeof import('./src/tools/unicode-formatter/unicode-formatter.vue')['default']
|
||||
UrlEncoder: typeof import('./src/tools/url-encoder/url-encoder.vue')['default']
|
||||
UrlParser: typeof import('./src/tools/url-parser/url-parser.vue')['default']
|
||||
UserAgentParser: typeof import('./src/tools/user-agent-parser/user-agent-parser.vue')['default']
|
||||
|
@ -6,6 +6,7 @@ import { tool as asciiTextDrawer } from './ascii-text-drawer';
|
||||
|
||||
import { tool as textToUnicode } from './text-to-unicode';
|
||||
import { tool as safelinkDecoder } from './safelink-decoder';
|
||||
import { tool as unicodeFormatter } from './unicode-formatter';
|
||||
import { tool as pdfSignatureChecker } from './pdf-signature-checker';
|
||||
import { tool as numeronymGenerator } from './numeronym-generator';
|
||||
import { tool as macAddressGenerator } from './mac-address-generator';
|
||||
@ -172,6 +173,7 @@ export const toolsByCategory: ToolCategory[] = [
|
||||
textDiff,
|
||||
numeronymGenerator,
|
||||
asciiTextDrawer,
|
||||
unicodeFormatter,
|
||||
],
|
||||
},
|
||||
{
|
||||
|
12
src/tools/unicode-formatter/index.ts
Normal file
12
src/tools/unicode-formatter/index.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Edit } from '@vicons/tabler';
|
||||
import { defineTool } from '../tool';
|
||||
|
||||
export const tool = defineTool({
|
||||
name: 'Unicode Formatter',
|
||||
path: '/unicode-formatter',
|
||||
description: 'Format text using Unicode fonts',
|
||||
keywords: ['unicode', 'formatter', 'fonts'],
|
||||
component: () => import('./unicode-formatter.vue'),
|
||||
icon: Edit,
|
||||
createdAt: new Date('2024-04-07'),
|
||||
});
|
29
src/tools/unicode-formatter/unicode-fonts.json
Normal file
29
src/tools/unicode-formatter/unicode-fonts.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"normal": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",
|
||||
"sans": "\"\\ !#$%&'()*+,-./𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫:;<=>?@𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹[]^_`𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓{|}~",
|
||||
"sansBold": "\"\\ !#$%&'()*+,-./𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵:;<=>?@𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭[]^_`𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇{|}~",
|
||||
"sansItalic": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡[]^_`𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻{|}~",
|
||||
"sansBoldItalic": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕[]^_`𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯{|}~",
|
||||
"monospace": "\"\\ !#$%&'()*+,-./𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿:;<=>?@𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉[]^_`𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣{|}~",
|
||||
"fullwidth": "\"\ !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~",
|
||||
"fraktur": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ[]^_`𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷{|}~",
|
||||
"boldFraktur": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅[]^_`𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟{|}~",
|
||||
"serifBold": "\"\\ !#$%&'()*+,-./𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗:;<=>?@𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙[]^_`𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳{|}~",
|
||||
"serifItalic": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍[]^_`𝑎𝑏𝑐𝑑𝑒𝑓𝑔ℎ𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧{|}~",
|
||||
"serifBoldItalic": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁[]^_`𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛{|}~",
|
||||
"doubleStruck": "\"\\ !#$%&'()*+,-./𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡:;<=>?@𝔸𝔹ℂ𝔻𝔼𝔽𝔾ℍ𝕀𝕁𝕂𝕃𝕄ℕ𝕆ℙℚℝ𝕊𝕋𝕌𝕍𝕎𝕏𝕐ℤ[]^_`𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫{|}~",
|
||||
"script": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝒜ℬ𝒞𝒟ℰℱ𝒢ℋℐ𝒥𝒦ℒℳ𝒩𝒪𝒫𝒬ℛ𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵[]^_`𝒶𝒷𝒸𝒹ℯ𝒻ℊ𝒽𝒾𝒿𝓀𝓁𝓂𝓃ℴ𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏{|}~",
|
||||
"boldScript": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩[]^_`𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃{|}~",
|
||||
"circled": "\"⦸ !#$%&'()⊛⊕,⊖⨀⊘⓪①②③④⑤⑥⑦⑧⑨:;⧀⊜⧁?@ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ[]^_`ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ{⦶}~",
|
||||
"circledNegative": "\"\\ !#$%&'()*+,-./⓿❶❷❸❹❺❻❼❽❾:;<=>?@🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩[]^_`🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩{|}~",
|
||||
"squared": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉[]^_`🄰🄱🄲🄳🄴🄵🄶🄷🄸🄹🄺🄻🄼🄽🄾🄿🅀🅁🅂🅃🅄🅅🅆🅇🅈🅉{|}~",
|
||||
"squaredNegative": "\"⧅ !#$%&'()⧆⊞,⊟⊡⧄0123456789:;<=>?@🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉[]^_`🅰🅱🅲🅳🅴🅵🅶🅷🅸🅹🅺🅻🅼🅽🅾🅿🆀🆁🆂🆃🆄🆅🆆🆇🆈🆉{|}~",
|
||||
"parenthesized": "\"\\ !#$%&'()*+,-./0⑴⑵⑶⑷⑸⑹⑺⑻⑼:;<=>?@⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵[]^_`⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵{|}~",
|
||||
"smallCaps": "\"\\ !#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴩꞯʀꜱᴛᴜᴠᴡxʏᴢ{|}~",
|
||||
"subscript": "\"\\ !#$%&'₍₎*₊,₋./₀₁₂₃₄₅₆₇₈₉:;<₌>?@ᴀʙᴄᴅᴇꜰɢʜɪᴊᴋʟᴍɴᴏᴘ🇶ʀꜱᴛᴜᴠᴡxʏᴢ[]^_`ₐᵦ𝒸𝒹ₑ𝒻𝓰ₕᵢⱼₖₗₘₙₒₚᵩᵣₛₜᵤᵥ𝓌ₓᵧ𝓏{|}~",
|
||||
"superscript": "\"\\ !#$%&'⁽⁾*⁺,⁻./⁰¹²³⁴⁵⁶⁷⁸⁹:;<⁼>?@ᴬᴮᶜᴰᴱᶠᴳᴴᴵᴶᴷᴸᴹᴺᴼᴾᵠᴿˢᵀᵁⱽᵂˣʸᶻ[]^_`ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖᵠʳˢᵗᵘᵛʷˣʸᶻ{|}~",
|
||||
"inverted": "„\\ ¡#$%⅋,)(*+‘-˙/0ƖՇƐᔭϛ9𝘓86:;<=>¿@∀ꓭↃꓷƎℲ⅁HIſꓘ⅂WNOԀῸꓤS⊥∩ꓥMX⅄Z][^‾`ɐqɔpǝɟƃɥıɾʞןɯuodbɹsʇnʌʍxʎz}|{~",
|
||||
"mirrored": "\"/ !#$%&')(*+,-.\\0߁ςƐ߂टმ٢8୧:;<=>⸮@AꓭↃꓷƎꟻӘHIႱꓘ⅃MИOꟼϘЯꙄTUVWXYZ][^_`ɒdↄbɘʇϱʜiįʞlmᴎoqpᴙꙅɈυvwxγz}|{~",
|
||||
"rotatedLeft": "=/ !#$%&-⏝⏜*+`ǀ∙\\ⴰ↽വ𝈐ፓහமΓꝏᓂ⠒;˅𝄥∧ᣇ@ᗉߘ𝈱⌓ш𝈯ᘎ⌶𝄩⥟𝈎⨼∑Zⴰᓇⵚᓚᔕ⊢⊃𝈷ᕒ×⤚𝇙⎵⎴‹|`ơᓄ𝈱ᓀш𝈯თ𝈦𝄩ᓜ𝈎⨼ᗴ⊂ⴰᓇᓂᓚᔕ𝀏⊃𝈷З×⤚𝇙⏟_⏞ಽ",
|
||||
"rotatedRight": "=/ !#$%&-⏜⏝*+`ǀ∙\\ⴰ⇀ᘚω𝈦හの⨼ꝏᓄ⠒;∧𝄥˅?@ᗆϖᴒᗜጠ╖ᘏ⌶𝄩ᓚ⌤⌐ᕒZⴰᓀᓄᓓᔕ⊣⊂<ᓬ×⤙𝇙⎴⎵›|`⌕ᓂᴒ௨ጠ╖மፓ𝄩ᓚ⌤⌐ᴟᴝⴰᓀᓄᓓᔕ𝀏⊂<ᓬ×⤙𝇙⏞_⏟ಽ"
|
||||
}
|
90
src/tools/unicode-formatter/unicode-formatter.service.ts
Normal file
90
src/tools/unicode-formatter/unicode-formatter.service.ts
Normal file
@ -0,0 +1,90 @@
|
||||
// prettier-ignore
|
||||
import fonts from './unicode-fonts.json';
|
||||
|
||||
export type AllFontNames = keyof typeof fonts;
|
||||
export interface AllOptions {
|
||||
remove?: string
|
||||
append?: string
|
||||
reverse?: boolean
|
||||
clear?: boolean
|
||||
};
|
||||
|
||||
// list of font characters for checking if character is formatted
|
||||
const allCharacters = new Set(Object.values(fonts).join(''));
|
||||
|
||||
// check if text is already formatted with a certain font
|
||||
function alreadyFormatted(text: string, font: AllFontNames) {
|
||||
const fontCharacters = new Set(fonts[font]);
|
||||
// flag as already formatted if all characters are in font or not in any other font
|
||||
return Array.from(text).every(char => fontCharacters.has(char) || !allCharacters.has(char));
|
||||
}
|
||||
|
||||
// check if text is already formatted with a certain font
|
||||
function alreadyAppended(text: string, append: string) {
|
||||
// check if at least half the characters are the append character
|
||||
return Array.from(text).filter(char => char === append).length >= text.length / 2;
|
||||
}
|
||||
|
||||
// format text into selected font
|
||||
function formatText(text: string, font: AllFontNames | undefined, options?: AllOptions) {
|
||||
// set font to normal if already formatted with selected font
|
||||
if (font && fonts[font] && alreadyFormatted(text, font)) {
|
||||
font = 'normal';
|
||||
}
|
||||
// remove and don't append if character is already appended
|
||||
if (options?.append) {
|
||||
options.remove = options.append;
|
||||
options.append = !alreadyAppended(text, options.append) ? options.append : '';
|
||||
}
|
||||
// Array.from() splits the string by symbol and not by code points
|
||||
let newText = Array.from(text);
|
||||
// exchange font symbols
|
||||
if (font && fonts[font]) {
|
||||
const targetFont = Array.from(fonts[font]);
|
||||
const charLists = Object.values(fonts);
|
||||
// map characters to new font
|
||||
newText = newText.map((char) => {
|
||||
let index = -1;
|
||||
// find the index of the character in some font
|
||||
const found = charLists.some((charList) => {
|
||||
index = Array.from(charList).indexOf(char);
|
||||
return index > -1;
|
||||
});
|
||||
// if found, replace with the corresponding character in the target font
|
||||
// if not found, keep the character the same
|
||||
return found ? targetFont[index] : char;
|
||||
});
|
||||
}
|
||||
// reverse text if reverse option is set
|
||||
newText = options?.reverse ? newText.reverse() : newText;
|
||||
// remove appended symbol of specific type from the end
|
||||
newText = options?.remove
|
||||
? newText.map(char => char.replace(new RegExp(`${options.remove}$`, 'u'), ''))
|
||||
: newText;
|
||||
// append symbol (underline, strikethrough, etc.) to end of each character if append is set
|
||||
newText = options?.append ? newText.map(char => char + options.append) : newText;
|
||||
// remove appended symbols (underline, strikethrough, etc.) if using eraser
|
||||
// \u035f = Underline, \u0333 = Double Underline, \u0335 = Short Strikethrough \u0336 = Strikethrough
|
||||
newText = options?.clear ? newText.map(char => char.replace(/\u035F|\u0333|\u0335|\u0336/gu, '')) : newText;
|
||||
// set textarea content and select text around the replacement
|
||||
return newText.join('');
|
||||
}
|
||||
|
||||
// format selected text
|
||||
export function formatSelection(textArea: HTMLTextAreaElement, font: AllFontNames | undefined, options?: AllOptions) {
|
||||
const selectionStart = textArea.selectionStart;
|
||||
const selectionEnd = textArea.selectionEnd;
|
||||
const oldText = textArea.value;
|
||||
|
||||
const regexSpaces = /^(\s*)(\S+)(\s*)$/g;
|
||||
const [_, spaceBefore, selection, spaceAfter] = regexSpaces.exec(oldText.substring(selectionStart, selectionEnd) || '') || [];
|
||||
|
||||
const prefix = oldText.substring(0, selectionStart);
|
||||
const newSelection = formatText(selection, font, options);
|
||||
const suffix = oldText.substring(selectionEnd);
|
||||
textArea.value = `${prefix}${spaceBefore}${newSelection}${spaceAfter}${suffix}`;
|
||||
|
||||
textArea.setSelectionRange(selectionStart, selectionStart + (spaceBefore?.length || 0) + newSelection.length + (spaceAfter?.length || 0));
|
||||
|
||||
textArea.focus();
|
||||
}
|
179
src/tools/unicode-formatter/unicode-formatter.vue
Normal file
179
src/tools/unicode-formatter/unicode-formatter.vue
Normal file
@ -0,0 +1,179 @@
|
||||
<script lang="ts" setup>
|
||||
// import {AllFontNames,AllOptions, bold,italic,monospace,strikethrough,underline,superscript,subscript,formatSelections} from './unicod'
|
||||
import { Copy } from '@vicons/tabler';
|
||||
import type { AllFontNames, AllOptions } from './unicode-formatter.service';
|
||||
import { formatSelection } from './unicode-formatter.service';
|
||||
|
||||
import { useCopy } from '@/composable/copy';
|
||||
|
||||
const textArea = ref<HTMLTextAreaElement>();
|
||||
function formatTextAreaSelection(format: AllFontNames | undefined, options: AllOptions) {
|
||||
if (!textArea.value) {
|
||||
return;
|
||||
}
|
||||
formatSelection(textArea.value, format, options);
|
||||
}
|
||||
|
||||
const formattedText = ref('Type your text here and 𝗳𝗼𝗿𝗺𝗮𝘁 it by highlighting the text you want to format and clicking one of the buttons above. Click "More fonts" to show more Unicode fonts. The eraser button will convert your selection back to normal text.');
|
||||
|
||||
const { copy: copyFormattedText } = useCopy({ source: formattedText, text: 'Formatted text copied to the clipboard' });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- Google Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Open+Sans&display=swap" rel="stylesheet">
|
||||
|
||||
<div
|
||||
max-w-600
|
||||
style="font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
|
||||
>
|
||||
<!-- First set of buttons -->
|
||||
<n-form-item label="Fonts:" mb-3 mt-3 w-full>
|
||||
<n-button title="Normal" @click="formatTextAreaSelection('normal', { clear: true })">
|
||||
<!-- Eraser Icon -->
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 24 24" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.586,15.408l4.299,4.299C7.072,19.895,7.326,20,7.592,20h0.001h2h0.4h9.6v-2h-6.958l7.222-7.222 c0.78-0.779,0.78-2.049,0-2.828L14.906,3c-0.779-0.779-2.049-0.779-2.828,0l-4.75,4.749l-4.754,4.843 C1.809,13.371,1.813,14.634,2.586,15.408z M13.492,4.414l4.95,4.95l-2.586,2.586L10.906,7L13.492,4.414z M8.749,9.156l0.743-0.742 l4.95,4.95l-4.557,4.557C9.86,17.946,9.838,17.973,9.816,18H9.593H8.006l-4.005-4.007L8.749,9.156z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button title="Bold" @click="formatTextAreaSelection('sansBold', { })">
|
||||
𝗕
|
||||
</n-button>
|
||||
<n-button title="Italic" @click="formatTextAreaSelection('sansItalic', { })">
|
||||
𝘐
|
||||
</n-button>
|
||||
<n-button title="Monospace" @click="formatTextAreaSelection('monospace', { })">
|
||||
𝙼
|
||||
</n-button>
|
||||
<n-button style="text-decoration: underline" @click="formatTextAreaSelection(undefined, { append: '͟' })">
|
||||
U
|
||||
</n-button>
|
||||
<n-button style="text-decoration: line-through" @click="formatTextAreaSelection(undefined, { append: '̶' })">
|
||||
S
|
||||
</n-button>
|
||||
</n-form-item>
|
||||
|
||||
<!-- Collapsible section with more buttons -->
|
||||
<details style="display:block" borders mb-3 w-full>
|
||||
<summary>More fonts</summary>
|
||||
<n-space mt-3 w-full>
|
||||
<n-button title="Math Sans" @click="formatTextAreaSelection('sans', { })">
|
||||
𝖬
|
||||
</n-button>
|
||||
<n-button title="Sans Bold italic" @click="formatTextAreaSelection('sansBoldItalic', { })">
|
||||
𝘽𝙄
|
||||
</n-button>
|
||||
<n-button title="Serif bold" @click="formatTextAreaSelection('serifBold', { })">
|
||||
𝐁
|
||||
</n-button>
|
||||
<n-button title="Serif italic" @click="formatTextAreaSelection('serifItalic', { })">
|
||||
𝐼
|
||||
</n-button>
|
||||
<n-button title="Serif bold italic" @click="formatTextAreaSelection('serifBoldItalic', { })">
|
||||
𝑩𝑰
|
||||
</n-button>
|
||||
<n-button title="Double Underline" @click="formatTextAreaSelection(undefined, { append: '̳' })">
|
||||
U̳
|
||||
</n-button>
|
||||
<n-button title="Short Strikethrough" style="text-decoration: line-through" @click="formatTextAreaSelection(undefined, { append: '̵' })">
|
||||
S̵
|
||||
</n-button>
|
||||
<n-button title="Math double-struck" @click="formatTextAreaSelection('doubleStruck', { })">
|
||||
𝔻
|
||||
</n-button>
|
||||
<n-button title="Fullwidth" @click="formatTextAreaSelection('fullwidth', { })">
|
||||
F
|
||||
</n-button>
|
||||
<n-button title="Math Fraktur" @click="formatTextAreaSelection('fraktur', { })">
|
||||
𝔉
|
||||
</n-button>
|
||||
<n-button title="Math Fraktur bold" @click="formatTextAreaSelection('boldFraktur', { })">
|
||||
𝕱
|
||||
</n-button>
|
||||
<n-button title="Script" @click="formatTextAreaSelection('script', { })">
|
||||
𝒮
|
||||
</n-button>
|
||||
<n-button title="Bold script" @click="formatTextAreaSelection('boldScript', { })">
|
||||
𝓢
|
||||
</n-button>
|
||||
<n-button title="Circled" @click="formatTextAreaSelection('circled', { })">
|
||||
Ⓒ
|
||||
</n-button>
|
||||
<n-button title="Circled negative" @click="formatTextAreaSelection('circledNegative', { })">
|
||||
🅒
|
||||
</n-button>
|
||||
<n-button title="Squared" @click="formatTextAreaSelection('squared', { })">
|
||||
🅂
|
||||
</n-button>
|
||||
<n-button title="Squared negative" @click="formatTextAreaSelection('squaredNegative', { })">
|
||||
🆂
|
||||
</n-button>
|
||||
<n-button title="Parenthesized" @click="formatTextAreaSelection('parenthesized', { })">
|
||||
⒫
|
||||
</n-button>
|
||||
<n-button title="Small caps" @click="formatTextAreaSelection('smallCaps', { })">
|
||||
Aʙ
|
||||
</n-button>
|
||||
<n-button title="Subscript" @click="formatTextAreaSelection('subscript', { })">
|
||||
ₛᵤᵦ
|
||||
</n-button>
|
||||
<n-button title="Superscript" @click="formatTextAreaSelection('superscript', { })">
|
||||
ˢᵘᵖ
|
||||
</n-button>
|
||||
<n-button title="Inverted" @click="formatTextAreaSelection('inverted', { })">
|
||||
∀
|
||||
</n-button>
|
||||
<n-button title="Rotated Left" @click="formatTextAreaSelection('rotatedLeft', { })">
|
||||
ᗉ
|
||||
</n-button>
|
||||
<n-button title="Rotated Right" @click="formatTextAreaSelection('rotatedRight', { })">
|
||||
ᗆ
|
||||
</n-button>
|
||||
<n-button title="Mirrored" @click="formatTextAreaSelection('mirrored', { })">
|
||||
Я
|
||||
</n-button>
|
||||
<n-button title="Reverse" @click="formatTextAreaSelection(undefined, { reverse: true })">
|
||||
<!-- Left/Right Arrow Icon -->
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 168v-16c0-13.255 10.745-24 24-24h360V80c0-21.367 25.899-32.042 40.971-16.971l80 80c9.372 9.373 9.372 24.569 0 33.941l-80 80C409.956 271.982 384 261.456 384 240v-48H24c-13.255 0-24-10.745-24-24zm488 152H128v-48c0-21.314-25.862-32.08-40.971-16.971l-80 80c-9.372 9.373-9.372 24.569 0 33.941l80 80C102.057 463.997 128 453.437 128 432v-48h360c13.255 0 24-10.745 24-24v-16c0-13.255-10.745-24-24-24z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button title="Inverted Reverse" @click="formatTextAreaSelection('inverted', { reverse: true })">
|
||||
∀
|
||||
<!-- Left/Right Arrow Icon -->
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M377.941 169.941V216H134.059v-46.059c0-21.382-25.851-32.09-40.971-16.971L7.029 239.029c-9.373 9.373-9.373 24.568 0 33.941l86.059 86.059c15.119 15.119 40.971 4.411 40.971-16.971V296h243.882v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.568 0-33.941l-86.059-86.059c-15.119-15.12-40.971-4.412-40.971 16.97z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button title="Rotated Right Reverse" @click="formatTextAreaSelection('rotatedRight', { reverse: true })">
|
||||
ᗆ
|
||||
<!-- Left/Right Arrow Icon -->
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M377.941 169.941V216H134.059v-46.059c0-21.382-25.851-32.09-40.971-16.971L7.029 239.029c-9.373 9.373-9.373 24.568 0 33.941l86.059 86.059c15.119 15.119 40.971 4.411 40.971-16.971V296h243.882v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.568 0-33.941l-86.059-86.059c-15.119-15.12-40.971-4.412-40.971 16.97z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button title="Mirrored Reverse" @click="formatTextAreaSelection('mirrored', { reverse: true })">
|
||||
Я
|
||||
<!-- Left/Right Arrow Icon -->
|
||||
<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 512 512" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M377.941 169.941V216H134.059v-46.059c0-21.382-25.851-32.09-40.971-16.971L7.029 239.029c-9.373 9.373-9.373 24.568 0 33.941l86.059 86.059c15.119 15.119 40.971 4.411 40.971-16.971V296h243.882v46.059c0 21.382 25.851 32.09 40.971 16.971l86.059-86.059c9.373-9.373 9.373-24.568 0-33.941l-86.059-86.059c-15.119-15.12-40.971-4.412-40.971 16.97z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
</n-space>
|
||||
</details>
|
||||
|
||||
<textarea
|
||||
ref="textArea" :value="formattedText" rows="12" mb-3 mt-3 w-full
|
||||
spellcheck="false"
|
||||
style="font-family: 'Open Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;"
|
||||
/>
|
||||
|
||||
<div text-center>
|
||||
<c-button @click="copyFormattedText()">
|
||||
<n-icon size="22">
|
||||
<Copy />
|
||||
</n-icon>
|
||||
</c-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
Loading…
Reference in New Issue
Block a user