@spheredata/embed 0.0.1 → 0.0.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/sphere-agent.js +47 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spheredata/embed",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "publishConfig": { "access": "public" },
5
5
  "description": "Embeddable SPHERE agent UI — the <sphere-agent> Web Component + optional tool packs.",
6
6
  "keywords": ["sphere", "spheredata", "web-component", "custom-element", "ai", "agent", "embed", "pyodide"],
package/sphere-agent.js CHANGED
@@ -72,6 +72,12 @@ input:focus, textarea:focus, select:focus { outline: none; border-color: var(--_
72
72
  .primary { background: var(--_accent); color: #fff; border: none; padding: 10px 18px; border-radius: var(--_radius); font-size: 14px; }
73
73
  .primary:hover:not(:disabled) { background: var(--_accent-hover); }
74
74
  .primary:disabled { opacity: .5; cursor: default; }
75
+ .divider { display: flex; align-items: center; gap: 10px; margin: 16px 0 4px; color: var(--_muted); font-size: 13px; }
76
+ .divider::before, .divider::after { content: ""; flex: 1; height: 1px; background: var(--_line); }
77
+ .oauth { display: flex; align-items: center; justify-content: center; gap: 8px; width: 100%; padding: 10px 14px; margin-top: 8px; background: var(--_surface); color: inherit; border: 1px solid var(--_line); border-radius: var(--_radius); font: inherit; font-size: 14px; cursor: pointer; }
78
+ .oauth:hover:not(:disabled) { border-color: var(--_accent); }
79
+ .oauth:disabled { opacity: .5; cursor: default; }
80
+ .oauth svg { width: 18px; height: 18px; flex: 0 0 18px; }
75
81
  .err { color: var(--_danger); font-size: 13px; margin-top: 10px; min-height: 1em; }
76
82
 
77
83
  /* chat */
@@ -275,7 +281,18 @@ class SphereAgent extends Base {
275
281
  }
276
282
 
277
283
  // ---- public API (properties) ----
278
- set sphere(s) { this._sphere = s; this._refresh(); }
284
+ set sphere(s) {
285
+ this._sphere = s;
286
+ // Returning from a hosted OAuth redirect? Ingest the tokens the backend appended
287
+ // to the URL, then strip them so they don't linger in history. No-op on a normal
288
+ // load (completeOAuth returns false when no oauth params are present).
289
+ try {
290
+ if (s && typeof s.completeOAuth === "function" && s.completeOAuth()) {
291
+ history.replaceState({}, "", location.pathname + location.hash);
292
+ }
293
+ } catch { /* ignore — a normal load just has no tokens to ingest */ }
294
+ this._refresh();
295
+ }
279
296
  get sphere() { return this._sphere; }
280
297
  set tools(t) { this._tools = Array.isArray(t) ? t : []; }
281
298
  set systemPrompt(s) { this._systemPrompt = s || ""; }
@@ -307,6 +324,15 @@ class SphereAgent extends Base {
307
324
  <label>Email</label><input id="email" type="email" autocomplete="email" placeholder="you@lab.edu">
308
325
  <label>Password</label><input id="password" type="password" autocomplete="current-password" placeholder="••••••••">
309
326
  <div style="margin-top:14px"><button class="primary" id="auth-go">Sign in</button></div>
327
+ <div class="divider"><span>or</span></div>
328
+ <button class="oauth" id="oauth-google" type="button">
329
+ <svg viewBox="0 0 18 18" aria-hidden="true"><path fill="#4285F4" d="M17.64 9.2c0-.64-.06-1.25-.16-1.84H9v3.48h4.84a4.14 4.14 0 0 1-1.8 2.72v2.26h2.92c1.7-1.57 2.68-3.88 2.68-6.62z"/><path fill="#34A853" d="M9 18c2.43 0 4.47-.8 5.96-2.18l-2.92-2.26c-.8.54-1.84.86-3.04.86-2.34 0-4.32-1.58-5.03-3.7H.96v2.33A9 9 0 0 0 9 18z"/><path fill="#FBBC05" d="M3.97 10.72a5.41 5.41 0 0 1 0-3.44V4.95H.96a9 9 0 0 0 0 8.1l3.01-2.33z"/><path fill="#EA4335" d="M9 3.58c1.32 0 2.5.45 3.44 1.35l2.58-2.58C13.46.89 11.43 0 9 0A9 9 0 0 0 .96 4.95l3.01 2.33C4.68 5.16 6.66 3.58 9 3.58z"/></svg>
330
+ Continue with Google
331
+ </button>
332
+ <button class="oauth" id="oauth-microsoft" type="button">
333
+ <svg viewBox="0 0 18 18" aria-hidden="true"><path fill="#F25022" d="M0 0h8.5v8.5H0z"/><path fill="#7FBA00" d="M9.5 0H18v8.5H9.5z"/><path fill="#00A4EF" d="M0 9.5h8.5V18H0z"/><path fill="#FFB900" d="M9.5 9.5H18V18H9.5z"/></svg>
334
+ Continue with Microsoft
335
+ </button>
310
336
  <div class="err" id="auth-err"></div>
311
337
  <p id="up-hint" class="hidden" style="margin-top:12px">New accounts start with <strong>$10</strong> in free credit.</p>
312
338
  </section>
@@ -348,6 +374,8 @@ class SphereAgent extends Base {
348
374
  $("tab-in").onclick = () => this._setMode("signin");
349
375
  $("tab-up").onclick = () => this._setMode("signup");
350
376
  $("auth-go").onclick = () => this._auth();
377
+ $("oauth-google").onclick = () => this._oauth("google");
378
+ $("oauth-microsoft").onclick = () => this._oauth("microsoft");
351
379
  $("signout").onclick = () => { this._sphere?.signOut(); this._messages = []; this._seeded = false; this._refresh(); };
352
380
  $("reset").onclick = () => this._resetConversation();
353
381
  $("send").onclick = () => this._send();
@@ -408,6 +436,24 @@ class SphereAgent extends Base {
408
436
  if (this._accountUrl) window.open(this._accountUrl, "_blank", "noopener");
409
437
  }
410
438
 
439
+ // Hosted OAuth (Google/Microsoft): redirect the page to the provider; on return the
440
+ // backend appends tokens to redirect_to, which `set sphere` ingests via completeOAuth().
441
+ // We drop a sessionStorage breadcrumb so the host page can reopen the agent view.
442
+ _oauth(provider) {
443
+ const err = this._$("auth-err");
444
+ if (!this._sphere || typeof this._sphere.oauthUrl !== "function") {
445
+ err.textContent = "Sign-in with " + provider + " isn't available.";
446
+ return;
447
+ }
448
+ try {
449
+ try { sessionStorage.setItem("sphere-agent:oauth-return", "1"); } catch { /* ignore */ }
450
+ const redirectTo = location.origin + location.pathname; // come back to this page
451
+ location.href = this._sphere.oauthUrl(provider, redirectTo);
452
+ } catch {
453
+ err.textContent = "Could not start sign-in. Please try again.";
454
+ }
455
+ }
456
+
411
457
  async _auth() {
412
458
  const $ = (id) => this._$(id);
413
459
  const email = $("email").value.trim(), password = $("password").value, name = $("name").value.trim();