Thanks for the script!
OK, well, here’s a Typescript version ported by ChatGPT and briefly reviewed by me:
I believe it is equivalent to your original script, but let me know if it missed any nuances. Here’s a quick test:
It’s really just two files:
index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Document Processor</title>
</head>
<body>
<h1>Document Processor</h1>
<div>
<label for="mainText">Main Document:</label><br />
<!-- Textarea accepts direct input for the main document -->
<textarea id="mainText" rows="10" cols="60" placeholder="Type or paste your main document here"></textarea>
<br />
<!-- File input to upload the main document -->
<input type="file" id="mainFile" accept=".txt" />
</div>
<div>
<label for="wordListText">Word List (comma-separated):</label><br />
<!-- Textarea accepts direct input for the word list -->
<textarea id="wordListText" rows="4" cols="60" placeholder="Type or paste your comma-separated word list here"></textarea>
<br />
<!-- File input to upload the word list -->
<input type="file" id="wordListFile" accept=".txt" />
</div>
<div>
<h2>Output</h2>
<!-- Readonly output field that will display the processed document -->
<textarea id="outputText" rows="10" cols="60" readonly></textarea>
<br />
<!-- Button to download the output -->
<button id="downloadBtn">Download Output</button>
</div>
<!-- The Vite app will load our main TypeScript module -->
<script type="module" src="/src/main.ts"></script>
</body>
</html>
And the code at src/main.ts
:
// src/main.ts
// ---------------------------------------------------------
// Utility function to escape RegExp special characters.
// This function is analogous to Python's `re.escape`.
// In Python, you would write: pattern = r'\b' + re.escape(word) + r'\b'
// In TypeScript/JavaScript, we have to create our own escape function.
function escapeRegExp(text: string): string {
// Replace any special regex character with an escaped version.
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// ---------------------------------------------------------
// Main processing function that mirrors the Python version.
// Instead of file paths, it receives the file contents as strings.
// In Python, file I/O is done with open(), but here we use the FileReader API.
function processDocument(
mainContent: string,
wordListContent: string,
paragraphSeparator = "\n\n"
): string {
// Split the comma-separated word list.
// Python does: content.split(',') and list comprehension to strip whitespace.
const wordsToFind: string[] = wordListContent
.split(',')
.map(word => word.trim())
.filter(word => word.length > 0);
// Split the main document into paragraphs.
const paragraphs: string[] = mainContent.split(paragraphSeparator);
const modifiedParagraphs: string[] = [];
// Process each paragraph.
paragraphs.forEach(paragraph => {
// Split into lines.
const lines: string[] = paragraph.split('\n');
// If there are fewer than 3 lines, leave the paragraph unchanged.
if (lines.length < 3) {
modifiedParagraphs.push(paragraph);
return;
}
// Keep the first two lines unchanged.
const firstTwoLines = lines.slice(0, 2);
const remainingLines = lines.slice(2);
// Join the remaining lines back together.
let remainingText = remainingLines.join('\n');
// Track words already replaced in this paragraph.
const wordsFound = new Set<string>();
// For each word to find...
wordsToFind.forEach(word => {
if (wordsFound.has(word)) return;
// Create a regular expression with word boundaries.
// Notice: Unlike Python which uses the re module, in JS/TS we use RegExp.
// No "count" parameter exists in JS replace—if the regex is not global,
// only the first occurrence is replaced by default.
const pattern = new RegExp(`\\b${escapeRegExp(word)}\\b`);
// Check if the word exists. In Python: re.search(pattern, text)
if (pattern.test(remainingText)) {
wordsFound.add(word);
// Replace only the first occurrence—JS replace (without /g flag) does that.
remainingText = remainingText.replace(pattern, `[[${word}]]`);
}
});
// Reconstruct the paragraph.
const modifiedParagraph = [...firstTwoLines, remainingText].join('\n');
modifiedParagraphs.push(modifiedParagraph);
});
// Join all paragraphs back together.
return modifiedParagraphs.join(paragraphSeparator);
}
// ---------------------------------------------------------
// Function to update the output text area based on current inputs.
function updateOutput() {
const mainTextArea = document.getElementById("mainText") as HTMLTextAreaElement;
const wordListTextArea = document.getElementById("wordListText") as HTMLTextAreaElement;
const outputTextArea = document.getElementById("outputText") as HTMLTextAreaElement;
// Process document using current content from the text areas.
const processedContent = processDocument(mainTextArea.value, wordListTextArea.value);
outputTextArea.value = processedContent;
}
// ---------------------------------------------------------
// Set up event listeners after the DOM is loaded.
window.addEventListener("DOMContentLoaded", () => {
// Retrieve HTML elements and cast them to the correct types.
const mainTextArea = document.getElementById("mainText") as HTMLTextAreaElement;
const wordListTextArea = document.getElementById("wordListText") as HTMLTextAreaElement;
const mainFileInput = document.getElementById("mainFile") as HTMLInputElement;
const wordListFileInput = document.getElementById("wordListFile") as HTMLInputElement;
const downloadBtn = document.getElementById("downloadBtn") as HTMLButtonElement;
// Update output in real-time when text is entered.
mainTextArea.addEventListener("input", updateOutput);
wordListTextArea.addEventListener("input", updateOutput);
// Handle main document file upload.
mainFileInput.addEventListener("change", event => {
const target = event.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
const file = target.files[0];
const reader = new FileReader();
reader.onload = () => {
// Set the file content into the text area.
mainTextArea.value = reader.result as string;
updateOutput();
};
reader.readAsText(file);
}
});
// Handle word list file upload.
wordListFileInput.addEventListener("change", event => {
const target = event.target as HTMLInputElement;
if (target.files && target.files.length > 0) {
const file = target.files[0];
const reader = new FileReader();
reader.onload = () => {
// Set the file content into the text area.
wordListTextArea.value = reader.result as string;
updateOutput();
};
reader.readAsText(file);
}
});
// Allow downloading the processed output.
downloadBtn.addEventListener("click", () => {
const outputTextArea = document.getElementById("outputText") as HTMLTextAreaElement;
const blob = new Blob([outputTextArea.value], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "output.txt"; // Default file name for download.
a.click();
URL.revokeObjectURL(url);
});
// Run an initial processing in case there is default content.
updateOutput();
});
/*
----------------------------------------------------------------
Major differences between TypeScript and Python in this code:
1. **Type Annotations**:
- TypeScript uses explicit type annotations (e.g., `string`, `HTMLTextAreaElement`)
to help catch errors at compile time.
- In Python, the language is dynamically typed and such declarations are not required.
2. **Regular Expression Handling**:
- In Python, regular expressions are managed with the `re` module
using functions like `re.search()`, `re.sub()`, and `re.escape()`.
- In TypeScript/JavaScript, regular expressions are built into the language
using the `RegExp` object. We manually define an `escapeRegExp` function to
safely escape special characters—this is similar to `re.escape` in Python.
- Also note: In JavaScript, calling `string.replace(regex, ...)` without the global (/g)
flag replaces only the first occurrence, matching the behavior of Python’s
`re.sub()` with `count=1`.
3. **String Manipulation**:
- Both languages support methods like `split`, `join`, and slicing.
- TypeScript manipulates strings and arrays explicitly (for example, using `slice`
to extract parts of arrays), which is shown when splitting paragraphs into lines.
- The spread operator (`...`) is used in TypeScript to combine arrays, a feature
not available in Python.
4. **File Handling**:
- Python performs file I/O using built-in open/read/write methods.
- In the browser (with TypeScript), file reading is done using the asynchronous
`FileReader` API. This avoids any network traffic since files are processed locally.
----------------------------------------------------------------
*/
It took ChatGPT a couple of seconds to write. I gave it the following prompt and then deployed it to Vercel.com as a free app.
Prompt used to convert code from Python to Typescript
Can you please help me convert this into a single-page Vite app in TypeScript, keeping the code as simple as possible for a beginner, and highlighting (in comments) any major differences between TS and Python, especially in their regular expression handling or string manipulation?
The TypeScript version should run in a browser window instead, and load the main file and word list from an Browse/Upload button and show them in a text box. The text boxes should also accept direct input (via typing or copy and paste). Then the output textbox will show the modified version, and provide an optional download button.
The string manipulation should happen in real-time on the browser directly. There should be no network traffic needed, as nothing needs to be sent to the server.
For this port, please don’t use any third-party libs so that the learner can learn basic TS.
Here’s the Python script:
[…]
I know there’s a lot in there you’ve probably never seen, but I just wanted to show you a basic example about how quick (and cheap) it would be to convert simple Python into a web app.
Is this helpful at all? If so, I’d be glad to break it down into more basic parts for you to learn, step-by-step (or of course you can also just ask an AI — it’s very good at explaining things like that).
But the basic parts of it are:
- The
index.html
describes the overall layout of the page. It’s where you define the text fields and buttons and such.
- The
main.ts
script is the actual code. TS is short for TypeScript, which is a variant of JavaScript that strongly defines each variable to be a certain type (like string[]
in TypeScript would be a list of strings in Python).
- Vercel is a free (for personal use) web host to deploy simple projects to.
- The “Vite” I mentioned is just a scaffolding tool to make developing and deploying TypeScript easier… it automates a bunch of boilerplate.
I’m happy to do a more thorough explanation if this approach seems reasonable. The main benefit of this approach is that it’s totally free, fast, and doesn’t require any downloads. Anybody who can access that URL can use the app without any signed executables to worry about, and their system security settings won’t matter. Nothing gets sent to the server, it’s all just processed directly in the browser. The downside, of course, is that it’s not Python.
But if you don’t like this approach and would rather find a way to directly deploy the Python script to the web, I’ll explore that option more tomorrow or later this week (if someone else doesn’t chime in first).