trueeditr 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 +61 -0
- package/index.js +242 -0
- package/package.json +28 -0
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# 🚀 TrueEditr: Smart. Slim. AI-Native.
|
|
2
|
+
|
|
3
|
+
TrueEditr is the next-gen WYSIWYG editor built for developers who want the power of AI without the bloat of traditional editors. Plug in your own AI keys, white-label everything, and get started for free.
|
|
4
|
+
|
|
5
|
+
## ✨ Why TrueEditr?
|
|
6
|
+
|
|
7
|
+
- **🤖 AI-Native**: Built-in integration for Gemini, GPT-4o, and Grok. (Connect your own keys!)
|
|
8
|
+
- **📦 Featherweight**: < 50KB gzipped. No heavy UI frameworks.
|
|
9
|
+
- **🛠 Pro Blocks**: Advanced tables, logic-aware code blocks, and beautiful callouts.
|
|
10
|
+
- **📈 Built-in Analytics**: Track loads, AI usage, and user engagement from one dashboard.
|
|
11
|
+
- **🖼 Media Webhooks**: Complete control over your image storage with HMAC-signed webhooks.
|
|
12
|
+
|
|
13
|
+
## 🚀 Quick Start
|
|
14
|
+
|
|
15
|
+
### 1. Installation
|
|
16
|
+
```bash
|
|
17
|
+
npm install trueeditr
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. Implementation
|
|
21
|
+
```javascript
|
|
22
|
+
import TrueEditr from 'trueeditr';
|
|
23
|
+
|
|
24
|
+
const editor = new TrueEditr({
|
|
25
|
+
key: 'te_your_api_key',
|
|
26
|
+
selector: '#editor-container',
|
|
27
|
+
config: {
|
|
28
|
+
placeholder: 'Start writing something amazing...',
|
|
29
|
+
watermark: false // Standard for Pro users
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 🛠 Advanced Features
|
|
35
|
+
|
|
36
|
+
### Pro Implementation (Tables, Code, Callouts)
|
|
37
|
+
Pro features are automatically enabled based on your API key's plan.
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
const editor = new TrueEditr({
|
|
41
|
+
key: 'te_pro_key',
|
|
42
|
+
selector: '#editor-container'
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Media Uploads (Cloud Storage)
|
|
47
|
+
Configure your webhook in the dashboard, then enable uploads. TrueEditr will proxy the files to your backend with security headers.
|
|
48
|
+
|
|
49
|
+
## ⚙️ Configuration Options
|
|
50
|
+
|
|
51
|
+
| Property | Type | Default | Description |
|
|
52
|
+
| :--- | :--- | :--- | :--- |
|
|
53
|
+
| `key` | `string` | `null` | **Required.** Your TrueEditr API Key. |
|
|
54
|
+
| `selector` | `string` | `null` | **Required.** CSS selector for the target element. |
|
|
55
|
+
| `apiUrl` | `string` | `"https://trueeditr.com/api"` | Base URL for the TrueEditr service. |
|
|
56
|
+
|
|
57
|
+
## 🌐 Dashboard & Enterprise
|
|
58
|
+
Manage your domains, check analytics, and upgrade to Pro at [trueeditr.com](https://trueeditr.com).
|
|
59
|
+
|
|
60
|
+
## 📄 License
|
|
61
|
+
MIT © 2026 TrueEditr Team.
|
package/index.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
class TrueEditr {
|
|
2
|
+
constructor(config) {
|
|
3
|
+
this.apiKey = config.key;
|
|
4
|
+
this.selector = config.selector;
|
|
5
|
+
this.container = typeof document !== 'undefined' ? document.querySelector(this.selector) : null;
|
|
6
|
+
this.apiUrl = config.apiUrl || "https://trueeditr.com/api";
|
|
7
|
+
this.aiConfig = null;
|
|
8
|
+
|
|
9
|
+
if (!this.container) {
|
|
10
|
+
console.error("TrueEditr: Container not found");
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
this.init();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async init() {
|
|
18
|
+
try {
|
|
19
|
+
const res = await fetch(`${this.apiUrl}/editor/init`, {
|
|
20
|
+
method: "POST",
|
|
21
|
+
headers: { "Content-Type": "application/json" },
|
|
22
|
+
body: JSON.stringify({ apiKey: this.apiKey })
|
|
23
|
+
});
|
|
24
|
+
const data = await res.json();
|
|
25
|
+
|
|
26
|
+
if (data.success) {
|
|
27
|
+
this.config = { ...data.config, ...this.config }; // Merge server config
|
|
28
|
+
this.aiConfig = data.config.ai;
|
|
29
|
+
this.renderEditor();
|
|
30
|
+
} else {
|
|
31
|
+
this.renderError(data.error, data.upgrade);
|
|
32
|
+
}
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error("TrueEditr Init Error:", err);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
renderError(msg, upgradeLink) {
|
|
39
|
+
this.container.innerHTML = `
|
|
40
|
+
<div style="padding:1rem; border:1px solid #fee2e2; border-radius:0.5rem; background:#fef2f2; color:#b91c1c; font-family:sans-serif;">
|
|
41
|
+
<strong>TrueEditr Error:</strong> ${msg}
|
|
42
|
+
${upgradeLink ? `<br><a href="${upgradeLink}" style="color:#7c3aed; font-weight:bold; text-decoration:none;">Upgrade Plan</a>` : ''}
|
|
43
|
+
</div>
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
renderEditor() {
|
|
48
|
+
// Advanced Styles
|
|
49
|
+
const styles = `
|
|
50
|
+
.true-editor-wrapper { font-family: 'Inter', -apple-system, sans-serif; border: 1px solid #e2e8f0; border-radius: 0.75rem; overflow: hidden; display: flex; flex-direction: column; height: 100%; min-height: 500px; position: relative; background: #fff; }
|
|
51
|
+
.true-editor-wrapper.fullscreen { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 9999; border-radius: 0; }
|
|
52
|
+
.true-editor-toolbar { background: #f8fafc; border-bottom: 1px solid #e2e8f0; padding: 0.5rem; display: flex; flex-direction: column; gap: 0.5rem; }
|
|
53
|
+
.true-toolbar-row { display: flex; gap: 0.25rem; flex-wrap: wrap; align-items: center; border-bottom: 1px solid #f1f5f9; padding-bottom: 0.4rem; }
|
|
54
|
+
.true-toolbar-row:last-child { border-bottom: none; padding-bottom: 0; }
|
|
55
|
+
.true-toolbar-group { display: flex; gap: 0.25rem; border-right: 1px solid #e2e8f0; padding-right: 0.5rem; margin-right: 0.25rem; }
|
|
56
|
+
.true-editor-btn { background: white; border: 1px solid #cbd5e1; border-radius: 0.375rem; padding: 0.4rem 0.6rem; cursor: pointer; font-size: 0.75rem; display: flex; align-items: center; justify-content: center; color: #475569; }
|
|
57
|
+
.true-editor-btn:hover { background: #f1f5f9; border-color: #94a3b8; }
|
|
58
|
+
.true-editor-content { flex: 1; padding: 2rem; outline: none; overflow-y: auto; line-height: 1.8; color: #1e293b; font-size: 16px; }
|
|
59
|
+
.true-editor-footer { background: #f8fafc; border-top: 1px solid #e2e8f0; padding: 0.4rem 1rem; display: flex; justify-content: space-between; align-items: center; font-size: 11px; color: #64748b; }
|
|
60
|
+
.true-ai-popup { position: absolute; background: white; border: 1px solid #e2e8f0; padding: 1.25rem; border-radius: 1rem; box-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1); width: 340px; z-index: 10000; display: none; }
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
if (typeof document !== 'undefined') {
|
|
64
|
+
const styleEl = document.createElement("style");
|
|
65
|
+
styleEl.id = "true-editor-styles";
|
|
66
|
+
styleEl.textContent = styles;
|
|
67
|
+
document.head.appendChild(styleEl);
|
|
68
|
+
|
|
69
|
+
this.wrapper = document.createElement("div");
|
|
70
|
+
this.wrapper.className = "true-editor-wrapper";
|
|
71
|
+
|
|
72
|
+
const toolbar = document.createElement("div");
|
|
73
|
+
toolbar.className = "true-editor-toolbar";
|
|
74
|
+
this.wrapper.appendChild(toolbar);
|
|
75
|
+
|
|
76
|
+
const feats = this.config.features || [];
|
|
77
|
+
|
|
78
|
+
// Row 1
|
|
79
|
+
const row1 = document.createElement("div");
|
|
80
|
+
row1.className = "true-toolbar-row";
|
|
81
|
+
toolbar.appendChild(row1);
|
|
82
|
+
|
|
83
|
+
const gHist = this.createGroup(row1);
|
|
84
|
+
this.addBtn(gHist, { cmd: "undo", label: "↶", title: "Undo" });
|
|
85
|
+
this.addBtn(gHist, { cmd: "redo", label: "↷", title: "Redo" });
|
|
86
|
+
|
|
87
|
+
const gFmt = this.createGroup(row1);
|
|
88
|
+
this.addBtn(gFmt, { cmd: "bold", label: "<b>B</b>", title: "Bold" });
|
|
89
|
+
this.addBtn(gFmt, { cmd: "italic", label: "<i>I</i>", title: "Italic" });
|
|
90
|
+
|
|
91
|
+
const gHead = this.createGroup(row1);
|
|
92
|
+
this.addBtn(gHead, { cmd: "formatBlock", val: "h1", label: "H1", title: "H1" });
|
|
93
|
+
this.addBtn(gHead, { cmd: "formatBlock", val: "h2", label: "H2", title: "H2" });
|
|
94
|
+
|
|
95
|
+
if (this.aiConfig?.enabled) {
|
|
96
|
+
const ai = document.createElement("button");
|
|
97
|
+
ai.className = "true-editor-btn";
|
|
98
|
+
ai.innerHTML = "✨ AI Pro";
|
|
99
|
+
ai.style.color = "#7c3aed";
|
|
100
|
+
ai.style.marginLeft = "auto";
|
|
101
|
+
ai.onclick = (e) => this.showAIPopup(e);
|
|
102
|
+
row1.appendChild(ai);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Row 2
|
|
106
|
+
const row2 = document.createElement("div");
|
|
107
|
+
row2.className = "true-toolbar-row";
|
|
108
|
+
toolbar.appendChild(row2);
|
|
109
|
+
|
|
110
|
+
const gIns = this.createGroup(row2);
|
|
111
|
+
if (feats.includes('image')) this.addBtn(gIns, { cmd: "insertImage", label: "🖼", title: "Image" });
|
|
112
|
+
if (feats.includes('table')) this.addBtn(gIns, { cmd: "insertTable", label: "⊞", title: "Table" });
|
|
113
|
+
if (feats.includes('code')) this.addBtn(gIns, { cmd: "insertCode", label: "‹/›", title: "Code" });
|
|
114
|
+
|
|
115
|
+
const gApps = this.createGroup(row2);
|
|
116
|
+
this.addBtn(gApps, { cmd: "templates", label: "📄 Templates" });
|
|
117
|
+
this.addBtn(gApps, { cmd: "export", label: "⬇ Export" });
|
|
118
|
+
this.addBtn(gApps, { cmd: "fullscreen", label: "⛶" });
|
|
119
|
+
|
|
120
|
+
this.editor = document.createElement("div");
|
|
121
|
+
this.editor.className = "true-editor-content";
|
|
122
|
+
this.editor.contentEditable = true;
|
|
123
|
+
this.wrapper.appendChild(this.editor);
|
|
124
|
+
|
|
125
|
+
this.footer = document.createElement("div");
|
|
126
|
+
this.footer.className = "true-editor-footer";
|
|
127
|
+
this.footer.innerHTML = `<div class="stats">Words: 0</div><div class="status">Draft active</div>`;
|
|
128
|
+
this.wrapper.appendChild(this.footer);
|
|
129
|
+
|
|
130
|
+
this.container.innerHTML = "";
|
|
131
|
+
this.container.appendChild(this.wrapper);
|
|
132
|
+
this.setupEventListeners();
|
|
133
|
+
this.setupAIPopup();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
createGroup(row) {
|
|
138
|
+
const g = document.createElement("div");
|
|
139
|
+
g.className = "true-toolbar-group";
|
|
140
|
+
row.appendChild(g);
|
|
141
|
+
return g;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
addBtn(parent, btn) {
|
|
145
|
+
const b = document.createElement("button");
|
|
146
|
+
b.className = "true-editor-btn";
|
|
147
|
+
b.innerHTML = btn.label;
|
|
148
|
+
b.title = btn.title || "";
|
|
149
|
+
b.onclick = (e) => this.handleAction(btn);
|
|
150
|
+
parent.appendChild(b);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
handleAction(btn) {
|
|
154
|
+
if (btn.cmd === "insertTable") this.insertTable();
|
|
155
|
+
else if (btn.cmd === "insertCode") this.insertCode();
|
|
156
|
+
else if (btn.cmd === "templates") this.insertTemplate();
|
|
157
|
+
else if (btn.cmd === "export") this.exportContent();
|
|
158
|
+
else if (btn.cmd === "fullscreen") this.wrapper.classList.toggle('fullscreen');
|
|
159
|
+
else if (btn.cmd === "insertImage") {
|
|
160
|
+
const url = prompt("Image URL:");
|
|
161
|
+
if (url) document.execCommand("insertImage", false, url);
|
|
162
|
+
} else if (btn.val) {
|
|
163
|
+
document.execCommand(btn.cmd, false, btn.val);
|
|
164
|
+
} else {
|
|
165
|
+
document.execCommand(btn.cmd, false, null);
|
|
166
|
+
}
|
|
167
|
+
this.updateStats();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
updateStats() {
|
|
171
|
+
const text = this.editor.innerText || "";
|
|
172
|
+
const words = text.trim() ? text.trim().split(/\s+/).length : 0;
|
|
173
|
+
if (this.footer) this.footer.querySelector('.stats').textContent = `Words: ${words}`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
setupEventListeners() {
|
|
177
|
+
if (this.editor) {
|
|
178
|
+
this.editor.addEventListener("input", () => this.updateStats());
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
setupAIPopup() {
|
|
183
|
+
this.aiPopup = document.createElement("div");
|
|
184
|
+
this.aiPopup.className = "true-ai-popup";
|
|
185
|
+
this.aiPopup.innerHTML = `
|
|
186
|
+
<div style="font-weight:700; margin-bottom:8px;">AI Write Pro</div>
|
|
187
|
+
<input type="text" style="width:100%; padding:8px; border:1px solid #ddd; border-radius:4px;" placeholder="Summarize, Rewrite, etc..." />
|
|
188
|
+
<button class="ai-gen-btn" style="margin-top:8px; background:#7c3aed; color:white; border:none; padding:6px 12px; border-radius:4px; cursor:pointer;">Generate</button>
|
|
189
|
+
`;
|
|
190
|
+
document.body.appendChild(this.aiPopup);
|
|
191
|
+
this.aiPopup.querySelector('.ai-gen-btn').onclick = () => this.handleAIRequest();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
showAIPopup(e) {
|
|
195
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
196
|
+
this.aiPopup.style.display = 'block';
|
|
197
|
+
this.aiPopup.style.top = (rect.bottom + window.scrollY + 5) + 'px';
|
|
198
|
+
this.aiPopup.style.left = (rect.right + window.scrollX - 340) + 'px';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async handleAIRequest() {
|
|
202
|
+
const input = this.aiPopup.querySelector('input').value;
|
|
203
|
+
const context = this.editor.innerText;
|
|
204
|
+
try {
|
|
205
|
+
const res = await fetch(`${this.apiUrl}/editor/ai-completion`, {
|
|
206
|
+
method: "POST",
|
|
207
|
+
headers: { "Content-Type": "application/json" },
|
|
208
|
+
body: JSON.stringify({ apiKey: this.apiKey, prompt: input, context })
|
|
209
|
+
});
|
|
210
|
+
const data = await res.json();
|
|
211
|
+
if (data.success) {
|
|
212
|
+
document.execCommand("insertText", false, data.text);
|
|
213
|
+
this.aiPopup.style.display = 'none';
|
|
214
|
+
}
|
|
215
|
+
} catch (err) { alert("AI Error"); }
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
insertTable() {
|
|
219
|
+
let html = '<table border="1" style="width:100%; border-collapse:collapse;"><tr><td>Cell</td><td>Cell</td></tr></table><p><br></p>';
|
|
220
|
+
document.execCommand('insertHTML', false, html);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
insertCode() {
|
|
224
|
+
let html = '<pre style="background:#f1f5f9; padding:1em; border-radius:4px;"><code>// Your code</code></pre><p><br></p>';
|
|
225
|
+
document.execCommand('insertHTML', false, html);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
insertTemplate() {
|
|
229
|
+
document.execCommand("insertHTML", false, "<h1>Template</h1><p>Content goes here...</p>");
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
exportContent() {
|
|
233
|
+
const blob = new Blob([this.editor.innerHTML], { type: 'text/html' });
|
|
234
|
+
const url = URL.createObjectURL(blob);
|
|
235
|
+
const a = document.createElement('a');
|
|
236
|
+
a.href = url;
|
|
237
|
+
a.download = "export.html";
|
|
238
|
+
a.click();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export default TrueEditr;
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "trueeditr",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Smart, AI-Native WYSIWYG Editor with built-in analytics and cloud storage support.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/trueeditr/trueeditr.git"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"editor",
|
|
18
|
+
"wysiwyg",
|
|
19
|
+
"ai",
|
|
20
|
+
"gemini",
|
|
21
|
+
"gpt-4o",
|
|
22
|
+
"rich-text",
|
|
23
|
+
"analytics",
|
|
24
|
+
"pro-editor"
|
|
25
|
+
],
|
|
26
|
+
"author": "TrueEditr Team",
|
|
27
|
+
"license": "MIT"
|
|
28
|
+
}
|