Wenlan
Wenlan은 소스 기반 AI 지식베이스·LLM 위키로, 문서·결정을 페이지화해 검색·검토·리뷰 가능
PDF·이미지·텍스트·웹 작업용 무료 온라인 도구로 변환·압축·편집·단어수·텍스트비교·JSON포맷·QR코드 생성이 가능합니다
Free online tools for PDFs, images, text and web work: convert and compress files, edit PDFs, count words, compare text, format JSON and make QR codes.
생산성TypeScript · JavaScriptGitHub @adamkhan4all-alt온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗React + TypeScript + Vite + React Router + Tailwind CSS. No backend yet — processing is mocked so the full UI/UX can be exercised end-to-end.
npm install
npm run dev # dev server
npm run build # production build (tsc -b && vite build)
npm run test # vitest — registry + routing tests
ToolDefinition (src/types/tool.ts)The single data contract every tool conforms to: id, name, slug,
category, description, acceptedFormats, maxFileSize,
processingMode ("CLIENT" | "SERVER"), endpoint, seo metadata
(title, metaDescription, h1, keywords), faq, relatedTools, plus
the fields the UI needs (options, howItWorks, features, etc).
processingMode: "CLIENT" — the tool runs entirely in the browser; endpoint is null.processingMode: "SERVER" — the tool uploads to endpoint for processing.Today compress-image, jpg-to-png, and png-to-jpg are marked CLIENT
(a canvas-based implementation is a natural fit); the rest are SERVER.
Neither mode has real processing wired up yet — both still go through the
same mocked pipeline — but the flag is threaded through the framework
(e.g. PrivacyNotice copy changes based on it) so swapping in the real
implementation later doesn't require touching the framework again.
ToolRegistry (src/lib/toolRegistry.ts)Centralized, validated lookup layer. Built once from
TOOL_DEFINITIONS (src/data/tools.ts) and exported as the toolRegistry
singleton. Throws at module load if two tools share an id or slug, so a
data-entry mistake fails immediately instead of producing a broken route.
API: getAll(), getById(id), getBySlug(slug), getByCategory(category),
getRelated(tool), search(query), has(slug), count().
The ToolRegistry class is also exported directly so tests can build
isolated registries from fixtures without touching the singleton.
src/App.tsx defines one dynamic route, /tools/:slug, which resolves
through src/pages/ToolPage.tsx (ToolPageRoute) — the only place the
router talks to tool data. It looks up the slug in toolRegistry, and
either renders the generic ToolPage framework component with the
resolved ToolDefinition, or redirects to /tools if the slug is unknown.
Adding tool #11 requires exactly one change: append a ToolDefinition
object to TOOL_DEFINITIONS in src/data/tools.ts. No new route, no new
page component, no registry code, no UI code.
src/components/tools/ + src/components/ui/)| Component | Responsibility |
|---|---|
ToolPage | Generic page composition — the only tool-page layout that exists |
ToolHeader | Breadcrumb + H1 + description |
FileDropZone | Drag-and-drop / file-picker upload surface |
FileList | Renders selected files + aggregate size for multi-file tools |
FilePreview | Thumbnail for images, type-glyph badge otherwise |
ToolOptions | Schema-driven select/radio options panel |
ProcessButton | Primary per-tool CTA, sticky on mobile once enabled |
ProcessingState | Uploading (determinate) / Processing (indeterminate) UI |
ProgressIndicator | Determinate + indeterminate progress bar primitives |
ResultCard | Completed-state card (uses DownloadButton) |
DownloadButton | Standalone download CTA |
ErrorState / inline error block | Error-state UI with retry |
PrivacyNotice | Trust copy, adapts to processingMode |
AdSlot | 5 reusable ad placeholder variants |
ToolInstructions | "How it works" numbered steps |
FAQ | Accordion, per-tool Q&A |
RelatedTools | Pulls related tools via toolRegistry.getRelated |
No tool page contains bespoke markup — every tool renders through the same
ToolPage component tree, differing only by the ToolDefinition passed in.
npm run test)src/lib/__tests__/toolRegistry.test.ts — registry contents, lookup by
slug/category, related-tool resolution (including unknown-id handling),
search, SEO/FAQ completeness per tool, and constructor-level duplicate-id/
slug validation.src/__tests__/routing.test.tsx — renders <App> at /, /tools, every
/tools/:slug route (asserting the correct H1 per tool), an unknown tool
slug (redirects to /tools), and an unrelated unknown path (404 page);
also asserts every tool's process button renders.//tools/tools/pdf-to-word/tools/word-to-pdf/tools/jpg-to-pdf/tools/pdf-to-jpg/tools/compress-pdf/tools/merge-pdf/tools/compress-image/tools/image-to-text/tools/jpg-to-png/tools/png-to-jpgThis is a client-rendered SPA (Vite + React Router, createRoot, no SSR
framework), which is fine for Googlebot — it executes JavaScript — but is a
real gap for crawlers/bots that don't (most social link-preview scrapers,
some simpler SEO tools). The SEO layer below has two halves that work
together: a runtime half for real visitors and JS-executing crawlers, and a
build-time prerender for everyone else.
Every one of the 10 tool routes (plus / and /tools) now has, driven
entirely by its own ToolDefinition (src/data/tools.ts — nothing shared/
boilerplate):
여기까지가 README 앞부분입니다. 나머지는 원문에서 읽을 수 있습니다. README 전문 보기 ↗