Skip to content

Commit 2b06b5b

Browse files
committed
feat(new tool): Charset Detector/Decoder
1 parent d450f3b commit 2b06b5b

File tree

4 files changed

+174
-5
lines changed

4 files changed

+174
-5
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
"bwip-js": "^4.5.1",
102102
"byte-data": "^19.0.1",
103103
"change-case": "^5.4.4",
104+
"chardet": "^2.1.0",
104105
"chatgpt-prompt-splitter": "^1.0.5",
105106
"chinesegen": "^0.3.3",
106107
"cidr-tools": "^11.0.3",

pnpm-lock.yaml

Lines changed: 12 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
<script setup lang="ts">
2+
import { Buffer } from 'node:buffer';
3+
import chardet from 'chardet';
4+
import iconv from 'iconv-lite';
5+
6+
const inputType = ref<'file' | 'content'>('file');
7+
const fileContent = ref('');
8+
const fileInput = ref() as Ref<File | null>;
9+
const error = ref('');
10+
const contentDecoded = ref('');
11+
12+
async function getUInt8ArrayFromInputs() {
13+
const fileContentValue = fileContent.value;
14+
const file = fileInput.value;
15+
if (inputType.value === 'file' && file) {
16+
return new Uint8Array(await file.arrayBuffer());
17+
}
18+
else if (inputType.value === 'content' && fileContentValue) {
19+
const uint8Array = new Uint8Array(fileContentValue.length);
20+
for (let i = 0; i < fileContentValue.length; i++) {
21+
uint8Array[i] = fileContentValue.charCodeAt(i);
22+
}
23+
return uint8Array;
24+
}
25+
else {
26+
return null;
27+
}
28+
}
29+
30+
const encodings = computedAsync(async () => {
31+
error.value = '';
32+
try {
33+
const buffer = await getUInt8ArrayFromInputs();
34+
if (buffer === null) {
35+
return null;
36+
}
37+
return chardet.analyse(buffer);
38+
}
39+
catch (e: any) {
40+
error.value = e.toString();
41+
return null;
42+
}
43+
});
44+
45+
async function generateEncodedOutput(encoding: string) {
46+
error.value = '';
47+
contentDecoded.value = '';
48+
try {
49+
const buffer = await getUInt8ArrayFromInputs();
50+
if (buffer === null) {
51+
return;
52+
}
53+
contentDecoded.value = iconv.decode(Buffer.from(buffer), encoding);
54+
}
55+
catch (e: any) {
56+
return e.toString();
57+
}
58+
}
59+
60+
function onUpload(file: File) {
61+
if (file) {
62+
fileInput.value = file;
63+
}
64+
}
65+
</script>
66+
67+
<template>
68+
<div style="max-width: 600px;">
69+
<c-card title="Input" mb-2>
70+
<n-radio-group v-model:value="inputType" name="radiogroup" mb-2 flex justify-center>
71+
<n-space>
72+
<n-radio
73+
value="file"
74+
label="File"
75+
/>
76+
<n-radio
77+
value="content"
78+
label="Content"
79+
/>
80+
</n-space>
81+
</n-radio-group>
82+
83+
<c-file-upload
84+
v-if="inputType === 'file'"
85+
title="Drag and drop TXT file here, or click to select a file"
86+
@file-upload="onUpload"
87+
/>
88+
89+
<c-input-text
90+
v-if="inputType === 'content'"
91+
v-model:value="fileContent"
92+
label="File Content"
93+
multiline
94+
placeholder="Put your text content here..."
95+
rows="15"
96+
mb-2
97+
/>
98+
</c-card>
99+
100+
<c-alert v-if="error">
101+
{{ error }}
102+
</c-alert>
103+
104+
<c-card v-if="!error && encodings" title="Possible encodings">
105+
<n-table>
106+
<thead>
107+
<tr>
108+
<th scope="col">
109+
Confidence
110+
</th>
111+
<th scope="col">
112+
Name
113+
</th>
114+
<th scope="col">
115+
Lang
116+
</th>
117+
<th />
118+
</tr>
119+
</thead>
120+
<tbody>
121+
<tr
122+
v-for="(enc, index) in encodings || []"
123+
:key="index"
124+
>
125+
<td>
126+
{{ enc.confidence }}
127+
</td>
128+
<td>
129+
{{ enc.name }}
130+
</td>
131+
<td>
132+
{{ enc.lang }}
133+
</td>
134+
<td>
135+
<n-button @click="generateEncodedOutput(enc.name.toLocaleLowerCase())">
136+
Decode
137+
</n-button>
138+
</td>
139+
</tr>
140+
</tbody>
141+
</n-table>
142+
143+
<n-card v-if="contentDecoded" title="Decoded text" mb-1>
144+
<textarea-copyable :value="contentDecoded" />
145+
</n-card>
146+
</c-card>
147+
</div>
148+
</template>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { FileText } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'Text Charset Detector/Decoder',
6+
path: '/charset-detector',
7+
description: 'Detect text possible charsets and allow to decode using each detected encoding',
8+
keywords: ['charset', 'ascii', 'iso', 'utf8', 'unicode', 'encoding', 'decode', 'text', 'detector'],
9+
component: () => import('./charset-detector.vue'),
10+
icon: FileText,
11+
createdAt: new Date('2025-03-09'),
12+
category: 'Forensic',
13+
});

0 commit comments

Comments
 (0)