diff --git a/docs/superpowers/plans/2026-08-14-text-toolkit.md b/docs/superpowers/plans/2026-08-14-text-toolkit.md new file mode 100644 index 0000000..bd3b7e9 --- /dev/null +++ b/docs/superpowers/plans/2026-08-14-text-toolkit.md @@ -0,0 +1,21 @@ +# Basic Text Toolkit — Plan + +**Date:** 2026-08-14 · Category: Dev · Three focused tools sharing one lib. + +Rationale: separate keyword-targeted pages ("word counter" alone is millions of searches/mo) beat one combined page that can only rank for one term. + +## Shared lib `src/tools/dev/text.lib.ts` (pure, unit-tested) +- `countText(text): TextStats` — characters, charactersNoSpaces, words, sentences, paragraphs, lines, readingMinutes (≈200 wpm). +- Case fns + `CASES` list: upper, lower, Title, Sentence, camel, Pascal, snake, kebab, CONSTANT (tokenizer respects camelCase + separators). +- Cleanup fns + `CLEANUP_OPS` + `cleanup(text, keys)`: trimLines, collapseSpaces, removeBlankLines, removeLineBreaks, stripHtml, removeAccents (NFD + strip ̀–ͯ), dedupeLines, sortLines. + +## Tools (islands, thin) +- `word-counter` (icon Baseline) → `WordCounter.tsx`: live stats grid + textarea. +- `case-converter` (icon CaseSensitive) → `CaseConverter.tsx`: input → case buttons → output + copy. +- `text-cleanup` (icon Brush) → `TextCleanup.tsx`: op checkboxes → live cleaned output + copy/download. + +## Registry + SEO +3 ToolDefs in `tools.ts`; EN + ID SEO for each in `tool-seo.ts` (title/description/intro/howTo/faqs). Bahasa "tool" loanword. + +## DoD +lib unit-tested (20 cases) · EN+ID SEO · vitest+lint+build green · 6 new routes built · develop→main→live verified. diff --git a/src/islands/dev/CaseConverter.tsx b/src/islands/dev/CaseConverter.tsx new file mode 100644 index 0000000..eb8c0ac --- /dev/null +++ b/src/islands/dev/CaseConverter.tsx @@ -0,0 +1,56 @@ +import { useState } from 'react'; +import { TextArea } from '@/components/ui/TextArea'; +import { Button } from '@/components/ui/Button'; +import { CopyButton } from '@/components/ui/CopyButton'; +import { CASES } from '@/tools/dev/text.lib'; +import type { Lang } from '@/i18n/config'; + +const TR: Record = { + en: { + intro: 'Convert text between UPPERCASE, lowercase, Title Case, Sentence case, camelCase, snake_case, kebab-case and more. Runs in your browser.', + input: 'Text', + output: 'Result', + placeholder: 'Type or paste your text…', + }, + id: { + intro: 'Ubah teks antara UPPERCASE, lowercase, Title Case, Sentence case, camelCase, snake_case, kebab-case, dan lainnya. Berjalan di browser Anda.', + input: 'Teks', + output: 'Hasil', + placeholder: 'Ketik atau tempel teks Anda…', + }, +}; + +export default function CaseConverter({ lang = 'en' }: { lang?: Lang }) { + const t = TR[lang] ?? TR.en; + const [text, setText] = useState(''); + const [output, setOutput] = useState(''); + + return ( +
+

{t.intro}

+ +
+ {t.input} +