Files
playground/grammar.html
T
2026-07-24 15:58:31 +02:00

252 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Text Corrector App</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsdiff/5.1.0/diff.min.js"></script>
<style>
body {
font-family: "JetBrains Mono", Menlo, Consolas, monospace;
font-optical-sizing: auto;
font-size: 16px;
padding: 2rem;
margin: 0;
line-height: 1.5;
}
h2 {
font-size: 1rem;
font-weight: bold;
margin-bottom: 1rem;
line-height: 1.2;
}
details {
margin-bottom: 1rem;
}
summary > h2 {
display: inline;
}
button {
padding: 0.75rem 1.5rem;
-webkit-appearance: none;
appearance: none;
background-color: #222;
color: #f9f9f9;
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
font-size: inherit;
}
/* Minimal styling for diff readability */
#diffOutput ins {
background-color: #d4ffd4;
text-decoration: none;
color: #22863a;
}
#diffOutput del {
background-color: #ffd4d4;
text-decoration: none;
color: #cb2431;
}
.text-container {
border: 1px solid #eee;
margin-bottom: 1rem;
white-space: pre-wrap;
word-wrap: break-word;
}
#loadingMessage,
#errorMessage {
margin-bottom: 15px;
}
</style>
</head>
<body>
<div>
<details>
<summary><h2>Original Text</h2></summary>
<div id="originalText" class="text-container"></div>
</details>
</div>
<div id="loadingMessage">Loading suggestion...</div>
<div id="errorMessage" style="color: red; display: none"></div>
<div id="suggestionArea" style="display: none">
<div>
<h2>Suggestion (Diff View):</h2>
<div id="diffOutput" class="text-container"></div>
</div>
<div>
<details>
<summary><h2>Suggested Text (Clean)</h2></summary>
<div id="cleanSuggestionOutput" class="text-container"></div>
</details>
</div>
<button id="acceptButton" disabled>Accept</button>
</div>
<script>
// DOM Elements
const originalTextEl = document.getElementById("originalText");
const loadingMessageEl = document.getElementById("loadingMessage");
const errorMessageEl = document.getElementById("errorMessage");
const suggestionAreaEl = document.getElementById("suggestionArea");
const diffOutputEl = document.getElementById("diffOutput");
const cleanSuggestionOutputEl = document.getElementById("cleanSuggestionOutput");
const acceptButtonEl = document.getElementById("acceptButton");
let currentSuggestedText = null;
async function getChatGPTSuggestion(textToReview, apiKey) {
// System prompt: Instructions for the AI model
const systemPrompt = `You are an English language assistant. Your task is to help a user who is learning English.
When presented with their text, please:
1. Identify and correct grammatical errors. Pay specific attention to:
- Missing or incorrect articles (a, an, the).
- Incorrect preposition usage.
- Errors in subject-verb agreement.
- Past tense and past perfect tense mistakes.
- Awkward or unnatural phrasing.
2. Rephrase sentences to be simpler, clearer, and more natural, while preserving the original meaning.
3. Preserve the original formatting (new lines, punctuation, etc.) as much as possible, unless a change is necessary for clarity or correctness.
4. Provide ONLY the fully revised text. Do not include any explanations, apologies, preambles, or surrounding quotes like """ or \`\`\`. Just the corrected and improved plain text.
5. If the text is already grammatically perfect and clearly phrased according to these instructions, return the original text without any changes.`;
try {
const response = await fetch("https://api.openai.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${apiKey}`,
},
body: JSON.stringify({
// Assuming window.env.OPENAI_MODEL is available as in your snippet
// If not, replace with a hardcoded model or pass it as an argument
model: window.env && window.env.OPENAI_MODEL ? window.env.OPENAI_MODEL : "gpt-5.1-nano",
messages: [
{
role: "system",
content: systemPrompt,
},
{
role: "user",
content: textToReview,
},
],
temperature: 0.8,
max_tokens: Math.max(250, textToReview.length * 2 + 50),
}),
});
if (!response.ok) {
const errorData = await response.json();
console.error("ChatGPT API Error:", errorData);
return { error: `API Error: ${errorData.error?.message || response.statusText}` };
}
const data = await response.json();
if (data.choices && data.choices.length > 0 && data.choices[0].message) {
return { suggestion: data.choices[0].message.content.trim() };
} else {
console.error("ChatGPT API Error: No choices in response", data);
return { error: "No suggestion received from API (empty response)." };
}
} catch (error) {
console.error("Error fetching ChatGPT suggestion:", error);
return { error: `Network/Request Error: ${error.message}` };
}
}
function displayDiff(original, revised, diffContainer) {
if (typeof Diff === "undefined" || !Diff.diffWordsWithSpace) {
diffContainer.textContent = "jsdiff library not loaded or diffWordsWithSpace not working.";
console.error("jsdiff (Diff.diffWordsWithSpace) is not available.");
return;
}
const diffs = Diff.diffWordsWithSpace(original, revised, { ignoreCase: true, ignoreWhitespace: true });
const fragment = document.createDocumentFragment();
diffs.forEach(function (part) {
const textNode = document.createTextNode(part.value);
if (part.added) {
const ins = document.createElement("ins");
ins.appendChild(textNode);
fragment.appendChild(ins);
} else if (part.removed) {
const del = document.createElement("del");
del.appendChild(textNode);
fragment.appendChild(del);
} else {
fragment.appendChild(textNode);
}
});
diffContainer.innerHTML = "";
diffContainer.appendChild(fragment);
}
function showError(message) {
loadingMessageEl.style.display = "none";
errorMessageEl.textContent = message;
errorMessageEl.style.display = "block";
suggestionAreaEl.style.display = "none";
}
async function initializeApp() {
if (typeof window.env.USER_INPUT === "undefined" || window.env.USER_INPUT === null) {
showError("Error: window.USER_INPUT is not defined. Please set it before loading the app.");
return;
}
if (typeof window.env.OPENAI_TOKEN === "undefined" || !window.env.OPENAI_TOKEN) {
showError("Error: window.OPENAI_TOKEN is not defined. Please set your OpenAI API key.");
return;
}
if (typeof Diff === "undefined") {
showError("Error: jsdiff library could not be loaded. Check your internet connection or the CDN link.");
return;
}
const originalText = window.env.USER_INPUT;
originalTextEl.textContent = originalText;
const result = await getChatGPTSuggestion(originalText, window.env.OPENAI_TOKEN);
loadingMessageEl.style.display = "none";
if (result.error) {
showError(result.error);
} else if (result.suggestion) {
currentSuggestedText = result.suggestion;
suggestionAreaEl.style.display = "block";
displayDiff(originalText, currentSuggestedText, diffOutputEl);
cleanSuggestionOutputEl.textContent = currentSuggestedText;
acceptButtonEl.disabled = false;
}
}
acceptButtonEl.addEventListener("click", () => {
if (currentSuggestedText !== null) {
if (window.app && typeof window.app.finish === "function") {
window.app.finish(currentSuggestedText);
// Optionally, provide feedback or close the "app" view
acceptButtonEl.textContent = "Accepted!";
acceptButtonEl.disabled = true;
} else {
alert("Error: window.app.finish is not defined. Cannot complete action.");
console.error("window.app.finish is not defined.");
}
}
});
// Initialize the app when the DOM is ready
document.addEventListener("DOMContentLoaded", initializeApp);
</script>
</body>
</html>