feat(new tool): Hex File Converter
Convert File to/from Hex representation Fix #724
This commit is contained in:
		
							parent
							
								
									318fb6efb9
								
							
						
					
					
						commit
						73e97f97bb
					
				
							
								
								
									
										8
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								components.d.ts
									
									
									
									
										vendored
									
									
								
							| @ -83,6 +83,7 @@ declare module '@vue/runtime-core' { | ||||
|     GitMemo: typeof import('./src/tools/git-memo/git-memo.vue')['default'] | ||||
|     'GitMemo.content': typeof import('./src/tools/git-memo/git-memo.content.md')['default'] | ||||
|     HashText: typeof import('./src/tools/hash-text/hash-text.vue')['default'] | ||||
|     HexFileConverter: typeof import('./src/tools/hex-file-converter/hex-file-converter.vue')['default'] | ||||
|     HmacGenerator: typeof import('./src/tools/hmac-generator/hmac-generator.vue')['default'] | ||||
|     'Home.page': typeof import('./src/pages/Home.page.vue')['default'] | ||||
|     HtmlEntities: typeof import('./src/tools/html-entities/html-entities.vue')['default'] | ||||
| @ -132,19 +133,18 @@ declare module '@vue/runtime-core' { | ||||
|     NCode: typeof import('naive-ui')['NCode'] | ||||
|     NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] | ||||
|     NConfigProvider: typeof import('naive-ui')['NConfigProvider'] | ||||
|     NDivider: typeof import('naive-ui')['NDivider'] | ||||
|     NEllipsis: typeof import('naive-ui')['NEllipsis'] | ||||
|     NForm: typeof import('naive-ui')['NForm'] | ||||
|     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'] | ||||
|     NLayout: typeof import('naive-ui')['NLayout'] | ||||
|     NLayoutSider: typeof import('naive-ui')['NLayoutSider'] | ||||
|     NMenu: typeof import('naive-ui')['NMenu'] | ||||
|     NScrollbar: typeof import('naive-ui')['NScrollbar'] | ||||
|     NSlider: typeof import('naive-ui')['NSlider'] | ||||
|     NSwitch: typeof import('naive-ui')['NSwitch'] | ||||
|     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'] | ||||
|  | ||||
| @ -134,6 +134,7 @@ | ||||
|     "unplugin-icons": "^0.17.0", | ||||
|     "unplugin-vue-components": "^0.25.0", | ||||
|     "vite": "^4.4.9", | ||||
|     "vite-plugin-node-polyfills": "^0.22.0", | ||||
|     "vite-plugin-pwa": "^0.16.0", | ||||
|     "vite-plugin-vue-markdown": "^0.23.5", | ||||
|     "vite-svg-loader": "^4.0.0", | ||||
|  | ||||
							
								
								
									
										640
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										640
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,6 +1,7 @@ | ||||
| import { extension as getExtensionFromMimeType, extension as getMimeTypeFromExtension } from 'mime-types'; | ||||
| import type { Ref } from 'vue'; | ||||
| import type { MaybeRef, Ref } from 'vue'; | ||||
| import _ from 'lodash'; | ||||
| import { get } from '@vueuse/core'; | ||||
| 
 | ||||
| export { | ||||
|   getMimeTypeFromBase64, | ||||
| @ -75,21 +76,11 @@ function downloadFromBase64({ sourceValue, filename, extension, fileMimeType }: | ||||
| } | ||||
| 
 | ||||
| function useDownloadFileFromBase64( | ||||
|   { source, filename, extension, fileMimeType }: | ||||
|   { source: Ref<string>; filename?: string; extension?: string; fileMimeType?: string }) { | ||||
|   return { | ||||
|     download() { | ||||
|       downloadFromBase64({ sourceValue: source.value, filename, extension, fileMimeType }); | ||||
|     }, | ||||
|   }; | ||||
| } | ||||
| 
 | ||||
| function useDownloadFileFromBase64Refs( | ||||
|   { source, filename, extension }: | ||||
|   { source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) { | ||||
|   { source: MaybeRef<string>; filename?: MaybeRef<string>; extension?: MaybeRef<string> }) { | ||||
|   return { | ||||
|     download() { | ||||
|       downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); | ||||
|       downloadFromBase64({ sourceValue: get(source), filename: get(filename), extension: get(extension) }); | ||||
|     }, | ||||
|   }; | ||||
| } | ||||
| @ -116,3 +107,13 @@ function previewImageFromBase64(base64String: string): HTMLImageElement { | ||||
| 
 | ||||
|   return img; | ||||
| } | ||||
| 
 | ||||
| function useDownloadFileFromBase64Refs( | ||||
|   { source, filename, extension }: | ||||
|   { source: Ref<string>; filename?: Ref<string>; extension?: Ref<string> }) { | ||||
|   return { | ||||
|     download() { | ||||
|       downloadFromBase64({ sourceValue: source.value, filename: filename?.value, extension: extension?.value }); | ||||
|     }, | ||||
|   }; | ||||
| } | ||||
|  | ||||
							
								
								
									
										145
									
								
								src/tools/hex-file-converter/hex-file-converter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/tools/hex-file-converter/hex-file-converter.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,145 @@ | ||||
| <script setup lang="ts"> | ||||
| import { Buffer } from 'node:buffer'; | ||||
| import type { Ref } from 'vue'; | ||||
| import { useCopy } from '@/composable/copy'; | ||||
| import { useDownloadFileFromBase64 } from '@/composable/downloadBase64'; | ||||
| 
 | ||||
| const fileName = ref(''); | ||||
| const fileExtension = ref(''); | ||||
| const hexInput = ref(''); | ||||
| const base64Input = computed(() => { | ||||
|   const hexString = hexInput.value?.replace(/[^\da-f]/gi, ''); | ||||
|   try { | ||||
|     return `data:application/octet-stream;base64,${Buffer.from(hexString, 'hex').toString('base64')}`; | ||||
|   } | ||||
|   catch { | ||||
|     return ''; | ||||
|   } | ||||
| }); | ||||
| const { download } = useDownloadFileFromBase64( | ||||
|   { | ||||
|     source: base64Input, | ||||
|     filename: fileName, | ||||
|     extension: fileExtension, | ||||
|   }); | ||||
| 
 | ||||
| function downloadFile() { | ||||
|   try { | ||||
|     download(); | ||||
|   } | ||||
|   catch (_) { | ||||
|     // | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| function buf2hex(buffer: ArrayBuffer, separator: string): string { | ||||
|   return [...new Uint8Array(buffer)] | ||||
|     .map(x => x.toString(16).padStart(2, '0')) | ||||
|     .join(separator); | ||||
| } | ||||
| 
 | ||||
| async function ReadFileAsHex(file: File, separator: string = ' '): Promise<string> { | ||||
|   return new Promise<string>((resolve, reject) => { | ||||
|     const reader = new FileReader(); | ||||
|     reader.onload = () => { | ||||
|       resolve(buf2hex(reader.result as ArrayBuffer, separator)); | ||||
|     }; | ||||
|     reader.onerror = () => reject(reader.error?.toString()); | ||||
|     reader.readAsArrayBuffer(file); | ||||
|   }); | ||||
| } | ||||
| 
 | ||||
| const separator = useStorage('hex-converter:sep', ' '); | ||||
| const fileInput = ref() as Ref<File>; | ||||
| const fileHex = computedAsync(async () => { | ||||
|   const file = fileInput.value; | ||||
|   const sep = separator.value; | ||||
| 
 | ||||
|   return await ReadFileAsHex(file, sep); | ||||
| }); | ||||
| const { copy: copyFileHex } = useCopy({ source: fileHex, text: 'Hex string copied to the clipboard' }); | ||||
| 
 | ||||
| function onUpload(file: File) { | ||||
|   if (file) { | ||||
|     fileInput.value = file; | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| 
 | ||||
| <template> | ||||
|   <c-card title="HEX to file"> | ||||
|     <n-grid cols="3" x-gap="12"> | ||||
|       <n-gi span="2"> | ||||
|         <c-input-text | ||||
|           v-model:value="fileName" | ||||
|           label="File Name" | ||||
|           placeholder="Download filename" | ||||
|           mb-2 | ||||
|         /> | ||||
|       </n-gi> | ||||
|       <n-gi> | ||||
|         <c-input-text | ||||
|           v-model:value="fileExtension" | ||||
|           label="Extension" | ||||
|           placeholder="Extension" | ||||
|         /> | ||||
|       </n-gi> | ||||
|     </n-grid> | ||||
| 
 | ||||
|     <n-form-item label="Content in Hex"> | ||||
|       <c-input-text | ||||
|         v-model:value="hexInput" | ||||
|         multiline | ||||
|         placeholder="Put your Hex file string here..." | ||||
|         rows="5" | ||||
|       /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <n-divider /> | ||||
| 
 | ||||
|     <div flex justify-center> | ||||
|       <c-button :disabled="hexInput === ''" @click="downloadFile()"> | ||||
|         Download file | ||||
|       </c-button> | ||||
|     </div> | ||||
|   </c-card> | ||||
| 
 | ||||
|   <c-card title="File to HEX"> | ||||
|     <c-file-upload | ||||
|       title="Drag and drop a file here, or click to select a file" | ||||
|       mb-2 | ||||
|       @file-upload="onUpload" | ||||
|     /> | ||||
| 
 | ||||
|     <c-input-text | ||||
|       v-model:value="separator" | ||||
|       label="Separator" | ||||
|       label-position="left" | ||||
|       placeholder="Separator" | ||||
|       mb-2 | ||||
|     /> | ||||
| 
 | ||||
|     <n-divider /> | ||||
| 
 | ||||
|     <n-form-item label="File in Hex"> | ||||
|       <c-input-text | ||||
|         :value="fileHex" | ||||
|         multiline readonly | ||||
|         placeholder="File in hex will be here" | ||||
|         rows="5" mb-2 | ||||
|       /> | ||||
|     </n-form-item> | ||||
| 
 | ||||
|     <div flex justify-center> | ||||
|       <c-button @click="copyFileHex()"> | ||||
|         Copy | ||||
|       </c-button> | ||||
|     </div> | ||||
|   </c-card> | ||||
| </template> | ||||
| 
 | ||||
| <style lang="less" scoped> | ||||
| ::v-deep(.n-upload-trigger) { | ||||
|   width: 100%; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										12
									
								
								src/tools/hex-file-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/tools/hex-file-converter/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { FileDigit } from '@vicons/tabler'; | ||||
| import { defineTool } from '../tool'; | ||||
| 
 | ||||
| export const tool = defineTool({ | ||||
|   name: 'HEX File Converter', | ||||
|   path: '/hex-file-converter', | ||||
|   description: 'Convert between file and hexadecimal representation', | ||||
|   keywords: ['hex', 'file', 'converter'], | ||||
|   component: () => import('./hex-file-converter.vue'), | ||||
|   icon: FileDigit, | ||||
|   createdAt: new Date('2024-08-15'), | ||||
| }); | ||||
| @ -2,6 +2,7 @@ import { tool as base64FileConverter } from './base64-file-converter'; | ||||
| import { tool as base64StringConverter } from './base64-string-converter'; | ||||
| import { tool as basicAuthGenerator } from './basic-auth-generator'; | ||||
| import { tool as emailNormalizer } from './email-normalizer'; | ||||
| import { tool as hexFileConverter } from './hex-file-converter'; | ||||
| 
 | ||||
| import { tool as asciiTextDrawer } from './ascii-text-drawer'; | ||||
| 
 | ||||
| @ -98,6 +99,7 @@ export const toolsByCategory: ToolCategory[] = [ | ||||
|       romanNumeralConverter, | ||||
|       base64StringConverter, | ||||
|       base64FileConverter, | ||||
|       hexFileConverter, | ||||
|       colorConverter, | ||||
|       caseConverter, | ||||
|       textToNatoAlphabet, | ||||
|  | ||||
| @ -15,6 +15,7 @@ import { VitePWA } from 'vite-plugin-pwa'; | ||||
| import markdown from 'vite-plugin-vue-markdown'; | ||||
| import svgLoader from 'vite-svg-loader'; | ||||
| import { configDefaults } from 'vitest/config'; | ||||
| import { nodePolyfills } from 'vite-plugin-node-polyfills' | ||||
| 
 | ||||
| const baseUrl = process.env.BASE_URL ?? '/'; | ||||
| 
 | ||||
| @ -97,6 +98,7 @@ export default defineConfig({ | ||||
|       resolvers: [NaiveUiResolver(), IconsResolver({ prefix: 'icon' })], | ||||
|     }), | ||||
|     Unocss(), | ||||
|     nodePolyfills(), | ||||
|   ], | ||||
|   base: baseUrl, | ||||
|   resolve: { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user