@liwe3/webcomponents 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.
@@ -0,0 +1,477 @@
1
+ const g = "ai-text-editor-api-key";
2
+ class l extends HTMLElement {
3
+ constructor() {
4
+ super(), this.typingTimer = null, this.fullSuggestion = null, this.suggestionParagraphs = [], this.currentParagraphIndex = 0, this.isShowingSuggestion = !1, this.apiKey = "", this.suggestionDelay = 1e3, this.systemPrompt = "You are a helpful writing assistant. Continue the user's text naturally and coherently. Provide 1-3 sentences that would logically follow their writing. Keep the same tone and style. Do not repeat what they've already written.", this.apiEndpoint = "https://api.openai.com/v1/chat/completions", this.modelName = "gpt-3.5-turbo", this.context = "", this.attachShadow({ mode: "open" }), this.render(), this.init();
5
+ }
6
+ /**
7
+ * Renders the component's HTML structure
8
+ */
9
+ render() {
10
+ this.shadowRoot.innerHTML = `
11
+ <style>
12
+ :host {
13
+ display: block;
14
+ width: 100%;
15
+ height: 100%;
16
+ }
17
+
18
+ .editor-container {
19
+ position: relative;
20
+ height: 100%;
21
+ display: flex;
22
+ flex-direction: column;
23
+ }
24
+
25
+ .editor-status {
26
+ position: absolute;
27
+ top: 5px;
28
+ left: 5px;
29
+ width: 10px;
30
+ height: 10px;
31
+ border-radius: 100%;
32
+ background: #777;
33
+ z-index: 10;
34
+ }
35
+
36
+ .editor-wrapper {
37
+ position: relative;
38
+ width: 100%;
39
+ flex: 1;
40
+ display: flex;
41
+ flex-direction: column;
42
+ }
43
+
44
+ .editor {
45
+ width: 100%;
46
+ height: 100%;
47
+ border: 2px solid #e1e5e9;
48
+ border-radius: 12px;
49
+ padding: 20px;
50
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
51
+ font-size: 14px;
52
+ line-height: 1.6;
53
+ resize: none;
54
+ background: #fafbfc;
55
+ transition: all 0.3s ease;
56
+ position: relative;
57
+ z-index: 2;
58
+ background: transparent;
59
+ box-sizing: border-box;
60
+ min-height: auto;
61
+ }
62
+
63
+ .editor:focus {
64
+ outline: none;
65
+ border-color: #4facfe;
66
+ box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);
67
+ }
68
+
69
+ .editor-background {
70
+ position: absolute;
71
+ top: 0;
72
+ left: 0;
73
+ width: 100%;
74
+ height: 100%;
75
+ border: 2px solid #e1e5e9;
76
+ border-radius: 12px;
77
+ padding: 20px;
78
+ font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
79
+ font-size: 14px;
80
+ line-height: 1.6;
81
+ background: #fafbfc;
82
+ z-index: 1;
83
+ pointer-events: none;
84
+ white-space: pre-wrap;
85
+ word-wrap: break-word;
86
+ overflow: hidden;
87
+ color: transparent;
88
+ box-sizing: border-box;
89
+ }
90
+
91
+ .editor-wrapper:focus-within .editor-background {
92
+ background: white;
93
+ border-color: #4facfe;
94
+ }
95
+
96
+ .suggestion-text {
97
+ color: #bbb;
98
+ position: relative;
99
+ }
100
+
101
+ .suggestion-text.accepted {
102
+ color: #ddd;
103
+ text-decoration: line-through;
104
+ }
105
+
106
+ .loading {
107
+ position: absolute;
108
+ top: 5px;
109
+ right: 10px;
110
+ z-index: 10;
111
+ display: none;
112
+ }
113
+
114
+ .loading.show {
115
+ display: block;
116
+ }
117
+
118
+ .spinner {
119
+ width: 10px;
120
+ height: 10px;
121
+ border: 2px solid #e1e5e9;
122
+ border-top: 2px solid #4facfe;
123
+ border-radius: 50%;
124
+ animation: spin 1s linear infinite;
125
+ }
126
+
127
+ @keyframes spin {
128
+ 0% { transform: rotate(0deg); }
129
+ 100% { transform: rotate(360deg); }
130
+ }
131
+ </style>
132
+
133
+ <div class="editor-container">
134
+ <div class="editor-status"></div>
135
+ <div class="loading" id="loading">
136
+ <div class="spinner"></div>
137
+ </div>
138
+
139
+ <div class="editor-wrapper">
140
+ <div class="editor-background" id="editorBackground"></div>
141
+ <textarea
142
+ class="editor"
143
+ id="editor"
144
+ placeholder="Start writing your markdown text here..."
145
+ ></textarea>
146
+ </div>
147
+ </div>
148
+ `;
149
+ }
150
+ /**
151
+ * Initializes the component after rendering
152
+ */
153
+ init() {
154
+ const t = this.shadowRoot.getElementById("editor"), e = this.shadowRoot.getElementById("editorBackground"), i = this.shadowRoot.getElementById("loading");
155
+ this.editorStatus = this.shadowRoot.querySelector(".editor-status"), this.editor = t, this.editorBackground = e, this.loading = i, this.editor.addEventListener("input", () => {
156
+ this.handleTextInput(), this.updateBackground(), this.dispatchEvent(new CustomEvent("change", { detail: { value: this.editor.value } }));
157
+ }), this.editor.addEventListener("keydown", (s) => {
158
+ this.handleKeyDown(s);
159
+ }), this.editor.addEventListener("keyup", (s) => {
160
+ this.isShowingSuggestion && ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(s.key) && this.hideSuggestion(), ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(s.key) && this.updateBackground();
161
+ }), this.editor.addEventListener("click", () => {
162
+ this.isShowingSuggestion && this.hideSuggestion(), setTimeout(() => this.updateBackground(), 0);
163
+ }), this.editor.addEventListener("scroll", () => {
164
+ this.syncScroll();
165
+ }), this._loadApiKey(), this._loadSettings(), this.updateBackground();
166
+ }
167
+ /**
168
+ * Updates the background layer with current text and suggestions
169
+ */
170
+ updateBackground() {
171
+ const t = this.editor.value, e = this.editor.selectionStart;
172
+ let i = "";
173
+ if (this.isShowingSuggestion && this.fullSuggestion) {
174
+ const s = t.substring(0, e), o = t.substring(e), a = (Array.isArray(this.suggestionParagraphs) ? this.suggestionParagraphs.slice(this.currentParagraphIndex) : []).join(" ");
175
+ if (a.trim().length > 0) {
176
+ const n = s.endsWith(" ") || s === "" || a.startsWith(" ") || a === "" ? "" : " ", d = `<span class="suggestion-text">${this.escapeHtml(a)}</span>`;
177
+ i = this.escapeHtml(s).replace(/\n/g, "<br>") + n + d + this.escapeHtml(o).replace(/\n/g, "<br>");
178
+ } else
179
+ i = this.escapeHtml(t).replace(/\n/g, "<br>");
180
+ } else
181
+ i = this.escapeHtml(t).replace(/\n/g, "<br>");
182
+ this.editorBackground.innerHTML = i, this.syncScroll();
183
+ }
184
+ /**
185
+ * Synchronizes scroll position between editor and background
186
+ */
187
+ syncScroll() {
188
+ this.editorBackground.scrollTop = this.editor.scrollTop, this.editorBackground.scrollLeft = this.editor.scrollLeft;
189
+ }
190
+ /**
191
+ * Handles text input events
192
+ */
193
+ handleTextInput() {
194
+ this.hideSuggestion(), this.typingTimer && clearTimeout(this.typingTimer), this.apiKey && (this.typingTimer = window.setTimeout(() => {
195
+ this.requestSuggestion();
196
+ }, this.suggestionDelay));
197
+ }
198
+ /**
199
+ * Handles keyboard events
200
+ */
201
+ handleKeyDown(t) {
202
+ this.isShowingSuggestion && (t.key === "Tab" ? (t.preventDefault(), this.acceptSuggestion()) : t.key === "Escape" && (t.preventDefault(), this.hideSuggestion()));
203
+ }
204
+ /**
205
+ * Requests an AI suggestion for the current text
206
+ */
207
+ async requestSuggestion() {
208
+ if (!this.apiKey) return;
209
+ const t = this.editor.value;
210
+ if (!t.trim()) return;
211
+ const e = this.editor.selectionStart, i = t.substring(0, e);
212
+ if (this.dispatchEvent(new CustomEvent("beforeSuggestion", {
213
+ detail: {
214
+ text: i,
215
+ context: this.context,
216
+ apiEndpoint: this.apiEndpoint,
217
+ modelName: this.modelName,
218
+ systemPrompt: this.systemPrompt
219
+ },
220
+ cancelable: !0
221
+ })) !== !1) {
222
+ this.showLoading();
223
+ try {
224
+ const o = await this.callOpenAI(i);
225
+ this.hideLoading(), o && this.showSuggestion(o);
226
+ } catch (o) {
227
+ this.hideLoading(), this.showError("Failed to get AI suggestion: " + o.message);
228
+ }
229
+ }
230
+ }
231
+ /**
232
+ * Calls the OpenAI API for text completion
233
+ */
234
+ async callOpenAI(t) {
235
+ const e = [];
236
+ this.context && this.context.trim() && e.push(`Context:
237
+ ${this.context.trim()}`), e.push(`Please continue this text naturally:
238
+
239
+ ${t}`);
240
+ const i = e.join(`
241
+
242
+ `), s = {
243
+ "Content-Type": "application/json"
244
+ };
245
+ this.apiKey && this.apiKey.trim() !== "" && (s.Authorization = `Bearer ${this.apiKey}`);
246
+ const o = {
247
+ model: this.modelName,
248
+ messages: [
249
+ {
250
+ role: "system",
251
+ content: this.systemPrompt
252
+ },
253
+ {
254
+ role: "user",
255
+ content: i
256
+ }
257
+ ],
258
+ max_tokens: 150,
259
+ temperature: 0.7
260
+ }, r = await fetch(this.apiEndpoint, {
261
+ method: "POST",
262
+ headers: s,
263
+ body: JSON.stringify(o)
264
+ });
265
+ if (!r.ok) {
266
+ let n = "API request failed";
267
+ try {
268
+ n = (await r.json()).error?.message || n;
269
+ } catch (d) {
270
+ console.error("Failed to parse error response:", d), n = `HTTP ${r.status}: ${r.statusText}`;
271
+ }
272
+ throw new Error(n);
273
+ }
274
+ return (await r.json()).choices[0]?.message?.content?.trim();
275
+ }
276
+ /**
277
+ * Shows an AI suggestion
278
+ */
279
+ showSuggestion(t) {
280
+ this.fullSuggestion = t, this.suggestionParagraphs = this.splitIntoParagraphs(t), this.currentParagraphIndex = 0, this.isShowingSuggestion = !0, this.updateBackground();
281
+ }
282
+ /**
283
+ * Splits text into paragraphs/sentences
284
+ */
285
+ splitIntoParagraphs(t) {
286
+ return t.split(new RegExp("(?<=\\.)\\s+(?=[A-Z])|(?:\\n\\s*\\n)")).filter((i) => i.trim().length > 0);
287
+ }
288
+ /**
289
+ * Hides the current suggestion
290
+ */
291
+ hideSuggestion() {
292
+ this.isShowingSuggestion = !1, this.fullSuggestion = null, this.suggestionParagraphs = [], this.currentParagraphIndex = 0, this.updateBackground();
293
+ }
294
+ /**
295
+ * Accepts the current suggestion paragraph
296
+ */
297
+ acceptSuggestion() {
298
+ if (this.fullSuggestion && this.currentParagraphIndex < this.suggestionParagraphs.length) {
299
+ const t = this.editor.value, e = this.editor.selectionStart, i = this.suggestionParagraphs[this.currentParagraphIndex], s = t.substring(0, e), o = t.substring(e), r = s.endsWith(" ") || s === "" || i.startsWith(" ") || i === "" ? "" : " ", a = s + r + i + o;
300
+ this.editor.value = a;
301
+ const n = e + r.length + i.length;
302
+ this.editor.setSelectionRange(n, n), this.dispatchEvent(new CustomEvent("change", { detail: { value: this.editor.value } })), this.currentParagraphIndex++, this.currentParagraphIndex >= this.suggestionParagraphs.length ? this.hideSuggestion() : this.updateBackground();
303
+ }
304
+ }
305
+ /**
306
+ * Shows loading indicator
307
+ */
308
+ showLoading() {
309
+ this.loading.classList.add("show");
310
+ }
311
+ /**
312
+ * Hides loading indicator
313
+ */
314
+ hideLoading() {
315
+ this.loading.classList.remove("show");
316
+ }
317
+ /**
318
+ * Shows an error message
319
+ */
320
+ showError(t) {
321
+ console.error("AI Text Editor Error:", t), this.dispatchEvent(new CustomEvent("error", {
322
+ detail: { message: t },
323
+ bubbles: !0,
324
+ composed: !0
325
+ }));
326
+ }
327
+ /**
328
+ * Escapes HTML special characters
329
+ */
330
+ escapeHtml(t) {
331
+ if (typeof t != "string") {
332
+ if (t === null || typeof t > "u")
333
+ return "";
334
+ t = String(t);
335
+ }
336
+ return t.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
337
+ }
338
+ /**
339
+ * Sets the text content
340
+ */
341
+ setText(t) {
342
+ this.editor.value = t, this.dispatchEvent(new CustomEvent("change", { detail: { value: this.editor.value } })), this.hideSuggestion(), this.updateBackground();
343
+ }
344
+ /**
345
+ * Gets the text content
346
+ */
347
+ getText() {
348
+ return this.editor.value;
349
+ }
350
+ /**
351
+ * Sets the API key
352
+ */
353
+ setApiKey(t) {
354
+ this.apiKey = t, this._saveApiKey(), this.editorStatus.style.backgroundColor = this.apiKey ? "#4caf50" : "#777";
355
+ }
356
+ /**
357
+ * Saves API key to localStorage
358
+ */
359
+ _saveApiKey() {
360
+ if (!this.apiKey) {
361
+ localStorage.removeItem(g);
362
+ return;
363
+ }
364
+ const t = btoa(this.apiKey);
365
+ localStorage.setItem(g, t);
366
+ }
367
+ /**
368
+ * Loads API key from localStorage
369
+ */
370
+ _loadApiKey() {
371
+ const t = localStorage.getItem(g);
372
+ if (!t) {
373
+ this.setApiKey("");
374
+ return;
375
+ }
376
+ const e = atob(t);
377
+ this.setApiKey(e);
378
+ }
379
+ /**
380
+ * Gets the API key
381
+ */
382
+ getApiKey() {
383
+ return this.apiKey;
384
+ }
385
+ /**
386
+ * Sets the suggestion delay in seconds
387
+ */
388
+ setSuggestionDelay(t) {
389
+ this.suggestionDelay = t * 1e3;
390
+ }
391
+ /**
392
+ * Gets the suggestion delay in seconds
393
+ */
394
+ getSuggestionDelay() {
395
+ return this.suggestionDelay / 1e3;
396
+ }
397
+ /**
398
+ * Sets the system prompt
399
+ */
400
+ setSystemPrompt(t) {
401
+ this.systemPrompt = t;
402
+ }
403
+ /**
404
+ * Gets the system prompt
405
+ */
406
+ getSystemPrompt() {
407
+ return this.systemPrompt;
408
+ }
409
+ /**
410
+ * Sets the API endpoint
411
+ */
412
+ setApiEndpoint(t) {
413
+ this.apiEndpoint = t, this._saveSettings();
414
+ }
415
+ /**
416
+ * Gets the API endpoint
417
+ */
418
+ getApiEndpoint() {
419
+ return this.apiEndpoint;
420
+ }
421
+ /**
422
+ * Sets the model name
423
+ */
424
+ setModelName(t) {
425
+ this.modelName = t, this._saveSettings();
426
+ }
427
+ /**
428
+ * Gets the model name
429
+ */
430
+ getModelName() {
431
+ return this.modelName;
432
+ }
433
+ /**
434
+ * Sets the context
435
+ */
436
+ setContext(t) {
437
+ this.context = typeof t == "string" ? t : "";
438
+ }
439
+ /**
440
+ * Gets the context
441
+ */
442
+ getContext() {
443
+ return this.context;
444
+ }
445
+ /**
446
+ * Saves settings to localStorage
447
+ */
448
+ _saveSettings() {
449
+ const t = {
450
+ apiEndpoint: this.apiEndpoint,
451
+ modelName: this.modelName
452
+ };
453
+ localStorage.setItem("ai-text-editor-settings", JSON.stringify(t));
454
+ }
455
+ /**
456
+ * Loads settings from localStorage
457
+ */
458
+ _loadSettings() {
459
+ const t = localStorage.getItem("ai-text-editor-settings");
460
+ if (t)
461
+ try {
462
+ const e = JSON.parse(t);
463
+ e.apiEndpoint && (this.apiEndpoint = e.apiEndpoint), e.modelName && (this.modelName = e.modelName);
464
+ } catch (e) {
465
+ console.warn("Failed to load saved settings:", e);
466
+ }
467
+ }
468
+ }
469
+ const c = (h = "liwe3-ai-text-editor") => {
470
+ typeof window < "u" && !window.customElements.get(h) && customElements.define(h, l);
471
+ };
472
+ c();
473
+ export {
474
+ l as AITextEditorElement,
475
+ c as defineAITextEditor
476
+ };
477
+ //# sourceMappingURL=AITextEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AITextEditor.js","sources":["../src/AITextEditor.ts"],"sourcesContent":["/**\n * AITextEditor Web Component\n * A text editor with AI-powered text continuation suggestions\n */\n\nconst AI_TEXT_EDITOR_API_KEY = 'ai-text-editor-api-key';\n\nexport interface AITextEditorConfig {\n apiKey?: string;\n suggestionDelay?: number;\n systemPrompt?: string;\n apiEndpoint?: string;\n modelName?: string;\n context?: string;\n}\n\nexport class AITextEditorElement extends HTMLElement {\n declare shadowRoot: ShadowRoot;\n private editor!: HTMLTextAreaElement;\n private editorBackground!: HTMLElement;\n private loading!: HTMLElement;\n private editorStatus!: HTMLElement;\n\n private typingTimer: number | null = null;\n private fullSuggestion: string | null = null;\n private suggestionParagraphs: string[] = [];\n private currentParagraphIndex: number = 0;\n private isShowingSuggestion: boolean = false;\n\n private apiKey: string = '';\n private suggestionDelay: number = 1000;\n private systemPrompt: string = \"You are a helpful writing assistant. Continue the user's text naturally and coherently. Provide 1-3 sentences that would logically follow their writing. Keep the same tone and style. Do not repeat what they've already written.\";\n private apiEndpoint: string = 'https://api.openai.com/v1/chat/completions';\n private modelName: string = 'gpt-3.5-turbo';\n private context: string = '';\n\n constructor() {\n super();\n this.attachShadow({ mode: 'open' });\n this.render();\n this.init();\n }\n\n /**\n * Renders the component's HTML structure\n */\n private render(): void {\n this.shadowRoot.innerHTML = `\n <style>\n :host {\n display: block;\n width: 100%;\n height: 100%;\n }\n\n .editor-container {\n position: relative;\n height: 100%;\n display: flex;\n flex-direction: column;\n }\n\n .editor-status {\n position: absolute;\n top: 5px;\n left: 5px;\n width: 10px;\n height: 10px;\n border-radius: 100%;\n background: #777;\n z-index: 10;\n }\n\n .editor-wrapper {\n position: relative;\n width: 100%;\n flex: 1;\n display: flex;\n flex-direction: column;\n }\n\n .editor {\n width: 100%;\n height: 100%;\n border: 2px solid #e1e5e9;\n border-radius: 12px;\n padding: 20px;\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n font-size: 14px;\n line-height: 1.6;\n resize: none;\n background: #fafbfc;\n transition: all 0.3s ease;\n position: relative;\n z-index: 2;\n background: transparent;\n box-sizing: border-box;\n min-height: auto;\n }\n\n .editor:focus {\n outline: none;\n border-color: #4facfe;\n box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);\n }\n\n .editor-background {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n border: 2px solid #e1e5e9;\n border-radius: 12px;\n padding: 20px;\n font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;\n font-size: 14px;\n line-height: 1.6;\n background: #fafbfc;\n z-index: 1;\n pointer-events: none;\n white-space: pre-wrap;\n word-wrap: break-word;\n overflow: hidden;\n color: transparent;\n box-sizing: border-box;\n }\n\n .editor-wrapper:focus-within .editor-background {\n background: white;\n border-color: #4facfe;\n }\n\n .suggestion-text {\n color: #bbb;\n position: relative;\n }\n\n .suggestion-text.accepted {\n color: #ddd;\n text-decoration: line-through;\n }\n\n .loading {\n position: absolute;\n top: 5px;\n right: 10px;\n z-index: 10;\n display: none;\n }\n\n .loading.show {\n display: block;\n }\n\n .spinner {\n width: 10px;\n height: 10px;\n border: 2px solid #e1e5e9;\n border-top: 2px solid #4facfe;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n </style>\n\n <div class=\"editor-container\">\n <div class=\"editor-status\"></div>\n <div class=\"loading\" id=\"loading\">\n <div class=\"spinner\"></div>\n </div>\n\n <div class=\"editor-wrapper\">\n <div class=\"editor-background\" id=\"editorBackground\"></div>\n <textarea\n class=\"editor\"\n id=\"editor\"\n placeholder=\"Start writing your markdown text here...\"\n ></textarea>\n </div>\n </div>\n `;\n }\n\n /**\n * Initializes the component after rendering\n */\n private init(): void {\n const editor = this.shadowRoot.getElementById('editor') as HTMLTextAreaElement;\n const editorBackground = this.shadowRoot.getElementById('editorBackground') as HTMLElement;\n const loading = this.shadowRoot.getElementById('loading') as HTMLElement;\n\n this.editorStatus = this.shadowRoot.querySelector('.editor-status') as HTMLElement;\n this.editor = editor;\n this.editorBackground = editorBackground;\n this.loading = loading;\n\n this.editor.addEventListener('input', () => {\n this.handleTextInput();\n this.updateBackground();\n this.dispatchEvent(new CustomEvent('change', { detail: { value: this.editor.value } }));\n });\n\n this.editor.addEventListener('keydown', (e) => {\n this.handleKeyDown(e);\n });\n\n this.editor.addEventListener('keyup', (e) => {\n if (this.isShowingSuggestion && ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(e.key)) {\n this.hideSuggestion();\n }\n // Update background on cursor movement, after potentially hiding suggestion\n if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(e.key)) {\n this.updateBackground();\n }\n });\n\n this.editor.addEventListener('click', () => {\n if (this.isShowingSuggestion) {\n this.hideSuggestion();\n }\n // Update background on mouse click (cursor position change)\n // setTimeout ensures cursor position is updated before background redraw\n setTimeout(() => this.updateBackground(), 0);\n });\n\n this.editor.addEventListener('scroll', () => {\n this.syncScroll();\n });\n\n this._loadApiKey();\n this._loadSettings();\n this.updateBackground();\n }\n\n /**\n * Updates the background layer with current text and suggestions\n */\n private updateBackground(): void {\n const currentText = this.editor.value;\n const cursorPosition = this.editor.selectionStart;\n\n let finalHtmlContent = '';\n\n if (this.isShowingSuggestion && this.fullSuggestion) {\n const beforeCursorText = currentText.substring(0, cursorPosition);\n const afterCursorText = currentText.substring(cursorPosition);\n\n // Determine the pending (not yet accepted) part of the suggestion\n const pendingParagraphs = Array.isArray(this.suggestionParagraphs)\n ? this.suggestionParagraphs.slice(this.currentParagraphIndex)\n : [];\n const pendingSuggestionText = pendingParagraphs.join(' ');\n\n if (pendingSuggestionText.trim().length > 0) {\n const mainSpacer = (beforeCursorText.endsWith(' ') || beforeCursorText === '' || pendingSuggestionText.startsWith(' ') || pendingSuggestionText === '') ? '' : ' ';\n const suggestionBlockHtml = `<span class=\"suggestion-text\">${this.escapeHtml(pendingSuggestionText)}</span>`;\n\n finalHtmlContent =\n this.escapeHtml(beforeCursorText).replace(/\\n/g, '<br>') +\n mainSpacer +\n suggestionBlockHtml +\n this.escapeHtml(afterCursorText).replace(/\\n/g, '<br>');\n } else {\n finalHtmlContent = this.escapeHtml(currentText).replace(/\\n/g, '<br>');\n }\n } else {\n finalHtmlContent = this.escapeHtml(currentText).replace(/\\n/g, '<br>');\n }\n\n this.editorBackground.innerHTML = finalHtmlContent;\n this.syncScroll();\n }\n\n /**\n * Synchronizes scroll position between editor and background\n */\n private syncScroll(): void {\n this.editorBackground.scrollTop = this.editor.scrollTop;\n this.editorBackground.scrollLeft = this.editor.scrollLeft;\n }\n\n /**\n * Handles text input events\n */\n private handleTextInput(): void {\n this.hideSuggestion();\n\n if (this.typingTimer) {\n clearTimeout(this.typingTimer);\n }\n\n if (!this.apiKey) return;\n\n this.typingTimer = window.setTimeout(() => {\n this.requestSuggestion();\n }, this.suggestionDelay);\n }\n\n /**\n * Handles keyboard events\n */\n private handleKeyDown(e: KeyboardEvent): void {\n if (this.isShowingSuggestion) {\n if (e.key === 'Tab') {\n e.preventDefault();\n this.acceptSuggestion();\n } else if (e.key === 'Escape') {\n e.preventDefault();\n this.hideSuggestion();\n }\n }\n }\n\n /**\n * Requests an AI suggestion for the current text\n */\n private async requestSuggestion(): Promise<void> {\n if (!this.apiKey) return;\n\n const currentText = this.editor.value;\n if (!currentText.trim()) return;\n\n // Get text up to cursor position for context\n const cursorPosition = this.editor.selectionStart;\n const textUpToCursor = currentText.substring(0, cursorPosition);\n\n // Dispatch an event before starting the AI request, allow listeners to cancel\n const proceed = this.dispatchEvent(new CustomEvent('beforeSuggestion', {\n detail: {\n text: textUpToCursor,\n context: this.context,\n apiEndpoint: this.apiEndpoint,\n modelName: this.modelName,\n systemPrompt: this.systemPrompt\n },\n cancelable: true\n }));\n\n if (proceed === false) {\n return; // aborted by listener via event.preventDefault()\n }\n\n this.showLoading();\n\n try {\n const suggestion = await this.callOpenAI(textUpToCursor);\n this.hideLoading();\n\n if (suggestion) {\n this.showSuggestion(suggestion);\n }\n } catch (error) {\n this.hideLoading();\n this.showError('Failed to get AI suggestion: ' + (error as Error).message);\n }\n }\n\n /**\n * Calls the OpenAI API for text completion\n */\n private async callOpenAI(text: string): Promise<string> {\n const parts: string[] = [];\n if (this.context && this.context.trim()) {\n parts.push(`Context:\\n${this.context.trim()}`);\n }\n parts.push(`Please continue this text naturally:\\n\\n${text}`);\n const userContent = parts.join('\\n\\n');\n\n // Prepare headers - only add Authorization if API key is provided\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json'\n };\n\n if (this.apiKey && this.apiKey.trim() !== '') {\n headers['Authorization'] = `Bearer ${this.apiKey}`;\n }\n\n const requestBody = {\n model: this.modelName,\n messages: [\n {\n role: 'system',\n content: this.systemPrompt\n },\n {\n role: 'user',\n content: userContent\n }\n ],\n max_tokens: 150,\n temperature: 0.7\n };\n\n const response = await fetch(this.apiEndpoint, {\n method: 'POST',\n headers: headers,\n body: JSON.stringify(requestBody)\n });\n\n if (!response.ok) {\n let errorMessage = 'API request failed';\n try {\n const errorData = await response.json();\n errorMessage = errorData.error?.message || errorMessage;\n } catch (parseError) {\n console.error('Failed to parse error response:', parseError);\n errorMessage = `HTTP ${response.status}: ${response.statusText}`;\n }\n throw new Error(errorMessage);\n }\n\n const data = await response.json();\n return data.choices[0]?.message?.content?.trim();\n }\n\n /**\n * Shows an AI suggestion\n */\n private showSuggestion(suggestion: string): void {\n this.fullSuggestion = suggestion;\n this.suggestionParagraphs = this.splitIntoParagraphs(suggestion);\n this.currentParagraphIndex = 0;\n this.isShowingSuggestion = true;\n this.updateBackground();\n }\n\n /**\n * Splits text into paragraphs/sentences\n */\n private splitIntoParagraphs(text: string): string[] {\n // Split on periods followed by space and capital letter, or double newlines\n const sentences = text.split(/(?<=\\.)\\s+(?=[A-Z])|(?:\\n\\s*\\n)/);\n return sentences.filter(sentence => sentence.trim().length > 0);\n }\n\n /**\n * Hides the current suggestion\n */\n private hideSuggestion(): void {\n this.isShowingSuggestion = false;\n this.fullSuggestion = null;\n this.suggestionParagraphs = [];\n this.currentParagraphIndex = 0;\n this.updateBackground();\n }\n\n /**\n * Accepts the current suggestion paragraph\n */\n private acceptSuggestion(): void {\n if (this.fullSuggestion && this.currentParagraphIndex < this.suggestionParagraphs.length) {\n const currentText = this.editor.value;\n const cursorPosition = this.editor.selectionStart;\n const paragraphToAdd = this.suggestionParagraphs[this.currentParagraphIndex];\n\n // Insert at cursor position\n const beforeCursor = currentText.substring(0, cursorPosition);\n const afterCursor = currentText.substring(cursorPosition);\n\n // Refined spacer logic\n const spacer = (beforeCursor.endsWith(' ') || beforeCursor === '' || paragraphToAdd.startsWith(' ') || paragraphToAdd === '') ? '' : ' ';\n const newText = beforeCursor + spacer + paragraphToAdd + afterCursor;\n\n this.editor.value = newText;\n\n // Update cursor position to after the inserted text\n const newCursorPosition = cursorPosition + spacer.length + paragraphToAdd.length;\n this.editor.setSelectionRange(newCursorPosition, newCursorPosition);\n this.dispatchEvent(new CustomEvent('change', { detail: { value: this.editor.value } }));\n\n // Move to next paragraph or hide if no more paragraphs\n this.currentParagraphIndex++;\n if (this.currentParagraphIndex >= this.suggestionParagraphs.length) {\n this.hideSuggestion();\n } else {\n this.updateBackground();\n }\n }\n }\n\n /**\n * Shows loading indicator\n */\n private showLoading(): void {\n this.loading.classList.add('show');\n }\n\n /**\n * Hides loading indicator\n */\n private hideLoading(): void {\n this.loading.classList.remove('show');\n }\n\n /**\n * Shows an error message\n */\n private showError(message: string): void {\n console.error('AI Text Editor Error:', message);\n // Try to dispatch a custom error event that can be handled by parent\n this.dispatchEvent(new CustomEvent('error', {\n detail: { message },\n bubbles: true,\n composed: true\n }));\n }\n\n /**\n * Escapes HTML special characters\n */\n private escapeHtml(unsafe: any): string {\n if (typeof unsafe !== 'string') {\n if (unsafe === null || typeof unsafe === 'undefined') {\n return '';\n }\n unsafe = String(unsafe);\n }\n return unsafe\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#039;\");\n }\n\n /**\n * Sets the text content\n */\n setText(text: string): void {\n this.editor.value = text;\n this.dispatchEvent(new CustomEvent('change', { detail: { value: this.editor.value } }));\n this.hideSuggestion();\n this.updateBackground();\n }\n\n /**\n * Gets the text content\n */\n getText(): string {\n return this.editor.value;\n }\n\n /**\n * Sets the API key\n */\n setApiKey(key: string): void {\n this.apiKey = key;\n this._saveApiKey();\n this.editorStatus.style.backgroundColor = this.apiKey ? '#4caf50' : '#777';\n }\n\n /**\n * Saves API key to localStorage\n */\n private _saveApiKey(): void {\n if (!this.apiKey) {\n localStorage.removeItem(AI_TEXT_EDITOR_API_KEY);\n return;\n }\n\n // Encrypt the API key in base64 format\n const encryptedKey = btoa(this.apiKey);\n localStorage.setItem(AI_TEXT_EDITOR_API_KEY, encryptedKey);\n }\n\n /**\n * Loads API key from localStorage\n */\n private _loadApiKey(): void {\n const savedKey = localStorage.getItem(AI_TEXT_EDITOR_API_KEY);\n if (!savedKey) {\n this.setApiKey('');\n return;\n }\n\n const decryptedKey = atob(savedKey);\n this.setApiKey(decryptedKey);\n }\n\n /**\n * Gets the API key\n */\n getApiKey(): string {\n return this.apiKey;\n }\n\n /**\n * Sets the suggestion delay in seconds\n */\n setSuggestionDelay(seconds: number): void {\n this.suggestionDelay = seconds * 1000;\n }\n\n /**\n * Gets the suggestion delay in seconds\n */\n getSuggestionDelay(): number {\n return this.suggestionDelay / 1000;\n }\n\n /**\n * Sets the system prompt\n */\n setSystemPrompt(prompt: string): void {\n this.systemPrompt = prompt;\n }\n\n /**\n * Gets the system prompt\n */\n getSystemPrompt(): string {\n return this.systemPrompt;\n }\n\n /**\n * Sets the API endpoint\n */\n setApiEndpoint(endpoint: string): void {\n this.apiEndpoint = endpoint;\n this._saveSettings();\n }\n\n /**\n * Gets the API endpoint\n */\n getApiEndpoint(): string {\n return this.apiEndpoint;\n }\n\n /**\n * Sets the model name\n */\n setModelName(modelName: string): void {\n this.modelName = modelName;\n this._saveSettings();\n }\n\n /**\n * Gets the model name\n */\n getModelName(): string {\n return this.modelName;\n }\n\n /**\n * Sets the context\n */\n setContext(context: string): void {\n this.context = typeof context === 'string' ? context : '';\n }\n\n /**\n * Gets the context\n */\n getContext(): string {\n return this.context;\n }\n\n /**\n * Saves settings to localStorage\n */\n private _saveSettings(): void {\n const settings = {\n apiEndpoint: this.apiEndpoint,\n modelName: this.modelName\n };\n localStorage.setItem('ai-text-editor-settings', JSON.stringify(settings));\n }\n\n /**\n * Loads settings from localStorage\n */\n private _loadSettings(): void {\n const savedSettings = localStorage.getItem('ai-text-editor-settings');\n if (savedSettings) {\n try {\n const settings = JSON.parse(savedSettings);\n if (settings.apiEndpoint) {\n this.apiEndpoint = settings.apiEndpoint;\n }\n if (settings.modelName) {\n this.modelName = settings.modelName;\n }\n } catch (error) {\n console.warn('Failed to load saved settings:', error);\n }\n }\n }\n}\n\n/**\n * Conditionally defines the custom element if in a browser environment.\n */\nconst defineAITextEditor = (tagName: string = 'liwe3-ai-text-editor'): void => {\n if (typeof window !== 'undefined' && !window.customElements.get(tagName)) {\n customElements.define(tagName, AITextEditorElement);\n }\n};\n\n// Auto-register with default tag name\ndefineAITextEditor();\n\nexport { defineAITextEditor };\n"],"names":["AI_TEXT_EDITOR_API_KEY","AITextEditorElement","editor","editorBackground","loading","e","currentText","cursorPosition","finalHtmlContent","beforeCursorText","afterCursorText","pendingSuggestionText","mainSpacer","suggestionBlockHtml","textUpToCursor","suggestion","error","text","parts","userContent","headers","requestBody","response","errorMessage","parseError","sentence","paragraphToAdd","beforeCursor","afterCursor","spacer","newText","newCursorPosition","message","unsafe","key","encryptedKey","savedKey","decryptedKey","seconds","prompt","endpoint","modelName","context","settings","savedSettings","defineAITextEditor","tagName"],"mappings":"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"}