diff --git a/docs/superpowers/plans/2026-08-14-pdf-favicon-sign.md b/docs/superpowers/plans/2026-08-14-pdf-favicon-sign.md new file mode 100644 index 0000000..6ad1d7e --- /dev/null +++ b/docs/superpowers/plans/2026-08-14-pdf-favicon-sign.md @@ -0,0 +1,20 @@ +# Sign PDF + Organize PDF + Favicon Generator — Plan + +**Date:** 2026-08-14. One branch/PR/promotion. + +## Favicon Generator (Image, icon AppWindow, `favicon-generator`) +Lib `src/tools/image/favicon.lib.ts`: `buildManifest`/`htmlSnippet` (pure, tested); `generateFavicons(file, name)` → ICO (`imageToIco`) + PNGs (`processImage` per size) + manifest + snippet. Island → Dropzone → preview grid → `createZip` + download. + +## Organize PDF (PDF, icon ListOrdered, `pdf-organize`) +Pure `src/tools/pdf/layout.lib.ts`: `pageNumberXY` (tested). `pdf.lib.ts` gains `organizePdf(file, order, pageNumbers?)` — mupdf-normalize → copy pages in `order` into fresh doc → optional `drawText` page numbers. Island: `openPdfRenderer` thumbnails, native HTML5 drag reorder + delete, page-number controls. + +## Sign PDF (PDF, icon FileSignature, `pdf-sign`) +Pure `layout.lib.ts`: `placementToPdfRect` (top-left ratio → bottom-left rect, y-flip; tested). `pdf.lib.ts` gains `signPdf(file, pngBytes, placements)` — embedPng + drawImage per placement. Island: `signature_pad` draw or PNG upload; drag-to-place over `openPdfRenderer` page; multi-page placements → `signPdf`. + +## Notes +- pdf.lib gotcha honored: copy pages into a fresh `PDFDocument.create()` (never draw on the mupdf-loaded doc). +- No new DnD dep — native HTML5 drag for reorder. +- EN + ID SEO for all three (Sign PDF FAQ flags e-signature legality caveat). + +## DoD +libs unit-tested · EN+ID SEO · vitest+lint+build green · 6 new routes · develop→main→live verified. 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} +