@liwe3/webcomponents 1.1.0 → 1.1.10

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 (56) hide show
  1. package/dist/AIMarkdownEditor.d.ts +35 -0
  2. package/dist/AIMarkdownEditor.d.ts.map +1 -0
  3. package/dist/AIMarkdownEditor.js +412 -0
  4. package/dist/AIMarkdownEditor.js.map +1 -0
  5. package/dist/AITextEditor.d.ts +10 -0
  6. package/dist/AITextEditor.d.ts.map +1 -1
  7. package/dist/AITextEditor.js +63 -27
  8. package/dist/AITextEditor.js.map +1 -1
  9. package/dist/ButtonToolbar.d.ts +35 -0
  10. package/dist/ButtonToolbar.d.ts.map +1 -0
  11. package/dist/ButtonToolbar.js +220 -0
  12. package/dist/ButtonToolbar.js.map +1 -0
  13. package/dist/CheckList.d.ts +31 -0
  14. package/dist/CheckList.d.ts.map +1 -0
  15. package/dist/CheckList.js +336 -0
  16. package/dist/CheckList.js.map +1 -0
  17. package/dist/ChunkUploader.d.ts +22 -0
  18. package/dist/ChunkUploader.d.ts.map +1 -1
  19. package/dist/ChunkUploader.js +245 -103
  20. package/dist/ChunkUploader.js.map +1 -1
  21. package/dist/ComicBalloon.d.ts +82 -0
  22. package/dist/ComicBalloon.d.ts.map +1 -0
  23. package/dist/ComicBalloon.js +346 -0
  24. package/dist/ComicBalloon.js.map +1 -0
  25. package/dist/Dialog.d.ts +102 -0
  26. package/dist/Dialog.d.ts.map +1 -0
  27. package/dist/Dialog.js +299 -0
  28. package/dist/Dialog.js.map +1 -0
  29. package/dist/MarkdownPreview.d.ts +25 -0
  30. package/dist/MarkdownPreview.d.ts.map +1 -0
  31. package/dist/MarkdownPreview.js +147 -0
  32. package/dist/MarkdownPreview.js.map +1 -0
  33. package/dist/ResizableCropper.d.ts +158 -0
  34. package/dist/ResizableCropper.d.ts.map +1 -0
  35. package/dist/ResizableCropper.js +562 -0
  36. package/dist/ResizableCropper.js.map +1 -0
  37. package/dist/SmartSelect.d.ts +1 -0
  38. package/dist/SmartSelect.d.ts.map +1 -1
  39. package/dist/SmartSelect.js +45 -2
  40. package/dist/SmartSelect.js.map +1 -1
  41. package/dist/index.d.ts +16 -9
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +52 -29
  44. package/dist/index.js.map +1 -1
  45. package/package.json +33 -3
  46. package/src/AIMarkdownEditor.ts +568 -0
  47. package/src/AITextEditor.ts +97 -2
  48. package/src/ButtonToolbar.ts +302 -0
  49. package/src/CheckList.ts +438 -0
  50. package/src/ChunkUploader.ts +837 -623
  51. package/src/ComicBalloon.ts +709 -0
  52. package/src/Dialog.ts +510 -0
  53. package/src/MarkdownPreview.ts +213 -0
  54. package/src/ResizableCropper.ts +1099 -0
  55. package/src/SmartSelect.ts +48 -2
  56. package/src/index.ts +110 -47
@@ -1,7 +1,7 @@
1
- const g = "ai-text-editor-api-key";
2
- class l extends HTMLElement {
1
+ const l = "ai-text-editor-api-key";
2
+ class g extends HTMLElement {
3
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();
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.embedded = !1, this.attachShadow({ mode: "open" }), this.render(), this.init();
5
5
  }
6
6
  /**
7
7
  * Renders the component's HTML structure
@@ -32,6 +32,10 @@ class l extends HTMLElement {
32
32
  background: #777;
33
33
  z-index: 10;
34
34
  }
35
+
36
+ :host([embedded]) .editor-status {
37
+ display: none;
38
+ }
35
39
 
36
40
  .editor-wrapper {
37
41
  position: relative;
@@ -59,12 +63,21 @@ class l extends HTMLElement {
59
63
  box-sizing: border-box;
60
64
  min-height: auto;
61
65
  }
66
+
67
+ :host([embedded]) .editor {
68
+ border: none;
69
+ border-radius: 0;
70
+ }
62
71
 
63
72
  .editor:focus {
64
73
  outline: none;
65
74
  border-color: #4facfe;
66
75
  box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.1);
67
76
  }
77
+
78
+ :host([embedded]) .editor:focus {
79
+ box-shadow: none;
80
+ }
68
81
 
69
82
  .editor-background {
70
83
  position: absolute;
@@ -87,11 +100,20 @@ class l extends HTMLElement {
87
100
  color: transparent;
88
101
  box-sizing: border-box;
89
102
  }
103
+
104
+ :host([embedded]) .editor-background {
105
+ border: none;
106
+ border-radius: 0;
107
+ }
90
108
 
91
109
  .editor-wrapper:focus-within .editor-background {
92
110
  background: white;
93
111
  border-color: #4facfe;
94
112
  }
113
+
114
+ :host([embedded]) .editor-wrapper:focus-within .editor-background {
115
+ border-color: transparent;
116
+ }
95
117
 
96
118
  .suggestion-text {
97
119
  color: #bbb;
@@ -110,6 +132,10 @@ class l extends HTMLElement {
110
132
  z-index: 10;
111
133
  display: none;
112
134
  }
135
+
136
+ :host([embedded]) .loading {
137
+ display: none !important;
138
+ }
113
139
 
114
140
  .loading.show {
115
141
  display: block;
@@ -173,8 +199,8 @@ class l extends HTMLElement {
173
199
  if (this.isShowingSuggestion && this.fullSuggestion) {
174
200
  const s = t.substring(0, e), o = t.substring(e), a = (Array.isArray(this.suggestionParagraphs) ? this.suggestionParagraphs.slice(this.currentParagraphIndex) : []).join(" ");
175
201
  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>");
202
+ const r = s.endsWith(" ") || s === "" || a.startsWith(" ") || a === "" ? "" : " ", d = `<span class="suggestion-text">${this.escapeHtml(a)}</span>`;
203
+ i = this.escapeHtml(s).replace(/\n/g, "<br>") + r + d + this.escapeHtml(o).replace(/\n/g, "<br>");
178
204
  } else
179
205
  i = this.escapeHtml(t).replace(/\n/g, "<br>");
180
206
  } else
@@ -224,7 +250,11 @@ class l extends HTMLElement {
224
250
  const o = await this.callOpenAI(i);
225
251
  this.hideLoading(), o && this.showSuggestion(o);
226
252
  } catch (o) {
227
- this.hideLoading(), this.showError("Failed to get AI suggestion: " + o.message);
253
+ this.hideLoading(), this.showError("Failed to get AI suggestion: " + o.message), this.dispatchEvent(new CustomEvent("oncompletionerror", {
254
+ detail: { error: o.message },
255
+ bubbles: !0,
256
+ composed: !0
257
+ }));
228
258
  }
229
259
  }
230
260
  }
@@ -257,21 +287,21 @@ ${t}`);
257
287
  ],
258
288
  max_tokens: 150,
259
289
  temperature: 0.7
260
- }, r = await fetch(this.apiEndpoint, {
290
+ }, n = await fetch(this.apiEndpoint, {
261
291
  method: "POST",
262
292
  headers: s,
263
293
  body: JSON.stringify(o)
264
294
  });
265
- if (!r.ok) {
266
- let n = "API request failed";
295
+ if (!n.ok) {
296
+ let r = "API request failed";
267
297
  try {
268
- n = (await r.json()).error?.message || n;
298
+ r = (await n.json()).error?.message || r;
269
299
  } catch (d) {
270
- console.error("Failed to parse error response:", d), n = `HTTP ${r.status}: ${r.statusText}`;
300
+ console.error("Failed to parse error response:", d), r = `HTTP ${n.status}: ${n.statusText}`;
271
301
  }
272
- throw new Error(n);
302
+ throw new Error(r);
273
303
  }
274
- return (await r.json()).choices[0]?.message?.content?.trim();
304
+ return (await n.json()).choices[0]?.message?.content?.trim();
275
305
  }
276
306
  /**
277
307
  * Shows an AI suggestion
@@ -296,23 +326,23 @@ ${t}`);
296
326
  */
297
327
  acceptSuggestion() {
298
328
  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;
329
+ const t = this.editor.value, e = this.editor.selectionStart, i = this.suggestionParagraphs[this.currentParagraphIndex], s = t.substring(0, e), o = t.substring(e), n = s.endsWith(" ") || s === "" || i.startsWith(" ") || i === "" ? "" : " ", a = s + n + i + o;
300
330
  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();
331
+ const r = e + n.length + i.length;
332
+ this.editor.setSelectionRange(r, r), this.dispatchEvent(new CustomEvent("change", { detail: { value: this.editor.value } })), this.currentParagraphIndex++, this.currentParagraphIndex >= this.suggestionParagraphs.length ? this.hideSuggestion() : this.updateBackground();
303
333
  }
304
334
  }
305
335
  /**
306
336
  * Shows loading indicator
307
337
  */
308
338
  showLoading() {
309
- this.loading.classList.add("show");
339
+ this.loading.classList.add("show"), this.onLoadingChangeCallback && this.onLoadingChangeCallback(!0);
310
340
  }
311
341
  /**
312
342
  * Hides loading indicator
313
343
  */
314
344
  hideLoading() {
315
- this.loading.classList.remove("show");
345
+ this.loading.classList.remove("show"), this.onLoadingChangeCallback && this.onLoadingChangeCallback(!1);
316
346
  }
317
347
  /**
318
348
  * Shows an error message
@@ -351,24 +381,24 @@ ${t}`);
351
381
  * Sets the API key
352
382
  */
353
383
  setApiKey(t) {
354
- this.apiKey = t, this._saveApiKey(), this.editorStatus.style.backgroundColor = this.apiKey ? "#4caf50" : "#777";
384
+ this.apiKey = t, this._saveApiKey(), this.editorStatus.style.backgroundColor = this.apiKey ? "#4caf50" : "#777", this.onStatusChangeCallback && this.onStatusChangeCallback(!!this.apiKey);
355
385
  }
356
386
  /**
357
387
  * Saves API key to localStorage
358
388
  */
359
389
  _saveApiKey() {
360
390
  if (!this.apiKey) {
361
- localStorage.removeItem(g);
391
+ localStorage.removeItem(l);
362
392
  return;
363
393
  }
364
394
  const t = btoa(this.apiKey);
365
- localStorage.setItem(g, t);
395
+ localStorage.setItem(l, t);
366
396
  }
367
397
  /**
368
398
  * Loads API key from localStorage
369
399
  */
370
400
  _loadApiKey() {
371
- const t = localStorage.getItem(g);
401
+ const t = localStorage.getItem(l);
372
402
  if (!t) {
373
403
  this.setApiKey("");
374
404
  return;
@@ -442,6 +472,12 @@ ${t}`);
442
472
  getContext() {
443
473
  return this.context;
444
474
  }
475
+ /**
476
+ * Configure the editor with callbacks and embedded mode
477
+ */
478
+ configure(t) {
479
+ t.embedded !== void 0 && (this.embedded = t.embedded, this.embedded ? this.setAttribute("embedded", "") : this.removeAttribute("embedded")), t.onStatusChange && (this.onStatusChangeCallback = t.onStatusChange, this.onStatusChangeCallback(!!this.apiKey)), t.onLoadingChange && (this.onLoadingChangeCallback = t.onLoadingChange), t.apiKey !== void 0 && this.setApiKey(t.apiKey), t.suggestionDelay !== void 0 && this.setSuggestionDelay(t.suggestionDelay), t.systemPrompt !== void 0 && this.setSystemPrompt(t.systemPrompt), t.apiEndpoint !== void 0 && this.setApiEndpoint(t.apiEndpoint), t.modelName !== void 0 && this.setModelName(t.modelName), t.context !== void 0 && this.setContext(t.context);
480
+ }
445
481
  /**
446
482
  * Saves settings to localStorage
447
483
  */
@@ -466,12 +502,12 @@ ${t}`);
466
502
  }
467
503
  }
468
504
  }
469
- const c = (h = "liwe3-ai-text-editor") => {
470
- typeof window < "u" && !window.customElements.get(h) && customElements.define(h, l);
505
+ const p = (h = "liwe3-ai-text-editor") => {
506
+ typeof window < "u" && !window.customElements.get(h) && customElements.define(h, g);
471
507
  };
472
- c();
508
+ p();
473
509
  export {
474
- l as AITextEditorElement,
475
- c as defineAITextEditor
510
+ g as AITextEditorElement,
511
+ p as defineAITextEditor
476
512
  };
477
513
  //# sourceMappingURL=AITextEditor.js.map
@@ -1 +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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"}
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; // Delay in seconds before showing AI suggestions\n systemPrompt?: string;\n apiEndpoint?: string;\n modelName?: string;\n context?: string;\n embedded?: boolean;\n onStatusChange?: (hasApiKey: boolean) => void;\n onLoadingChange?: (isLoading: boolean) => void;\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; // Stored in milliseconds (default: 1000ms = 1 second)\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 private embedded: boolean = false;\n private onStatusChangeCallback?: (hasApiKey: boolean) => void;\n private onLoadingChangeCallback?: (isLoading: boolean) => void;\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 :host([embedded]) .editor-status {\n display: none;\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 :host([embedded]) .editor {\n border: none;\n border-radius: 0;\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 :host([embedded]) .editor:focus {\n box-shadow: none;\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 :host([embedded]) .editor-background {\n border: none;\n border-radius: 0;\n }\n\n .editor-wrapper:focus-within .editor-background {\n background: white;\n border-color: #4facfe;\n }\n \n :host([embedded]) .editor-wrapper:focus-within .editor-background {\n border-color: transparent;\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 :host([embedded]) .loading {\n display: none !important;\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 this.dispatchEvent(new CustomEvent('oncompletionerror', {\n detail: { error: (error as Error).message },\n bubbles: true,\n composed: true\n }));\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 if (this.onLoadingChangeCallback) {\n this.onLoadingChangeCallback(true);\n }\n }\n\n /**\n * Hides loading indicator\n */\n private hideLoading(): void {\n this.loading.classList.remove('show');\n if (this.onLoadingChangeCallback) {\n this.onLoadingChangeCallback(false);\n }\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 if (this.onStatusChangeCallback) {\n this.onStatusChangeCallback(!!this.apiKey);\n }\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 * Configure the editor with callbacks and embedded mode\n */\n configure(config: Partial<AITextEditorConfig>): void {\n if (config.embedded !== undefined) {\n this.embedded = config.embedded;\n if (this.embedded) {\n this.setAttribute('embedded', '');\n } else {\n this.removeAttribute('embedded');\n }\n }\n \n if (config.onStatusChange) {\n this.onStatusChangeCallback = config.onStatusChange;\n // Immediately call with current status\n this.onStatusChangeCallback(!!this.apiKey);\n }\n \n if (config.onLoadingChange) {\n this.onLoadingChangeCallback = config.onLoadingChange;\n }\n \n if (config.apiKey !== undefined) {\n this.setApiKey(config.apiKey);\n }\n \n if (config.suggestionDelay !== undefined) {\n this.setSuggestionDelay(config.suggestionDelay);\n }\n \n if (config.systemPrompt !== undefined) {\n this.setSystemPrompt(config.systemPrompt);\n }\n \n if (config.apiEndpoint !== undefined) {\n this.setApiEndpoint(config.apiEndpoint);\n }\n \n if (config.modelName !== undefined) {\n this.setModelName(config.modelName);\n }\n \n if (config.context !== undefined) {\n this.setContext(config.context);\n }\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","config","settings","savedSettings","defineAITextEditor","tagName"],"mappings":"AAKA,MAAMA,IAAyB;AAcxB,MAAMC,UAA4B,YAAY;AAAA,EAwBnD,cAAc;AACZ,UAAA,GAlBF,KAAQ,cAA6B,MACrC,KAAQ,iBAAgC,MACxC,KAAQ,uBAAiC,CAAA,GACzC,KAAQ,wBAAgC,GACxC,KAAQ,sBAA+B,IAEvC,KAAQ,SAAiB,IACzB,KAAQ,kBAA0B,KAClC,KAAQ,eAAuB,sOAC/B,KAAQ,cAAsB,8CAC9B,KAAQ,YAAoB,iBAC5B,KAAQ,UAAkB,IAE1B,KAAQ,WAAoB,IAM1B,KAAK,aAAa,EAAE,MAAM,OAAA,CAAQ,GAClC,KAAK,OAAA,GACL,KAAK,KAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,SAAe;AACrB,SAAK,WAAW,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqK9B;AAAA;AAAA;AAAA;AAAA,EAKQ,OAAa;AACnB,UAAMC,IAAS,KAAK,WAAW,eAAe,QAAQ,GAChDC,IAAmB,KAAK,WAAW,eAAe,kBAAkB,GACpEC,IAAU,KAAK,WAAW,eAAe,SAAS;AAExD,SAAK,eAAe,KAAK,WAAW,cAAc,gBAAgB,GAClE,KAAK,SAASF,GACd,KAAK,mBAAmBC,GACxB,KAAK,UAAUC,GAEf,KAAK,OAAO,iBAAiB,SAAS,MAAM;AAC1C,WAAK,gBAAA,GACL,KAAK,iBAAA,GACL,KAAK,cAAc,IAAI,YAAY,UAAU,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,MAAA,EAAM,CAAG,CAAC;AAAA,IACxF,CAAC,GAED,KAAK,OAAO,iBAAiB,WAAW,CAACC,MAAM;AAC7C,WAAK,cAAcA,CAAC;AAAA,IACtB,CAAC,GAED,KAAK,OAAO,iBAAiB,SAAS,CAACA,MAAM;AAC3C,MAAI,KAAK,uBAAuB,CAAC,aAAa,cAAc,WAAW,aAAa,QAAQ,KAAK,EAAE,SAASA,EAAE,GAAG,KAC/G,KAAK,eAAA,GAGH,CAAC,aAAa,cAAc,WAAW,aAAa,QAAQ,KAAK,EAAE,SAASA,EAAE,GAAG,KACnF,KAAK,iBAAA;AAAA,IAET,CAAC,GAED,KAAK,OAAO,iBAAiB,SAAS,MAAM;AAC1C,MAAI,KAAK,uBACP,KAAK,eAAA,GAIP,WAAW,MAAM,KAAK,iBAAA,GAAoB,CAAC;AAAA,IAC7C,CAAC,GAED,KAAK,OAAO,iBAAiB,UAAU,MAAM;AAC3C,WAAK,WAAA;AAAA,IACP,CAAC,GAED,KAAK,YAAA,GACL,KAAK,cAAA,GACL,KAAK,iBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,UAAMC,IAAc,KAAK,OAAO,OAC1BC,IAAiB,KAAK,OAAO;AAEnC,QAAIC,IAAmB;AAEvB,QAAI,KAAK,uBAAuB,KAAK,gBAAgB;AACnD,YAAMC,IAAmBH,EAAY,UAAU,GAAGC,CAAc,GAC1DG,IAAkBJ,EAAY,UAAUC,CAAc,GAMtDI,KAHoB,MAAM,QAAQ,KAAK,oBAAoB,IAC7D,KAAK,qBAAqB,MAAM,KAAK,qBAAqB,IAC1D,CAAA,GAC4C,KAAK,GAAG;AAExD,UAAIA,EAAsB,OAAO,SAAS,GAAG;AAC3C,cAAMC,IAAcH,EAAiB,SAAS,GAAG,KAAKA,MAAqB,MAAME,EAAsB,WAAW,GAAG,KAAKA,MAA0B,KAAM,KAAK,KACzJE,IAAsB,iCAAiC,KAAK,WAAWF,CAAqB,CAAC;AAEnG,QAAAH,IACE,KAAK,WAAWC,CAAgB,EAAE,QAAQ,OAAO,MAAM,IACvDG,IACAC,IACA,KAAK,WAAWH,CAAe,EAAE,QAAQ,OAAO,MAAM;AAAA,MAC1D;AACE,QAAAF,IAAmB,KAAK,WAAWF,CAAW,EAAE,QAAQ,OAAO,MAAM;AAAA,IAEzE;AACE,MAAAE,IAAmB,KAAK,WAAWF,CAAW,EAAE,QAAQ,OAAO,MAAM;AAGvE,SAAK,iBAAiB,YAAYE,GAClC,KAAK,WAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,aAAmB;AACzB,SAAK,iBAAiB,YAAY,KAAK,OAAO,WAC9C,KAAK,iBAAiB,aAAa,KAAK,OAAO;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAwB;AAO9B,IANA,KAAK,eAAA,GAED,KAAK,eACP,aAAa,KAAK,WAAW,GAG1B,KAAK,WAEV,KAAK,cAAc,OAAO,WAAW,MAAM;AACzC,WAAK,kBAAA;AAAA,IACP,GAAG,KAAK,eAAe;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAcH,GAAwB;AAC5C,IAAI,KAAK,wBACHA,EAAE,QAAQ,SACZA,EAAE,eAAA,GACF,KAAK,iBAAA,KACIA,EAAE,QAAQ,aACnBA,EAAE,eAAA,GACF,KAAK,eAAA;AAAA,EAGX;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,oBAAmC;AAC/C,QAAI,CAAC,KAAK,OAAQ;AAElB,UAAMC,IAAc,KAAK,OAAO;AAChC,QAAI,CAACA,EAAY,OAAQ;AAGzB,UAAMC,IAAiB,KAAK,OAAO,gBAC7BO,IAAiBR,EAAY,UAAU,GAAGC,CAAc;AAc9D,QAXgB,KAAK,cAAc,IAAI,YAAY,oBAAoB;AAAA,MACrE,QAAQ;AAAA,QACN,MAAMO;AAAA,QACN,SAAS,KAAK;AAAA,QACd,aAAa,KAAK;AAAA,QAClB,WAAW,KAAK;AAAA,QAChB,cAAc,KAAK;AAAA,MAAA;AAAA,MAErB,YAAY;AAAA,IAAA,CACb,CAAC,MAEc,IAIhB;AAAA,WAAK,YAAA;AAEL,UAAI;AACF,cAAMC,IAAa,MAAM,KAAK,WAAWD,CAAc;AACvD,aAAK,YAAA,GAEDC,KACF,KAAK,eAAeA,CAAU;AAAA,MAElC,SAASC,GAAO;AACd,aAAK,YAAA,GACL,KAAK,UAAU,kCAAmCA,EAAgB,OAAO,GACzE,KAAK,cAAc,IAAI,YAAY,qBAAqB;AAAA,UACtD,QAAQ,EAAE,OAAQA,EAAgB,QAAA;AAAA,UAClC,SAAS;AAAA,UACT,UAAU;AAAA,QAAA,CACX,CAAC;AAAA,MACJ;AAAA;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,WAAWC,GAA+B;AACtD,UAAMC,IAAkB,CAAA;AACxB,IAAI,KAAK,WAAW,KAAK,QAAQ,UAC/BA,EAAM,KAAK;AAAA,EAAa,KAAK,QAAQ,KAAA,CAAM,EAAE,GAE/CA,EAAM,KAAK;AAAA;AAAA,EAA2CD,CAAI,EAAE;AAC5D,UAAME,IAAcD,EAAM,KAAK;AAAA;AAAA,CAAM,GAG/BE,IAAkC;AAAA,MACtC,gBAAgB;AAAA,IAAA;AAGlB,IAAI,KAAK,UAAU,KAAK,OAAO,KAAA,MAAW,OACxCA,EAAQ,gBAAmB,UAAU,KAAK,MAAM;AAGlD,UAAMC,IAAc;AAAA,MAClB,OAAO,KAAK;AAAA,MACZ,UAAU;AAAA,QACR;AAAA,UACE,MAAM;AAAA,UACN,SAAS,KAAK;AAAA,QAAA;AAAA,QAEhB;AAAA,UACE,MAAM;AAAA,UACN,SAASF;AAAA,QAAA;AAAA,MACX;AAAA,MAEF,YAAY;AAAA,MACZ,aAAa;AAAA,IAAA,GAGTG,IAAW,MAAM,MAAM,KAAK,aAAa;AAAA,MAC7C,QAAQ;AAAA,MACR,SAAAF;AAAA,MACA,MAAM,KAAK,UAAUC,CAAW;AAAA,IAAA,CACjC;AAED,QAAI,CAACC,EAAS,IAAI;AAChB,UAAIC,IAAe;AACnB,UAAI;AAEF,QAAAA,KADkB,MAAMD,EAAS,KAAA,GACR,OAAO,WAAWC;AAAA,MAC7C,SAASC,GAAY;AACnB,gBAAQ,MAAM,mCAAmCA,CAAU,GAC3DD,IAAe,QAAQD,EAAS,MAAM,KAAKA,EAAS,UAAU;AAAA,MAChE;AACA,YAAM,IAAI,MAAMC,CAAY;AAAA,IAC9B;AAGA,YADa,MAAMD,EAAS,KAAA,GAChB,QAAQ,CAAC,GAAG,SAAS,SAAS,KAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAeP,GAA0B;AAC/C,SAAK,iBAAiBA,GACtB,KAAK,uBAAuB,KAAK,oBAAoBA,CAAU,GAC/D,KAAK,wBAAwB,GAC7B,KAAK,sBAAsB,IAC3B,KAAK,iBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAAoBE,GAAwB;AAGlD,WADkBA,EAAK,MAAM,iDAAiC,GAC7C,OAAO,CAAAQ,MAAYA,EAAS,KAAA,EAAO,SAAS,CAAC;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAuB;AAC7B,SAAK,sBAAsB,IAC3B,KAAK,iBAAiB,MACtB,KAAK,uBAAuB,CAAA,GAC5B,KAAK,wBAAwB,GAC7B,KAAK,iBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,QAAI,KAAK,kBAAkB,KAAK,wBAAwB,KAAK,qBAAqB,QAAQ;AACxF,YAAMnB,IAAc,KAAK,OAAO,OAC1BC,IAAiB,KAAK,OAAO,gBAC7BmB,IAAiB,KAAK,qBAAqB,KAAK,qBAAqB,GAGrEC,IAAerB,EAAY,UAAU,GAAGC,CAAc,GACtDqB,IAActB,EAAY,UAAUC,CAAc,GAGlDsB,IAAUF,EAAa,SAAS,GAAG,KAAKA,MAAiB,MAAMD,EAAe,WAAW,GAAG,KAAKA,MAAmB,KAAM,KAAK,KAC/HI,IAAUH,IAAeE,IAASH,IAAiBE;AAEzD,WAAK,OAAO,QAAQE;AAGpB,YAAMC,IAAoBxB,IAAiBsB,EAAO,SAASH,EAAe;AAC1E,WAAK,OAAO,kBAAkBK,GAAmBA,CAAiB,GAClE,KAAK,cAAc,IAAI,YAAY,UAAU,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,MAAA,EAAM,CAAG,CAAC,GAGtF,KAAK,yBACD,KAAK,yBAAyB,KAAK,qBAAqB,SAC1D,KAAK,eAAA,IAEL,KAAK,iBAAA;AAAA,IAET;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC1B,SAAK,QAAQ,UAAU,IAAI,MAAM,GAC7B,KAAK,2BACP,KAAK,wBAAwB,EAAI;AAAA,EAErC;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC1B,SAAK,QAAQ,UAAU,OAAO,MAAM,GAChC,KAAK,2BACP,KAAK,wBAAwB,EAAK;AAAA,EAEtC;AAAA;AAAA;AAAA;AAAA,EAKQ,UAAUC,GAAuB;AACvC,YAAQ,MAAM,yBAAyBA,CAAO,GAE9C,KAAK,cAAc,IAAI,YAAY,SAAS;AAAA,MAC1C,QAAQ,EAAE,SAAAA,EAAA;AAAA,MACV,SAAS;AAAA,MACT,UAAU;AAAA,IAAA,CACX,CAAC;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA,EAKQ,WAAWC,GAAqB;AACtC,QAAI,OAAOA,KAAW,UAAU;AAC9B,UAAIA,MAAW,QAAQ,OAAOA,IAAW;AACvC,eAAO;AAET,MAAAA,IAAS,OAAOA,CAAM;AAAA,IACxB;AACA,WAAOA,EACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQhB,GAAoB;AAC1B,SAAK,OAAO,QAAQA,GACpB,KAAK,cAAc,IAAI,YAAY,UAAU,EAAE,QAAQ,EAAE,OAAO,KAAK,OAAO,MAAA,EAAM,CAAG,CAAC,GACtF,KAAK,eAAA,GACL,KAAK,iBAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,UAAkB;AAChB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA,EAKA,UAAUiB,GAAmB;AAC3B,SAAK,SAASA,GACd,KAAK,YAAA,GACL,KAAK,aAAa,MAAM,kBAAkB,KAAK,SAAS,YAAY,QAChE,KAAK,0BACP,KAAK,uBAAuB,CAAC,CAAC,KAAK,MAAM;AAAA,EAE7C;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,QAAQ;AAChB,mBAAa,WAAWlC,CAAsB;AAC9C;AAAA,IACF;AAGA,UAAMmC,IAAe,KAAK,KAAK,MAAM;AACrC,iBAAa,QAAQnC,GAAwBmC,CAAY;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAoB;AAC1B,UAAMC,IAAW,aAAa,QAAQpC,CAAsB;AAC5D,QAAI,CAACoC,GAAU;AACb,WAAK,UAAU,EAAE;AACjB;AAAA,IACF;AAEA,UAAMC,IAAe,KAAKD,CAAQ;AAClC,SAAK,UAAUC,CAAY;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA,EAKA,YAAoB;AAClB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmBC,GAAuB;AACxC,SAAK,kBAAkBA,IAAU;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA,EAKA,qBAA6B;AAC3B,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAKA,gBAAgBC,GAAsB;AACpC,SAAK,eAAeA;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,kBAA0B;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,eAAeC,GAAwB;AACrC,SAAK,cAAcA,GACnB,KAAK,cAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAaC,GAAyB;AACpC,SAAK,YAAYA,GACjB,KAAK,cAAA;AAAA,EACP;AAAA;AAAA;AAAA;AAAA,EAKA,eAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,WAAWC,GAAuB;AAChC,SAAK,UAAU,OAAOA,KAAY,WAAWA,IAAU;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA,EAKA,aAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,UAAUC,GAA2C;AACnD,IAAIA,EAAO,aAAa,WACtB,KAAK,WAAWA,EAAO,UACnB,KAAK,WACP,KAAK,aAAa,YAAY,EAAE,IAEhC,KAAK,gBAAgB,UAAU,IAI/BA,EAAO,mBACT,KAAK,yBAAyBA,EAAO,gBAErC,KAAK,uBAAuB,CAAC,CAAC,KAAK,MAAM,IAGvCA,EAAO,oBACT,KAAK,0BAA0BA,EAAO,kBAGpCA,EAAO,WAAW,UACpB,KAAK,UAAUA,EAAO,MAAM,GAG1BA,EAAO,oBAAoB,UAC7B,KAAK,mBAAmBA,EAAO,eAAe,GAG5CA,EAAO,iBAAiB,UAC1B,KAAK,gBAAgBA,EAAO,YAAY,GAGtCA,EAAO,gBAAgB,UACzB,KAAK,eAAeA,EAAO,WAAW,GAGpCA,EAAO,cAAc,UACvB,KAAK,aAAaA,EAAO,SAAS,GAGhCA,EAAO,YAAY,UACrB,KAAK,WAAWA,EAAO,OAAO;AAAA,EAElC;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAMC,IAAW;AAAA,MACf,aAAa,KAAK;AAAA,MAClB,WAAW,KAAK;AAAA,IAAA;AAElB,iBAAa,QAAQ,2BAA2B,KAAK,UAAUA,CAAQ,CAAC;AAAA,EAC1E;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAMC,IAAgB,aAAa,QAAQ,yBAAyB;AACpE,QAAIA;AACF,UAAI;AACF,cAAMD,IAAW,KAAK,MAAMC,CAAa;AACzC,QAAID,EAAS,gBACX,KAAK,cAAcA,EAAS,cAE1BA,EAAS,cACX,KAAK,YAAYA,EAAS;AAAA,MAE9B,SAAS5B,GAAO;AACd,gBAAQ,KAAK,kCAAkCA,CAAK;AAAA,MACtD;AAAA,EAEJ;AACF;AAKA,MAAM8B,IAAqB,CAACC,IAAkB,2BAAiC;AAC7E,EAAI,OAAO,SAAW,OAAe,CAAC,OAAO,eAAe,IAAIA,CAAO,KACrE,eAAe,OAAOA,GAAS9C,CAAmB;AAEtD;AAGA6C,EAAA;"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * ButtonToolbar Web Component
3
+ * A customizable toolbar with groups of buttons, supporting horizontal/vertical orientation
4
+ */
5
+ export type ButtonToolbarItem = {
6
+ id: string;
7
+ label?: string;
8
+ icon?: string;
9
+ image?: string;
10
+ type?: 'default' | 'info' | 'error' | 'warn' | 'success';
11
+ disabled?: boolean;
12
+ tooltip?: string;
13
+ action?: string;
14
+ };
15
+ export type ButtonToolbarGroup = {
16
+ id?: string;
17
+ items: ButtonToolbarItem[];
18
+ class?: string;
19
+ };
20
+ export declare class ButtonToolbarElement extends HTMLElement {
21
+ shadowRoot: ShadowRoot;
22
+ private _groups;
23
+ constructor();
24
+ static get observedAttributes(): string[];
25
+ attributeChangedCallback(_name: string, oldValue: string | null, newValue: string | null): void;
26
+ get orientation(): 'horizontal' | 'vertical';
27
+ set orientation(value: 'horizontal' | 'vertical');
28
+ get groups(): ButtonToolbarGroup[];
29
+ set groups(value: ButtonToolbarGroup[]);
30
+ connectedCallback(): void;
31
+ private handleButtonClick;
32
+ private render;
33
+ }
34
+ export declare const defineButtonToolbar: () => void;
35
+ //# sourceMappingURL=ButtonToolbar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ButtonToolbar.d.ts","sourceRoot":"","sources":["../src/ButtonToolbar.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACzD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,iBAAiB,EAAE,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,qBAAa,oBAAqB,SAAQ,WAAW;IAC3C,UAAU,EAAE,UAAU,CAAC;IAC/B,OAAO,CAAC,OAAO,CAA4B;;IAO3C,MAAM,KAAK,kBAAkB,IAAI,MAAM,EAAE,CAExC;IAED,wBAAwB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAM/F,IAAI,WAAW,IAAI,YAAY,GAAG,UAAU,CAE3C;IAED,IAAI,WAAW,CAAC,KAAK,EAAE,YAAY,GAAG,UAAU,EAE/C;IAED,IAAI,MAAM,IAAI,kBAAkB,EAAE,CAWjC;IAED,IAAI,MAAM,CAAC,KAAK,EAAE,kBAAkB,EAAE,EAcrC;IAED,iBAAiB,IAAI,IAAI;IAIzB,OAAO,CAAC,iBAAiB;IAezB,OAAO,CAAC,MAAM;CAsMf;AAED,eAAO,MAAM,mBAAmB,QAAO,IAItC,CAAC"}
@@ -0,0 +1,220 @@
1
+ class l extends HTMLElement {
2
+ constructor() {
3
+ super(), this._groups = [], this.attachShadow({ mode: "open" });
4
+ }
5
+ static get observedAttributes() {
6
+ return ["orientation", "groups"];
7
+ }
8
+ attributeChangedCallback(o, i, n) {
9
+ i !== n && this.render();
10
+ }
11
+ get orientation() {
12
+ return this.getAttribute("orientation") || "horizontal";
13
+ }
14
+ set orientation(o) {
15
+ this.setAttribute("orientation", o);
16
+ }
17
+ get groups() {
18
+ const o = this.getAttribute("groups");
19
+ if (o)
20
+ try {
21
+ return JSON.parse(o);
22
+ } catch (i) {
23
+ return console.error("Invalid groups format:", i), [];
24
+ }
25
+ return this._groups;
26
+ }
27
+ set groups(o) {
28
+ this._groups = o, this.setAttribute("groups", JSON.stringify(o));
29
+ }
30
+ connectedCallback() {
31
+ this.render();
32
+ }
33
+ handleButtonClick(o, i) {
34
+ o.disabled || this.dispatchEvent(new CustomEvent("button-click", {
35
+ detail: {
36
+ id: o.id,
37
+ action: o.action || o.id,
38
+ originalEvent: i,
39
+ item: o
40
+ },
41
+ bubbles: !0,
42
+ composed: !0
43
+ }));
44
+ }
45
+ render() {
46
+ if (!this.shadowRoot) return;
47
+ const o = `
48
+ :host {
49
+ display: block;
50
+ font-family: var(--liwe3-font-family, system-ui, -apple-system, sans-serif);
51
+ }
52
+
53
+ .toolbar {
54
+ display: flex;
55
+ gap: var(--liwe3-toolbar-gap, 0.5rem);
56
+ width: 100%;
57
+ box-sizing: border-box;
58
+ }
59
+
60
+ .toolbar.horizontal {
61
+ flex-direction: row;
62
+ align-items: center;
63
+ }
64
+
65
+ .toolbar.vertical {
66
+ flex-direction: column;
67
+ align-items: stretch;
68
+ }
69
+
70
+ .group {
71
+ display: flex;
72
+ gap: 1px; /* Gap between buttons in a group */
73
+ background-color: var(--liwe3-toolbar-group-bg, transparent);
74
+ border-radius: var(--liwe3-toolbar-radius, 0.375rem);
75
+ overflow: hidden;
76
+ }
77
+
78
+ .toolbar.horizontal .group {
79
+ flex-direction: row;
80
+ }
81
+
82
+ .toolbar.vertical .group {
83
+ flex-direction: column;
84
+ }
85
+
86
+ .button {
87
+ display: inline-flex;
88
+ align-items: center;
89
+ justify-content: center;
90
+ gap: 0.5rem;
91
+ padding: var(--liwe3-button-padding, 0.5rem 1rem);
92
+ border: none;
93
+ cursor: pointer;
94
+ font-size: var(--liwe3-button-font-size, 0.875rem);
95
+ line-height: 1.25;
96
+ transition: all 0.2s;
97
+ background-color: var(--liwe3-button-bg, #f3f4f6);
98
+ color: var(--liwe3-button-color, #1f2937);
99
+ min-height: var(--liwe3-button-height, 2.5rem);
100
+ }
101
+
102
+ .button:hover:not(:disabled) {
103
+ filter: brightness(0.95);
104
+ }
105
+
106
+ .button:active:not(:disabled) {
107
+ filter: brightness(0.9);
108
+ }
109
+
110
+ .button:disabled {
111
+ opacity: 0.5;
112
+ cursor: not-allowed;
113
+ }
114
+
115
+ /* Button Types */
116
+ .button.default {
117
+ background-color: var(--liwe3-button-default-bg, #f3f4f6);
118
+ color: var(--liwe3-button-default-color, #1f2937);
119
+ }
120
+
121
+ .button.info {
122
+ background-color: var(--liwe3-button-info-bg, #3b82f6);
123
+ color: var(--liwe3-button-info-color, #ffffff);
124
+ }
125
+
126
+ .button.error {
127
+ background-color: var(--liwe3-button-error-bg, #ef4444);
128
+ color: var(--liwe3-button-error-color, #ffffff);
129
+ }
130
+
131
+ .button.warn {
132
+ background-color: var(--liwe3-button-warn-bg, #f59e0b);
133
+ color: var(--liwe3-button-warn-color, #ffffff);
134
+ }
135
+
136
+ .button.success {
137
+ background-color: var(--liwe3-button-success-bg, #10b981);
138
+ color: var(--liwe3-button-success-color, #ffffff);
139
+ }
140
+
141
+ /* Content styling */
142
+ .icon {
143
+ width: 1.25em;
144
+ height: 1.25em;
145
+ display: flex;
146
+ align-items: center;
147
+ justify-content: center;
148
+ }
149
+
150
+ .icon svg {
151
+ width: 100%;
152
+ height: 100%;
153
+ fill: currentColor;
154
+ }
155
+
156
+ .image {
157
+ width: 1.5em;
158
+ height: 1.5em;
159
+ object-fit: cover;
160
+ border-radius: 50%;
161
+ }
162
+
163
+ /* Group styling - rounded corners logic */
164
+ .toolbar.horizontal .group .button:first-child {
165
+ border-top-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
166
+ border-bottom-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
167
+ }
168
+
169
+ .toolbar.horizontal .group .button:last-child {
170
+ border-top-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
171
+ border-bottom-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
172
+ }
173
+
174
+ .toolbar.vertical .group .button:first-child {
175
+ border-top-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
176
+ border-top-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
177
+ }
178
+
179
+ .toolbar.vertical .group .button:last-child {
180
+ border-bottom-left-radius: var(--liwe3-toolbar-radius, 0.375rem);
181
+ border-bottom-right-radius: var(--liwe3-toolbar-radius, 0.375rem);
182
+ }
183
+ `, i = (t) => {
184
+ const e = document.createElement("button");
185
+ if (e.className = `button ${t.type || "default"}`, t.disabled && (e.disabled = !0), t.tooltip && (e.title = t.tooltip), e.onclick = (r) => this.handleButtonClick(t, r), t.icon) {
186
+ const r = document.createElement("span");
187
+ if (r.className = "icon", t.icon.trim().startsWith("<"))
188
+ r.innerHTML = t.icon;
189
+ else {
190
+ const a = document.createElement("i");
191
+ a.className = t.icon, r.appendChild(a);
192
+ }
193
+ e.appendChild(r);
194
+ }
195
+ if (t.image) {
196
+ const r = document.createElement("img");
197
+ r.src = t.image, r.className = "image", r.alt = t.label || "", e.appendChild(r);
198
+ }
199
+ if (t.label) {
200
+ const r = document.createElement("span");
201
+ r.textContent = t.label, e.appendChild(r);
202
+ }
203
+ return e;
204
+ }, n = document.createElement("div");
205
+ n.className = `toolbar ${this.orientation}`, this._groups.forEach((t) => {
206
+ const e = document.createElement("div");
207
+ e.className = `group ${t.class || ""}`, t.items.forEach((r) => {
208
+ e.appendChild(i(r));
209
+ }), n.appendChild(e);
210
+ }), this.shadowRoot.innerHTML = `<style>${o}</style>`, this.shadowRoot.appendChild(n);
211
+ }
212
+ }
213
+ const u = () => {
214
+ typeof window < "u" && !customElements.get("liwe3-button-toolbar") && customElements.define("liwe3-button-toolbar", l);
215
+ };
216
+ export {
217
+ l as ButtonToolbarElement,
218
+ u as defineButtonToolbar
219
+ };
220
+ //# sourceMappingURL=ButtonToolbar.js.map