@@ -125,13 +126,21 @@ const AmIUsingTor = () => {
} catch {}
};
- const copyResult = () => {
+ const copyResult = async () => {
const result = `Tor Detection Result: ${torResult.isTor ? 'YES' : 'NO'}
Confidence: ${torResult.confidence}
Details: ${torResult.details}
User Agent: ${navigator.userAgent}
Timestamp: ${new Date().toISOString()}`;
- copy(result);
+ try {
+ await navigator.clipboard.writeText(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Am I Using Tor",
+ tool_action: "Copy",
+ });
+ } catch {}
};
const getStatusColor = () => {
diff --git a/app/components/developmentToolsComponent/apiKeyGenerator.tsx b/app/components/developmentToolsComponent/apiKeyGenerator.tsx
index 7458a0e..d04031b 100644
--- a/app/components/developmentToolsComponent/apiKeyGenerator.tsx
+++ b/app/components/developmentToolsComponent/apiKeyGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Charset = {
upper: boolean;
@@ -85,6 +86,16 @@ const ApiKeyGenerator: React.FC = () => {
run();
}, [length, count, groupSize, charset, prefix, suffix, autoUpdate]);
+ const handleGenerateClick = () => {
+ run();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "API Key Generator",
+ tool_action: "Generate",
+ });
+ };
+
const onCopy = async () => {
try {
await navigator.clipboard.writeText(keys.join("\n"));
@@ -239,7 +250,7 @@ const ApiKeyGenerator: React.FC = () => {
copy(rgbaCss)}
+ onClick={() => copy(rgbaCss, "Copy RGBA")}
className={`${DevelopmentToolsStyles.converterButton} text-black font-bold py-3 px-6 rounded-lg`}
>
Copy RGBA
diff --git a/app/components/developmentToolsComponent/creditCardGeneratorComponent.tsx b/app/components/developmentToolsComponent/creditCardGeneratorComponent.tsx
index 1500fed..3db4263 100644
--- a/app/components/developmentToolsComponent/creditCardGeneratorComponent.tsx
+++ b/app/components/developmentToolsComponent/creditCardGeneratorComponent.tsx
@@ -10,6 +10,7 @@ import DinersClubCardIcon from "../theme/Icon/dinersClubCardIcon";
import DiscoverCardIcon from "../theme/Icon/discoverCardIcon";
import JCBCardIcon from "../theme/Icon/JCBCardIcon";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
// Map card types to their respective icons
const cardIcons: any = {
@@ -155,6 +156,12 @@ const CreditCardGeneratorComponent = () => {
}
setCards(generatedCards); // ✅ Correctly update the state
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Credit Card Generator",
+ tool_action: "Generate",
+ });
};
const handleCopy = (fieldId: any, index: any) => {
diff --git a/app/components/developmentToolsComponent/creditCardValidatorComponent.tsx b/app/components/developmentToolsComponent/creditCardValidatorComponent.tsx
index dc296e7..667554c 100644
--- a/app/components/developmentToolsComponent/creditCardValidatorComponent.tsx
+++ b/app/components/developmentToolsComponent/creditCardValidatorComponent.tsx
@@ -3,6 +3,7 @@ import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const cardConfigs = {
Visa: { bin: /^4/, length: [16] },
@@ -60,6 +61,12 @@ const CreditCardValidatorComponent = () => {
const isValidCard = luhnCheck(sanitizedNumber);
setIsValid(isValidCard);
setCardType(getCardType(sanitizedNumber));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Credit Card Validator",
+ tool_action: "Validate",
+ });
// Show appropriate toast message
if (!isValidCard) {
diff --git a/app/components/developmentToolsComponent/crontabGenerator.tsx b/app/components/developmentToolsComponent/crontabGenerator.tsx
index 3e37b26..7c8da2c 100644
--- a/app/components/developmentToolsComponent/crontabGenerator.tsx
+++ b/app/components/developmentToolsComponent/crontabGenerator.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Field = "minute" | "hour" | "dom" | "month" | "dow";
@@ -238,7 +239,17 @@ const CrontabGenerator = () => {
setDow(parse(dw));
};
- const copy = async () => { try { await navigator.clipboard.writeText(cron); } catch {} };
+ const copy = async () => {
+ try {
+ await navigator.clipboard.writeText(cron);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Crontab Generator",
+ tool_action: "Copy",
+ });
+ } catch {}
+ };
return (
diff --git a/app/components/developmentToolsComponent/cssMinify.tsx b/app/components/developmentToolsComponent/cssMinify.tsx
index fad8ade..90a145d 100644
--- a/app/components/developmentToolsComponent/cssMinify.tsx
+++ b/app/components/developmentToolsComponent/cssMinify.tsx
@@ -2,6 +2,7 @@
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const minify = (css: string): string =>
css
@@ -43,6 +44,12 @@ const CssMinify = () => {
const src = input || example;
const result = minify(src);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS Minify",
+ tool_action: "Minify",
+ });
};
const copy = async () =>
diff --git a/app/components/developmentToolsComponent/cssPrettify.tsx b/app/components/developmentToolsComponent/cssPrettify.tsx
index 55286e2..ab60a63 100644
--- a/app/components/developmentToolsComponent/cssPrettify.tsx
+++ b/app/components/developmentToolsComponent/cssPrettify.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
// Very lightweight CSS formatter (not a full parser but practical)
const prettify = (css: string, indentSize: number): string => {
@@ -31,6 +32,12 @@ const CssPrettify = () => {
const src = input || example;
const result = prettify(src, indentSize);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS Prettify",
+ tool_action: "Prettify",
+ });
};
const copy = async () => output && (await navigator.clipboard.writeText(output));
diff --git a/app/components/developmentToolsComponent/cssToLess.tsx b/app/components/developmentToolsComponent/cssToLess.tsx
index 88ebdaf..817aaf8 100644
--- a/app/components/developmentToolsComponent/cssToLess.tsx
+++ b/app/components/developmentToolsComponent/cssToLess.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const Cmd = ({ children }: { children: string }) => (
{children}
@@ -73,7 +74,15 @@ const CssToLess = () => {
const less = useMemo(() => convertCssToLess(css), [css]);
const copyToClipboard = async (text: string) => {
- try { await navigator.clipboard.writeText(text); } catch {}
+ try {
+ await navigator.clipboard.writeText(text);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS to LESS",
+ tool_action: "Copy",
+ });
+ } catch {}
};
const clearAll = () => setCss("");
diff --git a/app/components/developmentToolsComponent/cssToSass.tsx b/app/components/developmentToolsComponent/cssToSass.tsx
index 7019f73..a33dc8b 100644
--- a/app/components/developmentToolsComponent/cssToSass.tsx
+++ b/app/components/developmentToolsComponent/cssToSass.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const Cmd = ({ children }: { children: string }) => (
{children}
@@ -102,6 +103,12 @@ h1 {
const copyToClipboard = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS to SASS",
+ tool_action: "Copy",
+ });
} catch (err) {
console.error('Failed to copy text: ', err);
}
diff --git a/app/components/developmentToolsComponent/cssToScssConverter.tsx b/app/components/developmentToolsComponent/cssToScssConverter.tsx
index d434986..c294694 100644
--- a/app/components/developmentToolsComponent/cssToScssConverter.tsx
+++ b/app/components/developmentToolsComponent/cssToScssConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type VarMap = Record;
@@ -114,7 +115,15 @@ const CSSToSCSSConverter = () => {
const [scss, setScss] = useState("");
const fileRef = useRef(null);
- const onConvert = () => setScss(convertCssToScss(css));
+ const onConvert = () => {
+ setScss(convertCssToScss(css));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS to SCSS Converter",
+ tool_action: "Convert",
+ });
+ };
const onCopy = async () => { try { await navigator.clipboard.writeText(scss); } catch {} };
const onDownload = () => {
const blob = new Blob([scss], { type: "text/x-scss;charset=utf-8" });
diff --git a/app/components/developmentToolsComponent/cssToStylus.tsx b/app/components/developmentToolsComponent/cssToStylus.tsx
index a4880ef..ce08758 100644
--- a/app/components/developmentToolsComponent/cssToStylus.tsx
+++ b/app/components/developmentToolsComponent/cssToStylus.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -106,6 +107,16 @@ const CssToStylus: React.FC = () => {
const convert = useCallback(() => setOutput(cssToStylus(input, options)), [input, options]);
useEffect(() => { if (autoUpdate) convert(); }, [input, options, autoUpdate, convert]);
+ const handleConvertClick = useCallback(() => {
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS to Stylus Converter",
+ tool_action: "Convert",
+ });
+ }, [convert]);
+
const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
@@ -136,7 +147,7 @@ const CssToStylus: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ Convert
Copy
diff --git a/app/components/developmentToolsComponent/cssValidator.tsx b/app/components/developmentToolsComponent/cssValidator.tsx
index 0546186..c0e8f5b 100644
--- a/app/components/developmentToolsComponent/cssValidator.tsx
+++ b/app/components/developmentToolsComponent/cssValidator.tsx
@@ -1,5 +1,6 @@
"use client";
-import React, { useMemo, useState } from "react";
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IssueType = "error" | "warning";
interface Issue { line: number; column: number; message: string; type: IssueType }
@@ -55,6 +56,29 @@ const CssValidator = () => {
const [css, setCss] = useState
(`/* Paste CSS here */\nbody {\n font-family: system-ui\n color: #222;\n}`);
const issues = useMemo(() => validateCss(css), [css]);
const hasErrors = issues.some((i) => i.type === "error");
+ const debounceTimerRef = useRef | null>(null);
+ const isFirstRenderRef = useRef(true);
+
+ useEffect(() => {
+ if (isFirstRenderRef.current) {
+ isFirstRenderRef.current = false;
+ return;
+ }
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ if (css.trim()) {
+ debounceTimerRef.current = setTimeout(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSS Validator",
+ tool_action: "Validate",
+ });
+ }, 1000);
+ }
+ return () => {
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ };
+ }, [css]);
return (
diff --git a/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx b/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx
index 152886a..7a94914 100644
--- a/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx
+++ b/app/components/developmentToolsComponent/csvToExcelFileConvertor.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ParsedCSV = {
rows: string[][];
@@ -116,6 +117,12 @@ const CsvToExcelFileConvertor = () => {
a.click();
URL.revokeObjectURL(url);
}
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSV to Excel File Convertor",
+ tool_action: "Download",
+ });
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/csvToJson.tsx b/app/components/developmentToolsComponent/csvToJson.tsx
index 182df85..d6647c0 100644
--- a/app/components/developmentToolsComponent/csvToJson.tsx
+++ b/app/components/developmentToolsComponent/csvToJson.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const detectDelimiter = (text: string): string => {
const candidates = [",", ";", "\t", "|", ":"]; // common delimiters
@@ -76,6 +77,12 @@ const CSVToJSON = () => {
const data = parseCSV(csv, { delimiter, hasHeader, trim, ignoreEmpty });
try {
setJsonOut(JSON.stringify(data, null, 2));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSV to JSON Converter",
+ tool_action: "Convert",
+ });
} catch {
setJsonOut("[]");
}
diff --git a/app/components/developmentToolsComponent/csvToTextConverter.tsx b/app/components/developmentToolsComponent/csvToTextConverter.tsx
index a80ae1f..7f96a56 100644
--- a/app/components/developmentToolsComponent/csvToTextConverter.tsx
+++ b/app/components/developmentToolsComponent/csvToTextConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState, useRef } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const CsvToTextConverter = () => {
const [csvText, setCsvText] = useState("");
@@ -173,6 +174,12 @@ const CsvToTextConverter = () => {
await navigator.clipboard.writeText(convertedText);
setError("Text copied to clipboard successfully!");
setTimeout(() => setError(""), 3000);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "CSV to TXT Converter",
+ tool_action: "Copy",
+ });
} catch (err) {
setError("Failed to copy to clipboard");
}
diff --git a/app/components/developmentToolsComponent/curlToCodeConverter.tsx b/app/components/developmentToolsComponent/curlToCodeConverter.tsx
index 039a945..be1dc37 100644
--- a/app/components/developmentToolsComponent/curlToCodeConverter.tsx
+++ b/app/components/developmentToolsComponent/curlToCodeConverter.tsx
@@ -3,6 +3,7 @@ import React, { useState, useCallback } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import CopyIcon from "../theme/Icon/copyIcon";
import ReloadIcon from "../theme/Icon/reload";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
// ─── Types ───────────────────────────────────────────────────────────────────
@@ -591,6 +592,12 @@ const CurlToCodeConverter = () => {
await navigator.clipboard.writeText(output);
setCopySuccess(true);
setTimeout(() => setCopySuccess(false), 2000);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Curl To Code Converter",
+ tool_action: "Copy",
+ });
} catch {
setError("Failed to copy to clipboard.");
}
diff --git a/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx b/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx
index f4d5799..7648c76 100644
--- a/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx
+++ b/app/components/developmentToolsComponent/decimalToAsciiConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const DecimalToAsciiConverter = () => {
const [input, setInput] = useState("");
@@ -35,7 +36,16 @@ const DecimalToAsciiConverter = () => {
return;
}
- setOutput(convertDecimalToAscii(input));
+ const result = convertDecimalToAscii(input);
+ setOutput(result);
+ if (!result.startsWith("Error")) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Decimal to ASCII Converter",
+ tool_action: "Convert",
+ });
+ }
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx b/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx
index 35d0bab..9920749 100644
--- a/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx
+++ b/app/components/developmentToolsComponent/decimalToBinaryConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseDecimal(input: string): number | null {
const clean = input.trim();
@@ -33,6 +34,18 @@ const DecimalToBinaryConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, groupSize, padTo, autoUpdate, convert]);
+ const onConvertClick = useCallback(() => {
+ convert();
+ if (parseDecimal(input) !== null) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Decimal to Binary Converter",
+ tool_action: "Convert",
+ });
+ }
+ }, [convert, input]);
+
const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
@@ -63,7 +76,7 @@ const DecimalToBinaryConverter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ Convert
Copy
diff --git a/app/components/developmentToolsComponent/decimalToGrayCode.tsx b/app/components/developmentToolsComponent/decimalToGrayCode.tsx
index ac491ad..e50a6e1 100644
--- a/app/components/developmentToolsComponent/decimalToGrayCode.tsx
+++ b/app/components/developmentToolsComponent/decimalToGrayCode.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ConvertOptions = {
showMapping: boolean;
@@ -83,6 +84,12 @@ const DecimalToGrayCode: React.FC = () => {
if (hasInvalid) {
setError("Some inputs were invalid. They are marked as Invalid.");
}
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Decimal to Grey Code",
+ tool_action: "Convert",
+ });
} catch (_e: unknown) {
setError("Failed to convert. Please check your input.");
}
diff --git a/app/components/developmentToolsComponent/decimalToHexConverter.tsx b/app/components/developmentToolsComponent/decimalToHexConverter.tsx
index a4df102..970ba00 100644
--- a/app/components/developmentToolsComponent/decimalToHexConverter.tsx
+++ b/app/components/developmentToolsComponent/decimalToHexConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseDecimal(input: string): number | null {
const clean = input.trim();
@@ -32,7 +33,19 @@ const DecimalToHexConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, uppercase, autoUpdate, convert]);
- const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
+ const onCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(output);
+ if (output) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Decimal to Hex Converter",
+ tool_action: "Copy",
+ });
+ }
+ } catch (_) {}
+ }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/decimalToOctalConverter.tsx b/app/components/developmentToolsComponent/decimalToOctalConverter.tsx
index 5c6df2a..cc739c1 100644
--- a/app/components/developmentToolsComponent/decimalToOctalConverter.tsx
+++ b/app/components/developmentToolsComponent/decimalToOctalConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseDecimal(input: string): number | null {
const clean = input.trim();
@@ -30,6 +31,18 @@ const DecimalToOctalConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, autoUpdate, convert]);
+ const onConvertClick = useCallback(() => {
+ convert();
+ if (parseDecimal(input) !== null) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Decimal to Octal Converter",
+ tool_action: "Convert",
+ });
+ }
+ }, [convert, input]);
+
const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
@@ -60,7 +73,7 @@ const DecimalToOctalConverter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ Convert
Copy
diff --git a/app/components/developmentToolsComponent/epochConverter.tsx b/app/components/developmentToolsComponent/epochConverter.tsx
index af8d025..bbfd506 100644
--- a/app/components/developmentToolsComponent/epochConverter.tsx
+++ b/app/components/developmentToolsComponent/epochConverter.tsx
@@ -7,6 +7,7 @@ import utc from "dayjs/plugin/utc";
import timezone from "dayjs/plugin/timezone";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { ReloadOutlined, DeleteOutlined, CopyOutlined, CheckOutlined } from "@ant-design/icons";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
dayjs.extend(relativeTime);
dayjs.extend(utc);
@@ -88,6 +89,15 @@ const EpochConverter = () => {
}
};
+ const trackCopy = () => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Unix Timestamp Converter",
+ tool_action: "Copy",
+ });
+ };
+
// Derived values for display
const validTimestamp = /^-?\d+$/.test(timestamp);
const currentDayjs = validTimestamp ? dayjs(isMilliseconds ? Number(timestamp) : Number(timestamp) * 1000) : null;
@@ -180,6 +190,7 @@ const EpochConverter = () => {
copyable={{
text: currentDayjs.format("YYYY-MM-DD HH:mm:ss"),
icon: [...copyIconPair],
+ onCopy: trackCopy,
}}
className="text-lg font-mono text-green-400 [&_.ant-typography-copy]:text-primary"
>
@@ -196,6 +207,7 @@ const EpochConverter = () => {
copyable={{
text: `${currentDayjs.utc().format("YYYY-MM-DD HH:mm:ss")} UTC`,
icon: [...copyIconPair],
+ onCopy: trackCopy,
}}
className="text-lg font-mono text-blue-400 [&_.ant-typography-copy]:text-primary"
>
@@ -212,6 +224,7 @@ const EpochConverter = () => {
copyable={{
text: currentDayjs.toISOString(),
icon: [...copyIconPair],
+ onCopy: trackCopy,
}}
className="text-sm font-mono text-yellow-500 break-all [&_.ant-typography-copy]:text-primary"
>
diff --git a/app/components/developmentToolsComponent/excelCompare.tsx b/app/components/developmentToolsComponent/excelCompare.tsx
index 7397451..45e4eea 100644
--- a/app/components/developmentToolsComponent/excelCompare.tsx
+++ b/app/components/developmentToolsComponent/excelCompare.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type CellState = "same" | "diff" | "added" | "removed";
@@ -145,6 +146,12 @@ const ExcelCompare = () => {
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Excel Compare",
+ tool_action: "Download",
+ });
};
return (
diff --git a/app/components/developmentToolsComponent/fibonacciCalculator.tsx b/app/components/developmentToolsComponent/fibonacciCalculator.tsx
index 1f5565b..9a2779c 100644
--- a/app/components/developmentToolsComponent/fibonacciCalculator.tsx
+++ b/app/components/developmentToolsComponent/fibonacciCalculator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Mode = "nth" | "sequence";
@@ -98,6 +99,12 @@ const FibonacciCalculator: React.FC = () => {
} else {
setOutput(computeSequence(n));
}
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Fabonacci Calculator",
+ tool_action: "Convert",
+ });
};
const onCopy = async () => {
diff --git a/app/components/developmentToolsComponent/findAndReplaceString.tsx b/app/components/developmentToolsComponent/findAndReplaceString.tsx
index ca21566..55994a7 100644
--- a/app/components/developmentToolsComponent/findAndReplaceString.tsx
+++ b/app/components/developmentToolsComponent/findAndReplaceString.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Flags = {
global: boolean;
@@ -76,6 +77,7 @@ const FindAndReplaceString: React.FC = () => {
setOutput(res.output);
setCount(res.count);
setError(res.error);
+ return res;
};
useEffect(() => {
@@ -83,6 +85,18 @@ const FindAndReplaceString: React.FC = () => {
run();
}, [input, find, replace, autoUpdate, useRegex, wholeWord, flags]);
+ const handleReplaceClick = () => {
+ const res = run();
+ if (!res.error) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Find and Replace String",
+ tool_action: "Replace",
+ });
+ }
+ };
+
const onCopy = async () => {
try {
await navigator.clipboard.writeText(output);
@@ -227,7 +241,7 @@ const FindAndReplaceString: React.FC = () => {
Replace
diff --git a/app/components/developmentToolsComponent/graphqlFormatter.tsx b/app/components/developmentToolsComponent/graphqlFormatter.tsx
index 5bff0fa..980fa39 100644
--- a/app/components/developmentToolsComponent/graphqlFormatter.tsx
+++ b/app/components/developmentToolsComponent/graphqlFormatter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -254,6 +255,12 @@ const GraphQLFormatter: React.FC = () => {
try {
const formatted = formatGraphQL(input, { indentSize });
setOutput(formatted);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "GraphQL Formatter",
+ tool_action: "Format",
+ });
} catch (e) {
setOutput("");
}
diff --git a/app/components/developmentToolsComponent/greyCodeToDesimal.tsx b/app/components/developmentToolsComponent/greyCodeToDesimal.tsx
index 449538e..abfd693 100644
--- a/app/components/developmentToolsComponent/greyCodeToDesimal.tsx
+++ b/app/components/developmentToolsComponent/greyCodeToDesimal.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ConvertOptions = {
showMapping: boolean; // show "input -> output" instead of raw numbers
@@ -82,6 +83,12 @@ const GreyCodeToDecimal: React.FC = () => {
} else {
setError("");
}
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Grey Code to Decimal",
+ tool_action: "Convert",
+ });
} catch (_e: unknown) {
setError("Failed to convert. Please check your input.");
}
diff --git a/app/components/developmentToolsComponent/harFileViewer.tsx b/app/components/developmentToolsComponent/harFileViewer.tsx
index a68c691..6e3e193 100644
--- a/app/components/developmentToolsComponent/harFileViewer.tsx
+++ b/app/components/developmentToolsComponent/harFileViewer.tsx
@@ -2,6 +2,7 @@
import React, { useMemo, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type HarNameValue = { name: string; value: string };
@@ -197,6 +198,14 @@ const HarFileViewer = () => {
setHar(res.har);
setError(res.error);
setActiveId(-1);
+ if (!res.error) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Har File Viewer",
+ tool_action: "Upload",
+ });
+ }
};
const onPickFile = () => fileInputRef.current?.click();
diff --git a/app/components/developmentToolsComponent/hexToAscii.tsx b/app/components/developmentToolsComponent/hexToAscii.tsx
index 9981448..1e495b7 100644
--- a/app/components/developmentToolsComponent/hexToAscii.tsx
+++ b/app/components/developmentToolsComponent/hexToAscii.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const HexToAscii = () => {
const [hexInput, setHexInput] = useState("");
@@ -49,6 +50,16 @@ const HexToAscii = () => {
setAsciiOutput(result);
};
+ const handleConvertClick = () => {
+ handleHexToAscii();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Hex to ASCII Converter",
+ tool_action: "Convert",
+ });
+ };
+
useEffect(() => {
if (autoUpdateHex) {
handleHexToAscii();
@@ -91,7 +102,7 @@ const HexToAscii = () => {
setAutoUpdateHex(e.target.checked)} />
Auto-update
- Convert
+ Convert
handleCopy(asciiOutput)} className="border border-black/30 px-3 py-1 rounded text-xs sm:text-sm bg-primary hover:bg-primary/90 text-black font-bold" disabled={!asciiOutput}>Copy
diff --git a/app/components/developmentToolsComponent/hexToBinaryConverter.tsx b/app/components/developmentToolsComponent/hexToBinaryConverter.tsx
index 88aaafe..af81392 100644
--- a/app/components/developmentToolsComponent/hexToBinaryConverter.tsx
+++ b/app/components/developmentToolsComponent/hexToBinaryConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseHex(input: string): string | null {
const clean = input.replace(/\s+/g, "").replace(/^0x/i, "");
@@ -38,7 +39,19 @@ const HexToBinaryConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, groupSize, autoUpdate, convert]);
- const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
+ const onCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(output);
+ if (output) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Hex to Binary Converter",
+ tool_action: "Copy",
+ });
+ }
+ } catch (_) {}
+ }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/hexToCmykConverter.tsx b/app/components/developmentToolsComponent/hexToCmykConverter.tsx
index 8ab1eff..fa36d56 100644
--- a/app/components/developmentToolsComponent/hexToCmykConverter.tsx
+++ b/app/components/developmentToolsComponent/hexToCmykConverter.tsx
@@ -2,6 +2,7 @@
import React, { useCallback, useEffect, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseHex(hex: string): { r: number; g: number; b: number } | null {
let h = hex.trim();
@@ -131,6 +132,12 @@ const HexToCmykConverter: React.FC = () => {
const y = Math.round(cmyk.y * 100);
const k = Math.round(cmyk.k * 100);
await navigator.clipboard.writeText(`cmyk(${c}%, ${m}%, ${y}%, ${k}%)`);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HEX to CMYK Converter",
+ tool_action: "Copy",
+ });
} catch (_) {}
}, [cmyk]);
diff --git a/app/components/developmentToolsComponent/hexToPantone.tsx b/app/components/developmentToolsComponent/hexToPantone.tsx
index 589d279..9d4d41c 100644
--- a/app/components/developmentToolsComponent/hexToPantone.tsx
+++ b/app/components/developmentToolsComponent/hexToPantone.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useMemo, useState, useCallback } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Pantone = { name: string; r: number; g: number; b: number };
@@ -56,6 +57,18 @@ const HexToPantone: React.FC = () => {
try { await navigator.clipboard.writeText(hex.toUpperCase()); } catch (_) {}
}, [hex]);
+ const onCopyPantoneName = useCallback(async (name: string) => {
+ try {
+ await navigator.clipboard.writeText(name);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Hex to Pantone",
+ tool_action: "Copy",
+ });
+ } catch (_) {}
+ }, []);
+
return (
@@ -121,7 +134,7 @@ const HexToPantone: React.FC = () => {
{it.pantone.name}
{ try { await navigator.clipboard.writeText(it.pantone.name); } catch (_) {} }}
+ onClick={() => onCopyPantoneName(it.pantone.name)}
className="border border-white/20 px-2 py-0.5 rounded text-[11px] bg-primary text-black font-bold"
>Copy
diff --git a/app/components/developmentToolsComponent/hexToRGBConverter.tsx b/app/components/developmentToolsComponent/hexToRGBConverter.tsx
index 2ef45f3..9e89089 100644
--- a/app/components/developmentToolsComponent/hexToRGBConverter.tsx
+++ b/app/components/developmentToolsComponent/hexToRGBConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ConvertOptions = {
outputCssFunction: boolean; // rgb()/rgba() vs raw numbers
@@ -65,6 +66,12 @@ const HexToRGBConverter: React.FC = () => {
try {
setError("");
setOutput(convertInputToOutput(input, options));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Hex to RGB Converter",
+ tool_action: "Convert",
+ });
} catch (e: unknown) {
setError("Failed to convert. Please check your input.");
}
diff --git a/app/components/developmentToolsComponent/hoursToSecounds.tsx b/app/components/developmentToolsComponent/hoursToSecounds.tsx
index 5f35a01..732a32b 100644
--- a/app/components/developmentToolsComponent/hoursToSecounds.tsx
+++ b/app/components/developmentToolsComponent/hoursToSecounds.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const HoursToSecounds = () => {
const [hours, setHours] = useState
("");
@@ -29,6 +30,12 @@ const HoursToSecounds = () => {
const handleCopy = async () => {
try {
await navigator.clipboard.writeText(String(totalSeconds));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Hours to Seconds",
+ tool_action: "Copy",
+ });
} catch (_) {}
};
diff --git a/app/components/developmentToolsComponent/htmlCodeGenerator.tsx b/app/components/developmentToolsComponent/htmlCodeGenerator.tsx
index 22b6da8..4752628 100644
--- a/app/components/developmentToolsComponent/htmlCodeGenerator.tsx
+++ b/app/components/developmentToolsComponent/htmlCodeGenerator.tsx
@@ -3,6 +3,7 @@ import React, { useState } from "react";
import CopyIcon from "../theme/Icon/copyIcon";
import { CheckIcon } from "../theme/Icon/checkIcon";
import DownOutlinedIcon from "../theme/Icon/downOutlinedIcon";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const HtmlCodeGenerator: React.FC = () => {
const [generated, setGenerated] = useState("");
@@ -62,6 +63,12 @@ const HtmlCodeGenerator: React.FC = () => {
const data = await res.json();
if (data?.html) {
setGenerated(data.html);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Code Generator",
+ tool_action: "Generate",
+ });
} else if (data?.error) {
setGenerated(``);
}
diff --git a/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx b/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx
index 1f2513a..ceedd8c 100644
--- a/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx
+++ b/app/components/developmentToolsComponent/htmlEntitiesToTextConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
// Decode basic named entities and numeric (decimal/hex) references
const decodeEntities = (input: string) => {
@@ -34,7 +35,15 @@ const HtmlEntitiesToTextConverter = () => {
const [input, setInput] = useState("");
const [output, setOutput] = useState("");
- const onConvert = () => setOutput(decodeEntities(input));
+ const onConvert = () => {
+ setOutput(decodeEntities(input));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Entities to Text Converter",
+ tool_action: "Convert",
+ });
+ };
const onCopy = async () => { try { await navigator.clipboard.writeText(output); } catch {} };
const onDownload = () => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
diff --git a/app/components/developmentToolsComponent/htmlEscape.tsx b/app/components/developmentToolsComponent/htmlEscape.tsx
index 4dc8078..d62a474 100644
--- a/app/components/developmentToolsComponent/htmlEscape.tsx
+++ b/app/components/developmentToolsComponent/htmlEscape.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const NAMED_MAP: Record = {
"&": "&",
@@ -44,6 +45,16 @@ const HtmlEscape: React.FC = () => {
run();
}, [input, autoUpdate, encodeQuotes, encodeNonAscii, useNamed]);
+ const handleEscapeClick = () => {
+ run();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Escape",
+ tool_action: "Escape",
+ });
+ };
+
const onCopy = async () => {
try {
await navigator.clipboard.writeText(output);
@@ -136,7 +147,7 @@ const HtmlEscape: React.FC = () => {
Escape
diff --git a/app/components/developmentToolsComponent/htmlMinify.tsx b/app/components/developmentToolsComponent/htmlMinify.tsx
index a9c651c..822ea65 100644
--- a/app/components/developmentToolsComponent/htmlMinify.tsx
+++ b/app/components/developmentToolsComponent/htmlMinify.tsx
@@ -2,6 +2,7 @@
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const minify = (html: string): string => {
return html
@@ -34,6 +35,12 @@ const HtmlMinify = () => {
const src = input || example;
const result = minify(src);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Minify",
+ tool_action: "Minify",
+ });
};
const copy = async () =>
diff --git a/app/components/developmentToolsComponent/htmlPrettify.tsx b/app/components/developmentToolsComponent/htmlPrettify.tsx
index f2683e7..38f9a34 100644
--- a/app/components/developmentToolsComponent/htmlPrettify.tsx
+++ b/app/components/developmentToolsComponent/htmlPrettify.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const prettify = (html: string, indentSize: number): string => {
const indent = (level: number) => " ".repeat(level * indentSize);
@@ -35,6 +36,12 @@ const HtmlPrettify = () => {
const src = input || example;
const result = prettify(src, indentSize);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Prettify",
+ tool_action: "Prettify",
+ });
};
const copy = async () => output && (await navigator.clipboard.writeText(output));
diff --git a/app/components/developmentToolsComponent/htmlTester.tsx b/app/components/developmentToolsComponent/htmlTester.tsx
index c08b628..7db76f2 100644
--- a/app/components/developmentToolsComponent/htmlTester.tsx
+++ b/app/components/developmentToolsComponent/htmlTester.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useState, useEffect } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const Cmd = ({ children }: { children: string }) => (
@@ -137,6 +138,12 @@ const HtmlTester = () => {
const errors = validateHTML(htmlCode);
setValidationErrors(errors);
setIsValidating(false);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Tester",
+ tool_action: "Validate",
+ });
};
const handleClear = () => {
diff --git a/app/components/developmentToolsComponent/htmlToBBCode.tsx b/app/components/developmentToolsComponent/htmlToBBCode.tsx
index 38a3068..7663b2a 100644
--- a/app/components/developmentToolsComponent/htmlToBBCode.tsx
+++ b/app/components/developmentToolsComponent/htmlToBBCode.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -95,7 +96,18 @@ const HtmlToBBCode: React.FC = () => {
const fileRef = useRef(null);
- const convert = useCallback(() => setOutput(htmlToBbcode(input, options)), [input, options]);
+ const convert = useCallback(() => {
+ const result = htmlToBbcode(input, options);
+ setOutput(result);
+ if (result) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML to BBCode",
+ tool_action: "Convert",
+ });
+ }
+ }, [input, options]);
useEffect(() => { if (autoUpdate) convert(); }, [input, options, autoUpdate, convert]);
const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
diff --git a/app/components/developmentToolsComponent/htmlToJade.tsx b/app/components/developmentToolsComponent/htmlToJade.tsx
index 7204547..90e6677 100644
--- a/app/components/developmentToolsComponent/htmlToJade.tsx
+++ b/app/components/developmentToolsComponent/htmlToJade.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -143,7 +144,20 @@ const HtmlToJade: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ {
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML To Jade",
+ tool_action: "Convert",
+ });
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >
+ Convert
+
Copy
diff --git a/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx b/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx
index d6acd61..67cdd5c 100644
--- a/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx
+++ b/app/components/developmentToolsComponent/htmlToMarkDownComponent.tsx
@@ -5,6 +5,7 @@ import { Editor } from "@monaco-editor/react";
import TurndownService from "turndown";
import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast
import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const HtmlToMarkDownComponent = () => {
const [htmlInput, setHtmlInput] = useState("");
@@ -62,6 +63,12 @@ const HtmlToMarkDownComponent = () => {
// Update state with the Markdown output
setMarkdownOutput(markdown);
toast.success("Conversion successful!");
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML To Markdown",
+ tool_action: "Convert",
+ });
};
const removeCss = (html: any) => {
diff --git a/app/components/developmentToolsComponent/htmlUnescape.tsx b/app/components/developmentToolsComponent/htmlUnescape.tsx
index a5f3950..7e62c21 100644
--- a/app/components/developmentToolsComponent/htmlUnescape.tsx
+++ b/app/components/developmentToolsComponent/htmlUnescape.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function unescapeHtml(input: string): string {
// handle numeric decimal, hex, and common named entities
@@ -28,6 +29,16 @@ const HtmlUnescape: React.FC = () => {
run();
}, [input, autoUpdate]);
+ const handleUnescapeClick = () => {
+ run();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Unescape",
+ tool_action: "Unescape",
+ });
+ };
+
const onCopy = async () => {
try {
await navigator.clipboard.writeText(output);
@@ -92,7 +103,7 @@ const HtmlUnescape: React.FC = () => {
Unescape
diff --git a/app/components/developmentToolsComponent/htmlValidator.tsx b/app/components/developmentToolsComponent/htmlValidator.tsx
index 61c403a..ffb8445 100644
--- a/app/components/developmentToolsComponent/htmlValidator.tsx
+++ b/app/components/developmentToolsComponent/htmlValidator.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Issue = { type: "error" | "warning"; message: string; selector?: string };
@@ -65,6 +66,12 @@ const HTMLValidator: React.FC = () => {
const onValidate = () => {
setIssues(runValidation(html));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Validator Online",
+ tool_action: "Validate",
+ });
};
const onUpload = (e: React.ChangeEvent) => {
diff --git a/app/components/developmentToolsComponent/htmlViewer.tsx b/app/components/developmentToolsComponent/htmlViewer.tsx
index 10f552f..7133ca8 100644
--- a/app/components/developmentToolsComponent/htmlViewer.tsx
+++ b/app/components/developmentToolsComponent/htmlViewer.tsx
@@ -2,6 +2,7 @@
import React, { useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const DEFAULT_HTML =
"Hello, HTML Viewer
\nEdit the HTML to see updates here.
";
@@ -39,6 +40,12 @@ const HtmlViewer: React.FC = () => {
const copy = async () => {
try {
await navigator.clipboard.writeText(editorValue);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "HTML Viewer Online",
+ tool_action: "Copy",
+ });
} catch {
// ignore
}
diff --git a/app/components/developmentToolsComponent/idnDecode.tsx b/app/components/developmentToolsComponent/idnDecode.tsx
index da72c22..20c662f 100644
--- a/app/components/developmentToolsComponent/idnDecode.tsx
+++ b/app/components/developmentToolsComponent/idnDecode.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -133,7 +134,19 @@ const IdnDecode: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Decode
+ {
+ if (!input.trim()) return;
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "IDN Decode",
+ tool_action: "Decode",
+ });
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >Decode
Copy
diff --git a/app/components/developmentToolsComponent/idnEncode.tsx b/app/components/developmentToolsComponent/idnEncode.tsx
index d474afe..1dab047 100644
--- a/app/components/developmentToolsComponent/idnEncode.tsx
+++ b/app/components/developmentToolsComponent/idnEncode.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -154,7 +155,19 @@ const IdnEncode: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Encode
+ {
+ if (!input.trim()) return;
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "IDN Encode",
+ tool_action: "Encode",
+ });
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >Encode
Copy
diff --git a/app/components/developmentToolsComponent/imageResizer.tsx b/app/components/developmentToolsComponent/imageResizer.tsx
index b8d35a0..a8116b7 100644
--- a/app/components/developmentToolsComponent/imageResizer.tsx
+++ b/app/components/developmentToolsComponent/imageResizer.tsx
@@ -2,6 +2,7 @@
import React, { useEffect, useMemo, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const clamp = (value: number, min: number, max: number) => {
return Math.min(max, Math.max(min, value));
@@ -178,6 +179,12 @@ const ImageResizer = () => {
const base = fileName ? fileName.replace(/\.[^.]+$/, "") : "image";
a.download = `${base}-${targetWidth}x${targetHeight}.${format === "jpg" ? "jpg" : format}`;
a.click();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Image Resizer",
+ tool_action: "Download",
+ });
};
const targetBytes = useMemo(() => {
@@ -220,6 +227,12 @@ const ImageResizer = () => {
return URL.createObjectURL(finalBlob);
});
setOutputBytes(finalBlob.size);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Image Resizer",
+ tool_action: "Compress",
+ });
} catch {
setError("Failed to optimize file size");
} finally {
diff --git a/app/components/developmentToolsComponent/imageToBase64Converter.tsx b/app/components/developmentToolsComponent/imageToBase64Converter.tsx
index 852df36..ea9e8bf 100644
--- a/app/components/developmentToolsComponent/imageToBase64Converter.tsx
+++ b/app/components/developmentToolsComponent/imageToBase64Converter.tsx
@@ -2,6 +2,7 @@
import React, { useCallback, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const MAX_BYTES = 10 * 1024 * 1024;
@@ -95,6 +96,12 @@ const ImageToBase64Converter: React.FC = () => {
if (!outputText) return;
try {
await navigator.clipboard.writeText(outputText);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Image To Base64 Converter",
+ tool_action: "Copy",
+ });
} catch {
// ignore
}
diff --git a/app/components/developmentToolsComponent/internetSpeedTest.tsx b/app/components/developmentToolsComponent/internetSpeedTest.tsx
index 49eba26..cf47a22 100644
--- a/app/components/developmentToolsComponent/internetSpeedTest.tsx
+++ b/app/components/developmentToolsComponent/internetSpeedTest.tsx
@@ -2,6 +2,7 @@
import React, { useState, useRef, useEffect } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
interface SpeedTestResult {
downloadSpeed: number;
@@ -77,6 +78,12 @@ const InternetSpeedTest = () => {
const testResult = await simulateSpeedTest();
setResult(testResult);
setTestHistory((prev) => [testResult, ...prev.slice(0, 4)]); // Keep last 5 results
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Internet Speed Test",
+ tool_action: "Run Test",
+ });
} catch (err) {
setError("Speed test failed. Please try again.");
} finally {
diff --git a/app/components/developmentToolsComponent/ipToHexConverter.tsx b/app/components/developmentToolsComponent/ipToHexConverter.tsx
index ca32541..6dabfd9 100644
--- a/app/components/developmentToolsComponent/ipToHexConverter.tsx
+++ b/app/components/developmentToolsComponent/ipToHexConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type GroupSeparator = "" | " " | ":" | "-";
@@ -82,7 +83,7 @@ const IpToHexConverter: React.FC = () => {
const fileRef = useRef(null);
- const convert = useCallback(() => {
+ const convert = useCallback((trackOnSuccess = false) => {
const v4 = parseIPv4(input);
const v6 = v4 ? null : parseIPv6(input);
if (!v4 && !v6) {
@@ -95,6 +96,14 @@ const IpToHexConverter: React.FC = () => {
let hex = toHex(bytes, uppercase, separator);
if (prefix0x && separator === "") hex = "0x" + (uppercase ? hex.toUpperCase() : hex.toLowerCase());
setOutput(hex);
+ if (trackOnSuccess) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "IP to Hex Converter",
+ tool_action: "Convert",
+ });
+ }
}, [input, uppercase, separator, prefix0x]);
useEffect(() => { if (autoUpdate) convert(); }, [input, uppercase, separator, prefix0x, autoUpdate, convert]);
@@ -129,7 +138,7 @@ const IpToHexConverter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ convert(true)} className="border border-black/30 px-3 py-1 rounded text-xs sm:text-sm bg-primary hover:bg-primary/90 text-black font-bold">Convert
Copy
diff --git a/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx b/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx
index 7d8fdcc..00849e2 100644
--- a/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx
+++ b/app/components/developmentToolsComponent/ipv4SubnetCalculator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
interface SubnetResults {
networkAddress: string;
@@ -208,6 +209,12 @@ const Ipv4SubnetCalculator: React.FC = () => {
} else {
setCidrInput(String(cidr));
}
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "IPv4 Subnet Calculator",
+ tool_action: "Calculate",
+ });
}
}, [ip, cidrInput, maskInput, inputMode]);
diff --git a/app/components/developmentToolsComponent/javascriptEscape.tsx b/app/components/developmentToolsComponent/javascriptEscape.tsx
index 794588d..5b8b062 100644
--- a/app/components/developmentToolsComponent/javascriptEscape.tsx
+++ b/app/components/developmentToolsComponent/javascriptEscape.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const Cmd = ({ children }: { children: string }) => (
{children}
@@ -48,7 +49,17 @@ const JavaScriptEscape = () => {
return `const s = "${inner}";`;
}, [mode]);
- const copy = async (text: string) => { try { await navigator.clipboard.writeText(text); } catch {} };
+ const copy = async (text: string) => {
+ try {
+ await navigator.clipboard.writeText(text);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JavaScript Escape",
+ tool_action: "Copy",
+ });
+ } catch {}
+ };
const clear = () => setRaw("");
return (
diff --git a/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx b/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx
index 07bef66..8c37b0e 100644
--- a/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx
+++ b/app/components/developmentToolsComponent/javascriptMinifierComponent.tsx
@@ -4,6 +4,7 @@ import MonacoEditor, { Editor } from "@monaco-editor/react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast
import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const JavascriptMinifierComponent = () => {
const [inputJson, setInputJson] = useState("");
@@ -19,6 +20,12 @@ const JavascriptMinifierComponent = () => {
const parsedJson = JSON.parse(inputJson);
setMinifiedJson(JSON.stringify(parsedJson));
toast.success("JSON successfully minified!");
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JavaScript Minifier",
+ tool_action: "Minify",
+ });
} catch (error) {
toast.error("Invalid JSON input. Please fix it and try again.");
}
diff --git a/app/components/developmentToolsComponent/javascriptRegexTester.tsx b/app/components/developmentToolsComponent/javascriptRegexTester.tsx
index 9221b66..9116d22 100644
--- a/app/components/developmentToolsComponent/javascriptRegexTester.tsx
+++ b/app/components/developmentToolsComponent/javascriptRegexTester.tsx
@@ -1,5 +1,6 @@
"use client"
import React, { useMemo, useState } from 'react'
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from '@/app/libs/analytics'
type RegexFlags = {
i: boolean
@@ -79,6 +80,12 @@ const JavascriptRegexTester = () => {
try {
const r = new RegExp(regex.source, flagsString.replace('g',''))
setText(text.replace(r, replaceWith))
+ trackEvent('dev_tool_used', {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: 'JavaScript Regex Tester',
+ tool_action: 'Replace First',
+ })
} catch {}
}
@@ -87,10 +94,24 @@ const JavascriptRegexTester = () => {
try {
const r = new RegExp(regex.source, flagsString.includes('g') ? flagsString : flagsString + 'g')
setText(text.replace(r, replaceWith))
+ trackEvent('dev_tool_used', {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: 'JavaScript Regex Tester',
+ tool_action: 'Replace All',
+ })
} catch {}
}
- const runTest = () => setManualRunKey((k) => k + 1)
+ const runTest = () => {
+ setManualRunKey((k) => k + 1)
+ trackEvent('dev_tool_used', {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: 'JavaScript Regex Tester',
+ tool_action: 'Test Match',
+ })
+ }
const resetAll = () => {
setPattern('')
setText('')
diff --git a/app/components/developmentToolsComponent/javascriptTester.tsx b/app/components/developmentToolsComponent/javascriptTester.tsx
index e7fc28c..5e390b0 100644
--- a/app/components/developmentToolsComponent/javascriptTester.tsx
+++ b/app/components/developmentToolsComponent/javascriptTester.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type LogEntry = { type: "log" | "warn" | "error"; message: string };
@@ -36,6 +37,12 @@ const JavaScriptTester = () => {
setSrcDoc(wrap);
// bump key to ensure fresh execution
setReloadKey((k) => k + 1);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JavaScript Tester",
+ tool_action: "Run",
+ });
};
useEffect(() => {
diff --git a/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx b/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx
index fb2b4a2..58b325f 100644
--- a/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx
+++ b/app/components/developmentToolsComponent/javascriptValidatorLinter.tsx
@@ -1,5 +1,6 @@
"use client";
-import React, { useMemo, useState } from "react";
+import React, { useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IssueType = "error" | "warning";
interface Issue { line: number; column: number; message: string; type: IssueType }
@@ -66,6 +67,29 @@ const JavaScriptValidatorLinter = () => {
const syntaxIssues = useMemo(() => checkSyntax(code), [code]);
const lintIssues = useMemo(() => (autoLint ? basicLint(code) : []), [code, autoLint]);
const hasErrors = syntaxIssues.length > 0;
+ const debounceTimerRef = useRef
| null>(null);
+ const isFirstRenderRef = useRef(true);
+
+ useEffect(() => {
+ if (isFirstRenderRef.current) {
+ isFirstRenderRef.current = false;
+ return;
+ }
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ if (code.trim()) {
+ debounceTimerRef.current = setTimeout(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JavaScript Validator & Linter",
+ tool_action: "Validate",
+ });
+ }, 1000);
+ }
+ return () => {
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ };
+ }, [code]);
return (
diff --git a/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx b/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx
index f7668ae..bd2b1e7 100644
--- a/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx
+++ b/app/components/developmentToolsComponent/jsObfuscatorComponent.tsx
@@ -4,6 +4,7 @@ import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import { Editor } from "@monaco-editor/react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const JsObfuscatorComponent = () => {
const [inputCode, setInputCode] = useState("");
@@ -22,6 +23,12 @@ const JsObfuscatorComponent = () => {
controlFlowFlattening: true,
}).getObfuscatedCode();
setOutputCode(obfuscatedCode);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JS Obfuscator",
+ tool_action: "Obfuscate",
+ });
} catch (error) {
setOutputCode("");
toast.error(`Error obfuscating code: ${(error as Error).message}`);
diff --git a/app/components/developmentToolsComponent/jsonCompare.tsx b/app/components/developmentToolsComponent/jsonCompare.tsx
index 5781819..9a0edfd 100644
--- a/app/components/developmentToolsComponent/jsonCompare.tsx
+++ b/app/components/developmentToolsComponent/jsonCompare.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ChangeType = "added" | "removed" | "modified";
interface ChangeItem { path: string; type: ChangeType; left?: any; right?: any }
@@ -124,6 +125,12 @@ const JSONCompare = () => {
const onCompare = () => {
try {
parseJson(left); parseJson(right); setCompared(true); setError(null);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Compare",
+ tool_action: "Compare",
+ });
} catch (e: any) { setError(e?.message || "Invalid JSON"); setCompared(false); }
};
diff --git a/app/components/developmentToolsComponent/jsonMinifierComponent.tsx b/app/components/developmentToolsComponent/jsonMinifierComponent.tsx
index 5d29c61..43e1b65 100644
--- a/app/components/developmentToolsComponent/jsonMinifierComponent.tsx
+++ b/app/components/developmentToolsComponent/jsonMinifierComponent.tsx
@@ -4,6 +4,7 @@ import { Editor } from "@monaco-editor/react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast
import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const JsonMinifierComponent = () => {
const [inputJson, setInputJson] = useState("");
@@ -39,6 +40,12 @@ const JsonMinifierComponent = () => {
const parsedJson = JSON.parse(inputJson);
setMinifiedJson(JSON.stringify(parsedJson));
toast.success("JSON minified successfully!");
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Minifier",
+ tool_action: "Minify",
+ });
} catch (error) {
toast.error("Invalid JSON input. Please fix it and try again.");
toast.success("");
diff --git a/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx b/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx
index b2ddea3..c5b987b 100644
--- a/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx
+++ b/app/components/developmentToolsComponent/jsonPrittifierComponent.tsx
@@ -5,6 +5,7 @@ import Editor from "@monaco-editor/react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { ToastContainer, toast } from "react-toastify"; // Import ToastContainer and toast
import "react-toastify/dist/ReactToastify.css"; // Import the toast CSS
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const JsonPrettifierComponent = () => {
const [inputJson, setInputJson] = useState("");
@@ -48,6 +49,12 @@ const JsonPrettifierComponent = () => {
const parsedJson = JSON.parse(inputJson);
setPrettifiedJson(JSON.stringify(parsedJson, null, indentation)); // Prettify JSON with selected indentation
toast.success("JSON successfully prettified!");
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Formatter/Prettifier Tool",
+ tool_action: "Prettify",
+ });
} catch (error) {
toast.error("Invalid JSON input. Please fix it and try again.");
}
diff --git a/app/components/developmentToolsComponent/jsonToCsvConverter.tsx b/app/components/developmentToolsComponent/jsonToCsvConverter.tsx
index 53eff7a..83c0553 100644
--- a/app/components/developmentToolsComponent/jsonToCsvConverter.tsx
+++ b/app/components/developmentToolsComponent/jsonToCsvConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
// Flatten nested objects: {user: {name: "Alice"}} => {"user.name": "Alice"}
const flattenObject = (obj: any, prefix = ""): Record
=> {
@@ -88,6 +89,12 @@ const JsonToCsvConverter: React.FC = () => {
}
const csv = jsonToCsv(parsed, { delimiter, includeHeader, flatten });
setCsvOut(csv);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON to CSV Converter",
+ tool_action: "Convert",
+ });
} catch (err) {
setError("Invalid JSON format");
setCsvOut("");
diff --git a/app/components/developmentToolsComponent/jsonToTxt.tsx b/app/components/developmentToolsComponent/jsonToTxt.tsx
index 5f1755c..6188a35 100644
--- a/app/components/developmentToolsComponent/jsonToTxt.tsx
+++ b/app/components/developmentToolsComponent/jsonToTxt.tsx
@@ -2,6 +2,7 @@
import React, { useEffect, useRef, useState } from "react";
import CopyButton from "../ui/CopyButton";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Mode = "json-lines" | "keys" | "values" | "key=value" | "path=value";
@@ -75,13 +76,21 @@ const JsonToTxt: React.FC = () => {
const [uniqueOnly, setUniqueOnly] = useState(false);
const fileRef = useRef(null);
- const convert = () => {
+ const convert = (manual = false) => {
let text = toText(input, mode, pretty);
if (uniqueOnly && !text.startsWith("Error:")) {
const lines = text.split(/\r?\n/);
text = Array.from(new Set(lines)).join("\n");
}
setOutput(text);
+ if (manual && !text.startsWith("Error:")) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON to TXT Online Converter",
+ tool_action: "Convert",
+ });
+ }
};
useEffect(() => {
@@ -178,7 +187,7 @@ const JsonToTxt: React.FC = () => {
convert(true)}
className="px-3 py-1 bg-primary hover:bg-primary/80 rounded text-sm transition-colors text-black font-bold"
>
Convert
diff --git a/app/components/developmentToolsComponent/jsonToTypeScript.tsx b/app/components/developmentToolsComponent/jsonToTypeScript.tsx
index 7a26fc1..afe9a74 100644
--- a/app/components/developmentToolsComponent/jsonToTypeScript.tsx
+++ b/app/components/developmentToolsComponent/jsonToTypeScript.tsx
@@ -3,6 +3,7 @@ import React, { useMemo, useState, useRef } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import ReloadIcon from "../theme/Icon/reload";
import CopyIcon from "../theme/Icon/copyIcon";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const JsonToTypeScript = () => {
const [jsonInput, setJsonInput] = useState("");
@@ -131,6 +132,12 @@ const JsonToTypeScript = () => {
await navigator.clipboard.writeText(typeScriptCode);
setError("TypeScript code copied to clipboard successfully!");
setTimeout(() => setError(""), 3000);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Json to Typescript Converter",
+ tool_action: "Copy",
+ });
} catch (err) {
setError("Failed to copy to clipboard");
}
diff --git a/app/components/developmentToolsComponent/jsonToXmlConverter.tsx b/app/components/developmentToolsComponent/jsonToXmlConverter.tsx
index 23ff3c9..c7bb4d3 100644
--- a/app/components/developmentToolsComponent/jsonToXmlConverter.tsx
+++ b/app/components/developmentToolsComponent/jsonToXmlConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -149,7 +150,21 @@ const JsonToXmlConverter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ {
+ convert();
+ try {
+ JSON.parse(input);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON to XML Converter",
+ tool_action: "Convert",
+ });
+ } catch (_) {}
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >Convert
Copy
diff --git a/app/components/developmentToolsComponent/jsonToYamlConverter.tsx b/app/components/developmentToolsComponent/jsonToYamlConverter.tsx
index 3078074..5affa55 100644
--- a/app/components/developmentToolsComponent/jsonToYamlConverter.tsx
+++ b/app/components/developmentToolsComponent/jsonToYamlConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -118,7 +119,21 @@ const JsonToYamlConverter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ {
+ convert();
+ try {
+ JSON.parse(input);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON to YAML Converter",
+ tool_action: "Convert",
+ });
+ } catch (_) {}
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >Convert
Copy
diff --git a/app/components/developmentToolsComponent/jsonValidator.tsx b/app/components/developmentToolsComponent/jsonValidator.tsx
index f83b21a..5c29bb4 100644
--- a/app/components/developmentToolsComponent/jsonValidator.tsx
+++ b/app/components/developmentToolsComponent/jsonValidator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
interface ValidationResult {
isValid: boolean;
@@ -236,6 +237,12 @@ const JsonValidator = () => {
const results = validateJson(jsonInput);
setValidationResults(results);
setIsLoading(false);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Validator Online",
+ tool_action: "Validate",
+ });
}, 100);
};
@@ -247,6 +254,12 @@ const JsonValidator = () => {
const results = validateAgainstSchema(jsonInput, schemaInput);
setSchemaValidation(results);
setIsLoading(false);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Validator Online",
+ tool_action: "Schema Validate",
+ });
}, 100);
};
@@ -280,6 +293,12 @@ const JsonValidator = () => {
);
setJsonInput(fixed);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Validator Online",
+ tool_action: "Smart Repair",
+ });
};
const handleFileUpload = (event: React.ChangeEvent
) => {
diff --git a/app/components/developmentToolsComponent/jwtDecoder.tsx b/app/components/developmentToolsComponent/jwtDecoder.tsx
index cae8b29..2df9983 100644
--- a/app/components/developmentToolsComponent/jwtDecoder.tsx
+++ b/app/components/developmentToolsComponent/jwtDecoder.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
interface JwtParts {
header: any;
@@ -98,7 +99,19 @@ const JwtDecoder: React.FC = () => {
useEffect(() => { if (autoUpdate) decode(); }, [input, formatJson, showExpiry, autoUpdate, decode]);
- const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
+ const onCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(output);
+ if (output) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JWT Decoder",
+ tool_action: "Copy",
+ });
+ }
+ } catch (_) {}
+ }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/kmToMilesConverter.tsx b/app/components/developmentToolsComponent/kmToMilesConverter.tsx
index 6bb02c7..7138a4a 100644
--- a/app/components/developmentToolsComponent/kmToMilesConverter.tsx
+++ b/app/components/developmentToolsComponent/kmToMilesConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const KM_TO_MI = 1 / 1.609344; // exact inverse
@@ -31,7 +32,19 @@ const KmToMilesConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, precision, autoUpdate, convert]);
- const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
+ const onCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(output);
+ if (output) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Kilometers to Miles Converter",
+ tool_action: "Copy",
+ });
+ }
+ } catch (_) {}
+ }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/lineCounterComponent.tsx b/app/components/developmentToolsComponent/lineCounterComponent.tsx
index c1cf05d..9f479ae 100644
--- a/app/components/developmentToolsComponent/lineCounterComponent.tsx
+++ b/app/components/developmentToolsComponent/lineCounterComponent.tsx
@@ -1,12 +1,32 @@
"use client";
-import React, { useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const LineCounterComponent = () => {
const [text, setText] = useState("");
+ const debounceTimerRef = useRef | null>(null);
+
+ useEffect(() => {
+ return () => {
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ };
+ }, []);
const handleTextChange = (e: any) => {
- setText(e.target.value);
+ const value = e.target.value;
+ setText(value);
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ if (value.trim()) {
+ debounceTimerRef.current = setTimeout(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Line Counter",
+ tool_action: "Count",
+ });
+ }, 1000);
+ }
};
const countLines = () => {
diff --git a/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx b/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx
index dc4ce36..47bac24 100644
--- a/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx
+++ b/app/components/developmentToolsComponent/loremIpsumGeneratorComponent.tsx
@@ -2,6 +2,7 @@
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
import { lorem } from "@/app/libs/constants";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const LoremIpsumGeneratorComponent = () => {
const [count, setCount] = useState(1);
@@ -59,6 +60,12 @@ const LoremIpsumGeneratorComponent = () => {
}
setLoremText(output);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Lorem Ipsum Generator",
+ tool_action: "Generate",
+ });
};
const copyToClipboard = (text: string) => {
diff --git a/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx b/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx
index 64d3abd..f0e96fc 100644
--- a/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx
+++ b/app/components/developmentToolsComponent/lowerCaseConverterComponent.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const LowerCaseConverterComponent = () => {
const [inputText, setInputText] = useState("");
@@ -13,6 +14,12 @@ const LowerCaseConverterComponent = () => {
const transformToLowerCase = () => {
setTransformedText(inputText.toLowerCase());
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Text to Lowercase Converter",
+ tool_action: "Convert",
+ });
};
const copyToClipboard = () => {
diff --git a/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx b/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx
index 68a52ae..0ad976c 100644
--- a/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx
+++ b/app/components/developmentToolsComponent/markDownToHTMLComponent.tsx
@@ -5,6 +5,7 @@ import { Editor } from "@monaco-editor/react";
import { marked } from "marked"; // Import marked.js for Markdown to HTML conversion
import { toast, ToastContainer } from "react-toastify"; // Import toast and ToastContainer
import "react-toastify/dist/ReactToastify.css"; // Import toast styles
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const MarkDownToHtmlComponent = () => {
const [markdownInput, setMarkdownInput] = useState("");
@@ -34,6 +35,12 @@ const MarkDownToHtmlComponent = () => {
const html: any = marked(markdownWithClassesRestored);
setHtmlOutput(html);
toast.success("Conversion successful!"); // Display success toast
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Markdown To HTML",
+ tool_action: "Convert",
+ });
};
const handleClear = () => {
diff --git a/app/components/developmentToolsComponent/markdownFormatter.tsx b/app/components/developmentToolsComponent/markdownFormatter.tsx
index 8b6d5e5..dc71e8b 100644
--- a/app/components/developmentToolsComponent/markdownFormatter.tsx
+++ b/app/components/developmentToolsComponent/markdownFormatter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
// Lightweight markdown formatter: trims trailing spaces, normalizes heading spaces,
// ensures single blank lines between blocks, normalizes list/blockquote/code fences.
@@ -78,6 +79,16 @@ const MarkdownFormatter: React.FC = () => {
);
};
+ const onFormatClick = () => {
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Markdown Formatter",
+ tool_action: "Format",
+ });
+ };
+
useEffect(() => {
if (autoUpdate) convert();
}, [
@@ -154,7 +165,7 @@ const MarkdownFormatter: React.FC = () => {
- Format
+ Format
diff --git a/app/components/developmentToolsComponent/milesToKmConverter.tsx b/app/components/developmentToolsComponent/milesToKmConverter.tsx
index b611d89..f26d6de 100644
--- a/app/components/developmentToolsComponent/milesToKmConverter.tsx
+++ b/app/components/developmentToolsComponent/milesToKmConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const MI_TO_KM = 1.609344; // exact
@@ -31,7 +32,19 @@ const MilesToKmConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, precision, autoUpdate, convert]);
- const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
+ const onCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(output);
+ if (output) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Miles to Kilometers Converter",
+ tool_action: "Copy",
+ });
+ }
+ } catch (_) {}
+ }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/morseCodeTranslator.tsx b/app/components/developmentToolsComponent/morseCodeTranslator.tsx
index 1fa9309..66cac29 100644
--- a/app/components/developmentToolsComponent/morseCodeTranslator.tsx
+++ b/app/components/developmentToolsComponent/morseCodeTranslator.tsx
@@ -1,5 +1,6 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const Cmd = ({ children }: { children: string }) => (
{children}
@@ -94,6 +95,18 @@ const MorseCodeTranslator = () => {
}, [input, mode]);
const copy = async (text: string) => { try { await navigator.clipboard.writeText(text); } catch {} };
+ const copyOutput = async () => {
+ if (!output) return;
+ try {
+ await navigator.clipboard.writeText(output);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Morse Code Translator",
+ tool_action: "Copy",
+ });
+ } catch {}
+ };
const clear = () => setInput("");
const swap = () => {
setMode((m) => (m === "text-to-morse" ? "morse-to-text" : "text-to-morse"));
@@ -182,7 +195,7 @@ const MorseCodeTranslator = () => {
>
)}
- copy(output)} className="px-3 py-1 bg-primary text-black rounded text-sm font-bold hover:bg-primary/80">Copy
+ Copy
diff --git a/app/components/developmentToolsComponent/numbersToWordsConverter.tsx b/app/components/developmentToolsComponent/numbersToWordsConverter.tsx
index 544e49c..d603f3e 100644
--- a/app/components/developmentToolsComponent/numbersToWordsConverter.tsx
+++ b/app/components/developmentToolsComponent/numbersToWordsConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ScaleTuple = [value: number, name: string];
@@ -184,6 +185,14 @@ const NumbersToWordsConverter: React.FC = () => {
capitalize,
});
setOutput(converted);
+ if (input.trim()) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Numbers to Words Converter",
+ tool_action: "Convert",
+ });
+ }
};
const onCopy = async () => {
diff --git a/app/components/developmentToolsComponent/octalToBinaryConverter.tsx b/app/components/developmentToolsComponent/octalToBinaryConverter.tsx
index 774c9a2..1e5b5ff 100644
--- a/app/components/developmentToolsComponent/octalToBinaryConverter.tsx
+++ b/app/components/developmentToolsComponent/octalToBinaryConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseOctal(input: string): string | null {
const clean = input.replace(/\s+/g, "");
@@ -38,7 +39,19 @@ const OctalToBinaryConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, groupSize, autoUpdate, convert]);
- const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
+ const onCopy = useCallback(async () => {
+ try {
+ await navigator.clipboard.writeText(output);
+ if (output) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Octal to Binary Converter",
+ tool_action: "Copy",
+ });
+ }
+ } catch (_) {}
+ }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/octalToDecimalConverter.tsx b/app/components/developmentToolsComponent/octalToDecimalConverter.tsx
index c979d1f..5c71ad9 100644
--- a/app/components/developmentToolsComponent/octalToDecimalConverter.tsx
+++ b/app/components/developmentToolsComponent/octalToDecimalConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function parseOctal(input: string): number | null {
const clean = input.trim();
@@ -27,6 +28,18 @@ const OctalToDecimalConverter: React.FC = () => {
useEffect(() => { if (autoUpdate) convert(); }, [input, autoUpdate, convert]);
+ const onConvertClick = useCallback(() => {
+ convert();
+ if (parseOctal(input) !== null) {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Octal to Decimal Converter",
+ tool_action: "Convert",
+ });
+ }
+ }, [convert, input]);
+
const onCopy = useCallback(async () => { try { await navigator.clipboard.writeText(output); } catch (_) {} }, [output]);
const onDownload = useCallback(() => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
@@ -57,7 +70,7 @@ const OctalToDecimalConverter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Convert
+ Convert
Copy
diff --git a/app/components/developmentToolsComponent/phoneNumberExtractor.tsx b/app/components/developmentToolsComponent/phoneNumberExtractor.tsx
index 5dfc420..fc04d2b 100644
--- a/app/components/developmentToolsComponent/phoneNumberExtractor.tsx
+++ b/app/components/developmentToolsComponent/phoneNumberExtractor.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Format = "raw" | "digits" | "+intl";
type Mode = "phone" | "number";
@@ -41,6 +42,12 @@ const PhoneNumberExtractor: React.FC = () => {
const found = mode === "phone" ? base.map((p) => normalize(p, format)) : base;
const list = uniqueOnly ? Array.from(new Set(found)) : found;
setOutput(list.filter((v) => v.length > 0));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Phone Number Extractor",
+ tool_action: "Extract",
+ });
};
const onCopy = async () => { try { await navigator.clipboard.writeText(output.join("\n")); } catch {} };
diff --git a/app/components/developmentToolsComponent/phpFormatter.tsx b/app/components/developmentToolsComponent/phpFormatter.tsx
index 9aef0e8..6f02ab3 100644
--- a/app/components/developmentToolsComponent/phpFormatter.tsx
+++ b/app/components/developmentToolsComponent/phpFormatter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -178,7 +179,20 @@ const PHPFormatter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Beautify
+ {
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "PHP Formatter",
+ tool_action: "Beautify",
+ });
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >
+ Beautify
+
diff --git a/app/components/developmentToolsComponent/placeholderImageGenerator.tsx b/app/components/developmentToolsComponent/placeholderImageGenerator.tsx
index 1b8f979..4b55015 100644
--- a/app/components/developmentToolsComponent/placeholderImageGenerator.tsx
+++ b/app/components/developmentToolsComponent/placeholderImageGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ImageFormat = "jpg" | "png" | "webp" | "gif";
@@ -95,6 +96,12 @@ const PlaceholderImageGenerator = () => {
const url = generatePlaceholderUrl();
setOutput(url);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Placeholder Image Generator",
+ tool_action: "Generate",
+ });
};
const copyUrl = async () => {
diff --git a/app/components/developmentToolsComponent/pxToRemConverter.tsx b/app/components/developmentToolsComponent/pxToRemConverter.tsx
index 6d1f38b..6aee027 100644
--- a/app/components/developmentToolsComponent/pxToRemConverter.tsx
+++ b/app/components/developmentToolsComponent/pxToRemConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ActiveField = "em" | "px" | "rem" | "percent" | "pt";
@@ -52,6 +53,16 @@ const PxToRemConverter = () => {
setBasePx(!isNaN(num) && num > 0 ? num : 16);
};
+ const handleConvertClick = () => {
+ recalcFrom(active, basePx, active === "px" ? px : active === "em" ? em : active === "rem" ? rem : active === "percent" ? percent : pt);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "PX to REM Converter",
+ tool_action: "Convert",
+ });
+ };
+
const reset = () => {
setBasePx(16);
setEm(1);
@@ -156,7 +167,7 @@ const PxToRemConverter = () => {
recalcFrom(active, basePx, active === "px" ? px : active === "em" ? em : active === "rem" ? rem : active === "percent" ? percent : pt)}
+ onClick={handleConvertClick}
className={`bg-white text-black font-bold py-3 px-6 rounded-lg`}
>
Convert
diff --git a/app/components/developmentToolsComponent/pythonFormatter.tsx b/app/components/developmentToolsComponent/pythonFormatter.tsx
index 05c92b0..d68aa1c 100644
--- a/app/components/developmentToolsComponent/pythonFormatter.tsx
+++ b/app/components/developmentToolsComponent/pythonFormatter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -179,7 +180,20 @@ const PythonFormatter: React.FC = () => {
setAutoUpdate(e.target.checked)} />
Auto-update
- Beautify
+ {
+ convert();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Python Formatter",
+ tool_action: "Beautify",
+ });
+ }}
+ className="bg-primary text-black font-semibold border border-black/30 px-3 py-1 rounded text-xs sm:text-sm"
+ >
+ Beautify
+
diff --git a/app/components/developmentToolsComponent/qrCodeGenerator.tsx b/app/components/developmentToolsComponent/qrCodeGenerator.tsx
index 6489bdd..fc23060 100644
--- a/app/components/developmentToolsComponent/qrCodeGenerator.tsx
+++ b/app/components/developmentToolsComponent/qrCodeGenerator.tsx
@@ -3,6 +3,7 @@ import React, { useState, useRef } from "react";
import { QRCodeCanvas } from "qrcode.react";
import CopyIcon from "../theme/Icon/copyIcon";
import { CheckIcon } from "../theme/Icon/checkIcon";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const QRCodeGenerator = () => {
const [inputText, setInputText] = useState("Hello, World!");
@@ -42,6 +43,12 @@ const QRCodeGenerator = () => {
link.href = canvas.toDataURL("image/png");
link.click();
setIsGenerating(false);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "QR Code Generator",
+ tool_action: "Download",
+ });
}, 500);
};
diff --git a/app/components/developmentToolsComponent/randomAddressGenerator.tsx b/app/components/developmentToolsComponent/randomAddressGenerator.tsx
index 3a14a5e..06df8ee 100644
--- a/app/components/developmentToolsComponent/randomAddressGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomAddressGenerator.tsx
@@ -3,6 +3,7 @@ import React, { useMemo, useState } from "react";
import ReloadIcon from "../theme/Icon/reload";
import CopyIcon from "../theme/Icon/copyIcon";
import { CheckIcon } from "../theme/Icon/checkIcon";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type SupportedCountry = "US" | "UK" | "CA" | "AU" | "IN";
@@ -127,6 +128,12 @@ const RandomAddressGenerator: React.FC = () => {
const a = generateOne();
const full = `${a.buildingNumber} ${a.streetName} ${a.streetType}, ${a.city}, ${a.state} ${a.postalCode}, ${a.country}`;
setAddresses([full]);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Address Generator",
+ tool_action: "Generate",
+ });
};
const copySingle = async (text: string, index: number) => {
diff --git a/app/components/developmentToolsComponent/randomCSVGenerator.tsx b/app/components/developmentToolsComponent/randomCSVGenerator.tsx
index a493681..d223364 100644
--- a/app/components/developmentToolsComponent/randomCSVGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomCSVGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ColumnType =
| "string"
@@ -211,6 +212,16 @@ const RandomCSVGenerator = () => {
const updateColumn = (id: string, patch: Partial
) =>
setColumns((cols) => cols.map((c) => (c.id === id ? { ...c, ...patch } : c)));
+ const handleRegenerate = () => {
+ setRegen((n) => n + 1);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random CSV Generator",
+ tool_action: "Regenerate",
+ });
+ };
+
return (
@@ -309,7 +320,7 @@ const RandomCSVGenerator = () => {
setRegen((n) => n + 1)}
+ onClick={handleRegenerate}
className="px-4 py-2 bg-primary hover:bg-primary text-black rounded-lg text-sm font-bold"
>
Regenerate
diff --git a/app/components/developmentToolsComponent/randomCharacterGenerator.tsx b/app/components/developmentToolsComponent/randomCharacterGenerator.tsx
index a5aa879..04bebbf 100644
--- a/app/components/developmentToolsComponent/randomCharacterGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomCharacterGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type CharacterType = "letters" | "numbers" | "symbols" | "mixed" | "custom";
type CaseType = "lowercase" | "uppercase" | "mixed" | "title";
@@ -89,6 +90,12 @@ const RandomCharacterGenerator = () => {
if (randomChar) {
const result = applyCase(randomChar, caseType);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Character Generator",
+ tool_action: "Generate",
+ });
} else {
setError("No characters available after exclusions");
}
diff --git a/app/components/developmentToolsComponent/randomClockTimeGenerator.tsx b/app/components/developmentToolsComponent/randomClockTimeGenerator.tsx
index 1859703..0dcf995 100644
--- a/app/components/developmentToolsComponent/randomClockTimeGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomClockTimeGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Separator = "newline" | "comma" | "space";
@@ -37,6 +38,12 @@ const RandomClockTimeGenerator = () => {
const items: string[] = [];
for (let i = 0; i < n; i++) items.push(randomTime());
setOutput(items.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Time Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => setOutput("");
diff --git a/app/components/developmentToolsComponent/randomColorGenerator.tsx b/app/components/developmentToolsComponent/randomColorGenerator.tsx
index 5e80f62..7cf3ad7 100644
--- a/app/components/developmentToolsComponent/randomColorGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomColorGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ColorFormat = "hex" | "rgb" | "hsl" | "all";
type ColorType = "any" | "bright" | "pastel" | "dark" | "monochrome";
@@ -195,6 +196,12 @@ const RandomColorGenerator = () => {
const result = colors.join(separatorChar);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Color Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/randomDateGenerator.tsx b/app/components/developmentToolsComponent/randomDateGenerator.tsx
index bdc8ef8..cd835c0 100644
--- a/app/components/developmentToolsComponent/randomDateGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomDateGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState, useRef } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type OutputFormat = "iso" | "locale" | "unix";
@@ -70,6 +71,12 @@ const RandomDateGenerator = () => {
results.push(s);
}
setOutput(results.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Date Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/randomDecimalNumberGenerator.tsx b/app/components/developmentToolsComponent/randomDecimalNumberGenerator.tsx
index 744fa10..6064cbc 100644
--- a/app/components/developmentToolsComponent/randomDecimalNumberGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomDecimalNumberGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Separator = "newline" | "comma" | "space" | "tab";
type DecimalFormat = "standard" | "scientific" | "engineering";
@@ -119,8 +120,14 @@ const RandomDecimalNumberGenerator = () => {
// Format and join numbers
const formattedNumbers = numbers.map((num) => formatDecimal(num, decimalFormat, decimalPlaces));
const result = formattedNumbers.join(separatorChar);
-
+
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Decimal Number Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/randomGUIDGenerator.tsx b/app/components/developmentToolsComponent/randomGUIDGenerator.tsx
index 4d0a5f5..fbf85d6 100644
--- a/app/components/developmentToolsComponent/randomGUIDGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomGUIDGenerator.tsx
@@ -5,6 +5,7 @@ import { useForm } from "react-hook-form";
import CopyIcon from "../theme/Icon/copyIcon";
import ReloadIcon from "../theme/Icon/reload";
import { CheckIcon } from "../theme/Icon/checkIcon";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const RandomGUIDGenerator = () => {
@@ -45,6 +46,12 @@ const RandomGUIDGenerator = () => {
newGUIDs.push(formatGUID(generateGUID()));
}
setGeneratedGUIDs(newGUIDs);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random GUID Generator",
+ tool_action: "Generate",
+ });
};
// Copy to clipboard
diff --git a/app/components/developmentToolsComponent/randomIPGenerator.tsx b/app/components/developmentToolsComponent/randomIPGenerator.tsx
index b2724fb..adaf16b 100644
--- a/app/components/developmentToolsComponent/randomIPGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomIPGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const isPrivateIPv4 = (octets: number[]) => {
const [a, b] = octets;
@@ -69,7 +70,15 @@ const RandomIPGenerator = () => {
return { out, text: out.join(delim) };
}, [version, count, uniqueOnly, includePrivate, includeReserved, delimiter]);
- const copy = () => navigator.clipboard.writeText(list.text);
+ const copy = () => {
+ navigator.clipboard.writeText(list.text);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random IP Generator",
+ tool_action: "Copy",
+ });
+ };
const download = () => {
const blob = new Blob([list.text], { type: "text/plain" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/randomJsonDataGenerator.tsx b/app/components/developmentToolsComponent/randomJsonDataGenerator.tsx
index b56fe14..48f1b13 100644
--- a/app/components/developmentToolsComponent/randomJsonDataGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomJsonDataGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type FieldType =
| "string"
@@ -217,6 +218,12 @@ const RandomJsonDataGenerator = () => {
result.push(obj);
}
setOutput(JSON.stringify(result, null, pretty ? 2 : 0));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "JSON Generator",
+ tool_action: "Generate",
+ });
};
const copyOutput = async () => {
diff --git a/app/components/developmentToolsComponent/randomNumberGenerator.tsx b/app/components/developmentToolsComponent/randomNumberGenerator.tsx
index a1cd765..ff0d942 100644
--- a/app/components/developmentToolsComponent/randomNumberGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomNumberGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type NumberType = "integer" | "decimal" | "percentage" | "currency";
type Separator = "newline" | "comma" | "space" | "tab";
@@ -155,6 +156,12 @@ const RandomNumberGenerator = () => {
const result = formattedNumbers.join(separatorChar);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Number Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/randomParagraphGenerator.tsx b/app/components/developmentToolsComponent/randomParagraphGenerator.tsx
index 30a011f..e3735ab 100644
--- a/app/components/developmentToolsComponent/randomParagraphGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomParagraphGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState, useRef, useCallback } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const CONSONANTS = "bcdfghjklmnpqrstvwxyz".split("");
const VOWELS = "aeiou".split("");
@@ -92,6 +93,12 @@ const RandomParagraphGenerator = () => {
if (!textToCopy) return;
try {
await navigator.clipboard.writeText(textToCopy);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Paragraph Generator",
+ tool_action: "Copy",
+ });
} catch (_) {}
};
diff --git a/app/components/developmentToolsComponent/randomPasswardGenerator.tsx b/app/components/developmentToolsComponent/randomPasswardGenerator.tsx
index 154397d..1b32506 100644
--- a/app/components/developmentToolsComponent/randomPasswardGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomPasswardGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Separator = "newline" | "comma" | "space";
@@ -63,6 +64,12 @@ const RandomPasswardGenerator = () => {
const items: string[] = [];
for (let i = 0; i < n; i++) items.push(buildOne());
setOutput(items.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "undefined",
+ tool_action: "Generate",
+ });
};
const clearAll = () => setOutput("");
diff --git a/app/components/developmentToolsComponent/randomSentanceGenerator.tsx b/app/components/developmentToolsComponent/randomSentanceGenerator.tsx
index 97427b1..30caa2e 100644
--- a/app/components/developmentToolsComponent/randomSentanceGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomSentanceGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState, useRef, useCallback } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Separator = "newline" | "paragraph";
@@ -63,6 +64,12 @@ const RandomSentanceGenerator = () => {
const sentences: string[] = [];
for (let i = 0; i < n; i++) sentences.push(randomSentence(minW, maxW));
setOutput(sentences.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "AI Sentence Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => {
@@ -139,6 +146,12 @@ const RandomSentanceGenerator = () => {
}
setAiOutput(aiText);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "AI Sentence Generator",
+ tool_action: "Generate",
+ });
} catch (err: any) {
if (err.name === 'AbortError') {
setErrorMessage("Request cancelled");
diff --git a/app/components/developmentToolsComponent/randomStringGenerator.tsx b/app/components/developmentToolsComponent/randomStringGenerator.tsx
index ee7f94d..217d621 100644
--- a/app/components/developmentToolsComponent/randomStringGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomStringGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Separator = "newline" | "comma" | "space";
@@ -49,6 +50,12 @@ const RandomStringGenerator = () => {
const items: string[] = [];
for (let i = 0; i < n; i++) items.push(buildOne());
setOutput(items.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random String Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => setOutput("");
diff --git a/app/components/developmentToolsComponent/randomTextFromRegEX.tsx b/app/components/developmentToolsComponent/randomTextFromRegEX.tsx
index 396547b..3c74f1c 100644
--- a/app/components/developmentToolsComponent/randomTextFromRegEX.tsx
+++ b/app/components/developmentToolsComponent/randomTextFromRegEX.tsx
@@ -5,6 +5,7 @@ import { useForm } from "react-hook-form";
import CopyIcon from "../theme/Icon/copyIcon";
import ReloadIcon from "../theme/Icon/reload";
import { CheckIcon } from "../theme/Icon/checkIcon";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const RandomTextFromRegEX = () => {
const { register, formState, setValue, watch } = useForm({});
@@ -166,6 +167,12 @@ const RandomTextFromRegEX = () => {
}
}
setGeneratedTexts(newTexts);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Text From Regex",
+ tool_action: "Generate",
+ });
} catch (error) {
setPatternError("Error generating text from pattern");
}
diff --git a/app/components/developmentToolsComponent/randomUsernameGenerator.tsx b/app/components/developmentToolsComponent/randomUsernameGenerator.tsx
index b3fc718..e055961 100644
--- a/app/components/developmentToolsComponent/randomUsernameGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomUsernameGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const LETTERS = "abcdefghijklmnopqrstuvwxyz";
const NUMBERS = "0123456789";
@@ -225,6 +226,12 @@ const RandomUsernameGenerator = () => {
const n = Math.min(200, Math.max(1, count));
for (let i = 0; i < n; i++) list.push(buildOne());
setOutput(list.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Username Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => setOutput("");
diff --git a/app/components/developmentToolsComponent/randomWordGenerator.tsx b/app/components/developmentToolsComponent/randomWordGenerator.tsx
index ca39565..9f34001 100644
--- a/app/components/developmentToolsComponent/randomWordGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomWordGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Separator = "newline" | "comma" | "space";
@@ -60,6 +61,12 @@ const RandomWordGenerator = () => {
items.push(randomWord(len));
}
setOutput(items.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Word Generator",
+ tool_action: "Generate",
+ });
};
const clearAll = () => setOutput("");
@@ -108,6 +115,12 @@ const RandomWordGenerator = () => {
.filter(Boolean);
setOutput(words.join("\n"));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random Word Generator",
+ tool_action: "Generate",
+ });
} catch (error) {
setError("Failed to generate AI words. Please try again.");
console.error(error);
diff --git a/app/components/developmentToolsComponent/randomXMLGenerator.tsx b/app/components/developmentToolsComponent/randomXMLGenerator.tsx
index a4b1aa0..eb0af63 100644
--- a/app/components/developmentToolsComponent/randomXMLGenerator.tsx
+++ b/app/components/developmentToolsComponent/randomXMLGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type FieldType = "string" | "number" | "boolean" | "date" | "uuid" | "email";
@@ -72,6 +73,12 @@ const RandomXMLGenerator = () => {
}
const xml = items.join("\n\n");
setOutput(pretty ? formatXML(xml) : xml);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Random XML Generator",
+ tool_action: "Generate",
+ });
};
const copyOutput = async () => {
diff --git a/app/components/developmentToolsComponent/remToPxConverter.tsx b/app/components/developmentToolsComponent/remToPxConverter.tsx
index 1c78a72..9256ded 100644
--- a/app/components/developmentToolsComponent/remToPxConverter.tsx
+++ b/app/components/developmentToolsComponent/remToPxConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ActiveField = "rem" | "px" | "em" | "percent" | "pt";
@@ -52,6 +53,16 @@ const RemToPxConverter = () => {
setBasePx(!isNaN(num) && num > 0 ? num : 16);
};
+ const handleConvert = () => {
+ recalcFrom(active, basePx, active === "px" ? px : active === "rem" ? rem : active === "em" ? em : active === "percent" ? percent : pt);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "REM to PX Converter",
+ tool_action: "Convert",
+ });
+ };
+
const reset = () => {
setBasePx(16);
setRem(1);
@@ -156,7 +167,7 @@ const RemToPxConverter = () => {
recalcFrom(active, basePx, active === "px" ? px : active === "rem" ? rem : active === "em" ? em : active === "percent" ? percent : pt)}
+ onClick={handleConvert}
className={`bg-white text-black font-bold py-3 px-6 rounded-lg`}
>
Convert
diff --git a/app/components/developmentToolsComponent/removeSpaces.tsx b/app/components/developmentToolsComponent/removeSpaces.tsx
index 921bc26..ea380b4 100644
--- a/app/components/developmentToolsComponent/removeSpaces.tsx
+++ b/app/components/developmentToolsComponent/removeSpaces.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const removeSpacesConvert = (
text: string,
@@ -126,7 +127,7 @@ const RemoveSpaces: React.FC = () => {
- Convert
+ { doConvert(); trackEvent("dev_tool_used", { page_type: PAGE_TYPE, platform: getRuntimePlatform(), tool_name: "Remove Spaces from Text", tool_action: "Convert" }); }} className="px-3 py-1 bg-primary hover:bg-primary/80 rounded text-sm transition-colors text-black font-bold">Convert
diff --git a/app/components/developmentToolsComponent/reverseTextGenerator.tsx b/app/components/developmentToolsComponent/reverseTextGenerator.tsx
index f8bc473..a67e903 100644
--- a/app/components/developmentToolsComponent/reverseTextGenerator.tsx
+++ b/app/components/developmentToolsComponent/reverseTextGenerator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Mode = "characters" | "words" | "lines";
@@ -46,7 +47,15 @@ const ReverseTextGenerator: React.FC = () => {
sentences: (input.match(/[^.!?]+[.!?]+/g) || (input.trim() ? [input] : [])).length,
}), [input]);
- const onReverse = () => setOutput(reverseByMode(input, mode));
+ const onReverse = () => {
+ setOutput(reverseByMode(input, mode));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Reverse Text Generator",
+ tool_action: "Reverse Text",
+ });
+ };
const onCopy = async () => { try { await navigator.clipboard.writeText(output); } catch {} };
const onDownload = () => {
const blob = new Blob([output], { type: "text/plain;charset=utf-8" });
@@ -94,11 +103,11 @@ const ReverseTextGenerator: React.FC = () => {
{/* Quick Actions */}
⇄ Reverse Text
- setOutput(mirrorWordLetters(input))} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⟫⟫ Mirrored Text
- setOutput(swapCase(input))} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⇄ Letter Flip
- setOutput(input.split(/\s+/).reverse().join(" "))} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⇆ Reverse Wording
- setOutput(toMirroredLetters(input))} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">🄂 Mirrored Letters
- setOutput(toUpsideDown(input))} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⇵ Upside Down
+ { setOutput(mirrorWordLetters(input)); trackEvent("dev_tool_used", { page_type: PAGE_TYPE, platform: getRuntimePlatform(), tool_name: "Reverse Text Generator", tool_action: "Mirrored Text" }); }} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⟫⟫ Mirrored Text
+ { setOutput(swapCase(input)); trackEvent("dev_tool_used", { page_type: PAGE_TYPE, platform: getRuntimePlatform(), tool_name: "Reverse Text Generator", tool_action: "Letter Flip" }); }} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⇄ Letter Flip
+ { setOutput(input.split(/\s+/).reverse().join(" ")); trackEvent("dev_tool_used", { page_type: PAGE_TYPE, platform: getRuntimePlatform(), tool_name: "Reverse Text Generator", tool_action: "Reverse Wording" }); }} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⇆ Reverse Wording
+ { setOutput(toMirroredLetters(input)); trackEvent("dev_tool_used", { page_type: PAGE_TYPE, platform: getRuntimePlatform(), tool_name: "Reverse Text Generator", tool_action: "Mirrored Letters" }); }} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">🄂 Mirrored Letters
+ { setOutput(toUpsideDown(input)); trackEvent("dev_tool_used", { page_type: PAGE_TYPE, platform: getRuntimePlatform(), tool_name: "Reverse Text Generator", tool_action: "Upside Down" }); }} className="px-4 py-2 border border-white/20 rounded-md hover:bg-white/5 text-sm">⇵ Upside Down
{/* Output */}
diff --git a/app/components/developmentToolsComponent/rgbToCmykConverter.tsx b/app/components/developmentToolsComponent/rgbToCmykConverter.tsx
index 1958c6b..31fc3ea 100644
--- a/app/components/developmentToolsComponent/rgbToCmykConverter.tsx
+++ b/app/components/developmentToolsComponent/rgbToCmykConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
// Options removed with simplified UI
@@ -59,7 +60,15 @@ const RgbToCmykConverter: React.FC = () => {
try { await navigator.clipboard.writeText(`rgb(${r}, ${g}, ${b})`); } catch (_) {}
}, [r, g, b]);
const copyCmyk = useCallback(async () => {
- try { await navigator.clipboard.writeText(`cmyk(${cOut}%, ${mOut}%, ${yOut}%, ${kOut}%)`); } catch (_) {}
+ try {
+ await navigator.clipboard.writeText(`cmyk(${cOut}%, ${mOut}%, ${yOut}%, ${kOut}%)`);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "RGB to CMYK Converter",
+ tool_action: "Copy",
+ });
+ } catch (_) {}
}, [cOut, mOut, yOut, kOut]);
return (
diff --git a/app/components/developmentToolsComponent/rgbToHexConverter.tsx b/app/components/developmentToolsComponent/rgbToHexConverter.tsx
index aef2958..2b2d766 100644
--- a/app/components/developmentToolsComponent/rgbToHexConverter.tsx
+++ b/app/components/developmentToolsComponent/rgbToHexConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ConvertOptions = {
includeHash: boolean;
@@ -146,6 +147,12 @@ const RgbToHexConverter: React.FC = () => {
try {
setError("");
setOutput(convertRgbToHex(input, options));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "RGB to Hex Converter",
+ tool_action: "Convert",
+ });
} catch (_e: unknown) {
setError("Failed to convert. Please check your input.");
}
diff --git a/app/components/developmentToolsComponent/rot13EncoderDecoderComponent.tsx b/app/components/developmentToolsComponent/rot13EncoderDecoderComponent.tsx
index 58f1402..a81042d 100644
--- a/app/components/developmentToolsComponent/rot13EncoderDecoderComponent.tsx
+++ b/app/components/developmentToolsComponent/rot13EncoderDecoderComponent.tsx
@@ -2,6 +2,7 @@
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function rot13Transform(text: string): string {
return text.replace(/[A-Za-z]/g, (char) => {
@@ -25,7 +26,15 @@ const Rot13EncoderDecoderComponent: React.FC = () => {
}, [inputText]);
const copyOutput = useCallback(async () => {
- try { await navigator.clipboard.writeText(outputText); } catch (_) {}
+ try {
+ await navigator.clipboard.writeText(outputText);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "ROT13 Encoder/Decoder",
+ tool_action: "Copy",
+ });
+ } catch (_) {}
}, [outputText]);
return (
diff --git a/app/components/developmentToolsComponent/rotateImageTool.tsx b/app/components/developmentToolsComponent/rotateImageTool.tsx
index afc1d44..72f17dd 100644
--- a/app/components/developmentToolsComponent/rotateImageTool.tsx
+++ b/app/components/developmentToolsComponent/rotateImageTool.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const clamp = (value: number, min: number, max: number) => {
return Math.min(max, Math.max(min, value));
@@ -100,6 +101,12 @@ const RotateImageTool = () => {
const ext = downloadFormat === "jpg" ? "jpg" : "png";
a.download = `${fileName ? fileName.replace(/\.[^.]+$/, '') + '-' : ''}rotated.${ext}`;
a.click();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Rotate Image Tool",
+ tool_action: "Download",
+ });
};
const clearAll = () => {
diff --git a/app/components/developmentToolsComponent/rotationCalculatorComponent.tsx b/app/components/developmentToolsComponent/rotationCalculatorComponent.tsx
index 4284f70..7801203 100644
--- a/app/components/developmentToolsComponent/rotationCalculatorComponent.tsx
+++ b/app/components/developmentToolsComponent/rotationCalculatorComponent.tsx
@@ -2,6 +2,7 @@
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type AngleUnit = "deg" | "rad" | "turn" | "grad";
@@ -47,7 +48,15 @@ const RotationCalculatorComponent: React.FC = () => {
}), [resultDeg]);
const copy = useCallback(async (text: string) => {
- try { await navigator.clipboard.writeText(text); } catch (_) {}
+ try {
+ await navigator.clipboard.writeText(text);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Rotation Calculator",
+ tool_action: "Copy",
+ });
+ } catch (_) {}
}, []);
const clear = useCallback(() => {
diff --git a/app/components/developmentToolsComponent/roundingCalculator.tsx b/app/components/developmentToolsComponent/roundingCalculator.tsx
index e4d2c3c..f39c586 100644
--- a/app/components/developmentToolsComponent/roundingCalculator.tsx
+++ b/app/components/developmentToolsComponent/roundingCalculator.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Mode =
| "round-half-up"
@@ -225,7 +226,15 @@ const RoundingCalculator: React.FC = () => {
Input
{
+ calculate();
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Rounding Calculator Online",
+ tool_action: "Calculate",
+ });
+ }}
className="px-3 py-1 bg-primary hover:bg-primary/80 rounded text-sm transition-colors text-black font-bold"
>
Calculate
diff --git a/app/components/developmentToolsComponent/scssToCssConverter.tsx b/app/components/developmentToolsComponent/scssToCssConverter.tsx
index f0fdc21..16eec84 100644
--- a/app/components/developmentToolsComponent/scssToCssConverter.tsx
+++ b/app/components/developmentToolsComponent/scssToCssConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type VarMap = Record;
@@ -111,7 +112,15 @@ const ScssToCssConverter: React.FC = () => {
const [css, setCss] = useState("");
const fileRef = useRef(null);
- const onConvert = () => setCss(transpileScssToCss(scss));
+ const onConvert = () => {
+ setCss(transpileScssToCss(scss));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "SCSS to CSS Converter",
+ tool_action: "Convert",
+ });
+ };
const onCopy = async () => { try { await navigator.clipboard.writeText(css); } catch {} };
const onDownload = () => {
const blob = new Blob([css], { type: "text/css;charset=utf-8" });
diff --git a/app/components/developmentToolsComponent/sentenceCounterComponent.tsx b/app/components/developmentToolsComponent/sentenceCounterComponent.tsx
index 1574f2d..fa60897 100644
--- a/app/components/developmentToolsComponent/sentenceCounterComponent.tsx
+++ b/app/components/developmentToolsComponent/sentenceCounterComponent.tsx
@@ -1,12 +1,32 @@
"use client";
-import React, { useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const SentenceCounterComponent = () => {
const [text, setText] = useState("");
+ const debounceTimerRef = useRef | null>(null);
+
+ useEffect(() => {
+ return () => {
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ };
+ }, []);
const handleTextChange = (e: any) => {
- setText(e.target.value);
+ const value = e.target.value;
+ setText(value);
+ if (debounceTimerRef.current) clearTimeout(debounceTimerRef.current);
+ if (value.trim()) {
+ debounceTimerRef.current = setTimeout(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Sentence Counter",
+ tool_action: "Count",
+ });
+ }, 1000);
+ }
};
const countSentences = () => {
diff --git a/app/components/developmentToolsComponent/shuffleLetters.tsx b/app/components/developmentToolsComponent/shuffleLetters.tsx
index 8892844..4a4a924 100644
--- a/app/components/developmentToolsComponent/shuffleLetters.tsx
+++ b/app/components/developmentToolsComponent/shuffleLetters.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Mode = "letters-in-words" | "all-characters" | "word-order";
@@ -102,7 +103,16 @@ const ShuffleLetters = () => {
const stats = useMemo(() => ({ lines: output ? output.split(/\r?\n/).length : 0, chars: output.length }), [output]);
- const copy = () => navigator.clipboard.writeText(output);
+ const copy = () => {
+ navigator.clipboard.writeText(output).then(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Shuffle Letters",
+ tool_action: "Copy",
+ });
+ });
+ };
const download = () => {
const blob = new Blob([output], { type: "text/plain" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/shuffleTextLines.tsx b/app/components/developmentToolsComponent/shuffleTextLines.tsx
index 1e10c99..59e1abd 100644
--- a/app/components/developmentToolsComponent/shuffleTextLines.tsx
+++ b/app/components/developmentToolsComponent/shuffleTextLines.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
function createSeededRng(seed: string) {
let h = 2166136261 >>> 0;
@@ -58,7 +59,16 @@ const ShuffleTextLines = () => {
const stats = useMemo(() => ({ lines: parsed.length }), [parsed]);
- const copy = () => navigator.clipboard.writeText(output);
+ const copy = () => {
+ navigator.clipboard.writeText(output).then(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Shuffle Text Lines",
+ tool_action: "Copy",
+ });
+ });
+ };
const download = () => {
const blob = new Blob([output], { type: "text/plain" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/sortNumbers.tsx b/app/components/developmentToolsComponent/sortNumbers.tsx
index e9b03e4..780e9e6 100644
--- a/app/components/developmentToolsComponent/sortNumbers.tsx
+++ b/app/components/developmentToolsComponent/sortNumbers.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Order = "asc" | "desc";
@@ -31,6 +32,12 @@ const SortNumbers: React.FC = () => {
if (uniqueOnly) nums = Array.from(new Set(nums));
nums.sort((a, b) => (order === "asc" ? a - b : b - a));
setOutput(nums.join(delimiter === "\\n" ? "\n" : delimiter));
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Sort Numbers Online",
+ tool_action: "Sort",
+ });
};
const onCopy = async () => { try { await navigator.clipboard.writeText(output); } catch {} };
diff --git a/app/components/developmentToolsComponent/sortWords.tsx b/app/components/developmentToolsComponent/sortWords.tsx
index c7dcf8f..e1ba76c 100644
--- a/app/components/developmentToolsComponent/sortWords.tsx
+++ b/app/components/developmentToolsComponent/sortWords.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Order = "asc" | "desc";
type Mode = "alphabetic" | "length" | "numeric";
@@ -55,6 +56,12 @@ const SortWords: React.FC = () => {
const result = words.map((w) => w.raw).join(delimiter === "\\n" ? "\n" : delimiter);
setOutput(result);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Online Alphabetizer: Sort Words Alphabetically",
+ tool_action: "Sort",
+ });
};
const onCopy = async () => { try { await navigator.clipboard.writeText(output); } catch {} };
diff --git a/app/components/developmentToolsComponent/sortingList.tsx b/app/components/developmentToolsComponent/sortingList.tsx
index a3b22c9..e2f28b0 100644
--- a/app/components/developmentToolsComponent/sortingList.tsx
+++ b/app/components/developmentToolsComponent/sortingList.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type Mode = "lex" | "numeric" | "length";
@@ -57,7 +58,16 @@ const SortingList = () => {
const stats = useMemo(() => ({ total: parsed.length, unique: new Set(parsed).size }), [parsed]);
- const copy = () => navigator.clipboard.writeText(output);
+ const copy = () => {
+ navigator.clipboard.writeText(output).then(() => {
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "Sorting List",
+ tool_action: "Copy",
+ });
+ });
+ };
const download = () => {
const blob = new Blob([output], { type: "text/plain" });
const url = URL.createObjectURL(blob);
diff --git a/app/components/developmentToolsComponent/sqlFormatterAndBeautifier.tsx b/app/components/developmentToolsComponent/sqlFormatterAndBeautifier.tsx
index 4ee6584..8c7cc62 100644
--- a/app/components/developmentToolsComponent/sqlFormatterAndBeautifier.tsx
+++ b/app/components/developmentToolsComponent/sqlFormatterAndBeautifier.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type FormatOptions = {
indentSize: number;
@@ -153,6 +154,12 @@ const SqlFormatterAndBeautifier: React.FC = () => {
setError("");
const formatted = formatSqlHeuristic(input, options);
setOutput(formatted);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "SQL Formatter and Beautifier",
+ tool_action: "Format",
+ });
} catch (_e: unknown) {
setError("Failed to format. Please check your input.");
}
diff --git a/app/components/developmentToolsComponent/sqlMinify.tsx b/app/components/developmentToolsComponent/sqlMinify.tsx
index 429e5a1..9c15fdb 100644
--- a/app/components/developmentToolsComponent/sqlMinify.tsx
+++ b/app/components/developmentToolsComponent/sqlMinify.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useMemo, useState } from 'react';
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from '@/app/libs/analytics';
type MinifyOptions = {
removeLineComments: boolean;
@@ -188,6 +189,12 @@ const SqlMinify: React.FC = () => {
setError('');
const result = minifySql(input, options);
setOutput(result);
+ trackEvent('dev_tool_used', {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: 'SQL Minify',
+ tool_action: 'Minify',
+ });
} catch (e: unknown) {
setError('Failed to minify SQL. Please check your input.');
}
diff --git a/app/components/developmentToolsComponent/sqlToCsvConverter.tsx b/app/components/developmentToolsComponent/sqlToCsvConverter.tsx
index db3485c..ee4126d 100644
--- a/app/components/developmentToolsComponent/sqlToCsvConverter.tsx
+++ b/app/components/developmentToolsComponent/sqlToCsvConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type ParseResult = { headers: string[]; rows: string[][] };
@@ -92,7 +93,16 @@ const SqlToCsvConverter = () => {
URL.revokeObjectURL(url);
};
- const copy = async () => csv && (await navigator.clipboard.writeText(csv));
+ const copy = async () => {
+ if (!csv) return;
+ await navigator.clipboard.writeText(csv);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "SQL to CSV Converter",
+ tool_action: "Copy",
+ });
+ };
const clearAll = () => { setSql(""); setError(""); };
return (
diff --git a/app/components/developmentToolsComponent/sqlToJson.tsx b/app/components/developmentToolsComponent/sqlToJson.tsx
index 22be16b..9030a70 100644
--- a/app/components/developmentToolsComponent/sqlToJson.tsx
+++ b/app/components/developmentToolsComponent/sqlToJson.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type IndentSize = 2 | 4;
@@ -168,6 +169,12 @@ const SqlToJson: React.FC = () => {
} else {
setError("");
setOutput(out);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "SQL to JSON",
+ tool_action: "Convert",
+ });
}
}, [input, options]);
diff --git a/app/components/developmentToolsComponent/stringDiffrenceChecker.tsx b/app/components/developmentToolsComponent/stringDiffrenceChecker.tsx
index a32bfbe..592a2cd 100644
--- a/app/components/developmentToolsComponent/stringDiffrenceChecker.tsx
+++ b/app/components/developmentToolsComponent/stringDiffrenceChecker.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useState } from "react";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
type DiffOp = "equal" | "insert" | "delete";
@@ -182,6 +183,12 @@ const StringDiffrenceChecker = () => {
}
setCompareError(null);
setCompared(true);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "String Difference Checker",
+ tool_action: "Compare",
+ });
};
return (
diff --git a/app/components/developmentToolsComponent/stripHTML.tsx b/app/components/developmentToolsComponent/stripHTML.tsx
index 549b07f..f32a661 100644
--- a/app/components/developmentToolsComponent/stripHTML.tsx
+++ b/app/components/developmentToolsComponent/stripHTML.tsx
@@ -1,5 +1,6 @@
"use client"
import React, { useMemo, useState } from 'react'
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from '@/app/libs/analytics'
const StripHTML = () => {
const [html, setHtml] = useState('')
@@ -36,7 +37,16 @@ const StripHTML = () => {
}, [html, preserveLineBreaks, collapseWhitespace, removeComments, dedupeBlankLines, bulletizeListItems])
const copy = async () => {
- try { await navigator.clipboard.writeText(stripped) } catch {}
+ if (!stripped) return
+ try {
+ await navigator.clipboard.writeText(stripped)
+ trackEvent('dev_tool_used', {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: 'Strip HTML',
+ tool_action: 'Copy',
+ })
+ } catch {}
}
const clear = () => setHtml('')
const paste = async () => { try { const t = await navigator.clipboard.readText(); setHtml(t) } catch {} }
diff --git a/app/components/developmentToolsComponent/svgConverter.tsx b/app/components/developmentToolsComponent/svgConverter.tsx
index c9636d4..f6e6f40 100644
--- a/app/components/developmentToolsComponent/svgConverter.tsx
+++ b/app/components/developmentToolsComponent/svgConverter.tsx
@@ -1,6 +1,7 @@
"use client";
import React, { useMemo, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const SvgConverter = () => {
const [svgInput, setSvgInput] = useState("");
@@ -208,6 +209,12 @@ export default SvgIcon;`;
if (!output) return;
try {
await navigator.clipboard.writeText(output);
+ trackEvent("dev_tool_used", {
+ page_type: PAGE_TYPE,
+ platform: getRuntimePlatform(),
+ tool_name: "SVG to React/CSS Utility",
+ tool_action: "Copy",
+ });
} catch (_) {}
};
diff --git a/app/components/developmentToolsComponent/svgViewer.tsx b/app/components/developmentToolsComponent/svgViewer.tsx
index 5284792..ab15cbb 100644
--- a/app/components/developmentToolsComponent/svgViewer.tsx
+++ b/app/components/developmentToolsComponent/svgViewer.tsx
@@ -2,6 +2,7 @@
import React, { useMemo, useRef, useState } from "react";
import DevelopmentToolsStyles from "../../developmentToolsStyles.module.scss";
+import { trackEvent, PAGE_TYPE, getRuntimePlatform } from "@/app/libs/analytics";
const SAMPLE_SVG = `