Skip to content

Commit bcfe44e

Browse files
committed
Merge branch 'feat/hex-file-converter' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml # src/tools/index.ts # vite.config.ts
2 parents 9deccfa + 8c65ad8 commit bcfe44e

File tree

5 files changed

+171
-9
lines changed

5 files changed

+171
-9
lines changed

pnpm-lock.yaml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
<script setup lang="ts">
2+
import { Buffer } from 'node:buffer';
3+
import type { Ref } from 'vue';
4+
import { useCopy } from '@/composable/copy';
5+
import { useDownloadFileFromBase64 } from '@/composable/downloadBase64';
6+
7+
const fileName = ref('');
8+
const fileExtension = ref('');
9+
const hexInput = ref('');
10+
const base64Input = computed(() => {
11+
const hexString = hexInput.value?.replace(/^(?:0x|&H|\\x)/gi, '').replace(/[^\da-f]/gi, '');
12+
try {
13+
return `data:application/octet-stream;base64,${Buffer.from(hexString, 'hex').toString('base64')}`;
14+
}
15+
catch {
16+
return '';
17+
}
18+
});
19+
const { download } = useDownloadFileFromBase64(
20+
{
21+
source: base64Input,
22+
filename: fileName,
23+
extension: fileExtension,
24+
});
25+
26+
function downloadFile() {
27+
try {
28+
download();
29+
}
30+
catch (_) {
31+
//
32+
}
33+
}
34+
35+
function buf2hex(buffer: ArrayBuffer, separator: string): string {
36+
return [...new Uint8Array(buffer)]
37+
.map(x => x.toString(16).padStart(2, '0'))
38+
.join(separator);
39+
}
40+
41+
async function ReadFileAsHex(file: File, separator: string = ' '): Promise<string> {
42+
return new Promise<string>((resolve, reject) => {
43+
const reader = new FileReader();
44+
reader.onload = () => {
45+
resolve(buf2hex(reader.result as ArrayBuffer, separator));
46+
};
47+
reader.onerror = () => reject(reader.error?.toString());
48+
reader.readAsArrayBuffer(file);
49+
});
50+
}
51+
52+
const separator = useStorage('hex-converter:sep', ' ');
53+
const fileInput = ref() as Ref<File>;
54+
const prefix = useStorage('hex-converter:prefix', '');
55+
const fileHex = computedAsync(async () => {
56+
const file = fileInput.value;
57+
const sep = separator.value;
58+
const pref = prefix.value;
59+
60+
return pref + await ReadFileAsHex(file, sep);
61+
});
62+
const { copy: copyFileHex } = useCopy({ source: fileHex, text: 'Hex string copied to the clipboard' });
63+
64+
function onUpload(file: File) {
65+
if (file) {
66+
fileInput.value = file;
67+
}
68+
}
69+
</script>
70+
71+
<template>
72+
<c-card title="HEX to file">
73+
<n-grid cols="3" x-gap="12">
74+
<n-gi span="2">
75+
<c-input-text
76+
v-model:value="fileName"
77+
label="File Name"
78+
placeholder="Download filename"
79+
mb-2
80+
/>
81+
</n-gi>
82+
<n-gi>
83+
<c-input-text
84+
v-model:value="fileExtension"
85+
label="Extension"
86+
placeholder="Extension"
87+
/>
88+
</n-gi>
89+
</n-grid>
90+
91+
<n-form-item label="Content in Hex">
92+
<c-input-text
93+
v-model:value="hexInput"
94+
multiline
95+
placeholder="Put your Hex file string here..."
96+
rows="5"
97+
/>
98+
</n-form-item>
99+
100+
<n-divider />
101+
102+
<div flex justify-center>
103+
<c-button :disabled="hexInput === ''" @click="downloadFile()">
104+
Download file
105+
</c-button>
106+
</div>
107+
</c-card>
108+
109+
<c-card title="File to HEX">
110+
<c-file-upload
111+
title="Drag and drop a file here, or click to select a file"
112+
mb-2
113+
@file-upload="onUpload"
114+
/>
115+
116+
<c-input-text
117+
v-model:value="separator"
118+
label="Separator"
119+
label-position="left"
120+
placeholder="Separator"
121+
mb-2
122+
/>
123+
124+
<c-input-text
125+
v-model:value="prefix"
126+
label="Prefix"
127+
label-position="left"
128+
placeholder="Enter a prefix (ie, 0x, &H or empty)"
129+
mb-2
130+
/>
131+
132+
<n-divider />
133+
134+
<n-form-item label="File in Hex">
135+
<c-input-text
136+
:value="fileHex"
137+
multiline readonly
138+
placeholder="File in hex will be here"
139+
rows="5" mb-2
140+
/>
141+
</n-form-item>
142+
143+
<div flex justify-center>
144+
<c-button @click="copyFileHex()">
145+
Copy
146+
</c-button>
147+
</div>
148+
</c-card>
149+
</template>
150+
151+
<style lang="less" scoped>
152+
::v-deep(.n-upload-trigger) {
153+
width: 100%;
154+
}
155+
</style>

src/tools/hex-file-converter/index.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { FileDigit } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'HEX File Converter',
6+
path: '/hex-file-converter',
7+
description: 'Convert between file and hexadecimal representation',
8+
keywords: ['hex', 'file', 'converter'],
9+
component: () => import('./hex-file-converter.vue'),
10+
icon: FileDigit,
11+
createdAt: new Date('2024-08-15'),
12+
});

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { tool as currencyConverter } from './currency-converter';
1010
import { tool as emailParser } from './email-parser';
1111
import { tool as outlookParser } from './outlook-parser';
1212
import { tool as fileHasher } from './file-hasher';
13+
import { tool as hexFileConverter } from './hex-file-converter';
1314

1415
import { tool as cssXpathConverter } from './css-xpath-converter';
1516
import { tool as cssSelectorsMemo } from './css-selectors-memo';
@@ -144,6 +145,7 @@ export const toolsByCategory: ToolCategory[] = [
144145
romanNumeralConverter,
145146
base64StringConverter,
146147
base64FileConverter,
148+
hexFileConverter,
147149
colorConverter,
148150
caseConverter,
149151
textToNatoAlphabet,

vite.config.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@ import { VitePWA } from 'vite-plugin-pwa';
1616
import markdown from 'vite-plugin-vue-markdown';
1717
import svgLoader from 'vite-svg-loader';
1818
import { configDefaults } from 'vitest/config';
19-
<<<<<<< HEAD
2019
import topLevelAwait from "vite-plugin-top-level-await";
21-
=======
22-
import { nodePolyfills } from 'vite-plugin-node-polyfills';
23-
>>>>>>> feat/email-parser
2420

2521
const baseUrl = process.env.BASE_URL ?? '/';
2622

@@ -104,15 +100,12 @@ export default defineConfig({
104100
}),
105101
Unocss(),
106102
nodePolyfills(),
107-
<<<<<<< HEAD
108103
topLevelAwait({
109104
// The export name of top-level await promise for each chunk module
110105
promiseExportName: '__tla',
111106
// The function to generate import names of top-level await promise in each chunk module
112107
promiseImportName: i => `__tla_${i}`,
113108
}),
114-
=======
115-
>>>>>>> feat/email-parser
116109
],
117110
base: baseUrl,
118111
resolve: {

0 commit comments

Comments
 (0)