133 |
134 |
135 |
145 |
146 |
147 |
--------------------------------------------------------------------------------
/annotated-presentations.docs.md:
--------------------------------------------------------------------------------
1 | The Annotated Presentation Creator allows users to upload presentation slides, add accessibility alt text, and create markdown annotations for each slide. Users can upload multiple image files, write descriptive alt text, and add formatted annotations that are previewed in real-time. The tool also provides OCR functionality to automatically generate alt text for slides, and includes a template system to export the annotated presentation as HTML.
2 |
3 |
--------------------------------------------------------------------------------
/apsw-query.docs.md:
--------------------------------------------------------------------------------
1 | This tool analyzes SQLite queries using the APSW (Another Python SQLite Wrapper) library. Enter your SQL query in the main text area or set up initial database schema in the collapsible section. You can add parameter values if your query uses parameterized statements. After clicking "Execute query," the tool provides detailed information about query execution plans, expanded SQL statements, and other diagnostic data to help you understand and optimize your SQLite queries.
2 |
3 |
--------------------------------------------------------------------------------
/arena-animated.docs.md:
--------------------------------------------------------------------------------
1 | This interactive visualization displays the evolution of Elo ratings for large language models in the LMSYS Chatbot Arena over time. The chart shows how different models compare against each other, with higher positions indicating stronger performance. You can control the animation speed using the slider, pause/play the animation with the button, or upload custom JSON data with model ratings. The timeline at the bottom shows the current date being displayed as the animation progresses.
2 |
3 |
--------------------------------------------------------------------------------
/ares.docs.md:
--------------------------------------------------------------------------------
1 | This converter transforms regular text into the ARES Phonetic Alphabet, commonly used in emergency communications. Enter text in the input field and click "Convert" to translate each letter and number into its corresponding phonetic code word (Alpha for A, Bravo for B, etc.). Spaces are marked as "(SPACE)" in the output to maintain clarity in the converted message.
2 |
3 |
--------------------------------------------------------------------------------
/ares.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | ARES Phonetic Alphabet Converter
7 |
63 |
64 |
65 |
66 |
ARES Phonetic Alphabet Converter
67 |
68 |
69 |
70 |
71 |
72 |
102 |
103 |
104 |
--------------------------------------------------------------------------------
/aria-live-regions.docs.md:
--------------------------------------------------------------------------------
1 | This demo page allows you to test how screen readers announce dynamic content changes through ARIA live regions. The interface lets you choose between "assertive" and "polite" announcement priorities, then test immediate or delayed notifications. The page includes detailed instructions for enabling and using VoiceOver on both macOS and iOS devices to experience how assistive technologies process these dynamic updates.
2 |
3 |
--------------------------------------------------------------------------------
/aria-live-regions.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Live region notification demo
7 |
94 |
95 |
96 |
Live region notification demo
97 |
98 |
99 |
Testing with VoiceOver on macOS
100 |
101 |
Press Command + F5 to turn on VoiceOver
102 |
103 |
If using a Touch Bar MacBook, press the Touch ID button three times
104 |
If prompted, click "Use VoiceOver" in the dialog
105 |
106 |
107 |
Try clicking both buttons below to hear the notifications
108 |
When finished, press Command + F5 again to turn off VoiceOver (or triple-press Touch ID)
109 |
110 |
111 |
112 |
113 |
Testing with VoiceOver on iOS
114 |
115 |
Go to Settings → Accessibility → VoiceOver
116 |
Toggle VoiceOver on
117 |
118 |
You can also add VoiceOver to Accessibility Shortcut: Settings → Accessibility → Accessibility Shortcut
119 |
Then triple-click the side button to toggle VoiceOver
120 |
121 |
122 |
Basic VoiceOver gestures:
123 |
124 |
Tap once to select an item
125 |
Double-tap anywhere to activate the selected item
126 |
Swipe right/left with one finger to move to next/previous item
127 |
128 |
129 |
Try using the buttons below to hear the notifications
130 |
When finished, return to Settings → Accessibility → VoiceOver and toggle it off
131 |
132 |
Or use your Accessibility Shortcut if configured
133 |
134 |
135 |
136 |
137 |
138 |
Demo
139 |
140 |
141 |
142 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/audio-spectrum.docs.md:
--------------------------------------------------------------------------------
1 | This Audio Spectrum Visualizer uses the Web Audio API to capture microphone input and display real-time frequency data. The application processes audio through an analyzer node and renders the spectrum as a series of colored bars on an HTML canvas, with the height and color of each bar representing different frequency amplitudes. Upon loading, the page requests microphone access and begins continuously updating the visualization.
2 |
3 |
--------------------------------------------------------------------------------
/audio-spectrum.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Audio Spectrum Visualizer
7 |
20 |
21 |
22 |
23 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/avatar-web-component.docs.md:
--------------------------------------------------------------------------------
1 | The Avatar Web Component allows users to select, crop, and resize profile images directly in the browser. Users can upload images by clicking, drag-and-drop, or pasting from clipboard, then precisely crop the image using the interactive resizing handles. The component automatically maintains the proper aspect ratio, generates a JPEG preview, and saves the resulting image data to a specified form field.
2 |
3 |
--------------------------------------------------------------------------------
/base64-gzip-decoder.docs.md:
--------------------------------------------------------------------------------
1 | This tool decodes base64 encoded gzip data. Paste your base64 encoded string into the input field and click the "Decode" button to convert it to plain text. The tool uses the pako JavaScript library to handle the gzip decompression after performing base64 decoding.
2 |
3 |
--------------------------------------------------------------------------------
/base64-gzip-decoder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Base64 Gzip Decoder
7 |
70 |
71 |
72 |
Base64 Gzip decoder
73 |
74 |
75 |
Paste base64 encoded gzip data below:
76 |
77 |
78 |
79 |
80 |
81 |
Decoded result:
82 |
83 |
84 |
85 |
86 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/bash/README.md:
--------------------------------------------------------------------------------
1 | # Bash scripts
2 |
3 | These Bash scripts were written using LLMs.
4 |
5 | ## extract-file-history.sh
6 |
7 | [extract-file-history.sh](https://raw.githubusercontent.com/simonw/tools/refs/heads/main/bash/extract-file-history.sh)
8 |
9 | Create a brand new GitHub repository and copy the history of a single file from another repository into it.
10 |
11 | ```bash
12 | ./extract-file-history.sh path/to/repo file-path-within-repo.txt path/to/new-repo [optional-new-file-name]
13 | ```
14 |
15 | I used it to create [this file history](https://github.com/simonw/llm-prices/commits/9a64678aa4635131dbb916ec99a735ee54050db1/) from [simonw/tools](https://github.com/simonw/tools):
16 |
17 | ```bash
18 | git clone https://github.com/simonw/tools
19 | ./extract-file-history.sh tools llm-prices.html llm-prices index.html
20 | ```
21 |
22 | ## mem.sh
23 |
24 | [mem.sh](https://raw.githubusercontent.com/simonw/tools/refs/heads/main/bash/mem.sh)
25 |
26 | On macOS show the memory usage of the top processes, grouped by name. E.g.
27 |
28 | ```bash
29 | ./mem.sh
30 | ```
31 | ```
32 | 11.58 GB 167 Visual Studio Code.app/Contents/Frameworks/Code Helper (Plugin).app/Contents/MacOS/Code Helper (Plugin)
33 | 11.21 GB 54 Visual Studio Code.app/Contents/Frameworks/Code Helper (Renderer).app/Contents/MacOS/Code Helper (Renderer)
34 | 2.51 GB 56 Visual Studio Code.app/Contents/Frameworks/Code Helper.app/Contents/MacOS/Code Helper
35 | 2.10 GB 12 Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/plugin-container
36 | 541.83 MB 1 Firefox.app/Contents/MacOS/firefox
37 | 456.03 MB 1 Visual Studio Code.app/Contents/MacOS/Electron
38 | ...
39 | ```
40 |
--------------------------------------------------------------------------------
/bash/extract-file-history.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # extract-file-history.sh
4 | #
5 | # Copy the full Git history of a single file into a brand-new repository.
6 | #
7 | # Usage:
8 | # ./extract-file-history.sh [output_filename]
9 | # Example:
10 | # ./extract-file-history.sh ~/projects/my-project src/utils/helper.js helper-history helper.js
11 | #
12 | # The new repository will contain one commit for every change ever made to the
13 | # specified file, preserving author, date and original commit message. If
14 | # output_filename is provided, the file in the new repo will be named accordingly.
15 |
16 | set -euo pipefail
17 |
18 | ###############################################################################
19 | # 1. Validate arguments & environment
20 | ###############################################################################
21 | if [ "$#" -lt 3 ] || [ "$#" -gt 4 ]; then
22 | echo "Usage: $0 [output_filename]"
23 | exit 1
24 | fi
25 |
26 | SOURCE_REPO=$1 # e.g. /path/to/original/repo
27 | TARGET_FILE=$2 # e.g. src/utils/helper.js
28 | NEW_REPO_NAME=$3 # e.g. helper-history
29 |
30 | # Determine output filename in new repo
31 | if [ "$#" -eq 4 ]; then
32 | OUTPUT_FILENAME=$4
33 | else
34 | OUTPUT_FILENAME=$(basename "$TARGET_FILE")
35 | fi
36 |
37 | # Ensure Git is installed
38 | command -v git >/dev/null 2>&1 || {
39 | echo "Error: Git is not installed. Please install Git and try again."
40 | exit 1
41 | }
42 |
43 | # Ensure SOURCE_REPO is a Git repository
44 | if [ ! -d "$SOURCE_REPO/.git" ]; then
45 | echo "Error: $SOURCE_REPO is not a Git repository."
46 | exit 1
47 | fi
48 |
49 | # Ensure TARGET_FILE exists *somewhere* in the repo history
50 | if ! git -C "$SOURCE_REPO" ls-files --error-unmatch -- "$TARGET_FILE" >/dev/null 2>&1; then
51 | echo "Error: $TARGET_FILE does not exist in $SOURCE_REPO (at HEAD)."
52 | exit 1
53 | fi
54 |
55 | ###############################################################################
56 | # 2. Create the new repository
57 | ###############################################################################
58 | echo "Creating new repository: ${NEW_REPO_NAME}..."
59 | mkdir -p "${NEW_REPO_NAME}"
60 | cd "${NEW_REPO_NAME}"
61 | git init -q
62 |
63 | ###############################################################################
64 | # 3. Iterate over commits that touched the file (oldest → newest)
65 | ###############################################################################
66 | echo "Retrieving commit list for ${TARGET_FILE}..."
67 | COMMITS=$(git -C "$SOURCE_REPO" log --follow --reverse --pretty=%H -- "$TARGET_FILE")
68 |
69 | TOTAL=0
70 | for COMMIT in $COMMITS; do
71 | # Extract metadata (author, email, date) and commit message
72 | AUTHOR_NAME=$(git -C "$SOURCE_REPO" show -s --format=%an "$COMMIT")
73 | AUTHOR_EMAIL=$(git -C "$SOURCE_REPO" show -s --format=%ae "$COMMIT")
74 | COMMIT_DATE=$(git -C "$SOURCE_REPO" show -s --format=%ad "$COMMIT")
75 | COMMIT_MSG=$(git -C "$SOURCE_REPO" show -s --format=%B "$COMMIT")
76 |
77 | printf 'Importing %s ...\n' "$(git -C "$SOURCE_REPO" rev-parse --short "$COMMIT")"
78 |
79 | # Write the file content for that commit into the new repo
80 | if ! git -C "$SOURCE_REPO" show "$COMMIT":"$TARGET_FILE" > "$OUTPUT_FILENAME" 2>/dev/null; then
81 | echo "Warning: could not extract ${TARGET_FILE} at ${COMMIT} – skipping."
82 | continue
83 | fi
84 |
85 | git add "$OUTPUT_FILENAME"
86 |
87 | # Re-create the commit with original metadata
88 | GIT_AUTHOR_NAME="$AUTHOR_NAME" \
89 | GIT_AUTHOR_EMAIL="$AUTHOR_EMAIL" \
90 | GIT_AUTHOR_DATE="$COMMIT_DATE" \
91 | GIT_COMMITTER_NAME="$AUTHOR_NAME" \
92 | GIT_COMMITTER_EMAIL="$AUTHOR_EMAIL" \
93 | GIT_COMMITTER_DATE="$COMMIT_DATE" \
94 | git commit -q -m "$COMMIT_MSG"
95 |
96 | TOTAL=$((TOTAL + 1))
97 | done
98 |
99 | ###############################################################################
100 | # 4. Done!
101 | ###############################################################################
102 | echo "Done! New repository '${NEW_REPO_NAME}' contains ${TOTAL} commit(s) of '${TARGET_FILE}' as '${OUTPUT_FILENAME}'."
103 |
104 |
--------------------------------------------------------------------------------
/bash/mem.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | #
3 | # mem.sh — show per‐command RSS usage, defaults to top 20 but
4 | # pass --all to list all.
5 |
6 | # parse flag
7 | show_all=false
8 | if [[ "$1" == "--all" ]]; then
9 | show_all=true
10 | fi
11 |
12 | # the core pipeline, without sorting/limiting
13 | pipeline() {
14 | ps -xmo rss,comm |
15 | awk 'NR>1 {
16 | rss = $1
17 | $1 = "" # drop the rss field
18 | sub(/^ +/, "") # trim leading spaces
19 | cmd = $0 # full command, spaces intact
20 | mem[cmd] += rss
21 | count[cmd]++
22 | }
23 | END {
24 | for (proc in mem) {
25 | display_name = proc
26 | if (proc ~ /^\/Applications\//)
27 | display_name = substr(proc, 15)
28 |
29 | memory_mb = mem[proc] / 1024
30 | if (memory_mb >= 1024) {
31 | memory_str = sprintf("%8.2f GB", memory_mb/1024)
32 | } else {
33 | memory_str = sprintf("%8.2f MB", memory_mb)
34 | }
35 |
36 | printf "%f\t%s\t%5d\t%s\n",
37 | memory_mb, memory_str, count[proc], display_name
38 | }
39 | }'
40 | }
41 |
42 | # run, then sort and optionally limit
43 | if $show_all; then
44 | pipeline | sort -nr | cut -f2-
45 | else
46 | pipeline | sort -nr | cut -f2- | head -n 20
47 | fi
48 |
49 |
--------------------------------------------------------------------------------
/bbox-cropper.docs.md:
--------------------------------------------------------------------------------
1 | This bounding box tool allows you to draw selection boxes on images to get coordinates. Upload an image by pasting, dragging, or selecting a file, then draw and adjust your bounding box. The tool generates coordinates as percentages of the image dimensions, making them useful for responsive applications or when working with images of different sizes.
2 |
3 |
--------------------------------------------------------------------------------
/bluesky-firehose.docs.md:
--------------------------------------------------------------------------------
1 | The Bluesky WebSocket Feed Monitor connects to the Bluesky social network's WebSocket API and displays real-time feed data in a log. Users can establish a connection, send custom JSON messages to filter the feed by specific collections or user DIDs, and view incoming messages in the output area. The interface provides controls to connect, disconnect, send messages, and clear the log history.
2 |
3 |
--------------------------------------------------------------------------------
/bluesky-resolve.docs.md:
--------------------------------------------------------------------------------
1 | This tool resolves a Bluesky handle to its corresponding DID (Decentralized Identifier). Enter any Bluesky handle in the input field and click "Resolve" to retrieve the associated DID. The tool communicates with the Bluesky API and displays the result or an error message if the handle cannot be found.
2 |
3 |
--------------------------------------------------------------------------------
/bluesky-resolve.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
51 |
52 |
53 |
Resolve Bluesky handle to DID
54 |
55 |
59 |
60 |
61 |
62 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/bluesky-thread.docs.md:
--------------------------------------------------------------------------------
1 | The Bluesky Thread Viewer allows you to view and export conversation threads from Bluesky social network. Enter a Bluesky post URL to fetch the entire thread with replies, which will display in a hierarchical format with color-coded depth indicators. The tool provides options to copy the thread as formatted text or raw JSON data for sharing or analysis.
2 |
3 |
--------------------------------------------------------------------------------
/bluesky-timeline.docs.md:
--------------------------------------------------------------------------------
1 | This web application allows you to view your Bluesky social media timeline by logging in with your username and app password. After authentication, it fetches your timeline and displays posts with avatars and content in a readable format. The timeline automatically refreshes every 10 seconds to show new content, and you can stop the auto-refresh at any time.
2 |
3 |
--------------------------------------------------------------------------------
/box-shadow.docs.md:
--------------------------------------------------------------------------------
1 | This interactive tool allows you to create and customize CSS box shadows using simple slider controls. Adjust horizontal and vertical offsets, blur radius, spread radius, color, and opacity to generate the perfect shadow effect for your web elements. The tool provides a live preview of your shadow and displays the corresponding CSS code, which you can copy to your clipboard with a single click.
2 |
3 |
--------------------------------------------------------------------------------
/broadcast-channel-chat.docs.md:
--------------------------------------------------------------------------------
1 | The Multi-Tab Chat application demonstrates real-time message synchronization across multiple browser tabs without requiring a backend server. Open the same page in several tabs to see messages instantly appear across all instances. The application utilizes the Broadcast Channel API to enable communication between tabs, with each tab receiving a unique identifier. Type messages in any tab and watch them propagate to all other open instances of the application.
2 |
3 |
--------------------------------------------------------------------------------
/california-clock-change.docs.md:
--------------------------------------------------------------------------------
1 | This web application provides information about clock changes due to Daylight Saving Time in California (Pacific Time zone). The page displays the most recent time change, the upcoming change, current time status (PST or PDT), and offers helpful information about how the change might affect daily routines, including pet schedules. The application automatically detects if users are in the Pacific timezone and shows relevant timing details for both past and future clock adjustments.
2 |
3 |
--------------------------------------------------------------------------------
/census-reporter-claude.docs.md:
--------------------------------------------------------------------------------
1 | The Census Explorer application provides an interface to explore American Community Survey data through the Census Reporter API. Users can search for geographic areas and specific census tables, then visualize the resulting data. The interface follows a clear three-step workflow: first search and select geographies and tables, then view the data in tabular or chart formats. Data can be toggled between different ACS releases and refreshed as needed.
2 |
3 |
--------------------------------------------------------------------------------
/census-reporter-gemini.docs.md:
--------------------------------------------------------------------------------
1 | This tool helps you explore U.S. Census data through the Census Reporter API. Search for geographical areas like cities or counties, then select data tables related to demographics, income, or other census topics. Once you've selected both a geography and at least one data table, you can retrieve and view the census data with estimates and margins of error displayed in organized tables.
2 |
3 |
--------------------------------------------------------------------------------
/chrome-prompt-playground.docs.md:
--------------------------------------------------------------------------------
1 | This playground allows you to run prompts against the Gemini Nano experimental model directly in Chrome Canary. Enter your prompt in the text area, click "Execute prompt" to see the generated response, and view your interaction history which is saved locally in your browser. The interface requires Chrome Canary with the "Prompt API for Gemini Nano" flag enabled.
2 |
3 |
--------------------------------------------------------------------------------
/claude-token-counter.docs.md:
--------------------------------------------------------------------------------
1 | The Claude Token Counter allows you to calculate token usage for messages sent to Claude 3.5 Sonnet. Enter an optional system prompt and required user message, then add image or PDF files by dragging and dropping them into the designated area. After providing your Anthropic API key (stored locally in your browser), click "Count Tokens" to receive a detailed breakdown of token consumption for your inputs.
2 |
3 |
--------------------------------------------------------------------------------
/click-grid-to-expand.docs.md:
--------------------------------------------------------------------------------
1 | This page displays an interactive CSS grid layout with a symmetrical animation effect. The grid contains four colored blocks of different sizes, with one block (colored green) that can be clicked to expand and cover the entire grid area. When expanded, clicking again triggers a smooth animation that returns the block to its original position and size.
2 |
3 |
--------------------------------------------------------------------------------
/clipboard-viewer.docs.md:
--------------------------------------------------------------------------------
1 | The Clipboard Format Viewer allows you to inspect the raw data available when pasting content. When you paste text, images, files, or other content, the tool displays all available formats and their corresponding data in an organized view. Each format is shown in its own section, with special handling for images and files to provide visual previews where possible.
2 |
3 |
--------------------------------------------------------------------------------
/code-with-claude-2025.docs.md:
--------------------------------------------------------------------------------
1 | This page allows users to download a conference schedule in ICS format for import into calendar applications. The page displays a preview of the ICS file containing 25 events from the San Francisco conference on May 22, 2025, including keynotes, workshops, panels, and other sessions. Users can download the complete schedule by clicking the download button at the top of the page.
2 |
3 |
--------------------------------------------------------------------------------
/compare-pdfs.docs.md:
--------------------------------------------------------------------------------
1 | This PDF comparison tool enables side-by-side visual comparison of two PDF documents. Upload two PDFs by dragging them into the drop zone or clicking to select files. The tool renders each page of both documents and generates a third visualization that highlights differences between corresponding pages in red, making it easy to identify discrepancies between versions.
2 |
3 |
--------------------------------------------------------------------------------
/css-text-wrapping.docs.md:
--------------------------------------------------------------------------------
1 | This guide provides a comprehensive overview of CSS text wrapping properties, including word-wrap/overflow-wrap, word-break, white-space, text-overflow, hyphens, line-break, and text-wrap. Each property is explained with multiple visual examples demonstrating different values, and includes tables summarizing the available options with their effects on text display. The page also includes browser compatibility information for each property and useful notes about implementation requirements.
2 |
3 |
--------------------------------------------------------------------------------
/csv-marker-map.docs.md:
--------------------------------------------------------------------------------
1 | This page creates an interactive map with markers based on CSV data. It accepts parameters in the URL to set the center, zoom level, search query, individual markers, and a CSV file URL for bulk marker placement. The markers are displayed on an OpenStreetMap base layer, and the map view automatically updates the URL when panned or zoomed.
2 |
3 |
--------------------------------------------------------------------------------
/csv-marker-map.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | CSV marker map - use ?csv=URL to CSV to populate
7 |
8 |
9 |
20 |
21 |
22 |
23 |
118 |
119 |
120 |
--------------------------------------------------------------------------------
/date-calculator.docs.md:
--------------------------------------------------------------------------------
1 | This Date Calculator tool enables users to calculate and visualize the time span between two dates. Enter a start and end date, and the calculator provides comprehensive information including total days, business days, weekend days, and weeks elapsed. The visual timeline displays the selected date range with a marker for the current date, allowing for easy visualization of time periods.
2 |
3 |
--------------------------------------------------------------------------------
/dev-requirements.txt:
--------------------------------------------------------------------------------
1 | pytest-playwright
--------------------------------------------------------------------------------
/encrypt.docs.md:
--------------------------------------------------------------------------------
1 | This tool enables secure message encryption with a passphrase. Enter your message and a passphrase to generate an encrypted link that you can share with others. Recipients can decrypt the message by visiting the link and entering the correct passphrase. All encryption and decryption occurs in the browser using the Web Crypto API for enhanced security.
2 |
3 |
--------------------------------------------------------------------------------
/escape-entities.docs.md:
--------------------------------------------------------------------------------
1 | This HTML Entity Escaper converts special characters in text to their corresponding HTML entities. The tool transforms characters like ampersands, angle brackets, and quotes into their HTML entity equivalents, making them safe to display on web pages without being interpreted as actual HTML code. After pasting text into the input box, the escaped version appears automatically in the output box for easy copying.
2 |
3 |
--------------------------------------------------------------------------------
/escape-entities.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | HTML Entity Escaper
7 |
48 |
49 |
50 |
HTML Entity Escaper
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
What this tool does:
59 |
This HTML Entity Escaper is a simple tool that helps you convert special characters in your text to their corresponding HTML entities. It's particularly useful when you need to display code or text containing HTML syntax on a web page without it being interpreted as actual HTML.
60 |
The tool escapes the following characters:
61 |
62 |
& becomes &
63 |
< becomes <
64 |
> becomes >
65 |
" becomes "
66 |
' becomes '
67 |
68 |
To use the tool, simply paste your text into the input box. The escaped version will automatically appear in the output box. You can then copy the escaped text to your clipboard using the "Copy to clipboard" button.
69 |
70 |
71 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/event-planner.docs.md:
--------------------------------------------------------------------------------
1 | The Event Planner tool allows users to create events by entering details such as title, description, location, date, time, and timezone. After submitting the form, the application displays the event information, calculates the time remaining until the event, and provides a Google Calendar link for easy addition to your personal calendar. The tool supports multiple US timezones and formats the event details in a clear, readable format.
2 |
3 |
--------------------------------------------------------------------------------
/event-planner.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Event Planner
7 |
8 |
9 |
39 |
40 |
41 |
Event Planner
42 |
70 |
71 |
72 |
73 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/exif.docs.md:
--------------------------------------------------------------------------------
1 | The EXIF Data Viewer allows you to extract and view metadata from your digital photos. Upload any image to display embedded EXIF information including GPS coordinates (latitude and longitude) if available. The tool presents both specific location data and a complete breakdown of all metadata tags contained within the image file.
2 |
3 |
--------------------------------------------------------------------------------
/exif.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | EXIF Data Viewer
7 |
40 |
41 |
42 |
43 |
EXIF Data Viewer
44 |
45 |
46 |
47 |
48 |
49 |
50 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/extract-urls.docs.md:
--------------------------------------------------------------------------------
1 | This tool extracts URLs from copied web page content. Paste HTML content into the editable area, and the tool automatically parses out all hyperlinks and displays them in a clean list format. The extracted URLs can be copied to your clipboard with a single click for use in other applications.
2 |
3 |
--------------------------------------------------------------------------------
/extract-urls.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Extract URLs
7 |
51 |
52 |
53 |
Extract URLs
54 |
Copy content from a web page and paste here to extract linked URLs:
55 |
56 |
57 |
Extracted
58 |
59 |
60 |
61 |
62 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simonw/tools/baa3dfb4f2ba57a7d807721baa3c5ddde3924d6a/favicon.ico
--------------------------------------------------------------------------------
/flexbox-playground.docs.md:
--------------------------------------------------------------------------------
1 | The CSS Flexbox Playground provides an interactive environment for experimenting with flexbox layout properties. Users can adjust container settings like flex-direction, justify-content, and align-items through the control panel, and modify individual flex item properties such as flex-grow and align-self by selecting specific items. The playground displays real-time visual results and generates the corresponding CSS code, making it a practical tool for learning and testing flexbox layouts.
2 |
3 |
--------------------------------------------------------------------------------
/footnotes-experiment.docs.md:
--------------------------------------------------------------------------------
1 | This page demonstrates an interactive footnote system that enhances content by providing additional information without disrupting reading flow. When users hover over or click a footnote marker, a popup appears with the footnote text, eliminating the need to scroll to the bottom of the page. The system includes accessibility features with proper ARIA roles and supports both desktop and mobile interactions through hover and click events.
2 |
3 |
--------------------------------------------------------------------------------
/gather_links.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import json
3 | import os
4 | import re
5 | import subprocess
6 | from pathlib import Path
7 | from datetime import datetime
8 |
9 |
10 | def get_file_commit_details(file_path):
11 | """
12 | Get the commit details for a specific file.
13 | Returns a list of dictionaries with hash, message, and date.
14 | """
15 | try:
16 | # Get each commit as a separate record with its hash, date, and message
17 | result = subprocess.run(
18 | ["git", "log", "--format=%H|%aI|%B%x00", "--", file_path],
19 | capture_output=True,
20 | text=True,
21 | check=True,
22 | )
23 |
24 | commits = []
25 | # Split by NULL character which safely separates commits
26 | raw_commits = result.stdout.strip().split("\0")
27 |
28 | for raw_commit in raw_commits:
29 | if not raw_commit.strip():
30 | continue
31 |
32 | # Find the first pipe to extract commit hash
33 | first_pipe = raw_commit.find("|")
34 | if first_pipe == -1:
35 | continue
36 |
37 | commit_hash = raw_commit[:first_pipe]
38 |
39 | # Find the second pipe to extract date
40 | second_pipe = raw_commit.find("|", first_pipe + 1)
41 | if second_pipe == -1:
42 | continue
43 |
44 | commit_date = raw_commit[first_pipe + 1 : second_pipe]
45 |
46 | # The rest is the commit message
47 | commit_message = raw_commit[second_pipe + 1 :]
48 |
49 | commits.append(
50 | {"hash": commit_hash, "date": commit_date, "message": commit_message}
51 | )
52 |
53 | return commits
54 | except subprocess.CalledProcessError as e:
55 | print(f"Error getting commit history for {file_path}: {e}")
56 | return []
57 |
58 |
59 | def extract_urls(text):
60 | """
61 | Extract URLs from text using regex pattern.
62 | Returns a list of URLs.
63 | """
64 | # Pattern for URLs that captures the full URL until whitespace or end of string
65 | url_pattern = r"(https?://[^\s]+)"
66 | return re.findall(url_pattern, text)
67 |
68 |
69 | def main():
70 | # Get current directory
71 | current_dir = Path.cwd()
72 |
73 | # Find all HTML files
74 | html_files = list(current_dir.glob("*.html"))
75 |
76 | # Dictionary to store results
77 | results = {"pages": {}}
78 |
79 | # Process each HTML file
80 | for html_file in html_files:
81 | file_name = html_file.name
82 | print(f"Processing {file_name}...")
83 |
84 | # Get commit details for this file
85 | commits = get_file_commit_details(html_file)
86 |
87 | if not commits:
88 | continue
89 |
90 | # Extract URLs from commit messages
91 | all_urls = []
92 | for commit in commits:
93 | urls = extract_urls(commit["message"])
94 | all_urls.extend(urls)
95 |
96 | # Remove duplicates but preserve order
97 | unique_urls = []
98 | for url in all_urls:
99 | if url not in unique_urls:
100 | unique_urls.append(url)
101 |
102 | # Add to results if any commits were found
103 | if commits:
104 | results["pages"][file_name] = {"commits": commits, "urls": unique_urls}
105 |
106 | # Save results to JSON file
107 | with open("gathered_links.json", "w") as f:
108 | json.dump(results, f, indent=2)
109 |
110 | print(f"Processed {len(html_files)} files")
111 | print(f"Found details for {len(results['pages'])} files")
112 | print("Results saved to gathered_links.json")
113 |
114 |
115 | if __name__ == "__main__":
116 | main()
117 |
--------------------------------------------------------------------------------
/gemini-bbox-tool.docs.md:
--------------------------------------------------------------------------------
1 | This page implements an automatic redirect to the "/gemini-bbox" location. When a user visits this page, their browser will immediately redirect them to the new location, with a fallback text link provided if the automatic redirection fails to work.
2 |
3 |
--------------------------------------------------------------------------------
/gemini-bbox-tool.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Redirecting to /gemini-bbox
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/gemini-bbox.docs.md:
--------------------------------------------------------------------------------
1 | This tool visualizes bounding box coordinates from the Gemini API on images. Upload an image, enter a prompt asking for bounding box coordinates in the format [ymin, xmin, ymax, xmax], and the application will display the image with color-coded bounding boxes overlaid. The canvas includes a coordinate grid system with axes labeled from 0-1000, and individual cropped sections of each detected object are displayed below the main visualization.
2 |
3 |
--------------------------------------------------------------------------------
/gemini-chat.docs.md:
--------------------------------------------------------------------------------
1 | This web application allows you to interact with Google's Gemini AI models through a simple chat interface. Users can select different Gemini model versions, send messages, and receive responses in real-time with markdown rendering support. The app requires a Gemini API key on first use and displays usage metadata and response time for each interaction.
2 |
3 |
--------------------------------------------------------------------------------
/gemini-image-json.docs.md:
--------------------------------------------------------------------------------
1 | This tool renders JSON output from Gemini's image generation API for better visualization. Paste your Gemini API response JSON in the text area and click "Render JSON" to display images and text in a readable format. The page also extracts and displays usage metadata and model version information from the JSON, making it easier to interpret the response data.
2 |
3 |
--------------------------------------------------------------------------------
/gemini-mask.docs.md:
--------------------------------------------------------------------------------
1 | This tool visualizes image segmentation masks generated by the Gemini API. Upload an image, enter a prompt requesting segmentation masks, and the application will display the results with bounding boxes overlaid on your image. The extracted masks are shown side-by-side with the corresponding image regions, and a coordinate system helps you understand the normalized coordinates used in the API response.
2 |
3 |
--------------------------------------------------------------------------------
/github-api-write.docs.md:
--------------------------------------------------------------------------------
1 | This tool allows you to upload files or images directly to GitHub repositories using the GitHub API. Enter your GitHub token, repository details, and file path, then choose between text content or image upload. The application handles the base64 encoding required by GitHub's API and provides a link to view your file once successfully uploaded.
2 |
3 |
--------------------------------------------------------------------------------
/github-issue-to-markdown.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts GitHub issues into markdown format for easy reference and sharing. Enter a GitHub issue URL, click "Convert to markdown," and the tool will fetch the issue content including all comments and format it as markdown. The converted markdown can be copied to your clipboard with a single click for use in documentation or other contexts.
2 |
3 |
--------------------------------------------------------------------------------
/github-issue.docs.md:
--------------------------------------------------------------------------------
1 | This page allows you to fetch and convert GitHub issues to markdown format. Enter a GitHub issue URL in the input field and click "Fetch Issue" to retrieve the issue content including comments. You can provide a personal access token for accessing private repositories or to avoid rate limiting, which will be saved in your browser for future use.
2 |
3 |
--------------------------------------------------------------------------------
/gpt-4o-audio-player.docs.md:
--------------------------------------------------------------------------------
1 | The Gist Audio Player allows you to play and download audio from GitHub Gists containing OpenAI GPT-4o audio responses. Enter a Gist URL containing base64-encoded WAV data from the GPT-4o-audio-preview model, and the player will extract the audio, display the transcript, and provide playback controls. You can also share specific audio by using the URL parameter format `?gist=GIST_ID`.
2 |
3 |
--------------------------------------------------------------------------------
/hacker-news-thread-export.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts Hacker News threads into a structured text format. Enter a Hacker News post ID or URL in the input field, then click "Fetch and format" to retrieve the thread. The tool organizes comments in a hierarchical format with path numbers (like [1.2.3]) indicating the reply structure, making it easy to track conversation flow and export threads for reference elsewhere.
2 |
3 |
--------------------------------------------------------------------------------
/haiku.docs.md:
--------------------------------------------------------------------------------
1 | This webpage enables you to generate haikus based on images captured from your device's camera. After granting camera access, capture an image using the center button, and Claude will analyze the image and generate a haiku inspired by what it sees. The interface allows switching between front and rear cameras if your device has multiple cameras available.
2 |
3 |
--------------------------------------------------------------------------------
/html-preview.docs.md:
--------------------------------------------------------------------------------
1 | This HTML Live Preview tool allows you to write HTML code and immediately see the rendered results. Type your HTML in the editor on the left side, and the preview will update on the right side (or toggle between views on mobile devices). The tool includes features to copy your HTML code to the clipboard and supports responsive layouts that adapt to different screen sizes.
2 |
3 |
--------------------------------------------------------------------------------
/huggingface-storage.docs.md:
--------------------------------------------------------------------------------
1 | This tool checks the storage size of any Hugging Face model. Enter a model URL or path (like "mlx-community/Llama-3.2-3B-Instruct-4bit"), and the application will fetch and display the model's size in MB or GB. The tool accepts both full Hugging Face URLs and direct model paths as input.
2 |
3 |
--------------------------------------------------------------------------------
/iframe-api-explorer.docs.md:
--------------------------------------------------------------------------------
1 | The API Explorer provides a sandboxed interface for testing APIs through a secure iframe implementation. Users can enter an API URL and view the JSON response without security risks, as the parent page handles the actual fetch requests through a message-passing protocol. The interface demonstrates secure cross-frame communication using `postMessage()` while maintaining appropriate isolation between contexts.
2 |
3 |
--------------------------------------------------------------------------------
/iframe-sandbox.docs.md:
--------------------------------------------------------------------------------
1 | This page provides an interactive sandbox for testing HTML and JavaScript code with customizable iframe security controls. It features a code editor on the left where you can write HTML, and a preview pane on the right that renders your code within an iframe. The sandbox controls beneath the preview allow you to enable or disable specific security restrictions such as scripts, forms, popups, and same-origin policy, letting you experiment with different security contexts.
2 |
3 |
--------------------------------------------------------------------------------
/image-resize-quality.docs.md:
--------------------------------------------------------------------------------
1 | This tool allows you to compare image quality and filesize at different compression levels. Upload an image by dragging, clicking, or pasting it into the interface. The tool will display versions of your image at original and half size with various quality settings (100%, 90%, 70%, 50%, and 30%), showing file size for each variation. For transparent images, a background color picker lets you choose the fill color.
2 |
3 |
--------------------------------------------------------------------------------
/image-to-jpeg.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts images to JPEG format with adjustable quality. Users can upload an image by clicking on the drop zone or dragging and dropping a file, then adjust the JPEG compression quality using the slider. The converted image is displayed with its data URI and approximate file size, allowing users to find the optimal balance between image quality and file size.
2 |
3 |
--------------------------------------------------------------------------------
/image-to-jpeg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Drag and Drop Image
5 |
29 |
30 |
31 |
Click or drag and drop an image here
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
JPEG Size: 0 bytes
43 |
44 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/image-to-svg.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts JPG or PNG images to SVG format using the imagetracerjs library. You can upload an image by dragging and dropping it onto the designated area or by clicking to browse your files. After conversion, the SVG appears on screen and its code is displayed in a text field below, which can be copied to the clipboard with a single click.
2 |
3 |
--------------------------------------------------------------------------------
/image-to-svg.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image to SVG
7 |
49 |
50 |
51 |
Drag and drop a JPG or PNG image here, or click to select a file
55 |
56 |
57 |
58 |
SVG Code:
59 |
60 |
61 |
62 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/incomplete-json-printer.docs.md:
--------------------------------------------------------------------------------
1 | The Incomplete JSON Pretty Printer formats truncated or incomplete JSON data into a readable structure. It features automatic formatting as you type, copy to clipboard functionality, and a sample example to demonstrate its capabilities. The tool helps developers visualize and work with partial JSON data by applying proper indentation and structure to make the content more readable.
2 |
3 |
--------------------------------------------------------------------------------
/jina-embeddings-image-token-calculator.docs.md:
--------------------------------------------------------------------------------
1 | The Image Token Calculator allows you to estimate the token cost for processing images with AI models. Upload an image by dragging and dropping it onto the designated area or by clicking to browse your files. After uploading, the calculator will display the image dimensions, number of tiles required to process the image, and the total token cost based on a calculation of 1000 tokens per tile.
2 |
3 |
--------------------------------------------------------------------------------
/jina-embeddings-image-token-calculator.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Image Token Calculator
7 |
35 |
36 |
37 |
Image Token Calculator
38 |
39 |
Drop an image here or click to select
40 |
41 |
42 |
43 |
44 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/jina-reader.docs.md:
--------------------------------------------------------------------------------
1 | Jina Reader provides a web interface to view and process online content using the Jina Reader API. Enter a URL to fetch its content in various formats (Markdown, HTML, Text, or LLM Markdown), and the result will be displayed in both raw and rendered forms. You can also run Claude AI analysis on the fetched content using customizable prompts to generate summaries or other text transformations.
2 |
3 |
--------------------------------------------------------------------------------
/json-schema-builder.docs.md:
--------------------------------------------------------------------------------
1 | The JSON Schema Builder allows you to create JSON schemas with a visual interface. Add properties by specifying their name, type, and whether they're required, then build nested objects and arrays. Your schema is displayed in real-time and can be copied to the clipboard with a single click. The tool also automatically saves your work in the URL, enabling you to share your schema with others.
2 |
3 |
--------------------------------------------------------------------------------
/json-to-yaml.docs.md:
--------------------------------------------------------------------------------
1 | This converter transforms JSON to YAML in three different formats: block style (standard indentation), flow style (compact representation), and quoted strings (all string values enclosed in double quotes). Simply paste your JSON into the input field and the tool automatically generates the corresponding YAML formats, with copy buttons for each output style.
2 |
3 |
--------------------------------------------------------------------------------
/json-to-yaml.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | JSON to YAML Converter
7 |
8 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
196 |
197 |
--------------------------------------------------------------------------------
/keyboard-filters.docs.md:
--------------------------------------------------------------------------------
1 | The Filter Badge Component allows users to create and manage data filtering criteria through an interactive interface. Users can add filters by clicking the "Add filter" button, then select a column, operator, and value to define each filter. Each part of the filter is keyboard accessible, with support for tab navigation, Enter/Space key actions, and direct value editing through a popover interface.
2 |
3 |
--------------------------------------------------------------------------------
/lightning-timer.docs.md:
--------------------------------------------------------------------------------
1 | The Lightning Timer is a customizable countdown application designed for time-sensitive presentations or tasks. Set your preferred duration and optional warning time through the settings gear icon. Click the timer to start, pause, or reset it. The screen changes color as time runs low, providing a visual cue when your allotted time is nearly exhausted.
2 |
3 |
--------------------------------------------------------------------------------
/link-temp.docs.md:
--------------------------------------------------------------------------------
1 | This code implements a Gemini model class for the LLM library, enabling interactions with Google's Gemini AI models. It handles configuration settings like API keys, model selection, and temperature, while providing methods to generate text completions and embeddings through the Google Generative AI API. The implementation includes proper error handling and parameter validation.
2 |
3 |
--------------------------------------------------------------------------------
/link-temp.html:
--------------------------------------------------------------------------------
1 | link
2 |
--------------------------------------------------------------------------------
/llm-prices.docs.md:
--------------------------------------------------------------------------------
1 | This HTML page implements an automatic redirect to the website www.llm-prices.com. The page uses a meta refresh tag with a zero-second delay to immediately redirect the visitor upon loading. For visitors whose browsers don't support automatic redirects or have them disabled, a fallback text message with a clickable link is provided.
2 |
3 |
--------------------------------------------------------------------------------
/llm-prices.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Redirecting to llm-prices.com
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/markdown-math.docs.md:
--------------------------------------------------------------------------------
1 | This interactive tool renders Markdown and LaTeX math formulas in real-time. As you type in the text area, the content is converted to formatted HTML in the preview pane, supporting both inline math expressions ($...$) and block math equations ($$...$$). The generated HTML can be copied to clipboard for use in other applications or websites.
2 |
3 |
--------------------------------------------------------------------------------
/markdown-math.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Markdown and Math Live Renderer
7 |
8 |
9 |
10 |
11 |
49 |
50 |
51 |
Markdown and Math Live Renderer
52 |
80 |
Preview:
81 |
82 |
Generated HTML:
83 |
84 |
85 |
86 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/mask-visualizer.docs.md:
--------------------------------------------------------------------------------
1 | The Mask Visualizer is a tool for rendering JSON data containing image masks with bounding boxes in a configurable coordinate system. Users can paste JSON that includes box coordinates and base64-encoded mask images, then select one of four coordinate system origins (Top Left, Top Right, Bottom Left, or Bottom Right). The visualization displays the masks with colored bounding boxes and provides detailed coordinate information for both original and transformed coordinates.
2 |
3 |
--------------------------------------------------------------------------------
/mdn-timelines.docs.md:
--------------------------------------------------------------------------------
1 | This web application displays timeline views of browser support for web APIs based on MDN's browser compatibility data. Users can search for specific APIs by name, view when different browsers added support for those APIs, and access detailed information including MDN documentation links, specifications, and status indicators. The interface shows a chronological timeline of browser support with version numbers and release dates, along with information about browsers that don't support the selected API.
2 |
3 |
--------------------------------------------------------------------------------
/nav-for-headings.docs.md:
--------------------------------------------------------------------------------
1 | This tool processes HTML content by adding unique ID attributes to all header elements (h1-h6) that don't already have them. After processing, it generates a list of anchor links pointing to each header, using the page URL you provide combined with the header IDs as fragment identifiers. The processed HTML and a formatted list of header links are displayed in separate output fields.
2 |
3 |
--------------------------------------------------------------------------------
/nav-for-headings.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | HTML Header Processor
7 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/ocr.docs.md:
--------------------------------------------------------------------------------
1 | This tool performs Optical Character Recognition (OCR) on PDF documents and images directly in your web browser. Upload a file by dragging and dropping or clicking the dropzone, and the text content will be extracted using Tesseract.js. For PDFs, each page is processed separately and compiled into a full document, with support for multiple languages.
2 |
3 |
--------------------------------------------------------------------------------
/openai-audio-output.docs.md:
--------------------------------------------------------------------------------
1 | This page allows you to generate human-like speech from text using OpenAI's GPT-4o audio models. Enter your prompts, select a model and voice, then generate audio output which can be played, downloaded, or shared via GitHub Gist. The interface displays both the audio player and a transcript of the spoken content, along with the complete API response for reference.
2 |
3 |
--------------------------------------------------------------------------------
/openai-audio.docs.md:
--------------------------------------------------------------------------------
1 | This page provides an interface for recording audio, which can then be submitted to OpenAI's GPT-4o audio model along with a text prompt. The application allows users to record speech through their microphone, review the recording, and submit it to the API for processing. After submission, the page displays both the formatted response and detailed information about token usage and associated costs.
2 |
3 |
--------------------------------------------------------------------------------
/openai-webrtc.docs.md:
--------------------------------------------------------------------------------
1 | This interface connects to OpenAI's real-time audio API using WebRTC technology. Users can start an audio session by providing their OpenAI API token and selecting a voice option, allowing for voice-based interactions with GPT-4o. The page displays detailed statistics about each interaction including token usage and estimated costs, while also maintaining a running log of all session events.
2 |
3 |
--------------------------------------------------------------------------------
/openfreemap-demo.docs.md:
--------------------------------------------------------------------------------
1 | This demonstration showcases MapLibre GL integration with OpenFreeMap, displaying 1000 randomly generated points within San Francisco. The code provides three different marker implementation options: scaled markers using MapLibre's built-in functionality, custom HTML elements styled as circular markers, and a more performant circle layer using GeoJSON data. The map initializes with a tilted perspective and automatically adjusts its bounds to encompass all generated markers.
2 |
3 |
--------------------------------------------------------------------------------
/openfreemap-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | MapLibre GL + OpenFreeMap demo
5 |
6 |
7 |
13 |
14 |
15 |
16 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/passkeys.docs.md:
--------------------------------------------------------------------------------
1 | This page provides a demonstration environment for testing and understanding passkeys. Users can register new passkeys, authenticate with existing ones, and manage their stored credentials. The demo operates entirely in the browser using localStorage for credential storage, with a debugging section that shows technical details of credential creation and authentication responses.
2 |
3 |
--------------------------------------------------------------------------------
/paste-html-subset.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts rich text into a clean HTML subset by letting you paste formatted content from any source. Paste text into the editable area to generate sanitized HTML code that only includes supported elements like paragraphs, headings, links, and lists. The tool provides both a code view of the resulting HTML and a live preview window to verify the output appearance.
2 |
3 |
--------------------------------------------------------------------------------
/paste-rich-text.docs.md:
--------------------------------------------------------------------------------
1 | This tool allows you to extract HTML code from formatted text copied from websites. Paste content into the editable area to see the underlying HTML code displayed in a dedicated output field. The extracted HTML can be copied to your clipboard with a single click and a preview shows how the content will render when used in another webpage.
2 |
3 |
--------------------------------------------------------------------------------
/paste-rich-text.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Rich Text HTML Extractor
7 |
91 |
92 |
93 |
Rich text HTML extractor
94 |
Copy formatted text from a webpage and paste here to extract the HTML:
95 |
96 |
97 |
Paste using Ctrl+V or ⌘+V to capture the rich text HTML.
98 |
99 |
100 |
HTML code
101 |
102 |
103 |
104 |
105 |
106 |
107 |
Preview
108 |
109 |
110 |
111 |
112 |
181 |
182 |
183 |
--------------------------------------------------------------------------------
/pdf-ocr.docs.md:
--------------------------------------------------------------------------------
1 | This page serves as an automatic redirect to the OCR application. It uses the HTML meta refresh tag to immediately forward visitors to the /ocr URL, while also providing a fallback link for users whose browsers don't support automatic redirects or have disabled this feature.
2 |
3 |
--------------------------------------------------------------------------------
/pdf-ocr.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Redirecting...
6 |
7 |
8 |
9 |
If you are not redirected automatically, follow this link to OCR.
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/php-deserializer.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts PHP serialized data to JSON format. Paste serialized PHP data in the input field and it will automatically be converted to formatted JSON, which you can then copy to your clipboard with a single click. If there are any errors in the deserialization process, the tool will display a helpful error message.
2 |
3 |
--------------------------------------------------------------------------------
/php-deserializer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | PHP deserializer
7 |
68 |
69 |
70 |
PHP deserializer
71 |
Paste serialized PHP data below to convert it to JSON
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/pipfile.docs.md:
--------------------------------------------------------------------------------
1 | This tool extracts and formats Python dependencies from a Pipfile.lock file. Paste your Pipfile.lock JSON content into the text area and click "Parse Dependencies" to convert it into both Pipfile format and requirements.txt format. You can copy either format to your clipboard using the designated buttons.
2 |
3 |
--------------------------------------------------------------------------------
/pomodoro.docs.md:
--------------------------------------------------------------------------------
1 | The Pomodoro Timer is a productivity tool that helps you manage work sessions using timed intervals. Enter your goal, select a duration (5-60 minutes), and track your progress with detailed session logs. The application records your session history including start/end times, durations, and any pauses, storing this data locally for future reference.
2 |
3 |
--------------------------------------------------------------------------------
/progress.docs.md:
--------------------------------------------------------------------------------
1 | This page tracks the progress of the current U.S. presidential term with a visual progress bar and statistical information. It displays days elapsed, days remaining, and the percentage of the term completed, while also tracking the time until midterm elections. The progress bar includes a marker showing when midterms occur during the presidential term, with additional statistics about the days until midterms and how far through the first half of the term we currently are.
2 |
3 |
--------------------------------------------------------------------------------
/prompts-js.docs.md:
--------------------------------------------------------------------------------
1 | Prompts.js is a JavaScript library that provides modern alternatives to the browser's built-in alert, confirm, and prompt dialogs. The library offers an async/await syntax allowing for cleaner code when working with user interactions. Demonstrations on this page show how to trigger different types of dialogs, with the results displayed in a dedicated area below the buttons.
2 |
3 |
--------------------------------------------------------------------------------
/prompts-js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Prompts.js
5 |
6 |
39 |
40 |
41 |
42 |
Prompts.js
43 |
A lightweight JavaScript library for creating modal dialogs with alert, confirm, and prompt functionalities.
44 |
45 | await Prompts.alert("This is an alert message!");
46 | const resultBoolean = await Prompts.confirm("Do you want to proceed?");
47 | const name = await Prompts.prompt("What is your name?");
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/python/extract_har.py:
--------------------------------------------------------------------------------
1 | # /// script
2 | # requires-python = ">=3.12"
3 | # dependencies = [
4 | # "click",
5 | # ]
6 | # ///
7 |
8 | import json
9 | import zipfile
10 | from pathlib import Path
11 | import click
12 | import mimetypes
13 | from urllib.parse import urlparse
14 |
15 |
16 | def get_extension_for_mimetype(mimetype):
17 | """Get the most common file extension for a given MIME type."""
18 | ext = mimetypes.guess_extension(mimetype)
19 | if ext:
20 | return ext
21 |
22 | # Fallback mappings for common types
23 | fallbacks = {
24 | "application/json": ".json",
25 | "image/svg+xml": ".svg",
26 | "text/html": ".html",
27 | "text/css": ".css",
28 | "application/javascript": ".js",
29 | }
30 | return fallbacks.get(mimetype, ".bin")
31 |
32 |
33 | def extract_path_from_url(url):
34 | """Convert a URL into a filesystem path, preserving the path structure."""
35 | parsed = urlparse(url)
36 | path = parsed.path.lstrip("/")
37 |
38 | # Handle empty paths
39 | if not path:
40 | path = "index"
41 |
42 | # Remove trailing slashes
43 | path = path.rstrip("/")
44 |
45 | return path
46 |
47 |
48 | @click.command()
49 | @click.argument("harzip", type=click.Path(exists=True))
50 | @click.argument("mimetypes", nargs=-1, required=True)
51 | @click.option(
52 | "-o",
53 | "--output",
54 | type=click.Path(),
55 | default=".",
56 | help="Output directory for extracted files",
57 | )
58 | @click.option(
59 | "--paths",
60 | is_flag=True,
61 | help="Use URL paths for filenames instead of original names",
62 | )
63 | @click.option(
64 | "--pretty-json",
65 | is_flag=True,
66 | help="Pretty print JSON files with 2-space indentation",
67 | )
68 | def extract_har(harzip, mimetypes, output, paths, pretty_json):
69 | """Extract files of specified MIME types from a HAR archive."""
70 | output_dir = Path(output)
71 | output_dir.mkdir(parents=True, exist_ok=True)
72 |
73 | with zipfile.ZipFile(harzip) as zf:
74 | # Read the HAR JSON file
75 | try:
76 | har_content = json.loads(zf.read("har.har"))
77 | except KeyError:
78 | click.echo("Error: har.har not found in archive", err=True)
79 | return
80 | except json.JSONDecodeError:
81 | click.echo("Error: Invalid JSON in har.har", err=True)
82 | return
83 |
84 | # Process each entry
85 | for entry in har_content.get("log", {}).get("entries", []):
86 | response = entry.get("response", {})
87 | content = response.get("content", {})
88 |
89 | # Check if this entry matches our MIME type filter
90 | if content.get("mimeType") not in mimetypes:
91 | continue
92 |
93 | # Get the file reference and URL
94 | file_ref = content.get("_file")
95 | if not file_ref:
96 | continue
97 |
98 | request_url = entry.get("request", {}).get("url", "")
99 |
100 | try:
101 | # Extract the file
102 | file_content = zf.read(file_ref)
103 |
104 | if paths:
105 | # Use URL path for filename
106 | path = extract_path_from_url(request_url)
107 | # Add appropriate extension if not present
108 | if not Path(path).suffix:
109 | path += get_extension_for_mimetype(content["mimeType"])
110 | outpath = output_dir / path
111 | else:
112 | # Use original filename
113 | outpath = output_dir / file_ref
114 |
115 | # Ensure parent directories exist
116 | outpath.parent.mkdir(parents=True, exist_ok=True)
117 |
118 | # Handle JSON pretty printing if requested
119 | if pretty_json and content["mimeType"] == "application/json":
120 | try:
121 | json_data = json.loads(file_content)
122 | file_content = json.dumps(json_data, indent=2).encode("utf-8")
123 | except json.JSONDecodeError:
124 | click.echo(
125 | f"Warning: Could not pretty print {outpath} - invalid JSON",
126 | err=True,
127 | )
128 |
129 | # Write the file
130 | outpath.write_bytes(file_content)
131 | click.echo(f"Extracted: {outpath}")
132 |
133 | except KeyError:
134 | click.echo(f"Warning: File {file_ref} not found in archive", err=True)
135 | continue
136 |
137 |
138 | if __name__ == "__main__":
139 | extract_har()
140 |
--------------------------------------------------------------------------------
/python/extract_sourcemap.py:
--------------------------------------------------------------------------------
1 | # /// script
2 | # requires-python = ">=3.12"
3 | # dependencies = [
4 | # "click",
5 | # ]
6 | # ///
7 |
8 | import re
9 | import base64
10 | import sys
11 | import click
12 |
13 |
14 | @click.command()
15 | @click.argument("file_path", type=click.Path(exists=True))
16 | def extract_sourcemap(file_path):
17 | """
18 | Extract and decode a base64-encoded source map from a JavaScript file.
19 |
20 | This tool searches for sourceMappingURL with base64-encoded data and outputs
21 | the decoded content to stdout.
22 | """
23 | try:
24 | # Read the JavaScript file
25 | with open(file_path, "r", encoding="utf-8") as f:
26 | content = f.read()
27 |
28 | # Regular expression to find the sourceMappingURL with base64 data
29 | pattern = r"//# sourceMappingURL=data:application/json;base64,([a-zA-Z0-9+/=]+)"
30 | match = re.search(pattern, content)
31 |
32 | if match:
33 | # Extract the base64 encoded part
34 | base64_data = match.group(1)
35 |
36 | # Decode the base64 data
37 | decoded_data = base64.b64decode(base64_data).decode("utf-8")
38 |
39 | # Print the decoded content to stdout
40 | click.echo(decoded_data)
41 |
42 | return 0
43 | else:
44 | click.echo("No source map data found in the file.", err=True)
45 | return 1
46 |
47 | except Exception as e:
48 | click.echo(f"Error: {str(e)}", err=True)
49 | return 1
50 |
51 |
52 | if __name__ == "__main__":
53 | sys.exit(extract_sourcemap())
54 |
--------------------------------------------------------------------------------
/python/gguf_inspect.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """
3 | gguf_inspect.py – Dump ALL metadata key/value pairs from a GGUF file.
4 |
5 | • Default output: YAML (each value in a literal block scalar).
6 | • --json Pretty-print JSON instead.
7 | • --exclude PREFIX Skip keys that begin with PREFIX. May be repeated.
8 |
9 | Example:
10 | ./gguf_dump_meta.py llama.gguf # YAML
11 | ./gguf_dump_meta.py --json llama.gguf # JSON
12 | ./gguf_dump_meta.py --exclude tokenizer.ggml. llama.gguf # YAML, filtered
13 | """
14 |
15 | import argparse
16 | import json
17 | import struct
18 | import sys
19 | from pathlib import Path
20 | from typing import Any, Dict, List
21 |
22 | # ──────────────────────────────────────────────────────
23 | # gguf helpers
24 | # ──────────────────────────────────────────────────────
25 |
26 | _PRIM_FMT = {
27 | 0: "B", 1: "b",
28 | 2: "H", 3: "h",
29 | 4: "I", 5: "i",
30 | 6: "f",
31 | 7: "B", # BOOL
32 | 10: "Q", 11: "q",
33 | 12: "d",
34 | }
35 | _PRIM_SIZE = {k: struct.calcsize(v) for k, v in _PRIM_FMT.items()}
36 |
37 | STRING, ARRAY = 8, 9
38 |
39 |
40 | def _read(fmt: str, fh):
41 | size = struct.calcsize(fmt)
42 | data = fh.read(size)
43 | if len(data) != size:
44 | raise EOFError("unexpected EOF")
45 | return struct.unpack("<" + fmt, data)
46 |
47 |
48 | def _read_string(fh) -> str:
49 | (length,) = _read("Q", fh)
50 | raw = fh.read(length)
51 | if len(raw) != length:
52 | raise EOFError("unexpected EOF in string")
53 | return raw.decode("utf-8", errors="replace")
54 |
55 |
56 | def _read_scalar(typ: int, fh):
57 | (val,) = _read(_PRIM_FMT[typ], fh)
58 | return bool(val) if typ == 7 else val
59 |
60 |
61 | def _read_array(elem_type: int, count: int, fh) -> List[Any]:
62 | if elem_type == STRING:
63 | return [_read_string(fh) for _ in range(count)]
64 |
65 | elem_size = _PRIM_SIZE[elem_type]
66 | data = fh.read(elem_size * count)
67 | if len(data) != elem_size * count:
68 | raise EOFError("unexpected EOF in array")
69 | fmt = f"<{count}{_PRIM_FMT[elem_type]}"
70 | out = list(struct.unpack(fmt, data))
71 | if elem_type == 7:
72 | out = [bool(v) for v in out]
73 | return out
74 |
75 |
76 | def extract_metadata(path: Path) -> Dict[str, Any]:
77 | meta: Dict[str, Any] = {}
78 | with path.open("rb") as fh:
79 | if fh.read(4) != b"GGUF":
80 | raise ValueError("not a GGUF file")
81 | _version, = _read("I", fh)
82 | _ntensors, n_kv = _read("QQ", fh)
83 |
84 | for _ in range(n_kv):
85 | key = _read_string(fh)
86 | (val_type,) = _read("I", fh)
87 |
88 | if val_type == STRING:
89 | value = _read_string(fh)
90 | elif val_type == ARRAY:
91 | elem_type, = _read("I", fh)
92 | (count,) = _read("Q", fh)
93 | value = _read_array(elem_type, count, fh)
94 | else:
95 | value = _read_scalar(val_type, fh)
96 |
97 | meta[key] = value
98 | return meta
99 |
100 |
101 | # ──────────────────────────────────────────────────────
102 | # Output helpers
103 | # ──────────────────────────────────────────────────────
104 |
105 | def dump_yaml(meta: Dict[str, Any]) -> None:
106 | for key in sorted(meta):
107 | print(f"{key}: |")
108 | lines = json.dumps(meta[key], ensure_ascii=False, indent=2).splitlines()
109 | for ln in lines or [""]:
110 | print(f" {ln}")
111 | print() # blank line between keys
112 |
113 |
114 | def dump_json(meta: Dict[str, Any]) -> None:
115 | print(json.dumps(meta, ensure_ascii=False, indent=2))
116 |
117 |
118 | # ──────────────────────────────────────────────────────
119 | # CLI
120 | # ──────────────────────────────────────────────────────
121 |
122 | def parse_args():
123 | p = argparse.ArgumentParser(
124 | description="Dump metadata key/value pairs from a GGUF file."
125 | )
126 | p.add_argument(
127 | "--json",
128 | action="store_true",
129 | help="output as pretty-printed JSON instead of YAML",
130 | )
131 | p.add_argument(
132 | "--exclude",
133 | action="append",
134 | default=[],
135 | metavar="PREFIX",
136 | help="exclude keys that start with PREFIX (can be given multiple times)",
137 | )
138 | p.add_argument("gguf", metavar="MODEL.GGUF", help="path to GGUF file")
139 | return p.parse_args()
140 |
141 |
142 | def should_include(key: str, prefixes: List[str]) -> bool:
143 | return not any(key.startswith(pref) for pref in prefixes)
144 |
145 |
146 | def main():
147 | args = parse_args()
148 |
149 | path = Path(args.gguf)
150 | if not path.is_file():
151 | sys.exit(f"File not found: {path}")
152 |
153 | meta = extract_metadata(path)
154 | if args.exclude:
155 | meta = {k: v for k, v in meta.items() if should_include(k, args.exclude)}
156 |
157 | if args.json:
158 | dump_json(meta)
159 | else:
160 | dump_yaml(meta)
161 |
162 |
163 | if __name__ == "__main__":
164 | main()
165 |
--------------------------------------------------------------------------------
/python/highlight.py:
--------------------------------------------------------------------------------
1 | # /// script
2 | # requires-python = ">=3.9"
3 | # dependencies = [
4 | # "click",
5 | # ]
6 | # ///
7 |
8 | import sys
9 | import re
10 | from dataclasses import dataclass
11 | from typing import List, Union, Tuple
12 | import click
13 |
14 |
15 | @dataclass
16 | class Text:
17 | """Represents non-matching text"""
18 |
19 | content: str
20 |
21 |
22 | @dataclass
23 | class Match:
24 | """Represents matching text that should be highlighted"""
25 |
26 | content: str
27 |
28 |
29 | @dataclass
30 | class Ellipsis:
31 | """Represents ... for truncated content"""
32 |
33 | pass
34 |
35 |
36 | def find_matches(
37 | text: str, terms: List[str], context: int = 30
38 | ) -> List[Union[Text, Match, Ellipsis]]:
39 | """
40 | Find all matches of the given terms in the text, with surrounding context.
41 | Returns a list of Text, Match, and Ellipsis objects.
42 | """
43 | if not terms:
44 | return [Text(text)]
45 |
46 | # Create pattern that matches any of the terms
47 | pattern = "|".join(re.escape(term) for term in terms)
48 | regex = re.compile(pattern, re.IGNORECASE)
49 |
50 | # Find all matches with their positions
51 | matches = list(regex.finditer(text))
52 | if not matches:
53 | return [Text(text)]
54 |
55 | # Create segments with context
56 | segments: List[Tuple[int, int]] = [] # List of (start, end) for context regions
57 | match_positions: List[Tuple[int, int]] = (
58 | []
59 | ) # List of (start, end) for actual matches
60 |
61 | for match in matches:
62 | start, end = match.span()
63 | match_positions.append((start, end))
64 | context_start = max(0, start - context)
65 | context_end = min(len(text), end + context)
66 | segments.append((context_start, context_end))
67 |
68 | # Merge overlapping context segments
69 | merged_segments = []
70 | current = segments[0]
71 | for next_seg in segments[1:]:
72 | if next_seg[0] <= current[1]:
73 | # Segments overlap, merge them
74 | current = (current[0], max(current[1], next_seg[1]))
75 | else:
76 | merged_segments.append(current)
77 | current = next_seg
78 | merged_segments.append(current)
79 |
80 | # Convert segments to result objects
81 | result = []
82 | last_end = 0
83 |
84 | for seg_start, seg_end in merged_segments:
85 | # Add ellipsis if there's a gap
86 | if seg_start > last_end:
87 | if last_end > 0: # Only add if not at the start
88 | result.append(Ellipsis())
89 |
90 | # Find all matches within this segment
91 | segment_matches = [
92 | (s, e) for s, e in match_positions if s >= seg_start and e <= seg_end
93 | ]
94 |
95 | # Add content with proper highlighting
96 | pos = seg_start
97 | for match_start, match_end in segment_matches:
98 | # Add text before match
99 | if match_start > pos:
100 | result.append(Text(text[pos:match_start]))
101 | # Add match
102 | result.append(Match(text[match_start:match_end]))
103 | pos = match_end
104 |
105 | # Add remaining text in segment
106 | if pos < seg_end:
107 | result.append(Text(text[pos:seg_end]))
108 |
109 | last_end = seg_end
110 |
111 | # Add final ellipsis if we're not at the end
112 | if last_end < len(text):
113 | result.append(Ellipsis())
114 |
115 | return result
116 |
117 |
118 | @click.command()
119 | @click.argument("terms", nargs=-1, required=True)
120 | @click.option(
121 | "-c", "--context", default=30, help="Number of context characters around matches"
122 | )
123 | def main(terms: List[str], context: int):
124 | """Search for terms in stdin and output colored matches with context."""
125 | text = sys.stdin.read()
126 | results = find_matches(text, terms, context)
127 |
128 | # ANSI color codes
129 | YELLOW = "\033[93m"
130 | RED = "\033[91m"
131 | END = "\033[0m"
132 |
133 | # Convert results to colored output
134 | for item in results:
135 | if isinstance(item, Text):
136 | print(item.content, end="")
137 | elif isinstance(item, Match):
138 | print(f"{RED}{item.content}{END}", end="")
139 | elif isinstance(item, Ellipsis):
140 | print(f"{YELLOW}...{END}", end="")
141 | print() # Final newline
142 |
143 |
144 | if __name__ == "__main__":
145 | main()
146 |
--------------------------------------------------------------------------------
/python/json_extractor.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import json
3 | import sys
4 | import argparse
5 |
6 |
7 | def extract_json_objects(text):
8 | """
9 | Extract all valid JSON objects from text using a state machine parser.
10 |
11 | Returns a list of valid JSON objects as Python dictionaries.
12 | """
13 | result = []
14 | i = 0
15 | text_length = len(text)
16 |
17 | while i < text_length:
18 | # Look for the start of a potential JSON object
19 | if text[i] == "{":
20 | # Try to parse a complete JSON object starting at this position
21 | potential_json, end_pos = parse_json_object(text, i)
22 | if potential_json is not None:
23 | try:
24 | # Directly parse without fixing - invalid JSON will be rejected
25 | parsed_obj = json.loads(potential_json)
26 | result.append(parsed_obj)
27 | except json.JSONDecodeError:
28 | pass
29 | # Skip to the end of this object to avoid finding nested objects
30 | i = end_pos
31 | else:
32 | i += 1
33 | else:
34 | i += 1
35 |
36 | return result
37 |
38 |
39 | def parse_json_object(text, start_pos):
40 | """
41 | Parse a JSON object starting at start_pos.
42 |
43 | Returns (json_string, end_position) if successful, or (None, start_pos) if not.
44 | """
45 | i = start_pos
46 | if i >= len(text) or text[i] != "{":
47 | return None, start_pos
48 |
49 | # State variables
50 | brace_count = 1 # We already found the first '{'
51 | in_string = False
52 | escape_next = False
53 |
54 | # Start accumulating the JSON string
55 | json_str = text[i]
56 | i += 1
57 |
58 | while i < len(text) and brace_count > 0:
59 | char = text[i]
60 | json_str += char
61 |
62 | # Handle string state
63 | if in_string:
64 | if escape_next:
65 | escape_next = False
66 | elif char == "\\":
67 | escape_next = True
68 | elif char == '"':
69 | in_string = False
70 | else:
71 | # Only count braces outside of strings
72 | if char == '"':
73 | in_string = True
74 | elif char == "{":
75 | brace_count += 1
76 | elif char == "}":
77 | brace_count -= 1
78 | # If we've closed all braces, we have a potential complete JSON object
79 | if brace_count == 0:
80 | return json_str, i + 1
81 |
82 | i += 1
83 |
84 | # If we got here, we didn't find a complete JSON object
85 | return None, start_pos
86 |
87 |
88 | def main():
89 | # Parse command line arguments
90 | parser = argparse.ArgumentParser(description="Extract JSON objects from text.")
91 | parser.add_argument(
92 | "file",
93 | nargs="?",
94 | type=str,
95 | default=None,
96 | help="File to read (defaults to stdin)",
97 | )
98 | parser.add_argument(
99 | "--all",
100 | "-a",
101 | action="store_true",
102 | help="Output all JSON objects found, not just the best one",
103 | )
104 | args = parser.parse_args()
105 |
106 | # Read the input
107 | if args.file:
108 | try:
109 | with open(args.file, "r") as f:
110 | text = f.read()
111 | except Exception as e:
112 | print(f"Error reading file {args.file}: {e}", file=sys.stderr)
113 | sys.exit(1)
114 | else:
115 | text = sys.stdin.read()
116 |
117 | # Extract JSON objects
118 | json_objects = extract_json_objects(text)
119 |
120 | # Output the results
121 | if not json_objects:
122 | print("No valid JSON objects found.", file=sys.stderr)
123 | sys.exit(1)
124 |
125 | if args.all:
126 | # Output all objects
127 | output = json_objects
128 | else:
129 | # Output the best (most complex) object
130 | output = max(json_objects, key=lambda x: len(json.dumps(x)))
131 |
132 | # Always print with 2-space indent
133 | print(json.dumps(output, indent=2))
134 |
135 |
136 | if __name__ == "__main__":
137 | main()
138 |
--------------------------------------------------------------------------------
/python/whitespace_cleaner.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # /// script
3 | # requires-python = ">=3.12"
4 | # ///
5 |
6 | import os
7 | import sys
8 | import argparse
9 | from pathlib import Path
10 |
11 |
12 | def is_text_file(file_path):
13 | """
14 | Check if a file is a text file by attempting to read it as UTF-8.
15 | """
16 | try:
17 | with open(file_path, "r", encoding="utf-8") as f:
18 | f.read(1024) # Try reading a small chunk
19 | return True
20 | except UnicodeDecodeError:
21 | return False
22 | except Exception:
23 | return False
24 |
25 |
26 | def process_file(file_path, dry_run=False):
27 | """
28 | Process a single file, replacing lines with only whitespace with empty lines.
29 | Returns a tuple of (file_path, changes_made, lines_changed)
30 | """
31 | if not is_text_file(file_path):
32 | return file_path, False, 0
33 |
34 | try:
35 | with open(file_path, "r", encoding="utf-8") as f:
36 | lines = f.readlines()
37 |
38 | changes_needed = False
39 | lines_changed = 0
40 | new_lines = []
41 |
42 | for line in lines:
43 | # Check if the line contains only whitespace characters (spaces, tabs)
44 | # but is not already just a newline
45 | if line.strip() == "" and line != "\n":
46 | new_lines.append("\n")
47 | changes_needed = True
48 | lines_changed += 1
49 | else:
50 | new_lines.append(line)
51 |
52 | if changes_needed and not dry_run:
53 | with open(file_path, "w", encoding="utf-8") as f:
54 | f.writelines(new_lines)
55 |
56 | return file_path, changes_needed, lines_changed
57 |
58 | except Exception as e:
59 | print(f"Error processing {file_path}: {e}", file=sys.stderr)
60 | return file_path, False, 0
61 |
62 |
63 | def main():
64 | parser = argparse.ArgumentParser(
65 | description="Replace whitespace-only lines with blank lines in text files."
66 | )
67 | parser.add_argument(
68 | "paths", nargs="+", help="One or more files or directories to process"
69 | )
70 | parser.add_argument(
71 | "--dry-run",
72 | action="store_true",
73 | help="Show what would be changed without making changes",
74 | )
75 |
76 | args = parser.parse_args()
77 |
78 | total_files_processed = 0
79 | total_files_changed = 0
80 | total_lines_changed = 0
81 |
82 | for path_str in args.paths:
83 | path = Path(path_str)
84 |
85 | if not path.exists():
86 | print(f"Path does not exist: {path}", file=sys.stderr)
87 | continue
88 |
89 | if path.is_file():
90 | # Process a single file
91 | filepath, changed, lines = process_file(path, args.dry_run)
92 | total_files_processed += 1
93 | if changed:
94 | total_files_changed += 1
95 | total_lines_changed += lines
96 | action = "Would replace" if args.dry_run else "Replaced"
97 | print(f"{action} {lines} whitespace-only line(s) in {filepath}")
98 |
99 | elif path.is_dir():
100 | # Recursively process a directory
101 | for root, _, files in os.walk(path):
102 | for file in files:
103 | filepath = os.path.join(root, file)
104 | filepath_obj = Path(filepath)
105 |
106 | # Skip hidden files and directories
107 | if filepath_obj.name.startswith(".") or any(
108 | part.startswith(".") for part in filepath_obj.parts
109 | ):
110 | continue
111 |
112 | filepath, changed, lines = process_file(filepath, args.dry_run)
113 | total_files_processed += 1
114 | if changed:
115 | total_files_changed += 1
116 | total_lines_changed += lines
117 | action = "Would replace" if args.dry_run else "Replaced"
118 | print(f"{action} {lines} whitespace-only line(s) in {filepath}")
119 |
120 | # Print summary
121 | print(f"\nSummary:")
122 | print(f"Files processed: {total_files_processed}")
123 |
124 | if args.dry_run:
125 | print(f"Files that would be changed: {total_files_changed}")
126 | print(f"Lines that would be changed: {total_lines_changed}")
127 | else:
128 | print(f"Files changed: {total_files_changed}")
129 | print(f"Lines changed: {total_lines_changed}")
130 |
131 |
132 | if __name__ == "__main__":
133 | main()
134 |
--------------------------------------------------------------------------------
/qr.docs.md:
--------------------------------------------------------------------------------
1 | This QR Code Decoder allows you to extract text and links from QR code images. Upload an image file, drag and drop it onto the designated area, or paste an image from your clipboard. The tool automatically processes the image and displays the decoded content, converting URLs into clickable links for easy access.
2 |
3 |
--------------------------------------------------------------------------------
/render-claude-citations.docs.md:
--------------------------------------------------------------------------------
1 | This tool renders JSON responses from Claude API that contain citations. Paste a JSON response into the text area and click "Render message" to display the message with proper formatting and citation blockquotes. The renderer uses a sandboxed iframe for security, ensuring the content is displayed safely while maintaining proper styling and organization of the text and cited passages.
2 |
3 |
--------------------------------------------------------------------------------
/render-markdown.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts Markdown to HTML using GitHub's API. Enter your Markdown text in the input area, click "Render" to see it transformed into HTML with proper formatting. You can customize the output by choosing between standard Markdown or GitHub Flavored Markdown, and optionally clean up the HTML to remove hidden elements. The rendered HTML can be copied to the clipboard.
2 |
3 |
--------------------------------------------------------------------------------
/rtf-to-html.docs.md:
--------------------------------------------------------------------------------
1 | This tool inspects and converts Rich Text Format (RTF) content from your clipboard into HTML with color formatting preserved. When you paste RTF text using keyboard shortcuts, the application parses the RTF color table, applies appropriate color styling to text segments, and displays both a visual preview and the generated HTML markup for you to copy.
2 |
3 |
--------------------------------------------------------------------------------
/rtf-to-html.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Clipboard RTF to HTML Inspector
7 |
21 |
22 |
23 |
Clipboard RTF to HTML Inspector
24 |
25 |
26 |
Press Cmd+V (Mac) or Ctrl+V (Windows) to paste and convert RTF to colorized HTML.
27 |
28 |
29 |
30 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/schema-dsl.docs.md:
--------------------------------------------------------------------------------
1 | This tool converts simple schema definitions written in a compact DSL format into JSON Schema. Enter field definitions with types and descriptions in the text area, and the converter generates the appropriate JSON Schema in real-time. Toggle the "Array items schema" checkbox to wrap your schema in an array structure, and use the "Load example" button to see how the syntax works.
2 |
3 |
--------------------------------------------------------------------------------
/side-panel-dialog.docs.md:
--------------------------------------------------------------------------------
1 | This page displays a product catalog with a side panel dialog system. When users click on a product, details appear in a side panel implemented using the HTML `