@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.js
CHANGED
|
@@ -1,7 +1,104 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createHelpView,
|
|
3
|
-
el
|
|
4
|
-
|
|
3
|
+
el,
|
|
4
|
+
listItem
|
|
5
|
+
} from "./chunk-4FKYF6OB.js";
|
|
6
|
+
import "./chunk-7DOGZKWD.js";
|
|
7
|
+
|
|
8
|
+
// src/messenger.ts
|
|
9
|
+
import { foldPathIds, pageLabel } from "@guidelytech/shared/runtime";
|
|
10
|
+
|
|
11
|
+
// src/howto-view.ts
|
|
12
|
+
var pct = (v) => `${(v * 100).toFixed(3)}%`;
|
|
13
|
+
var place = (node, r) => Object.assign(node.style, {
|
|
14
|
+
left: pct(r.x),
|
|
15
|
+
top: pct(r.y),
|
|
16
|
+
width: pct(r.width),
|
|
17
|
+
height: pct(r.height)
|
|
18
|
+
});
|
|
19
|
+
function paragraphs(doc, text, cls) {
|
|
20
|
+
const parts = text.split(/\n{2,}/).map((p) => p.trim()).filter(Boolean);
|
|
21
|
+
if (!parts.length) return null;
|
|
22
|
+
const wrap = el(doc, "div", cls);
|
|
23
|
+
for (const p of parts) wrap.appendChild(el(doc, "p", "", p));
|
|
24
|
+
return wrap;
|
|
25
|
+
}
|
|
26
|
+
function screenshot(doc, image, alt) {
|
|
27
|
+
if (!/^https?:\/\//i.test(image.url)) return null;
|
|
28
|
+
const frame = el(doc, "div", "g-howto-shot");
|
|
29
|
+
const img = (cls) => {
|
|
30
|
+
const i = el(doc, "img", cls);
|
|
31
|
+
i.src = image.url;
|
|
32
|
+
i.alt = alt;
|
|
33
|
+
i.loading = "lazy";
|
|
34
|
+
i.decoding = "async";
|
|
35
|
+
return i;
|
|
36
|
+
};
|
|
37
|
+
const crop = image.crop;
|
|
38
|
+
if (crop && crop.width > 0 && crop.height > 0) {
|
|
39
|
+
const ghost = img("g-howto-ghost");
|
|
40
|
+
ghost.alt = "";
|
|
41
|
+
ghost.setAttribute("aria-hidden", "true");
|
|
42
|
+
const zoomed = img("g-howto-zoomed");
|
|
43
|
+
Object.assign(zoomed.style, {
|
|
44
|
+
width: pct(1 / crop.width),
|
|
45
|
+
left: pct(-crop.x / crop.width),
|
|
46
|
+
top: pct(-crop.y / crop.height)
|
|
47
|
+
});
|
|
48
|
+
frame.append(ghost, zoomed);
|
|
49
|
+
} else frame.appendChild(img(""));
|
|
50
|
+
if (image.highlight) {
|
|
51
|
+
const mark = el(doc, "div", "g-howto-mark");
|
|
52
|
+
mark.setAttribute("aria-hidden", "true");
|
|
53
|
+
place(mark, image.highlight);
|
|
54
|
+
frame.appendChild(mark);
|
|
55
|
+
}
|
|
56
|
+
return frame;
|
|
57
|
+
}
|
|
58
|
+
function renderHowTo(doc, howTo, onShowMe) {
|
|
59
|
+
const wrap = el(doc, "div", "g-howto");
|
|
60
|
+
wrap.appendChild(el(doc, "h3", "g-title", howTo.title));
|
|
61
|
+
const description = paragraphs(doc, howTo.description, "g-howto-desc");
|
|
62
|
+
if (description) wrap.appendChild(description);
|
|
63
|
+
const guideKey = howTo.guideKey;
|
|
64
|
+
if (guideKey && onShowMe) {
|
|
65
|
+
const show = el(doc, "button", "g-btn g-btn-primary g-howto-show", "\u25B6 Show me in the app");
|
|
66
|
+
show.type = "button";
|
|
67
|
+
show.addEventListener("click", () => onShowMe(guideKey));
|
|
68
|
+
wrap.appendChild(show);
|
|
69
|
+
}
|
|
70
|
+
const list = el(doc, "ol", "g-howto-steps");
|
|
71
|
+
let n = 0;
|
|
72
|
+
for (const s of howTo.steps) {
|
|
73
|
+
const li = el(doc, "li", `g-howto-step g-howto-${s.kind}`);
|
|
74
|
+
if (s.kind === "heading") li.appendChild(el(doc, "h4", "", s.title));
|
|
75
|
+
else if (s.kind === "tip" || s.kind === "alert") {
|
|
76
|
+
li.appendChild(el(doc, "strong", "", s.title || (s.kind === "tip" ? "Tip" : "Important")));
|
|
77
|
+
const body = paragraphs(doc, s.body, "");
|
|
78
|
+
if (body) li.appendChild(body);
|
|
79
|
+
} else if (s.kind === "text") {
|
|
80
|
+
if (s.title) li.appendChild(el(doc, "strong", "", s.title));
|
|
81
|
+
const body = paragraphs(doc, s.body, "g-howto-note");
|
|
82
|
+
if (body) li.appendChild(body);
|
|
83
|
+
} else {
|
|
84
|
+
const row = el(doc, "div", "g-howto-row");
|
|
85
|
+
const num = el(doc, "span", "g-howto-num", String(++n));
|
|
86
|
+
const text = el(doc, "div", "g-howto-text");
|
|
87
|
+
text.appendChild(el(doc, "p", "g-howto-title", s.title));
|
|
88
|
+
const body = paragraphs(doc, s.body, "g-howto-note");
|
|
89
|
+
if (body) text.appendChild(body);
|
|
90
|
+
row.append(num, text);
|
|
91
|
+
li.appendChild(row);
|
|
92
|
+
const shot = s.image ? screenshot(doc, s.image, s.title) : null;
|
|
93
|
+
if (shot) li.appendChild(shot);
|
|
94
|
+
}
|
|
95
|
+
list.appendChild(li);
|
|
96
|
+
}
|
|
97
|
+
if (!list.childNodes.length)
|
|
98
|
+
wrap.appendChild(el(doc, "div", "g-rc-empty", "This how-to has no steps yet."));
|
|
99
|
+
else wrap.appendChild(list);
|
|
100
|
+
return wrap;
|
|
101
|
+
}
|
|
5
102
|
|
|
6
103
|
// src/messenger-styles.ts
|
|
7
104
|
var MESSENGER_STYLES = `
|
|
@@ -17,17 +114,16 @@ var MESSENGER_STYLES = `
|
|
|
17
114
|
.g-m-head .g-close{color:inherit;top:50%;transform:translateY(-50%)}
|
|
18
115
|
.g-m-head .g-close:hover,.g-m-head .g-close:focus-visible{background:rgba(255,255,255,.18);color:inherit}
|
|
19
116
|
.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}
|
|
20
|
-
.g-m-back{border:0;background:transparent;color:inherit;font:inherit;
|
|
117
|
+
.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}
|
|
21
118
|
.g-m-back:hover,.g-m-back:focus-visible{background:rgba(255,255,255,.18);outline:none}
|
|
22
119
|
.g-m-main{flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}
|
|
23
120
|
.g-m-scroll{flex:1;overflow:auto;padding:16px}
|
|
24
|
-
.g-m-hero{font-size:22px;font-weight:700;margin:4px 0 4px}
|
|
25
|
-
.g-m-greet{color:var(--g-muted);margin:0 0 16px}
|
|
26
121
|
.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)}
|
|
27
122
|
.g-m-card:hover{background:var(--g-subtle)}
|
|
28
123
|
.g-m-card:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}
|
|
29
|
-
.g-m-card-title{font-weight:600}
|
|
30
|
-
.g-m-card-sub{color:var(--g-muted);font-size:13px;margin-top:2px}
|
|
124
|
+
.g-m-card-title{display:block;font-weight:600}
|
|
125
|
+
.g-m-card-sub{display:block;color:var(--g-muted);font-size:13px;margin-top:2px}
|
|
126
|
+
.g-m-card-label{display:block;font-size:12px;color:var(--g-muted);font-weight:500}
|
|
31
127
|
.g-m-row{display:flex;align-items:flex-start;gap:10px}
|
|
32
128
|
.g-m-row .g-m-grow{flex:1;min-width:0}
|
|
33
129
|
.g-m-ellipsis{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
@@ -37,7 +133,9 @@ var MESSENGER_STYLES = `
|
|
|
37
133
|
.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}
|
|
38
134
|
.g-m-tab[aria-selected="true"]{color:var(--g-primary)}
|
|
39
135
|
.g-m-tab:focus-visible{outline:2px solid var(--g-primary);outline-offset:-2px}
|
|
40
|
-
.g-m-tab-icon{
|
|
136
|
+
.g-m-tab-icon{position:relative;display:inline-flex;line-height:1}
|
|
137
|
+
.g-m-tab-icon .g-m-ico{width:22px;height:22px}
|
|
138
|
+
.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}
|
|
41
139
|
.g-m-log{flex:1;overflow:auto;padding:16px;display:flex;flex-direction:column;gap:10px}
|
|
42
140
|
.g-m-msg{max-width:85%;display:flex;flex-direction:column;gap:4px}
|
|
43
141
|
.g-m-msg.g-from-user{align-self:flex-end;align-items:flex-end}
|
|
@@ -66,7 +164,8 @@ var MESSENGER_STYLES = `
|
|
|
66
164
|
.g-m-compose{display:flex;align-items:flex-end;gap:8px}
|
|
67
165
|
.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)}
|
|
68
166
|
.g-m-compose textarea:focus-visible{outline:2px solid var(--g-primary);outline-offset:1px}
|
|
69
|
-
.g-m-send{width:40px;height:40px;border-radius:999px;border:0;background:var(--g-primary);color:var(--g-primary-text);cursor:pointer;
|
|
167
|
+
.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}
|
|
168
|
+
.g-m-send .g-m-ico{width:18px;height:18px}
|
|
70
169
|
.g-m-send:disabled{opacity:.45;cursor:not-allowed}
|
|
71
170
|
.g-m-send:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px}
|
|
72
171
|
.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}
|
|
@@ -76,6 +175,79 @@ var MESSENGER_STYLES = `
|
|
|
76
175
|
.g-m-rate button:hover,.g-m-rate button:focus-visible{background:var(--g-subtle);outline:none}
|
|
77
176
|
.g-m-note{color:var(--g-muted);font-size:12px;margin-top:6px}
|
|
78
177
|
.g-m-empty{color:var(--g-muted);text-align:center;padding:24px 8px}
|
|
178
|
+
.g-howto .g-title{margin:0 0 6px}
|
|
179
|
+
.g-howto-desc,.g-howto-note{color:var(--g-muted);font-size:13px}
|
|
180
|
+
.g-howto p{margin:0 0 6px;white-space:pre-line}
|
|
181
|
+
.g-howto-show{width:100%;margin:6px 0 4px}
|
|
182
|
+
.g-howto-steps{list-style:none;margin:12px 0 0;padding:0;display:flex;flex-direction:column;gap:16px}
|
|
183
|
+
.g-howto-step h4{margin:4px 0 0;font-size:15px}
|
|
184
|
+
.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}
|
|
185
|
+
.g-howto-alert{border-left-color:#d97706}
|
|
186
|
+
.g-howto-row{display:flex;gap:10px;align-items:flex-start}
|
|
187
|
+
.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}
|
|
188
|
+
.g-howto-text{flex:1;min-width:0;padding-top:2px}
|
|
189
|
+
.g-howto-title{font-weight:600}
|
|
190
|
+
.g-howto-shot{position:relative;overflow:hidden;margin-top:8px;border:1px solid var(--g-border);border-radius:8px;background:var(--g-subtle)}
|
|
191
|
+
.g-howto-shot img{display:block;width:100%;height:auto}
|
|
192
|
+
.g-howto-shot .g-howto-ghost{visibility:hidden}
|
|
193
|
+
.g-howto-shot .g-howto-zoomed{position:absolute;max-width:none}
|
|
194
|
+
.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)}
|
|
195
|
+
.g-m-ico{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none}
|
|
196
|
+
.g-close .g-m-ico,.g-m-back .g-m-ico{width:18px;height:18px}
|
|
197
|
+
.g-m-row.g-m-center{align-items:center}
|
|
198
|
+
.g-m-row.g-m-baseline{align-items:baseline}
|
|
199
|
+
.g-m-static{cursor:default}
|
|
200
|
+
.g-m-static:hover{background:var(--g-bg)}
|
|
201
|
+
.g-m-wide{width:100%}
|
|
202
|
+
.g-m-home{position:relative;padding:0}
|
|
203
|
+
.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%)}
|
|
204
|
+
.g-m-home-inner{position:relative;padding:16px;display:flex;flex-direction:column;gap:12px}
|
|
205
|
+
.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)}
|
|
206
|
+
.g-m-hero-top{display:flex;align-items:center;justify-content:space-between;gap:12px;min-height:40px;padding:2px 4px 0}
|
|
207
|
+
.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}
|
|
208
|
+
.g-m-logo img{width:100%;height:100%;object-fit:contain}
|
|
209
|
+
.g-m-team{display:flex}
|
|
210
|
+
.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}
|
|
211
|
+
.g-m-avatar:first-child{margin-left:0}
|
|
212
|
+
.g-m-avatar-1{background:#d9f0e3;color:#1f5e3b}
|
|
213
|
+
.g-m-avatar-2{background:#e3e1fa;color:#3b3486}
|
|
214
|
+
.g-m-avatar img{width:100%;height:100%;object-fit:cover}
|
|
215
|
+
.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}
|
|
216
|
+
.g-m-hero-close .g-m-ico{width:18px;height:18px}
|
|
217
|
+
.g-m-hero-close:hover{background:rgba(255,255,255,.28)}
|
|
218
|
+
.g-m-hero-close:focus-visible{outline:2px solid var(--g-primary-text);outline-offset:2px}
|
|
219
|
+
.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}
|
|
220
|
+
.g-m-hello-1{opacity:.8}
|
|
221
|
+
.g-m-trail{color:var(--g-primary)}
|
|
222
|
+
.g-m-lead{color:var(--g-muted)}
|
|
223
|
+
.g-m-status-ico{width:26px;height:26px}
|
|
224
|
+
.g-m-status-operational .g-m-status-ico{color:#15803d}
|
|
225
|
+
.g-m-status-degraded .g-m-status-ico{color:#b45309}
|
|
226
|
+
.g-m-status-outage .g-m-status-ico{color:#b91c1c}
|
|
227
|
+
.g-m-status-maintenance .g-m-status-ico{color:#1d4ed8}
|
|
228
|
+
.g-m-bar{display:block;height:6px;border-radius:999px;background:var(--g-border);margin-top:10px;overflow:hidden}
|
|
229
|
+
.g-m-bar-fill{display:block;height:100%;background:var(--g-primary);border-radius:999px}
|
|
230
|
+
.g-m-link{color:var(--g-primary);font-weight:600;font-size:13px;flex:none}
|
|
231
|
+
.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}
|
|
232
|
+
.g-m-badge-ico .g-m-ico{width:16px;height:16px}
|
|
233
|
+
.g-m-badge-ico.g-m-accent{background:color-mix(in srgb,var(--g-primary) 12%,var(--g-bg));color:var(--g-primary)}
|
|
234
|
+
.g-m-suggest{padding-bottom:4px}
|
|
235
|
+
.g-m-items{display:flex;flex-direction:column;margin-top:8px}
|
|
236
|
+
.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}
|
|
237
|
+
.g-m-item:hover .g-m-item-title{color:var(--g-primary)}
|
|
238
|
+
.g-m-item:focus-visible{outline:2px solid var(--g-primary);outline-offset:2px;border-radius:4px}
|
|
239
|
+
.g-m-item .g-m-grow{flex:1;min-width:0}
|
|
240
|
+
.g-m-item-title{display:block;font-weight:600;overflow-wrap:anywhere}
|
|
241
|
+
.g-m-news{padding:0;overflow:hidden}
|
|
242
|
+
.g-m-news-img{display:block;width:100%;height:150px;object-fit:cover;background:var(--g-subtle)}
|
|
243
|
+
.g-m-news-body{display:block;padding:14px 16px}
|
|
244
|
+
.g-m-news-meta{gap:8px;margin-bottom:4px}
|
|
245
|
+
.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}
|
|
246
|
+
.g-m-clamp{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
|
|
247
|
+
.g-m-context{align-self:center;font-size:12px;color:var(--g-muted);background:var(--g-subtle);border-radius:999px;padding:3px 10px}
|
|
248
|
+
.g-m-asks{align-items:flex-start}
|
|
249
|
+
.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}
|
|
250
|
+
.g-m-chip.g-m-ask:disabled{opacity:.5;cursor:not-allowed}
|
|
79
251
|
`;
|
|
80
252
|
|
|
81
253
|
// src/render-safe.ts
|
|
@@ -164,6 +336,73 @@ function chatIcon(doc) {
|
|
|
164
336
|
svg.appendChild(path);
|
|
165
337
|
return svg;
|
|
166
338
|
}
|
|
339
|
+
var ICONS = {
|
|
340
|
+
home: ["M3 10.5 12 3l9 7.5V20a1 1 0 0 1-1 1h-5v-6H9v6H4a1 1 0 0 1-1-1z"],
|
|
341
|
+
messages: ["M4 5h16v11H8l-4 4z"],
|
|
342
|
+
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"],
|
|
343
|
+
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"],
|
|
344
|
+
arrow: ["M5 12h14", "M13 6l6 6-6 6"],
|
|
345
|
+
close: ["M6 6l12 12", "M18 6 6 18"],
|
|
346
|
+
back: ["M15 6l-6 6 6 6"],
|
|
347
|
+
chevron: ["m9 6 6 6-6 6"],
|
|
348
|
+
check: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z", "m8 12 3 3 5-6"],
|
|
349
|
+
alert: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z", "M12 8v4", "M12 16h.01"],
|
|
350
|
+
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"],
|
|
351
|
+
play: ["M8 5v14l11-7z"],
|
|
352
|
+
steps: ["M9 6h11", "M9 12h11", "M9 18h11", "M4 6h.01", "M4 12h.01", "M4 18h.01"],
|
|
353
|
+
article: ["M6 3h9l4 4v14H6z", "M9 12h6", "M9 16h6"],
|
|
354
|
+
ask: ["M4 5h16v11H8l-4 4z", "M9 9.5h6", "M9 12.5h4"],
|
|
355
|
+
search: ["M11 4a7 7 0 1 0 0 14 7 7 0 0 0 0-14z", "m20 20-3.5-3.5"],
|
|
356
|
+
circle: ["M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z"]
|
|
357
|
+
};
|
|
358
|
+
function icon(doc, name, cls = "g-m-ico") {
|
|
359
|
+
const svg = doc.createElementNS(SVG_NS, "svg");
|
|
360
|
+
svg.setAttribute("viewBox", "0 0 24 24");
|
|
361
|
+
svg.setAttribute("aria-hidden", "true");
|
|
362
|
+
svg.setAttribute("class", cls);
|
|
363
|
+
for (const d of ICONS[name] ?? []) {
|
|
364
|
+
const path = doc.createElementNS(SVG_NS, "path");
|
|
365
|
+
path.setAttribute("d", d);
|
|
366
|
+
svg.appendChild(path);
|
|
367
|
+
}
|
|
368
|
+
return svg;
|
|
369
|
+
}
|
|
370
|
+
var REPLY_TIME_TEXT = {
|
|
371
|
+
minutes: "typically replies in a few minutes",
|
|
372
|
+
hour: "typically replies within an hour",
|
|
373
|
+
hours: "typically replies in a few hours",
|
|
374
|
+
day: "typically replies within a day"
|
|
375
|
+
};
|
|
376
|
+
var STATUS_TEXT = {
|
|
377
|
+
operational: { title: "All systems operational", icon: "check" },
|
|
378
|
+
degraded: { title: "Degraded performance", icon: "alert" },
|
|
379
|
+
outage: { title: "Service outage", icon: "alert" },
|
|
380
|
+
maintenance: { title: "Scheduled maintenance", icon: "wrench" }
|
|
381
|
+
};
|
|
382
|
+
function shortDateTime(iso) {
|
|
383
|
+
const d = new Date(iso);
|
|
384
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
385
|
+
try {
|
|
386
|
+
return d.toLocaleString(void 0, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" });
|
|
387
|
+
} catch {
|
|
388
|
+
return d.toISOString().slice(0, 16).replace("T", " ");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
function shortDate(iso) {
|
|
392
|
+
const d = new Date(iso);
|
|
393
|
+
if (Number.isNaN(d.getTime())) return "";
|
|
394
|
+
try {
|
|
395
|
+
return d.toLocaleDateString(void 0, { month: "short", day: "numeric" });
|
|
396
|
+
} catch {
|
|
397
|
+
return d.toISOString().slice(0, 10);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
var initialOf = (name) => (name.trim().match(/\p{L}/u)?.[0] ?? "?").toUpperCase();
|
|
401
|
+
function personalGreeting(greeting, firstName) {
|
|
402
|
+
if (!firstName) return greeting;
|
|
403
|
+
const rest = greeting.replace(/^\s*(hi|hello|hey)( there)?\s*[!,.]?\s*/i, "");
|
|
404
|
+
return rest ? `Hi ${firstName}! ${rest}` : `Hi ${firstName}!`;
|
|
405
|
+
}
|
|
167
406
|
function relativeTime(iso, now = Date.now()) {
|
|
168
407
|
const s = Math.max(0, Math.round((now - Date.parse(iso)) / 1e3));
|
|
169
408
|
if (!Number.isFinite(s) || s < 60) return "now";
|
|
@@ -199,6 +438,25 @@ function renderMessenger(options) {
|
|
|
199
438
|
let threadTimer = null;
|
|
200
439
|
let backgroundTimer = null;
|
|
201
440
|
let threadSeq = 0;
|
|
441
|
+
let howTos = null;
|
|
442
|
+
let howTosSeq = 0;
|
|
443
|
+
const howToCache = /* @__PURE__ */ new Map();
|
|
444
|
+
let page = null;
|
|
445
|
+
let pageKey = null;
|
|
446
|
+
let pageAt = 0;
|
|
447
|
+
let pageSeq = 0;
|
|
448
|
+
let pageLoading = false;
|
|
449
|
+
const PAGE_TTL_MS = 12e4;
|
|
450
|
+
const cards = { suggestions: config.home?.suggestions !== false, progress: config.home?.progress !== false, news: config.home?.news !== false };
|
|
451
|
+
const visitor = () => {
|
|
452
|
+
try {
|
|
453
|
+
const v = options.visitor?.();
|
|
454
|
+
if (v) return v;
|
|
455
|
+
} catch {
|
|
456
|
+
}
|
|
457
|
+
return { firstName: null, path: null, pageGuides: [], resumeGuide: null, checklist: null, news: [] };
|
|
458
|
+
};
|
|
459
|
+
const news = () => cards.news ? visitor().news : [];
|
|
202
460
|
const pos = `g-pos-${config.launcher.position}`;
|
|
203
461
|
const rootNode = scope.getRootNode();
|
|
204
462
|
const shadowHost = "host" in rootNode ? rootNode.host : null;
|
|
@@ -230,6 +488,7 @@ function renderMessenger(options) {
|
|
|
230
488
|
data: () => ({
|
|
231
489
|
collections: articles?.collections ?? options.resourceCenter?.collections ?? [],
|
|
232
490
|
articles: articles?.articles ?? options.resourceCenter?.articles ?? [],
|
|
491
|
+
suggested: helpSuggestions(),
|
|
233
492
|
recommended: rcExtras(options.resourceCenter?.recommendedGuideIds),
|
|
234
493
|
checklists: rcExtras(options.resourceCenter?.checklistGuideIds),
|
|
235
494
|
faqs: options.resourceCenter?.faqs,
|
|
@@ -246,7 +505,8 @@ function renderMessenger(options) {
|
|
|
246
505
|
setOpen(false);
|
|
247
506
|
options.onOpenChecklist?.(id);
|
|
248
507
|
},
|
|
249
|
-
onArticleView: options.onArticleView
|
|
508
|
+
onArticleView: options.onArticleView,
|
|
509
|
+
extra: (q) => howToItems(q)
|
|
250
510
|
});
|
|
251
511
|
const unreadTotal = () => conversations.reduce((n, c) => n + (c.unread > 0 ? 1 : 0), 0);
|
|
252
512
|
const updateBadge = () => {
|
|
@@ -276,6 +536,32 @@ function renderMessenger(options) {
|
|
|
276
536
|
}).catch(() => {
|
|
277
537
|
}).finally(() => articlesLoading = false);
|
|
278
538
|
};
|
|
539
|
+
const loadHowTos = () => {
|
|
540
|
+
if (!api.listHowTos) return;
|
|
541
|
+
const seq = ++howTosSeq;
|
|
542
|
+
api.listHowTos().then((res) => {
|
|
543
|
+
if (seq !== howTosSeq || destroyed) return;
|
|
544
|
+
howTos = res;
|
|
545
|
+
if (open && view.kind === "tab" && view.tab === "home") render();
|
|
546
|
+
else if (open && view.kind === "tab" && view.tab === "help") help.render();
|
|
547
|
+
}).catch(() => {
|
|
548
|
+
});
|
|
549
|
+
};
|
|
550
|
+
const pageHowTos = () => (howTos?.forPage ?? []).map((id) => howTos?.howTos.find((h) => h.id === id)).filter((h) => !!h);
|
|
551
|
+
const stepsLabel = (h) => `${h.stepCount} ${h.stepCount === 1 ? "step" : "steps"}`;
|
|
552
|
+
const openHowTo = (id) => go({ kind: "howto", id, from: view });
|
|
553
|
+
function howToItems(q) {
|
|
554
|
+
const list = q ? (howTos?.howTos ?? []).filter((h) => h.title.toLowerCase().includes(q) || h.description.toLowerCase().includes(q)).slice(0, 10) : [];
|
|
555
|
+
if (!list.length) return [];
|
|
556
|
+
return [
|
|
557
|
+
el(doc, "div", "g-rc-section", `How-tos (${list.length})`),
|
|
558
|
+
...list.map((h) => {
|
|
559
|
+
const b = listItem(doc, "\u{1F4CB}", h.title, h.description || stepsLabel(h));
|
|
560
|
+
b.addEventListener("click", () => openHowTo(h.id));
|
|
561
|
+
return b;
|
|
562
|
+
})
|
|
563
|
+
];
|
|
564
|
+
}
|
|
279
565
|
const refreshConversations = () => api.listConversations().then((list) => {
|
|
280
566
|
conversations = [...list].sort((a, b) => b.lastMessageAt.localeCompare(a.lastMessageAt));
|
|
281
567
|
updateBadge();
|
|
@@ -284,92 +570,379 @@ function renderMessenger(options) {
|
|
|
284
570
|
});
|
|
285
571
|
function renderHead(title, back) {
|
|
286
572
|
head.textContent = "";
|
|
573
|
+
head.hidden = false;
|
|
287
574
|
if (back) {
|
|
288
|
-
const b = el(doc, "button", "g-m-back"
|
|
575
|
+
const b = el(doc, "button", "g-m-back");
|
|
289
576
|
b.type = "button";
|
|
290
577
|
b.setAttribute("aria-label", "Back");
|
|
578
|
+
b.appendChild(icon(doc, "back"));
|
|
291
579
|
b.addEventListener("click", back);
|
|
292
580
|
head.appendChild(b);
|
|
293
581
|
}
|
|
294
582
|
head.appendChild(el(doc, "h2", "", title));
|
|
295
|
-
|
|
583
|
+
head.appendChild(closeButton("g-close"));
|
|
584
|
+
}
|
|
585
|
+
function closeButton(cls) {
|
|
586
|
+
const close = el(doc, "button", cls);
|
|
296
587
|
close.type = "button";
|
|
297
588
|
close.setAttribute("aria-label", "Close");
|
|
589
|
+
close.appendChild(icon(doc, "close"));
|
|
298
590
|
close.addEventListener("click", () => setOpen(false));
|
|
299
|
-
|
|
591
|
+
return close;
|
|
300
592
|
}
|
|
301
593
|
function renderTabs() {
|
|
302
594
|
tabs.textContent = "";
|
|
303
|
-
tabs.hidden = view.kind
|
|
595
|
+
tabs.hidden = view.kind !== "tab";
|
|
304
596
|
const items = [
|
|
305
|
-
["home", "
|
|
306
|
-
["messages", "
|
|
307
|
-
["help", "
|
|
597
|
+
["home", "home", "Home"],
|
|
598
|
+
["messages", "messages", "Messages"],
|
|
599
|
+
["help", "help", "Help"]
|
|
308
600
|
];
|
|
309
|
-
|
|
601
|
+
const latest = news();
|
|
602
|
+
if (latest.length) items.push(["news", "news", "News"]);
|
|
603
|
+
for (const [tab, ico, label] of items) {
|
|
310
604
|
const b = el(doc, "button", "g-m-tab");
|
|
311
605
|
b.type = "button";
|
|
312
606
|
b.setAttribute("role", "tab");
|
|
313
607
|
b.setAttribute("aria-selected", String(view.kind === "tab" && view.tab === tab));
|
|
314
|
-
const i = el(doc, "span", "g-m-tab-icon"
|
|
315
|
-
i.
|
|
608
|
+
const i = el(doc, "span", "g-m-tab-icon");
|
|
609
|
+
i.appendChild(icon(doc, ico));
|
|
316
610
|
b.append(i, el(doc, "span", "", label));
|
|
611
|
+
if (tab === "news" && latest.some((n) => n.unread)) {
|
|
612
|
+
const dot = el(doc, "span", "g-m-tab-dot");
|
|
613
|
+
dot.setAttribute("aria-label", "New");
|
|
614
|
+
i.appendChild(dot);
|
|
615
|
+
}
|
|
317
616
|
b.addEventListener("click", () => go({ kind: "tab", tab }));
|
|
318
617
|
tabs.appendChild(b);
|
|
319
618
|
}
|
|
320
619
|
}
|
|
321
|
-
function conversationCard(c) {
|
|
620
|
+
function conversationCard(c, label) {
|
|
322
621
|
const card = el(doc, "button", "g-m-card");
|
|
323
622
|
card.type = "button";
|
|
324
623
|
const row = el(doc, "div", "g-m-row");
|
|
325
624
|
if (c.unread > 0) row.appendChild(el(doc, "span", "g-m-dot"));
|
|
326
625
|
const text = el(doc, "div", "g-m-grow");
|
|
626
|
+
if (label) text.appendChild(el(doc, "div", "g-m-card-label", label));
|
|
327
627
|
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));
|
|
328
628
|
row.append(text, el(doc, "span", "g-m-time", relativeTime(c.lastMessageAt)));
|
|
329
629
|
card.appendChild(row);
|
|
330
|
-
card.setAttribute("aria-label", `${c.subject || "Conversation"}${c.unread ? ", unread" : ""}`);
|
|
630
|
+
card.setAttribute("aria-label", `${label ? `${label}: ` : ""}${c.subject || "Conversation"}${c.unread ? ", unread" : ""}`);
|
|
331
631
|
card.addEventListener("click", () => go({ kind: "thread", conversationId: c.id }));
|
|
332
632
|
return card;
|
|
333
633
|
}
|
|
634
|
+
function actionCard(opts) {
|
|
635
|
+
const card = el(doc, "button", `g-m-card g-m-row g-m-center${opts.cls ? ` ${opts.cls}` : ""}`);
|
|
636
|
+
card.type = "button";
|
|
637
|
+
if (opts.lead) card.appendChild(opts.lead);
|
|
638
|
+
const text = el(doc, "span", "g-m-grow");
|
|
639
|
+
text.appendChild(el(doc, "span", "g-m-card-title", opts.title));
|
|
640
|
+
if (opts.sub) text.appendChild(el(doc, "span", "g-m-card-sub", opts.sub));
|
|
641
|
+
card.appendChild(text);
|
|
642
|
+
if (opts.trail) card.appendChild(icon(doc, "arrow", "g-m-ico g-m-trail"));
|
|
643
|
+
card.addEventListener("click", opts.onClick);
|
|
644
|
+
return card;
|
|
645
|
+
}
|
|
646
|
+
function progressBar(done, total) {
|
|
647
|
+
const bar = el(doc, "span", "g-m-bar");
|
|
648
|
+
bar.setAttribute("role", "progressbar");
|
|
649
|
+
bar.setAttribute("aria-valuemin", "0");
|
|
650
|
+
bar.setAttribute("aria-valuemax", String(total));
|
|
651
|
+
bar.setAttribute("aria-valuenow", String(done));
|
|
652
|
+
const fill = el(doc, "span", "g-m-bar-fill");
|
|
653
|
+
fill.style.width = `${total ? Math.round(Math.min(done, total) / total * 100) : 0}%`;
|
|
654
|
+
bar.appendChild(fill);
|
|
655
|
+
return bar;
|
|
656
|
+
}
|
|
657
|
+
function teamAvatars() {
|
|
658
|
+
const team = (config.team ?? []).slice(0, 3);
|
|
659
|
+
if (!team.length) return null;
|
|
660
|
+
const wrap = el(doc, "div", "g-m-team");
|
|
661
|
+
wrap.setAttribute("role", "img");
|
|
662
|
+
wrap.setAttribute("aria-label", `The team: ${team.map((t) => t.name).join(", ")}`);
|
|
663
|
+
team.forEach((t, i) => {
|
|
664
|
+
const a = el(doc, "span", `g-m-avatar g-m-avatar-${i % 3}`);
|
|
665
|
+
if (t.avatarUrl) {
|
|
666
|
+
const img = el(doc, "img");
|
|
667
|
+
img.src = t.avatarUrl;
|
|
668
|
+
img.alt = "";
|
|
669
|
+
img.referrerPolicy = "no-referrer";
|
|
670
|
+
img.addEventListener("error", () => {
|
|
671
|
+
img.remove();
|
|
672
|
+
a.textContent = initialOf(t.name);
|
|
673
|
+
});
|
|
674
|
+
a.appendChild(img);
|
|
675
|
+
} else a.textContent = initialOf(t.name);
|
|
676
|
+
wrap.appendChild(a);
|
|
677
|
+
});
|
|
678
|
+
return wrap;
|
|
679
|
+
}
|
|
680
|
+
function brandMark() {
|
|
681
|
+
const name = options.brand?.name || config.name;
|
|
682
|
+
const mark = el(doc, "span", "g-m-logo");
|
|
683
|
+
const url = options.brand?.logoUrl;
|
|
684
|
+
if (url && /^(https?:)?\/\//.test(url)) {
|
|
685
|
+
const img = el(doc, "img");
|
|
686
|
+
img.src = url;
|
|
687
|
+
img.alt = name;
|
|
688
|
+
img.addEventListener("error", () => {
|
|
689
|
+
img.remove();
|
|
690
|
+
mark.textContent = initialOf(name);
|
|
691
|
+
});
|
|
692
|
+
mark.appendChild(img);
|
|
693
|
+
} else {
|
|
694
|
+
mark.textContent = initialOf(name);
|
|
695
|
+
mark.setAttribute("aria-label", name);
|
|
696
|
+
mark.setAttribute("role", "img");
|
|
697
|
+
}
|
|
698
|
+
return mark;
|
|
699
|
+
}
|
|
700
|
+
function messageSub() {
|
|
701
|
+
const reply = config.replyTime ? REPLY_TIME_TEXT[config.replyTime] : null;
|
|
702
|
+
if (config.aiAvailable) return reply ? `${config.name} answers right away. The team ${reply}.` : `${config.name} answers right away, and the team can help too.`;
|
|
703
|
+
return reply ? `The team ${reply}.` : "The team will reply here.";
|
|
704
|
+
}
|
|
705
|
+
function statusCard(status) {
|
|
706
|
+
const meta = STATUS_TEXT[status.level] ?? STATUS_TEXT.operational;
|
|
707
|
+
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}`);
|
|
708
|
+
card.setAttribute("role", "status");
|
|
709
|
+
card.appendChild(icon(doc, meta.icon, "g-m-ico g-m-status-ico"));
|
|
710
|
+
const text = el(doc, "span", "g-m-grow");
|
|
711
|
+
text.appendChild(el(doc, "span", "g-m-card-title", status.message || meta.title));
|
|
712
|
+
const when = shortDateTime(status.updatedAt);
|
|
713
|
+
if (when) text.appendChild(el(doc, "span", "g-m-card-sub", `Updated ${when}`));
|
|
714
|
+
card.appendChild(text);
|
|
715
|
+
return card;
|
|
716
|
+
}
|
|
717
|
+
function progressCards(v) {
|
|
718
|
+
const out = [];
|
|
719
|
+
if (v.resumeGuide) {
|
|
720
|
+
const g = v.resumeGuide;
|
|
721
|
+
const card = el(doc, "button", "g-m-card");
|
|
722
|
+
card.type = "button";
|
|
723
|
+
const row = el(doc, "span", "g-m-row g-m-center");
|
|
724
|
+
row.appendChild(iconTile("play", true));
|
|
725
|
+
const text = el(doc, "span", "g-m-grow");
|
|
726
|
+
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}`));
|
|
727
|
+
row.append(text, el(doc, "span", "g-m-link", "Resume"));
|
|
728
|
+
card.append(row, progressBar(g.stepIndex, g.stepCount));
|
|
729
|
+
card.addEventListener("click", () => {
|
|
730
|
+
setOpen(false);
|
|
731
|
+
options.onResumeGuide?.(g.id, g.stepId);
|
|
732
|
+
});
|
|
733
|
+
out.push(card);
|
|
734
|
+
}
|
|
735
|
+
if (v.checklist && v.checklist.total > 0) {
|
|
736
|
+
const c = v.checklist;
|
|
737
|
+
const card = el(doc, "button", "g-m-card");
|
|
738
|
+
card.type = "button";
|
|
739
|
+
const top = el(doc, "span", "g-m-row g-m-baseline");
|
|
740
|
+
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`));
|
|
741
|
+
card.append(top, progressBar(c.done, c.total));
|
|
742
|
+
if (c.nextTask) card.appendChild(el(doc, "span", "g-m-card-sub", `Next: ${c.nextTask}`));
|
|
743
|
+
card.addEventListener("click", () => {
|
|
744
|
+
setOpen(false);
|
|
745
|
+
options.onOpenChecklist?.(c.id);
|
|
746
|
+
});
|
|
747
|
+
out.push(card);
|
|
748
|
+
}
|
|
749
|
+
return out;
|
|
750
|
+
}
|
|
751
|
+
function iconTile(name, accent = false) {
|
|
752
|
+
const b = el(doc, "span", `g-m-badge-ico${accent ? " g-m-accent" : ""}`);
|
|
753
|
+
b.appendChild(icon(doc, name));
|
|
754
|
+
return b;
|
|
755
|
+
}
|
|
756
|
+
function pageSuggestions(v, max = 4) {
|
|
757
|
+
if (!cards.suggestions) return [];
|
|
758
|
+
const out = [];
|
|
759
|
+
for (const g of v.pageGuides.slice(0, 2)) {
|
|
760
|
+
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) });
|
|
761
|
+
}
|
|
762
|
+
const inApp = /* @__PURE__ */ new Set();
|
|
763
|
+
for (const h of pageHowTos()) {
|
|
764
|
+
if (out.length >= max) break;
|
|
765
|
+
inApp.add(h.title.toLowerCase());
|
|
766
|
+
out.push({ icon: "steps", title: h.title, sub: `How-to \xB7 ${stepsLabel(h)}`, run: () => openHowTo(h.id) });
|
|
767
|
+
}
|
|
768
|
+
for (const s of page?.suggestions ?? []) {
|
|
769
|
+
if (out.length >= max) break;
|
|
770
|
+
if (s.type === "howto" && inApp.has(s.title.toLowerCase())) continue;
|
|
771
|
+
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) });
|
|
772
|
+
else out.push({ icon: "article", title: s.title, sub: s.summary || "Article", run: () => openArticle(s.articleId) });
|
|
773
|
+
}
|
|
774
|
+
return out.slice(0, max);
|
|
775
|
+
}
|
|
776
|
+
function startGuide(keyOrId) {
|
|
777
|
+
setOpen(false);
|
|
778
|
+
options.onStartGuide?.(keyOrId);
|
|
779
|
+
}
|
|
780
|
+
function openChecklist(id) {
|
|
781
|
+
setOpen(false);
|
|
782
|
+
options.onOpenChecklist?.(id);
|
|
783
|
+
}
|
|
784
|
+
function ask(question) {
|
|
785
|
+
go({ kind: "thread", conversationId: null });
|
|
786
|
+
if (!textarea) return;
|
|
787
|
+
textarea.value = question;
|
|
788
|
+
submit();
|
|
789
|
+
}
|
|
790
|
+
function suggestionButton(s) {
|
|
791
|
+
const b = el(doc, "button", "g-m-item");
|
|
792
|
+
b.type = "button";
|
|
793
|
+
b.appendChild(iconTile(s.icon, s.accent));
|
|
794
|
+
const text = el(doc, "span", "g-m-grow");
|
|
795
|
+
text.append(el(doc, "span", "g-m-item-title", s.title), el(doc, "span", "g-m-card-sub g-m-ellipsis", s.sub));
|
|
796
|
+
b.appendChild(text);
|
|
797
|
+
b.addEventListener("click", s.run);
|
|
798
|
+
return b;
|
|
799
|
+
}
|
|
800
|
+
function newsCard(n) {
|
|
801
|
+
const card = el(doc, "button", "g-m-card g-m-news");
|
|
802
|
+
card.type = "button";
|
|
803
|
+
if (n.imageUrl && /^https?:\/\//.test(n.imageUrl)) {
|
|
804
|
+
const img = el(doc, "img", "g-m-news-img");
|
|
805
|
+
img.src = n.imageUrl;
|
|
806
|
+
img.alt = "";
|
|
807
|
+
img.loading = "lazy";
|
|
808
|
+
img.addEventListener("error", () => img.remove());
|
|
809
|
+
card.appendChild(img);
|
|
810
|
+
}
|
|
811
|
+
const body = el(doc, "span", "g-m-news-body");
|
|
812
|
+
const meta = el(doc, "span", "g-m-row g-m-center g-m-news-meta");
|
|
813
|
+
if (n.unread) meta.appendChild(el(doc, "span", "g-m-tag", "New"));
|
|
814
|
+
meta.appendChild(el(doc, "span", "g-m-time", shortDate(n.publishedAt)));
|
|
815
|
+
body.append(meta, el(doc, "span", "g-m-card-title", n.title));
|
|
816
|
+
if (n.body) body.appendChild(el(doc, "span", "g-m-card-sub g-m-clamp", n.body));
|
|
817
|
+
card.appendChild(body);
|
|
818
|
+
card.addEventListener("click", () => startGuide(n.key || n.id));
|
|
819
|
+
return card;
|
|
820
|
+
}
|
|
334
821
|
function renderHome() {
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
822
|
+
head.hidden = true;
|
|
823
|
+
const v = visitor();
|
|
824
|
+
loadPage();
|
|
825
|
+
const scroll = el(doc, "div", "g-m-scroll g-m-home");
|
|
826
|
+
const hero = el(doc, "div", "g-m-hero-bg");
|
|
827
|
+
hero.setAttribute("aria-hidden", "true");
|
|
828
|
+
const inner = el(doc, "div", "g-m-home-inner");
|
|
829
|
+
const top = el(doc, "div", "g-m-hero-top");
|
|
830
|
+
const right = el(doc, "div", "g-m-row g-m-center");
|
|
831
|
+
const team = teamAvatars();
|
|
832
|
+
if (team) right.appendChild(team);
|
|
833
|
+
right.appendChild(closeButton("g-m-hero-close"));
|
|
834
|
+
top.append(brandMark(), right);
|
|
835
|
+
const hello = el(doc, "h2", "g-m-hello");
|
|
836
|
+
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?"));
|
|
837
|
+
inner.append(top, hello);
|
|
838
|
+
inner.appendChild(actionCard({ title: "Send us a message", sub: messageSub(), trail: true, onClick: () => go({ kind: "thread", conversationId: null }) }));
|
|
343
839
|
const recent = conversations[0];
|
|
344
|
-
if (recent)
|
|
345
|
-
|
|
346
|
-
|
|
840
|
+
if (recent) inner.appendChild(conversationCard(recent, recent.status === "closed" && !recent.unread ? "Recent conversation" : "Continue the conversation"));
|
|
841
|
+
if (config.status) inner.appendChild(statusCard(config.status));
|
|
842
|
+
if (cards.progress) progressCards(v).forEach((c) => inner.appendChild(c));
|
|
843
|
+
const suggestions = pageSuggestions(v);
|
|
844
|
+
const questions = cards.suggestions ? (page?.questions ?? []).slice(0, 2) : [];
|
|
845
|
+
const label = v.path ? pageLabel(v.path) : null;
|
|
846
|
+
const fallback = !suggestions.length && !questions.length ? rcExtras(options.resourceCenter?.recommendedGuideIds).slice(0, 3) : [];
|
|
847
|
+
if (suggestions.length || questions.length || fallback.length) {
|
|
848
|
+
const box = el(doc, "div", "g-m-card g-m-static g-m-suggest");
|
|
849
|
+
box.appendChild(el(doc, "div", "g-m-card-title", fallback.length ? "Recommended" : "Suggested for you"));
|
|
850
|
+
if (!fallback.length && label) box.appendChild(el(doc, "div", "g-m-card-sub", `For the ${label} page`));
|
|
851
|
+
const list = el(doc, "div", "g-m-items");
|
|
852
|
+
suggestions.forEach((s) => list.appendChild(suggestionButton(s)));
|
|
853
|
+
questions.forEach((q) => list.appendChild(suggestionButton({ icon: "ask", title: q, sub: config.aiAvailable ? `Ask ${config.name}` : "Ask the team", run: () => ask(q) })));
|
|
854
|
+
fallback.forEach((g) => list.appendChild(suggestionButton({ icon: "play", title: g.name, sub: "Interactive guide", accent: true, run: () => startGuide(g.key || g.id) })));
|
|
855
|
+
box.appendChild(list);
|
|
856
|
+
inner.appendChild(box);
|
|
347
857
|
}
|
|
348
|
-
const
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
858
|
+
const latest = news()[0];
|
|
859
|
+
if (latest) inner.appendChild(newsCard(latest));
|
|
860
|
+
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" }) }));
|
|
861
|
+
scroll.append(hero, inner);
|
|
862
|
+
main.appendChild(scroll);
|
|
863
|
+
}
|
|
864
|
+
function renderHowToView(id, from) {
|
|
865
|
+
const cached = howToCache.get(id);
|
|
866
|
+
renderHead(cached?.title ?? "How-to", () => go(from));
|
|
867
|
+
const scroll = el(doc, "div", "g-m-scroll");
|
|
868
|
+
if (cached) {
|
|
869
|
+
scroll.appendChild(
|
|
870
|
+
renderHowTo(doc, cached, (key) => {
|
|
871
|
+
setOpen(false);
|
|
872
|
+
options.onStartGuide?.(key);
|
|
873
|
+
})
|
|
874
|
+
);
|
|
875
|
+
} else scroll.appendChild(el(doc, "div", "g-m-empty", cached === null ? "This how-to isn't available any more." : "Loading\u2026"));
|
|
353
876
|
main.appendChild(scroll);
|
|
877
|
+
if (cached !== void 0 || !api.getHowTo) return;
|
|
878
|
+
const seq = threadSeq;
|
|
879
|
+
api.getHowTo(id).catch(() => null).then((h) => {
|
|
880
|
+
howToCache.set(id, h);
|
|
881
|
+
if (seq === threadSeq && view.kind === "howto" && view.id === id) render();
|
|
882
|
+
});
|
|
354
883
|
}
|
|
355
884
|
function renderMessagesTab() {
|
|
356
885
|
renderHead("Messages");
|
|
357
886
|
const scroll = el(doc, "div", "g-m-scroll");
|
|
358
887
|
if (!conversations.length) scroll.appendChild(el(doc, "div", "g-m-empty", "No conversations yet."));
|
|
359
888
|
conversations.forEach((c) => scroll.appendChild(conversationCard(c)));
|
|
360
|
-
const start = el(doc, "button", "g-btn g-btn-primary", "Send us a message");
|
|
889
|
+
const start = el(doc, "button", "g-btn g-btn-primary g-m-wide", "Send us a message");
|
|
361
890
|
start.type = "button";
|
|
362
|
-
start.style.width = "100%";
|
|
363
891
|
start.addEventListener("click", () => go({ kind: "thread", conversationId: null }));
|
|
364
892
|
scroll.appendChild(start);
|
|
365
893
|
main.appendChild(scroll);
|
|
366
894
|
}
|
|
895
|
+
function helpSuggestions() {
|
|
896
|
+
const v = visitor();
|
|
897
|
+
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 }));
|
|
898
|
+
if (!items.length) return void 0;
|
|
899
|
+
const label = v.path ? pageLabel(v.path) : null;
|
|
900
|
+
return { title: label ? `For the ${label} page` : "Suggested for you", items };
|
|
901
|
+
}
|
|
367
902
|
function renderHelpTab() {
|
|
368
903
|
renderHead("Help");
|
|
369
904
|
loadArticles();
|
|
905
|
+
loadPage();
|
|
370
906
|
help.render();
|
|
371
907
|
main.appendChild(help.el);
|
|
372
908
|
}
|
|
909
|
+
function renderNewsTab() {
|
|
910
|
+
renderHead("News");
|
|
911
|
+
const scroll = el(doc, "div", "g-m-scroll");
|
|
912
|
+
const items = news();
|
|
913
|
+
if (!items.length) scroll.appendChild(el(doc, "div", "g-m-empty", "No news yet."));
|
|
914
|
+
items.forEach((n) => scroll.appendChild(newsCard(n)));
|
|
915
|
+
main.appendChild(scroll);
|
|
916
|
+
const unread = items.filter((n) => n.unread).map((n) => n.id);
|
|
917
|
+
if (unread.length) options.onNewsSeen?.(unread);
|
|
918
|
+
}
|
|
919
|
+
function loadPage(force = false) {
|
|
920
|
+
const path = visitor().path;
|
|
921
|
+
if (!path || !api.pageContent || !cards.suggestions) {
|
|
922
|
+
page = null;
|
|
923
|
+
pageKey = null;
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
const key = foldPathIds(path).toLowerCase();
|
|
927
|
+
if (!force && key === pageKey && (pageLoading || Date.now() - pageAt < PAGE_TTL_MS)) return;
|
|
928
|
+
if (key !== pageKey) page = null;
|
|
929
|
+
pageKey = key;
|
|
930
|
+
pageLoading = true;
|
|
931
|
+
const seq = ++pageSeq;
|
|
932
|
+
api.pageContent(path).then((res) => {
|
|
933
|
+
if (seq !== pageSeq) return;
|
|
934
|
+
page = res;
|
|
935
|
+
pageAt = Date.now();
|
|
936
|
+
if (!open) return;
|
|
937
|
+
if (view.kind === "tab" && view.tab === "home") render();
|
|
938
|
+
else if (view.kind === "tab" && view.tab === "help") help.render();
|
|
939
|
+
else if (view.kind === "thread" && !messages.length) renderLog();
|
|
940
|
+
}).catch(() => {
|
|
941
|
+
if (seq === pageSeq) pageAt = Date.now();
|
|
942
|
+
}).finally(() => {
|
|
943
|
+
if (seq === pageSeq) pageLoading = false;
|
|
944
|
+
});
|
|
945
|
+
}
|
|
373
946
|
let log = null;
|
|
374
947
|
let foot = null;
|
|
375
948
|
let textarea = null;
|
|
@@ -400,7 +973,7 @@ function renderMessenger(options) {
|
|
|
400
973
|
if (m.status === "pending") bubble.setAttribute("aria-busy", "true");
|
|
401
974
|
bubble.dataset.messageId = m.id;
|
|
402
975
|
wrap.appendChild(bubble);
|
|
403
|
-
if (m.citations.length || m.suggestedGuides.length) {
|
|
976
|
+
if (m.citations.length || m.suggestedGuides.length || m.howTos?.length) {
|
|
404
977
|
const chips = el(doc, "div", "g-m-chips");
|
|
405
978
|
for (const g of m.suggestedGuides) {
|
|
406
979
|
const b = el(doc, "button", "g-m-chip g-m-guide", `\u25B6 Show me: ${g.name}`);
|
|
@@ -417,6 +990,12 @@ function renderMessenger(options) {
|
|
|
417
990
|
b.addEventListener("click", () => openArticle(c.articleId));
|
|
418
991
|
chips.appendChild(b);
|
|
419
992
|
}
|
|
993
|
+
for (const h of m.howTos ?? []) {
|
|
994
|
+
const b = el(doc, "button", "g-m-chip", `\u{1F4CB} ${h.title}`);
|
|
995
|
+
b.type = "button";
|
|
996
|
+
b.addEventListener("click", () => openHowTo(h.howToId));
|
|
997
|
+
chips.appendChild(b);
|
|
998
|
+
}
|
|
420
999
|
wrap.appendChild(chips);
|
|
421
1000
|
}
|
|
422
1001
|
return wrap;
|
|
@@ -426,8 +1005,27 @@ function renderMessenger(options) {
|
|
|
426
1005
|
const atBottom = log.scrollHeight - log.scrollTop - log.clientHeight < 40;
|
|
427
1006
|
log.textContent = "";
|
|
428
1007
|
if (!messages.length) {
|
|
429
|
-
const
|
|
1008
|
+
const v = visitor();
|
|
1009
|
+
const label = v.path ? pageLabel(v.path) : null;
|
|
1010
|
+
if (label && config.aiAvailable && !current) log.appendChild(el(doc, "div", "g-m-context", `Asking from the ${label} page`));
|
|
1011
|
+
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() };
|
|
430
1012
|
log.appendChild(messageNode(intro));
|
|
1013
|
+
const questions = !current && cards.suggestions ? page?.questions ?? [] : [];
|
|
1014
|
+
if (questions.length && !sending) {
|
|
1015
|
+
const chips = el(doc, "div", "g-m-chips g-m-asks");
|
|
1016
|
+
chips.setAttribute("aria-label", "Common questions on this page");
|
|
1017
|
+
for (const q of questions) {
|
|
1018
|
+
const b = el(doc, "button", "g-m-chip g-m-ask", q);
|
|
1019
|
+
b.type = "button";
|
|
1020
|
+
b.addEventListener("click", () => {
|
|
1021
|
+
if (!textarea || sending) return;
|
|
1022
|
+
textarea.value = q;
|
|
1023
|
+
submit();
|
|
1024
|
+
});
|
|
1025
|
+
chips.appendChild(b);
|
|
1026
|
+
}
|
|
1027
|
+
log.appendChild(chips);
|
|
1028
|
+
}
|
|
431
1029
|
}
|
|
432
1030
|
messages.forEach((m) => log.appendChild(messageNode(m)));
|
|
433
1031
|
if (atBottom || sending) log.scrollTop = log.scrollHeight;
|
|
@@ -493,7 +1091,8 @@ function renderMessenger(options) {
|
|
|
493
1091
|
textarea.placeholder = current?.status === "open" ? "Reply to the team\u2026" : "Ask a question\u2026";
|
|
494
1092
|
textarea.setAttribute("aria-label", "Message");
|
|
495
1093
|
textarea.value = draft;
|
|
496
|
-
sendButton = el(doc, "button", "g-m-send"
|
|
1094
|
+
sendButton = el(doc, "button", "g-m-send");
|
|
1095
|
+
sendButton.appendChild(icon(doc, "arrow"));
|
|
497
1096
|
sendButton.type = "submit";
|
|
498
1097
|
sendButton.setAttribute("aria-label", "Send");
|
|
499
1098
|
compose.append(textarea, sendButton);
|
|
@@ -523,9 +1122,14 @@ function renderMessenger(options) {
|
|
|
523
1122
|
textarea = null;
|
|
524
1123
|
sendButton = null;
|
|
525
1124
|
if (view.kind === "thread") renderThread(view.conversationId);
|
|
1125
|
+
else if (view.kind === "howto") renderHowToView(view.id, view.from);
|
|
526
1126
|
else if (view.tab === "home") renderHome();
|
|
527
1127
|
else if (view.tab === "messages") renderMessagesTab();
|
|
528
|
-
else
|
|
1128
|
+
else if (view.tab === "news" && news().length) renderNewsTab();
|
|
1129
|
+
else if (view.tab === "news") {
|
|
1130
|
+
view = { kind: "tab", tab: "home" };
|
|
1131
|
+
renderHome();
|
|
1132
|
+
} else renderHelpTab();
|
|
529
1133
|
renderTabs();
|
|
530
1134
|
}
|
|
531
1135
|
const handlers = (seq) => ({
|
|
@@ -622,13 +1226,14 @@ function renderMessenger(options) {
|
|
|
622
1226
|
threadSeq++;
|
|
623
1227
|
if (threadTimer) clearTimeout(threadTimer);
|
|
624
1228
|
threadTimer = null;
|
|
625
|
-
if (view.kind === "tab" && view.tab === "help" && !(next.kind === "tab" && next.tab === "help")) help.reset();
|
|
1229
|
+
if (view.kind === "tab" && view.tab === "help" && !(next.kind === "tab" && next.tab === "help") && next.kind !== "howto") help.reset();
|
|
626
1230
|
view = next;
|
|
627
1231
|
if (next.kind === "thread") {
|
|
628
1232
|
messages = [];
|
|
629
1233
|
streaming.clear();
|
|
630
1234
|
sending = false;
|
|
631
1235
|
current = next.conversationId ? conversations.find((c) => c.id === next.conversationId) ?? null : null;
|
|
1236
|
+
if (!next.conversationId) loadPage();
|
|
632
1237
|
render();
|
|
633
1238
|
if (next.conversationId) {
|
|
634
1239
|
const seq = threadSeq;
|
|
@@ -667,6 +1272,7 @@ function renderMessenger(options) {
|
|
|
667
1272
|
if (v) {
|
|
668
1273
|
render();
|
|
669
1274
|
void refreshConversations();
|
|
1275
|
+
loadHowTos();
|
|
670
1276
|
if (view.kind === "thread") scheduleThreadPoll();
|
|
671
1277
|
options.onOpen?.();
|
|
672
1278
|
} else {
|
|
@@ -709,6 +1315,16 @@ function renderMessenger(options) {
|
|
|
709
1315
|
setOpen(true);
|
|
710
1316
|
openArticle(id);
|
|
711
1317
|
},
|
|
1318
|
+
refresh() {
|
|
1319
|
+
if (destroyed) return;
|
|
1320
|
+
if (!open) {
|
|
1321
|
+
pageKey = null;
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
loadPage();
|
|
1325
|
+
if (view.kind === "tab") render();
|
|
1326
|
+
else if (!messages.length) renderLog();
|
|
1327
|
+
},
|
|
712
1328
|
destroy() {
|
|
713
1329
|
destroyed = true;
|
|
714
1330
|
if (threadTimer) clearTimeout(threadTimer);
|
|
@@ -721,6 +1337,7 @@ function renderMessenger(options) {
|
|
|
721
1337
|
};
|
|
722
1338
|
}
|
|
723
1339
|
export {
|
|
1340
|
+
personalGreeting,
|
|
724
1341
|
relativeTime,
|
|
725
1342
|
renderMessenger
|
|
726
1343
|
};
|