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.
Files changed (3) hide show
  1. package/README.md +61 -0
  2. package/index.js +242 -0
  3. 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
+ }