Skip to content

Commit 0c9d69d

Browse files
committed
- grammar check v0.2
1 parent 3bccba5 commit 0c9d69d

File tree

1 file changed

+170
-145
lines changed

1 file changed

+170
-145
lines changed

tools/grammar_check.html

Lines changed: 170 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1,179 +1,204 @@
11
<!DOCTYPE html>
22
<html lang="en">
33
<head>
4+
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>📝</text></svg>">
45
<meta charset="UTF-8">
56
<meta name="viewport" content="width=device-width, initial-scale=1.0">
6-
<title>Grammar Checker</title>
77
<script src="https://cdn.tailwindcss.com"></script>
88
<script src="https://cdnjs.cloudflare.com/ajax/libs/json5/2.2.3/index.min.js"></script>
9-
<script src="https://cdn.jsdelivr.net/npm/languagedetect/languagedetect.js"></script>
9+
<title>GrammarPro</title>
1010
<style>
11-
.error-spelling { background: rgba(255, 0, 0, 0.2); }
12-
.error-grammar { background: rgba(255, 165, 0, 0.2); }
13-
.error-style { background: rgba(0, 128, 0, 0.2); }
14-
.error-punctuation { background: rgba(0, 0, 255, 0.2); }
15-
.tooltip { position: absolute; background: white; border: 1px solid #ccc; padding: 8px; border-radius: 4px; box-shadow: 2px 2px 10px rgba(0,0,0,0.1); z-index: 1000; max-width: 300px; }
11+
.error-highlight {
12+
position: relative;
13+
display: inline-block;
14+
cursor: pointer;
15+
}
16+
17+
.grammar { border-bottom: 2px solid #ef4444; }
18+
.spelling { border-bottom: 2px solid #3b82f6; }
19+
.style { border-bottom: 2px solid #10b981; }
20+
.punctuation { border-bottom: 2px solid #f59e0b; }
21+
22+
.tooltip {
23+
visibility: hidden;
24+
position: absolute;
25+
bottom: 100%;
26+
left: 50%;
27+
transform: translateX(-50%);
28+
padding: 8px;
29+
border-radius: 4px;
30+
font-size: 14px;
31+
white-space: nowrap;
32+
z-index: 100;
33+
transition: visibility 0.2s;
34+
}
35+
36+
.error-highlight:hover .tooltip {
37+
visibility: visible;
38+
}
1639
</style>
1740
</head>
18-
<body class="bg-gray-100 min-h-screen">
19-
<div class="container mx-auto max-w-4xl p-6">
20-
<h1 class="text-3xl font-bold text-center mb-6">Grammar Checker</h1>
21-
<div class="bg-white rounded-lg shadow-lg p-6">
22-
<textarea id="textInput" class="w-full h-40 p-4 border rounded-lg mb-4" placeholder="Enter your text here..."></textarea>
23-
<button id="checkGrammar" class="w-full bg-blue-600 text-white py-2 rounded-lg hover:bg-blue-700 transition-colors">Check Grammar</button>
24-
<div id="result" class="mt-6 p-4 border rounded-lg"></div>
41+
<body class="bg-gray-100 flex justify-center items-center min-h-screen">
42+
<div class="container mx-auto max-w-4xl p-5 bg-white rounded-lg shadow-lg">
43+
<h1 class="text-3xl font-bold text-center mb-6">GrammarPro</h1>
44+
45+
<div class="mb-6">
46+
<label for="input" class="block text-sm font-medium text-gray-700 mb-2">Enter Text:</label>
47+
<textarea id="input" rows="6" class="w-full p-3 border rounded-md shadow-sm focus:border-indigo-500 focus:ring-indigo-500"
48+
placeholder="Type or paste your text here..."></textarea>
2549
</div>
26-
<div class="mt-4 flex gap-4 justify-center">
27-
<div class="flex items-center"><span class="w-4 h-4 inline-block mr-2 error-spelling"></span>Spelling</div>
28-
<div class="flex items-center"><span class="w-4 h-4 inline-block mr-2 error-grammar"></span>Grammar</div>
29-
<div class="flex items-center"><span class="w-4 h-4 inline-block mr-2 error-style"></span>Style</div>
30-
<div class="flex items-center"><span class="w-4 h-4 inline-block mr-2 error-punctuation"></span>Punctuation</div>
50+
51+
<div class="flex justify-between items-center mb-6">
52+
<button id="check-grammar" class="px-6 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500">
53+
Check Grammar
54+
</button>
55+
56+
<div class="flex gap-4">
57+
<div class="flex items-center">
58+
<span class="w-3 h-3 bg-red-500 rounded-full mr-2"></span>
59+
<span class="text-sm">Grammar</span>
60+
</div>
61+
<div class="flex items-center">
62+
<span class="w-3 h-3 bg-blue-500 rounded-full mr-2"></span>
63+
<span class="text-sm">Spelling</span>
64+
</div>
65+
<div class="flex items-center">
66+
<span class="w-3 h-3 bg-green-500 rounded-full mr-2"></span>
67+
<span class="text-sm">Style</span>
68+
</div>
69+
<div class="flex items-center">
70+
<span class="w-3 h-3 bg-yellow-500 rounded-full mr-2"></span>
71+
<span class="text-sm">Punctuation</span>
72+
</div>
73+
</div>
3174
</div>
75+
76+
<div id="result" class="p-4 border rounded-md min-h-[100px] bg-gray-50"></div>
3277
</div>
3378

3479
<script>
35-
const grammarChecker = {
36-
init() {
37-
this.checkButton = document.getElementById('checkGrammar');
38-
this.textInput = document.getElementById('textInput');
39-
this.resultDiv = document.getElementById('result');
40-
this.checkButton.addEventListener('click', () => this.check());
41-
this.tooltip = null;
42-
},
43-
44-
async check() {
45-
const text = this.textInput.value.trim();
46-
if (!text) return;
47-
48-
this.checkButton.disabled = true;
49-
this.checkButton.textContent = 'Checking...';
50-
51-
try {
52-
const response = await this.callLLM(text);
53-
this.displayResults(response);
54-
} catch (error) {
55-
console.error('Error:', error);
56-
this.resultDiv.innerHTML = 'An error occurred while checking the text.';
57-
} finally {
58-
this.checkButton.disabled = false;
59-
this.checkButton.textContent = 'Check Grammar';
60-
}
61-
},
62-
63-
async callLLM(text) {
64-
const prompt = `Act as a grammar checking API. Analyze the following text and return a JSON response in this exact format:
65-
{
66-
"errors": [
67-
{
68-
"type": "spelling|grammar|style|punctuation",
69-
"start": <number>,
70-
"end": <number>,
71-
"text": "<problematic text>",
72-
"suggestion": "<corrected text>",
73-
"description": "<error description>"
74-
}
75-
]
76-
}
77-
78-
Example input: "I dont like there attitude."
79-
Example response: {
80-
"errors": [
81-
{
82-
"type": "spelling",
83-
"start": 2,
84-
"end": 6,
85-
"text": "dont",
86-
"suggestion": "don't",
87-
"description": "Missing apostrophe in contraction"
88-
},
89-
{
90-
"type": "grammar",
91-
"start": 7,
92-
"end": 12,
93-
"text": "there",
94-
"suggestion": "their",
95-
"description": "Incorrect use of 'there' instead of the possessive 'their'"
80+
document.getElementById('check-grammar').addEventListener('click', checkGrammar);
81+
82+
async function checkGrammar() {
83+
const inputElement = document.getElementById('input');
84+
const resultContainer = document.getElementById('result');
85+
const button = document.getElementById('check-grammar');
86+
87+
const inputText = inputElement.value.trim();
88+
if (!inputText) {
89+
alert('Please enter some text to check.');
90+
return;
9691
}
97-
]
98-
}
9992

100-
Text to analyze: ${text}`;
93+
button.disabled = true;
94+
button.textContent = 'Checking...';
95+
96+
const botMessage = `I am a grammar checking API. I analyze text for grammar, spelling, style, and punctuation errors. I always respond in this JSON format:
97+
{
98+
"detectedLanguage": "string",
99+
"errors": [
100+
{
101+
"type": "grammar|spelling|style|punctuation",
102+
"text": "original text",
103+
"suggestion": "suggested correction",
104+
"description": "error description"
105+
}
106+
]
107+
}
101108
109+
Example response for "I dont like there attitude":
110+
{
111+
"detectedLanguage": "English",
112+
"errors": [
113+
{
114+
"type": "spelling",
115+
"text": "dont",
116+
"suggestion": "don't",
117+
"description": "Missing apostrophe in contraction"
118+
},
119+
{
120+
"type": "grammar",
121+
"text": "there",
122+
"suggestion": "their",
123+
"description": "Incorrect use of 'there'. Use 'their' for possession"
124+
}
125+
]
126+
}`;
127+
128+
try {
102129
const response = await fetch('https://chatgpt.tobiasmue91.workers.dev/', {
103130
method: 'POST',
104131
headers: {"Content-Type": "application/json"},
105132
body: JSON.stringify({
106133
model: "gpt-3.5-turbo",
134+
max_tokens: 800,
135+
temperature: 0.3,
107136
messages: [
108-
{role: "user", content: prompt}
109-
]
137+
{
138+
role: "assistant",
139+
content: botMessage,
140+
},
141+
{
142+
role: "user",
143+
content: `Check this text: ${inputText}`,
144+
},
145+
],
110146
})
111147
});
112148

149+
if (!response.ok) throw new Error('Network response was not ok');
150+
113151
const data = await response.json();
114-
const content = data.choices[0].message.content;
115-
return JSON.parse(content);
116-
},
117-
118-
displayResults(response) {
119-
const text = this.textInput.value;
120-
let html = text;
121-
const errors = response.errors.sort((a, b) => b.start - a.start);
122-
123-
errors.forEach(error => {
124-
const errorClass = `error-${error.type}`;
125-
const replacement = `<span class="${errorClass}" data-error='${JSON.stringify(error)}'>${error.text}</span>`;
126-
html = html.substring(0, error.start) + replacement + html.substring(error.end);
127-
});
152+
const responseContent = data.choices[0].message.content;
153+
const jsonMatch = responseContent.match(/\{[\s\S]*\}/);
128154

129-
this.resultDiv.innerHTML = html;
130-
this.attachErrorListeners();
131-
},
155+
if (!jsonMatch) throw new Error('Invalid response format');
132156

133-
attachErrorListeners() {
134-
const errorSpans = this.resultDiv.querySelectorAll('span[data-error]');
135-
errorSpans.forEach(span => {
136-
span.addEventListener('mouseover', (e) => this.showTooltip(e));
137-
span.addEventListener('mouseout', () => this.hideTooltip());
138-
span.addEventListener('click', (e) => this.applyCorrection(e));
139-
});
140-
},
141-
142-
showTooltip(event) {
143-
const error = JSON.parse(event.target.dataset.error);
144-
if (this.tooltip) this.hideTooltip();
145-
146-
this.tooltip = document.createElement('div');
147-
this.tooltip.className = 'tooltip';
148-
this.tooltip.innerHTML = `
149-
<div class="font-bold">${error.type.charAt(0).toUpperCase() + error.type.slice(1)} Error</div>
150-
<div class="mt-1">${error.description}</div>
151-
<div class="mt-2 text-sm text-gray-600">Click to replace with: "${error.suggestion}"</div>
152-
`;
153-
154-
document.body.appendChild(this.tooltip);
155-
const rect = event.target.getBoundingClientRect();
156-
this.tooltip.style.left = `${rect.left}px`;
157-
this.tooltip.style.top = `${rect.bottom + 5}px`;
158-
},
159-
160-
hideTooltip() {
161-
if (this.tooltip) {
162-
this.tooltip.remove();
163-
this.tooltip = null;
164-
}
165-
},
166-
167-
applyCorrection(event) {
168-
const error = JSON.parse(event.target.dataset.error);
169-
event.target.textContent = error.suggestion;
170-
event.target.classList.remove(`error-${error.type}`);
171-
event.target.removeAttribute('data-error');
172-
this.hideTooltip();
173-
}
174-
};
157+
const grammarData = JSON5.parse(jsonMatch[0]);
158+
displayResults(inputText, grammarData.errors);
175159

176-
document.addEventListener('DOMContentLoaded', () => grammarChecker.init());
160+
} catch (error) {
161+
console.error('Error:', error);
162+
resultContainer.textContent = 'An error occurred while checking the text. Please try again.';
163+
} finally {
164+
button.disabled = false;
165+
button.textContent = 'Check Grammar';
166+
}
167+
}
168+
169+
function displayResults(originalText, errors) {
170+
const resultContainer = document.getElementById('result');
171+
let displayText = originalText;
172+
173+
// Sort errors by position (longest matches first to avoid nested replacements)
174+
errors.sort((a, b) => b.text.length - a.text.length);
175+
176+
// Replace each error with highlighted span
177+
errors.forEach(error => {
178+
const highlightHtml = `<span class="error-highlight ${error.type}">
179+
${error.text}
180+
<div class="tooltip bg-gray-800 text-white">
181+
${error.description}<br>
182+
Suggestion: ${error.suggestion}
183+
</div>
184+
</span>`;
185+
186+
displayText = displayText.replace(error.text, highlightHtml);
187+
});
188+
189+
resultContainer.innerHTML = displayText;
190+
191+
// Add click handlers for corrections
192+
document.querySelectorAll('.error-highlight').forEach(highlight => {
193+
highlight.addEventListener('click', function() {
194+
const error = errors.find(e => e.text === this.textContent.trim());
195+
if (error) {
196+
const input = document.getElementById('input');
197+
input.value = input.value.replace(error.text, error.suggestion);
198+
}
199+
});
200+
});
201+
}
177202
</script>
178203
</body>
179204
</html>

0 commit comments

Comments
 (0)