@deepfrog/pangents-widget 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -17
- package/dist/Widget.d.ts +4 -1
- package/dist/components/ChatbotWidget.d.ts +2 -0
- package/dist/components/CountryCodePicker.d.ts +9 -0
- package/dist/components/WidgetPopup.d.ts +2 -1
- package/dist/context/ChatContext.d.ts +5 -1
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.es.js +10 -16
- package/dist/index.es.js.map +1 -1
- package/dist/index.html +837 -0
- package/dist/pages/DataAgent/Components/AIAnswerRenderer.d.ts +6 -0
- package/dist/pages/DataAgent/Components/ChatHistory.d.ts +14 -0
- package/dist/pages/DataAgent/Components/ChatInput.d.ts +10 -0
- package/dist/pages/DataAgent/Components/DataAgentHeader.d.ts +8 -0
- package/dist/pages/DataAgent/Components/MessageBubble.d.ts +8 -0
- package/dist/pages/DataAgent/Components/WelcomeScreen.d.ts +6 -0
- package/dist/pages/DataAgent/Components/index.d.ts +5 -0
- package/dist/pages/DataAgent/index.d.ts +1 -0
- package/dist/pages/DataAgentV2/Components/AIAnswerRenderer.d.ts +6 -0
- package/dist/pages/DataAgentV2/Components/ChartRenderer.d.ts +10 -0
- package/dist/pages/DataAgentV2/Components/ChatHistory.d.ts +14 -0
- package/dist/pages/DataAgentV2/Components/ChatInput.d.ts +34 -0
- package/dist/pages/DataAgentV2/Components/DataAgentHeader.d.ts +9 -0
- package/dist/pages/DataAgentV2/Components/LoadingStepper.d.ts +5 -0
- package/dist/pages/DataAgentV2/Components/MessageBubble.d.ts +9 -0
- package/dist/pages/DataAgentV2/Components/WelcomeScreen.d.ts +6 -0
- package/dist/pages/DataAgentV2/Components/index.d.ts +5 -0
- package/dist/pages/DataAgentV2/index.d.ts +1 -0
- package/dist/pages/EmailLeads/LeadAction.d.ts +1 -0
- package/dist/pangents-widget.css +1 -1
- package/dist/preview-isolation.html +1 -0
- package/dist/preview-new.html +1 -0
- package/dist/preview-playground.html +792 -0
- package/dist/preview-production.html +293 -0
- package/dist/preview-staging.html +293 -0
- package/dist/preview.html +3 -4
- package/dist/services/api/client.d.ts +3 -0
- package/dist/services/api/dataAgentService.d.ts +38 -0
- package/dist/services/api/dataAgentServiceV2.d.ts +40 -0
- package/dist/services/api/emailLeadsService.d.ts +35 -0
- package/dist/services/mock/mockDataAgentService.d.ts +34 -0
- package/dist/services/mock/mockDataAgentServiceV2.d.ts +34 -0
- package/dist/stores/dataAgentStore.d.ts +37 -0
- package/dist/stores/dataAgentStoreV2.d.ts +40 -0
- package/dist/types/dataAgent.d.ts +37 -0
- package/dist/types/dataAgentV2.d.ts +97 -0
- package/dist/types/email.d.ts +28 -0
- package/dist/types/index.d.ts +0 -2
- package/dist/utils/countryCodes.d.ts +11 -0
- package/dist/widget-embed-CmAr6iD-.cjs.js +90 -0
- package/dist/widget-embed-CmAr6iD-.cjs.js.map +1 -0
- package/dist/widget-embed-DgNAEfZX.es.js +83784 -0
- package/dist/widget-embed-DgNAEfZX.es.js.map +1 -0
- package/dist/widget-embed.cjs.js +2 -2
- package/dist/widget-embed.d.ts +3 -0
- package/dist/widget-embed.es.js +2 -2
- package/dist/widget.js +76792 -23925
- package/package.json +5 -1
- package/dist/widget-embed-DCSN_j6Y.cjs.js +0 -46
- package/dist/widget-embed-DCSN_j6Y.cjs.js.map +0 -1
- package/dist/widget-embed-DzLM7DN8.es.js +0 -29838
- package/dist/widget-embed-DzLM7DN8.es.js.map +0 -1
package/dist/index.html
ADDED
|
@@ -0,0 +1,837 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>Pangent Widget — Interactive Playground</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--radius: 12px;
|
|
10
|
+
--bg: #f1f5f9;
|
|
11
|
+
--surface: #ffffff;
|
|
12
|
+
--border: #e2e8f0;
|
|
13
|
+
--text: #0f172a;
|
|
14
|
+
--muted: #64748b;
|
|
15
|
+
--accent: #1e40af;
|
|
16
|
+
--accent-hover: #1d3a9e;
|
|
17
|
+
--danger: #dc2626;
|
|
18
|
+
--success: #059669;
|
|
19
|
+
--warning: #d97706;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
26
|
+
background: var(--bg);
|
|
27
|
+
color: var(--text);
|
|
28
|
+
min-height: 100vh;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* ── TOP BAR ── */
|
|
32
|
+
.topbar {
|
|
33
|
+
background: var(--accent);
|
|
34
|
+
color: #fff;
|
|
35
|
+
padding: 0 2rem;
|
|
36
|
+
height: 56px;
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
justify-content: space-between;
|
|
40
|
+
box-shadow: 0 2px 8px rgba(0,0,0,.15);
|
|
41
|
+
position: sticky;
|
|
42
|
+
top: 0;
|
|
43
|
+
z-index: 100;
|
|
44
|
+
}
|
|
45
|
+
.topbar-brand {
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
gap: 10px;
|
|
49
|
+
font-weight: 700;
|
|
50
|
+
font-size: 1.05rem;
|
|
51
|
+
}
|
|
52
|
+
.topbar-brand .dot {
|
|
53
|
+
width: 10px; height: 10px;
|
|
54
|
+
border-radius: 50%;
|
|
55
|
+
background: #4ade80;
|
|
56
|
+
box-shadow: 0 0 6px #4ade80;
|
|
57
|
+
}
|
|
58
|
+
.topbar-links {
|
|
59
|
+
display: flex;
|
|
60
|
+
gap: 8px;
|
|
61
|
+
}
|
|
62
|
+
.topbar-links a {
|
|
63
|
+
color: rgba(255,255,255,.8);
|
|
64
|
+
text-decoration: none;
|
|
65
|
+
font-size: 0.85rem;
|
|
66
|
+
padding: 4px 10px;
|
|
67
|
+
border-radius: 6px;
|
|
68
|
+
transition: background .2s;
|
|
69
|
+
}
|
|
70
|
+
.topbar-links a:hover { background: rgba(255,255,255,.15); color: #fff; }
|
|
71
|
+
|
|
72
|
+
/* ── LAYOUT ── */
|
|
73
|
+
.layout {
|
|
74
|
+
display: grid;
|
|
75
|
+
grid-template-columns: 380px 1fr;
|
|
76
|
+
gap: 0;
|
|
77
|
+
min-height: calc(100vh - 56px);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/* ── SIDEBAR ── */
|
|
81
|
+
.sidebar {
|
|
82
|
+
background: var(--surface);
|
|
83
|
+
border-right: 1px solid var(--border);
|
|
84
|
+
padding: 1.5rem;
|
|
85
|
+
overflow-y: auto;
|
|
86
|
+
display: flex;
|
|
87
|
+
flex-direction: column;
|
|
88
|
+
gap: 1.25rem;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.sidebar-title {
|
|
92
|
+
font-size: 0.7rem;
|
|
93
|
+
font-weight: 700;
|
|
94
|
+
text-transform: uppercase;
|
|
95
|
+
letter-spacing: .08em;
|
|
96
|
+
color: var(--muted);
|
|
97
|
+
margin-bottom: .25rem;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ── CARD ── */
|
|
101
|
+
.card {
|
|
102
|
+
background: var(--surface);
|
|
103
|
+
border: 1px solid var(--border);
|
|
104
|
+
border-radius: var(--radius);
|
|
105
|
+
padding: 1.25rem;
|
|
106
|
+
}
|
|
107
|
+
.card-title {
|
|
108
|
+
font-size: 0.85rem;
|
|
109
|
+
font-weight: 700;
|
|
110
|
+
color: var(--text);
|
|
111
|
+
margin-bottom: 1rem;
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
gap: 6px;
|
|
115
|
+
}
|
|
116
|
+
.card-title .icon { font-size: 1rem; }
|
|
117
|
+
|
|
118
|
+
/* ── FORM ── */
|
|
119
|
+
.field { margin-bottom: .85rem; }
|
|
120
|
+
.field:last-child { margin-bottom: 0; }
|
|
121
|
+
label {
|
|
122
|
+
display: block;
|
|
123
|
+
font-size: 0.78rem;
|
|
124
|
+
font-weight: 600;
|
|
125
|
+
color: var(--muted);
|
|
126
|
+
margin-bottom: 5px;
|
|
127
|
+
text-transform: uppercase;
|
|
128
|
+
letter-spacing: .04em;
|
|
129
|
+
}
|
|
130
|
+
input[type="text"], input[type="password"], select {
|
|
131
|
+
width: 100%;
|
|
132
|
+
padding: 9px 12px;
|
|
133
|
+
border: 1.5px solid var(--border);
|
|
134
|
+
border-radius: 8px;
|
|
135
|
+
font-size: 0.88rem;
|
|
136
|
+
color: var(--text);
|
|
137
|
+
background: #f8fafc;
|
|
138
|
+
transition: border-color .2s, box-shadow .2s;
|
|
139
|
+
outline: none;
|
|
140
|
+
}
|
|
141
|
+
input[type="text"]:focus, input[type="password"]:focus, select:focus {
|
|
142
|
+
border-color: var(--accent);
|
|
143
|
+
box-shadow: 0 0 0 3px rgba(30,64,175,.12);
|
|
144
|
+
background: #fff;
|
|
145
|
+
}
|
|
146
|
+
.input-hint {
|
|
147
|
+
font-size: 0.72rem;
|
|
148
|
+
color: var(--muted);
|
|
149
|
+
margin-top: 4px;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* ── ENV PILLS ── */
|
|
153
|
+
.env-pills {
|
|
154
|
+
display: flex;
|
|
155
|
+
gap: 6px;
|
|
156
|
+
}
|
|
157
|
+
.env-pill {
|
|
158
|
+
flex: 1;
|
|
159
|
+
padding: 8px 4px;
|
|
160
|
+
border: 1.5px solid var(--border);
|
|
161
|
+
border-radius: 8px;
|
|
162
|
+
text-align: center;
|
|
163
|
+
font-size: 0.78rem;
|
|
164
|
+
font-weight: 600;
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
transition: all .2s;
|
|
167
|
+
background: #f8fafc;
|
|
168
|
+
color: var(--muted);
|
|
169
|
+
}
|
|
170
|
+
.env-pill:hover { border-color: #94a3b8; color: var(--text); }
|
|
171
|
+
.env-pill.active[data-env="development"] {
|
|
172
|
+
background: #eff6ff; border-color: #3b82f6; color: #1d4ed8;
|
|
173
|
+
}
|
|
174
|
+
.env-pill.active[data-env="staging"] {
|
|
175
|
+
background: #fffbeb; border-color: #f59e0b; color: #92400e;
|
|
176
|
+
}
|
|
177
|
+
.env-pill.active[data-env="production"] {
|
|
178
|
+
background: #f0fdf4; border-color: #10b981; color: #065f46;
|
|
179
|
+
}
|
|
180
|
+
.env-url {
|
|
181
|
+
font-size: 0.72rem;
|
|
182
|
+
color: var(--muted);
|
|
183
|
+
margin-top: 6px;
|
|
184
|
+
padding: 6px 10px;
|
|
185
|
+
background: #f8fafc;
|
|
186
|
+
border-radius: 6px;
|
|
187
|
+
border: 1px solid var(--border);
|
|
188
|
+
font-family: monospace;
|
|
189
|
+
word-break: break-all;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* ── COLOR SWATCH ── */
|
|
193
|
+
.color-row {
|
|
194
|
+
display: flex;
|
|
195
|
+
align-items: center;
|
|
196
|
+
gap: 8px;
|
|
197
|
+
}
|
|
198
|
+
.color-row input[type="color"] {
|
|
199
|
+
width: 36px;
|
|
200
|
+
height: 36px;
|
|
201
|
+
border: 1.5px solid var(--border);
|
|
202
|
+
border-radius: 8px;
|
|
203
|
+
padding: 2px;
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
background: #f8fafc;
|
|
206
|
+
}
|
|
207
|
+
.color-row input[type="text"] {
|
|
208
|
+
flex: 1;
|
|
209
|
+
font-family: monospace;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* ── POSITION GRID ── */
|
|
213
|
+
.position-grid {
|
|
214
|
+
display: grid;
|
|
215
|
+
grid-template-columns: 1fr 1fr;
|
|
216
|
+
gap: 6px;
|
|
217
|
+
}
|
|
218
|
+
.pos-btn {
|
|
219
|
+
padding: 8px;
|
|
220
|
+
border: 1.5px solid var(--border);
|
|
221
|
+
border-radius: 8px;
|
|
222
|
+
font-size: 0.78rem;
|
|
223
|
+
font-weight: 600;
|
|
224
|
+
cursor: pointer;
|
|
225
|
+
background: #f8fafc;
|
|
226
|
+
color: var(--muted);
|
|
227
|
+
transition: all .2s;
|
|
228
|
+
text-align: center;
|
|
229
|
+
}
|
|
230
|
+
.pos-btn:hover { border-color: #94a3b8; color: var(--text); }
|
|
231
|
+
.pos-btn.active {
|
|
232
|
+
background: #eff6ff;
|
|
233
|
+
border-color: var(--accent);
|
|
234
|
+
color: var(--accent);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/* ── BUTTONS ── */
|
|
238
|
+
.btn {
|
|
239
|
+
width: 100%;
|
|
240
|
+
padding: 11px;
|
|
241
|
+
border: none;
|
|
242
|
+
border-radius: 9px;
|
|
243
|
+
font-size: 0.9rem;
|
|
244
|
+
font-weight: 700;
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
transition: all .2s;
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
justify-content: center;
|
|
250
|
+
gap: 7px;
|
|
251
|
+
}
|
|
252
|
+
.btn-primary {
|
|
253
|
+
background: var(--accent);
|
|
254
|
+
color: #fff;
|
|
255
|
+
}
|
|
256
|
+
.btn-primary:hover { background: var(--accent-hover); transform: translateY(-1px); box-shadow: 0 4px 12px rgba(30,64,175,.3); }
|
|
257
|
+
.btn-danger {
|
|
258
|
+
background: #fef2f2;
|
|
259
|
+
color: var(--danger);
|
|
260
|
+
border: 1.5px solid #fecaca;
|
|
261
|
+
}
|
|
262
|
+
.btn-danger:hover { background: #fee2e2; }
|
|
263
|
+
.btn-ghost {
|
|
264
|
+
background: #f1f5f9;
|
|
265
|
+
color: var(--muted);
|
|
266
|
+
border: 1.5px solid var(--border);
|
|
267
|
+
}
|
|
268
|
+
.btn-ghost:hover { background: #e2e8f0; color: var(--text); }
|
|
269
|
+
|
|
270
|
+
/* ── STATUS BADGE ── */
|
|
271
|
+
.status-row {
|
|
272
|
+
display: flex;
|
|
273
|
+
align-items: center;
|
|
274
|
+
justify-content: space-between;
|
|
275
|
+
padding: 10px 12px;
|
|
276
|
+
border-radius: 8px;
|
|
277
|
+
font-size: 0.82rem;
|
|
278
|
+
font-weight: 600;
|
|
279
|
+
}
|
|
280
|
+
.status-row.idle { background: #f1f5f9; color: var(--muted); }
|
|
281
|
+
.status-row.active { background: #f0fdf4; color: #065f46; }
|
|
282
|
+
.status-row.error { background: #fef2f2; color: var(--danger); }
|
|
283
|
+
.status-dot {
|
|
284
|
+
width: 8px; height: 8px;
|
|
285
|
+
border-radius: 50%;
|
|
286
|
+
display: inline-block;
|
|
287
|
+
margin-right: 6px;
|
|
288
|
+
}
|
|
289
|
+
.status-dot.idle { background: #94a3b8; }
|
|
290
|
+
.status-dot.active { background: #10b981; box-shadow: 0 0 5px #10b981; }
|
|
291
|
+
.status-dot.error { background: var(--danger); }
|
|
292
|
+
|
|
293
|
+
/* ── MAIN AREA ── */
|
|
294
|
+
.main {
|
|
295
|
+
display: flex;
|
|
296
|
+
flex-direction: column;
|
|
297
|
+
align-items: center;
|
|
298
|
+
justify-content: center;
|
|
299
|
+
padding: 3rem 2rem;
|
|
300
|
+
gap: 2rem;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.preview-hero {
|
|
304
|
+
text-align: center;
|
|
305
|
+
max-width: 560px;
|
|
306
|
+
}
|
|
307
|
+
.preview-hero h1 {
|
|
308
|
+
font-size: 2rem;
|
|
309
|
+
font-weight: 800;
|
|
310
|
+
margin-bottom: .75rem;
|
|
311
|
+
color: var(--text);
|
|
312
|
+
}
|
|
313
|
+
.preview-hero p {
|
|
314
|
+
color: var(--muted);
|
|
315
|
+
font-size: 1rem;
|
|
316
|
+
line-height: 1.6;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.info-grid {
|
|
320
|
+
display: grid;
|
|
321
|
+
grid-template-columns: repeat(3, 1fr);
|
|
322
|
+
gap: 1rem;
|
|
323
|
+
width: 100%;
|
|
324
|
+
max-width: 700px;
|
|
325
|
+
}
|
|
326
|
+
.info-tile {
|
|
327
|
+
background: var(--surface);
|
|
328
|
+
border: 1px solid var(--border);
|
|
329
|
+
border-radius: var(--radius);
|
|
330
|
+
padding: 1.25rem;
|
|
331
|
+
text-align: center;
|
|
332
|
+
}
|
|
333
|
+
.info-tile .tile-label {
|
|
334
|
+
font-size: 0.72rem;
|
|
335
|
+
font-weight: 700;
|
|
336
|
+
text-transform: uppercase;
|
|
337
|
+
letter-spacing: .06em;
|
|
338
|
+
color: var(--muted);
|
|
339
|
+
margin-bottom: 6px;
|
|
340
|
+
}
|
|
341
|
+
.info-tile .tile-value {
|
|
342
|
+
font-size: 0.88rem;
|
|
343
|
+
font-weight: 700;
|
|
344
|
+
color: var(--text);
|
|
345
|
+
word-break: break-all;
|
|
346
|
+
font-family: monospace;
|
|
347
|
+
}
|
|
348
|
+
.tile-env-dev { border-top: 3px solid #3b82f6; }
|
|
349
|
+
.tile-env-stag { border-top: 3px solid #f59e0b; }
|
|
350
|
+
.tile-env-prod { border-top: 3px solid #10b981; }
|
|
351
|
+
|
|
352
|
+
.code-block {
|
|
353
|
+
background: #0f172a;
|
|
354
|
+
color: #e2e8f0;
|
|
355
|
+
border-radius: var(--radius);
|
|
356
|
+
padding: 1.25rem 1.5rem;
|
|
357
|
+
font-family: monospace;
|
|
358
|
+
font-size: 0.82rem;
|
|
359
|
+
line-height: 1.7;
|
|
360
|
+
width: 100%;
|
|
361
|
+
max-width: 700px;
|
|
362
|
+
overflow-x: auto;
|
|
363
|
+
position: relative;
|
|
364
|
+
}
|
|
365
|
+
.code-block .kw { color: #93c5fd; }
|
|
366
|
+
.code-block .str { color: #86efac; }
|
|
367
|
+
.code-block .key { color: #fde68a; }
|
|
368
|
+
.copy-btn {
|
|
369
|
+
position: absolute;
|
|
370
|
+
top: 10px; right: 10px;
|
|
371
|
+
background: rgba(255,255,255,.1);
|
|
372
|
+
border: none;
|
|
373
|
+
color: #94a3b8;
|
|
374
|
+
font-size: 0.75rem;
|
|
375
|
+
padding: 4px 10px;
|
|
376
|
+
border-radius: 6px;
|
|
377
|
+
cursor: pointer;
|
|
378
|
+
transition: all .2s;
|
|
379
|
+
}
|
|
380
|
+
.copy-btn:hover { background: rgba(255,255,255,.2); color: #fff; }
|
|
381
|
+
|
|
382
|
+
/* ── TOAST ── */
|
|
383
|
+
#toast {
|
|
384
|
+
position: fixed;
|
|
385
|
+
bottom: 2rem;
|
|
386
|
+
left: 50%;
|
|
387
|
+
transform: translateX(-50%) translateY(80px);
|
|
388
|
+
background: #0f172a;
|
|
389
|
+
color: #fff;
|
|
390
|
+
padding: 10px 20px;
|
|
391
|
+
border-radius: 999px;
|
|
392
|
+
font-size: 0.85rem;
|
|
393
|
+
font-weight: 600;
|
|
394
|
+
opacity: 0;
|
|
395
|
+
transition: all .3s ease;
|
|
396
|
+
z-index: 99999;
|
|
397
|
+
pointer-events: none;
|
|
398
|
+
}
|
|
399
|
+
#toast.show {
|
|
400
|
+
opacity: 1;
|
|
401
|
+
transform: translateX(-50%) translateY(0);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/* ── RESUME BANNER ── */
|
|
405
|
+
#resumeBanner {
|
|
406
|
+
display: none;
|
|
407
|
+
position: fixed;
|
|
408
|
+
top: 56px;
|
|
409
|
+
left: 0; right: 0;
|
|
410
|
+
z-index: 200;
|
|
411
|
+
background: linear-gradient(90deg, #1e40af, #3b82f6);
|
|
412
|
+
color: #fff;
|
|
413
|
+
padding: 10px 20px;
|
|
414
|
+
align-items: center;
|
|
415
|
+
justify-content: center;
|
|
416
|
+
gap: 10px;
|
|
417
|
+
font-size: 0.88rem;
|
|
418
|
+
font-weight: 600;
|
|
419
|
+
box-shadow: 0 2px 8px rgba(0,0,0,.2);
|
|
420
|
+
}
|
|
421
|
+
.resume-spinner {
|
|
422
|
+
width: 16px; height: 16px;
|
|
423
|
+
border: 2px solid rgba(255,255,255,.4);
|
|
424
|
+
border-top-color: #fff;
|
|
425
|
+
border-radius: 50%;
|
|
426
|
+
animation: spin .7s linear infinite;
|
|
427
|
+
flex-shrink: 0;
|
|
428
|
+
}
|
|
429
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
430
|
+
|
|
431
|
+
/* ── RESPONSIVE ── */
|
|
432
|
+
@media (max-width: 900px) {
|
|
433
|
+
.layout { grid-template-columns: 1fr; }
|
|
434
|
+
.sidebar { border-right: none; border-bottom: 1px solid var(--border); }
|
|
435
|
+
.info-grid { grid-template-columns: 1fr; }
|
|
436
|
+
}
|
|
437
|
+
</style>
|
|
438
|
+
</head>
|
|
439
|
+
<body>
|
|
440
|
+
|
|
441
|
+
<!-- Resume session banner -->
|
|
442
|
+
<div id="resumeBanner">
|
|
443
|
+
<div class="resume-spinner"></div>
|
|
444
|
+
<span>Resuming your last session…</span>
|
|
445
|
+
</div>
|
|
446
|
+
|
|
447
|
+
<div class="layout">
|
|
448
|
+
|
|
449
|
+
<!-- ══════════════════════════════ SIDEBAR ══════════════════════════════ -->
|
|
450
|
+
<aside class="sidebar">
|
|
451
|
+
|
|
452
|
+
<!-- Status -->
|
|
453
|
+
<div>
|
|
454
|
+
<div class="sidebar-title">Widget Status</div>
|
|
455
|
+
<div id="statusBadge" class="status-row idle">
|
|
456
|
+
<span><span class="status-dot idle" id="statusDot"></span><span id="statusText">Not initialized</span></span>
|
|
457
|
+
</div>
|
|
458
|
+
</div>
|
|
459
|
+
|
|
460
|
+
<!-- Credentials -->
|
|
461
|
+
<div class="card">
|
|
462
|
+
<div class="card-title"><span class="icon">🔑</span> Credentials</div>
|
|
463
|
+
<div class="field">
|
|
464
|
+
<label>Pangents API Key</label>
|
|
465
|
+
<input type="password" id="apiKey" placeholder="Paste your API key…" autocomplete="off" />
|
|
466
|
+
<div class="input-hint">Changing this clears localStorage & restarts the widget.</div>
|
|
467
|
+
</div>
|
|
468
|
+
<div class="field">
|
|
469
|
+
<label>Tenant ID</label>
|
|
470
|
+
<input type="text" id="tenantId" placeholder="e.g. 695a29fd4cb57717dcf02f5f" autocomplete="off" />
|
|
471
|
+
</div>
|
|
472
|
+
<div class="field">
|
|
473
|
+
<label>Email (optional)</label>
|
|
474
|
+
<input type="text" id="email" placeholder="user@example.com" autocomplete="off" />
|
|
475
|
+
</div>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
<!-- Environment -->
|
|
479
|
+
<div class="card">
|
|
480
|
+
<div class="card-title"><span class="icon">🌐</span> Environment</div>
|
|
481
|
+
<div class="env-pills">
|
|
482
|
+
<div class="env-pill active" data-env="development" onclick="selectEnv('development')">Dev</div>
|
|
483
|
+
<div class="env-pill" data-env="staging" onclick="selectEnv('staging')">Staging</div>
|
|
484
|
+
<div class="env-pill" data-env="production" onclick="selectEnv('production')">Prod</div>
|
|
485
|
+
</div>
|
|
486
|
+
<div id="envUrl" class="env-url">https://dev.backend.pangents.ai</div>
|
|
487
|
+
</div>
|
|
488
|
+
|
|
489
|
+
<!-- Theme -->
|
|
490
|
+
<div class="card">
|
|
491
|
+
<div class="card-title"><span class="icon">🎨</span> Theme</div>
|
|
492
|
+
<div class="field">
|
|
493
|
+
<label>Primary Color</label>
|
|
494
|
+
<div class="color-row">
|
|
495
|
+
<input type="color" id="colorPicker" value="#1e40af" oninput="syncColor(this.value)" />
|
|
496
|
+
<input type="text" id="colorHex" value="#1e40af" placeholder="#1e40af" oninput="syncColorFromText(this.value)" maxlength="7" />
|
|
497
|
+
</div>
|
|
498
|
+
</div>
|
|
499
|
+
<div class="field">
|
|
500
|
+
<label>Bubble Icon URL</label>
|
|
501
|
+
<input type="text" id="bubbleIcon" value="https://app.ultrashiptms.ai/icon.ico?25f91bedca5c761d" placeholder="https://…" />
|
|
502
|
+
</div>
|
|
503
|
+
</div>
|
|
504
|
+
|
|
505
|
+
<!-- Position -->
|
|
506
|
+
<div class="card">
|
|
507
|
+
<div class="card-title"><span class="icon">📌</span> Position</div>
|
|
508
|
+
<div class="position-grid">
|
|
509
|
+
<div class="pos-btn" data-pos="top-left" onclick="selectPos('top-left')">↖ Top Left</div>
|
|
510
|
+
<div class="pos-btn" data-pos="top-right" onclick="selectPos('top-right')">↗ Top Right</div>
|
|
511
|
+
<div class="pos-btn" data-pos="bottom-left" onclick="selectPos('bottom-left')">↙ Bottom Left</div>
|
|
512
|
+
<div class="pos-btn active" data-pos="bottom-right" onclick="selectPos('bottom-right')">↘ Bottom Right</div>
|
|
513
|
+
</div>
|
|
514
|
+
</div>
|
|
515
|
+
|
|
516
|
+
<!-- Actions -->
|
|
517
|
+
<div style="display:flex;flex-direction:column;gap:8px;">
|
|
518
|
+
<button class="btn btn-primary" onclick="applyConfig()">
|
|
519
|
+
▶ Apply & Launch Widget
|
|
520
|
+
</button>
|
|
521
|
+
<button class="btn btn-danger" onclick="resetAll()">
|
|
522
|
+
🗑 Clear localStorage & Reset
|
|
523
|
+
</button>
|
|
524
|
+
<button class="btn btn-ghost" onclick="destroyWidget()">
|
|
525
|
+
✕ Destroy Widget
|
|
526
|
+
</button>
|
|
527
|
+
</div>
|
|
528
|
+
|
|
529
|
+
</aside>
|
|
530
|
+
|
|
531
|
+
<!-- ══════════════════════════════ MAIN ══════════════════════════════ -->
|
|
532
|
+
<main class="main">
|
|
533
|
+
|
|
534
|
+
<div class="preview-hero">
|
|
535
|
+
<h1>Interactive Playground</h1>
|
|
536
|
+
<p>Configure credentials, environment, and theme on the left, then click <strong>Apply & Launch Widget</strong>. Changing the API key automatically clears localStorage so you start fresh.</p>
|
|
537
|
+
</div>
|
|
538
|
+
|
|
539
|
+
<!-- Live config tiles -->
|
|
540
|
+
<div class="info-grid">
|
|
541
|
+
<div class="info-tile" id="tileEnv">
|
|
542
|
+
<div class="tile-label">Environment</div>
|
|
543
|
+
<div class="tile-value" id="tileEnvVal">development</div>
|
|
544
|
+
</div>
|
|
545
|
+
<div class="info-tile">
|
|
546
|
+
<div class="tile-label">API Key</div>
|
|
547
|
+
<div class="tile-value" id="tileKey">—</div>
|
|
548
|
+
</div>
|
|
549
|
+
<div class="info-tile">
|
|
550
|
+
<div class="tile-label">Tenant ID</div>
|
|
551
|
+
<div class="tile-value" id="tileTenant">—</div>
|
|
552
|
+
</div>
|
|
553
|
+
</div>
|
|
554
|
+
|
|
555
|
+
<!-- Generated snippet -->
|
|
556
|
+
<div class="code-block" id="codeBlock">
|
|
557
|
+
<button class="copy-btn" onclick="copyCode()">Copy</button>
|
|
558
|
+
<pre id="codeSnippet">// Fill in the form and click Apply to see the generated snippet.</pre>
|
|
559
|
+
</div>
|
|
560
|
+
|
|
561
|
+
</main>
|
|
562
|
+
</div>
|
|
563
|
+
|
|
564
|
+
<!-- Toast -->
|
|
565
|
+
<div id="toast"></div>
|
|
566
|
+
|
|
567
|
+
<script type="module">
|
|
568
|
+
// ── State ──────────────────────────────────────────────────────────────
|
|
569
|
+
let widgetLoaded = false;
|
|
570
|
+
let Chatbot = null;
|
|
571
|
+
let currentEnv = 'development';
|
|
572
|
+
let currentPos = 'bottom-right';
|
|
573
|
+
|
|
574
|
+
const ENV_URLS = {
|
|
575
|
+
development: 'https://dev.backend.pangents.ai',
|
|
576
|
+
staging: 'https://stage.backend.pangents.ai',
|
|
577
|
+
production: 'https://app.backend.pangents.ai',
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
const WIDGET_JS = 'https://chat-widget-gamma-azure.vercel.app/widget.js';
|
|
581
|
+
|
|
582
|
+
// ── Expose helpers to global scope (called from onclick) ───────────────
|
|
583
|
+
window.selectEnv = (env) => {
|
|
584
|
+
currentEnv = env;
|
|
585
|
+
document.querySelectorAll('.env-pill').forEach(p => {
|
|
586
|
+
p.classList.toggle('active', p.dataset.env === env);
|
|
587
|
+
});
|
|
588
|
+
document.getElementById('envUrl').textContent = ENV_URLS[env];
|
|
589
|
+
updateTiles();
|
|
590
|
+
updateSnippet();
|
|
591
|
+
saveSettings();
|
|
592
|
+
};
|
|
593
|
+
|
|
594
|
+
window.selectPos = (pos) => {
|
|
595
|
+
currentPos = pos;
|
|
596
|
+
document.querySelectorAll('.pos-btn').forEach(p => {
|
|
597
|
+
p.classList.toggle('active', p.dataset.pos === pos);
|
|
598
|
+
});
|
|
599
|
+
updateSnippet();
|
|
600
|
+
saveSettings();
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
window.syncColor = (val) => {
|
|
604
|
+
document.getElementById('colorHex').value = val;
|
|
605
|
+
updateSnippet();
|
|
606
|
+
saveSettings();
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
window.syncColorFromText = (val) => {
|
|
610
|
+
if (/^#[0-9a-fA-F]{6}$/.test(val)) {
|
|
611
|
+
document.getElementById('colorPicker').value = val;
|
|
612
|
+
}
|
|
613
|
+
updateSnippet();
|
|
614
|
+
saveSettings();
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
window.applyConfig = async () => {
|
|
618
|
+
const apiKey = document.getElementById('apiKey').value.trim();
|
|
619
|
+
const tenantId = document.getElementById('tenantId').value.trim();
|
|
620
|
+
const email = document.getElementById('email').value.trim();
|
|
621
|
+
const color = document.getElementById('colorHex').value.trim() || '#1e40af';
|
|
622
|
+
const icon = document.getElementById('bubbleIcon').value.trim();
|
|
623
|
+
|
|
624
|
+
if (!apiKey || !tenantId) {
|
|
625
|
+
toast('⚠️ API Key and Tenant ID are required');
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// If API key changed → clear localStorage for a fresh start
|
|
630
|
+
const prevKey = sessionStorage.getItem('pg_playground_key');
|
|
631
|
+
if (prevKey && prevKey !== apiKey) {
|
|
632
|
+
clearStorage();
|
|
633
|
+
toast('🗑 API key changed — localStorage cleared');
|
|
634
|
+
}
|
|
635
|
+
sessionStorage.setItem('pg_playground_key', apiKey);
|
|
636
|
+
|
|
637
|
+
setStatus('active', 'Initializing…');
|
|
638
|
+
|
|
639
|
+
try {
|
|
640
|
+
if (!widgetLoaded) {
|
|
641
|
+
const mod = await import(WIDGET_JS);
|
|
642
|
+
Chatbot = mod.default;
|
|
643
|
+
widgetLoaded = true;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
Chatbot.init({
|
|
647
|
+
pangentsApiKey: apiKey,
|
|
648
|
+
tenantId,
|
|
649
|
+
email: email || undefined,
|
|
650
|
+
environment: currentEnv,
|
|
651
|
+
theme: {
|
|
652
|
+
primaryColor: color,
|
|
653
|
+
headerBg: color,
|
|
654
|
+
headerText: '#ffffff',
|
|
655
|
+
bubbleIcon: icon || undefined,
|
|
656
|
+
},
|
|
657
|
+
position: currentPos,
|
|
658
|
+
margin: '20px',
|
|
659
|
+
paddingWidgetContainer: '10px 20px',
|
|
660
|
+
zIndex: 9999,
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
setStatus('active', 'Widget running — ' + currentEnv);
|
|
664
|
+
updateTiles();
|
|
665
|
+
updateSnippet();
|
|
666
|
+
toast('✅ Widget launched!');
|
|
667
|
+
} catch (err) {
|
|
668
|
+
setStatus('error', 'Error: ' + err.message);
|
|
669
|
+
toast('❌ Failed to load widget');
|
|
670
|
+
console.error(err);
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
|
|
674
|
+
window.destroyWidget = () => {
|
|
675
|
+
if (Chatbot) {
|
|
676
|
+
try { Chatbot.destroy(); } catch(e) {}
|
|
677
|
+
}
|
|
678
|
+
setStatus('idle', 'Not initialized');
|
|
679
|
+
toast('Widget destroyed');
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
window.resetAll = () => {
|
|
683
|
+
clearStorage();
|
|
684
|
+
if (Chatbot) {
|
|
685
|
+
try { Chatbot.destroy(); } catch(e) {}
|
|
686
|
+
}
|
|
687
|
+
sessionStorage.removeItem('pg_playground_key');
|
|
688
|
+
localStorage.removeItem(PG_KEY);
|
|
689
|
+
setStatus('idle', 'Not initialized');
|
|
690
|
+
toast('🗑 localStorage cleared & widget destroyed');
|
|
691
|
+
};
|
|
692
|
+
|
|
693
|
+
window.copyCode = () => {
|
|
694
|
+
const text = document.getElementById('codeSnippet').textContent;
|
|
695
|
+
navigator.clipboard.writeText(text).then(() => toast('📋 Copied!'));
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
// ── Helpers ────────────────────────────────────────────────────────────
|
|
699
|
+
function clearStorage() {
|
|
700
|
+
const keysToRemove = [];
|
|
701
|
+
for (let i = 0; i < localStorage.length; i++) {
|
|
702
|
+
const k = localStorage.key(i);
|
|
703
|
+
if (k) keysToRemove.push(k);
|
|
704
|
+
}
|
|
705
|
+
keysToRemove.forEach(k => localStorage.removeItem(k));
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function setStatus(type, text) {
|
|
709
|
+
const badge = document.getElementById('statusBadge');
|
|
710
|
+
const dot = document.getElementById('statusDot');
|
|
711
|
+
const label = document.getElementById('statusText');
|
|
712
|
+
badge.className = 'status-row ' + type;
|
|
713
|
+
dot.className = 'status-dot ' + type;
|
|
714
|
+
label.textContent = text;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function updateTiles() {
|
|
718
|
+
const apiKey = document.getElementById('apiKey').value.trim();
|
|
719
|
+
const tenantId = document.getElementById('tenantId').value.trim();
|
|
720
|
+
|
|
721
|
+
const tileEnv = document.getElementById('tileEnv');
|
|
722
|
+
tileEnv.className = 'info-tile tile-env-' + { development:'dev', staging:'stag', production:'prod' }[currentEnv];
|
|
723
|
+
document.getElementById('tileEnvVal').textContent = currentEnv;
|
|
724
|
+
document.getElementById('tileKey').textContent = apiKey ? apiKey.slice(0,8) + '…' : '—';
|
|
725
|
+
document.getElementById('tileTenant').textContent = tenantId ? tenantId.slice(0,10) + '…' : '—';
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function updateSnippet() {
|
|
729
|
+
const apiKey = document.getElementById('apiKey').value.trim() || '<api_key>';
|
|
730
|
+
const tenantId = document.getElementById('tenantId').value.trim() || '<tenant_id>';
|
|
731
|
+
const email = document.getElementById('email').value.trim();
|
|
732
|
+
const color = document.getElementById('colorHex').value.trim() || '#1e40af';
|
|
733
|
+
const icon = document.getElementById('bubbleIcon').value.trim() || '<icon_url>';
|
|
734
|
+
|
|
735
|
+
const emailLine = email ? `\n email: "${email}",` : '';
|
|
736
|
+
|
|
737
|
+
document.getElementById('codeSnippet').innerHTML =
|
|
738
|
+
`<span class="kw">import</span> Chatbot <span class="kw">from</span> <span class="str">"https://chat-widget-gamma-azure.vercel.app/widget.js"</span>;\n\nChatbot.<span class="kw">init</span>({\n <span class="key">pangentsApiKey</span>: <span class="str">"${esc(apiKey)}"</span>,\n <span class="key">tenantId</span>: <span class="str">"${esc(tenantId)}"</span>,${emailLine}\n <span class="key">environment</span>: <span class="str">"${currentEnv}"</span>,\n <span class="key">theme</span>: {\n <span class="key">primaryColor</span>: <span class="str">"${esc(color)}"</span>,\n <span class="key">headerBg</span>: <span class="str">"${esc(color)}"</span>,\n <span class="key">headerText</span>: <span class="str">"#ffffff"</span>,\n <span class="key">bubbleIcon</span>: <span class="str">"${esc(icon)}"</span>,\n },\n <span class="key">position</span>: <span class="str">"${currentPos}"</span>,\n <span class="key">margin</span>: <span class="str">"20px"</span>,\n <span class="key">paddingWidgetContainer</span>: <span class="str">"10px 20px"</span>,\n <span class="key">zIndex</span>: 9999,\n});`;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
function esc(s) {
|
|
742
|
+
return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
function toast(msg) {
|
|
746
|
+
const el = document.getElementById('toast');
|
|
747
|
+
el.textContent = msg;
|
|
748
|
+
el.classList.add('show');
|
|
749
|
+
setTimeout(() => el.classList.remove('show'), 2800);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// ── Persist settings to localStorage ──────────────────────────────
|
|
753
|
+
const PG_KEY = 'pg_playground_settings';
|
|
754
|
+
|
|
755
|
+
function saveSettings() {
|
|
756
|
+
const settings = {
|
|
757
|
+
apiKey: document.getElementById('apiKey').value,
|
|
758
|
+
tenantId: document.getElementById('tenantId').value,
|
|
759
|
+
email: document.getElementById('email').value,
|
|
760
|
+
color: document.getElementById('colorHex').value,
|
|
761
|
+
bubbleIcon: document.getElementById('bubbleIcon').value,
|
|
762
|
+
env: currentEnv,
|
|
763
|
+
pos: currentPos,
|
|
764
|
+
};
|
|
765
|
+
localStorage.setItem(PG_KEY, JSON.stringify(settings));
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function loadSettings() {
|
|
769
|
+
try {
|
|
770
|
+
const raw = localStorage.getItem(PG_KEY);
|
|
771
|
+
if (!raw) return;
|
|
772
|
+
const s = JSON.parse(raw);
|
|
773
|
+
if (s.apiKey) document.getElementById('apiKey').value = s.apiKey;
|
|
774
|
+
if (s.tenantId) document.getElementById('tenantId').value = s.tenantId;
|
|
775
|
+
if (s.email) document.getElementById('email').value = s.email;
|
|
776
|
+
if (s.color) {
|
|
777
|
+
document.getElementById('colorHex').value = s.color;
|
|
778
|
+
document.getElementById('colorPicker').value = s.color;
|
|
779
|
+
}
|
|
780
|
+
if (s.bubbleIcon) document.getElementById('bubbleIcon').value = s.bubbleIcon;
|
|
781
|
+
if (s.env) {
|
|
782
|
+
currentEnv = s.env;
|
|
783
|
+
document.querySelectorAll('.env-pill').forEach(p => {
|
|
784
|
+
p.classList.toggle('active', p.dataset.env === s.env);
|
|
785
|
+
});
|
|
786
|
+
document.getElementById('envUrl').textContent = ENV_URLS[s.env];
|
|
787
|
+
}
|
|
788
|
+
if (s.pos) {
|
|
789
|
+
currentPos = s.pos;
|
|
790
|
+
document.querySelectorAll('.pos-btn').forEach(p => {
|
|
791
|
+
p.classList.toggle('active', p.dataset.pos === s.pos);
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
} catch(e) {}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// ── Live snippet update on any input ──────────────────────────────
|
|
798
|
+
['apiKey','tenantId','email','bubbleIcon','colorHex'].forEach(id => {
|
|
799
|
+
document.getElementById(id).addEventListener('input', () => {
|
|
800
|
+
updateTiles();
|
|
801
|
+
updateSnippet();
|
|
802
|
+
saveSettings();
|
|
803
|
+
});
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
// ── Restore settings on load, then auto-launch if credentials exist ──
|
|
807
|
+
const hasSavedSession = (() => {
|
|
808
|
+
try {
|
|
809
|
+
const raw = localStorage.getItem(PG_KEY);
|
|
810
|
+
if (!raw) return false;
|
|
811
|
+
const s = JSON.parse(raw);
|
|
812
|
+
return !!(s.apiKey && s.tenantId);
|
|
813
|
+
} catch(e) { return false; }
|
|
814
|
+
})();
|
|
815
|
+
|
|
816
|
+
loadSettings();
|
|
817
|
+
updateTiles();
|
|
818
|
+
updateSnippet();
|
|
819
|
+
|
|
820
|
+
if (hasSavedSession) {
|
|
821
|
+
// Show resume banner
|
|
822
|
+
const banner = document.getElementById('resumeBanner');
|
|
823
|
+
if (banner) banner.style.display = 'flex';
|
|
824
|
+
|
|
825
|
+
setStatus('active', 'Resuming session…');
|
|
826
|
+
|
|
827
|
+
// Small delay so the page paints first, then auto-launch
|
|
828
|
+
setTimeout(() => {
|
|
829
|
+
applyConfig().then(() => {
|
|
830
|
+
const banner = document.getElementById('resumeBanner');
|
|
831
|
+
if (banner) banner.style.display = 'none';
|
|
832
|
+
});
|
|
833
|
+
}, 400);
|
|
834
|
+
}
|
|
835
|
+
</script>
|
|
836
|
+
</body>
|
|
837
|
+
</html>
|