@liip/liipgpt 0.0.15 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # LiipGPT Chat & Library
2
+
3
+ This package provides two main features:
4
+
5
+ 1. A web component for embedding the LiipGPT chat UI
6
+ 2. A client library for interacting with the LiipGPT API
7
+
8
+ # Chat UI
9
+
10
+ ## Setup
11
+
12
+ ### With NPM
13
+
14
+ ```html
15
+ <liipgpt-chat org="..." api-url="..." api-key="..."></liipgpt-chat>
16
+
17
+ <script type="module">
18
+ import '@liip/liipgpt/chat';
19
+ </script>
20
+ ```
21
+
22
+ ### With CDN
23
+
24
+ ```html
25
+ <!DOCTYPE html>
26
+ <html>
27
+ <head>
28
+ <script type="module" src="https://unpkg.com/@liip/liipgpt/chat/liipgpt-chat.iife.js"></script>
29
+ </head>
30
+ <body>
31
+ <liipgpt-chat org="..." api-url="..." api-key="..."></liipgpt-chat>
32
+ </body>
33
+ </html>
34
+ ```
35
+
36
+ ## Web Component Attributes
37
+
38
+ - `org`: Is used to select theme and translation based on the organization. All available orgs are previewed here: [Preview](https://liipgpt.pages.liip.ch/liipgpt-chat/?org=liipgpt)
39
+ - `api-url`: URL to the chat backend
40
+ - `api-key`: API key that can be configured in the admin ui
41
+ - `lang` (optional): this can be used to control the chat ui language by the parent
42
+
43
+ ## Flyout Configuration
44
+
45
+ The chat ui is responsive based on the parent container. If you want embed the chat ui (e.g. in a flyout) make sure the parent has a predefined width and height.
46
+
47
+ ## Language Support
48
+
49
+ The component supports multiple languages with the following priority:
50
+
51
+ 1. Language was set via the `lang` attribute
52
+ 2. User selected language in the chat UI
53
+ 3. Browser language
54
+ 4. Fallback to "en"
55
+
56
+ Supported UI languages: 'en', 'de', 'fr', 'it'
57
+
58
+ # Library Usage
59
+
60
+ For custom AI integrations, you can use the LiipGPT client:
61
+
62
+ ```typescript
63
+ import { LiipGPTClient } from '@liip/liipgpt';
64
+
65
+ const client = new LiipGPTClient({
66
+ apiUrl: 'YOUR_API_URL',
67
+ apiKey: 'YOUR_API_KEY',
68
+ });
69
+
70
+ // Start a chat conversation
71
+ const response = client.chat('What can you tell me about Generative AI?');
72
+
73
+ // Subscribe to streaming responses
74
+ response.subscribe((message) => {
75
+ if (message.state === 'streaming') {
76
+ console.log(message.payload.message.markdown);
77
+ }
78
+ if (message.state === 'done') {
79
+ console.log('Chat complete');
80
+ }
81
+ if (message.state === 'error') {
82
+ console.error(message.errorMessage);
83
+ }
84
+ });
85
+ ```
86
+
87
+ # Loader settings
88
+
89
+ A simple loader is provided by default, you can override colors to fit your brand by using the `loaderBackground` and `loaderColor` attributes:
90
+
91
+ ```html
92
+ <liipgpt-chat ... loaderBackground="green" loaderColor="white"></liipgpt-chat>
93
+ ```
94
+
95
+ Remarks:
96
+
97
+ - it's recommended to set the same background color (as `loaderBackground`) on the parent container, to avoid a potential flickering.
98
+ - you can pass CSS gradients on the `loaderBackground` attribute.
99
+
100
+ ## Custom loader
101
+
102
+ You can also provide your own loader by using the `slot="loader"` attribute on a `div`, within the `liipgpt-chat` element:
103
+
104
+ ```html
105
+ <liipgpt-chat org="..." api-url="..." api-key="...">
106
+ <div slot="loader" style="...">
107
+ <svg>your animated svg here</svg>
108
+ <div>or a simple message</div>
109
+ </div>
110
+ </liipgpt-chat>
111
+ ```
package/chat/index.html CHANGED
@@ -3,6 +3,13 @@
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <script>
7
+ const params = new URLSearchParams(window.location.search);
8
+
9
+ if (params.get('org') === 'liipgpt') {
10
+ document.documentElement.style.background = 'linear-gradient(0deg, #1B181E 0%, #2A2440 90%)';
11
+ }
12
+ </script>
6
13
  <title>LiipGPT Preview</title>
7
14
  </head>
8
15
  <body>
@@ -29,16 +36,33 @@
29
36
  sidebarTop.appendChild(button);
30
37
  });
31
38
 
32
- // Add layout toggle buttons
33
- const fullscreenBtn = document.createElement('button');
34
- fullscreenBtn.textContent = 'Fullscreen';
35
- fullscreenBtn.onclick = () => document.getElementById('chat-wrapper').classList.remove('box-layout');
36
- sidebarBottom.appendChild(fullscreenBtn);
39
+ // Add layout toggle button
40
+ const toggleLayoutBtn = document.createElement('button');
41
+ toggleLayoutBtn.textContent = `Box layout${params.get('boxLayout') === 'true' ? ' ✓' : ''}`;
42
+ toggleLayoutBtn.onclick = () => {
43
+ const chatWrapper = document.getElementById('chat-wrapper');
44
+ const url = new URL(window.location.href);
37
45
 
38
- const boxLayoutBtn = document.createElement('button');
39
- boxLayoutBtn.textContent = 'Box layout';
40
- boxLayoutBtn.onclick = () => document.getElementById('chat-wrapper').classList.add('box-layout');
41
- sidebarBottom.appendChild(boxLayoutBtn);
46
+ if (chatWrapper.classList.contains('box-layout')) {
47
+ chatWrapper.classList.remove('box-layout');
48
+ url.searchParams.delete('boxLayout');
49
+ } else {
50
+ chatWrapper.classList.add('box-layout');
51
+ url.searchParams.set('boxLayout', 'true');
52
+ }
53
+ window.history.pushState({}, '', url.toString());
54
+ toggleLayoutBtn.textContent = `Box layout${window.location.search.includes('boxLayout=true') ? ' ✓' : ''}`;
55
+ };
56
+ sidebarBottom.appendChild(toggleLayoutBtn);
57
+
58
+ // Add loader toggle button
59
+ const toggleLoaderBtn = document.createElement('button');
60
+ toggleLoaderBtn.textContent = `Custom loader${params.get('customLoader') === 'true' ? ' ✓' : ''}`;
61
+ toggleLoaderBtn.onclick = () => {
62
+ toggleCustomLoader();
63
+ toggleLoaderBtn.textContent = `Custom loader${window.location.search.includes('customLoader=true') ? ' ✓' : ''}`;
64
+ };
65
+ sidebarBottom.appendChild(toggleLoaderBtn);
42
66
 
43
67
  // Add language toggle buttons
44
68
  const langContainer = document.createElement('div');
@@ -69,7 +93,6 @@
69
93
  function setOrg(org) {
70
94
  chat?.remove();
71
95
 
72
- const params = new URLSearchParams(window.location.search);
73
96
  const forceLang = params.get('lang');
74
97
 
75
98
  chat = document.createElement('liipgpt-chat');
@@ -78,6 +101,11 @@
78
101
  chat.setAttribute('apiUrl', 'https://liipgpt.api.dev.genai.liip.ch/liipgpt');
79
102
  chat.setAttribute('apiKey', 'X9hL4Gp5W2D7eRtF');
80
103
  chat.setAttribute('lang', forceLang);
104
+ if (org === 'liipgpt') {
105
+ chat.setAttribute('loaderBackground', 'linear-gradient(0deg, #1B181E 0%, #2A2440 90%)');
106
+ chat.setAttribute('loaderColor', '#9C92EE');
107
+ }
108
+
81
109
  document.getElementById('chat-wrapper').appendChild(chat);
82
110
 
83
111
  const url = new URL(window.location.href);
@@ -85,8 +113,51 @@
85
113
  window.history.pushState({}, '', url.toString());
86
114
  }
87
115
 
88
- const params = new URLSearchParams(window.location.search);
116
+ function toggleCustomLoader() {
117
+ const chat = document.getElementById('chat');
118
+ const existingLoader = chat.querySelector('[slot="loader"]');
119
+ const url = new URL(window.location.href);
120
+
121
+ if (existingLoader) {
122
+ existingLoader.remove();
123
+ url.searchParams.delete('customLoader');
124
+ } else {
125
+ const loader = document.createElement('div');
126
+ loader.setAttribute('slot', 'loader');
127
+ loader.style.cssText =
128
+ 'display: flex; flex-direction: column; gap:8px; justify-content: center; align-items: center; width: 100%; height: 100%; background: linear-gradient(0deg, #1B181E 0%, #2A2440 90%);';
129
+ loader.innerHTML = `
130
+ <svg width="48" height="48" stroke="rgba(127, 127, 127, 0.4)" viewBox="0 0 24 24">
131
+ <g>
132
+ <circle cx="12" cy="12" r="9.5" fill="none" stroke-width="1" stroke-linecap="round">
133
+ <animate attributeName="stroke-dasharray" dur="1.3s" calcMode="spline" values="0 150;42 150;42 150;42 150" keyTimes="0;0.475;0.7;1" keySplines="0.42,0,0.58,1;0.42,0,0.58,1;0.42,0,0.58,1" repeatCount="indefinite"/>
134
+ <animate attributeName="stroke-dashoffset" dur="1.3s" calcMode="spline" values="0;-2;-59;-59" keyTimes="0;0.475;0.98;1" keySplines="0.42,0,0.58,1;0.42,0,0.58,1;0.42,0,0.58,1" repeatCount="indefinite"/>
135
+ </circle>
136
+ <animateTransform attributeName="transform" type="rotate" dur="1.6s" values="0 12 12;360 12 12" repeatCount="indefinite"/>
137
+ </g>
138
+ </svg>
139
+ `;
140
+ chat.appendChild(loader);
141
+ url.searchParams.set('customLoader', 'true');
142
+ }
143
+ window.history.pushState({}, '', url.toString());
144
+ }
145
+
89
146
  setOrg(params.get('org') || 'liipgpt');
147
+
148
+ // Add this to initialize loader based on URL param
149
+ if (params.get('customLoader') === 'true') {
150
+ toggleCustomLoader();
151
+ }
152
+
153
+ // Initialize layout based on URL param
154
+ if (params.get('boxLayout') === 'true') {
155
+ document.getElementById('chat-wrapper').classList.add('box-layout');
156
+ }
157
+
158
+ if (params.get('org') === 'liipgpt') {
159
+ document.documentElement.style.background = 'linear-gradient(0deg, #1B181E 0%, #2A2440 90%)';
160
+ }
90
161
  </script>
91
162
 
92
163
  <style>
@@ -116,7 +187,7 @@
116
187
  padding: 16px;
117
188
  background-color: #f5f5f5;
118
189
  border-right: 1px solid #ddd;
119
- min-width: 150px;
190
+ min-width: 180px;
120
191
  height: 100vh;
121
192
  box-sizing: border-box;
122
193
  font-size: 0.8rem;
@@ -136,7 +207,7 @@
136
207
 
137
208
  #sidebar button,
138
209
  #sidebar .button {
139
- padding: 8px 16px;
210
+ padding: 8px 12px;
140
211
  border: 1px solid #ddd;
141
212
  border-radius: 4px;
142
213
  background-color: white;
@@ -165,7 +236,7 @@
165
236
  right: 40px;
166
237
 
167
238
  border-radius: 10px;
168
- box-shadow: 0 10px 20px 2px rgba(0, 0, 0, 0.4);
239
+ box-shadow: 0 10px 20px 2px rgba(0, 0, 0, 0.2);
169
240
  }
170
241
  </style>
171
242
  </body>