@guidelytech/guide-runtime 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/messenger.cjs +154 -4
- package/dist/messenger.cjs.map +1 -1
- package/dist/messenger.d.cts +13 -1
- package/dist/messenger.d.ts +13 -1
- package/dist/messenger.js +153 -4
- package/dist/messenger.js.map +1 -1
- package/package.json +2 -2
package/dist/messenger.d.cts
CHANGED
|
@@ -57,6 +57,14 @@ interface IdeasApi {
|
|
|
57
57
|
ideaPortalLink?(): Promise<string>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/** Where the visitor left the launcher. `y`: 0 = the default bottom spot, 1 = as high as it goes. */
|
|
61
|
+
interface LauncherSpot {
|
|
62
|
+
side: "left" | "right";
|
|
63
|
+
y: number;
|
|
64
|
+
}
|
|
65
|
+
/** Reads a stored spot, ignoring anything malformed. */
|
|
66
|
+
declare function parseLauncherSpot(raw: unknown): LauncherSpot | null;
|
|
67
|
+
|
|
60
68
|
/** Callbacks the transport invokes while a message is sent and Scout replies. */
|
|
61
69
|
interface MessengerStreamHandlers {
|
|
62
70
|
/** A new conversation was created (first message only). */
|
|
@@ -159,6 +167,10 @@ interface MessengerOptions {
|
|
|
159
167
|
/** The visitor saw these announcements (News tab opened). */
|
|
160
168
|
onNewsSeen?: (ids: string[]) => void;
|
|
161
169
|
hideLauncher?: boolean;
|
|
170
|
+
/** Where the visitor last dragged the launcher on this device (null = the configured corner). */
|
|
171
|
+
launcherSpot?: LauncherSpot | null;
|
|
172
|
+
/** The visitor dragged the launcher somewhere else (save it for next time). */
|
|
173
|
+
onLauncherMove?: (spot: LauncherSpot) => void;
|
|
162
174
|
/** Poll intervals (ms): open thread, and unread badge while closed. */
|
|
163
175
|
pollMs?: {
|
|
164
176
|
thread: number;
|
|
@@ -188,4 +200,4 @@ declare function relativeTime(iso: string, now?: number): string;
|
|
|
188
200
|
/** Intercom-style messenger: launcher + panel with Home, Messages (chat with Scout / the team) and Help tabs. */
|
|
189
201
|
declare function renderMessenger(options: MessengerOptions): MessengerWidget;
|
|
190
202
|
|
|
191
|
-
export { type IdeaInput, type IdeaListQuery, type IdeaVoteInput, type IdeasApi, type MessengerApi, type MessengerChecklistProgress, type MessengerNewsItem, type MessengerOptions, type MessengerResumeGuide, type MessengerStreamHandlers, type MessengerTab, type MessengerVisitor, type MessengerWidget, personalGreeting, relativeTime, renderMessenger };
|
|
203
|
+
export { type IdeaInput, type IdeaListQuery, type IdeaVoteInput, type IdeasApi, type LauncherSpot, type MessengerApi, type MessengerChecklistProgress, type MessengerNewsItem, type MessengerOptions, type MessengerResumeGuide, type MessengerStreamHandlers, type MessengerTab, type MessengerVisitor, type MessengerWidget, parseLauncherSpot, personalGreeting, relativeTime, renderMessenger };
|
package/dist/messenger.d.ts
CHANGED
|
@@ -57,6 +57,14 @@ interface IdeasApi {
|
|
|
57
57
|
ideaPortalLink?(): Promise<string>;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
/** Where the visitor left the launcher. `y`: 0 = the default bottom spot, 1 = as high as it goes. */
|
|
61
|
+
interface LauncherSpot {
|
|
62
|
+
side: "left" | "right";
|
|
63
|
+
y: number;
|
|
64
|
+
}
|
|
65
|
+
/** Reads a stored spot, ignoring anything malformed. */
|
|
66
|
+
declare function parseLauncherSpot(raw: unknown): LauncherSpot | null;
|
|
67
|
+
|
|
60
68
|
/** Callbacks the transport invokes while a message is sent and Scout replies. */
|
|
61
69
|
interface MessengerStreamHandlers {
|
|
62
70
|
/** A new conversation was created (first message only). */
|
|
@@ -159,6 +167,10 @@ interface MessengerOptions {
|
|
|
159
167
|
/** The visitor saw these announcements (News tab opened). */
|
|
160
168
|
onNewsSeen?: (ids: string[]) => void;
|
|
161
169
|
hideLauncher?: boolean;
|
|
170
|
+
/** Where the visitor last dragged the launcher on this device (null = the configured corner). */
|
|
171
|
+
launcherSpot?: LauncherSpot | null;
|
|
172
|
+
/** The visitor dragged the launcher somewhere else (save it for next time). */
|
|
173
|
+
onLauncherMove?: (spot: LauncherSpot) => void;
|
|
162
174
|
/** Poll intervals (ms): open thread, and unread badge while closed. */
|
|
163
175
|
pollMs?: {
|
|
164
176
|
thread: number;
|
|
@@ -188,4 +200,4 @@ declare function relativeTime(iso: string, now?: number): string;
|
|
|
188
200
|
/** Intercom-style messenger: launcher + panel with Home, Messages (chat with Scout / the team) and Help tabs. */
|
|
189
201
|
declare function renderMessenger(options: MessengerOptions): MessengerWidget;
|
|
190
202
|
|
|
191
|
-
export { type IdeaInput, type IdeaListQuery, type IdeaVoteInput, type IdeasApi, type MessengerApi, type MessengerChecklistProgress, type MessengerNewsItem, type MessengerOptions, type MessengerResumeGuide, type MessengerStreamHandlers, type MessengerTab, type MessengerVisitor, type MessengerWidget, personalGreeting, relativeTime, renderMessenger };
|
|
203
|
+
export { type IdeaInput, type IdeaListQuery, type IdeaVoteInput, type IdeasApi, type LauncherSpot, type MessengerApi, type MessengerChecklistProgress, type MessengerNewsItem, type MessengerOptions, type MessengerResumeGuide, type MessengerStreamHandlers, type MessengerTab, type MessengerVisitor, type MessengerWidget, parseLauncherSpot, personalGreeting, relativeTime, renderMessenger };
|
package/dist/messenger.js
CHANGED
|
@@ -1247,6 +1247,107 @@ function createIdeas(o) {
|
|
|
1247
1247
|
};
|
|
1248
1248
|
}
|
|
1249
1249
|
|
|
1250
|
+
// src/launcher-drag.ts
|
|
1251
|
+
var DRAG_THRESHOLD = 6;
|
|
1252
|
+
var MARGIN = 16;
|
|
1253
|
+
function parseLauncherSpot(raw) {
|
|
1254
|
+
let v = raw;
|
|
1255
|
+
if (typeof v === "string") {
|
|
1256
|
+
try {
|
|
1257
|
+
v = JSON.parse(v);
|
|
1258
|
+
} catch {
|
|
1259
|
+
return null;
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1262
|
+
if (!v || typeof v !== "object") return null;
|
|
1263
|
+
const { side, y } = v;
|
|
1264
|
+
if (side !== "left" && side !== "right" || typeof y !== "number" || !Number.isFinite(y)) return null;
|
|
1265
|
+
return { side, y: Math.min(1, Math.max(0, y)) };
|
|
1266
|
+
}
|
|
1267
|
+
function maxLauncherBottom(viewportHeight, size) {
|
|
1268
|
+
return Math.max(0, viewportHeight - size - MARGIN * 2);
|
|
1269
|
+
}
|
|
1270
|
+
function snapLauncher(drop, viewport, size) {
|
|
1271
|
+
const side = drop.left + size / 2 < viewport.width / 2 ? "left" : "right";
|
|
1272
|
+
const max = maxLauncherBottom(viewport.height, size);
|
|
1273
|
+
const bottom = Math.min(max, Math.max(0, viewport.height - MARGIN - size - drop.top));
|
|
1274
|
+
return { side, y: max ? bottom / max : 0 };
|
|
1275
|
+
}
|
|
1276
|
+
function enableLauncherDrag({ launcher, onMove }) {
|
|
1277
|
+
const win = launcher.ownerDocument.defaultView ?? window;
|
|
1278
|
+
let start = null;
|
|
1279
|
+
let dragging = false;
|
|
1280
|
+
let swallowClickUntil = 0;
|
|
1281
|
+
const onDown = (e) => {
|
|
1282
|
+
if (e.button !== 0) return;
|
|
1283
|
+
const r = launcher.getBoundingClientRect();
|
|
1284
|
+
start = { id: e.pointerId, x: e.clientX, y: e.clientY, left: r.left, top: r.top };
|
|
1285
|
+
dragging = false;
|
|
1286
|
+
try {
|
|
1287
|
+
launcher.setPointerCapture(e.pointerId);
|
|
1288
|
+
} catch {
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1291
|
+
const onMovePointer = (e) => {
|
|
1292
|
+
if (!start || e.pointerId !== start.id) return;
|
|
1293
|
+
const dx = e.clientX - start.x;
|
|
1294
|
+
const dy = e.clientY - start.y;
|
|
1295
|
+
if (!dragging) {
|
|
1296
|
+
if (Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
|
|
1297
|
+
dragging = true;
|
|
1298
|
+
launcher.classList.add("g-m-dragging");
|
|
1299
|
+
}
|
|
1300
|
+
e.preventDefault();
|
|
1301
|
+
const size = launcher.offsetWidth || 56;
|
|
1302
|
+
const left = Math.min(win.innerWidth - size - MARGIN, Math.max(MARGIN, start.left + dx));
|
|
1303
|
+
const top = Math.min(win.innerHeight - size - MARGIN, Math.max(MARGIN, start.top + dy));
|
|
1304
|
+
Object.assign(launcher.style, { left: `${left}px`, top: `${top}px`, right: "auto", bottom: "auto", margin: "0" });
|
|
1305
|
+
};
|
|
1306
|
+
const onUp = (e) => {
|
|
1307
|
+
if (!start || e.pointerId !== start.id) return;
|
|
1308
|
+
start = null;
|
|
1309
|
+
if (!dragging) return;
|
|
1310
|
+
dragging = false;
|
|
1311
|
+
swallowClickUntil = Date.now() + 400;
|
|
1312
|
+
const before = launcher.getBoundingClientRect();
|
|
1313
|
+
launcher.classList.remove("g-m-dragging");
|
|
1314
|
+
Object.assign(launcher.style, { left: "", top: "", right: "", margin: "" });
|
|
1315
|
+
onMove(snapLauncher({ left: before.left, top: before.top }, { width: win.innerWidth, height: win.innerHeight }, before.width || 56));
|
|
1316
|
+
const after = launcher.getBoundingClientRect();
|
|
1317
|
+
const dx = before.left - after.left;
|
|
1318
|
+
const dy = before.top - after.top;
|
|
1319
|
+
if (!dx && !dy) return;
|
|
1320
|
+
launcher.style.transition = "none";
|
|
1321
|
+
launcher.style.transform = `translate(${dx}px, ${dy}px)`;
|
|
1322
|
+
void launcher.offsetWidth;
|
|
1323
|
+
launcher.style.transition = "";
|
|
1324
|
+
launcher.classList.add("g-m-snapping");
|
|
1325
|
+
launcher.style.transform = "";
|
|
1326
|
+
const done = () => launcher.classList.remove("g-m-snapping");
|
|
1327
|
+
launcher.addEventListener("transitionend", done, { once: true });
|
|
1328
|
+
win.setTimeout(done, 400);
|
|
1329
|
+
};
|
|
1330
|
+
const onClick = (e) => {
|
|
1331
|
+
if (Date.now() < swallowClickUntil) {
|
|
1332
|
+
swallowClickUntil = 0;
|
|
1333
|
+
e.preventDefault();
|
|
1334
|
+
e.stopImmediatePropagation();
|
|
1335
|
+
}
|
|
1336
|
+
};
|
|
1337
|
+
launcher.addEventListener("pointerdown", onDown);
|
|
1338
|
+
launcher.addEventListener("pointermove", onMovePointer);
|
|
1339
|
+
launcher.addEventListener("pointerup", onUp);
|
|
1340
|
+
launcher.addEventListener("pointercancel", onUp);
|
|
1341
|
+
launcher.addEventListener("click", onClick, true);
|
|
1342
|
+
return () => {
|
|
1343
|
+
launcher.removeEventListener("pointerdown", onDown);
|
|
1344
|
+
launcher.removeEventListener("pointermove", onMovePointer);
|
|
1345
|
+
launcher.removeEventListener("pointerup", onUp);
|
|
1346
|
+
launcher.removeEventListener("pointercancel", onUp);
|
|
1347
|
+
launcher.removeEventListener("click", onClick, true);
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1250
1351
|
// src/messenger-styles.ts
|
|
1251
1352
|
var MESSENGER_STYLES = `
|
|
1252
1353
|
:host([data-g-messenger="bottom-right"]) .g-launcher.g-pos-bottom-right{right:72px}
|
|
@@ -1255,9 +1356,13 @@ var MESSENGER_STYLES = `
|
|
|
1255
1356
|
.g-m-launcher:hover{transform:scale(1.05)}
|
|
1256
1357
|
.g-m-launcher:focus-visible{outline:2px solid var(--g-primary);outline-offset:3px}
|
|
1257
1358
|
.g-m-launcher svg{width:26px;height:26px;fill:currentColor}
|
|
1359
|
+
.g-m-launcher{touch-action:none;-webkit-user-select:none;user-select:none}
|
|
1360
|
+
.g-m-launcher.g-m-dragging{transition:none;cursor:grabbing;transform:scale(1.08)}
|
|
1361
|
+
.g-m-launcher.g-m-snapping{transition:transform .3s cubic-bezier(.2,.8,.2,1)}
|
|
1258
1362
|
.g-m-badge{position:absolute;top:-2px;right:-2px;min-width:20px;height:20px;padding:0 5px;border-radius:999px;background:#e11d48;color:#fff;font-size:11px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;border:2px solid var(--g-bg)}
|
|
1259
|
-
.g-messenger{bottom:76px;height:min(640px,calc(100vh - 110px));transition:width .2s ease,height .2s ease,max-height .2s ease}
|
|
1260
|
-
.g-messenger.g-m-expanded{width:min(720px,calc(100vw - 32px));height:calc(100vh - 110px);max-height:calc(100vh - 110px)}
|
|
1363
|
+
.g-messenger{bottom:var(--g-m-bottom,76px);height:min(640px,calc(100vh - var(--g-m-reserve,110px)));transition:width .2s ease,height .2s ease,max-height .2s ease}
|
|
1364
|
+
.g-messenger.g-m-expanded{width:min(720px,calc(100vw - 32px));height:calc(100vh - var(--g-m-reserve,110px));max-height:calc(100vh - var(--g-m-reserve,110px))}
|
|
1365
|
+
.g-messenger.g-m-below{bottom:auto;top:var(--g-m-top)}
|
|
1261
1366
|
.g-m-expanded .g-rc-article,.g-m-expanded .g-m-scroll{padding:24px clamp(20px,5vw,40px)}
|
|
1262
1367
|
.g-m-expanded .g-rc-article>.g-title{font-size:24px;line-height:1.25;margin-bottom:12px}
|
|
1263
1368
|
.g-m-expanded .g-rc-article .g-body{font-size:15px}
|
|
@@ -1309,7 +1414,7 @@ var MESSENGER_STYLES = `
|
|
|
1309
1414
|
.g-m-typing span{width:6px;height:6px;border-radius:999px;background:var(--g-muted);animation:g-m-bounce 1s infinite}
|
|
1310
1415
|
.g-m-typing span:nth-child(2){animation-delay:.15s}.g-m-typing span:nth-child(3){animation-delay:.3s}
|
|
1311
1416
|
@keyframes g-m-bounce{0%,60%,100%{opacity:.3;transform:translateY(0)}30%{opacity:1;transform:translateY(-3px)}}
|
|
1312
|
-
@media (prefers-reduced-motion: reduce){.g-m-typing span{animation:none}.g-m-launcher{transition:none}}
|
|
1417
|
+
@media (prefers-reduced-motion: reduce){.g-m-typing span{animation:none}.g-m-launcher,.g-m-launcher.g-m-snapping{transition:none}}
|
|
1313
1418
|
.g-m-chips{display:flex;flex-direction:column;gap:6px;margin-top:2px}
|
|
1314
1419
|
.g-m-chip{display:flex;align-items:center;gap:8px;text-align:left;border:1px solid var(--g-border);background:var(--g-bg);color:var(--g-text);border-radius:10px;padding:8px 10px;font:inherit;font-size:13px;cursor:pointer}
|
|
1315
1420
|
.g-m-chip:hover{background:var(--g-subtle)}
|
|
@@ -1658,7 +1763,6 @@ function renderMessenger(options) {
|
|
|
1658
1763
|
const pos = `g-pos-${config.launcher.position}`;
|
|
1659
1764
|
const rootNode = scope.getRootNode();
|
|
1660
1765
|
const shadowHost = "host" in rootNode ? rootNode.host : null;
|
|
1661
|
-
shadowHost?.setAttribute("data-g-messenger", config.launcher.position);
|
|
1662
1766
|
const root = el(doc, "div", "g-messenger-root");
|
|
1663
1767
|
const launcher = el(doc, "button", `g-m-launcher ${pos}`);
|
|
1664
1768
|
launcher.type = "button";
|
|
@@ -1670,6 +1774,47 @@ function renderMessenger(options) {
|
|
|
1670
1774
|
launcher.appendChild(badge);
|
|
1671
1775
|
if (options.hideLauncher) launcher.hidden = true;
|
|
1672
1776
|
const panel = el(doc, "div", `g-panel g-messenger ${pos}`);
|
|
1777
|
+
let spot = options.launcherSpot ?? null;
|
|
1778
|
+
const win = doc.defaultView ?? window;
|
|
1779
|
+
function placeLauncher() {
|
|
1780
|
+
const side = spot?.side ?? (config.launcher.position === "bottom-left" ? "left" : "right");
|
|
1781
|
+
for (const node of [launcher, panel]) {
|
|
1782
|
+
node.classList.toggle("g-pos-bottom-left", side === "left");
|
|
1783
|
+
node.classList.toggle("g-pos-bottom-right", side === "right");
|
|
1784
|
+
}
|
|
1785
|
+
const size = launcher.offsetWidth || 56;
|
|
1786
|
+
const bottom = spot && !options.hideLauncher ? Math.round(spot.y * maxLauncherBottom(win.innerHeight, size)) : 0;
|
|
1787
|
+
launcher.style.bottom = bottom ? `${bottom}px` : "";
|
|
1788
|
+
if (bottom < size + 16) shadowHost?.setAttribute("data-g-messenger", `bottom-${side}`);
|
|
1789
|
+
else shadowHost?.removeAttribute("data-g-messenger");
|
|
1790
|
+
const launcherTop = win.innerHeight - 16 - size - bottom;
|
|
1791
|
+
const below = bottom > 0 && launcherTop < 480 && bottom > launcherTop;
|
|
1792
|
+
panel.classList.toggle("g-m-below", below);
|
|
1793
|
+
if (!bottom) {
|
|
1794
|
+
panel.style.removeProperty("--g-m-bottom");
|
|
1795
|
+
panel.style.removeProperty("--g-m-top");
|
|
1796
|
+
panel.style.removeProperty("--g-m-reserve");
|
|
1797
|
+
} else if (below) {
|
|
1798
|
+
const top = launcherTop + size - 12;
|
|
1799
|
+
panel.style.setProperty("--g-m-top", `${top}px`);
|
|
1800
|
+
panel.style.setProperty("--g-m-reserve", `${top + 40}px`);
|
|
1801
|
+
} else {
|
|
1802
|
+
panel.style.setProperty("--g-m-bottom", `${bottom + 76}px`);
|
|
1803
|
+
panel.style.setProperty("--g-m-reserve", `${bottom + 110}px`);
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
const stopDrag = enableLauncherDrag({
|
|
1807
|
+
launcher,
|
|
1808
|
+
onMove(next) {
|
|
1809
|
+
spot = next;
|
|
1810
|
+
placeLauncher();
|
|
1811
|
+
options.onLauncherMove?.(next);
|
|
1812
|
+
}
|
|
1813
|
+
});
|
|
1814
|
+
const onResize = () => {
|
|
1815
|
+
if (spot) placeLauncher();
|
|
1816
|
+
};
|
|
1817
|
+
win.addEventListener("resize", onResize);
|
|
1673
1818
|
panel.setAttribute("role", "dialog");
|
|
1674
1819
|
panel.setAttribute("aria-label", config.name);
|
|
1675
1820
|
panel.hidden = true;
|
|
@@ -1680,6 +1825,7 @@ function renderMessenger(options) {
|
|
|
1680
1825
|
panel.append(head, main, tabs);
|
|
1681
1826
|
root.append(launcher, panel);
|
|
1682
1827
|
scope.appendChild(root);
|
|
1828
|
+
placeLauncher();
|
|
1683
1829
|
function withRcText(items, rc, apply) {
|
|
1684
1830
|
if (!rc?.length) return items;
|
|
1685
1831
|
const byId = new Map(rc.map((x) => [x.id, x]));
|
|
@@ -2766,6 +2912,8 @@ function renderMessenger(options) {
|
|
|
2766
2912
|
if (threadTimer) clearTimeout(threadTimer);
|
|
2767
2913
|
if (backgroundTimer) clearInterval(backgroundTimer);
|
|
2768
2914
|
panel.removeEventListener("keydown", onKey);
|
|
2915
|
+
stopDrag();
|
|
2916
|
+
win.removeEventListener("resize", onResize);
|
|
2769
2917
|
help.destroy();
|
|
2770
2918
|
ideas.destroy();
|
|
2771
2919
|
shadowHost?.removeAttribute("data-g-messenger");
|
|
@@ -2774,6 +2922,7 @@ function renderMessenger(options) {
|
|
|
2774
2922
|
};
|
|
2775
2923
|
}
|
|
2776
2924
|
export {
|
|
2925
|
+
parseLauncherSpot,
|
|
2777
2926
|
personalGreeting,
|
|
2778
2927
|
relativeTime,
|
|
2779
2928
|
renderMessenger
|