@oyehello/chat 0.5.2 → 0.6.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/LICENSE +1 -1
- package/README.md +7 -5
- package/dist/index.cjs +116 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.global.js +116 -8
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +116 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -192,6 +192,7 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
192
192
|
private idleTimer;
|
|
193
193
|
private idleReset;
|
|
194
194
|
private uninstallSpaHooks;
|
|
195
|
+
private unwatchPageTheme;
|
|
195
196
|
private oc;
|
|
196
197
|
private bodyEl;
|
|
197
198
|
private textareaEl;
|
|
@@ -209,6 +210,8 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
209
210
|
private applyBrandLogo;
|
|
210
211
|
disconnectedCallback(): void;
|
|
211
212
|
registerTools(handlers: Record<string, ChatToolHandler>): void;
|
|
213
|
+
/** Open the chat window, exactly as a launcher click does. No-op if already open or not rendered. */
|
|
214
|
+
open(): void;
|
|
212
215
|
private key;
|
|
213
216
|
private get attrSuggestions();
|
|
214
217
|
private get chipLabels();
|
package/dist/index.d.ts
CHANGED
|
@@ -192,6 +192,7 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
192
192
|
private idleTimer;
|
|
193
193
|
private idleReset;
|
|
194
194
|
private uninstallSpaHooks;
|
|
195
|
+
private unwatchPageTheme;
|
|
195
196
|
private oc;
|
|
196
197
|
private bodyEl;
|
|
197
198
|
private textareaEl;
|
|
@@ -209,6 +210,8 @@ declare class OyehelloChatElement extends HTMLElement {
|
|
|
209
210
|
private applyBrandLogo;
|
|
210
211
|
disconnectedCallback(): void;
|
|
211
212
|
registerTools(handlers: Record<string, ChatToolHandler>): void;
|
|
213
|
+
/** Open the chat window, exactly as a launcher click does. No-op if already open or not rendered. */
|
|
214
|
+
open(): void;
|
|
212
215
|
private key;
|
|
213
216
|
private get attrSuggestions();
|
|
214
217
|
private get chipLabels();
|
package/dist/index.global.js
CHANGED
|
@@ -61,6 +61,16 @@ var OyehelloChat = (function (exports) {
|
|
|
61
61
|
});
|
|
62
62
|
if (!res.ok) throw await asError(res, res.status === 401 ? "session_expired" : void 0);
|
|
63
63
|
}
|
|
64
|
+
async function getHistory(opts) {
|
|
65
|
+
const res = await opts.fetchImpl(`${opts.baseUrl}/chat/sessions/${opts.sessionId}/messages`, {
|
|
66
|
+
method: "GET",
|
|
67
|
+
credentials: "omit",
|
|
68
|
+
headers: { Authorization: `Bearer ${opts.sessionToken}` }
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok) throw await asError(res, res.status === 401 ? "session_expired" : void 0);
|
|
71
|
+
const body = await res.json();
|
|
72
|
+
return body.messages;
|
|
73
|
+
}
|
|
64
74
|
async function postPageViews(opts) {
|
|
65
75
|
const res = await opts.fetchImpl(`${opts.baseUrl}/chat/sessions/${opts.sessionId}/page-views`, {
|
|
66
76
|
method: "POST",
|
|
@@ -317,17 +327,21 @@ var OyehelloChat = (function (exports) {
|
|
|
317
327
|
return false;
|
|
318
328
|
}
|
|
319
329
|
};
|
|
330
|
+
var AFTER_RESULT = "__ohAfterResult";
|
|
320
331
|
var BUILTIN_TOOL_HANDLERS = {
|
|
321
332
|
navigate: (args) => {
|
|
322
333
|
const path = String(args.path ?? "");
|
|
323
334
|
if (!isSafeRelativePath(path)) return { ok: false, error: "unsafe navigate path" };
|
|
324
|
-
window.location.
|
|
325
|
-
|
|
335
|
+
const here = window.location.pathname + window.location.search + window.location.hash;
|
|
336
|
+
const target = new URL(path, window.location.href);
|
|
337
|
+
if (target.pathname + target.search + target.hash === here) return { ok: true, note: "already on this page" };
|
|
338
|
+
return { ok: true, [AFTER_RESULT]: () => window.location.assign(path) };
|
|
326
339
|
},
|
|
327
340
|
openUrl: (args) => {
|
|
328
341
|
const url = String(args.url ?? "");
|
|
329
342
|
if (!isSafeHttpUrl(url)) return { ok: false, error: "unsafe url" };
|
|
330
|
-
|
|
343
|
+
const sameHost = new URL(url, window.location.origin).hostname === window.location.hostname;
|
|
344
|
+
window.open(url, sameHost ? "_self" : "_blank", "noopener,noreferrer");
|
|
331
345
|
return { ok: true };
|
|
332
346
|
}
|
|
333
347
|
};
|
|
@@ -439,6 +453,23 @@ var OyehelloChat = (function (exports) {
|
|
|
439
453
|
} catch {
|
|
440
454
|
}
|
|
441
455
|
};
|
|
456
|
+
const callsKey = `oyehello-chat-calls:${options.agentId}`;
|
|
457
|
+
const handledCallIds = () => {
|
|
458
|
+
try {
|
|
459
|
+
const raw = store2?.getItem(callsKey);
|
|
460
|
+
const v2 = raw ? JSON.parse(raw) : null;
|
|
461
|
+
return v2 && session && v2.sessionId === session.sessionId && Array.isArray(v2.ids) ? v2.ids : [];
|
|
462
|
+
} catch {
|
|
463
|
+
return [];
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
const rememberHandledCall = (callId) => {
|
|
467
|
+
if (!session) return;
|
|
468
|
+
try {
|
|
469
|
+
store2?.setItem(callsKey, JSON.stringify({ sessionId: session.sessionId, ids: [...handledCallIds(), callId].slice(-50) }));
|
|
470
|
+
} catch {
|
|
471
|
+
}
|
|
472
|
+
};
|
|
442
473
|
const emit = (e) => {
|
|
443
474
|
for (const l of listeners) l(e);
|
|
444
475
|
};
|
|
@@ -487,8 +518,9 @@ var OyehelloChat = (function (exports) {
|
|
|
487
518
|
} else if (ev.type === "error") {
|
|
488
519
|
emit({ type: "error", error: new Error(ev.message) });
|
|
489
520
|
} else if (ev.type === "tool_call") {
|
|
490
|
-
if (seenCallIds.has(ev.callId)) return;
|
|
521
|
+
if (seenCallIds.has(ev.callId) || handledCallIds().includes(ev.callId)) return;
|
|
491
522
|
seenCallIds.add(ev.callId);
|
|
523
|
+
rememberHandledCall(ev.callId);
|
|
492
524
|
const handler = toolHandlers.get(ev.name) ?? BUILTIN_TOOL_HANDLERS[ev.name];
|
|
493
525
|
void (async () => {
|
|
494
526
|
if (!session) return;
|
|
@@ -498,8 +530,16 @@ var OyehelloChat = (function (exports) {
|
|
|
498
530
|
return;
|
|
499
531
|
}
|
|
500
532
|
try {
|
|
501
|
-
const
|
|
533
|
+
const raw = await handler(ev.args);
|
|
534
|
+
let after = null;
|
|
535
|
+
let out = raw;
|
|
536
|
+
if (raw && typeof raw === "object" && typeof raw[AFTER_RESULT] === "function") {
|
|
537
|
+
const { [AFTER_RESULT]: run, ...rest } = raw;
|
|
538
|
+
after = run;
|
|
539
|
+
out = rest;
|
|
540
|
+
}
|
|
502
541
|
await postToolResult({ ...base, ok: true, result: typeof out === "string" ? out : JSON.stringify(out ?? { ok: true }) });
|
|
542
|
+
after?.();
|
|
503
543
|
} catch (err) {
|
|
504
544
|
await postToolResult({ ...base, ok: false, result: err.message });
|
|
505
545
|
}
|
|
@@ -581,6 +621,16 @@ var OyehelloChat = (function (exports) {
|
|
|
581
621
|
if (resumed) {
|
|
582
622
|
session = resumed;
|
|
583
623
|
emit({ type: "agent", agent: resumed.agent });
|
|
624
|
+
try {
|
|
625
|
+
const history2 = await getHistory({ baseUrl: options.baseUrl, sessionId: resumed.sessionId, sessionToken: resumed.sessionToken, fetchImpl });
|
|
626
|
+
for (const h of history2) {
|
|
627
|
+
const turnId = h.role === "assistant" || h.role === "human" ? h.turnId : void 0;
|
|
628
|
+
messages.push({ id: uid("m"), role: h.role, text: h.text, streaming: false, turnId });
|
|
629
|
+
if (h.turnId) seenTurnIds.add(h.turnId);
|
|
630
|
+
}
|
|
631
|
+
emitMessages();
|
|
632
|
+
} catch {
|
|
633
|
+
}
|
|
584
634
|
await openStream();
|
|
585
635
|
scheduleRefresh();
|
|
586
636
|
flushJourney();
|
|
@@ -722,6 +772,44 @@ var OyehelloChat = (function (exports) {
|
|
|
722
772
|
};
|
|
723
773
|
}
|
|
724
774
|
|
|
775
|
+
// src/page-theme.ts
|
|
776
|
+
var WATCHED = ["class", "data-theme", "data-mode", "style"];
|
|
777
|
+
function themeOf(node) {
|
|
778
|
+
for (const attr of ["data-theme", "data-mode"]) {
|
|
779
|
+
const v2 = node.getAttribute(attr);
|
|
780
|
+
if (v2 === "dark" || v2 === "light") return v2;
|
|
781
|
+
}
|
|
782
|
+
if (node.classList.contains("dark")) return "dark";
|
|
783
|
+
if (node.classList.contains("light")) return "light";
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
function resolvePageTheme(el) {
|
|
787
|
+
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
788
|
+
const t = themeOf(node);
|
|
789
|
+
if (t) return t;
|
|
790
|
+
}
|
|
791
|
+
const scheme = (document.documentElement.style.colorScheme || getComputedStyle(document.documentElement).colorScheme || "").trim();
|
|
792
|
+
return scheme === "dark" || scheme === "light" ? scheme : null;
|
|
793
|
+
}
|
|
794
|
+
function watchPageTheme(el, onChange) {
|
|
795
|
+
let last;
|
|
796
|
+
const check = () => {
|
|
797
|
+
const t = resolvePageTheme(el);
|
|
798
|
+
if (t !== last) {
|
|
799
|
+
last = t;
|
|
800
|
+
onChange(t);
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
check();
|
|
804
|
+
if (typeof MutationObserver === "undefined") return () => {
|
|
805
|
+
};
|
|
806
|
+
const mo = new MutationObserver(check);
|
|
807
|
+
for (let node = el.parentElement; node; node = node.parentElement) {
|
|
808
|
+
mo.observe(node, { attributes: true, attributeFilter: WATCHED });
|
|
809
|
+
}
|
|
810
|
+
return () => mo.disconnect();
|
|
811
|
+
}
|
|
812
|
+
|
|
725
813
|
// ../../node_modules/marked/lib/marked.esm.js
|
|
726
814
|
function A() {
|
|
727
815
|
return { async: false, breaks: false, extensions: null, gfm: true, hooks: null, pedantic: false, renderer: null, silent: false, tokenizer: null, walkTokens: null };
|
|
@@ -3683,11 +3771,15 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3683
3771
|
* body children (oyehello-chat { display:none }) can't silently kill the widget. Also pinned
|
|
3684
3772
|
* inline with !important at mount (see element.ts) to beat an external !important rule. */
|
|
3685
3773
|
display: block; visibility: visible; opacity: 1;
|
|
3774
|
+
/* The closed panel still takes up its full size in the layout (it fades, it is not removed), so
|
|
3775
|
+
* the host box covers ~384x620px of the page. The host and its wrapper must not catch clicks;
|
|
3776
|
+
* only the visible parts opt back in (launcher, teaser while shown, panel while open). */
|
|
3777
|
+
pointer-events: none;
|
|
3686
3778
|
font-family: 'Geist', system-ui, -apple-system, 'Helvetica Neue', sans-serif;
|
|
3687
3779
|
-webkit-font-smoothing: antialiased;
|
|
3688
3780
|
}
|
|
3689
3781
|
@media (prefers-color-scheme: dark) {
|
|
3690
|
-
:host(:not([theme="light"])) {
|
|
3782
|
+
:host(:not([theme="light"]):not([data-oc-page-theme="light"])) {
|
|
3691
3783
|
--oc-bg: #000000; --oc-bg-sunk: #000000;
|
|
3692
3784
|
--oc-fg: #FFFFFF; --oc-fg-muted: rgba(255,255,255,0.68); --oc-fg-dim: rgba(255,255,255,0.42);
|
|
3693
3785
|
--oc-border: rgba(255,255,255,0.09);
|
|
@@ -3697,7 +3789,9 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3697
3789
|
--oc-glass-border: rgba(255,255,255,0.12);
|
|
3698
3790
|
}
|
|
3699
3791
|
}
|
|
3700
|
-
|
|
3792
|
+
/* Explicit dark, or auto on a page that declares dark (data-oc-page-theme, set by page-theme.ts). The OS rule above
|
|
3793
|
+
* only applies when the page declares nothing; a page that declares light keeps the light base. */
|
|
3794
|
+
:host([theme="dark"]), :host(:not([theme="light"])[data-oc-page-theme="dark"]) {
|
|
3701
3795
|
--oc-bg: #000000; --oc-bg-sunk: #000000;
|
|
3702
3796
|
--oc-fg: #FFFFFF; --oc-fg-muted: rgba(255,255,255,0.68); --oc-fg-dim: rgba(255,255,255,0.42);
|
|
3703
3797
|
--oc-border: rgba(255,255,255,0.09);
|
|
@@ -3709,7 +3803,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3709
3803
|
* { box-sizing: border-box; }
|
|
3710
3804
|
button { font-family: inherit; }
|
|
3711
3805
|
|
|
3712
|
-
.oc { display: flex; flex-direction: column; align-items: flex-end; gap: 14px; }
|
|
3806
|
+
.oc { display: flex; flex-direction: column; align-items: flex-end; gap: 14px; pointer-events: none; }
|
|
3713
3807
|
|
|
3714
3808
|
/* \u2500\u2500 Launcher \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
3715
3809
|
.launcher {
|
|
@@ -3718,6 +3812,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
3718
3812
|
box-shadow: 0 8px 22px -8px color-mix(in oklab, var(--oc-ac) 70%, transparent), 0 18px 48px -18px rgba(0,0,0,0.6);
|
|
3719
3813
|
color: #fff; display: flex; align-items: center; justify-content: center; cursor: pointer;
|
|
3720
3814
|
transition: transform .26s var(--oc-bounce), filter .2s var(--oc-ease); align-self: flex-end;
|
|
3815
|
+
pointer-events: auto;
|
|
3721
3816
|
}
|
|
3722
3817
|
.launcher:hover { transform: scale(1.06); }
|
|
3723
3818
|
.launcher:active { transform: scale(0.93); }
|
|
@@ -4085,6 +4180,7 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4085
4180
|
// Uninstaller for the SPA history-navigation hooks (journey tracking), installed in
|
|
4086
4181
|
// connectedCallback and torn down in disconnectedCallback alongside everything else.
|
|
4087
4182
|
uninstallSpaHooks = null;
|
|
4183
|
+
unwatchPageTheme = null;
|
|
4088
4184
|
// ── shadow refs ──
|
|
4089
4185
|
oc;
|
|
4090
4186
|
bodyEl;
|
|
@@ -4118,6 +4214,11 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4118
4214
|
this.uninstallSpaHooks = null;
|
|
4119
4215
|
}
|
|
4120
4216
|
this.renderShell();
|
|
4217
|
+
this.unwatchPageTheme?.();
|
|
4218
|
+
this.unwatchPageTheme = watchPageTheme(this, (t) => {
|
|
4219
|
+
if (t) this.setAttribute("data-oc-page-theme", t);
|
|
4220
|
+
else this.removeAttribute("data-oc-page-theme");
|
|
4221
|
+
});
|
|
4121
4222
|
this.style.setProperty("display", "block", "important");
|
|
4122
4223
|
this.style.setProperty("visibility", "visible", "important");
|
|
4123
4224
|
this.style.setProperty("opacity", "1", "important");
|
|
@@ -4228,6 +4329,8 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4228
4329
|
this.teardownIdle();
|
|
4229
4330
|
this.uninstallSpaHooks?.();
|
|
4230
4331
|
this.uninstallSpaHooks = null;
|
|
4332
|
+
this.unwatchPageTheme?.();
|
|
4333
|
+
this.unwatchPageTheme = null;
|
|
4231
4334
|
for (const t of this.timers) clearTimeout(t);
|
|
4232
4335
|
for (const url of this.imagePreviewCache.values()) {
|
|
4233
4336
|
if (typeof URL.revokeObjectURL === "function") URL.revokeObjectURL(url);
|
|
@@ -4238,6 +4341,11 @@ Please report this to https://github.com/markedjs/marked.`, e) {
|
|
|
4238
4341
|
if (this.client) this.client.registerTools(handlers);
|
|
4239
4342
|
else Object.assign(this.pendingTools, handlers);
|
|
4240
4343
|
}
|
|
4344
|
+
/** Open the chat window, exactly as a launcher click does. No-op if already open or not rendered. */
|
|
4345
|
+
open() {
|
|
4346
|
+
if (!this.oc || this.uiOpen) return;
|
|
4347
|
+
this.openPanel();
|
|
4348
|
+
}
|
|
4241
4349
|
key(k2) {
|
|
4242
4350
|
return `oyehello-chat:${k2}:${this.getAttribute("agent-id") ?? ""}`;
|
|
4243
4351
|
}
|