Skip to content

Add model selection and customizable system prompt to omit-needless-words#130

Merged
simonw merged 6 commits intomainfrom
claude/model-selection-custom-prompts-01YWf3qZzsnnQ7orv94b3iah
Nov 30, 2025
Merged

Add model selection and customizable system prompt to omit-needless-words#130
simonw merged 6 commits intomainfrom
claude/model-selection-custom-prompts-01YWf3qZzsnnQ7orv94b3iah

Conversation

@simonw
Copy link
Owner

@simonw simonw commented Nov 30, 2025

  • Add model selector dropdown defaulting to Haiku 4.5, with options for
    Sonnet 4.5 and Opus 4.5
  • Add collapsible system prompt editor in a details/summary element
  • Save custom prompts to localStorage with buttons to save and reset
  • Show visible banner when using a custom prompt with toggle button
    to switch back to default

Modify omit needless words to include a select topics the model - default to haiku (find the model string in the commit history) but also allows opus 4.5 or sonnet 4.5
Make the system prompt editable - put that in a details/summary that defaults to closed
Save custom prompts in local storage - if one is set show a visible note that it is running with a custom prompt and provide a button to toggle between default and custom and back again

…ords

- Add model selector dropdown defaulting to Haiku 4.5, with options for
  Sonnet 4.5 and Opus 4.5
- Add collapsible system prompt editor in a details/summary element
- Save custom prompts to localStorage with buttons to save and reset
- Show visible banner when using a custom prompt with toggle button
  to switch back to default
@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Nov 30, 2025

Deploying tools with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4508f1d
Status:⚡️  Build in progress...

View logs

@claude
Copy link
Contributor

claude bot commented Nov 30, 2025

Claude finished @simonw's task —— View job


Code Review Complete ✅

Reviewing PR #130: Add model selection and customizable system prompt to omit-needless-words

Review Tasks:

  • Read and understand the repository context (CLAUDE.md if exists)
  • Read the modified omit-needless-words.html file to understand the changes
  • Analyze code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Review security concerns
  • Assess test coverage needs
  • Provide comprehensive review feedback via GitHub comment

Code Review Summary

This PR successfully adds model selection and customizable system prompt functionality to the omit-needless-words tool. The implementation is solid overall, but there are several areas for improvement:

Strengths

  1. Clean UI Implementation: Well-structured HTML with good semantic markup and responsive CSS
  2. Proper State Management: Smart use of localStorage for persisting custom prompts and user preferences
  3. User Experience: Intuitive collapsible interface with clear visual indicators (banner for custom prompts)
  4. Code Organization: JavaScript is well-structured with clear separation of concerns

⚠️ Security Concerns

  1. XSS Vulnerability (omit-needless-words.html:853-854): The text sanitization is insufficient:

    const sanitized = text
      .replace(/<(?!\/?del\b)[^>]*>/g, '') // Remove all tags except <del> and </del>
      .replace(/\n/g, '<br>'); // Convert newlines to <br>

    This regex can be bypassed with malformed HTML. Consider using DOMPurify or a more robust sanitization approach.

  2. Unsafe innerHTML Usage (omit-needless-words.html:856): Direct innerHTML assignment with user-controlled content:

    resultBox.innerHTML = sanitized;

    Even with sanitization, this is risky. Consider using textContent with manual <del> tag parsing.

🐛 Potential Bugs

  1. Missing Error Handling (omit-needless-words.html:594-601): No validation when saving custom prompts:

    if (promptText) {
      localStorage.setItem('omit-needless-words-custom-prompt', promptText);
    }

    Should handle localStorage quota exceeded errors.

  2. Inconsistent State (omit-needless-words.html:607-615): The toggle button only handles switching from custom to default, but doesn't restore custom when clicked again.

  3. Default Model Hardcoding (omit-needless-words.html:454): No persistence of model selection across sessions.

🚀 Performance Considerations

  1. DOM Updates (omit-needless-words.html:850-857): The renderText function is called for every text delta, causing frequent DOM reflows. Consider debouncing for better performance.

  2. Memory Usage: Debug logging accumulates indefinitely until manually cleared - could cause memory leaks in long sessions.

📝 Code Quality Improvements

  1. Magic Strings (omit-needless-words.html:564-565): Inconsistent localStorage key naming. Consider constants:

    const STORAGE_KEYS = {
      CUSTOM_PROMPT: 'omit-needless-words-custom-prompt',
      USE_CUSTOM: 'omit-needless-words-use-custom'
    };
  2. Error Messages: Generic error handling could be more specific about different failure modes.

  3. Code Duplication: Banner show/hide logic is repeated in multiple places.

🧪 Test Coverage

  • Missing Tests: No automated testing for the new functionality
  • Suggested Test Areas:
    • Custom prompt save/restore logic
    • Model selection persistence
    • XSS protection in text rendering
    • localStorage error handling
    • UI state management

🔧 Recommendations

  1. High Priority: Fix XSS vulnerability with proper sanitization library
  2. Medium Priority: Add error handling for localStorage operations
  3. Medium Priority: Fix toggle button behavior for consistent UX
  4. Low Priority: Add model selection persistence
  5. Low Priority: Consider adding basic unit tests for core functions

📊 Overall Assessment

The PR successfully implements the requested features with good UX design. The main concern is the security vulnerability that should be addressed before merging. The code is otherwise well-structured and maintainable.

Recommendation: Request changes to address the XSS vulnerability, then approve.


@claude
Copy link
Contributor

claude bot commented Nov 30, 2025

Claude finished @simonw's task —— View job


Code Review Complete ✅

Reviewing PR #130: Add model selection and customizable system prompt to omit-needless-words

Review Tasks:

  • Read and understand the repository context (CLAUDE.md if exists)
  • Read the modified omit-needless-words.html file to understand the changes
  • Analyze code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Review security concerns
  • Assess test coverage needs
  • Provide comprehensive review feedback via GitHub comment

Code Review Summary

This PR successfully adds model selection and customizable system prompt functionality to the omit-needless-words tool. The implementation is solid overall, but there are several areas for improvement:

Strengths

  1. Clean UI Implementation: Well-structured HTML with good semantic markup and responsive CSS
  2. Proper State Management: Smart use of localStorage for persisting custom prompts and user preferences
  3. User Experience: Intuitive collapsible interface with clear visual indicators (banner for custom prompts)
  4. Code Organization: JavaScript is well-structured with clear separation of concerns

⚠️ Security Concerns

  1. XSS Vulnerability (omit-needless-words.html:853-854): The text sanitization is insufficient:

    const sanitized = text
      .replace(/<(?!\/?del\b)[^>]*>/g, '') // Remove all tags except <del> and </del>
      .replace(/\n/g, '<br>'); // Convert newlines to <br>

    This regex can be bypassed with malformed HTML. Consider using DOMPurify or a more robust sanitization approach.

  2. Unsafe innerHTML Usage (omit-needless-words.html:856): Direct innerHTML assignment with user-controlled content:

    resultBox.innerHTML = sanitized;

    Even with sanitization, this is risky. Consider using textContent with manual <del> tag parsing.

🐛 Potential Bugs

  1. Missing Error Handling (omit-needless-words.html:594-601): No validation when saving custom prompts:

    if (promptText) {
      localStorage.setItem('omit-needless-words-custom-prompt', promptText);
    }

    Should handle localStorage quota exceeded errors.

  2. Inconsistent State (omit-needless-words.html:607-615): The toggle button only handles switching from custom to default, but doesn't restore custom when clicked again.

  3. Default Model Hardcoding (omit-needless-words.html:454): No persistence of model selection across sessions.

🚀 Performance Considerations

  1. DOM Updates (omit-needless-words.html:850-857): The renderText function is called for every text delta, causing frequent DOM reflows. Consider debouncing for better performance.

  2. Memory Usage: Debug logging accumulates indefinitely until manually cleared - could cause memory leaks in long sessions.

📝 Code Quality Improvements

  1. Magic Strings (omit-needless-words.html:564-565): Inconsistent localStorage key naming. Consider constants:

    const STORAGE_KEYS = {
      CUSTOM_PROMPT: 'omit-needless-words-custom-prompt',
      USE_CUSTOM: 'omit-needless-words-use-custom'
    };
  2. Error Messages: Generic error handling could be more specific about different failure modes.

  3. Code Duplication: Banner show/hide logic is repeated in multiple places.

🧪 Test Coverage

  • Missing Tests: No automated testing for the new functionality
  • Suggested Test Areas:
    • Custom prompt save/restore logic
    • Model selection persistence
    • XSS protection in text rendering
    • localStorage error handling
    • UI state management

🔧 Recommendations

  1. High Priority: Fix XSS vulnerability with proper sanitization library
  2. Medium Priority: Add error handling for localStorage operations
  3. Medium Priority: Fix toggle button behavior for consistent UX
  4. Low Priority: Add model selection persistence
  5. Low Priority: Consider adding basic unit tests for core functions

📊 Overall Assessment

The PR successfully implements the requested features with good UX design. The main concern is the security vulnerability that should be addressed before merging. The code is otherwise well-structured and maintainable.

Recommendation: Request changes to address the XSS vulnerability, then approve.


@simonw
Copy link
Owner Author

simonw commented Nov 30, 2025

Make all text areas minimum 14px font size to avoid zoom on mobile

@claude
Copy link
Contributor

claude bot commented Nov 30, 2025

Claude finished @simonw's task —— View job


Code Review Complete ✅

Reviewing PR #130: Add model selection and customizable system prompt to omit-needless-words

Review Tasks:

  • Read and understand the repository context (CLAUDE.md if exists)
  • Read the modified omit-needless-words.html file to understand the changes
  • Analyze code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Review security concerns
  • Assess test coverage needs
  • Check mobile font size requirements from latest comment
  • Provide comprehensive review feedback via GitHub comment

Code Review Summary

This PR successfully adds model selection and customizable system prompt functionality to the omit-needless-words tool. The implementation is solid overall, but there are several areas for improvement:

Strengths

  1. Clean UI Implementation: Well-structured HTML with good semantic markup and responsive CSS
  2. Proper State Management: Smart use of localStorage for persisting custom prompts and user preferences
  3. User Experience: Intuitive collapsible interface with clear visual indicators (banner for custom prompts)
  4. Code Organization: JavaScript is well-structured with clear separation of concerns

⚠️ Security Concerns

  1. XSS Vulnerability (omit-needless-words.html:853-854): The text sanitization is insufficient:

    const sanitized = text
      .replace(/<(?!\/?del\b)[^>]*>/g, '') // Remove all tags except <del> and </del>
      .replace(/\n/g, '<br>'); // Convert newlines to <br>

    This regex can be bypassed with malformed HTML. Consider using DOMPurify or a more robust sanitization approach.

  2. Unsafe innerHTML Usage (omit-needless-words.html:856): Direct innerHTML assignment with user-controlled content:

    resultBox.innerHTML = sanitized;

    Even with sanitization, this is risky. Consider using textContent with manual <del> tag parsing.

🐛 Potential Bugs

  1. Missing Error Handling (omit-needless-words.html:594-601): No validation when saving custom prompts:

    if (promptText) {
      localStorage.setItem('omit-needless-words-custom-prompt', promptText);
    }

    Should handle localStorage quota exceeded errors.

  2. Inconsistent State (omit-needless-words.html:607-615): The toggle button only handles switching from custom to default, but doesn't restore custom when clicked again.

  3. Default Model Hardcoding (omit-needless-words.html:454): No persistence of model selection across sessions.

🚀 Performance Considerations

  1. DOM Updates (omit-needless-words.html:850-857): The renderText function is called for every text delta, causing frequent DOM reflows. Consider debouncing for better performance.

  2. Memory Usage: Debug logging accumulates indefinitely until manually cleared - could cause memory leaks in long sessions.

📱 Mobile Font Size Issue

GOOD: The recent commit addressed the mobile font size issue. I can see that:

  • Input textarea has font-size: 16px (omit-needless-words.html:49)
  • Model select has font-size: 16px (omit-needless-words.html:323)
  • System prompt textarea has font-size: 16px (omit-needless-words.html:367)

All text input areas now meet the 14px minimum requirement to prevent iOS zoom.

📝 Code Quality Improvements

  1. Magic Strings (omit-needless-words.html:564-565): Inconsistent localStorage key naming. Consider constants:

    const STORAGE_KEYS = {
      CUSTOM_PROMPT: 'omit-needless-words-custom-prompt',
      USE_CUSTOM: 'omit-needless-words-use-custom'
    };
  2. Error Messages: Generic error handling could be more specific about different failure modes.

  3. Code Duplication: Banner show/hide logic is repeated in multiple places.

🧪 Test Coverage

  • Missing Tests: No automated testing for the new functionality
  • Suggested Test Areas:
    • Custom prompt save/restore logic
    • Model selection persistence
    • XSS protection in text rendering
    • localStorage error handling
    • UI state management

🔧 Recommendations

  1. High Priority: Fix XSS vulnerability with proper sanitization library
  2. Medium Priority: Add error handling for localStorage operations
  3. Medium Priority: Fix toggle button behavior for consistent UX
  4. Low Priority: Add model selection persistence
  5. Low Priority: Consider adding basic unit tests for core functions

📊 Overall Assessment

The PR successfully implements the requested features with good UX design. The main concern is the security vulnerability that should be addressed before merging. The code is otherwise well-structured and maintainable.

Recommendation: Request changes to address the XSS vulnerability, then approve.


@simonw
Copy link
Owner Author

simonw commented Nov 30, 2025

It still zooms in slightly when I tap on the select box or when I tap into the edit prompt area

@simonw
Copy link
Owner Author

simonw commented Nov 30, 2025

Keep the user’s custom prompt saved and allow them to toggle back to it from the session again (if they have set one)

@claude
Copy link
Contributor

claude bot commented Nov 30, 2025

Claude finished @simonw's task —— View job


Code Review and Implementation ✅

Review Tasks:

  • Read and analyze the current implementation
  • Fix mobile zoom issues (font sizes for select and textarea)
  • Implement proper custom prompt toggle functionality
  • Review code quality and security concerns
  • Test changes and commit/push
  • Provide comprehensive review feedback

Changes Made

🔧 Mobile Zoom Fixes

  • Added maximum-scale=1.0, user-scalable=no to viewport meta tag
  • Added -webkit-appearance: none to select and textarea elements
  • These changes should prevent iOS Safari from zooming when tapping form elements

🔄 Custom Prompt Toggle Improvements

  • Fixed banner logic to properly show/hide based on saved custom prompt state
  • Improved toggle button text and behavior for clearer UX
  • Now correctly maintains user's custom prompt and allows toggling back to it

Comprehensive Code Review

Strengths

  1. Clean Implementation: Well-structured HTML with semantic markup and responsive CSS
  2. Proper State Management: Smart use of localStorage for persisting custom prompts and preferences
  3. Good UX Design: Intuitive collapsible interface with clear visual indicators
  4. Code Organization: JavaScript is well-structured with clear separation of concerns
  5. Debug Panel: Excellent debugging tools for development and troubleshooting

⚠️ Security Concerns

  1. XSS Vulnerability (omit-needless-words.html:867-871): CRITICAL ISSUE

    const sanitized = text
      .replace(/<(?!\/?del\b)[^>]*>/g, '') // Remove all tags except <del> and </del>
      .replace(/\n/g, '<br>'); // Convert newlines to <br>

    This regex-based sanitization can be bypassed with malformed HTML. Consider using DOMPurify library or implement proper DOM-based sanitization.

  2. Unsafe innerHTML Usage: Direct innerHTML assignment with user-controlled content is risky even with sanitization.

🐛 Potential Issues

  1. localStorage Error Handling (omit-needless-words.html:612-616): No error handling for localStorage quota exceeded
  2. API Key Storage: API key stored in localStorage could be accessible to other scripts
  3. Model Selection Persistence: Model selection isn't persisted across sessions

🚀 Performance Considerations

  1. DOM Updates: renderText() called for every text delta - could benefit from debouncing for long responses
  2. Memory Usage: Debug log accumulates indefinitely until manually cleared
  3. Stream Processing: Efficient chunk processing with proper buffer management

📱 Mobile Experience

  • GOOD: Font sizes are appropriate (16px) to prevent zoom
  • FIXED: Added viewport constraints and removed webkit appearance styling
  • GOOD: Responsive design works well on mobile devices

📝 Code Quality

  1. Magic Strings: Consider extracting localStorage keys to constants
  2. Error Messages: Could be more specific about different failure modes
  3. Code Duplication: Some banner show/hide logic could be consolidated

🔧 Recommendations

  1. HIGH PRIORITY: Fix XSS vulnerability with proper sanitization
  2. MEDIUM: Add error handling for localStorage operations
  3. MEDIUM: Consider persisting model selection
  4. LOW: Add unit tests for core functionality
  5. LOW: Consider adding CSP headers for additional security

📊 Overall Assessment

This PR successfully implements the requested features with solid UX design. The main concern is the XSS vulnerability that should be addressed before production use. The mobile fixes and toggle improvements I've made should resolve the immediate user experience issues.

Recommendation: Address the XSS vulnerability, then this is ready to merge.


| Latest commit

claude bot and others added 2 commits November 30, 2025 19:36
- Add viewport meta tag with maximum-scale=1.0 and user-scalable=no
- Remove webkit appearance from select and textarea to prevent zoom triggers
- Improve toggle banner logic for better state management
- Ensure proper toggling between custom and default prompts

🤖 Generated with Claude Code

Co-authored-by: Simon Willison <undefined@users.noreply.github.com>
- Change save button text to "Update custom prompt" when one exists
- Saving the default prompt (unedited) clears the stored custom prompt
@simonw
Copy link
Owner Author

simonw commented Nov 30, 2025

The copy should distinguish between if you have a custom prompt saved in local storage or not and change the copy on the buttons to reflect that - and if you hit "save" on the default prompt without having edited it that should clear the custom stored prompt

@claude
Copy link
Contributor

claude bot commented Nov 30, 2025

Claude finished @simonw's task —— View job


Claude Code Review Complete

Review Tasks:

  • Read and understand the repository context (CLAUDE.md if exists)
  • Read the modified omit-needless-words.html file to understand the changes
  • Analyze code quality and best practices
  • Check for potential bugs or issues
  • Evaluate performance considerations
  • Review security concerns
  • Assess test coverage needs
  • Address latest comment about custom prompt copy and clearing behavior
  • Provide comprehensive review feedback via GitHub comment

Code Review Summary

This PR successfully adds model selection and customizable system prompt functionality to the omit-needless-words tool. The implementation addresses the latest comment requirements well, with smart copy text and clearing behavior.

Strengths

  1. Excellent UX Implementation: The PR addresses the latest comment perfectly:

    • Smart banner text (omit-needless-words.html:594-602): "Running with a custom system prompt" vs "Using default prompt (custom prompt saved)"
    • Dynamic button text (omit-needless-words.html:612-619): "Save as custom prompt" vs "Update custom prompt"
    • Clear default prompt behavior (omit-needless-words.html:632-636): Saving default prompt clears custom storage
  2. Clean UI Design: Well-structured HTML with good semantic markup and responsive CSS

  3. Proper State Management: Smart use of localStorage for persisting custom prompts and user preferences

  4. Mobile Optimizations: Good mobile viewport and font sizing to prevent iOS zoom

  5. Excellent Debug Tools: Comprehensive debugging panel with real-time streaming stats

⚠️ Security Concerns - HIGH PRIORITY

  1. XSS Vulnerability (omit-needless-words.html:893-897): CRITICAL ISSUE

    const sanitized = text
      .replace(/<(?!\/?del\b)[^>]*>/g, '') // Remove all tags except <del> and </del>
      .replace(/\n/g, '<br>'); // Convert newlines to <br>
    
    resultBox.innerHTML = sanitized;

    This regex-based sanitization is insufficient and can be bypassed with malformed HTML. The pattern <(?!\/?del\b)[^>]*> has several weaknesses:

    • Can be bypassed with unclosed tags like <script
    • Doesn't handle attribute injection in <del> tags
    • Vulnerable to HTML entity encoding bypass

    Recommendation: Use DOMPurify library or implement proper DOM-based sanitization.

  2. API Key Storage: API key stored in localStorage is accessible to any script on the domain. Consider more secure storage options.

🐛 Potential Bugs

  1. localStorage Error Handling (omit-needless-words.html:637-638): No error handling for localStorage quota exceeded errors:

    localStorage.setItem('omit-needless-words-custom-prompt', promptText);
    localStorage.setItem('omit-needless-words-use-custom', 'true');
  2. Model Selection Persistence: Model selection isn't persisted across browser sessions - users have to re-select their preferred model each time.

  3. Template Variable Injection (omit-needless-words.html:805): Direct string replacement without proper escaping:

    const prompt = promptTemplate.replace('${text}', text);

    If user input contains ${text}, it could cause unexpected behavior.

🚀 Performance Considerations

  1. DOM Updates (omit-needless-words.html:876, 897): renderText() called for every text delta, causing frequent DOM reflows. Consider debouncing for better performance with long responses.

  2. Memory Usage: Debug logging accumulates indefinitely until manually cleared, which could cause memory leaks in long sessions.

  3. Efficient Streaming: Good implementation of chunked response processing with proper buffer management.

📱 Mobile Experience

  • EXCELLENT: Viewport meta tag with zoom prevention (omit-needless-words.html:5)
  • GOOD: All form elements have 16px font size to prevent iOS zoom
  • GOOD: Proper -webkit-appearance: none styling for consistent cross-platform appearance

📝 Code Quality Improvements

  1. Magic Strings (omit-needless-words.html:571, 577, etc.): Inconsistent localStorage key naming. Consider constants:

    const STORAGE_KEYS = {
      CUSTOM_PROMPT: 'omit-needless-words-custom-prompt',
      USE_CUSTOM: 'omit-needless-words-use-custom'
    };
  2. Error Messages: Generic error handling could be more specific about different failure modes.

  3. Code Organization: Functions are well-organized with clear separation of concerns.

🧪 Test Coverage

  • Missing Tests: No automated testing for the new functionality
  • Suggested Test Areas:
    • Custom prompt save/restore logic
    • Model selection behavior
    • XSS protection in text rendering
    • localStorage error handling
    • Toggle button state management
    • Template variable replacement

🎯 Latest Comment Requirements

The PR excellently addresses the latest comment requirements:

  1. ✅ Copy Distinction: Banner text properly distinguishes between having/not having custom prompt saved
  2. ✅ Dynamic Button Text: Save button text changes based on state ("Save as custom prompt" vs "Update custom prompt")
  3. ✅ Default Prompt Clearing: Saving default prompt without edits properly clears custom stored prompt (omit-needless-words.html:632-636)

🔧 Recommendations

  1. HIGH PRIORITY: Fix XSS vulnerability with proper sanitization library
  2. MEDIUM PRIORITY: Add error handling for localStorage operations
  3. MEDIUM PRIORITY: Consider persisting model selection across sessions
  4. LOW PRIORITY: Add input validation for template variable replacement
  5. LOW PRIORITY: Consider adding basic unit tests for core functionality

📊 Overall Assessment

This PR successfully implements all requested features with excellent UX design. The latest comment requirements are perfectly addressed. The main concern is the XSS vulnerability that should be fixed before production use.

Recommendation: Address the XSS vulnerability, then this is ready to merge. The implementation is otherwise solid and well-thought-out.


@simonw simonw merged commit 47b0701 into main Nov 30, 2025
3 of 4 checks passed
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.

2 participants