llmasaservice-ui 0.3.15 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.css CHANGED
@@ -165,6 +165,38 @@
165
165
  border: 1px solid #777;
166
166
  padding: 8px;
167
167
  }
168
+ .side-panel .save-button {
169
+ background-color: #d1ecf1;
170
+ color: black;
171
+ border: none;
172
+ border-radius: 8px;
173
+ cursor: pointer;
174
+ padding: 10px;
175
+ margin-top: 5px;
176
+ }
177
+ .suggestions-container {
178
+ display: flex;
179
+ flex-wrap: wrap;
180
+ gap: 5px;
181
+ }
182
+ .suggestion-button {
183
+ background-color: #007bff;
184
+ color: white;
185
+ border: none;
186
+ border-radius: 5px;
187
+ padding: 5px 10px;
188
+ font-size: 0.8em;
189
+ cursor: pointer;
190
+ margin: 5px 0;
191
+ }
192
+ .suggestion-button:hover {
193
+ background-color: #0056b3;
194
+ }
195
+ .suggestion-button:disabled {
196
+ background-color: #cccccc;
197
+ color: #666666;
198
+ cursor: not-allowed;
199
+ }
168
200
  .side-panel-dark {
169
201
  font-family:
170
202
  "Roboto",
@@ -340,3 +372,25 @@
340
372
  .side-panel-dark a:hover {
341
373
  color: #fff;
342
374
  }
375
+ .side-panel-dark .save-button {
376
+ background-color: #3a3737;
377
+ color: white;
378
+ border: none;
379
+ border-radius: 8px;
380
+ cursor: pointer;
381
+ padding: 10px;
382
+ margin-top: 5px;
383
+ }
384
+ .powered-by {
385
+ display: flex;
386
+ justify-content: center;
387
+ align-items: center;
388
+ color: lightgrey;
389
+ font-size: 0.8em;
390
+ height: 100%;
391
+ text-align: center;
392
+ }
393
+ .powered-by a {
394
+ color: lightgrey;
395
+ text-decoration: underline;
396
+ }
package/dist/index.d.mts CHANGED
@@ -42,6 +42,11 @@ interface ChatPanelProps {
42
42
  callback?: (match: string, groups: any[]) => void;
43
43
  clickCode?: string;
44
44
  }[];
45
+ showSaveButton?: boolean;
46
+ followOnQuestions?: string[];
47
+ clearFollowOnQuestionsNextPrompt?: boolean;
48
+ followOnPrompt?: string;
49
+ showPoweredBy?: boolean;
45
50
  }
46
51
  interface ExtraProps extends React.HTMLAttributes<HTMLElement> {
47
52
  inline?: boolean;
package/dist/index.d.ts CHANGED
@@ -42,6 +42,11 @@ interface ChatPanelProps {
42
42
  callback?: (match: string, groups: any[]) => void;
43
43
  clickCode?: string;
44
44
  }[];
45
+ showSaveButton?: boolean;
46
+ followOnQuestions?: string[];
47
+ clearFollowOnQuestionsNextPrompt?: boolean;
48
+ followOnPrompt?: string;
49
+ showPoweredBy?: boolean;
45
50
  }
46
51
  interface ExtraProps extends React.HTMLAttributes<HTMLElement> {
47
52
  inline?: boolean;
package/dist/index.js CHANGED
@@ -68,6 +68,7 @@ var import_llmasaservice_client = require("llmasaservice-client");
68
68
  var import_react = __toESM(require("react"));
69
69
  var import_react_markdown = __toESM(require("react-markdown"));
70
70
  var import_rehype_raw = __toESM(require("rehype-raw"));
71
+ var import_server = __toESM(require("react-dom/server"));
71
72
  var import_remark_gfm = __toESM(require("remark-gfm"));
72
73
  var import_react_syntax_highlighter = require("react-syntax-highlighter");
73
74
  var import_material_dark = __toESM(require("react-syntax-highlighter/dist/cjs/styles/prism/material-dark.js"));
@@ -94,7 +95,12 @@ var ChatPanel = ({
94
95
  service = null,
95
96
  historyChangedCallback = null,
96
97
  promptTemplate = "",
97
- actions = []
98
+ actions = [],
99
+ showSaveButton = true,
100
+ followOnQuestions = [],
101
+ clearFollowOnQuestionsNextPrompt = false,
102
+ followOnPrompt = "",
103
+ showPoweredBy = true
98
104
  }) => {
99
105
  const { send, response, idle, stop, lastCallId } = (0, import_llmasaservice_client.useLLM)({
100
106
  project_id,
@@ -109,6 +115,9 @@ var ChatPanel = ({
109
115
  const [hasScroll, setHasScroll] = (0, import_react.useState)(false);
110
116
  const bottomRef = (0, import_react.useRef)(null);
111
117
  const [isAtBottom, setIsAtBottom] = (0, import_react.useState)(true);
118
+ const [lastFollowOnPrompt, setLastFollowOnPrompt] = (0, import_react.useState)(
119
+ null
120
+ );
112
121
  const responseAreaRef = (0, import_react.useRef)(null);
113
122
  (0, import_react.useEffect)(() => {
114
123
  if (response && response.length > 0) {
@@ -202,7 +211,13 @@ var ChatPanel = ({
202
211
  historyChangedCallback(history);
203
212
  }
204
213
  }, [history, historyChangedCallback]);
205
- const continueChat = () => {
214
+ (0, import_react.useEffect)(() => {
215
+ if (followOnPrompt && followOnPrompt !== "" && (lastFollowOnPrompt != null ? lastFollowOnPrompt : "") !== followOnPrompt) {
216
+ continueChat(followOnPrompt);
217
+ setLastFollowOnPrompt(followOnPrompt);
218
+ }
219
+ }, [followOnPrompt]);
220
+ const continueChat = (suggestion) => {
206
221
  var _a, _b, _c;
207
222
  if (!idle) {
208
223
  stop(lastController);
@@ -216,38 +231,50 @@ var ChatPanel = ({
216
231
  });
217
232
  return;
218
233
  }
219
- if (nextPrompt && nextPrompt !== "") {
234
+ if (clearFollowOnQuestionsNextPrompt) {
235
+ followOnQuestions = [];
236
+ const suggestionsContainer = document.querySelector(
237
+ ".suggestions-container"
238
+ );
239
+ if (suggestionsContainer) {
240
+ suggestionsContainer.innerHTML = "";
241
+ }
242
+ }
243
+ if (suggestion && suggestion !== "" || nextPrompt && nextPrompt !== "") {
220
244
  setIsLoading(true);
221
245
  const messagesAndHistory = messages;
222
246
  Object.entries(history).forEach(([prompt, response2]) => {
223
247
  var _a2, _b2, _c2;
224
- let promptToSend2 = prompt;
248
+ let promptToSend = prompt;
225
249
  if (promptTemplate && promptTemplate !== "") {
226
- promptToSend2 = promptTemplate.replace("{{prompt}}", promptToSend2);
250
+ promptToSend = promptTemplate.replace("{{prompt}}", promptToSend);
227
251
  for (let i = 0; i < data.length; i++) {
228
- promptToSend2 = promptToSend2.replace(
252
+ promptToSend = promptToSend.replace(
229
253
  "{{" + ((_a2 = data[i]) == null ? void 0 : _a2.key) + "}}",
230
254
  (_c2 = (_b2 = data[i]) == null ? void 0 : _b2.data) != null ? _c2 : ""
231
255
  );
232
256
  }
233
257
  }
234
- messagesAndHistory.push({ role: "user", content: promptToSend2 });
258
+ messagesAndHistory.push({ role: "user", content: promptToSend });
235
259
  messagesAndHistory.push({
236
260
  role: "assistant",
237
261
  content: response2.content
238
262
  });
239
263
  });
264
+ let nextPromptToSend = suggestion != null ? suggestion : nextPrompt;
240
265
  setHistory((prevHistory) => {
241
266
  return __spreadProps(__spreadValues({}, prevHistory), {
242
- [nextPrompt != null ? nextPrompt : ""]: { content: "", callId: "" }
267
+ [nextPromptToSend != null ? nextPromptToSend : ""]: { content: "", callId: "" }
243
268
  });
244
269
  });
245
- let promptToSend = nextPrompt;
246
270
  if (initialPrompt && initialPrompt !== "" && Object.keys(history).length === 1 || (!initialPrompt || initialPrompt === "") && Object.keys(history).length === 0) {
247
271
  if (promptTemplate && promptTemplate !== "") {
248
- promptToSend = promptTemplate.replace("{{prompt}}", nextPrompt);
272
+ nextPromptToSend = promptTemplate.replace(
273
+ "{{prompt}}",
274
+ nextPromptToSend
275
+ );
249
276
  for (let i = 0; i < data.length; i++) {
250
- promptToSend = promptToSend.replace(
277
+ nextPromptToSend = nextPromptToSend.replace(
251
278
  "{{" + ((_a = data[i]) == null ? void 0 : _a.key) + "}}",
252
279
  (_c = (_b = data[i]) == null ? void 0 : _b.data) != null ? _c : ""
253
280
  );
@@ -256,7 +283,7 @@ var ChatPanel = ({
256
283
  }
257
284
  const controller = new AbortController();
258
285
  send(
259
- promptToSend,
286
+ nextPromptToSend,
260
287
  messagesAndHistory,
261
288
  data,
262
289
  true,
@@ -264,7 +291,7 @@ var ChatPanel = ({
264
291
  service,
265
292
  controller
266
293
  );
267
- setLastPrompt(nextPrompt);
294
+ setLastPrompt(nextPromptToSend);
268
295
  setLastController(controller);
269
296
  setNextPrompt("");
270
297
  }
@@ -339,6 +366,102 @@ var ChatPanel = ({
339
366
  var _d = _c, { href, children } = _d, props = __objRest(_d, ["href", "children"]);
340
367
  return /* @__PURE__ */ import_react.default.createElement("a", __spreadValues({ href, target: "_blank", rel: "noopener noreferrer" }, props), children);
341
368
  };
369
+ const convertMarkdownToHTML = (markdown) => {
370
+ const html = import_server.default.renderToStaticMarkup(
371
+ /* @__PURE__ */ import_react.default.createElement(
372
+ import_react_markdown.default,
373
+ {
374
+ className: markdownClass,
375
+ remarkPlugins: [import_remark_gfm.default],
376
+ rehypePlugins: [import_rehype_raw.default]
377
+ },
378
+ markdown
379
+ )
380
+ );
381
+ return html;
382
+ };
383
+ const convertHistoryToHTML = (history2) => {
384
+ const stylesheet = `
385
+ <style>
386
+ .conversation-history {
387
+ font-family: Arial, sans-serif;
388
+ line-height: 1;
389
+ }
390
+ .history-entry {
391
+ margin-bottom: 15px;
392
+ display: flex;
393
+ flex-direction: column;
394
+ }
395
+ .prompt-container, .response-container {
396
+ display: flex;
397
+ flex-direction: column;
398
+ margin-bottom: 3px;
399
+ }
400
+ .prompt {
401
+ background-color: #efefef;
402
+ padding: 5px;
403
+ border-radius: 5px;
404
+ max-width: 80%;
405
+ margin-left: 0;
406
+ }
407
+ .response {
408
+ background-color: #f0fcfd;
409
+ padding: 5px;
410
+ border-radius: 5px;
411
+ max-width: 80%;
412
+ margin-left: 25px;
413
+ }
414
+ </style>
415
+ `;
416
+ let html = `
417
+ <html>
418
+ <head>
419
+ ${stylesheet}
420
+ </head>
421
+ <body>
422
+ <h1>Conversation History (${(/* @__PURE__ */ new Date()).toLocaleString()})</h1>
423
+ <div class="conversation-history">
424
+ `;
425
+ Object.entries(history2).forEach(([prompt, response2], index) => {
426
+ if (hideInitialPrompt && index === 0) {
427
+ return;
428
+ }
429
+ html += `
430
+ <div class="history-entry">
431
+ <div class="prompt-container">
432
+ <div class="prompt">${convertMarkdownToHTML(prompt)}</div>
433
+ </div>
434
+ <div class="response-container">
435
+ <div class="response">${convertMarkdownToHTML(response2.content)}</div>
436
+ </div>
437
+ </div>
438
+ `;
439
+ });
440
+ html += `
441
+ </div>
442
+ </body>
443
+ </html>
444
+ `;
445
+ return html;
446
+ };
447
+ const saveHTMLToFile = (html, filename) => {
448
+ const blob = new Blob([html], { type: "text/html" });
449
+ const link = document.createElement("a");
450
+ link.href = URL.createObjectURL(blob);
451
+ link.download = filename;
452
+ document.body.appendChild(link);
453
+ link.click();
454
+ document.body.removeChild(link);
455
+ };
456
+ const shareViaEmail = (html, subject) => {
457
+ const mailtoLink = `mailto:?subject=${encodeURIComponent(
458
+ subject
459
+ )}&body=${encodeURIComponent(html)}`;
460
+ window.location.href = mailtoLink;
461
+ };
462
+ const handleSuggestionClick = (suggestion) => {
463
+ continueChat(suggestion);
464
+ };
342
465
  return /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement(
343
466
  "div",
344
467
  {
@@ -413,7 +536,27 @@ var ChatPanel = ({
413
536
  className: "icon-svg"
414
537
  },
415
538
  /* @__PURE__ */ import_react.default.createElement("path", { d: "M18.38 3.25H6.81C5.72 3.25 4.79 4.03 4.6 5.11L3.29 12.61C3.18 13.27 3.36 13.94 3.78 14.45C4.21 14.96 4.83 15.25 5.5 15.25H9.53V18C9.53 19.52 10.76 20.75 12.36 20.75C13.06 20.75 13.69 20.33 13.97 19.68L16.51 13.75H18.39C19.7 13.75 20.76 12.69 20.76 11.38V5.61C20.76 4.31 19.7 3.25 18.39 3.25H18.38ZM15.26 12.85L12.59 19.1C12.55 19.19 12.46 19.26 12.27 19.26C11.58 19.26 11.03 18.7 11.03 18.01V13.76H5.5C5.27 13.76 5.07 13.67 4.93 13.5C4.78 13.33 4.73 13.11 4.77 12.88L6.08 5.38C6.14 5.02 6.45001 4.76 6.82 4.76H15.26V12.85ZM19.25 11.38C19.25 11.86 18.86 12.25 18.38 12.25H16.77V4.75H18.38C18.86 4.75 19.25 5.14 19.25 5.61V11.38Z" })
416
- )) : null)))), /* @__PURE__ */ import_react.default.createElement("div", { ref: bottomRef }), hasScroll && !isAtBottom && /* @__PURE__ */ import_react.default.createElement("button", { className: "scroll-button", onClick: scrollToBottom }, "\u2193")),
539
+ )) : null)))), followOnQuestions && followOnQuestions.length > 0 && /* @__PURE__ */ import_react.default.createElement("div", { className: "suggestions-container" }, followOnQuestions.map((question, index) => /* @__PURE__ */ import_react.default.createElement(
540
+ "button",
541
+ {
542
+ key: index,
543
+ className: "suggestion-button",
544
+ onClick: () => handleSuggestionClick(question),
545
+ disabled: !idle
546
+ },
547
+ question
548
+ ))), /* @__PURE__ */ import_react.default.createElement("div", { ref: bottomRef }), hasScroll && !isAtBottom && /* @__PURE__ */ import_react.default.createElement("button", { className: "scroll-button", onClick: scrollToBottom }, "\u2193")),
549
+ showSaveButton && /* @__PURE__ */ import_react.default.createElement(
550
+ "button",
551
+ {
552
+ className: "save-button",
553
+ onClick: () => saveHTMLToFile(
554
+ convertHistoryToHTML(history),
555
+ `conversation-${(/* @__PURE__ */ new Date()).toISOString()}.html`
556
+ )
557
+ },
558
+ "Save Conversation"
559
+ ),
417
560
  /* @__PURE__ */ import_react.default.createElement("div", { className: "input-container" }, /* @__PURE__ */ import_react.default.createElement(
418
561
  "textarea",
419
562
  {
@@ -427,7 +570,7 @@ var ChatPanel = ({
427
570
  }
428
571
  }
429
572
  }
430
- ), /* @__PURE__ */ import_react.default.createElement("button", { className: "send-button", onClick: continueChat }, idle ? /* @__PURE__ */ import_react.default.createElement(
573
+ ), /* @__PURE__ */ import_react.default.createElement("button", { className: "send-button", onClick: () => continueChat() }, idle ? /* @__PURE__ */ import_react.default.createElement(
431
574
  "svg",
432
575
  {
433
576
  xmlns: "http://www.w3.org/2000/svg",
@@ -453,6 +596,204 @@ var ChatPanel = ({
453
596
  className: "icon-svg-large"
454
597
  },
455
598
  /* @__PURE__ */ import_react.default.createElement("path", { d: "M8 8h16v16H8z" })
599
+ ))),
600
+ showPoweredBy && /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("div", { className: "powered-by" }, /* @__PURE__ */ import_react.default.createElement(
601
+ "svg",
602
+ {
603
+ width: "16",
604
+ height: "16",
605
+ viewBox: "0 0 72 72",
606
+ fill: "none",
607
+ xmlns: "http://www.w3.org/2000/svg"
608
+ },
609
+ /* @__PURE__ */ import_react.default.createElement(
610
+ "ellipse",
611
+ {
612
+ cx: "14.0868",
613
+ cy: "59.2146",
614
+ rx: "7.8261",
615
+ ry: "7.7854",
616
+ fill: "#2487D8"
617
+ }
618
+ ),
619
+ /* @__PURE__ */ import_react.default.createElement(
620
+ "ellipse",
621
+ {
622
+ cx: "24.9013",
623
+ cy: "43.0776",
624
+ rx: "6.11858",
625
+ ry: "6.08676",
626
+ fill: "#2487D8"
627
+ }
628
+ ),
629
+ /* @__PURE__ */ import_react.default.createElement(
630
+ "ellipse",
631
+ {
632
+ cx: "45.391",
633
+ cy: "43.0776",
634
+ rx: "6.11858",
635
+ ry: "6.08676",
636
+ fill: "#2487D8"
637
+ }
638
+ ),
639
+ /* @__PURE__ */ import_react.default.createElement(
640
+ "ellipse",
641
+ {
642
+ cx: "65.8813",
643
+ cy: "43.0776",
644
+ rx: "6.11858",
645
+ ry: "6.08676",
646
+ fill: "#2487D8"
647
+ }
648
+ ),
649
+ /* @__PURE__ */ import_react.default.createElement(
650
+ "ellipse",
651
+ {
652
+ cx: "13.9444",
653
+ cy: "30.4795",
654
+ rx: "4.83795",
655
+ ry: "4.81279",
656
+ fill: "#2487D8"
657
+ }
658
+ ),
659
+ /* @__PURE__ */ import_react.default.createElement(
660
+ "ellipse",
661
+ {
662
+ cx: "34.7193",
663
+ cy: "30.4795",
664
+ rx: "4.83795",
665
+ ry: "4.81279",
666
+ fill: "#2487D8"
667
+ }
668
+ ),
669
+ /* @__PURE__ */ import_react.default.createElement(
670
+ "ellipse",
671
+ {
672
+ cx: "55.4942",
673
+ cy: "30.4795",
674
+ rx: "4.83795",
675
+ ry: "4.81279",
676
+ fill: "#2487D8"
677
+ }
678
+ ),
679
+ /* @__PURE__ */ import_react.default.createElement(
680
+ "ellipse",
681
+ {
682
+ cx: "3.27273",
683
+ cy: "20.4293",
684
+ rx: "3.27273",
685
+ ry: "3.25571",
686
+ fill: "#2487D8"
687
+ }
688
+ ),
689
+ /* @__PURE__ */ import_react.default.createElement(
690
+ "ellipse",
691
+ {
692
+ cx: "24.9011",
693
+ cy: "20.4293",
694
+ rx: "3.27273",
695
+ ry: "3.25571",
696
+ fill: "#2487D8"
697
+ }
698
+ ),
699
+ /* @__PURE__ */ import_react.default.createElement(
700
+ "ellipse",
701
+ {
702
+ cx: "45.3914",
703
+ cy: "20.4293",
704
+ rx: "3.27273",
705
+ ry: "3.25571",
706
+ fill: "#2487D8"
707
+ }
708
+ ),
709
+ /* @__PURE__ */ import_react.default.createElement(
710
+ "ellipse",
711
+ {
712
+ cx: "12.2373",
713
+ cy: "13.4931",
714
+ rx: "1.70751",
715
+ ry: "1.69863",
716
+ fill: "#2487D8"
717
+ }
718
+ ),
719
+ /* @__PURE__ */ import_react.default.createElement(
720
+ "ellipse",
721
+ {
722
+ cx: "33.0122",
723
+ cy: "13.4931",
724
+ rx: "1.70751",
725
+ ry: "1.69863",
726
+ fill: "#2487D8"
727
+ }
728
+ ),
729
+ /* @__PURE__ */ import_react.default.createElement(
730
+ "ellipse",
731
+ {
732
+ cx: "53.5019",
733
+ cy: "13.4931",
734
+ rx: "1.70751",
735
+ ry: "1.69863",
736
+ fill: "#2487D8"
737
+ }
738
+ ),
739
+ /* @__PURE__ */ import_react.default.createElement(
740
+ "ellipse",
741
+ {
742
+ cx: "19.3517",
743
+ cy: "6.13242",
744
+ rx: "1.13834",
745
+ ry: "1.13242",
746
+ fill: "#2487D8"
747
+ }
748
+ ),
749
+ /* @__PURE__ */ import_react.default.createElement(
750
+ "ellipse",
751
+ {
752
+ cx: "40.1266",
753
+ cy: "6.13242",
754
+ rx: "1.13834",
755
+ ry: "1.13242",
756
+ fill: "#2487D8"
757
+ }
758
+ ),
759
+ /* @__PURE__ */ import_react.default.createElement(
760
+ "ellipse",
761
+ {
762
+ cx: "60.901",
763
+ cy: "6.13242",
764
+ rx: "1.13834",
765
+ ry: "1.13242",
766
+ fill: "#2487D8"
767
+ }
768
+ ),
769
+ /* @__PURE__ */ import_react.default.createElement(
770
+ "ellipse",
771
+ {
772
+ cx: "34.8617",
773
+ cy: "59.2146",
774
+ rx: "7.8261",
775
+ ry: "7.7854",
776
+ fill: "#2487D8"
777
+ }
778
+ ),
779
+ /* @__PURE__ */ import_react.default.createElement(
780
+ "ellipse",
781
+ {
782
+ cx: "55.6366",
783
+ cy: "59.2146",
784
+ rx: "7.8261",
785
+ ry: "7.7854",
786
+ fill: "#ED7D31"
787
+ }
788
+ )
789
+ ), " ", "\xA0\xA0powered by\xA0", /* @__PURE__ */ import_react.default.createElement(
790
+ "a",
791
+ {
792
+ href: "https://llmasaservice.io",
793
+ target: "_blank",
794
+ rel: "noopener noreferrer"
795
+ },
796
+ "llmasaservice.io"
456
797
  )))
457
798
  ));
458
799
  };