Tech 5 min read

Lab Tool Expansion Plan: Candidate Text Processing Tools

Introduction

I asked Gemini to review lilting.ch/lab and provide a comparison with DevToys.

If DevToys is “a toolbox for programmers,” your site feels like “a workshop for creators (especially for imagery, AI, and web production)”, so the direction is quite different — and interesting.

That’s exactly right. The Lab currently has many image-processing tools, but it’s light on text data processing.

Current tool distribution (31 total):

CategoryCount
Image Conversion4
Image Transform4
Image Editing4
Image Effects3
Viewers6
NLP4
Utility6

For text processing, we do have NLP-leaning items like “Markdown editor,” “morphological analysis,” and “OCR,” but we lack developer-oriented tools such as a JSON formatter and Base64 conversion.

Candidate Tools

I listed candidate additions with priorities.

PriorityToolUse case
HighJSON FormatterPretty-print, validate, minify JSON
HighBase64 ConverterEncode/decode text or images
MediumYAML FormatterFormat/validate YAML; convert with JSON
MediumText DiffShow diff between two texts
MediumURL Encode/DecodeConvert Japanese URLs, etc.
MediumTimestamp ConverterUNIX time ↔ date-time
MediumHTML Entity Converter<&lt; and so on
LowUUID GeneratorRandom ID generation
LowHash GeneratorMD5/SHA256 calculation
LowJWT DecoderInspect authentication tokens
Low.env EditorFormat/validate env files
LowINI EditorFormat/validate INI files

Implementation Specs for Each Tool

JSON Formatter (Priority: High)

Overview:

  • Pretty-print JSON strings
  • Minify (compress)
  • Validation (detect syntax errors)
  • Syntax highlighting

UI:

  • Left: input text area
  • Right: output text area (with highlighting)
  • Top: indent settings (2/4 spaces, tab), pretty/minify buttons
  • On error: show error position and message

Library candidates:

  • Syntax highlighting: Prism.js or custom CSS
  • JSON handling: browser-native JSON.parse() / JSON.stringify() is sufficient

Implementation notes:

  • JSON.stringify(obj, null, indent) for pretty print
  • JSON.stringify(obj) for minify
  • Use try-catch for validation; extract line numbers from error messages

Base64 Converter (Priority: High)

Overview:

  • Text → Base64 encode
  • Base64 → Text decode
  • Image → Base64 (data URI)
  • Base64 → Image preview

UI:

  • Tabs: “Text” and “Image”
  • Text mode: input/output text areas + encode/decode buttons
  • Image mode: drag-and-drop area + Base64 output + preview

Library candidates:

  • Browser APIs: btoa()/atob() (ASCII), TextEncoder/TextDecoder (UTF-8)
  • Image handling: FileReader API

Implementation notes:

  • UTF-8-safe encoding:
    const base64 = btoa(unescape(encodeURIComponent(text)));
  • UTF-8-safe decoding:
    const text = decodeURIComponent(escape(atob(base64)));
  • Data URI for images: data:image/png;base64,${base64}

YAML Formatter (Priority: Medium)

Overview:

  • Format YAML strings
  • Validation (detect syntax errors)
  • JSON ↔ YAML conversion

UI:

  • Left: input text area
  • Right: output text area
  • Top: format button, to-JSON button, to-YAML button
  • On error: show error position and message

Library candidates:

  • js-yaml: the classic YAML parser
  • yaml: a newer option

Implementation notes:

  • With js-yaml:
    import yaml from 'js-yaml';
    // YAML → JSON
    const obj = yaml.load(yamlString);
    const json = JSON.stringify(obj, null, 2);
    // JSON → YAML
    const yamlOutput = yaml.dump(JSON.parse(jsonString));
  • Because YAML indentation is meaningful, expose options for indent width

Text Diff (Priority: Medium)

Overview:

  • Place two texts side-by-side
  • Highlight differences (additions = green, deletions = red)
  • Toggle line-level/char-level
  • Toggle unified/split view

UI:

  • Top: two input text areas (horizontal or vertical)
  • Bottom: diff display area
  • Options: ignore whitespace, case-insensitive

Library candidates:

  • diff: a lightweight diff library
  • monaco-editor: VS Code’s editor (has diff view but heavy)

Implementation notes:

  • Use diffLines() / diffChars() from the diff library
  • Convert results to HTML and highlight
  • Avoid monaco-editor for simple use cases due to bundle size

URL Encode/Decode (Priority: Medium)

Overview:

  • Text → URL-encode
  • URL-encoded text → decode
  • Toggle full-URL vs component encoding

UI:

  • Input text area
  • Output text area
  • Buttons: encode/decode
  • Option: switch between encodeURI and encodeURIComponent

Library candidates:

  • Browser APIs: encodeURI()/decodeURI(), encodeURIComponent()/decodeURIComponent()

Implementation notes:

  • encodeURI: encodes a whole URL (keeps /, ?, &, etc.)
  • encodeURIComponent: encodes parts like query params (converts all special chars)
  • It helps to explain the difference in the UI

Timestamp Converter (Priority: Medium)

Overview:

  • UNIX timestamp → human-readable date-time
  • Date-time → UNIX timestamp
  • Toggle milliseconds/seconds
  • Time zones (UTC/local/custom)

UI:

  • Top: UNIX timestamp input + “Now” button
  • Bottom: date-time display (multiple formats)
  • Reverse: date-time input → timestamp output
  • Time zone dropdown

Library candidates:

  • Browser-native: Date object is enough
  • Formatting: date-fns (lightweight), dayjs (Moment.js alternative)

Implementation notes:

  • Basic conversions:
    // タイムスタンプ → 日時
    const date = new Date(timestamp * 1000); // 秒の場合
    const date = new Date(timestamp);        // ミリ秒の場合
    
    // 日時 → タイムスタンプ
    const timestamp = Math.floor(date.getTime() / 1000); // 秒
    const timestampMs = date.getTime();                   // ミリ秒
  • ISO 8601: date.toISOString()
  • Localized: date.toLocaleString('ja-JP')
  • Auto-detect seconds vs milliseconds by digit length (10 = sec, 13 = ms)

HTML Entity Converter (Priority: Medium)

Overview:

  • HTML special characters → entities (escape)
  • Entities → HTML special characters (unescape)
  • Support numeric refs (&#60;) and named refs (&lt;)

UI:

  • Input text area
  • Output text area
  • Buttons: encode/decode
  • Option: convert all characters vs minimal set

Library candidates:

  • he: go-to for HTML entities
  • Hand-rolled: fine if you only need the basics (<>&"')

Implementation notes:

  • Basic escape:
    function escapeHtml(text) {
      const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
      return text.replace(/[&<>"']/g, c => map[c]);
    }
  • Unescape (via browser API):
    function unescapeHtml(text) {
      const doc = new DOMParser().parseFromString(text, 'text/html');
      return doc.documentElement.textContent;
    }
  • To convert every char to a numeric ref: &#${char.charCodeAt(0)};

UUID Generator (Priority: Low)

Overview:

  • Generate UUID v4 (random)
  • Bulk-generate multiple values
  • Toggle upper/lower case
  • Toggle with/without hyphens

UI:

  • Generate button
  • Count input (1–100)
  • Output text area (with copy button)
  • Options: upper/lower, hyphen on/off

Library candidates:

  • uuid: the standard choice
  • Browser-native: crypto.randomUUID() (modern browsers)

Implementation notes:

  • If crypto.randomUUID() is available, no library needed
  • Remove hyphens: uuid.replace(/-/g, '')
  • Uppercase: uuid.toUpperCase()

Hash Generator (Priority: Low)

Overview:

  • Compute a hash from text
  • Algorithms: MD5, SHA-1, SHA-256, SHA-512
  • Compute from files as well

UI:

  • Input text area or file drop area
  • Algorithm selection (radio or dropdown)
  • Output: hash value (with copy button)

Library candidates:

  • Web Crypto API: crypto.subtle.digest() (SHA family)
  • js-md5: for MD5 (not in Web Crypto)
  • crypto-js: supports multiple algorithms

Implementation notes:

  • Web Crypto example:
    const buffer = new TextEncoder().encode(text);
    const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
  • MD5 is discouraged but often requested, so include it

JWT Decoder (Priority: Low)

Overview:

  • Decode a JWT token and show contents
  • Display header, payload, and signature separately
  • Convert the exp timestamp to a human-readable date
  • Do not verify signatures (no secret key)

UI:

  • Input: JWT token (text area)
  • Output: header (JSON), payload (JSON), signature (Base64)
  • If exp exists, show “expired” or “in N days”

Library candidates:

  • No library needed: Base64 decode is enough

Implementation notes:

  • JWT has the form header.payload.signature (dot-separated)
  • Header and payload use Base64URL encoding
  • Base64URL decode:
    function base64UrlDecode(str) {
      const base64 = str.replace(/-/g, '+').replace(/_/g, '/');
      return decodeURIComponent(escape(atob(base64)));
    }
  • exp is a UNIX timestamp in seconds

.env Editor (Priority: Low)

Overview:

  • Format .env files (KEY=value)
  • Detect duplicate keys
  • Preserve comment lines
  • Normalize quoting of values

UI:

  • Input text area (.env format)
  • Output: table view (key/value list)
  • Options: sort (alphabetical), unify quoting

Library candidates:

  • dotenv: standard but Node.js-oriented
  • Homegrown parser: fine for browsers

Implementation notes:

  • Basic parsing:
    function parseEnv(text) {
      const result = {};
      for (const line of text.split('\n')) {
        if (line.startsWith('#') || !line.includes('=')) continue;
        const [key, ...valueParts] = line.split('=');
        result[key.trim()] = valueParts.join('=').trim();
      }
      return result;
    }
  • Remove quotes: value.replace(/^["']|["']$/g, '')
  • Skip multiline (heredoc-like) values to keep it simple

INI Editor (Priority: Low)

Overview:

  • Format INI files
  • Parse sections ([section]) and key=value pairs
  • Validation (detect syntax errors)
  • Preserve comment lines

UI:

  • Input text area (INI format)
  • Output: tree view (section > key/value)
  • Options: sort, convert to JSON

Library candidates:

  • ini: Node.js-oriented but can be used in browsers
  • Homegrown parser: simple enough for sections and key=value

Implementation notes:

  • Basic parsing:
    function parseIni(text) {
      const result = {};
      let currentSection = '';
      for (const line of text.split('\n')) {
        const trimmed = line.trim();
        if (trimmed.startsWith(';') || trimmed.startsWith('#')) continue;
        if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
          currentSection = trimmed.slice(1, -1);
          result[currentSection] = {};
        } else if (trimmed.includes('=')) {
          const [key, ...valueParts] = trimmed.split('=');
          const target = currentSection ? result[currentSection] : result;
          target[key.trim()] = valueParts.join('=').trim();
        }
      }
      return result;
    }
  • Allow keys without a section (root level)

Implementation Approach

Adding Categories

Current category definition (src/lib/lab-tools.ts):

export type LabToolCategory =
  | 'image-convert'
  | 'image-transform'
  | 'image-edit'
  | 'image-effect'
  | 'viewer'
  | 'nlp'
  | 'utility';

Options to add a dedicated category for text processing:

  • text-process: text processing (JSON, Base64, URL, hashing, etc.)
  • developer: developer tools (broader scope)

Alternatively, include them under the existing utility for now and split later as the number grows.

Tool Registration Steps

  1. Create a new Astro file under src/pages/lab/
  2. Add an entry to the tools array in src/lib/lab-tools.ts
  3. The list view at src/pages/lab/index.astro will pick it up automatically

Shared Components

Since text-processing tools have similar UIs, shared components will help:

  • TextArea: text area with a copy button
  • ConvertButton: convert button
  • OptionToggle: option toggle

That said, I’ll only get around to implementation when I have a bit of time… so probably when the mood strikes.