@spektra-cloudevents/application-copilot-widget 1.0.0 → 1.2.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/dist/index.cjs +223 -3
- package/dist/index.d.cts +10 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +223 -3
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -77,7 +77,7 @@ var widgetStyles = `
|
|
|
77
77
|
/* Panel */
|
|
78
78
|
.acw-panel {
|
|
79
79
|
position: fixed; right: 24px; bottom: 96px; z-index: 2147483000;
|
|
80
|
-
width:
|
|
80
|
+
width: 480px; max-width: calc(100vw - 48px); height: 660px; max-height: calc(100vh - 120px);
|
|
81
81
|
display: none; flex-direction: column;
|
|
82
82
|
background: var(--acw-bg); color: var(--acw-fg);
|
|
83
83
|
border: 1px solid var(--acw-border); border-radius: 16px; overflow: hidden;
|
|
@@ -135,6 +135,26 @@ var widgetStyles = `
|
|
|
135
135
|
.acw-input:focus { outline: 2px solid var(--acw-accent); outline-offset: 0; }
|
|
136
136
|
.acw-send { border: none; border-radius: 10px; padding: 0 16px; background: var(--acw-accent); color: #fff; cursor: pointer; font-size: 14px; font-family: inherit; }
|
|
137
137
|
.acw-send:hover { background: var(--acw-accent-dark); }
|
|
138
|
+
|
|
139
|
+
/* Support-ticket draft card */
|
|
140
|
+
.acw-ticket { background: var(--acw-bg); border: 1px solid var(--acw-border); border-radius: 12px; padding: 14px; margin: 12px 0; border-left: 4px solid var(--acw-accent); }
|
|
141
|
+
.acw-ticket-title { font-size: 15px; font-weight: 600; color: var(--acw-fg); margin: 0 0 4px; }
|
|
142
|
+
.acw-ticket-sub { font-size: 12px; color: var(--acw-muted); margin: 0 0 10px; }
|
|
143
|
+
.acw-ticket-label { display: block; font-size: 12px; font-weight: 600; color: var(--acw-muted); margin: 12px 0 5px; }
|
|
144
|
+
.acw-ticket-input, .acw-ticket-desc {
|
|
145
|
+
width: 100%; box-sizing: border-box; font-family: inherit; font-size: 13px;
|
|
146
|
+
padding: 10px 12px; border: 1px solid var(--acw-border); border-radius: 10px;
|
|
147
|
+
color: var(--acw-fg); background: var(--acw-bg);
|
|
148
|
+
}
|
|
149
|
+
.acw-ticket-desc { resize: vertical; min-height: 170px; line-height: 1.5; }
|
|
150
|
+
.acw-ticket-input:focus, .acw-ticket-desc:focus { outline: 2px solid var(--acw-accent); outline-offset: 0; }
|
|
151
|
+
.acw-ticket-actions { display: flex; gap: 10px; margin-top: 14px; }
|
|
152
|
+
.acw-btn-primary { border: none; border-radius: 10px; padding: 10px 20px; cursor: pointer; background: var(--acw-accent); color: #fff; font-size: 14px; font-weight: 600; font-family: inherit; }
|
|
153
|
+
.acw-btn-primary:hover { background: var(--acw-accent-dark); }
|
|
154
|
+
.acw-btn-ghost { border: 1px solid var(--acw-border); border-radius: 10px; padding: 10px 16px; cursor: pointer; background: var(--acw-bg); color: var(--acw-fg); font-size: 14px; font-family: inherit; }
|
|
155
|
+
.acw-btn-ghost:hover { background: #f0eefe; }
|
|
156
|
+
.acw-link-btn { margin-top: 8px; border: none; background: transparent; color: var(--acw-accent); font-size: 12px; font-family: inherit; cursor: pointer; padding: 0; text-decoration: underline; }
|
|
157
|
+
.acw-link-btn:hover { color: var(--acw-accent-dark); }
|
|
138
158
|
`;
|
|
139
159
|
|
|
140
160
|
// src/widget.ts
|
|
@@ -146,10 +166,15 @@ var PROGRESS_LABELS = {
|
|
|
146
166
|
};
|
|
147
167
|
var CopilotWidget = class {
|
|
148
168
|
constructor() {
|
|
169
|
+
this.lastQuestion = "";
|
|
149
170
|
this.mode = "explain";
|
|
150
171
|
}
|
|
151
172
|
init(config) {
|
|
152
173
|
this.config = config;
|
|
174
|
+
try {
|
|
175
|
+
console.log("[ApplicationCopilot] widget loaded \u2014 support-ticket build v2");
|
|
176
|
+
} catch {
|
|
177
|
+
}
|
|
153
178
|
this.mount();
|
|
154
179
|
this.connectSignalR().catch(() => {
|
|
155
180
|
});
|
|
@@ -161,6 +186,17 @@ var CopilotWidget = class {
|
|
|
161
186
|
close() {
|
|
162
187
|
this.panel?.classList.remove("acw-open");
|
|
163
188
|
}
|
|
189
|
+
/** Show the launcher (widget host). No-op before init(). */
|
|
190
|
+
show() {
|
|
191
|
+
if (this.host) this.host.style.display = "";
|
|
192
|
+
}
|
|
193
|
+
/** Hide the launcher and close the panel. Used to gate the widget by route. */
|
|
194
|
+
hide() {
|
|
195
|
+
if (this.host) {
|
|
196
|
+
this.host.style.display = "none";
|
|
197
|
+
}
|
|
198
|
+
this.close();
|
|
199
|
+
}
|
|
164
200
|
setContext(context) {
|
|
165
201
|
this.contextOverride = context;
|
|
166
202
|
}
|
|
@@ -168,6 +204,25 @@ var CopilotWidget = class {
|
|
|
168
204
|
this.recentError = errorContext;
|
|
169
205
|
this.open();
|
|
170
206
|
}
|
|
207
|
+
/** Proactive nudge: show + open the panel with a message and an optional one-click suggested question. */
|
|
208
|
+
notify(text, suggestion) {
|
|
209
|
+
this.show();
|
|
210
|
+
this.open();
|
|
211
|
+
if (!this.body) return;
|
|
212
|
+
const el = document.createElement("div");
|
|
213
|
+
el.className = "acw-msg acw-assistant acw-nudge";
|
|
214
|
+
el.innerHTML = `<div class="acw-reason">${escapeHtml(text)}</div>`;
|
|
215
|
+
if (suggestion) {
|
|
216
|
+
const btn = document.createElement("button");
|
|
217
|
+
btn.type = "button";
|
|
218
|
+
btn.className = "acw-chip";
|
|
219
|
+
btn.textContent = suggestion;
|
|
220
|
+
btn.addEventListener("click", () => this.ask(suggestion));
|
|
221
|
+
el.appendChild(btn);
|
|
222
|
+
}
|
|
223
|
+
this.body.appendChild(el);
|
|
224
|
+
this.body.scrollTop = this.body.scrollHeight;
|
|
225
|
+
}
|
|
171
226
|
ask(question) {
|
|
172
227
|
if (this.mode === "intake") {
|
|
173
228
|
void this.runIntakeAsk(question);
|
|
@@ -194,27 +249,79 @@ var CopilotWidget = class {
|
|
|
194
249
|
wrap.querySelector(".acw-launch").addEventListener("click", () => this.panel?.classList.contains("acw-open") ? this.close() : this.open());
|
|
195
250
|
const panel = document.createElement("div");
|
|
196
251
|
panel.className = "acw-panel";
|
|
252
|
+
const uploadIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>';
|
|
197
253
|
panel.innerHTML = `
|
|
198
254
|
<div class="acw-header">
|
|
199
255
|
${sparkle}
|
|
200
256
|
<span class="acw-title">Application Copilot</span>
|
|
257
|
+
<button class="acw-kb" type="button" aria-label="Add a document to the knowledge base" title="Add to knowledge base (admins)" style="background:none;border:none;color:inherit;cursor:pointer;padding:2px 6px;display:inline-flex;align-items:center;">${uploadIcon}</button>
|
|
201
258
|
<button class="acw-close" type="button" aria-label="Close">×</button>
|
|
202
259
|
</div>
|
|
203
260
|
<div class="acw-body"></div>
|
|
204
261
|
<div class="acw-footer">
|
|
205
262
|
<input class="acw-input" type="text" placeholder="Ask a question\u2026" />
|
|
206
263
|
<button class="acw-send" type="button">Send</button>
|
|
207
|
-
</div
|
|
264
|
+
</div>
|
|
265
|
+
<input class="acw-file" type="file" accept=".pdf,.docx,.xlsx,.xlsm,.txt,.md,.markdown,.csv" style="display:none" />`;
|
|
208
266
|
this.root.appendChild(panel);
|
|
209
267
|
this.panel = panel;
|
|
210
268
|
this.body = panel.querySelector(".acw-body");
|
|
211
269
|
this.input = panel.querySelector(".acw-input");
|
|
270
|
+
this.fileInput = panel.querySelector(".acw-file");
|
|
212
271
|
panel.querySelector(".acw-close").addEventListener("click", () => this.close());
|
|
213
272
|
panel.querySelector(".acw-send").addEventListener("click", () => this.submitFromInput());
|
|
273
|
+
panel.querySelector(".acw-kb").addEventListener("click", () => this.promptUpload());
|
|
274
|
+
this.fileInput.addEventListener("change", () => {
|
|
275
|
+
const f = this.fileInput?.files?.[0];
|
|
276
|
+
if (f) {
|
|
277
|
+
void this.uploadDocument(f);
|
|
278
|
+
if (this.fileInput) this.fileInput.value = "";
|
|
279
|
+
}
|
|
280
|
+
});
|
|
214
281
|
this.input.addEventListener("keydown", (e) => {
|
|
215
282
|
if (e.key === "Enter") this.submitFromInput();
|
|
216
283
|
});
|
|
217
284
|
}
|
|
285
|
+
/** Open the file picker to add a document to the knowledge base (server enforces admin-only). */
|
|
286
|
+
promptUpload() {
|
|
287
|
+
this.open();
|
|
288
|
+
this.fileInput?.click();
|
|
289
|
+
}
|
|
290
|
+
async uploadDocument(file) {
|
|
291
|
+
this.open();
|
|
292
|
+
this.addMessage(`Uploading \u201C${file.name}\u201D to the knowledge base\u2026`, "user");
|
|
293
|
+
this.setProgress("Extracting and indexing\u2026");
|
|
294
|
+
try {
|
|
295
|
+
const form = new FormData();
|
|
296
|
+
form.append("file", file, file.name);
|
|
297
|
+
form.append("tenantUniqueName", this.config.tenantUniqueName);
|
|
298
|
+
form.append("environment", this.config.environment);
|
|
299
|
+
const h = {};
|
|
300
|
+
const token = this.config.getAuthToken?.();
|
|
301
|
+
const email = typeof this.config.devUserEmail === "function" ? this.config.devUserEmail() : this.config.devUserEmail;
|
|
302
|
+
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
303
|
+
else if (email) h["X-User-Email"] = email;
|
|
304
|
+
const res = await fetch(`${this.config.apiBaseUrl}/api/knowledge/upload`, { method: "POST", headers: h, body: form });
|
|
305
|
+
this.setProgress("");
|
|
306
|
+
if (res.status === 403) {
|
|
307
|
+
this.renderAnswer({ status: "Forbidden", primaryReason: "Only Spektra Admins can add to the knowledge base." });
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (!res.ok) {
|
|
311
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: `Upload failed (${res.status}).` });
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const data = await res.json();
|
|
315
|
+
if (data?.status === "Indexed") {
|
|
316
|
+
this.renderAnswer({ status: "Answered", disabled: false, primaryReason: `Added \u201C${data.title}\u201D to the knowledge base (${data.chunks} chunk${data.chunks === 1 ? "" : "s"}). I can now use it to answer questions.` });
|
|
317
|
+
} else {
|
|
318
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: data?.reason || "Could not add the document." });
|
|
319
|
+
}
|
|
320
|
+
} catch {
|
|
321
|
+
this.setProgress("");
|
|
322
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: "Could not reach the copilot service." });
|
|
323
|
+
}
|
|
324
|
+
}
|
|
218
325
|
submitFromInput() {
|
|
219
326
|
const q = this.input?.value.trim();
|
|
220
327
|
if (q) {
|
|
@@ -341,8 +448,115 @@ var CopilotWidget = class {
|
|
|
341
448
|
el.innerHTML = `<div class="acw-reason">${escapeHtml(answer.primaryReason || "")}</div>` + (answer.verifiedRoleLabel ? `<div class="acw-role">Verified role: ${escapeHtml(answer.verifiedRoleLabel)}</div>` : "") + (sourceLabel ? `<div class="acw-role">Source: ${sourceLabel}</div>` : "") + (evidence ? `<div class="acw-evidence">Evidence: ${evidence}</div>` : "");
|
|
342
449
|
}
|
|
343
450
|
this.body.appendChild(el);
|
|
451
|
+
const role = (answer.verifiedRoleLabel || "").toLowerCase();
|
|
452
|
+
const eligibleRole = role === "requestor" || role === "approver";
|
|
453
|
+
const looksBlocked = /lock(ed)?|can only be (changed|edited)|not editable|isn'?t permitted|not permitted|can'?t (change|edit|update)|cannot (change|edit|update)|read-only|don'?t have access|not allowed/i.test(answer.primaryReason || "");
|
|
454
|
+
const offerTicket = answer.supportTicketEligible === true || (answer.disabled === true || looksBlocked) && eligibleRole;
|
|
455
|
+
if (!isFailure && offerTicket) {
|
|
456
|
+
const btn = document.createElement("button");
|
|
457
|
+
btn.type = "button";
|
|
458
|
+
btn.className = "acw-chip";
|
|
459
|
+
btn.textContent = "Raise a support ticket";
|
|
460
|
+
btn.addEventListener("click", () => this.openTicketDraft(answer));
|
|
461
|
+
el.appendChild(btn);
|
|
462
|
+
}
|
|
344
463
|
this.body.scrollTop = this.body.scrollHeight;
|
|
345
464
|
}
|
|
465
|
+
currentRequestId() {
|
|
466
|
+
const ctx = this.currentContext();
|
|
467
|
+
const explicit = ctx.page?.requestId || ctx.page?.RequestId;
|
|
468
|
+
if (explicit) return String(explicit);
|
|
469
|
+
const route = ctx.page?.route || (typeof window !== "undefined" ? window.location.pathname : "");
|
|
470
|
+
const ms = /MS[A-Z0-9]{6,}/i.exec(route || "");
|
|
471
|
+
if (ms) return ms[0];
|
|
472
|
+
const guid = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/.exec(route || "");
|
|
473
|
+
return guid ? guid[0] : void 0;
|
|
474
|
+
}
|
|
475
|
+
/** Build a pre-filled, editable ticket draft and let the user review + Send (they trigger the API call). */
|
|
476
|
+
openTicketDraft(answer) {
|
|
477
|
+
const fromAnswer = /MS[A-Z0-9]{6,}/i.exec(answer.primaryReason || "");
|
|
478
|
+
const requestId = fromAnswer ? fromAnswer[0] : this.currentRequestId();
|
|
479
|
+
const role = answer.verifiedRoleLabel || "";
|
|
480
|
+
const reason = answer.primaryReason || "I am blocked from completing an action.";
|
|
481
|
+
const subject = requestId ? `[Cloud Events] Help needed on request ${requestId}` : "[Cloud Events] Help needed with an event request";
|
|
482
|
+
const description = `Raised from the Application Copilot.
|
|
483
|
+
|
|
484
|
+
` + (role ? `Role: ${role}
|
|
485
|
+
` : "") + `Request Id: ${requestId || "(not on a specific request)"}
|
|
486
|
+
` + (this.lastQuestion ? `Question: ${this.lastQuestion}
|
|
487
|
+
` : "") + `
|
|
488
|
+
What the copilot reported:
|
|
489
|
+
${reason}`;
|
|
490
|
+
const card = document.createElement("div");
|
|
491
|
+
card.className = "acw-ticket";
|
|
492
|
+
card.innerHTML = `<div class="acw-ticket-title">Raise a support ticket</div><div class="acw-ticket-sub">Review the details below and click Send. It goes to the support team as a Zoho Desk ticket.</div><label class="acw-ticket-label">Subject</label><input class="acw-ticket-subject acw-ticket-input" type="text" /><label class="acw-ticket-label">Description</label><textarea class="acw-ticket-desc" rows="8"></textarea><div class="acw-ticket-actions"><button class="acw-ticket-send acw-btn-primary" type="button">Send ticket</button><button class="acw-ticket-cancel acw-btn-ghost" type="button">Cancel</button></div>`;
|
|
493
|
+
card.querySelector(".acw-ticket-subject").value = subject;
|
|
494
|
+
card.querySelector(".acw-ticket-desc").value = description;
|
|
495
|
+
card.querySelector(".acw-ticket-cancel").addEventListener("click", () => card.remove());
|
|
496
|
+
card.querySelector(".acw-ticket-send").addEventListener("click", () => {
|
|
497
|
+
const subj = card.querySelector(".acw-ticket-subject").value.trim();
|
|
498
|
+
const desc = card.querySelector(".acw-ticket-desc").value.trim();
|
|
499
|
+
card.remove();
|
|
500
|
+
void this.submitTicket(subj, desc, requestId);
|
|
501
|
+
});
|
|
502
|
+
this.body.appendChild(card);
|
|
503
|
+
this.body.scrollTop = this.body.scrollHeight;
|
|
504
|
+
}
|
|
505
|
+
async submitTicket(subject, description, requestId) {
|
|
506
|
+
this.setProgress("Sending your support ticket\u2026");
|
|
507
|
+
try {
|
|
508
|
+
const sessionId = await this.ensureSession();
|
|
509
|
+
const res = await fetch(`${this.config.apiBaseUrl}/api/sessions/${sessionId}/support-ticket`, {
|
|
510
|
+
method: "POST",
|
|
511
|
+
headers: this.headers(),
|
|
512
|
+
body: JSON.stringify({
|
|
513
|
+
tenantUniqueName: this.config.tenantUniqueName,
|
|
514
|
+
environment: this.config.environment,
|
|
515
|
+
requestId,
|
|
516
|
+
subject,
|
|
517
|
+
description
|
|
518
|
+
})
|
|
519
|
+
});
|
|
520
|
+
this.setProgress("");
|
|
521
|
+
if (res.status === 403) {
|
|
522
|
+
this.renderAnswer({ status: "Forbidden", primaryReason: "Support tickets are available for Requestor/Approver roles." });
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
if (!res.ok) {
|
|
526
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: `Could not send the ticket (${res.status}).` });
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
const data = await res.json();
|
|
530
|
+
if (data?.status === "Created") {
|
|
531
|
+
const num = data.ticketNumber ? `#${data.ticketNumber}` : "";
|
|
532
|
+
const el = document.createElement("div");
|
|
533
|
+
el.className = "acw-answer acw-okstate";
|
|
534
|
+
el.innerHTML = `<div class="acw-reason">Support ticket ${escapeHtml(num)} has been created. The support team will follow up.</div>`;
|
|
535
|
+
if (data.webUrl || data.id || this.config.onOpenTicket) {
|
|
536
|
+
const open = document.createElement("button");
|
|
537
|
+
open.type = "button";
|
|
538
|
+
open.className = "acw-link-btn";
|
|
539
|
+
open.textContent = "Open the ticket";
|
|
540
|
+
open.addEventListener("click", () => {
|
|
541
|
+
const ticket = { id: data.id, number: data.ticketNumber, webUrl: data.webUrl };
|
|
542
|
+
if (this.config.onOpenTicket) {
|
|
543
|
+
this.config.onOpenTicket(ticket);
|
|
544
|
+
} else if (data.webUrl) {
|
|
545
|
+
window.open(data.webUrl, "_blank", "noopener");
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
el.appendChild(open);
|
|
549
|
+
}
|
|
550
|
+
this.body.appendChild(el);
|
|
551
|
+
this.body.scrollTop = this.body.scrollHeight;
|
|
552
|
+
} else {
|
|
553
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: data?.reason || "Could not create the ticket." });
|
|
554
|
+
}
|
|
555
|
+
} catch {
|
|
556
|
+
this.setProgress("");
|
|
557
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: "Could not reach the copilot service." });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
346
560
|
// ---- networking ----
|
|
347
561
|
headers() {
|
|
348
562
|
const h = { "Content-Type": "application/json" };
|
|
@@ -372,6 +586,7 @@ var CopilotWidget = class {
|
|
|
372
586
|
}
|
|
373
587
|
async runAsk(question) {
|
|
374
588
|
this.open();
|
|
589
|
+
this.lastQuestion = question;
|
|
375
590
|
this.addMessage(question, "user");
|
|
376
591
|
this.setProgress("Reading your question\u2026");
|
|
377
592
|
try {
|
|
@@ -389,7 +604,8 @@ var CopilotWidget = class {
|
|
|
389
604
|
this.renderAnswer(await res.json());
|
|
390
605
|
} catch (err) {
|
|
391
606
|
this.setProgress("");
|
|
392
|
-
|
|
607
|
+
const msg = err?.message ? String(err.message) : "Could not reach the copilot service.";
|
|
608
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: msg });
|
|
393
609
|
}
|
|
394
610
|
}
|
|
395
611
|
async connectSignalR() {
|
|
@@ -415,6 +631,10 @@ var ApplicationCopilotWidget = {
|
|
|
415
631
|
init: (config) => instance.init(config),
|
|
416
632
|
open: () => instance.open(),
|
|
417
633
|
close: () => instance.close(),
|
|
634
|
+
show: () => instance.show(),
|
|
635
|
+
hide: () => instance.hide(),
|
|
636
|
+
notify: (text, suggestion) => instance.notify(text, suggestion),
|
|
637
|
+
promptUpload: () => instance.promptUpload(),
|
|
418
638
|
ask: (question) => instance.ask(question),
|
|
419
639
|
setContext: (context) => instance.setContext(context),
|
|
420
640
|
captureError: (errorContext) => instance.captureError(errorContext)
|
package/dist/index.d.cts
CHANGED
|
@@ -46,6 +46,12 @@ interface WidgetConfig {
|
|
|
46
46
|
buttonText?: string;
|
|
47
47
|
/** Host navigation hook (e.g. Angular router). If omitted, the widget falls back to window.location. */
|
|
48
48
|
onNavigate?: (path: string) => void;
|
|
49
|
+
/** Host hook to open a created support ticket (e.g. the embedded Zoho ASAP widget). If omitted, the widget opens webUrl in a new tab. */
|
|
50
|
+
onOpenTicket?: (ticket: {
|
|
51
|
+
id?: string;
|
|
52
|
+
number?: string;
|
|
53
|
+
webUrl?: string;
|
|
54
|
+
}) => void;
|
|
49
55
|
}
|
|
50
56
|
interface CopilotAnswer {
|
|
51
57
|
status: string;
|
|
@@ -70,6 +76,10 @@ declare const ApplicationCopilotWidget: {
|
|
|
70
76
|
init: (config: WidgetConfig) => void;
|
|
71
77
|
open: () => void;
|
|
72
78
|
close: () => void;
|
|
79
|
+
show: () => void;
|
|
80
|
+
hide: () => void;
|
|
81
|
+
notify: (text: string, suggestion?: string) => void;
|
|
82
|
+
promptUpload: () => void;
|
|
73
83
|
ask: (question: string) => void;
|
|
74
84
|
setContext: (context: ApplicationContext) => void;
|
|
75
85
|
captureError: (errorContext: ApplicationContext["recentError"]) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -46,6 +46,12 @@ interface WidgetConfig {
|
|
|
46
46
|
buttonText?: string;
|
|
47
47
|
/** Host navigation hook (e.g. Angular router). If omitted, the widget falls back to window.location. */
|
|
48
48
|
onNavigate?: (path: string) => void;
|
|
49
|
+
/** Host hook to open a created support ticket (e.g. the embedded Zoho ASAP widget). If omitted, the widget opens webUrl in a new tab. */
|
|
50
|
+
onOpenTicket?: (ticket: {
|
|
51
|
+
id?: string;
|
|
52
|
+
number?: string;
|
|
53
|
+
webUrl?: string;
|
|
54
|
+
}) => void;
|
|
49
55
|
}
|
|
50
56
|
interface CopilotAnswer {
|
|
51
57
|
status: string;
|
|
@@ -70,6 +76,10 @@ declare const ApplicationCopilotWidget: {
|
|
|
70
76
|
init: (config: WidgetConfig) => void;
|
|
71
77
|
open: () => void;
|
|
72
78
|
close: () => void;
|
|
79
|
+
show: () => void;
|
|
80
|
+
hide: () => void;
|
|
81
|
+
notify: (text: string, suggestion?: string) => void;
|
|
82
|
+
promptUpload: () => void;
|
|
73
83
|
ask: (question: string) => void;
|
|
74
84
|
setContext: (context: ApplicationContext) => void;
|
|
75
85
|
captureError: (errorContext: ApplicationContext["recentError"]) => void;
|
package/dist/index.js
CHANGED
|
@@ -40,7 +40,7 @@ var widgetStyles = `
|
|
|
40
40
|
/* Panel */
|
|
41
41
|
.acw-panel {
|
|
42
42
|
position: fixed; right: 24px; bottom: 96px; z-index: 2147483000;
|
|
43
|
-
width:
|
|
43
|
+
width: 480px; max-width: calc(100vw - 48px); height: 660px; max-height: calc(100vh - 120px);
|
|
44
44
|
display: none; flex-direction: column;
|
|
45
45
|
background: var(--acw-bg); color: var(--acw-fg);
|
|
46
46
|
border: 1px solid var(--acw-border); border-radius: 16px; overflow: hidden;
|
|
@@ -98,6 +98,26 @@ var widgetStyles = `
|
|
|
98
98
|
.acw-input:focus { outline: 2px solid var(--acw-accent); outline-offset: 0; }
|
|
99
99
|
.acw-send { border: none; border-radius: 10px; padding: 0 16px; background: var(--acw-accent); color: #fff; cursor: pointer; font-size: 14px; font-family: inherit; }
|
|
100
100
|
.acw-send:hover { background: var(--acw-accent-dark); }
|
|
101
|
+
|
|
102
|
+
/* Support-ticket draft card */
|
|
103
|
+
.acw-ticket { background: var(--acw-bg); border: 1px solid var(--acw-border); border-radius: 12px; padding: 14px; margin: 12px 0; border-left: 4px solid var(--acw-accent); }
|
|
104
|
+
.acw-ticket-title { font-size: 15px; font-weight: 600; color: var(--acw-fg); margin: 0 0 4px; }
|
|
105
|
+
.acw-ticket-sub { font-size: 12px; color: var(--acw-muted); margin: 0 0 10px; }
|
|
106
|
+
.acw-ticket-label { display: block; font-size: 12px; font-weight: 600; color: var(--acw-muted); margin: 12px 0 5px; }
|
|
107
|
+
.acw-ticket-input, .acw-ticket-desc {
|
|
108
|
+
width: 100%; box-sizing: border-box; font-family: inherit; font-size: 13px;
|
|
109
|
+
padding: 10px 12px; border: 1px solid var(--acw-border); border-radius: 10px;
|
|
110
|
+
color: var(--acw-fg); background: var(--acw-bg);
|
|
111
|
+
}
|
|
112
|
+
.acw-ticket-desc { resize: vertical; min-height: 170px; line-height: 1.5; }
|
|
113
|
+
.acw-ticket-input:focus, .acw-ticket-desc:focus { outline: 2px solid var(--acw-accent); outline-offset: 0; }
|
|
114
|
+
.acw-ticket-actions { display: flex; gap: 10px; margin-top: 14px; }
|
|
115
|
+
.acw-btn-primary { border: none; border-radius: 10px; padding: 10px 20px; cursor: pointer; background: var(--acw-accent); color: #fff; font-size: 14px; font-weight: 600; font-family: inherit; }
|
|
116
|
+
.acw-btn-primary:hover { background: var(--acw-accent-dark); }
|
|
117
|
+
.acw-btn-ghost { border: 1px solid var(--acw-border); border-radius: 10px; padding: 10px 16px; cursor: pointer; background: var(--acw-bg); color: var(--acw-fg); font-size: 14px; font-family: inherit; }
|
|
118
|
+
.acw-btn-ghost:hover { background: #f0eefe; }
|
|
119
|
+
.acw-link-btn { margin-top: 8px; border: none; background: transparent; color: var(--acw-accent); font-size: 12px; font-family: inherit; cursor: pointer; padding: 0; text-decoration: underline; }
|
|
120
|
+
.acw-link-btn:hover { color: var(--acw-accent-dark); }
|
|
101
121
|
`;
|
|
102
122
|
|
|
103
123
|
// src/widget.ts
|
|
@@ -109,10 +129,15 @@ var PROGRESS_LABELS = {
|
|
|
109
129
|
};
|
|
110
130
|
var CopilotWidget = class {
|
|
111
131
|
constructor() {
|
|
132
|
+
this.lastQuestion = "";
|
|
112
133
|
this.mode = "explain";
|
|
113
134
|
}
|
|
114
135
|
init(config) {
|
|
115
136
|
this.config = config;
|
|
137
|
+
try {
|
|
138
|
+
console.log("[ApplicationCopilot] widget loaded \u2014 support-ticket build v2");
|
|
139
|
+
} catch {
|
|
140
|
+
}
|
|
116
141
|
this.mount();
|
|
117
142
|
this.connectSignalR().catch(() => {
|
|
118
143
|
});
|
|
@@ -124,6 +149,17 @@ var CopilotWidget = class {
|
|
|
124
149
|
close() {
|
|
125
150
|
this.panel?.classList.remove("acw-open");
|
|
126
151
|
}
|
|
152
|
+
/** Show the launcher (widget host). No-op before init(). */
|
|
153
|
+
show() {
|
|
154
|
+
if (this.host) this.host.style.display = "";
|
|
155
|
+
}
|
|
156
|
+
/** Hide the launcher and close the panel. Used to gate the widget by route. */
|
|
157
|
+
hide() {
|
|
158
|
+
if (this.host) {
|
|
159
|
+
this.host.style.display = "none";
|
|
160
|
+
}
|
|
161
|
+
this.close();
|
|
162
|
+
}
|
|
127
163
|
setContext(context) {
|
|
128
164
|
this.contextOverride = context;
|
|
129
165
|
}
|
|
@@ -131,6 +167,25 @@ var CopilotWidget = class {
|
|
|
131
167
|
this.recentError = errorContext;
|
|
132
168
|
this.open();
|
|
133
169
|
}
|
|
170
|
+
/** Proactive nudge: show + open the panel with a message and an optional one-click suggested question. */
|
|
171
|
+
notify(text, suggestion) {
|
|
172
|
+
this.show();
|
|
173
|
+
this.open();
|
|
174
|
+
if (!this.body) return;
|
|
175
|
+
const el = document.createElement("div");
|
|
176
|
+
el.className = "acw-msg acw-assistant acw-nudge";
|
|
177
|
+
el.innerHTML = `<div class="acw-reason">${escapeHtml(text)}</div>`;
|
|
178
|
+
if (suggestion) {
|
|
179
|
+
const btn = document.createElement("button");
|
|
180
|
+
btn.type = "button";
|
|
181
|
+
btn.className = "acw-chip";
|
|
182
|
+
btn.textContent = suggestion;
|
|
183
|
+
btn.addEventListener("click", () => this.ask(suggestion));
|
|
184
|
+
el.appendChild(btn);
|
|
185
|
+
}
|
|
186
|
+
this.body.appendChild(el);
|
|
187
|
+
this.body.scrollTop = this.body.scrollHeight;
|
|
188
|
+
}
|
|
134
189
|
ask(question) {
|
|
135
190
|
if (this.mode === "intake") {
|
|
136
191
|
void this.runIntakeAsk(question);
|
|
@@ -157,27 +212,79 @@ var CopilotWidget = class {
|
|
|
157
212
|
wrap.querySelector(".acw-launch").addEventListener("click", () => this.panel?.classList.contains("acw-open") ? this.close() : this.open());
|
|
158
213
|
const panel = document.createElement("div");
|
|
159
214
|
panel.className = "acw-panel";
|
|
215
|
+
const uploadIcon = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>';
|
|
160
216
|
panel.innerHTML = `
|
|
161
217
|
<div class="acw-header">
|
|
162
218
|
${sparkle}
|
|
163
219
|
<span class="acw-title">Application Copilot</span>
|
|
220
|
+
<button class="acw-kb" type="button" aria-label="Add a document to the knowledge base" title="Add to knowledge base (admins)" style="background:none;border:none;color:inherit;cursor:pointer;padding:2px 6px;display:inline-flex;align-items:center;">${uploadIcon}</button>
|
|
164
221
|
<button class="acw-close" type="button" aria-label="Close">×</button>
|
|
165
222
|
</div>
|
|
166
223
|
<div class="acw-body"></div>
|
|
167
224
|
<div class="acw-footer">
|
|
168
225
|
<input class="acw-input" type="text" placeholder="Ask a question\u2026" />
|
|
169
226
|
<button class="acw-send" type="button">Send</button>
|
|
170
|
-
</div
|
|
227
|
+
</div>
|
|
228
|
+
<input class="acw-file" type="file" accept=".pdf,.docx,.xlsx,.xlsm,.txt,.md,.markdown,.csv" style="display:none" />`;
|
|
171
229
|
this.root.appendChild(panel);
|
|
172
230
|
this.panel = panel;
|
|
173
231
|
this.body = panel.querySelector(".acw-body");
|
|
174
232
|
this.input = panel.querySelector(".acw-input");
|
|
233
|
+
this.fileInput = panel.querySelector(".acw-file");
|
|
175
234
|
panel.querySelector(".acw-close").addEventListener("click", () => this.close());
|
|
176
235
|
panel.querySelector(".acw-send").addEventListener("click", () => this.submitFromInput());
|
|
236
|
+
panel.querySelector(".acw-kb").addEventListener("click", () => this.promptUpload());
|
|
237
|
+
this.fileInput.addEventListener("change", () => {
|
|
238
|
+
const f = this.fileInput?.files?.[0];
|
|
239
|
+
if (f) {
|
|
240
|
+
void this.uploadDocument(f);
|
|
241
|
+
if (this.fileInput) this.fileInput.value = "";
|
|
242
|
+
}
|
|
243
|
+
});
|
|
177
244
|
this.input.addEventListener("keydown", (e) => {
|
|
178
245
|
if (e.key === "Enter") this.submitFromInput();
|
|
179
246
|
});
|
|
180
247
|
}
|
|
248
|
+
/** Open the file picker to add a document to the knowledge base (server enforces admin-only). */
|
|
249
|
+
promptUpload() {
|
|
250
|
+
this.open();
|
|
251
|
+
this.fileInput?.click();
|
|
252
|
+
}
|
|
253
|
+
async uploadDocument(file) {
|
|
254
|
+
this.open();
|
|
255
|
+
this.addMessage(`Uploading \u201C${file.name}\u201D to the knowledge base\u2026`, "user");
|
|
256
|
+
this.setProgress("Extracting and indexing\u2026");
|
|
257
|
+
try {
|
|
258
|
+
const form = new FormData();
|
|
259
|
+
form.append("file", file, file.name);
|
|
260
|
+
form.append("tenantUniqueName", this.config.tenantUniqueName);
|
|
261
|
+
form.append("environment", this.config.environment);
|
|
262
|
+
const h = {};
|
|
263
|
+
const token = this.config.getAuthToken?.();
|
|
264
|
+
const email = typeof this.config.devUserEmail === "function" ? this.config.devUserEmail() : this.config.devUserEmail;
|
|
265
|
+
if (token) h["Authorization"] = `Bearer ${token}`;
|
|
266
|
+
else if (email) h["X-User-Email"] = email;
|
|
267
|
+
const res = await fetch(`${this.config.apiBaseUrl}/api/knowledge/upload`, { method: "POST", headers: h, body: form });
|
|
268
|
+
this.setProgress("");
|
|
269
|
+
if (res.status === 403) {
|
|
270
|
+
this.renderAnswer({ status: "Forbidden", primaryReason: "Only Spektra Admins can add to the knowledge base." });
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
if (!res.ok) {
|
|
274
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: `Upload failed (${res.status}).` });
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
const data = await res.json();
|
|
278
|
+
if (data?.status === "Indexed") {
|
|
279
|
+
this.renderAnswer({ status: "Answered", disabled: false, primaryReason: `Added \u201C${data.title}\u201D to the knowledge base (${data.chunks} chunk${data.chunks === 1 ? "" : "s"}). I can now use it to answer questions.` });
|
|
280
|
+
} else {
|
|
281
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: data?.reason || "Could not add the document." });
|
|
282
|
+
}
|
|
283
|
+
} catch {
|
|
284
|
+
this.setProgress("");
|
|
285
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: "Could not reach the copilot service." });
|
|
286
|
+
}
|
|
287
|
+
}
|
|
181
288
|
submitFromInput() {
|
|
182
289
|
const q = this.input?.value.trim();
|
|
183
290
|
if (q) {
|
|
@@ -304,8 +411,115 @@ var CopilotWidget = class {
|
|
|
304
411
|
el.innerHTML = `<div class="acw-reason">${escapeHtml(answer.primaryReason || "")}</div>` + (answer.verifiedRoleLabel ? `<div class="acw-role">Verified role: ${escapeHtml(answer.verifiedRoleLabel)}</div>` : "") + (sourceLabel ? `<div class="acw-role">Source: ${sourceLabel}</div>` : "") + (evidence ? `<div class="acw-evidence">Evidence: ${evidence}</div>` : "");
|
|
305
412
|
}
|
|
306
413
|
this.body.appendChild(el);
|
|
414
|
+
const role = (answer.verifiedRoleLabel || "").toLowerCase();
|
|
415
|
+
const eligibleRole = role === "requestor" || role === "approver";
|
|
416
|
+
const looksBlocked = /lock(ed)?|can only be (changed|edited)|not editable|isn'?t permitted|not permitted|can'?t (change|edit|update)|cannot (change|edit|update)|read-only|don'?t have access|not allowed/i.test(answer.primaryReason || "");
|
|
417
|
+
const offerTicket = answer.supportTicketEligible === true || (answer.disabled === true || looksBlocked) && eligibleRole;
|
|
418
|
+
if (!isFailure && offerTicket) {
|
|
419
|
+
const btn = document.createElement("button");
|
|
420
|
+
btn.type = "button";
|
|
421
|
+
btn.className = "acw-chip";
|
|
422
|
+
btn.textContent = "Raise a support ticket";
|
|
423
|
+
btn.addEventListener("click", () => this.openTicketDraft(answer));
|
|
424
|
+
el.appendChild(btn);
|
|
425
|
+
}
|
|
307
426
|
this.body.scrollTop = this.body.scrollHeight;
|
|
308
427
|
}
|
|
428
|
+
currentRequestId() {
|
|
429
|
+
const ctx = this.currentContext();
|
|
430
|
+
const explicit = ctx.page?.requestId || ctx.page?.RequestId;
|
|
431
|
+
if (explicit) return String(explicit);
|
|
432
|
+
const route = ctx.page?.route || (typeof window !== "undefined" ? window.location.pathname : "");
|
|
433
|
+
const ms = /MS[A-Z0-9]{6,}/i.exec(route || "");
|
|
434
|
+
if (ms) return ms[0];
|
|
435
|
+
const guid = /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/.exec(route || "");
|
|
436
|
+
return guid ? guid[0] : void 0;
|
|
437
|
+
}
|
|
438
|
+
/** Build a pre-filled, editable ticket draft and let the user review + Send (they trigger the API call). */
|
|
439
|
+
openTicketDraft(answer) {
|
|
440
|
+
const fromAnswer = /MS[A-Z0-9]{6,}/i.exec(answer.primaryReason || "");
|
|
441
|
+
const requestId = fromAnswer ? fromAnswer[0] : this.currentRequestId();
|
|
442
|
+
const role = answer.verifiedRoleLabel || "";
|
|
443
|
+
const reason = answer.primaryReason || "I am blocked from completing an action.";
|
|
444
|
+
const subject = requestId ? `[Cloud Events] Help needed on request ${requestId}` : "[Cloud Events] Help needed with an event request";
|
|
445
|
+
const description = `Raised from the Application Copilot.
|
|
446
|
+
|
|
447
|
+
` + (role ? `Role: ${role}
|
|
448
|
+
` : "") + `Request Id: ${requestId || "(not on a specific request)"}
|
|
449
|
+
` + (this.lastQuestion ? `Question: ${this.lastQuestion}
|
|
450
|
+
` : "") + `
|
|
451
|
+
What the copilot reported:
|
|
452
|
+
${reason}`;
|
|
453
|
+
const card = document.createElement("div");
|
|
454
|
+
card.className = "acw-ticket";
|
|
455
|
+
card.innerHTML = `<div class="acw-ticket-title">Raise a support ticket</div><div class="acw-ticket-sub">Review the details below and click Send. It goes to the support team as a Zoho Desk ticket.</div><label class="acw-ticket-label">Subject</label><input class="acw-ticket-subject acw-ticket-input" type="text" /><label class="acw-ticket-label">Description</label><textarea class="acw-ticket-desc" rows="8"></textarea><div class="acw-ticket-actions"><button class="acw-ticket-send acw-btn-primary" type="button">Send ticket</button><button class="acw-ticket-cancel acw-btn-ghost" type="button">Cancel</button></div>`;
|
|
456
|
+
card.querySelector(".acw-ticket-subject").value = subject;
|
|
457
|
+
card.querySelector(".acw-ticket-desc").value = description;
|
|
458
|
+
card.querySelector(".acw-ticket-cancel").addEventListener("click", () => card.remove());
|
|
459
|
+
card.querySelector(".acw-ticket-send").addEventListener("click", () => {
|
|
460
|
+
const subj = card.querySelector(".acw-ticket-subject").value.trim();
|
|
461
|
+
const desc = card.querySelector(".acw-ticket-desc").value.trim();
|
|
462
|
+
card.remove();
|
|
463
|
+
void this.submitTicket(subj, desc, requestId);
|
|
464
|
+
});
|
|
465
|
+
this.body.appendChild(card);
|
|
466
|
+
this.body.scrollTop = this.body.scrollHeight;
|
|
467
|
+
}
|
|
468
|
+
async submitTicket(subject, description, requestId) {
|
|
469
|
+
this.setProgress("Sending your support ticket\u2026");
|
|
470
|
+
try {
|
|
471
|
+
const sessionId = await this.ensureSession();
|
|
472
|
+
const res = await fetch(`${this.config.apiBaseUrl}/api/sessions/${sessionId}/support-ticket`, {
|
|
473
|
+
method: "POST",
|
|
474
|
+
headers: this.headers(),
|
|
475
|
+
body: JSON.stringify({
|
|
476
|
+
tenantUniqueName: this.config.tenantUniqueName,
|
|
477
|
+
environment: this.config.environment,
|
|
478
|
+
requestId,
|
|
479
|
+
subject,
|
|
480
|
+
description
|
|
481
|
+
})
|
|
482
|
+
});
|
|
483
|
+
this.setProgress("");
|
|
484
|
+
if (res.status === 403) {
|
|
485
|
+
this.renderAnswer({ status: "Forbidden", primaryReason: "Support tickets are available for Requestor/Approver roles." });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
if (!res.ok) {
|
|
489
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: `Could not send the ticket (${res.status}).` });
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
const data = await res.json();
|
|
493
|
+
if (data?.status === "Created") {
|
|
494
|
+
const num = data.ticketNumber ? `#${data.ticketNumber}` : "";
|
|
495
|
+
const el = document.createElement("div");
|
|
496
|
+
el.className = "acw-answer acw-okstate";
|
|
497
|
+
el.innerHTML = `<div class="acw-reason">Support ticket ${escapeHtml(num)} has been created. The support team will follow up.</div>`;
|
|
498
|
+
if (data.webUrl || data.id || this.config.onOpenTicket) {
|
|
499
|
+
const open = document.createElement("button");
|
|
500
|
+
open.type = "button";
|
|
501
|
+
open.className = "acw-link-btn";
|
|
502
|
+
open.textContent = "Open the ticket";
|
|
503
|
+
open.addEventListener("click", () => {
|
|
504
|
+
const ticket = { id: data.id, number: data.ticketNumber, webUrl: data.webUrl };
|
|
505
|
+
if (this.config.onOpenTicket) {
|
|
506
|
+
this.config.onOpenTicket(ticket);
|
|
507
|
+
} else if (data.webUrl) {
|
|
508
|
+
window.open(data.webUrl, "_blank", "noopener");
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
el.appendChild(open);
|
|
512
|
+
}
|
|
513
|
+
this.body.appendChild(el);
|
|
514
|
+
this.body.scrollTop = this.body.scrollHeight;
|
|
515
|
+
} else {
|
|
516
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: data?.reason || "Could not create the ticket." });
|
|
517
|
+
}
|
|
518
|
+
} catch {
|
|
519
|
+
this.setProgress("");
|
|
520
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: "Could not reach the copilot service." });
|
|
521
|
+
}
|
|
522
|
+
}
|
|
309
523
|
// ---- networking ----
|
|
310
524
|
headers() {
|
|
311
525
|
const h = { "Content-Type": "application/json" };
|
|
@@ -335,6 +549,7 @@ var CopilotWidget = class {
|
|
|
335
549
|
}
|
|
336
550
|
async runAsk(question) {
|
|
337
551
|
this.open();
|
|
552
|
+
this.lastQuestion = question;
|
|
338
553
|
this.addMessage(question, "user");
|
|
339
554
|
this.setProgress("Reading your question\u2026");
|
|
340
555
|
try {
|
|
@@ -352,7 +567,8 @@ var CopilotWidget = class {
|
|
|
352
567
|
this.renderAnswer(await res.json());
|
|
353
568
|
} catch (err) {
|
|
354
569
|
this.setProgress("");
|
|
355
|
-
|
|
570
|
+
const msg = err?.message ? String(err.message) : "Could not reach the copilot service.";
|
|
571
|
+
this.renderAnswer({ status: "RequestFailed", primaryReason: msg });
|
|
356
572
|
}
|
|
357
573
|
}
|
|
358
574
|
async connectSignalR() {
|
|
@@ -378,6 +594,10 @@ var ApplicationCopilotWidget = {
|
|
|
378
594
|
init: (config) => instance.init(config),
|
|
379
595
|
open: () => instance.open(),
|
|
380
596
|
close: () => instance.close(),
|
|
597
|
+
show: () => instance.show(),
|
|
598
|
+
hide: () => instance.hide(),
|
|
599
|
+
notify: (text, suggestion) => instance.notify(text, suggestion),
|
|
600
|
+
promptUpload: () => instance.promptUpload(),
|
|
381
601
|
ask: (question) => instance.ask(question),
|
|
382
602
|
setContext: (context) => instance.setContext(context),
|
|
383
603
|
captureError: (errorContext) => instance.captureError(errorContext)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spektra-cloudevents/application-copilot-widget",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Spektra Application Copilot widget — framework-agnostic and CSS-library agnostic via Shadow DOM isolation.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|