word-meaning-plugin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -0
- package/package.json +18 -0
- package/word-meaning-plugin.css +211 -0
- package/word-meaning-plugin.js +319 -0
package/README.md
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# 📚 Word Meaning Plugin
|
|
2
|
+
|
|
3
|
+
A lightweight JavaScript plugin that adds instant word meaning functionality to any website. Simply select any word and see its definition, translation, and example sentence in a beautiful popup.
|
|
4
|
+
|
|
5
|
+
## ✨ Features
|
|
6
|
+
|
|
7
|
+
- **Instant Meanings**: Select any word to see its meaning immediately
|
|
8
|
+
- **Bilingual Support**: English definitions with Hindi translations
|
|
9
|
+
- **Context-Aware**: Provides meanings based on surrounding text context
|
|
10
|
+
- **Text-to-Speech**: Listen to pronunciations in English and Hindi
|
|
11
|
+
- **Beautiful Design**: Modern, responsive popup with smooth animations
|
|
12
|
+
- **Zero Configuration**: Works out of the box with no setup required
|
|
13
|
+
- **Mobile Friendly**: Fully responsive design for all devices
|
|
14
|
+
- **Lightweight**: Small file size with minimal impact on page load
|
|
15
|
+
|
|
16
|
+
## 🚀 Quick Start
|
|
17
|
+
|
|
18
|
+
### Method 1: Simple Installation (Recommended)
|
|
19
|
+
|
|
20
|
+
Just add these two lines to your HTML file:
|
|
21
|
+
|
|
22
|
+
```html
|
|
23
|
+
<!-- Add this to your HTML head -->
|
|
24
|
+
<link rel="stylesheet" href="word-meaning-plugin.css">
|
|
25
|
+
|
|
26
|
+
<!-- Add this before the closing </body> tag -->
|
|
27
|
+
<script src="word-meaning-plugin.js"></script>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Method 2: JavaScript Only
|
|
31
|
+
|
|
32
|
+
If you prefer, you can add only the JavaScript file and it will automatically load the CSS:
|
|
33
|
+
|
|
34
|
+
```html
|
|
35
|
+
<script src="word-meaning-plugin.js"></script>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Method 3: CDN Installation (Coming Soon)
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/word-meaning-plugin@1.0/word-meaning-plugin.css">
|
|
42
|
+
<script src="https://cdn.jsdelivr.net/npm/word-meaning-plugin@1.0/word-meaning-plugin.js"></script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## 📖 Usage
|
|
46
|
+
|
|
47
|
+
Once installed, the plugin works automatically:
|
|
48
|
+
|
|
49
|
+
1. **Select any word** on your website by double-clicking or dragging
|
|
50
|
+
2. **See the popup** appear with English meaning, Hindi translation, and example
|
|
51
|
+
3. **Click the speaker icons** to hear pronunciations
|
|
52
|
+
4. **Click anywhere else** to close the popup
|
|
53
|
+
|
|
54
|
+
## 🔧 Advanced Configuration
|
|
55
|
+
|
|
56
|
+
You can access the plugin's public API through `window.wordMeaningPlugin`:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
// Update API key (if you want to use your own)
|
|
60
|
+
window.wordMeaningPlugin.config.apiKey = 'your-huggingface-api-key';
|
|
61
|
+
|
|
62
|
+
// Manually hide the popup
|
|
63
|
+
window.wordMeaningPlugin.hidePopup();
|
|
64
|
+
|
|
65
|
+
// Use text-to-speech programmatically
|
|
66
|
+
window.wordMeaningPlugin.speakText('Hello world', 'en'); // English
|
|
67
|
+
window.wordMeaningPlugin.speakText('नमस्ते दुनिया', 'hi'); // Hindi
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## 🎨 Customization
|
|
71
|
+
|
|
72
|
+
The plugin uses separate CSS for easy styling. You can override these styles:
|
|
73
|
+
|
|
74
|
+
```css
|
|
75
|
+
/* Custom popup colors */
|
|
76
|
+
#word-meaning-popup {
|
|
77
|
+
background: your-custom-gradient;
|
|
78
|
+
border: your-custom-border;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* Custom button styles */
|
|
82
|
+
#word-meaning-popup button {
|
|
83
|
+
background: your-button-color;
|
|
84
|
+
border-radius: your-border-radius;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* Custom typography */
|
|
88
|
+
#word-meaning-popup strong {
|
|
89
|
+
color: your-accent-color;
|
|
90
|
+
font-size: your-font-size;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### CSS Features
|
|
95
|
+
|
|
96
|
+
- **Responsive Design**: Mobile-optimized styles
|
|
97
|
+
- **Dark Theme**: Automatic dark mode support
|
|
98
|
+
- **Accessibility**: High contrast and reduced motion support
|
|
99
|
+
- **Performance**: CSS animations for smooth interactions
|
|
100
|
+
|
|
101
|
+
## 🌐 Browser Support
|
|
102
|
+
|
|
103
|
+
- Chrome 60+
|
|
104
|
+
- Firefox 55+
|
|
105
|
+
- Safari 11+
|
|
106
|
+
- Edge 79+
|
|
107
|
+
|
|
108
|
+
## 📋 Requirements
|
|
109
|
+
|
|
110
|
+
- Modern web browser with JavaScript enabled
|
|
111
|
+
- Internet connection for API calls
|
|
112
|
+
- (Optional) HuggingFace API key for custom configuration
|
|
113
|
+
|
|
114
|
+
## 🔒 API Usage
|
|
115
|
+
|
|
116
|
+
The plugin uses HuggingFace's API for word meanings. The default API key has usage limits. For production use, get your free API key from [HuggingFace](https://huggingface.co/) and update it:
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
window.wordMeaningPlugin.config.apiKey = 'your-api-key-here';
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## 📁 File Structure
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
word-meaning-plugin/
|
|
126
|
+
├── word-meaning-plugin.js # Main plugin file
|
|
127
|
+
├── word-meaning-plugin.css # Plugin styles
|
|
128
|
+
├── demo.html # Basic demo
|
|
129
|
+
├── demo-with-css.html # CSS version demo
|
|
130
|
+
├── cdn-example.html # CDN usage example
|
|
131
|
+
├── README.md # Documentation
|
|
132
|
+
└── disnorty/ # Original project files
|
|
133
|
+
├── index.html
|
|
134
|
+
├── script.js
|
|
135
|
+
└── style.css
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## 🎯 Demo
|
|
139
|
+
|
|
140
|
+
Check out the live demos:
|
|
141
|
+
- `demo.html` - Basic demo with auto-loaded CSS
|
|
142
|
+
- `demo-with-css.html` - Demo with manually loaded CSS
|
|
143
|
+
- `cdn-example.html` - CDN usage example (when CDN is available)
|
|
144
|
+
|
|
145
|
+
## 🤝 Contributing
|
|
146
|
+
|
|
147
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
148
|
+
|
|
149
|
+
## 📄 License
|
|
150
|
+
|
|
151
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
152
|
+
|
|
153
|
+
## 🙏 Acknowledgments
|
|
154
|
+
|
|
155
|
+
- HuggingFace for providing the AI API
|
|
156
|
+
- All contributors and users of this plugin
|
|
157
|
+
|
|
158
|
+
## 📞 Support
|
|
159
|
+
|
|
160
|
+
If you encounter any issues or have questions, please open an issue on the GitHub repository.
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
**Made with ❤️ for making web content more accessible and educational**
|
package/package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "word-meaning-plugin",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A simple word meaning plugin",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"author": "Adi",
|
|
7
|
+
"type": "commonjs",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"dictionary",
|
|
10
|
+
"word meaning",
|
|
11
|
+
"plugin",
|
|
12
|
+
"tooltip"
|
|
13
|
+
],
|
|
14
|
+
"main": "word-meaning-plugin.js",
|
|
15
|
+
"scripts": {
|
|
16
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Word Meaning Plugin CSS v1.0
|
|
3
|
+
* Styles for the word meaning dictionary popup
|
|
4
|
+
* Author: Your Name
|
|
5
|
+
* License: MIT
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/* Main popup container */
|
|
9
|
+
#word-meaning-popup {
|
|
10
|
+
display: none;
|
|
11
|
+
position: absolute;
|
|
12
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
13
|
+
color: #fff;
|
|
14
|
+
padding: 16px 20px;
|
|
15
|
+
border-radius: 12px;
|
|
16
|
+
max-width: 320px;
|
|
17
|
+
font-size: 14px;
|
|
18
|
+
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2),
|
|
19
|
+
0 5px 10px rgba(0, 0, 0, 0.15),
|
|
20
|
+
inset 0 1px 0 rgba(255, 255, 255, 0.1);
|
|
21
|
+
z-index: 10000;
|
|
22
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
23
|
+
backdrop-filter: blur(10px);
|
|
24
|
+
transition: opacity 0.3s ease, transform 0.3s ease;
|
|
25
|
+
opacity: 0;
|
|
26
|
+
transform: translateY(10px);
|
|
27
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
|
|
28
|
+
line-height: 1.6;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Popup show state */
|
|
32
|
+
#word-meaning-popup.show {
|
|
33
|
+
opacity: 1;
|
|
34
|
+
transform: translateY(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* Popup arrow */
|
|
38
|
+
#word-meaning-popup::after {
|
|
39
|
+
content: '';
|
|
40
|
+
position: absolute;
|
|
41
|
+
top: -8px;
|
|
42
|
+
left: 50%;
|
|
43
|
+
transform: translateX(-50%);
|
|
44
|
+
border-left: 8px solid transparent;
|
|
45
|
+
border-right: 8px solid transparent;
|
|
46
|
+
border-bottom: 8px solid #1a1a2e;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Word title */
|
|
50
|
+
#word-meaning-popup strong {
|
|
51
|
+
color: #4fc3f7;
|
|
52
|
+
font-size: 18px;
|
|
53
|
+
margin-bottom: 8px;
|
|
54
|
+
display: block;
|
|
55
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
|
56
|
+
padding-bottom: 6px;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Section containers */
|
|
60
|
+
#word-meaning-popup > div {
|
|
61
|
+
margin-top: 8px;
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: start;
|
|
64
|
+
gap: 10px;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* Labels */
|
|
68
|
+
#word-meaning-popup b {
|
|
69
|
+
color: #fff;
|
|
70
|
+
font-weight: 600;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* Content areas */
|
|
74
|
+
#word-meaning-popup > div > div:first-child {
|
|
75
|
+
flex: 1;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/* Example section */
|
|
79
|
+
#word-meaning-popup > div[style*="italic"] {
|
|
80
|
+
font-style: italic;
|
|
81
|
+
color: #ccc;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* Speaker buttons */
|
|
85
|
+
#word-meaning-popup button {
|
|
86
|
+
background: none;
|
|
87
|
+
border: none;
|
|
88
|
+
cursor: pointer;
|
|
89
|
+
padding: 4px;
|
|
90
|
+
border-radius: 4px;
|
|
91
|
+
color: #fff;
|
|
92
|
+
font-size: 14px;
|
|
93
|
+
transition: background-color 0.2s ease;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#word-meaning-popup button:hover {
|
|
97
|
+
background: rgba(255, 255, 255, 0.1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
#word-meaning-popup button:active {
|
|
101
|
+
transform: scale(0.95);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/* Responsive design */
|
|
105
|
+
@media (max-width: 480px) {
|
|
106
|
+
#word-meaning-popup {
|
|
107
|
+
max-width: 280px;
|
|
108
|
+
font-size: 13px;
|
|
109
|
+
padding: 12px 16px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#word-meaning-popup strong {
|
|
113
|
+
font-size: 16px;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
#word-meaning-popup button {
|
|
117
|
+
font-size: 12px;
|
|
118
|
+
padding: 3px;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Dark theme support */
|
|
123
|
+
@media (prefers-color-scheme: dark) {
|
|
124
|
+
#word-meaning-popup {
|
|
125
|
+
background: linear-gradient(135deg, #0f0f23 0%, #1a1a2e 100%);
|
|
126
|
+
border: 1px solid rgba(255, 255, 255, 0.15);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#word-meaning-popup::after {
|
|
130
|
+
border-bottom-color: #0f0f23;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* High contrast mode support */
|
|
135
|
+
@media (prefers-contrast: high) {
|
|
136
|
+
#word-meaning-popup {
|
|
137
|
+
border: 2px solid #fff;
|
|
138
|
+
background: #000;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
#word-meaning-popup::after {
|
|
142
|
+
border-bottom-color: #000;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
#word-meaning-popup strong {
|
|
146
|
+
color: #fff;
|
|
147
|
+
border-bottom-color: #fff;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/* Reduced motion support */
|
|
152
|
+
@media (prefers-reduced-motion: reduce) {
|
|
153
|
+
#word-meaning-popup {
|
|
154
|
+
transition: none;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#word-meaning-popup button {
|
|
158
|
+
transition: none;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/* Loading state */
|
|
163
|
+
#word-meaning-popup.loading {
|
|
164
|
+
pointer-events: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#word-meaning-popup.loading::after {
|
|
168
|
+
content: '';
|
|
169
|
+
position: absolute;
|
|
170
|
+
bottom: 8px;
|
|
171
|
+
right: 8px;
|
|
172
|
+
width: 16px;
|
|
173
|
+
height: 16px;
|
|
174
|
+
border: 2px solid rgba(255, 255, 255, 0.3);
|
|
175
|
+
border-top: 2px solid #4fc3f7;
|
|
176
|
+
border-radius: 50%;
|
|
177
|
+
animation: spin 1s linear infinite;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
@keyframes spin {
|
|
181
|
+
0% { transform: rotate(0deg); }
|
|
182
|
+
100% { transform: rotate(360deg); }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Error state */
|
|
186
|
+
#word-meaning-popup.error {
|
|
187
|
+
background: linear-gradient(135deg, #c62828 0%, #b71c1c 100%);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#word-meaning-popup.error::after {
|
|
191
|
+
border-bottom-color: #c62828;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/* Custom scrollbar for popup content */
|
|
195
|
+
#word-meaning-popup::-webkit-scrollbar {
|
|
196
|
+
width: 6px;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
#word-meaning-popup::-webkit-scrollbar-track {
|
|
200
|
+
background: rgba(255, 255, 255, 0.1);
|
|
201
|
+
border-radius: 3px;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#word-meaning-popup::-webkit-scrollbar-thumb {
|
|
205
|
+
background: rgba(255, 255, 255, 0.3);
|
|
206
|
+
border-radius: 3px;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
#word-meaning-popup::-webkit-scrollbar-thumb:hover {
|
|
210
|
+
background: rgba(255, 255, 255, 0.5);
|
|
211
|
+
}
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Word Meaning Plugin v1.0
|
|
3
|
+
* Easy word meaning dictionary for any website
|
|
4
|
+
* Just add this script and select any word to see its meaning
|
|
5
|
+
* Author: Your Name
|
|
6
|
+
* License: MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
(function() {
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
// Plugin configuration
|
|
13
|
+
const config = {
|
|
14
|
+
apiKey: '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', // You can make this configurable
|
|
15
|
+
apiUrl: 'https://router.huggingface.co/v1/chat/completions',
|
|
16
|
+
model: 'moonshotai/Kimi-K2-Instruct-0905',
|
|
17
|
+
popupWidth: 320,
|
|
18
|
+
popupHeight: 150,
|
|
19
|
+
animationDuration: 300
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Create popup element
|
|
23
|
+
let popup = null;
|
|
24
|
+
let isLoading = false;
|
|
25
|
+
let cssLoaded = false;
|
|
26
|
+
|
|
27
|
+
// Initialize plugin
|
|
28
|
+
function init() {
|
|
29
|
+
loadCSS();
|
|
30
|
+
createPopup();
|
|
31
|
+
addEventListeners();
|
|
32
|
+
console.log('Word Meaning Plugin initialized');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Load CSS file
|
|
36
|
+
function loadCSS() {
|
|
37
|
+
if (cssLoaded) return;
|
|
38
|
+
|
|
39
|
+
const link = document.createElement('link');
|
|
40
|
+
link.rel = 'stylesheet';
|
|
41
|
+
link.type = 'text/css';
|
|
42
|
+
link.href = getCSSPath();
|
|
43
|
+
document.head.appendChild(link);
|
|
44
|
+
cssLoaded = true;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Get CSS file path
|
|
48
|
+
function getCSSPath() {
|
|
49
|
+
// Try to detect the plugin's script path and use same directory for CSS
|
|
50
|
+
const scripts = document.getElementsByTagName('script');
|
|
51
|
+
for (let i = scripts.length - 1; i >= 0; i--) {
|
|
52
|
+
const script = scripts[i];
|
|
53
|
+
if (script.src && script.src.includes('word-meaning-plugin.js')) {
|
|
54
|
+
return script.src.replace('word-meaning-plugin.js', 'word-meaning-plugin.css');
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Fallback to same directory
|
|
58
|
+
return 'word-meaning-plugin.css';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Create popup element
|
|
62
|
+
function createPopup() {
|
|
63
|
+
popup = document.createElement('div');
|
|
64
|
+
popup.id = 'word-meaning-popup';
|
|
65
|
+
document.body.appendChild(popup);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Add event listeners
|
|
69
|
+
function addEventListeners() {
|
|
70
|
+
// Handle text selection
|
|
71
|
+
document.addEventListener('mouseup', handleTextSelection);
|
|
72
|
+
|
|
73
|
+
// Close popup when clicking outside
|
|
74
|
+
document.addEventListener('mousedown', function(event) {
|
|
75
|
+
if (popup.style.display === 'block' && !popup.contains(event.target)) {
|
|
76
|
+
hidePopup();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Handle window resize
|
|
81
|
+
window.addEventListener('resize', hidePopup);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Handle text selection
|
|
85
|
+
function handleTextSelection(event) {
|
|
86
|
+
const selection = window.getSelection();
|
|
87
|
+
const word = selection.toString().trim();
|
|
88
|
+
|
|
89
|
+
// Don't show popup for empty selection or multiple words
|
|
90
|
+
if (word.length === 0 || word.includes(' ') || isLoading) return;
|
|
91
|
+
|
|
92
|
+
// Don't show if clicking on input fields or buttons
|
|
93
|
+
if (event.target.tagName === 'INPUT' || event.target.tagName === 'TEXTAREA' || event.target.tagName === 'BUTTON') return;
|
|
94
|
+
|
|
95
|
+
// Get context for better meaning
|
|
96
|
+
const range = selection.getRangeAt(0);
|
|
97
|
+
const container = range.startContainer;
|
|
98
|
+
const textContent = container.textContent || container.innerText;
|
|
99
|
+
const startOffset = range.startOffset;
|
|
100
|
+
const endOffset = range.endOffset;
|
|
101
|
+
|
|
102
|
+
const contextStart = Math.max(0, startOffset - 50);
|
|
103
|
+
const contextEnd = Math.min(textContent.length, endOffset + 50);
|
|
104
|
+
const context = textContent.substring(contextStart, contextEnd).trim();
|
|
105
|
+
|
|
106
|
+
// Position popup
|
|
107
|
+
const position = calculatePopupPosition(event.pageX, event.pageY);
|
|
108
|
+
|
|
109
|
+
// Show loading state
|
|
110
|
+
showPopup(word, 'Loading definition...', '', '', position);
|
|
111
|
+
|
|
112
|
+
// Fetch meaning
|
|
113
|
+
fetchWordMeaning(word, context, position);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Calculate popup position to keep it within viewport
|
|
117
|
+
function calculatePopupPosition(x, y) {
|
|
118
|
+
const viewportWidth = window.innerWidth;
|
|
119
|
+
const viewportHeight = window.innerHeight;
|
|
120
|
+
|
|
121
|
+
let finalX = x + 10;
|
|
122
|
+
let finalY = y + 10;
|
|
123
|
+
|
|
124
|
+
// Check if popup would go off screen horizontally
|
|
125
|
+
if (finalX + config.popupWidth > viewportWidth) {
|
|
126
|
+
finalX = viewportWidth - config.popupWidth - 20;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check if popup would go off screen vertically
|
|
130
|
+
if (finalY + config.popupHeight > viewportHeight) {
|
|
131
|
+
finalY = y - config.popupHeight - 20;
|
|
132
|
+
popup.style.transformOrigin = 'center bottom';
|
|
133
|
+
} else {
|
|
134
|
+
popup.style.transformOrigin = 'center top';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return { x: finalX, y: finalY };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Fetch word meaning from API
|
|
141
|
+
function fetchWordMeaning(word, context, position) {
|
|
142
|
+
isLoading = true;
|
|
143
|
+
|
|
144
|
+
fetch(config.apiUrl, {
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: {
|
|
147
|
+
'Content-Type': 'application/json',
|
|
148
|
+
'Authorization': `Bearer ${config.apiKey}`
|
|
149
|
+
},
|
|
150
|
+
body: JSON.stringify({
|
|
151
|
+
model: config.model,
|
|
152
|
+
messages: [
|
|
153
|
+
{
|
|
154
|
+
role: 'system',
|
|
155
|
+
content: `You are a context-aware dictionary. Given a word and its surrounding context, provide the most relevant meaning for that specific context.
|
|
156
|
+
|
|
157
|
+
Context: "${context}"
|
|
158
|
+
Word: "${word}"
|
|
159
|
+
|
|
160
|
+
Provide the response in this exact format:
|
|
161
|
+
English: [context-appropriate meaning]
|
|
162
|
+
Hindi: [Hindi translation]
|
|
163
|
+
Example: [example sentence using the word in this context]`
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
role: 'user',
|
|
167
|
+
content: `What is the meaning of "${word}" in the context: "${context}"?`
|
|
168
|
+
}
|
|
169
|
+
],
|
|
170
|
+
max_tokens: 150,
|
|
171
|
+
temperature: 0.3
|
|
172
|
+
})
|
|
173
|
+
})
|
|
174
|
+
.then(response => response.json())
|
|
175
|
+
.then(data => {
|
|
176
|
+
if (!data.choices || !data.choices[0] || !data.choices[0].message) {
|
|
177
|
+
throw new Error('Invalid API response');
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const response = data.choices[0].message.content.trim();
|
|
181
|
+
const parsed = parseResponse(response);
|
|
182
|
+
|
|
183
|
+
showPopup(word, parsed.enMeaning, parsed.hiMeaning, parsed.example, position);
|
|
184
|
+
})
|
|
185
|
+
.catch(error => {
|
|
186
|
+
console.error('Error fetching word meaning:', error);
|
|
187
|
+
showPopup(word, 'Meaning not found', 'मतलब नहीं मिला', 'No example available.', position);
|
|
188
|
+
})
|
|
189
|
+
.finally(() => {
|
|
190
|
+
isLoading = false;
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Parse API response
|
|
195
|
+
function parseResponse(response) {
|
|
196
|
+
const lines = response.split('\n');
|
|
197
|
+
let enMeaning = 'Meaning not found';
|
|
198
|
+
let hiMeaning = 'अनुवाद उपलब्ध नहीं';
|
|
199
|
+
let example = 'No example available';
|
|
200
|
+
|
|
201
|
+
lines.forEach(line => {
|
|
202
|
+
if (line.toLowerCase().includes('english') || line.toLowerCase().includes('meaning')) {
|
|
203
|
+
enMeaning = line.replace(/english:|meaning:/gi, '').trim();
|
|
204
|
+
} else if (line.toLowerCase().includes('hindi') || line.toLowerCase().includes('translation')) {
|
|
205
|
+
hiMeaning = line.replace(/hindi:|translation:/gi, '').trim();
|
|
206
|
+
} else if (line.toLowerCase().includes('example')) {
|
|
207
|
+
example = line.replace(/example:/gi, '').trim();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
return { enMeaning, hiMeaning, example };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Show popup with content
|
|
215
|
+
function showPopup(word, enMeaning, hiMeaning, example, position) {
|
|
216
|
+
popup.innerHTML = `
|
|
217
|
+
<strong style="color: #4fc3f7; font-size: 18px; margin-bottom: 8px; display: block; border-bottom: 1px solid rgba(255, 255, 255, 0.1); padding-bottom: 6px;">${word}</strong>
|
|
218
|
+
|
|
219
|
+
<div style="margin-top:8px; display: flex; align-items: start; gap: 10px;">
|
|
220
|
+
<div style="flex: 1;">
|
|
221
|
+
<b>English:</b><br>${enMeaning}
|
|
222
|
+
</div>
|
|
223
|
+
<button onclick="event.stopPropagation(); window.wordMeaningPlugin.speakText('${enMeaning.replace(/'/g, "\\'")}', 'en')"
|
|
224
|
+
style="background: none; border: none; cursor: pointer; padding: 4px; border-radius: 4px;"
|
|
225
|
+
title="Speak English">
|
|
226
|
+
🔊
|
|
227
|
+
</button>
|
|
228
|
+
</div>
|
|
229
|
+
|
|
230
|
+
<div style="margin-top:8px; display: flex; align-items: start; gap: 10px;">
|
|
231
|
+
<div style="flex: 1;">
|
|
232
|
+
<b>Hindi:</b><br>${hiMeaning}
|
|
233
|
+
</div>
|
|
234
|
+
<button onclick="event.stopPropagation(); window.wordMeaningPlugin.speakText('${hiMeaning.replace(/'/g, "\\'")}', 'hi')"
|
|
235
|
+
style="background: none; border: none; cursor: pointer; padding: 4px; border-radius: 4px;"
|
|
236
|
+
title="Speak Hindi">
|
|
237
|
+
🔊
|
|
238
|
+
</button>
|
|
239
|
+
</div>
|
|
240
|
+
|
|
241
|
+
<div style="margin-top:8px; font-style:italic; color:#ccc; display: flex; align-items: start; gap: 10px;">
|
|
242
|
+
<div style="flex: 1;">
|
|
243
|
+
<b>Example:</b><br>${example}
|
|
244
|
+
</div>
|
|
245
|
+
<button onclick="event.stopPropagation(); window.wordMeaningPlugin.speakText('${example.replace(/'/g, "\\'")}', 'en')"
|
|
246
|
+
style="background: none; border: none; cursor: pointer; padding: 4px; border-radius: 4px;"
|
|
247
|
+
title="Speak Example">
|
|
248
|
+
🔊
|
|
249
|
+
</button>
|
|
250
|
+
</div>
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
popup.style.left = position.x + 'px';
|
|
254
|
+
popup.style.top = position.y + 'px';
|
|
255
|
+
popup.style.display = 'block';
|
|
256
|
+
|
|
257
|
+
// Trigger animation
|
|
258
|
+
setTimeout(() => {
|
|
259
|
+
popup.style.opacity = '1';
|
|
260
|
+
popup.style.transform = 'translateY(0)';
|
|
261
|
+
}, 10);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Hide popup
|
|
265
|
+
function hidePopup() {
|
|
266
|
+
popup.style.opacity = '0';
|
|
267
|
+
popup.style.transform = 'translateY(10px)';
|
|
268
|
+
setTimeout(() => {
|
|
269
|
+
popup.style.display = 'none';
|
|
270
|
+
}, config.animationDuration);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Text-to-speech function
|
|
274
|
+
function speakText(text, lang) {
|
|
275
|
+
if (!window.speechSynthesis) return;
|
|
276
|
+
|
|
277
|
+
window.speechSynthesis.cancel();
|
|
278
|
+
|
|
279
|
+
const utterance = new SpeechSynthesisUtterance(text);
|
|
280
|
+
utterance.lang = lang;
|
|
281
|
+
utterance.rate = 0.9;
|
|
282
|
+
utterance.pitch = 1;
|
|
283
|
+
utterance.volume = 1;
|
|
284
|
+
|
|
285
|
+
const voices = window.speechSynthesis.getVoices();
|
|
286
|
+
if (lang === 'hi') {
|
|
287
|
+
const hindiVoice = voices.find(voice => voice.lang.includes('hi') || voice.lang.includes('hin'));
|
|
288
|
+
if (hindiVoice) utterance.voice = hindiVoice;
|
|
289
|
+
} else {
|
|
290
|
+
const englishVoice = voices.find(voice => voice.lang.includes('en') || voice.lang.includes('en-US'));
|
|
291
|
+
if (englishVoice) utterance.voice = englishVoice;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
window.speechSynthesis.speak(utterance);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Load voices when available
|
|
298
|
+
if (window.speechSynthesis) {
|
|
299
|
+
window.speechSynthesis.onvoiceschanged = function() {
|
|
300
|
+
window.speechSynthesis.getVoices();
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Public API
|
|
305
|
+
window.wordMeaningPlugin = {
|
|
306
|
+
init: init,
|
|
307
|
+
speakText: speakText,
|
|
308
|
+
hidePopup: hidePopup,
|
|
309
|
+
config: config
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
// Auto-initialize when DOM is ready
|
|
313
|
+
if (document.readyState === 'loading') {
|
|
314
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
315
|
+
} else {
|
|
316
|
+
init();
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
})();
|