Skip to content

Commit 376adab

Browse files
committed
Edit functionality for HashCode website tool
1 parent e63a8ec commit 376adab

1 file changed

Lines changed: 92 additions & 6 deletions

File tree

uuid_tool/src/pages/Index.tsx

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,41 @@ export default function Index() {
3535
const [isDragging, setIsDragging] = useState(false);
3636
const [activeTab, setActiveTab] = useState<"binary" | "image">("binary");
3737
const [error, setError] = useState<string | null>(null);
38+
const [isEditing, setIsEditing] = useState(false);
39+
const [editText, setEditText] = useState("");
40+
41+
const uuidsToBytes = (uuidList: string[]): Uint8Array => {
42+
const bytes = new Uint8Array(uuidList.length * 16);
43+
uuidList.forEach((uuid, i) => {
44+
const hex = uuid.replace(/-/g, "");
45+
for (let j = 0; j < 16; j++) {
46+
bytes[i * 16 + j] = parseInt(hex.slice(j * 2, j * 2 + 2), 16);
47+
}
48+
});
49+
return bytes;
50+
};
51+
52+
const handleEdit = () => {
53+
setEditText(uuids.join("\n"));
54+
setIsEditing(true);
55+
};
56+
57+
const handleSave = () => {
58+
const lines = editText.split("\n").map(l => l.trim()).filter(l => l.length > 0);
59+
60+
const bytes = uuidsToBytes(lines);
61+
const blob = new Blob([bytes.buffer as ArrayBuffer], { type: "application/octet-stream" });
62+
const url = URL.createObjectURL(blob);
63+
const a = document.createElement("a");
64+
a.href = url;
65+
a.download = "uuids.bin";
66+
a.click();
67+
URL.revokeObjectURL(url);
68+
69+
setUuids(lines);
70+
setIsEditing(false);
71+
setError(null);
72+
};
3873

3974
const bytesToHex = (bytes: Uint8Array): string =>
4075
Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
@@ -206,17 +241,68 @@ export default function Index() {
206241
{uuids.length > 0 && (
207242
<div className="mt-6 border border-border rounded-lg p-4">
208243
<div className="mb-3 flex items-center justify-between">
209-
<span className="text-sm text-muted-foreground">{uuids.length} UUID{uuids.length !== 1 && "s"}</span>
210-
<button onClick={copyToClipboard} className="px-3 py-1 text-sm rounded hover:bg-muted transition-colors">
211-
{copied ? "✓ Copied" : "Copy All"}
212-
</button>
244+
<span className="text-sm text-muted-foreground">
245+
{(() => {
246+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
247+
if (!isEditing) {
248+
const uniqueCount = new Set(uuids.map(u => u.toLowerCase())).size;
249+
return `${uniqueCount} UUID${uniqueCount !== 1 ? "s" : ""}`;
250+
}
251+
const lines = editText.split("\n").map(l => l.trim().toLowerCase()).filter(l => l.length > 0);
252+
const invalidCount = lines.filter(l => !uuidRegex.test(l)).length;
253+
if (invalidCount > 0) return <span className="text-red-500">{invalidCount} invalid UUID{invalidCount !== 1 ? "s" : ""}</span>;
254+
const uniqueCount = new Set(lines).size;
255+
return `${uniqueCount} UUID${uniqueCount !== 1 ? "s" : ""}`;
256+
})()}
257+
</span>
258+
<div className="flex gap-2">
259+
<button onClick={copyToClipboard} className="px-3 py-1 text-sm rounded hover:bg-muted transition-colors">
260+
{copied ? "✓ Copied" : "Copy All"}
261+
</button>
262+
{isEditing ? (
263+
(() => {
264+
const lines = editText.split("\n").map(l => l.trim().toLowerCase()).filter(l => l.length > 0);
265+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
266+
const hasInvalid = lines.some(l => !uuidRegex.test(l));
267+
const editSet = new Set(lines);
268+
const originalSet = new Set(uuids.map(u => u.toLowerCase()));
269+
const setsEqual = editSet.size === originalSet.size && [...editSet].every(u => originalSet.has(u));
270+
const isDisabled = setsEqual || hasInvalid;
271+
return (
272+
<button
273+
onClick={handleSave}
274+
disabled={isDisabled}
275+
className={`px-3 py-1 text-sm rounded transition-colors ${
276+
isDisabled
277+
? "bg-muted text-muted-foreground cursor-not-allowed"
278+
: "bg-accent text-white hover:bg-accent/80"
279+
}`}
280+
>
281+
Save
282+
</button>
283+
);
284+
})()
285+
) : (
286+
<button onClick={handleEdit} className="px-3 py-1 text-sm rounded hover:bg-muted transition-colors">
287+
Edit
288+
</button>
289+
)}
290+
</div>
213291
</div>
214292
<div className="max-h-96 overflow-auto rounded bg-muted p-4">
215-
<pre className="font-mono text-sm whitespace-pre-wrap break-all">{uuids.join("\n")}</pre>
293+
{isEditing ? (
294+
<textarea
295+
value={editText}
296+
onChange={(e) => setEditText(e.target.value)}
297+
className="w-full h-64 font-mono text-sm bg-transparent border-none outline-none resize-none"
298+
/>
299+
) : (
300+
<pre className="font-mono text-sm whitespace-pre-wrap break-all">{uuids.join("\n")}</pre>
301+
)}
216302
</div>
217303
</div>
218304
)}
219305
</div>
220306
</div>
221307
);
222-
}
308+
}

0 commit comments

Comments
 (0)