252 lines
11 KiB
HTML
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>
|