From 6988db600ddfa3bd01917112926f1e069254433d Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Tue, 3 Jun 2025 21:40:58 +0200 Subject: [PATCH 1/4] add base64 encoding/decoding --- .../string/base64/base64.service.test.ts | 64 +++++++++++++ src/pages/tools/string/base64/index.tsx | 89 +++++++++++++++++++ src/pages/tools/string/base64/meta.ts | 13 +++ src/pages/tools/string/base64/service.ts | 7 ++ src/pages/tools/string/index.ts | 4 +- 5 files changed, 176 insertions(+), 1 deletion(-) create mode 100644 src/pages/tools/string/base64/base64.service.test.ts create mode 100644 src/pages/tools/string/base64/index.tsx create mode 100644 src/pages/tools/string/base64/meta.ts create mode 100644 src/pages/tools/string/base64/service.ts diff --git a/src/pages/tools/string/base64/base64.service.test.ts b/src/pages/tools/string/base64/base64.service.test.ts new file mode 100644 index 0000000..8512379 --- /dev/null +++ b/src/pages/tools/string/base64/base64.service.test.ts @@ -0,0 +1,64 @@ +import { expect, describe, it } from 'vitest'; +import { base64 } from './service'; + +describe('base64', () => { + it('should encode a simple string using Base64 correctly', () => { + const input = 'hello'; + const result = base64(input, true); + expect(result).toBe('aGVsbG8='); + }); + + it('should decode a simple Base64-encoded string correctly', () => { + const input = 'aGVsbG8='; + const result = base64(input, false); + expect(result).toBe('hello'); + }); + + it('should handle special characters encoding correctly', () => { + const input = 'Hello, World!'; + const result = base64(input, true); + expect(result).toBe('SGVsbG8sIFdvcmxkIQ=='); + }); + + it('should handle special characters decoding correctly', () => { + const input = 'SGVsbG8sIFdvcmxkIQ=='; + const result = base64(input, false); + expect(result).toBe('Hello, World!'); + }); + + it('should handle an empty string encoding correctly', () => { + const input = ''; + const result = base64(input, true); + expect(result).toBe(''); + }); + + it('should handle an empty string decoding correctly', () => { + const input = ''; + const result = base64(input, false); + expect(result).toBe(''); + }); + + it('should handle a newline encoding correctly', () => { + const input = '\n'; + const result = base64(input, true); + expect(result).toBe('Cg=='); + }); + + it('should handle a newline decoding correctly', () => { + const input = 'Cg=='; + const result = base64(input, false); + expect(result).toBe('\n'); + }); + + it('should handle a string with symbols encoding correctly', () => { + const input = '!@#$%^&*()_+-='; + const result = base64(input, true); + expect(result).toBe('IUAjJCVeJiooKV8rLT0='); + }); + + it('should handle a string with mixed characters decoding correctly', () => { + const input = 'IUAjJCVeJiooKV8rLT0='; + const result = base64(input, false); + expect(result).toBe('!@#$%^&*()_+-='); + }); +}); diff --git a/src/pages/tools/string/base64/index.tsx b/src/pages/tools/string/base64/index.tsx new file mode 100644 index 0000000..61a579d --- /dev/null +++ b/src/pages/tools/string/base64/index.tsx @@ -0,0 +1,89 @@ +import { useState } from 'react'; +import ToolContent from '@components/ToolContent'; +import ToolTextInput from '@components/input/ToolTextInput'; +import ToolTextResult from '@components/result/ToolTextResult'; +import { base64 } from './service'; +import { CardExampleType } from '@components/examples/ToolExamples'; +import { ToolComponentProps } from '@tools/defineTool'; +import { GetGroupsType } from '@components/options/ToolOptions'; +import { Box } from '@mui/material'; +import SimpleRadio from '@components/options/SimpleRadio'; + +interface InitialValuesType { + mode: 'encode' | 'decode'; +} + +const initialValues: InitialValuesType = { + mode: 'encode' +}; + +const exampleCards: CardExampleType[] = [ + { + title: 'Encode data in UTF-8 with Base64', + description: 'This example shows how to encode a simple text using Base64.', + sampleText: 'Hello, World!', + sampleResult: 'SGVsbG8sIFdvcmxkIQ==', + sampleOptions: { mode: 'encode' } + }, + { + title: 'Decode Base64-encoded data to UTF-8', + description: + 'This example shows how to decode data that was encoded with Base64.', + sampleText: 'SGVsbG8sIFdvcmxkIQ==', + sampleResult: 'Hello, World!', + sampleOptions: { mode: 'decode' } + } +]; + +export default function Base64({ title }: ToolComponentProps) { + const [input, setInput] = useState(''); + const [result, setResult] = useState(''); + + const compute = (optionsValues: InitialValuesType, input: string) => { + if (input) setResult(base64(input, optionsValues.mode === 'encode')); + }; + + const getGroups: GetGroupsType = ({ + values, + updateField + }) => [ + { + title: 'Base64 Options', + component: ( + + updateField('mode', 'encode')} + checked={values.mode === 'encode'} + title={'Base64 Encode'} + /> + updateField('mode', 'decode')} + checked={values.mode === 'decode'} + title={'Base64 Decode'} + /> + + ) + } + ]; + + return ( + + } + resultComponent={} + initialValues={initialValues} + getGroups={getGroups} + toolInfo={{ + title: 'What is Base64?', + description: + 'Base64 is an encoding scheme that represents data in an ASCII string format by translating it into a radix-64 representation. Although it can be used to encode strings, it is commonly used to encode binary data for transmission over media that are designed to deal with textual data.' + }} + exampleCards={exampleCards} + input={input} + setInput={setInput} + compute={compute} + /> + ); +} diff --git a/src/pages/tools/string/base64/meta.ts b/src/pages/tools/string/base64/meta.ts new file mode 100644 index 0000000..2e0d104 --- /dev/null +++ b/src/pages/tools/string/base64/meta.ts @@ -0,0 +1,13 @@ +import { defineTool } from '@tools/defineTool'; +import { lazy } from 'react'; + +export const tool = defineTool('string', { + name: 'Base64', + path: 'base64', + icon: 'mdi:code-tags', + description: + 'A simple tool to encode or decode data using Base64, which is commonly used in web applications.', + shortDescription: 'Encode or decode data using Base64.', + keywords: ['base64'], + component: lazy(() => import('./index')) +}); diff --git a/src/pages/tools/string/base64/service.ts b/src/pages/tools/string/base64/service.ts new file mode 100644 index 0000000..b28fce4 --- /dev/null +++ b/src/pages/tools/string/base64/service.ts @@ -0,0 +1,7 @@ +import { Buffer } from 'buffer'; + +export function base64(input: string, encode: boolean): string { + return encode + ? Buffer.from(input, 'utf-8').toString('base64') + : Buffer.from(input, 'base64').toString('utf-8'); +} diff --git a/src/pages/tools/string/index.ts b/src/pages/tools/string/index.ts index b2aa502..758dd02 100644 --- a/src/pages/tools/string/index.ts +++ b/src/pages/tools/string/index.ts @@ -14,6 +14,7 @@ import { tool as stringJoin } from './join/meta'; import { tool as stringReplace } from './text-replacer/meta'; import { tool as stringRepeat } from './repeat/meta'; import { tool as stringTruncate } from './truncate/meta'; +import { tool as stringBase64 } from './base64/meta'; export const stringTools = [ stringSplit, @@ -31,5 +32,6 @@ export const stringTools = [ stringPalindrome, stringQuote, stringRotate, - stringRot13 + stringRot13, + stringBase64 ]; From 92dad2a6eb4d6c206f3e97d422390db751c8f69c Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 4 Jun 2025 20:23:37 +0200 Subject: [PATCH 2/4] extract InitialValuesType to types.ts (PR feedback incorporated) --- src/pages/tools/string/base64/index.tsx | 5 +---- src/pages/tools/string/base64/types.ts | 3 +++ 2 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 src/pages/tools/string/base64/types.ts diff --git a/src/pages/tools/string/base64/index.tsx b/src/pages/tools/string/base64/index.tsx index 61a579d..6176367 100644 --- a/src/pages/tools/string/base64/index.tsx +++ b/src/pages/tools/string/base64/index.tsx @@ -8,10 +8,7 @@ import { ToolComponentProps } from '@tools/defineTool'; import { GetGroupsType } from '@components/options/ToolOptions'; import { Box } from '@mui/material'; import SimpleRadio from '@components/options/SimpleRadio'; - -interface InitialValuesType { - mode: 'encode' | 'decode'; -} +import { InitialValuesType } from './types'; const initialValues: InitialValuesType = { mode: 'encode' diff --git a/src/pages/tools/string/base64/types.ts b/src/pages/tools/string/base64/types.ts new file mode 100644 index 0000000..c8070f5 --- /dev/null +++ b/src/pages/tools/string/base64/types.ts @@ -0,0 +1,3 @@ +export type InitialValuesType = { + mode: 'encode' | 'decode'; +}; From 82c3d0b5b5f3f9bf4c128b0c86ac3eb28eec3cdb Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Thu, 5 Jun 2025 18:37:55 +0100 Subject: [PATCH 3/4] fix: missing buffer --- package-lock.json | 34 ++++++++++++++++++++++++++++++---- package.json | 1 + 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 69f255b..4241de4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "@types/morsee": "^1.0.2", "@types/omggif": "^1.0.5", "browser-image-compression": "^2.0.2", + "buffer": "^6.0.3", "color": "^4.2.3", "dayjs": "^1.11.13", "formik": "^2.4.6", @@ -1634,6 +1635,30 @@ "tinycolor2": "^1.6.0" } }, + "node_modules/@jimp/core/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/@jimp/custom": { "version": "0.22.12", "resolved": "https://registry.npmjs.org/@jimp/custom/-/custom-0.22.12.tgz", @@ -4099,9 +4124,9 @@ } }, "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -4116,9 +4141,10 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", - "ieee754": "^1.1.13" + "ieee754": "^1.2.1" } }, "node_modules/buffer-equal": { diff --git a/package.json b/package.json index 6b1cb59..1bebbe8 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "@types/morsee": "^1.0.2", "@types/omggif": "^1.0.5", "browser-image-compression": "^2.0.2", + "buffer": "^6.0.3", "color": "^4.2.3", "dayjs": "^1.11.13", "formik": "^2.4.6", From de47536f6617024cac6972a78015e52d16d28571 Mon Sep 17 00:00:00 2001 From: "Ibrahima G. Coulibaly" Date: Thu, 5 Jun 2025 18:44:19 +0100 Subject: [PATCH 4/4] fix: icon --- src/pages/tools/string/base64/meta.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/tools/string/base64/meta.ts b/src/pages/tools/string/base64/meta.ts index 2e0d104..31a9337 100644 --- a/src/pages/tools/string/base64/meta.ts +++ b/src/pages/tools/string/base64/meta.ts @@ -4,7 +4,7 @@ import { lazy } from 'react'; export const tool = defineTool('string', { name: 'Base64', path: 'base64', - icon: 'mdi:code-tags', + icon: 'tabler:number-64-small', description: 'A simple tool to encode or decode data using Base64, which is commonly used in web applications.', shortDescription: 'Encode or decode data using Base64.',