@guidelytech/guide-runtime 0.5.0 → 0.6.2
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/capture.cjs +1336 -0
- package/dist/capture.cjs.map +1 -0
- package/dist/capture.d.cts +184 -0
- package/dist/capture.d.ts +184 -0
- package/dist/capture.js +857 -0
- package/dist/capture.js.map +1 -0
- package/dist/{chunk-SLBUGXNH.js → chunk-4FKYF6OB.js} +196 -23
- package/dist/chunk-4FKYF6OB.js.map +1 -0
- package/dist/{chunk-SGFC66YD.js → chunk-4UW6YF44.js} +150 -8
- package/dist/chunk-4UW6YF44.js.map +1 -0
- package/dist/chunk-7DOGZKWD.js +322 -0
- package/dist/chunk-7DOGZKWD.js.map +1 -0
- package/dist/chunk-GUSKGNNR.js +301 -0
- package/dist/chunk-GUSKGNNR.js.map +1 -0
- package/dist/index.cjs +192 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +29 -561
- package/dist/index.js.map +1 -1
- package/dist/messenger.cjs +825 -57
- package/dist/messenger.cjs.map +1 -1
- package/dist/messenger.d.cts +61 -3
- package/dist/messenger.d.ts +61 -3
- package/dist/messenger.js +662 -45
- package/dist/messenger.js.map +1 -1
- package/package.json +7 -2
- package/dist/chunk-SGFC66YD.js.map +0 -1
- package/dist/chunk-SLBUGXNH.js.map +0 -1
package/dist/messenger.cjs
CHANGED
|
@@ -20,19 +20,162 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/messenger.ts
|
|
21
21
|
var messenger_exports = {};
|
|
22
22
|
__export(messenger_exports, {
|
|
23
|
+
personalGreeting: () => personalGreeting,
|
|
23
24
|
relativeTime: () => relativeTime,
|
|
24
25
|
renderMessenger: () => renderMessenger
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(messenger_exports);
|
|
28
|
+
var import_runtime4 = require("@guidelytech/shared/runtime");
|
|
27
29
|
|
|
28
30
|
// src/help-view.ts
|
|
29
|
-
var
|
|
31
|
+
var import_runtime3 = require("@guidelytech/shared/runtime");
|
|
30
32
|
|
|
31
|
-
// src/
|
|
33
|
+
// src/pdf-viewer.ts
|
|
32
34
|
var import_runtime = require("@guidelytech/shared/runtime");
|
|
35
|
+
|
|
36
|
+
// src/host.ts
|
|
37
|
+
function prefersReducedMotion() {
|
|
38
|
+
try {
|
|
39
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/pdf-viewer.ts
|
|
46
|
+
function enhancePdfViewers(root, doc = root.ownerDocument) {
|
|
47
|
+
for (const block of Array.from(root.querySelectorAll(`div.${import_runtime.PDF_BLOCK_CLASS}`))) {
|
|
48
|
+
const link2 = block.querySelector(`a.${import_runtime.FILE_LINK_CLASS}`);
|
|
49
|
+
const strip = block.querySelector(`.${import_runtime.PDF_PAGES_CLASS}`);
|
|
50
|
+
if (!link2 || !strip || block.hasAttribute("data-g-ready")) continue;
|
|
51
|
+
for (const img of Array.from(strip.querySelectorAll("img:not([src])"))) img.remove();
|
|
52
|
+
const pages = Array.from(strip.querySelectorAll("img"));
|
|
53
|
+
if (!pages.length) {
|
|
54
|
+
strip.remove();
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
block.setAttribute("data-g-ready", "");
|
|
58
|
+
const name = (link2.textContent || "").trim() || "PDF";
|
|
59
|
+
block.setAttribute("role", "group");
|
|
60
|
+
block.setAttribute("aria-label", `${name}, ${pages.length} ${pages.length === 1 ? "page" : "pages"}`);
|
|
61
|
+
link2.setAttribute("aria-label", `Read ${name} full screen`);
|
|
62
|
+
link2.addEventListener("click", (e) => {
|
|
63
|
+
e.preventDefault();
|
|
64
|
+
openReader(doc, block, link2, pages, 0);
|
|
65
|
+
});
|
|
66
|
+
const viewport = doc.createElement("div");
|
|
67
|
+
viewport.className = "g-pdf-viewport";
|
|
68
|
+
strip.replaceWith(viewport);
|
|
69
|
+
viewport.appendChild(strip);
|
|
70
|
+
strip.tabIndex = 0;
|
|
71
|
+
strip.setAttribute("aria-label", `${name} pages`);
|
|
72
|
+
const counter = doc.createElement("span");
|
|
73
|
+
counter.className = "g-pdf-count";
|
|
74
|
+
counter.setAttribute("aria-live", "polite");
|
|
75
|
+
const prev = navButton(doc, "prev", "Previous page", "\u2039");
|
|
76
|
+
const next = navButton(doc, "next", "Next page", "\u203A");
|
|
77
|
+
viewport.append(prev, next, counter);
|
|
78
|
+
const stride = () => Math.max(1, pages.length > 1 ? pages[1].offsetLeft - pages[0].offsetLeft : strip.clientWidth);
|
|
79
|
+
const current = () => Math.round(strip.scrollLeft / stride());
|
|
80
|
+
const go = (i) => strip.scrollTo({ left: Math.max(0, Math.min(pages.length - 1, i)) * stride(), behavior: prefersReducedMotion() ? "auto" : "smooth" });
|
|
81
|
+
const update = () => {
|
|
82
|
+
const i = Math.min(pages.length - 1, current());
|
|
83
|
+
counter.textContent = `${i + 1} / ${pages.length}`;
|
|
84
|
+
prev.disabled = i === 0;
|
|
85
|
+
next.disabled = i >= pages.length - 1;
|
|
86
|
+
};
|
|
87
|
+
prev.addEventListener("click", () => go(current() - 1));
|
|
88
|
+
next.addEventListener("click", () => go(current() + 1));
|
|
89
|
+
strip.addEventListener("scroll", update, { passive: true });
|
|
90
|
+
strip.addEventListener("keydown", (e) => {
|
|
91
|
+
if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
|
|
92
|
+
e.preventDefault();
|
|
93
|
+
go(current() + (e.key === "ArrowRight" ? 1 : -1));
|
|
94
|
+
} else if (e.key === "Enter") openReader(doc, block, link2, pages, current());
|
|
95
|
+
});
|
|
96
|
+
pages.forEach((img, i) => img.addEventListener("click", () => openReader(doc, block, link2, pages, i)));
|
|
97
|
+
if (pages.length === 1) viewport.classList.add("g-pdf-single");
|
|
98
|
+
update();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
function navButton(doc, cls, label, text) {
|
|
102
|
+
const b = doc.createElement("button");
|
|
103
|
+
b.type = "button";
|
|
104
|
+
b.className = `g-pdf-nav g-pdf-${cls}`;
|
|
105
|
+
b.setAttribute("aria-label", label);
|
|
106
|
+
b.textContent = text;
|
|
107
|
+
return b;
|
|
108
|
+
}
|
|
109
|
+
function openReader(doc, block, link2, pages, start) {
|
|
110
|
+
const host = block.closest(".g-scope") ?? doc.body;
|
|
111
|
+
const returnFocus = block.getRootNode().activeElement;
|
|
112
|
+
const name = (link2.textContent || "").trim() || "PDF";
|
|
113
|
+
const reader = doc.createElement("div");
|
|
114
|
+
reader.className = "g-pdf-reader";
|
|
115
|
+
reader.setAttribute("role", "dialog");
|
|
116
|
+
reader.setAttribute("aria-modal", "true");
|
|
117
|
+
reader.setAttribute("aria-label", name);
|
|
118
|
+
const bar = doc.createElement("div");
|
|
119
|
+
bar.className = "g-pdf-bar";
|
|
120
|
+
const title = doc.createElement("span");
|
|
121
|
+
title.className = "g-pdf-title";
|
|
122
|
+
title.textContent = name;
|
|
123
|
+
const original = doc.createElement("a");
|
|
124
|
+
original.className = "g-btn g-btn-secondary";
|
|
125
|
+
original.href = link2.href;
|
|
126
|
+
original.target = "_blank";
|
|
127
|
+
original.rel = "noopener noreferrer";
|
|
128
|
+
original.textContent = "Open PDF \u2197";
|
|
129
|
+
const close = doc.createElement("button");
|
|
130
|
+
close.type = "button";
|
|
131
|
+
close.className = "g-close g-pdf-close";
|
|
132
|
+
close.setAttribute("aria-label", "Close");
|
|
133
|
+
close.textContent = "\xD7";
|
|
134
|
+
bar.append(title, original, close);
|
|
135
|
+
const scroller = doc.createElement("div");
|
|
136
|
+
scroller.className = "g-pdf-scroll";
|
|
137
|
+
scroller.tabIndex = 0;
|
|
138
|
+
for (const page of pages) {
|
|
139
|
+
const img = doc.createElement("img");
|
|
140
|
+
img.src = page.src;
|
|
141
|
+
img.alt = page.alt;
|
|
142
|
+
img.decoding = "async";
|
|
143
|
+
for (const dim of ["width", "height"]) if (page.hasAttribute(dim)) img.setAttribute(dim, page.getAttribute(dim));
|
|
144
|
+
scroller.appendChild(img);
|
|
145
|
+
}
|
|
146
|
+
reader.append(bar, scroller);
|
|
147
|
+
const onKey = (e) => {
|
|
148
|
+
if (e.key === "Escape") {
|
|
149
|
+
e.stopPropagation();
|
|
150
|
+
done();
|
|
151
|
+
} else if (e.key === "Tab") {
|
|
152
|
+
const items = [original, close, scroller];
|
|
153
|
+
const at = items.indexOf(reader.getRootNode().activeElement);
|
|
154
|
+
e.preventDefault();
|
|
155
|
+
items[(at + (e.shiftKey ? items.length - 1 : 1)) % items.length].focus();
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const done = () => {
|
|
159
|
+
reader.remove();
|
|
160
|
+
returnFocus?.focus?.({ preventScroll: true });
|
|
161
|
+
};
|
|
162
|
+
close.addEventListener("click", done);
|
|
163
|
+
reader.addEventListener("keydown", onKey);
|
|
164
|
+
host.appendChild(reader);
|
|
165
|
+
close.focus();
|
|
166
|
+
const target = scroller.children[start];
|
|
167
|
+
if (target && start > 0) {
|
|
168
|
+
const land = () => scroller.scrollTop = target.offsetTop - 12;
|
|
169
|
+
requestAnimationFrame(land);
|
|
170
|
+
if (!target.hasAttribute("height")) for (const img of Array.from(scroller.children).slice(0, start)) img.addEventListener("load", land, { once: true });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// src/sanitize.ts
|
|
175
|
+
var import_runtime2 = require("@guidelytech/shared/runtime");
|
|
33
176
|
var ALLOWED_TAGS = /* @__PURE__ */ new Set(["p", "br", "strong", "b", "em", "i", "u", "a", "ul", "ol", "li", "img", "h1", "h2", "h3", "h4", "code", "pre", "blockquote", "span", "div", "hr", "button"]);
|
|
34
177
|
var DROP_TAGS = /* @__PURE__ */ new Set(["script", "style", "object", "embed", "form", "input", "textarea", "select", "link", "meta", "base", "template", "svg", "math", "frame", "frameset", "noscript"]);
|
|
35
|
-
var ALLOWED_ATTRS = /* @__PURE__ */ new Set(["href", "src", "alt", "title", "target", "rel", "class", "data-g-action", "data-g-value"]);
|
|
178
|
+
var ALLOWED_ATTRS = /* @__PURE__ */ new Set(["href", "src", "alt", "title", "target", "rel", "class", "width", "height", "data-g-action", "data-g-value"]);
|
|
36
179
|
function isSafeUrl(value, attr, tag) {
|
|
37
180
|
const v = value.trim().replace(/[\u0000-\u001f\s]/g, "").toLowerCase();
|
|
38
181
|
if (!v) return false;
|
|
@@ -59,6 +202,7 @@ function sanitizeElement(el2) {
|
|
|
59
202
|
}
|
|
60
203
|
if (tag === "button") el2.setAttribute("type", "button");
|
|
61
204
|
if (tag === "img") {
|
|
205
|
+
for (const dim of ["width", "height"]) if (el2.hasAttribute(dim) && !/^\d{1,5}$/.test(el2.getAttribute(dim))) el2.removeAttribute(dim);
|
|
62
206
|
el2.setAttribute("loading", "lazy");
|
|
63
207
|
if (!el2.getAttribute("alt")) el2.setAttribute("alt", "");
|
|
64
208
|
}
|
|
@@ -69,18 +213,18 @@ function rebuildMedia(el2) {
|
|
|
69
213
|
const src = el2.getAttribute("src");
|
|
70
214
|
let out;
|
|
71
215
|
if (tag === "iframe") {
|
|
72
|
-
if (!(0,
|
|
216
|
+
if (!(0, import_runtime2.isAllowedEmbedSrc)(src)) return null;
|
|
73
217
|
out = doc.createElement("iframe");
|
|
74
218
|
out.setAttribute("src", src);
|
|
75
219
|
out.setAttribute("title", el2.getAttribute("title") || "Embedded video");
|
|
76
|
-
for (const [k, v] of Object.entries(
|
|
220
|
+
for (const [k, v] of Object.entries(import_runtime2.EMBED_IFRAME_ATTRS)) out.setAttribute(k, v);
|
|
77
221
|
} else {
|
|
78
|
-
if (!(0,
|
|
222
|
+
if (!(0, import_runtime2.isSafeMediaUrl)(src)) return null;
|
|
79
223
|
out = doc.createElement("video");
|
|
80
224
|
out.setAttribute("src", src);
|
|
81
225
|
const poster = el2.getAttribute("poster");
|
|
82
|
-
if ((0,
|
|
83
|
-
for (const [k, v] of Object.entries(
|
|
226
|
+
if ((0, import_runtime2.isSafeMediaUrl)(poster)) out.setAttribute("poster", poster);
|
|
227
|
+
for (const [k, v] of Object.entries(import_runtime2.VIDEO_ATTRS)) out.setAttribute(k, v);
|
|
84
228
|
}
|
|
85
229
|
return out;
|
|
86
230
|
}
|
|
@@ -132,10 +276,10 @@ function el(doc, tag, cls, text) {
|
|
|
132
276
|
if (text != null) e.textContent = text;
|
|
133
277
|
return e;
|
|
134
278
|
}
|
|
135
|
-
function listItem(doc,
|
|
279
|
+
function listItem(doc, icon2, title, summary) {
|
|
136
280
|
const b = el(doc, "button", "g-rc-item");
|
|
137
281
|
b.type = "button";
|
|
138
|
-
const i = el(doc, "span", "g-rc-icon",
|
|
282
|
+
const i = el(doc, "span", "g-rc-icon", icon2);
|
|
139
283
|
i.setAttribute("aria-hidden", "true");
|
|
140
284
|
const text = el(doc, "span");
|
|
141
285
|
text.appendChild(el(doc, "div", "g-rc-title", title));
|
|
@@ -197,8 +341,10 @@ function createHelpView(options) {
|
|
|
197
341
|
const title = el(doc, "h3", "g-title", article.title);
|
|
198
342
|
const content = el(doc, "div", "g-body");
|
|
199
343
|
if (loading) content.appendChild(el(doc, "div", "g-rc-empty", "Loading\u2026"));
|
|
200
|
-
else if (article.body)
|
|
201
|
-
|
|
344
|
+
else if (article.body) {
|
|
345
|
+
setSanitizedHtml(content, article.body);
|
|
346
|
+
enhancePdfViewers(content, doc);
|
|
347
|
+
} else content.appendChild(el(doc, "div", "g-rc-empty", "This article couldn't be loaded."));
|
|
202
348
|
wrap.append(back, title, content);
|
|
203
349
|
body.appendChild(wrap);
|
|
204
350
|
back.focus?.({ preventScroll: true });
|
|
@@ -218,19 +364,29 @@ function createHelpView(options) {
|
|
|
218
364
|
if (active) return renderArticle(active);
|
|
219
365
|
const data = options.data();
|
|
220
366
|
const member = options.audienceIds?.() ?? [];
|
|
221
|
-
const sections = (0,
|
|
367
|
+
const sections = (0, import_runtime3.resourceCenterSections)(data.collections, data.articles, member);
|
|
222
368
|
const q = query.trim().toLowerCase();
|
|
369
|
+
body.append(...options.extra?.(q) ?? []);
|
|
223
370
|
if (q) {
|
|
224
371
|
const visible = sections.flatMap((s) => s.articles);
|
|
225
372
|
const local = visible.filter((a) => a.title.toLowerCase().includes(q) || a.summary.toLowerCase().includes(q));
|
|
226
373
|
const seen = new Set(local.map((a) => a.id));
|
|
227
374
|
const hidden = new Set(data.articles.map((a) => a.id).filter((id) => !visible.some((v) => v.id === id)));
|
|
228
|
-
const merged = [...local, ...serverResults.filter((a) => !seen.has(a.id) && !hidden.has(a.id) && (0,
|
|
375
|
+
const merged = [...local, ...serverResults.filter((a) => !seen.has(a.id) && !hidden.has(a.id) && (0, import_runtime3.isArticleListed)(data.collections, a, member))];
|
|
229
376
|
section(`Results (${merged.length})`);
|
|
230
377
|
if (!merged.length) body.appendChild(el(doc, "div", "g-rc-empty", "No articles match your search."));
|
|
231
378
|
merged.forEach(articleButton);
|
|
232
379
|
return;
|
|
233
380
|
}
|
|
381
|
+
if (data.suggested?.items.length) {
|
|
382
|
+
section(data.suggested.title);
|
|
383
|
+
const glyph = { guide: "\u25B6", howto: "\u2630", article: "\u{1F4C4}" };
|
|
384
|
+
data.suggested.items.forEach((item) => {
|
|
385
|
+
const b = listItem(doc, glyph[item.kind], item.title, item.summary);
|
|
386
|
+
b.addEventListener("click", item.run);
|
|
387
|
+
body.appendChild(b);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
234
390
|
if (data.recommended?.length) {
|
|
235
391
|
section("Recommended");
|
|
236
392
|
data.recommended.forEach((g) => {
|
|
@@ -330,6 +486,98 @@ function createHelpView(options) {
|
|
|
330
486
|
};
|
|
331
487
|
}
|
|
332
488
|
|
|
489
|
+
// src/howto-view.ts
|
|
490
|
+
var pct = (v) => `${(v * 100).toFixed(3)}%`;
|
|
491
|
+
var place = (node, r) => Object.assign(node.style, {
|
|
492
|
+
left: pct(r.x),
|
|
493
|
+
top: pct(r.y),
|
|
494
|
+
width: pct(r.width),
|
|
495
|
+
height: pct(r.height)
|
|
496
|
+
});
|
|
497
|
+
function paragraphs(doc, text, cls) {
|
|
498
|
+
const parts = text.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean);
|
|
499
|
+
if (!parts.length) return null;
|
|
500
|
+
const wrap = el(doc, "div", cls);
|
|
501
|
+
for (const p of parts) wrap.appendChild(el(doc, "p", "", p));
|
|
502
|
+
return wrap;
|
|
503
|
+
}
|
|
504
|
+
function screenshot(doc, image, alt) {
|
|
505
|
+
if (!/^https?:\/\//i.test(image.url)) return null;
|
|
506
|
+
const frame = el(doc, "div", "g-howto-shot");
|
|
507
|
+
const img = (cls) => {
|
|
508
|
+
const i = el(doc, "img", cls);
|
|
509
|
+
i.src = image.url;
|
|
510
|
+
i.alt = alt;
|
|
511
|
+
i.loading = "lazy";
|
|
512
|
+
i.decoding = "async";
|
|
513
|
+
return i;
|
|
514
|
+
};
|
|
515
|
+
const crop = image.crop;
|
|
516
|
+
if (crop && crop.width > 0 && crop.height > 0) {
|
|
517
|
+
const ghost = img("g-howto-ghost");
|
|
518
|
+
ghost.alt = "";
|
|
519
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
520
|
+
const zoomed = img("g-howto-zoomed");
|
|
521
|
+
Object.assign(zoomed.style, {
|
|
522
|
+
width: pct(1 / crop.width),
|
|
523
|
+
left: pct(-crop.x / crop.width),
|
|
524
|
+
top: pct(-crop.y / crop.height)
|
|
525
|
+
});
|
|
526
|
+
frame.append(ghost, zoomed);
|
|
527
|
+
} else frame.appendChild(img(""));
|
|
528
|
+
if (image.highlight) {
|
|
529
|
+
const mark = el(doc, "div", "g-howto-mark");
|
|
530
|
+
mark.setAttribute("aria-hidden", "true");
|
|
531
|
+
place(mark, image.highlight);
|
|
532
|
+
frame.appendChild(mark);
|
|
533
|
+
}
|
|
534
|
+
return frame;
|
|
535
|
+
}
|
|
536
|
+
function renderHowTo(doc, howTo, onShowMe) {
|
|
537
|
+
const wrap = el(doc, "div", "g-howto");
|
|
538
|
+
wrap.appendChild(el(doc, "h3", "g-title", howTo.title));
|
|
539
|
+
const description = paragraphs(doc, howTo.description, "g-howto-desc");
|
|
540
|
+
if (description) wrap.appendChild(description);
|
|
541
|
+
const guideKey = howTo.guideKey;
|
|
542
|
+
if (guideKey && onShowMe) {
|
|
543
|
+
const show = el(doc, "button", "g-btn g-btn-primary g-howto-show", "\u25B6 Show me in the app");
|
|
544
|
+
show.type = "button";
|
|
545
|
+
show.addEventListener("click", () => onShowMe(guideKey));
|
|
546
|
+
wrap.appendChild(show);
|
|
547
|
+
}
|
|
548
|
+
const list = el(doc, "ol", "g-howto-steps");
|
|
549
|
+
let n = 0;
|
|
550
|
+
for (const s of howTo.steps) {
|
|
551
|
+
const li = el(doc, "li", `g-howto-step g-howto-${s.kind}`);
|
|
552
|
+
if (s.kind === "heading") li.appendChild(el(doc, "h4", "", s.title));
|
|
553
|
+
else if (s.kind === "tip" || s.kind === "alert") {
|
|
554
|
+
li.appendChild(el(doc, "strong", "", s.title || (s.kind === "tip" ? "Tip" : "Important")));
|
|
555
|
+
const body = paragraphs(doc, s.body, "");
|
|
556
|
+
if (body) li.appendChild(body);
|
|
557
|
+
} else if (s.kind === "text") {
|
|
558
|
+
if (s.title) li.appendChild(el(doc, "strong", "", s.title));
|
|
559
|
+
const body = paragraphs(doc, s.body, "g-howto-note");
|
|
560
|
+
if (body) li.appendChild(body);
|
|
561
|
+
} else {
|
|
562
|
+
const row = el(doc, "div", "g-howto-row");
|
|
563
|
+
const num = el(doc, "span", "g-howto-num", String(++n));
|
|
564
|
+
const text = el(doc, "div", "g-howto-text");
|
|
565
|
+
text.appendChild(el(doc, "p", "g-howto-title", s.title));
|
|
566
|
+
const body = paragraphs(doc, s.body, "g-howto-note");
|
|
567
|
+
if (body) text.appendChild(body);
|
|
568
|
+
row.append(num, text);
|
|
569
|
+
li.appendChild(row);
|
|
570
|
+
const shot = s.image ? screenshot(doc, s.image, s.title) : null;
|
|
571
|
+
if (shot) li.appendChild(shot);
|
|
572
|
+
}
|
|
573
|
+
list.appendChild(li);
|
|
574
|
+
}
|
|
575
|
+
if (!list.childNodes.length)
|
|
576
|
+
wrap.appendChild(el(doc, "div", "g-rc-empty", "This how-to has no steps yet."));
|
|
577
|
+
else wrap.appendChild(list);
|
|
578
|
+
return wrap;
|
|
579
|
+
}
|
|
580
|
+
|
|
333
581
|
// src/messenger-styles.ts
|
|
334
582
|
var MESSENGER_STYLES = `
|
|
335
583
|
:host([data-g-messenger="bottom-right"]) .g-launcher.g-pos-bottom-right{right:72px}
|
|
@@ -344,17 +592,16 @@ var MESSENGER_STYLES = `
|
|
|
344
592
|
.g-m-head .g-close{color:inherit;top:50%;transform:translateY(-50%)}
|
|
345
593
|
.g-m-head .g-close:hover,.g-m-head .g-close:focus-visible{background:rgba(255,255,255,.18);color:inherit}
|
|
346
594
|
.g-m-head h2{margin:0;font-size:16px;font-weight:600;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
347
|
-
.g-m-back{border:0;background:transparent;color:inherit;font:inherit;
|
|
595
|
+
.g-m-back{border:0;background:transparent;color:inherit;font:inherit;cursor:pointer;width:32px;height:32px;border-radius:999px;margin-left:-8px;display:inline-flex;align-items:center;justify-content:center;padding:0}
|
|
348
596
|
.g-m-back:hover,.g-m-back:focus-visible{background:rgba(255,255,255,.18);outline:none}
|
|
349
597
|
.g-m-main{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
350
598
|
.g-m-scroll{flex:1;overflow:auto;padding:16px}
|
|
351
|
-
.g-m-hero{font-size:22px;font-weight:700;margin:4px 0 4px}
|
|
352
|
-
.g-m-greet{color:var(--g-muted);margin:0 0 16px}
|
|
353
599
|
.g-m-card{display:block;width:100%;text-align:left;border:1px solid var(--g-border);background:var(--g-bg);color:var(--g-text);border-radius:calc(var(--g-radius) * .8);padding:14px 16px;margin:0 0 12px;font:inherit;cursor:pointer;box-shadow:0 1px 2px rgba(0,0,0,.04)}
|
|
354
600
|
.g-m-card:hover{background:var(--g-subtle)}
|
|
355
601
|
.g-m-card:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}
|
|
356
|
-
.g-m-card-title{font-weight:600}
|
|
357
|
-
.g-m-card-sub{color:var(--g-muted);font-size:13px;margin-top:2px}
|
|
602
|
+
.g-m-card-title{display:block;font-weight:600}
|
|
603
|
+
.g-m-card-sub{display:block;color:var(--g-muted);font-size:13px;margin-top:2px}
|
|
604
|
+
.g-m-card-label{display:block;font-size:12px;color:var(--g-muted);font-weight:500}
|
|
358
605
|
.g-m-row{display:flex;align-items:flex-start;gap:10px}
|
|
359
606
|
.g-m-row .g-m-grow{flex:1;min-width:0}
|
|
360
607
|
.g-m-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
@@ -364,7 +611,9 @@ var MESSENGER_STYLES = `
|
|
|
364
611
|
.g-m-tab{flex:1;border:0;background:transparent;color:var(--g-muted);font:inherit;font-size:12px;font-weight:600;padding:10px 4px 12px;cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:2px}
|
|
365
612
|
.g-m-tab[aria-selected="true"]{color:var(--g-primary)}
|
|
366
613
|
.g-m-tab:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px}
|
|
367
|
-
.g-m-tab-icon{
|
|
614
|
+
.g-m-tab-icon{position:relative;display:inline-flex;line-height:1}
|
|
615
|
+
.g-m-tab-icon .g-m-ico{width:22px;height:22px}
|
|
616
|
+
.g-m-tab-dot{position:absolute;top:-2px;right:-4px;width:8px;height:8px;border-radius:999px;background:#e11d48;border:2px solid var(--g-bg);box-sizing:content-box}
|
|
368
617
|
.g-m-log{flex:1;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:10px}
|
|
369
618
|
.g-m-msg{max-width:85%;display:flex;flex-direction:column;gap:4px}
|
|
370
619
|
.g-m-msg.g-from-user{align-self:flex-end;align-items:flex-end}
|
|
@@ -393,7 +642,8 @@ var MESSENGER_STYLES = `
|
|
|
393
642
|
.g-m-compose{display:flex;align-items:flex-end;gap:8px}
|
|
394
643
|
.g-m-compose textarea{flex:1;resize:none;min-height:40px;max-height:120px;border:1px solid var(--g-border);border-radius:10px;padding:9px 10px;font:inherit;font-size:14px;color:var(--g-text);background:var(--g-bg)}
|
|
395
644
|
.g-m-compose textarea:focus-visible{outline:2px solid var(--g-primary);outline-offset:1px}
|
|
396
|
-
.g-m-send{width:40px;height:40px;border-radius:999px;border:0;background:var(--g-primary);color:var(--g-primary-text);cursor:pointer;
|
|
645
|
+
.g-m-send{width:40px;height:40px;border-radius:999px;border:0;background:var(--g-primary);color:var(--g-primary-text);cursor:pointer;flex:none;display:inline-flex;align-items:center;justify-content:center;padding:0}
|
|
646
|
+
.g-m-send .g-m-ico{width:18px;height:18px}
|
|
397
647
|
.g-m-send:disabled{opacity:.45;cursor:not-allowed}
|
|
398
648
|
.g-m-send:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}
|
|
399
649
|
.g-m-human{border:0;background:transparent;color:var(--g-muted);font:inherit;font-size:12px;cursor:pointer;padding:6px 2px 0;text-decoration:underline}
|
|
@@ -403,6 +653,79 @@ var MESSENGER_STYLES = `
|
|
|
403
653
|
.g-m-rate button:hover,.g-m-rate button:focus-visible{background:var(--g-subtle);outline:none}
|
|
404
654
|
.g-m-note{color:var(--g-muted);font-size:12px;margin-top:6px}
|
|
405
655
|
.g-m-empty{color:var(--g-muted);text-align:center;padding:24px 8px}
|
|
656
|
+
.g-howto .g-title{margin:0 0 6px}
|
|
657
|
+
.g-howto-desc,.g-howto-note{color:var(--g-muted);font-size:13px}
|
|
658
|
+
.g-howto p{margin:0 0 6px;white-space:pre-line}
|
|
659
|
+
.g-howto-show{width:100%;margin:6px 0 4px}
|
|
660
|
+
.g-howto-steps{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:16px}
|
|
661
|
+
.g-howto-step h4{margin:4px 0 0;font-size:15px}
|
|
662
|
+
.g-howto-tip,.g-howto-alert{border-left:3px solid var(--g-primary);background:var(--g-subtle);border-radius:6px;padding:8px 10px;font-size:13px}
|
|
663
|
+
.g-howto-alert{border-left-color:#d97706}
|
|
664
|
+
.g-howto-row{display:flex;gap:10px;align-items:flex-start}
|
|
665
|
+
.g-howto-num{flex:none;width:24px;height:24px;border-radius:999px;background:var(--g-primary);color:var(--g-primary-text);font-size:12px;font-weight:700;display:inline-flex;align-items:center;justify-content:center}
|
|
666
|
+
.g-howto-text{flex:1;min-width:0;padding-top:2px}
|
|
667
|
+
.g-howto-title{font-weight:600}
|
|
668
|
+
.g-howto-shot{position:relative;overflow:hidden;margin-top:8px;border:1px solid var(--g-border);border-radius:8px;background:var(--g-subtle)}
|
|
669
|
+
.g-howto-shot img{display:block;width:100%;height:auto}
|
|
670
|
+
.g-howto-shot .g-howto-ghost{visibility:hidden}
|
|
671
|
+
.g-howto-shot .g-howto-zoomed{position:absolute;max-width:none}
|
|
672
|
+
.g-howto-mark{position:absolute;pointer-events:none;border:3px solid var(--g-primary);border-radius:6px;box-shadow:0 0 0 3px color-mix(in srgb,var(--g-primary) 25%,transparent)}
|
|
673
|
+
.g-m-ico{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}
|
|
674
|
+
.g-close .g-m-ico,.g-m-back .g-m-ico{width:18px;height:18px}
|
|
675
|
+
.g-m-row.g-m-center{align-items:center}
|
|
676
|
+
.g-m-row.g-m-baseline{align-items:baseline}
|
|
677
|
+
.g-m-static{cursor:default}
|
|
678
|
+
.g-m-static:hover{background:var(--g-bg)}
|
|
679
|
+
.g-m-wide{width:100%}
|
|
680
|
+
.g-m-home{position:relative;padding:0}
|
|
681
|
+
.g-m-hero-bg{position:absolute;top:0;left:0;right:0;height:300px;pointer-events:none;background:linear-gradient(180deg,var(--g-primary) 0%,var(--g-primary) 52%,color-mix(in srgb,var(--g-primary) 30%,var(--g-bg)) 84%,var(--g-bg) 100%)}
|
|
682
|
+
.g-m-home-inner{position:relative;padding:16px;display:flex;flex-direction:column;gap:12px}
|
|
683
|
+
.g-m-home .g-m-card{margin:0;box-shadow:0 1px 2px rgba(0,0,0,.04),0 4px 14px rgba(17,24,39,.06)}
|
|
684
|
+
.g-m-hero-top{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:40px;padding:2px 4px 0}
|
|
685
|
+
.g-m-logo{width:36px;height:36px;border-radius:9px;background:var(--g-bg);color:var(--g-primary);display:inline-flex;align-items:center;justify-content:center;font-weight:700;font-size:17px;overflow:hidden;flex:none}
|
|
686
|
+
.g-m-logo img{width:100%;height:100%;object-fit:contain}
|
|
687
|
+
.g-m-team{display:flex}
|
|
688
|
+
.g-m-avatar{width:32px;height:32px;border-radius:999px;border:2px solid var(--g-primary);display:inline-flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;overflow:hidden;margin-left:-8px;background:#fde7c7;color:#7a4a0a}
|
|
689
|
+
.g-m-avatar:first-child{margin-left:0}
|
|
690
|
+
.g-m-avatar-1{background:#d9f0e3;color:#1f5e3b}
|
|
691
|
+
.g-m-avatar-2{background:#e3e1fa;color:#3b3486}
|
|
692
|
+
.g-m-avatar img{width:100%;height:100%;object-fit:cover}
|
|
693
|
+
.g-m-hero-close{width:36px;height:36px;border:0;border-radius:999px;background:rgba(255,255,255,.16);color:var(--g-primary-text);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;padding:0;margin-left:10px}
|
|
694
|
+
.g-m-hero-close .g-m-ico{width:18px;height:18px}
|
|
695
|
+
.g-m-hero-close:hover{background:rgba(255,255,255,.28)}
|
|
696
|
+
.g-m-hero-close:focus-visible{outline:2px solid var(--g-primary-text);outline-offset:2px}
|
|
697
|
+
.g-m-hello{margin:36px 4px 6px;color:var(--g-primary-text);font-size:26px;font-weight:700;line-height:1.2;letter-spacing:-.01em;display:flex;flex-direction:column}
|
|
698
|
+
.g-m-hello-1{opacity:.8}
|
|
699
|
+
.g-m-trail{color:var(--g-primary)}
|
|
700
|
+
.g-m-lead{color:var(--g-muted)}
|
|
701
|
+
.g-m-status-ico{width:26px;height:26px}
|
|
702
|
+
.g-m-status-operational .g-m-status-ico{color:#15803d}
|
|
703
|
+
.g-m-status-degraded .g-m-status-ico{color:#b45309}
|
|
704
|
+
.g-m-status-outage .g-m-status-ico{color:#b91c1c}
|
|
705
|
+
.g-m-status-maintenance .g-m-status-ico{color:#1d4ed8}
|
|
706
|
+
.g-m-bar{display:block;height:6px;border-radius:999px;background:var(--g-border);margin-top:10px;overflow:hidden}
|
|
707
|
+
.g-m-bar-fill{display:block;height:100%;background:var(--g-primary);border-radius:999px}
|
|
708
|
+
.g-m-link{color:var(--g-primary);font-weight:600;font-size:13px;flex:none}
|
|
709
|
+
.g-m-badge-ico{width:32px;height:32px;border-radius:8px;background:var(--g-subtle);color:color-mix(in srgb,var(--g-text) 78%,transparent);display:inline-flex;align-items:center;justify-content:center;flex:none}
|
|
710
|
+
.g-m-badge-ico .g-m-ico{width:16px;height:16px}
|
|
711
|
+
.g-m-badge-ico.g-m-accent{background:color-mix(in srgb,var(--g-primary) 12%,var(--g-bg));color:var(--g-primary)}
|
|
712
|
+
.g-m-suggest{padding-bottom:4px}
|
|
713
|
+
.g-m-items{display:flex;flex-direction:column;margin-top:8px}
|
|
714
|
+
.g-m-item{display:flex;align-items:center;gap:12px;width:100%;text-align:left;border:0;border-top:1px solid var(--g-border);background:transparent;color:var(--g-text);padding:10px 0;font:inherit;cursor:pointer;min-height:44px}
|
|
715
|
+
.g-m-item:hover .g-m-item-title{color:var(--g-primary)}
|
|
716
|
+
.g-m-item:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px;border-radius:4px}
|
|
717
|
+
.g-m-item .g-m-grow{flex:1;min-width:0}
|
|
718
|
+
.g-m-item-title{display:block;font-weight:600;overflow-wrap:anywhere}
|
|
719
|
+
.g-m-news{padding:0;overflow:hidden}
|
|
720
|
+
.g-m-news-img{display:block;width:100%;height:150px;object-fit:cover;background:var(--g-subtle)}
|
|
721
|
+
.g-m-news-body{display:block;padding:14px 16px}
|
|
722
|
+
.g-m-news-meta{gap:8px;margin-bottom:4px}
|
|
723
|
+
.g-m-tag{font-size:11px;font-weight:700;color:var(--g-primary-text);background:var(--g-primary);border-radius:4px;padding:1px 6px;text-transform:uppercase;letter-spacing:.03em}
|
|
724
|
+
.g-m-clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
725
|
+
.g-m-context{align-self:center;font-size:12px;color:var(--g-muted);background:var(--g-subtle);border-radius:999px;padding:3px 10px}
|
|
726
|
+
.g-m-asks{align-items:flex-start}
|
|
727
|
+
.g-m-chip.g-m-ask{border-color:var(--g-primary);color:var(--g-primary);font-weight:600;border-radius:999px;padding:8px 14px;min-height:36px}
|
|
728
|
+
.g-m-chip.g-m-ask:disabled{opacity:.5;cursor:not-allowed}
|
|
406
729
|
`;
|
|
407
730
|
|
|
408
731
|
// src/render-safe.ts
|
|
@@ -491,6 +814,73 @@ function chatIcon(doc) {
|
|
|
491
814
|
svg.appendChild(path);
|
|
492
815
|
return svg;
|
|
493
816
|
}
|
|
817
|
+
var ICONS = {
|
|
818
|
+
home: ["M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6H9v6H4a1 1 0 0 1-1-1z"],
|
|
819
|
+
messages: ["M4 5h16v11H8l-4 4z"],
|
|
820
|
+
help: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z", "M9.5 9.5a2.5 2.5 0 1 1 3.5 2.3c-.6.3-1 .8-1 1.5V14", "M12 17h.01"],
|
|
821
|
+
news: ["M3 11v2a1 1 0 0 0 1 1h2l5 4V6L6 10H4a1 1 0 0 0-1 1z", "M15 9a4 4 0 0 1 0 6", "M18 6a8 8 0 0 1 0 12"],
|
|
822
|
+
arrow: ["M5 12h14", "M13 6l6 6-6 6"],
|
|
823
|
+
close: ["M6 6l12 12", "M18 6 6 18"],
|
|
824
|
+
back: ["M15 6l-6 6 6 6"],
|
|
825
|
+
chevron: ["m9 6 6 6-6 6"],
|
|
826
|
+
check: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z", "m8 12 3 3 5-6"],
|
|
827
|
+
alert: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z", "M12 8v4", "M12 16h.01"],
|
|
828
|
+
wrench: ["M14.7 6.3a4 4 0 0 0-5.4 5.4L4 17l3 3 5.3-5.3a4 4 0 0 0 5.4-5.4l-2.5 2.5-2.4-.6-.6-2.4z"],
|
|
829
|
+
play: ["M8 5v14l11-7z"],
|
|
830
|
+
steps: ["M9 6h11", "M9 12h11", "M9 18h11", "M4 6h.01", "M4 12h.01", "M4 18h.01"],
|
|
831
|
+
article: ["M6 3h9l4 4v14H6z", "M9 12h6", "M9 16h6"],
|
|
832
|
+
ask: ["M4 5h16v11H8l-4 4z", "M9 9.5h6", "M9 12.5h4"],
|
|
833
|
+
search: ["M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14z", "m20 20-3.5-3.5"],
|
|
834
|
+
circle: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z"]
|
|
835
|
+
};
|
|
836
|
+
function icon(doc, name, cls = "g-m-ico") {
|
|
837
|
+
const svg = doc.createElementNS(SVG_NS, "svg");
|
|
838
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
839
|
+
svg.setAttribute("aria-hidden", "true");
|
|
840
|
+
svg.setAttribute("class", cls);
|
|
841
|
+
for (const d of ICONS[name] ?? []) {
|
|
842
|
+
const path = doc.createElementNS(SVG_NS, "path");
|
|
843
|
+
path.setAttribute("d", d);
|
|
844
|
+
svg.appendChild(path);
|
|
845
|
+
}
|
|
846
|
+
return svg;
|
|
847
|
+
}
|
|
848
|
+
var REPLY_TIME_TEXT = {
|
|
849
|
+
minutes: "typically replies in a few minutes",
|
|
850
|
+
hour: "typically replies within an hour",
|
|
851
|
+
hours: "typically replies in a few hours",
|
|
852
|
+
day: "typically replies within a day"
|
|
853
|
+
};
|
|
854
|
+
var STATUS_TEXT = {
|
|
855
|
+
operational: { title: "All systems operational", icon: "check" },
|
|
856
|
+
degraded: { title: "Degraded performance", icon: "alert" },
|
|
857
|
+
outage: { title: "Service outage", icon: "alert" },
|
|
858
|
+
maintenance: { title: "Scheduled maintenance", icon: "wrench" }
|
|
859
|
+
};
|
|
860
|
+
function shortDateTime(iso) {
|
|
861
|
+
const d = new Date(iso);
|
|
862
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
863
|
+
try {
|
|
864
|
+
return d.toLocaleString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
865
|
+
} catch {
|
|
866
|
+
return d.toISOString().slice(0, 16).replace("T", " ");
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
function shortDate(iso) {
|
|
870
|
+
const d = new Date(iso);
|
|
871
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
872
|
+
try {
|
|
873
|
+
return d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
874
|
+
} catch {
|
|
875
|
+
return d.toISOString().slice(0, 10);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
var initialOf = (name) => (name.trim().match(/\p{L}/u)?.[0] ?? "?").toUpperCase();
|
|
879
|
+
function personalGreeting(greeting, firstName) {
|
|
880
|
+
if (!firstName) return greeting;
|
|
881
|
+
const rest = greeting.replace(/^\s*(hi|hello|hey)( there)?\s*[!,.]?\s*/i, "");
|
|
882
|
+
return rest ? `Hi ${firstName}! ${rest}` : `Hi ${firstName}!`;
|
|
883
|
+
}
|
|
494
884
|
function relativeTime(iso, now = Date.now()) {
|
|
495
885
|
const s = Math.max(0, Math.round((now - Date.parse(iso)) / 1e3));
|
|
496
886
|
if (!Number.isFinite(s) || s < 60) return "now";
|
|
@@ -526,6 +916,25 @@ function renderMessenger(options) {
|
|
|
526
916
|
let threadTimer = null;
|
|
527
917
|
let backgroundTimer = null;
|
|
528
918
|
let threadSeq = 0;
|
|
919
|
+
let howTos = null;
|
|
920
|
+
let howTosSeq = 0;
|
|
921
|
+
const howToCache = /* @__PURE__ */ new Map();
|
|
922
|
+
let page = null;
|
|
923
|
+
let pageKey = null;
|
|
924
|
+
let pageAt = 0;
|
|
925
|
+
let pageSeq = 0;
|
|
926
|
+
let pageLoading = false;
|
|
927
|
+
const PAGE_TTL_MS = 12e4;
|
|
928
|
+
const cards = { suggestions: config.home?.suggestions !== false, progress: config.home?.progress !== false, news: config.home?.news !== false };
|
|
929
|
+
const visitor = () => {
|
|
930
|
+
try {
|
|
931
|
+
const v = options.visitor?.();
|
|
932
|
+
if (v) return v;
|
|
933
|
+
} catch {
|
|
934
|
+
}
|
|
935
|
+
return { firstName: null, path: null, pageGuides: [], resumeGuide: null, checklist: null, news: [] };
|
|
936
|
+
};
|
|
937
|
+
const news = () => cards.news ? visitor().news : [];
|
|
529
938
|
const pos = `g-pos-${config.launcher.position}`;
|
|
530
939
|
const rootNode = scope.getRootNode();
|
|
531
940
|
const shadowHost = "host" in rootNode ? rootNode.host : null;
|
|
@@ -557,6 +966,7 @@ function renderMessenger(options) {
|
|
|
557
966
|
data: () => ({
|
|
558
967
|
collections: articles?.collections ?? options.resourceCenter?.collections ?? [],
|
|
559
968
|
articles: articles?.articles ?? options.resourceCenter?.articles ?? [],
|
|
969
|
+
suggested: helpSuggestions(),
|
|
560
970
|
recommended: rcExtras(options.resourceCenter?.recommendedGuideIds),
|
|
561
971
|
checklists: rcExtras(options.resourceCenter?.checklistGuideIds),
|
|
562
972
|
faqs: options.resourceCenter?.faqs,
|
|
@@ -573,7 +983,8 @@ function renderMessenger(options) {
|
|
|
573
983
|
setOpen(false);
|
|
574
984
|
options.onOpenChecklist?.(id);
|
|
575
985
|
},
|
|
576
|
-
onArticleView: options.onArticleView
|
|
986
|
+
onArticleView: options.onArticleView,
|
|
987
|
+
extra: (q) => howToItems(q)
|
|
577
988
|
});
|
|
578
989
|
const unreadTotal = () => conversations.reduce((n, c) => n + (c.unread > 0 ? 1 : 0), 0);
|
|
579
990
|
const updateBadge = () => {
|
|
@@ -603,6 +1014,32 @@ function renderMessenger(options) {
|
|
|
603
1014
|
}).catch(() => {
|
|
604
1015
|
}).finally(() => articlesLoading = false);
|
|
605
1016
|
};
|
|
1017
|
+
const loadHowTos = () => {
|
|
1018
|
+
if (!api.listHowTos) return;
|
|
1019
|
+
const seq = ++howTosSeq;
|
|
1020
|
+
api.listHowTos().then((res) => {
|
|
1021
|
+
if (seq !== howTosSeq || destroyed) return;
|
|
1022
|
+
howTos = res;
|
|
1023
|
+
if (open && view.kind === "tab" && view.tab === "home") render();
|
|
1024
|
+
else if (open && view.kind === "tab" && view.tab === "help") help.render();
|
|
1025
|
+
}).catch(() => {
|
|
1026
|
+
});
|
|
1027
|
+
};
|
|
1028
|
+
const pageHowTos = () => (howTos?.forPage ?? []).map((id) => howTos?.howTos.find((h) => h.id === id)).filter((h) => !!h);
|
|
1029
|
+
const stepsLabel = (h) => `${h.stepCount} ${h.stepCount === 1 ? "step" : "steps"}`;
|
|
1030
|
+
const openHowTo = (id) => go({ kind: "howto", id, from: view });
|
|
1031
|
+
function howToItems(q) {
|
|
1032
|
+
const list = q ? (howTos?.howTos ?? []).filter((h) => h.title.toLowerCase().includes(q) || h.description.toLowerCase().includes(q)).slice(0, 10) : [];
|
|
1033
|
+
if (!list.length) return [];
|
|
1034
|
+
return [
|
|
1035
|
+
el(doc, "div", "g-rc-section", `How-tos (${list.length})`),
|
|
1036
|
+
...list.map((h) => {
|
|
1037
|
+
const b = listItem(doc, "\u{1F4CB}", h.title, h.description || stepsLabel(h));
|
|
1038
|
+
b.addEventListener("click", () => openHowTo(h.id));
|
|
1039
|
+
return b;
|
|
1040
|
+
})
|
|
1041
|
+
];
|
|
1042
|
+
}
|
|
606
1043
|
const refreshConversations = () => api.listConversations().then((list) => {
|
|
607
1044
|
conversations = [...list].sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt));
|
|
608
1045
|
updateBadge();
|
|
@@ -611,92 +1048,379 @@ function renderMessenger(options) {
|
|
|
611
1048
|
});
|
|
612
1049
|
function renderHead(title, back) {
|
|
613
1050
|
head.textContent = "";
|
|
1051
|
+
head.hidden = false;
|
|
614
1052
|
if (back) {
|
|
615
|
-
const b = el(doc, "button", "g-m-back"
|
|
1053
|
+
const b = el(doc, "button", "g-m-back");
|
|
616
1054
|
b.type = "button";
|
|
617
1055
|
b.setAttribute("aria-label", "Back");
|
|
1056
|
+
b.appendChild(icon(doc, "back"));
|
|
618
1057
|
b.addEventListener("click", back);
|
|
619
1058
|
head.appendChild(b);
|
|
620
1059
|
}
|
|
621
1060
|
head.appendChild(el(doc, "h2", "", title));
|
|
622
|
-
|
|
1061
|
+
head.appendChild(closeButton("g-close"));
|
|
1062
|
+
}
|
|
1063
|
+
function closeButton(cls) {
|
|
1064
|
+
const close = el(doc, "button", cls);
|
|
623
1065
|
close.type = "button";
|
|
624
1066
|
close.setAttribute("aria-label", "Close");
|
|
1067
|
+
close.appendChild(icon(doc, "close"));
|
|
625
1068
|
close.addEventListener("click", () => setOpen(false));
|
|
626
|
-
|
|
1069
|
+
return close;
|
|
627
1070
|
}
|
|
628
1071
|
function renderTabs() {
|
|
629
1072
|
tabs.textContent = "";
|
|
630
|
-
tabs.hidden = view.kind
|
|
1073
|
+
tabs.hidden = view.kind !== "tab";
|
|
631
1074
|
const items = [
|
|
632
|
-
["home", "
|
|
633
|
-
["messages", "
|
|
634
|
-
["help", "
|
|
1075
|
+
["home", "home", "Home"],
|
|
1076
|
+
["messages", "messages", "Messages"],
|
|
1077
|
+
["help", "help", "Help"]
|
|
635
1078
|
];
|
|
636
|
-
|
|
1079
|
+
const latest = news();
|
|
1080
|
+
if (latest.length) items.push(["news", "news", "News"]);
|
|
1081
|
+
for (const [tab, ico, label] of items) {
|
|
637
1082
|
const b = el(doc, "button", "g-m-tab");
|
|
638
1083
|
b.type = "button";
|
|
639
1084
|
b.setAttribute("role", "tab");
|
|
640
1085
|
b.setAttribute("aria-selected", String(view.kind === "tab" && view.tab === tab));
|
|
641
|
-
const i = el(doc, "span", "g-m-tab-icon"
|
|
642
|
-
i.
|
|
1086
|
+
const i = el(doc, "span", "g-m-tab-icon");
|
|
1087
|
+
i.appendChild(icon(doc, ico));
|
|
643
1088
|
b.append(i, el(doc, "span", "", label));
|
|
1089
|
+
if (tab === "news" && latest.some((n) => n.unread)) {
|
|
1090
|
+
const dot = el(doc, "span", "g-m-tab-dot");
|
|
1091
|
+
dot.setAttribute("aria-label", "New");
|
|
1092
|
+
i.appendChild(dot);
|
|
1093
|
+
}
|
|
644
1094
|
b.addEventListener("click", () => go({ kind: "tab", tab }));
|
|
645
1095
|
tabs.appendChild(b);
|
|
646
1096
|
}
|
|
647
1097
|
}
|
|
648
|
-
function conversationCard(c) {
|
|
1098
|
+
function conversationCard(c, label) {
|
|
649
1099
|
const card = el(doc, "button", "g-m-card");
|
|
650
1100
|
card.type = "button";
|
|
651
1101
|
const row = el(doc, "div", "g-m-row");
|
|
652
1102
|
if (c.unread > 0) row.appendChild(el(doc, "span", "g-m-dot"));
|
|
653
1103
|
const text = el(doc, "div", "g-m-grow");
|
|
1104
|
+
if (label) text.appendChild(el(doc, "div", "g-m-card-label", label));
|
|
654
1105
|
text.append(el(doc, "div", "g-m-card-title g-m-ellipsis", c.subject || "Conversation"), el(doc, "div", "g-m-card-sub g-m-ellipsis", c.lastMessagePreview));
|
|
655
1106
|
row.append(text, el(doc, "span", "g-m-time", relativeTime(c.lastMessageAt)));
|
|
656
1107
|
card.appendChild(row);
|
|
657
|
-
card.setAttribute("aria-label", `${c.subject || "Conversation"}${c.unread ? ", unread" : ""}`);
|
|
1108
|
+
card.setAttribute("aria-label", `${label ? `${label}: ` : ""}${c.subject || "Conversation"}${c.unread ? ", unread" : ""}`);
|
|
658
1109
|
card.addEventListener("click", () => go({ kind: "thread", conversationId: c.id }));
|
|
659
1110
|
return card;
|
|
660
1111
|
}
|
|
1112
|
+
function actionCard(opts) {
|
|
1113
|
+
const card = el(doc, "button", `g-m-card g-m-row g-m-center${opts.cls ? ` ${opts.cls}` : ""}`);
|
|
1114
|
+
card.type = "button";
|
|
1115
|
+
if (opts.lead) card.appendChild(opts.lead);
|
|
1116
|
+
const text = el(doc, "span", "g-m-grow");
|
|
1117
|
+
text.appendChild(el(doc, "span", "g-m-card-title", opts.title));
|
|
1118
|
+
if (opts.sub) text.appendChild(el(doc, "span", "g-m-card-sub", opts.sub));
|
|
1119
|
+
card.appendChild(text);
|
|
1120
|
+
if (opts.trail) card.appendChild(icon(doc, "arrow", "g-m-ico g-m-trail"));
|
|
1121
|
+
card.addEventListener("click", opts.onClick);
|
|
1122
|
+
return card;
|
|
1123
|
+
}
|
|
1124
|
+
function progressBar(done, total) {
|
|
1125
|
+
const bar = el(doc, "span", "g-m-bar");
|
|
1126
|
+
bar.setAttribute("role", "progressbar");
|
|
1127
|
+
bar.setAttribute("aria-valuemin", "0");
|
|
1128
|
+
bar.setAttribute("aria-valuemax", String(total));
|
|
1129
|
+
bar.setAttribute("aria-valuenow", String(done));
|
|
1130
|
+
const fill = el(doc, "span", "g-m-bar-fill");
|
|
1131
|
+
fill.style.width = `${total ? Math.round(Math.min(done, total) / total * 100) : 0}%`;
|
|
1132
|
+
bar.appendChild(fill);
|
|
1133
|
+
return bar;
|
|
1134
|
+
}
|
|
1135
|
+
function teamAvatars() {
|
|
1136
|
+
const team = (config.team ?? []).slice(0, 3);
|
|
1137
|
+
if (!team.length) return null;
|
|
1138
|
+
const wrap = el(doc, "div", "g-m-team");
|
|
1139
|
+
wrap.setAttribute("role", "img");
|
|
1140
|
+
wrap.setAttribute("aria-label", `The team: ${team.map((t) => t.name).join(", ")}`);
|
|
1141
|
+
team.forEach((t, i) => {
|
|
1142
|
+
const a = el(doc, "span", `g-m-avatar g-m-avatar-${i % 3}`);
|
|
1143
|
+
if (t.avatarUrl) {
|
|
1144
|
+
const img = el(doc, "img");
|
|
1145
|
+
img.src = t.avatarUrl;
|
|
1146
|
+
img.alt = "";
|
|
1147
|
+
img.referrerPolicy = "no-referrer";
|
|
1148
|
+
img.addEventListener("error", () => {
|
|
1149
|
+
img.remove();
|
|
1150
|
+
a.textContent = initialOf(t.name);
|
|
1151
|
+
});
|
|
1152
|
+
a.appendChild(img);
|
|
1153
|
+
} else a.textContent = initialOf(t.name);
|
|
1154
|
+
wrap.appendChild(a);
|
|
1155
|
+
});
|
|
1156
|
+
return wrap;
|
|
1157
|
+
}
|
|
1158
|
+
function brandMark() {
|
|
1159
|
+
const name = options.brand?.name || config.name;
|
|
1160
|
+
const mark = el(doc, "span", "g-m-logo");
|
|
1161
|
+
const url = options.brand?.logoUrl;
|
|
1162
|
+
if (url && /^(https?:)?\/\//.test(url)) {
|
|
1163
|
+
const img = el(doc, "img");
|
|
1164
|
+
img.src = url;
|
|
1165
|
+
img.alt = name;
|
|
1166
|
+
img.addEventListener("error", () => {
|
|
1167
|
+
img.remove();
|
|
1168
|
+
mark.textContent = initialOf(name);
|
|
1169
|
+
});
|
|
1170
|
+
mark.appendChild(img);
|
|
1171
|
+
} else {
|
|
1172
|
+
mark.textContent = initialOf(name);
|
|
1173
|
+
mark.setAttribute("aria-label", name);
|
|
1174
|
+
mark.setAttribute("role", "img");
|
|
1175
|
+
}
|
|
1176
|
+
return mark;
|
|
1177
|
+
}
|
|
1178
|
+
function messageSub() {
|
|
1179
|
+
const reply = config.replyTime ? REPLY_TIME_TEXT[config.replyTime] : null;
|
|
1180
|
+
if (config.aiAvailable) return reply ? `${config.name} answers right away. The team ${reply}.` : `${config.name} answers right away, and the team can help too.`;
|
|
1181
|
+
return reply ? `The team ${reply}.` : "The team will reply here.";
|
|
1182
|
+
}
|
|
1183
|
+
function statusCard(status) {
|
|
1184
|
+
const meta = STATUS_TEXT[status.level] ?? STATUS_TEXT.operational;
|
|
1185
|
+
const card = el(doc, "div", `g-m-card g-m-static g-m-row g-m-center g-m-status g-m-status-${status.level}`);
|
|
1186
|
+
card.setAttribute("role", "status");
|
|
1187
|
+
card.appendChild(icon(doc, meta.icon, "g-m-ico g-m-status-ico"));
|
|
1188
|
+
const text = el(doc, "span", "g-m-grow");
|
|
1189
|
+
text.appendChild(el(doc, "span", "g-m-card-title", status.message || meta.title));
|
|
1190
|
+
const when = shortDateTime(status.updatedAt);
|
|
1191
|
+
if (when) text.appendChild(el(doc, "span", "g-m-card-sub", `Updated ${when}`));
|
|
1192
|
+
card.appendChild(text);
|
|
1193
|
+
return card;
|
|
1194
|
+
}
|
|
1195
|
+
function progressCards(v) {
|
|
1196
|
+
const out = [];
|
|
1197
|
+
if (v.resumeGuide) {
|
|
1198
|
+
const g = v.resumeGuide;
|
|
1199
|
+
const card = el(doc, "button", "g-m-card");
|
|
1200
|
+
card.type = "button";
|
|
1201
|
+
const row = el(doc, "span", "g-m-row g-m-center");
|
|
1202
|
+
row.appendChild(iconTile("play", true));
|
|
1203
|
+
const text = el(doc, "span", "g-m-grow");
|
|
1204
|
+
text.append(el(doc, "span", "g-m-card-label", "Pick up where you left off"), el(doc, "span", "g-m-card-title g-m-ellipsis", g.name), el(doc, "span", "g-m-card-sub", `Step ${g.stepIndex + 1} of ${g.stepCount}`));
|
|
1205
|
+
row.append(text, el(doc, "span", "g-m-link", "Resume"));
|
|
1206
|
+
card.append(row, progressBar(g.stepIndex, g.stepCount));
|
|
1207
|
+
card.addEventListener("click", () => {
|
|
1208
|
+
setOpen(false);
|
|
1209
|
+
options.onResumeGuide?.(g.id, g.stepId);
|
|
1210
|
+
});
|
|
1211
|
+
out.push(card);
|
|
1212
|
+
}
|
|
1213
|
+
if (v.checklist && v.checklist.total > 0) {
|
|
1214
|
+
const c = v.checklist;
|
|
1215
|
+
const card = el(doc, "button", "g-m-card");
|
|
1216
|
+
card.type = "button";
|
|
1217
|
+
const top = el(doc, "span", "g-m-row g-m-baseline");
|
|
1218
|
+
top.append(el(doc, "span", "g-m-card-title g-m-grow g-m-ellipsis", c.name), el(doc, "span", "g-m-time", `${c.done} of ${c.total} done`));
|
|
1219
|
+
card.append(top, progressBar(c.done, c.total));
|
|
1220
|
+
if (c.nextTask) card.appendChild(el(doc, "span", "g-m-card-sub", `Next: ${c.nextTask}`));
|
|
1221
|
+
card.addEventListener("click", () => {
|
|
1222
|
+
setOpen(false);
|
|
1223
|
+
options.onOpenChecklist?.(c.id);
|
|
1224
|
+
});
|
|
1225
|
+
out.push(card);
|
|
1226
|
+
}
|
|
1227
|
+
return out;
|
|
1228
|
+
}
|
|
1229
|
+
function iconTile(name, accent = false) {
|
|
1230
|
+
const b = el(doc, "span", `g-m-badge-ico${accent ? " g-m-accent" : ""}`);
|
|
1231
|
+
b.appendChild(icon(doc, name));
|
|
1232
|
+
return b;
|
|
1233
|
+
}
|
|
1234
|
+
function pageSuggestions(v, max = 4) {
|
|
1235
|
+
if (!cards.suggestions) return [];
|
|
1236
|
+
const out = [];
|
|
1237
|
+
for (const g of v.pageGuides.slice(0, 2)) {
|
|
1238
|
+
out.push({ icon: "play", title: g.name, sub: g.kind === "checklist" ? "Checklist" : "Interactive guide", accent: true, run: () => g.kind === "checklist" ? openChecklist(g.id) : startGuide(g.key || g.id) });
|
|
1239
|
+
}
|
|
1240
|
+
const inApp = /* @__PURE__ */ new Set();
|
|
1241
|
+
for (const h of pageHowTos()) {
|
|
1242
|
+
if (out.length >= max) break;
|
|
1243
|
+
inApp.add(h.title.toLowerCase());
|
|
1244
|
+
out.push({ icon: "steps", title: h.title, sub: `How-to \xB7 ${stepsLabel(h)}`, run: () => openHowTo(h.id) });
|
|
1245
|
+
}
|
|
1246
|
+
for (const s of page?.suggestions ?? []) {
|
|
1247
|
+
if (out.length >= max) break;
|
|
1248
|
+
if (s.type === "howto" && inApp.has(s.title.toLowerCase())) continue;
|
|
1249
|
+
if (s.type === "howto") out.push({ icon: "steps", title: s.title, sub: `How-to \xB7 ${s.steps} ${s.steps === 1 ? "step" : "steps"}`, run: () => openArticle(s.articleId) });
|
|
1250
|
+
else out.push({ icon: "article", title: s.title, sub: s.summary || "Article", run: () => openArticle(s.articleId) });
|
|
1251
|
+
}
|
|
1252
|
+
return out.slice(0, max);
|
|
1253
|
+
}
|
|
1254
|
+
function startGuide(keyOrId) {
|
|
1255
|
+
setOpen(false);
|
|
1256
|
+
options.onStartGuide?.(keyOrId);
|
|
1257
|
+
}
|
|
1258
|
+
function openChecklist(id) {
|
|
1259
|
+
setOpen(false);
|
|
1260
|
+
options.onOpenChecklist?.(id);
|
|
1261
|
+
}
|
|
1262
|
+
function ask(question) {
|
|
1263
|
+
go({ kind: "thread", conversationId: null });
|
|
1264
|
+
if (!textarea) return;
|
|
1265
|
+
textarea.value = question;
|
|
1266
|
+
submit();
|
|
1267
|
+
}
|
|
1268
|
+
function suggestionButton(s) {
|
|
1269
|
+
const b = el(doc, "button", "g-m-item");
|
|
1270
|
+
b.type = "button";
|
|
1271
|
+
b.appendChild(iconTile(s.icon, s.accent));
|
|
1272
|
+
const text = el(doc, "span", "g-m-grow");
|
|
1273
|
+
text.append(el(doc, "span", "g-m-item-title", s.title), el(doc, "span", "g-m-card-sub g-m-ellipsis", s.sub));
|
|
1274
|
+
b.appendChild(text);
|
|
1275
|
+
b.addEventListener("click", s.run);
|
|
1276
|
+
return b;
|
|
1277
|
+
}
|
|
1278
|
+
function newsCard(n) {
|
|
1279
|
+
const card = el(doc, "button", "g-m-card g-m-news");
|
|
1280
|
+
card.type = "button";
|
|
1281
|
+
if (n.imageUrl && /^https?:\/\//.test(n.imageUrl)) {
|
|
1282
|
+
const img = el(doc, "img", "g-m-news-img");
|
|
1283
|
+
img.src = n.imageUrl;
|
|
1284
|
+
img.alt = "";
|
|
1285
|
+
img.loading = "lazy";
|
|
1286
|
+
img.addEventListener("error", () => img.remove());
|
|
1287
|
+
card.appendChild(img);
|
|
1288
|
+
}
|
|
1289
|
+
const body = el(doc, "span", "g-m-news-body");
|
|
1290
|
+
const meta = el(doc, "span", "g-m-row g-m-center g-m-news-meta");
|
|
1291
|
+
if (n.unread) meta.appendChild(el(doc, "span", "g-m-tag", "New"));
|
|
1292
|
+
meta.appendChild(el(doc, "span", "g-m-time", shortDate(n.publishedAt)));
|
|
1293
|
+
body.append(meta, el(doc, "span", "g-m-card-title", n.title));
|
|
1294
|
+
if (n.body) body.appendChild(el(doc, "span", "g-m-card-sub g-m-clamp", n.body));
|
|
1295
|
+
card.appendChild(body);
|
|
1296
|
+
card.addEventListener("click", () => startGuide(n.key || n.id));
|
|
1297
|
+
return card;
|
|
1298
|
+
}
|
|
661
1299
|
function renderHome() {
|
|
662
|
-
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
const
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
1300
|
+
head.hidden = true;
|
|
1301
|
+
const v = visitor();
|
|
1302
|
+
loadPage();
|
|
1303
|
+
const scroll = el(doc, "div", "g-m-scroll g-m-home");
|
|
1304
|
+
const hero = el(doc, "div", "g-m-hero-bg");
|
|
1305
|
+
hero.setAttribute("aria-hidden", "true");
|
|
1306
|
+
const inner = el(doc, "div", "g-m-home-inner");
|
|
1307
|
+
const top = el(doc, "div", "g-m-hero-top");
|
|
1308
|
+
const right = el(doc, "div", "g-m-row g-m-center");
|
|
1309
|
+
const team = teamAvatars();
|
|
1310
|
+
if (team) right.appendChild(team);
|
|
1311
|
+
right.appendChild(closeButton("g-m-hero-close"));
|
|
1312
|
+
top.append(brandMark(), right);
|
|
1313
|
+
const hello = el(doc, "h2", "g-m-hello");
|
|
1314
|
+
hello.append(el(doc, "span", "g-m-hello-1", v.firstName ? `Hi ${v.firstName}.` : "Hi there."), el(doc, "span", "g-m-hello-2", "How can we help?"));
|
|
1315
|
+
inner.append(top, hello);
|
|
1316
|
+
inner.appendChild(actionCard({ title: "Send us a message", sub: messageSub(), trail: true, onClick: () => go({ kind: "thread", conversationId: null }) }));
|
|
670
1317
|
const recent = conversations[0];
|
|
671
|
-
if (recent)
|
|
672
|
-
|
|
673
|
-
|
|
1318
|
+
if (recent) inner.appendChild(conversationCard(recent, recent.status === "closed" && !recent.unread ? "Recent conversation" : "Continue the conversation"));
|
|
1319
|
+
if (config.status) inner.appendChild(statusCard(config.status));
|
|
1320
|
+
if (cards.progress) progressCards(v).forEach((c) => inner.appendChild(c));
|
|
1321
|
+
const suggestions = pageSuggestions(v);
|
|
1322
|
+
const questions = cards.suggestions ? (page?.questions ?? []).slice(0, 2) : [];
|
|
1323
|
+
const label = v.path ? (0, import_runtime4.pageLabel)(v.path) : null;
|
|
1324
|
+
const fallback = !suggestions.length && !questions.length ? rcExtras(options.resourceCenter?.recommendedGuideIds).slice(0, 3) : [];
|
|
1325
|
+
if (suggestions.length || questions.length || fallback.length) {
|
|
1326
|
+
const box = el(doc, "div", "g-m-card g-m-static g-m-suggest");
|
|
1327
|
+
box.appendChild(el(doc, "div", "g-m-card-title", fallback.length ? "Recommended" : "Suggested for you"));
|
|
1328
|
+
if (!fallback.length && label) box.appendChild(el(doc, "div", "g-m-card-sub", `For the ${label} page`));
|
|
1329
|
+
const list = el(doc, "div", "g-m-items");
|
|
1330
|
+
suggestions.forEach((s) => list.appendChild(suggestionButton(s)));
|
|
1331
|
+
questions.forEach((q) => list.appendChild(suggestionButton({ icon: "ask", title: q, sub: config.aiAvailable ? `Ask ${config.name}` : "Ask the team", run: () => ask(q) })));
|
|
1332
|
+
fallback.forEach((g) => list.appendChild(suggestionButton({ icon: "play", title: g.name, sub: "Interactive guide", accent: true, run: () => startGuide(g.key || g.id) })));
|
|
1333
|
+
box.appendChild(list);
|
|
1334
|
+
inner.appendChild(box);
|
|
674
1335
|
}
|
|
675
|
-
const
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
1336
|
+
const latest = news()[0];
|
|
1337
|
+
if (latest) inner.appendChild(newsCard(latest));
|
|
1338
|
+
inner.appendChild(actionCard({ title: "Search for help", sub: "Articles, guides and how-tos", lead: icon(doc, "search", "g-m-ico g-m-lead"), onClick: () => go({ kind: "tab", tab: "help" }) }));
|
|
1339
|
+
scroll.append(hero, inner);
|
|
1340
|
+
main.appendChild(scroll);
|
|
1341
|
+
}
|
|
1342
|
+
function renderHowToView(id, from) {
|
|
1343
|
+
const cached = howToCache.get(id);
|
|
1344
|
+
renderHead(cached?.title ?? "How-to", () => go(from));
|
|
1345
|
+
const scroll = el(doc, "div", "g-m-scroll");
|
|
1346
|
+
if (cached) {
|
|
1347
|
+
scroll.appendChild(
|
|
1348
|
+
renderHowTo(doc, cached, (key) => {
|
|
1349
|
+
setOpen(false);
|
|
1350
|
+
options.onStartGuide?.(key);
|
|
1351
|
+
})
|
|
1352
|
+
);
|
|
1353
|
+
} else scroll.appendChild(el(doc, "div", "g-m-empty", cached === null ? "This how-to isn't available any more." : "Loading\u2026"));
|
|
680
1354
|
main.appendChild(scroll);
|
|
1355
|
+
if (cached !== void 0 || !api.getHowTo) return;
|
|
1356
|
+
const seq = threadSeq;
|
|
1357
|
+
api.getHowTo(id).catch(() => null).then((h) => {
|
|
1358
|
+
howToCache.set(id, h);
|
|
1359
|
+
if (seq === threadSeq && view.kind === "howto" && view.id === id) render();
|
|
1360
|
+
});
|
|
681
1361
|
}
|
|
682
1362
|
function renderMessagesTab() {
|
|
683
1363
|
renderHead("Messages");
|
|
684
1364
|
const scroll = el(doc, "div", "g-m-scroll");
|
|
685
1365
|
if (!conversations.length) scroll.appendChild(el(doc, "div", "g-m-empty", "No conversations yet."));
|
|
686
1366
|
conversations.forEach((c) => scroll.appendChild(conversationCard(c)));
|
|
687
|
-
const start = el(doc, "button", "g-btn g-btn-primary", "Send us a message");
|
|
1367
|
+
const start = el(doc, "button", "g-btn g-btn-primary g-m-wide", "Send us a message");
|
|
688
1368
|
start.type = "button";
|
|
689
|
-
start.style.width = "100%";
|
|
690
1369
|
start.addEventListener("click", () => go({ kind: "thread", conversationId: null }));
|
|
691
1370
|
scroll.appendChild(start);
|
|
692
1371
|
main.appendChild(scroll);
|
|
693
1372
|
}
|
|
1373
|
+
function helpSuggestions() {
|
|
1374
|
+
const v = visitor();
|
|
1375
|
+
const items = pageSuggestions(v, 5).map((s) => ({ kind: s.icon === "play" ? "guide" : s.icon === "steps" ? "howto" : "article", title: s.title, summary: s.sub, run: s.run }));
|
|
1376
|
+
if (!items.length) return void 0;
|
|
1377
|
+
const label = v.path ? (0, import_runtime4.pageLabel)(v.path) : null;
|
|
1378
|
+
return { title: label ? `For the ${label} page` : "Suggested for you", items };
|
|
1379
|
+
}
|
|
694
1380
|
function renderHelpTab() {
|
|
695
1381
|
renderHead("Help");
|
|
696
1382
|
loadArticles();
|
|
1383
|
+
loadPage();
|
|
697
1384
|
help.render();
|
|
698
1385
|
main.appendChild(help.el);
|
|
699
1386
|
}
|
|
1387
|
+
function renderNewsTab() {
|
|
1388
|
+
renderHead("News");
|
|
1389
|
+
const scroll = el(doc, "div", "g-m-scroll");
|
|
1390
|
+
const items = news();
|
|
1391
|
+
if (!items.length) scroll.appendChild(el(doc, "div", "g-m-empty", "No news yet."));
|
|
1392
|
+
items.forEach((n) => scroll.appendChild(newsCard(n)));
|
|
1393
|
+
main.appendChild(scroll);
|
|
1394
|
+
const unread = items.filter((n) => n.unread).map((n) => n.id);
|
|
1395
|
+
if (unread.length) options.onNewsSeen?.(unread);
|
|
1396
|
+
}
|
|
1397
|
+
function loadPage(force = false) {
|
|
1398
|
+
const path = visitor().path;
|
|
1399
|
+
if (!path || !api.pageContent || !cards.suggestions) {
|
|
1400
|
+
page = null;
|
|
1401
|
+
pageKey = null;
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const key = (0, import_runtime4.foldPathIds)(path).toLowerCase();
|
|
1405
|
+
if (!force && key === pageKey && (pageLoading || Date.now() - pageAt < PAGE_TTL_MS)) return;
|
|
1406
|
+
if (key !== pageKey) page = null;
|
|
1407
|
+
pageKey = key;
|
|
1408
|
+
pageLoading = true;
|
|
1409
|
+
const seq = ++pageSeq;
|
|
1410
|
+
api.pageContent(path).then((res) => {
|
|
1411
|
+
if (seq !== pageSeq) return;
|
|
1412
|
+
page = res;
|
|
1413
|
+
pageAt = Date.now();
|
|
1414
|
+
if (!open) return;
|
|
1415
|
+
if (view.kind === "tab" && view.tab === "home") render();
|
|
1416
|
+
else if (view.kind === "tab" && view.tab === "help") help.render();
|
|
1417
|
+
else if (view.kind === "thread" && !messages.length) renderLog();
|
|
1418
|
+
}).catch(() => {
|
|
1419
|
+
if (seq === pageSeq) pageAt = Date.now();
|
|
1420
|
+
}).finally(() => {
|
|
1421
|
+
if (seq === pageSeq) pageLoading = false;
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
700
1424
|
let log = null;
|
|
701
1425
|
let foot = null;
|
|
702
1426
|
let textarea = null;
|
|
@@ -727,7 +1451,7 @@ function renderMessenger(options) {
|
|
|
727
1451
|
if (m.status === "pending") bubble.setAttribute("aria-busy", "true");
|
|
728
1452
|
bubble.dataset.messageId = m.id;
|
|
729
1453
|
wrap.appendChild(bubble);
|
|
730
|
-
if (m.citations.length || m.suggestedGuides.length) {
|
|
1454
|
+
if (m.citations.length || m.suggestedGuides.length || m.howTos?.length) {
|
|
731
1455
|
const chips = el(doc, "div", "g-m-chips");
|
|
732
1456
|
for (const g of m.suggestedGuides) {
|
|
733
1457
|
const b = el(doc, "button", "g-m-chip g-m-guide", `\u25B6 Show me: ${g.name}`);
|
|
@@ -744,6 +1468,12 @@ function renderMessenger(options) {
|
|
|
744
1468
|
b.addEventListener("click", () => openArticle(c.articleId));
|
|
745
1469
|
chips.appendChild(b);
|
|
746
1470
|
}
|
|
1471
|
+
for (const h of m.howTos ?? []) {
|
|
1472
|
+
const b = el(doc, "button", "g-m-chip", `\u{1F4CB} ${h.title}`);
|
|
1473
|
+
b.type = "button";
|
|
1474
|
+
b.addEventListener("click", () => openHowTo(h.howToId));
|
|
1475
|
+
chips.appendChild(b);
|
|
1476
|
+
}
|
|
747
1477
|
wrap.appendChild(chips);
|
|
748
1478
|
}
|
|
749
1479
|
return wrap;
|
|
@@ -753,8 +1483,27 @@ function renderMessenger(options) {
|
|
|
753
1483
|
const atBottom = log.scrollHeight - log.scrollTop - log.clientHeight < 40;
|
|
754
1484
|
log.textContent = "";
|
|
755
1485
|
if (!messages.length) {
|
|
756
|
-
const
|
|
1486
|
+
const v = visitor();
|
|
1487
|
+
const label = v.path ? (0, import_runtime4.pageLabel)(v.path) : null;
|
|
1488
|
+
if (label && config.aiAvailable && !current) log.appendChild(el(doc, "div", "g-m-context", `Asking from the ${label} page`));
|
|
1489
|
+
const intro = { id: "intro", author: config.aiAvailable ? "scout" : "agent", authorName: "Support team", body: personalGreeting(config.greeting, v.firstName), citations: [], suggestedGuides: [], status: "complete", createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
757
1490
|
log.appendChild(messageNode(intro));
|
|
1491
|
+
const questions = !current && cards.suggestions ? page?.questions ?? [] : [];
|
|
1492
|
+
if (questions.length && !sending) {
|
|
1493
|
+
const chips = el(doc, "div", "g-m-chips g-m-asks");
|
|
1494
|
+
chips.setAttribute("aria-label", "Common questions on this page");
|
|
1495
|
+
for (const q of questions) {
|
|
1496
|
+
const b = el(doc, "button", "g-m-chip g-m-ask", q);
|
|
1497
|
+
b.type = "button";
|
|
1498
|
+
b.addEventListener("click", () => {
|
|
1499
|
+
if (!textarea || sending) return;
|
|
1500
|
+
textarea.value = q;
|
|
1501
|
+
submit();
|
|
1502
|
+
});
|
|
1503
|
+
chips.appendChild(b);
|
|
1504
|
+
}
|
|
1505
|
+
log.appendChild(chips);
|
|
1506
|
+
}
|
|
758
1507
|
}
|
|
759
1508
|
messages.forEach((m) => log.appendChild(messageNode(m)));
|
|
760
1509
|
if (atBottom || sending) log.scrollTop = log.scrollHeight;
|
|
@@ -820,7 +1569,8 @@ function renderMessenger(options) {
|
|
|
820
1569
|
textarea.placeholder = current?.status === "open" ? "Reply to the team\u2026" : "Ask a question\u2026";
|
|
821
1570
|
textarea.setAttribute("aria-label", "Message");
|
|
822
1571
|
textarea.value = draft;
|
|
823
|
-
sendButton = el(doc, "button", "g-m-send"
|
|
1572
|
+
sendButton = el(doc, "button", "g-m-send");
|
|
1573
|
+
sendButton.appendChild(icon(doc, "arrow"));
|
|
824
1574
|
sendButton.type = "submit";
|
|
825
1575
|
sendButton.setAttribute("aria-label", "Send");
|
|
826
1576
|
compose.append(textarea, sendButton);
|
|
@@ -850,9 +1600,14 @@ function renderMessenger(options) {
|
|
|
850
1600
|
textarea = null;
|
|
851
1601
|
sendButton = null;
|
|
852
1602
|
if (view.kind === "thread") renderThread(view.conversationId);
|
|
1603
|
+
else if (view.kind === "howto") renderHowToView(view.id, view.from);
|
|
853
1604
|
else if (view.tab === "home") renderHome();
|
|
854
1605
|
else if (view.tab === "messages") renderMessagesTab();
|
|
855
|
-
else
|
|
1606
|
+
else if (view.tab === "news" && news().length) renderNewsTab();
|
|
1607
|
+
else if (view.tab === "news") {
|
|
1608
|
+
view = { kind: "tab", tab: "home" };
|
|
1609
|
+
renderHome();
|
|
1610
|
+
} else renderHelpTab();
|
|
856
1611
|
renderTabs();
|
|
857
1612
|
}
|
|
858
1613
|
const handlers = (seq) => ({
|
|
@@ -949,13 +1704,14 @@ function renderMessenger(options) {
|
|
|
949
1704
|
threadSeq++;
|
|
950
1705
|
if (threadTimer) clearTimeout(threadTimer);
|
|
951
1706
|
threadTimer = null;
|
|
952
|
-
if (view.kind === "tab" && view.tab === "help" && !(next.kind === "tab" && next.tab === "help")) help.reset();
|
|
1707
|
+
if (view.kind === "tab" && view.tab === "help" && !(next.kind === "tab" && next.tab === "help") && next.kind !== "howto") help.reset();
|
|
953
1708
|
view = next;
|
|
954
1709
|
if (next.kind === "thread") {
|
|
955
1710
|
messages = [];
|
|
956
1711
|
streaming.clear();
|
|
957
1712
|
sending = false;
|
|
958
1713
|
current = next.conversationId ? conversations.find((c) => c.id === next.conversationId) ?? null : null;
|
|
1714
|
+
if (!next.conversationId) loadPage();
|
|
959
1715
|
render();
|
|
960
1716
|
if (next.conversationId) {
|
|
961
1717
|
const seq = threadSeq;
|
|
@@ -994,6 +1750,7 @@ function renderMessenger(options) {
|
|
|
994
1750
|
if (v) {
|
|
995
1751
|
render();
|
|
996
1752
|
void refreshConversations();
|
|
1753
|
+
loadHowTos();
|
|
997
1754
|
if (view.kind === "thread") scheduleThreadPoll();
|
|
998
1755
|
options.onOpen?.();
|
|
999
1756
|
} else {
|
|
@@ -1036,6 +1793,16 @@ function renderMessenger(options) {
|
|
|
1036
1793
|
setOpen(true);
|
|
1037
1794
|
openArticle(id);
|
|
1038
1795
|
},
|
|
1796
|
+
refresh() {
|
|
1797
|
+
if (destroyed) return;
|
|
1798
|
+
if (!open) {
|
|
1799
|
+
pageKey = null;
|
|
1800
|
+
return;
|
|
1801
|
+
}
|
|
1802
|
+
loadPage();
|
|
1803
|
+
if (view.kind === "tab") render();
|
|
1804
|
+
else if (!messages.length) renderLog();
|
|
1805
|
+
},
|
|
1039
1806
|
destroy() {
|
|
1040
1807
|
destroyed = true;
|
|
1041
1808
|
if (threadTimer) clearTimeout(threadTimer);
|
|
@@ -1049,6 +1816,7 @@ function renderMessenger(options) {
|
|
|
1049
1816
|
}
|
|
1050
1817
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1051
1818
|
0 && (module.exports = {
|
|
1819
|
+
personalGreeting,
|
|
1052
1820
|
relativeTime,
|
|
1053
1821
|
renderMessenger
|
|
1054
1822
|
});
|