Skip to content

Commit 4514aee

Browse files
KEN0421claude
andcommitted
feat: +6 tools (86 total, 284 pages) including device-info-checker
User-requested PC info tool + 5 math/text utilities: - text: device-info-checker (browser, OS, CPU, GPU, network, screen) - text: wpm-counter (live typing speed test, color-coded errors) - math: prime-checker (BigInt primality + factorization) - math: quadratic-equation-solver (formula + discriminant + vertex) - math: fraction-calculator (BigInt fraction arithmetic) - math: gcd-lcm-calculator (Euclidean algorithm + factorizations) device-info-checker dumps every public Web API readable from the browser: User-Agent, OS, screen size, devicePixelRatio, CPU threads, device memory, GPU via WebGL, storage quota, network type/speed, language, timezone, dark-mode preference. Useful for bug reports and understanding what websites can see about your device. All 6 tools include full en/ja/zh-CN with 8 FAQ + 3 article sections. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 309f8f5 commit 4514aee

31 files changed

Lines changed: 2220 additions & 0 deletions

File tree

‎site/src/lib/tools/registry.ts‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ import mdToHtml from "@/tools/markdown-to-html";
8080
import htmlToMd from "@/tools/html-to-markdown";
8181
import colorPalette from "@/tools/color-palette-generator";
8282
import angle from "@/tools/angle-converter";
83+
import deviceInfo from "@/tools/device-info-checker";
84+
import primeChecker from "@/tools/prime-checker";
85+
import quadratic from "@/tools/quadratic-equation-solver";
86+
import fraction from "@/tools/fraction-calculator";
87+
import wpm from "@/tools/wpm-counter";
88+
import gcdLcm from "@/tools/gcd-lcm-calculator";
8389

8490
/**
8591
* 全ツールの中央レジストリ。新規ツール追加時はここに登録するだけで
@@ -166,6 +172,12 @@ export const TOOLS: ToolDefinition[] = [
166172
htmlToMd,
167173
colorPalette,
168174
angle,
175+
deviceInfo,
176+
primeChecker,
177+
quadratic,
178+
fraction,
179+
wpm,
180+
gcdLcm,
169181
];
170182

171183
const SLUG_INDEX = new Map(TOOLS.map((t) => [t.meta.slug, t]));
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
"use client";
2+
3+
import { useEffect, useState } from "react";
4+
import { useTranslations } from "next-intl";
5+
6+
interface DeviceInfo {
7+
userAgent: string;
8+
browser: string;
9+
os: string;
10+
platform: string;
11+
language: string;
12+
languages: string[];
13+
timezone: string;
14+
cookiesEnabled: boolean;
15+
online: boolean;
16+
doNotTrack: string;
17+
screenWidth: number;
18+
screenHeight: number;
19+
screenAvailWidth: number;
20+
screenAvailHeight: number;
21+
colorDepth: number;
22+
pixelDepth: number;
23+
devicePixelRatio: number;
24+
windowWidth: number;
25+
windowHeight: number;
26+
hardwareConcurrency: number;
27+
deviceMemory: number | null;
28+
maxTouchPoints: number;
29+
webgl: string;
30+
storageQuota: number | null;
31+
storageUsage: number | null;
32+
connection: { effectiveType: string; downlink: number; rtt: number } | null;
33+
prefersDarkMode: boolean;
34+
prefersReducedMotion: boolean;
35+
pdfViewer: boolean;
36+
webdriver: boolean;
37+
}
38+
39+
declare global {
40+
interface Navigator {
41+
deviceMemory?: number;
42+
connection?: { effectiveType: string; downlink: number; rtt: number };
43+
pdfViewerEnabled?: boolean;
44+
}
45+
}
46+
47+
function detectBrowser(ua: string): string {
48+
if (/Edg\//.test(ua)) return "Microsoft Edge";
49+
if (/OPR\//.test(ua) || /Opera/.test(ua)) return "Opera";
50+
if (/Firefox/.test(ua)) return "Firefox";
51+
if (/Chrome/.test(ua)) return "Chrome";
52+
if (/Safari/.test(ua) && !/Chrome/.test(ua)) return "Safari";
53+
if (/MSIE|Trident/.test(ua)) return "Internet Explorer";
54+
return "Unknown";
55+
}
56+
57+
function detectOS(ua: string): string {
58+
if (/Windows NT 10\.0/.test(ua)) return "Windows 10/11";
59+
if (/Windows NT 6\.3/.test(ua)) return "Windows 8.1";
60+
if (/Windows NT 6\.1/.test(ua)) return "Windows 7";
61+
if (/Windows/.test(ua)) return "Windows";
62+
if (/Mac OS X (\d+[._]\d+)/.test(ua)) {
63+
const m = /Mac OS X (\d+[._]\d+(?:[._]\d+)?)/.exec(ua);
64+
return `macOS ${m ? m[1]!.replace(/_/g, ".") : ""}`;
65+
}
66+
if (/Mac/.test(ua)) return "macOS";
67+
if (/Android (\d+)/.test(ua)) {
68+
const m = /Android (\d+(?:\.\d+)?)/.exec(ua);
69+
return `Android ${m ? m[1] : ""}`;
70+
}
71+
if (/iPhone|iPad|iPod/.test(ua)) {
72+
const m = /OS (\d+[._]\d+)/.exec(ua);
73+
return `iOS/iPadOS ${m ? m[1]!.replace(/_/g, ".") : ""}`;
74+
}
75+
if (/Linux/.test(ua)) return "Linux";
76+
return "Unknown";
77+
}
78+
79+
function detectWebGL(): string {
80+
try {
81+
const canvas = document.createElement("canvas");
82+
const gl = (canvas.getContext("webgl2") ?? canvas.getContext("webgl")) as WebGLRenderingContext | null;
83+
if (!gl) return "Not supported";
84+
const debugInfo = gl.getExtension("WEBGL_debug_renderer_info");
85+
if (debugInfo) {
86+
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
87+
return String(renderer);
88+
}
89+
return gl.getParameter(gl.RENDERER) as string;
90+
} catch {
91+
return "Not available";
92+
}
93+
}
94+
95+
export default function DeviceInfoChecker() {
96+
const t = useTranslations("tools.device-info-checker");
97+
const [info, setInfo] = useState<DeviceInfo | null>(null);
98+
const [copied, setCopied] = useState(false);
99+
100+
useEffect(() => {
101+
async function gather() {
102+
const ua = navigator.userAgent;
103+
let storageQuota: number | null = null;
104+
let storageUsage: number | null = null;
105+
try {
106+
if (navigator.storage?.estimate) {
107+
const est = await navigator.storage.estimate();
108+
storageQuota = est.quota ?? null;
109+
storageUsage = est.usage ?? null;
110+
}
111+
} catch {
112+
// ignore
113+
}
114+
setInfo({
115+
userAgent: ua,
116+
browser: detectBrowser(ua),
117+
os: detectOS(ua),
118+
platform: navigator.platform,
119+
language: navigator.language,
120+
languages: Array.from(navigator.languages),
121+
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
122+
cookiesEnabled: navigator.cookieEnabled,
123+
online: navigator.onLine,
124+
doNotTrack: navigator.doNotTrack ?? "(unset)",
125+
screenWidth: window.screen.width,
126+
screenHeight: window.screen.height,
127+
screenAvailWidth: window.screen.availWidth,
128+
screenAvailHeight: window.screen.availHeight,
129+
colorDepth: window.screen.colorDepth,
130+
pixelDepth: window.screen.pixelDepth,
131+
devicePixelRatio: window.devicePixelRatio,
132+
windowWidth: window.innerWidth,
133+
windowHeight: window.innerHeight,
134+
hardwareConcurrency: navigator.hardwareConcurrency ?? 0,
135+
deviceMemory: navigator.deviceMemory ?? null,
136+
maxTouchPoints: navigator.maxTouchPoints ?? 0,
137+
webgl: detectWebGL(),
138+
storageQuota,
139+
storageUsage,
140+
connection: navigator.connection ? { effectiveType: navigator.connection.effectiveType, downlink: navigator.connection.downlink, rtt: navigator.connection.rtt } : null,
141+
prefersDarkMode: window.matchMedia("(prefers-color-scheme: dark)").matches,
142+
prefersReducedMotion: window.matchMedia("(prefers-reduced-motion: reduce)").matches,
143+
pdfViewer: navigator.pdfViewerEnabled ?? false,
144+
webdriver: navigator.webdriver ?? false,
145+
});
146+
}
147+
gather();
148+
const onResize = () => gather();
149+
window.addEventListener("resize", onResize);
150+
return () => window.removeEventListener("resize", onResize);
151+
}, []);
152+
153+
function fmtBytes(n: number | null): string {
154+
if (n == null) return t("notAvailable");
155+
const units = ["B", "KB", "MB", "GB", "TB"];
156+
let i = 0;
157+
let v = n;
158+
while (v >= 1024 && i < units.length - 1) {
159+
v /= 1024;
160+
i++;
161+
}
162+
return `${v.toFixed(2)} ${units[i]}`;
163+
}
164+
165+
async function copyAll() {
166+
if (!info) return;
167+
const text = JSON.stringify(info, null, 2);
168+
await navigator.clipboard.writeText(text);
169+
setCopied(true);
170+
setTimeout(() => setCopied(false), 1500);
171+
}
172+
173+
if (!info) {
174+
return <div className="text-sm text-slate-500">{t("loading")}</div>;
175+
}
176+
177+
const Row = ({ label, value }: { label: string; value: React.ReactNode }) => (
178+
<div className="flex items-start justify-between gap-3 border-b border-slate-200 py-2 dark:border-slate-800">
179+
<dt className="flex-shrink-0 text-sm text-slate-600 dark:text-slate-400">{label}</dt>
180+
<dd className="text-right font-mono text-sm tabular-nums">{value}</dd>
181+
</div>
182+
);
183+
184+
return (
185+
<div>
186+
<div className="mb-3 flex justify-end">
187+
<button onClick={copyAll} className="rounded bg-brand-600 px-3 py-1.5 text-sm font-medium text-white">
188+
{copied ? t("copied") : t("copyJson")}
189+
</button>
190+
</div>
191+
192+
<h3 className="mt-2 text-sm font-bold uppercase tracking-wide text-slate-500">{t("section.browser")}</h3>
193+
<dl>
194+
<Row label={t("field.browser")} value={info.browser} />
195+
<Row label={t("field.os")} value={info.os} />
196+
<Row label={t("field.platform")} value={info.platform} />
197+
<Row label={t("field.language")} value={info.language} />
198+
<Row label={t("field.languages")} value={info.languages.join(", ")} />
199+
<Row label={t("field.timezone")} value={info.timezone} />
200+
<Row label={t("field.cookiesEnabled")} value={info.cookiesEnabled ? "✓" : "✗"} />
201+
<Row label={t("field.online")} value={info.online ? "✓" : "✗"} />
202+
<Row label={t("field.doNotTrack")} value={info.doNotTrack} />
203+
<Row label={t("field.webdriver")} value={info.webdriver ? t("automation") : t("real")} />
204+
</dl>
205+
206+
<h3 className="mt-6 text-sm font-bold uppercase tracking-wide text-slate-500">{t("section.display")}</h3>
207+
<dl>
208+
<Row label={t("field.screenSize")} value={`${info.screenWidth} × ${info.screenHeight}`} />
209+
<Row label={t("field.availSize")} value={`${info.screenAvailWidth} × ${info.screenAvailHeight}`} />
210+
<Row label={t("field.windowSize")} value={`${info.windowWidth} × ${info.windowHeight}`} />
211+
<Row label={t("field.colorDepth")} value={`${info.colorDepth} bit`} />
212+
<Row label={t("field.pixelRatio")} value={info.devicePixelRatio} />
213+
<Row label={t("field.darkMode")} value={info.prefersDarkMode ? "✓" : "✗"} />
214+
<Row label={t("field.reducedMotion")} value={info.prefersReducedMotion ? "✓" : "✗"} />
215+
</dl>
216+
217+
<h3 className="mt-6 text-sm font-bold uppercase tracking-wide text-slate-500">{t("section.hardware")}</h3>
218+
<dl>
219+
<Row label={t("field.cpuCores")} value={info.hardwareConcurrency} />
220+
<Row label={t("field.deviceMemory")} value={info.deviceMemory != null ? `${info.deviceMemory} GB` : t("notAvailable")} />
221+
<Row label={t("field.touchPoints")} value={info.maxTouchPoints} />
222+
<Row label={t("field.gpu")} value={info.webgl} />
223+
<Row label={t("field.storageQuota")} value={fmtBytes(info.storageQuota)} />
224+
<Row label={t("field.storageUsage")} value={fmtBytes(info.storageUsage)} />
225+
</dl>
226+
227+
{info.connection && (
228+
<>
229+
<h3 className="mt-6 text-sm font-bold uppercase tracking-wide text-slate-500">{t("section.network")}</h3>
230+
<dl>
231+
<Row label={t("field.connectionType")} value={info.connection.effectiveType} />
232+
<Row label={t("field.downlink")} value={`${info.connection.downlink} Mbps`} />
233+
<Row label={t("field.rtt")} value={`${info.connection.rtt} ms`} />
234+
</dl>
235+
</>
236+
)}
237+
238+
<h3 className="mt-6 text-sm font-bold uppercase tracking-wide text-slate-500">{t("section.userAgent")}</h3>
239+
<pre className="overflow-auto rounded bg-slate-100 p-3 font-mono text-xs dark:bg-slate-800">{info.userAgent}</pre>
240+
</div>
241+
);
242+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { ToolDefinition } from "@/lib/tools/types";
2+
import Component from "./Component";
3+
4+
const def: ToolDefinition = {
5+
meta: {
6+
slug: "device-info-checker",
7+
category: "text",
8+
applicationCategory: "DeveloperApplication",
9+
updatedAt: "2026-05-07",
10+
related: ["aspect-ratio-calculator", "color-converter", "url-encoder"],
11+
primaryKeyword: {
12+
en: "device info checker",
13+
ja: "PC 情報 表示",
14+
"zh-CN": "PC 信息 查看",
15+
},
16+
hasHowTo: true,
17+
hasFaq: true,
18+
},
19+
Component,
20+
};
21+
22+
export default def;
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{
2+
"title": "Device Info Checker (browser, OS, hardware, network)",
3+
"shortDescription": "See your device's browser, OS, screen, hardware, GPU, storage, network, and dark-mode preferences in one place. All detected client-side.",
4+
"description": "Browser-side dump of every public Web API the device exposes: user agent, OS, browser version, screen size, devicePixelRatio, hardware concurrency (CPU cores), device memory, GPU info via WebGL, storage quota, network type and speed, language, timezone, dark-mode and reduced-motion preferences.",
5+
"metaDescription": "Free device info checker. Browser, OS, CPU cores, memory, GPU, screen, network, language, timezone — all your device info in a single page.",
6+
"keywords": ["device info checker", "browser detection", "system info", "user agent", "hardware info"],
7+
"loading": "Detecting…",
8+
"copyJson": "Copy JSON",
9+
"copied": "Copied!",
10+
"notAvailable": "(not available)",
11+
"automation": "Automation detected",
12+
"real": "Real browser",
13+
"section": {
14+
"browser": "Browser & OS",
15+
"display": "Display",
16+
"hardware": "Hardware",
17+
"network": "Network",
18+
"userAgent": "User-Agent string"
19+
},
20+
"field": {
21+
"browser": "Browser",
22+
"os": "Operating system",
23+
"platform": "Platform",
24+
"language": "Primary language",
25+
"languages": "All languages",
26+
"timezone": "Timezone",
27+
"cookiesEnabled": "Cookies enabled",
28+
"online": "Online status",
29+
"doNotTrack": "Do Not Track",
30+
"webdriver": "Webdriver",
31+
"screenSize": "Screen size",
32+
"availSize": "Available area",
33+
"windowSize": "Window size",
34+
"colorDepth": "Color depth",
35+
"pixelRatio": "devicePixelRatio",
36+
"darkMode": "Prefers dark mode",
37+
"reducedMotion": "Reduced motion",
38+
"cpuCores": "CPU threads",
39+
"deviceMemory": "Device memory",
40+
"touchPoints": "Touch points",
41+
"gpu": "GPU (WebGL)",
42+
"storageQuota": "Storage quota",
43+
"storageUsage": "Storage used",
44+
"connectionType": "Connection type",
45+
"downlink": "Downlink",
46+
"rtt": "Round-trip time"
47+
},
48+
"article": {
49+
"sections": [
50+
{
51+
"heading": "What's collected",
52+
"paragraphs": [
53+
"Browser & OS: user-agent, parsed browser/OS, language, timezone, cookies, online status. Same info any website you visit can read.",
54+
"Display: screen size, viewport size, color depth, devicePixelRatio (Retina/HiDPI), prefers-color-scheme, prefers-reduced-motion. Used by sites for responsive design and accessibility.",
55+
"Hardware: CPU thread count (navigator.hardwareConcurrency), device memory class (navigator.deviceMemory, in GB, ≥4 reports clipped to 8), max touch points, GPU info from WebGL, storage quota and current usage.",
56+
"Network: connection effective type (slow-2g/2g/3g/4g), downlink (Mbps), and round-trip time (ms) when supported by the browser. Useful for adaptive loading."
57+
]
58+
},
59+
{
60+
"heading": "Privacy: what websites see vs. don't",
61+
"paragraphs": [
62+
"Everything shown here is information any website you visit can already access via standard JavaScript APIs. There's no special permission needed; it's all in the public navigator and screen objects. So this tool is informational, not extractive.",
63+
"What websites do NOT see (and this tool cannot show): your IP address (server-side only, this tool is fully client-side), your local file system, other browser tabs' content, or anything outside the browser sandbox. Browser fingerprinting combines many of these visible fields to identify a unique browser, even without cookies — a privacy concern documented widely."
64+
]
65+
},
66+
{
67+
"heading": "Useful for debugging",
68+
"paragraphs": [
69+
"Mobile vs desktop testing: the user agent reveals what the site sees. Sometimes responsive issues come from misdetected device class.",
70+
"GPU/WebGL: 'GPU (WebGL)' shows your graphics renderer. Useful when complex 3D or canvas-heavy sites perform poorly — your GPU may be the bottleneck.",
71+
"Storage: storage quota tells you how much localStorage/IndexedDB/cache the site can use. Default is typically several GBs on desktop, less on mobile. If a web app is hitting 'quota exceeded', this number is why."
72+
]
73+
}
74+
],
75+
"howTo": [
76+
{ "name": "Open this page", "text": "Detection runs automatically when the page loads." },
77+
{ "name": "Read each section", "text": "Browser & OS, Display, Hardware, Network, User-Agent." },
78+
{ "name": "Resize your window", "text": "Window size and dpi-related fields update live." },
79+
{ "name": "Optional: copy as JSON", "text": "Click 'Copy JSON' to capture the full snapshot for bug reports or comparisons." }
80+
]
81+
},
82+
"faq": [
83+
{ "q": "Does this send my data anywhere?", "a": "No. Everything is detected and rendered locally in your browser. Nothing is logged or transmitted." },
84+
{ "q": "Why are some fields '(not available)'?", "a": "Browsers vary in what APIs they expose. navigator.deviceMemory is Chrome/Edge only; navigator.connection is mostly Chrome; storage quota requires HTTPS. Safari and Firefox restrict more in privacy mode." },
85+
{ "q": "Why does my screen size differ from the expected resolution?", "a": "Browsers report 'CSS pixel' size, not physical pixel size. devicePixelRatio shows the multiplier (e.g., 2.0 on Retina). Physical resolution = screen size × devicePixelRatio." },
86+
{ "q": "What does 'GPU (WebGL)' show?", "a": "Your graphics renderer name as reported by WebGL. Newer browsers may obscure this for fingerprinting protection — you might see 'Apple GPU' instead of 'M2 Pro' on macOS, for example." },
87+
{ "q": "Can I trust the user agent?", "a": "User agent strings can be spoofed by extensions or browser settings. They're a hint, not a guarantee. Some browsers actively reduce UA detail (Safari, Firefox in privacy mode)." },
88+
{ "q": "Why does 'CPU threads' show 8 even though my CPU has 12?", "a": "navigator.hardwareConcurrency may be capped to protect against fingerprinting. Some browsers report a maximum of 8 or 16 even on higher-core systems." },
89+
{ "q": "What if my mobile shows 0 touch points?", "a": "On mobile, maxTouchPoints is normally ≥1. If 0, you may be in a desktop view mode. Try refreshing or switch to mobile view in your browser settings." },
90+
{ "q": "Does this work on iPhone?", "a": "Yes, with Safari iOS limitations. Some fields (deviceMemory, connection) may not be available. The user-agent and basic browser/OS detection always work." }
91+
]
92+
}

0 commit comments

Comments
 (0)