Skip to content

Complete offline rewriting with enhanced detection and UI improvements#8

Merged
jvalentini merged 21 commits into
masterfrom
feat/complete-offline-rewriting
Jan 2, 2026
Merged

Complete offline rewriting with enhanced detection and UI improvements#8
jvalentini merged 21 commits into
masterfrom
feat/complete-offline-rewriting

Conversation

@jvalentini

Copy link
Copy Markdown
Owner

Summary

Complete offline rewriting epic (ws-qmw) with advanced profanity/insult detection, sarcasm support, and redesigned UI layout.

  • Fuzzy matching catches edge cases while avoiding false positives
  • Token-based adjacency rules detect nuanced attack/frustration patterns
  • Sarcasm detection (opt-in, dictionary mode only)
  • Compact right sidebar replaces centered settings
  • Friendlier replacement text (friend/friends instead of person/jerk)
  • Ctrl+Enter auto-scrolls to output

Key Changes

Detection Engine:

  • Constrained fuzzy matching with edit distance ≤1 for high-impact profanity
  • Adjacency rule engine for "you idiot" and "this is ridiculous" patterns
  • Sentence segmentation for clause-level rewrites
  • Protected token handling (URLs, @Handles, #channels)
  • Sarcasm patterns with localStorage persistence

Replacement Logic:

  • All insults/profanity → friend/friends (more lighthearted)
  • Added retard variants to dictionary
  • Removed [removed] fallback, replaced with "friend"

UI/UX:

  • Settings in 240px fixed right sidebar (desktop) / drawer (mobile)
  • Post-it note rotation matching input/output aesthetic
  • API config only shows when AI toggle enabled
  • Improved cursor visibility with red caret
  • Ctrl+Enter scrolls to output section

Testing:

  • 162 tests passing
  • Table-driven test harness for transformations
  • Performance sanity checks for long inputs

Files Changed

  • src/lib/detector.ts: Fuzzy matching, adjacency engine, sentence segmentation
  • src/lib/dictionaries/*: Updated replacement mappings, added sarcasm.ts
  • src/lib/components/SettingsPanel.vue: Redesigned as compact sidebar
  • src/lib/components/App.vue: Grid layout with right sidebar
  • Tests updated for new replacement text

Testing

bun test      # All 162 tests pass
bun run lint  # Clean

Manual testing verified fuzzy matching edge cases, sarcasm detection toggle, mobile drawer, and Ctrl+Enter scroll behavior.

- Implement runCase() helper function for table-driven testing
- Add 5 baseline test cases covering profanity, insults, and phrase replacements
- All tests pass (22 tests total)
- Fulfills ws-qmw.1 acceptance criteria
Extended the masked-text strategy in detector.ts to mask protected tokens
(URLs, @Handles, #channels) so they are never modified by the text replacement
system. Protected tokens are treated as ineligible zones, similar to quoted
email threads and fenced code blocks.

Added comprehensive test coverage for:
- Profanity adjacent to protected tokens
- Profanity inside URL paths
- Multiple protected tokens in the same text
- Both http and https URLs
- Edge cases with profanity-like strings in handles/channels

Closes ws-qmw.3
- Add segmentSentences() function that operates on eligible text only
- Segments on sentence boundaries (. ! ? and newlines)
- Respects excluded zones (quoted lines and fenced code blocks)
- Treats protected tokens (URLs/@handles/#channels) as atomic
- Add comprehensive test coverage for all edge cases
- All 28 tests passing

Closes ws-qmw.4
- Fixed isInExcludedZone check that was incorrectly marking sentences with trailing newlines as excluded
- Added logic to track clause-rewrite-candidate sentences with protected tokens
- These sentences are now skipped entirely (no phrase/word replacements)
- This prevents partial rewrites of sentences that should be clause-rewritten but can't be due to protected tokens
- Fixed test expectation for protected token preservation (should contain @alice)
- Updated detector tests to reflect clause rewrites now having higher priority than phrase matches
- All 90 lib tests passing

Task: ws-qmw.6
- Fixed isInExcludedZone check that was incorrectly marking sentences with trailing newlines as excluded
- Added logic to track clause-rewrite-candidate sentences with protected tokens
- These sentences are now skipped entirely (no phrase/word replacements)
- This prevents partial rewrites of sentences that should be clause-rewritten but can't be due to protected tokens
- Fixed test expectation for protected token preservation (should contain @alice)
- Updated detector tests to reflect clause rewrites now having higher priority than phrase matches
- All 90 lib tests passing

Task: ws-qmw.6
Replaces exact regex patterns with sliding window adjacency analysis for
clause rewrites, increasing recall beyond exact phrase matches.

Core Implementation:
- Tokenization with position tracking (raw, normalized, startIndex, endIndex)
- Sliding window analysis (default 6 tokens) with distance-weighted scoring
- Deterministic score formula: 1/(1+distance)
- Protected token barriers (URLs, @Handles, #channels)
- Excluded zone support (code blocks, quoted lines)

Integration:
- Clause rewrites trigger when adjacency score >= 0.2 threshold
- Falls back to word-level replacements when score < threshold
- Replaces previous exact regex matching for attack/frustration patterns

Testing:
- 8 tokenization unit tests (position tracking, protected tokens, excluded zones)
- 7 adjacency scoring unit tests (distance scoring, barriers, determinism)
- 8 integration tests (clause rewrites via adjacency, window enforcement)
- All 110 detector/replacer tests passing
- No type errors, no lint warnings

Addresses: ws-qmw.7
…atterns

- Implement multi-token sequence matching for passive-aggressive phrases
- Add adjacency detection for 'just checking in', 'per my last email', 'friendly reminder'
- Support obfuscated patterns with extra words/punctuation between tokens
- Skip adjacency detection when exact phrase regex matches
- Add comprehensive tests for adjacency detection with various patterns
- Ensure existing phrase-regex behavior remains intact

Closes ws-qmw.8
Add user-facing toggle for sarcasm detection mode:
- Default OFF, persists to localStorage
- UI toggle in settings panel (minimal design)
- Comprehensive unit tests for persistence
- Only affects dictionary mode (offline processing)

Tests: All 132 tests pass including 4 new persistence tests
Refs: ws-qmw.10
- Add sarcasm dictionary with 15 curated phrases using punctuation cues
- Add 'sarcasm' detection type to Detection interface
- Refactor detectIssues to accept DetectionOptions with sarcasmMode flag
- Implement sarcasm detection that only runs when sarcasmMode is enabled
- Update state management to pass sarcasmMode from global state
- Add comprehensive test suite:
  * Tests with sarcasm OFF (no rewrite)
  * Tests with sarcasm ON (detects and rewrites)
  * 4+ negative tests (genuine praise not modified)
  * Excluded zones and protected tokens respected
- Clean up debug test files
- All tests pass (149/149)

Closes ws-qmw.11
- Add 5 performance tests for 10K+ character inputs
- Test scenarios: mixed content, protected tokens, excluded zones, adjacency rules, pathological nested zones
- All tests complete within 5 seconds
- Verify all new behaviors covered: excluded zones, protected tokens, clause rewrites, adjacency
- All toolchain checks pass: 154 tests, 0 type errors, 0 lint warnings

Closes ws-qmw.12
- Implement Levenshtein distance algorithm for edit-distance matching
- Add feature flag ENABLE_FUZZY_MATCHING with configurable MAX_EDIT_DISTANCE
- Create curated list of high-impact terms for fuzzy matching
- Add comprehensive safe word blacklist to prevent false positives
- Constrain matching with minimum length (3 chars) and max length diff (1 char)
- Add 8 comprehensive tests (3 positive typo detection, 5 negative safe word protection)
- All tests passing (36/36)

Resolves: ws-qmw.9
- Add minimum length requirement (3 chars) for substring matching
- Add common words to safe list: hello, help, sure, yeah
- Prevents single/short words from matching profanity substrings
- All 162 tests now passing

Closes ws-qmw
All 162 tests passing. Fuzzy matching false positives fixed.
Dictionary mode remains 100% offline and deterministic.
- Move settings panel above input section for better workflow
- Add Ctrl+Enter auto-scroll to output section
- Make textarea cursor more visible with red caret color
- Replace person/jerk with friend/friends in all dictionaries
- Add retard variants to insults dictionary
- Replace [removed] fallback with friend
- Move settings to 240px fixed sidebar on right with grid layout
- Reduce whitespace with compact padding and spacing
- Add post-it note rotation (0.8deg) to match theme
- Show API config only when Lundberg AI toggle is enabled
- Implement mobile drawer that slides from right
- Reverse green output post-it rotation to -1.5deg
- Reposition TPS Report flair to left of output section
- Remove unused _targetTerm parameter from isSubstringOfSafeWord
- Add safe localStorage wrappers with proper error logging
- Update README with new UI features and sarcasm detection

All tests pass, linting clean, type checking successful.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jan 2, 2026

Copy link
Copy Markdown

Deploying worksafe with  Cloudflare Pages  Cloudflare Pages

Latest commit: 35d9bbf
Status:⚡️  Build in progress...

View logs

@jvalentini
jvalentini merged commit ce323c8 into master Jan 2, 2026
4 of 5 checks passed
@jvalentini
jvalentini deleted the feat/complete-offline-rewriting branch January 2, 2026 19:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant