Spaces:
Running
Running
Fix: Google Sign-In redirect mode for HF iframe + POST handler for redirect callback
Browse files
backend/api.py
CHANGED
|
@@ -426,6 +426,42 @@ async def analyze_image_endpoint(
|
|
| 426 |
raise HTTPException(status_code=500, detail=str(e))
|
| 427 |
|
| 428 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
# --- Serve Vue Frontend (production) ---
|
| 430 |
static_dir = Path(__file__).parent / "static"
|
| 431 |
if static_dir.exists():
|
|
|
|
| 426 |
raise HTTPException(status_code=500, detail=str(e))
|
| 427 |
|
| 428 |
|
| 429 |
+
# --- Google OAuth Redirect Handler ---
|
| 430 |
+
# When using ux_mode='redirect', Google POSTs credential as form data to login_uri
|
| 431 |
+
@app.post("/")
|
| 432 |
+
async def google_redirect_callback(request: Request):
|
| 433 |
+
"""Handle Google Sign-In redirect (POST with credential in form data)."""
|
| 434 |
+
form = await request.form()
|
| 435 |
+
credential = form.get("credential", "")
|
| 436 |
+
if credential and AUTH_ENABLED:
|
| 437 |
+
try:
|
| 438 |
+
idinfo = google_id_token.verify_oauth2_token(
|
| 439 |
+
str(credential),
|
| 440 |
+
google_requests.Request(),
|
| 441 |
+
GOOGLE_CLIENT_ID,
|
| 442 |
+
)
|
| 443 |
+
email = idinfo.get("email", "")
|
| 444 |
+
name = idinfo.get("name", email)
|
| 445 |
+
picture = idinfo.get("picture", "")
|
| 446 |
+
token = create_jwt(email, name, picture)
|
| 447 |
+
|
| 448 |
+
# Return an HTML page that stores the token and redirects to the app
|
| 449 |
+
html = f"""<!DOCTYPE html>
|
| 450 |
+
<html><head><title>Logging in...</title></head>
|
| 451 |
+
<body><script>
|
| 452 |
+
localStorage.setItem('lp_auth_token', '{token}');
|
| 453 |
+
window.location.href = '/';
|
| 454 |
+
</script><p>Logging in...</p></body></html>"""
|
| 455 |
+
from fastapi.responses import HTMLResponse
|
| 456 |
+
return HTMLResponse(content=html)
|
| 457 |
+
except Exception as e:
|
| 458 |
+
logger.error(f"Google redirect auth failed: {e}")
|
| 459 |
+
|
| 460 |
+
# Fallback: serve index.html
|
| 461 |
+
static_dir = Path(__file__).parent / "static"
|
| 462 |
+
return FileResponse(str(static_dir / "index.html"))
|
| 463 |
+
|
| 464 |
+
|
| 465 |
# --- Serve Vue Frontend (production) ---
|
| 466 |
static_dir = Path(__file__).parent / "static"
|
| 467 |
if static_dir.exists():
|
backend/static/assets/index-BVDvGT2M.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
@charset "UTF-8";.btn{width:100%;height:100%;position:relative;background:var(--v854fa1ce) no-repeat;background-size:100% 100%;color:var(--v548a9e4c);-webkit-user-select:none;user-select:none;-webkit-mask-image:var(--v854fa1ce);-webkit-mask-size:100% 100%}.btn__capt{width:100%;position:absolute;text-align:center;bottom:0}.btn--d{font-size:14px;letter-spacing:.5px;line-height:180%}.btn--s{height:32px;line-height:32px;min-width:100px}.btn--m{height:48px;line-height:48px;min-width:200px}*:not([disabled])+.btn:hover:before{content:"";position:absolute;width:100%;height:100%;background:var(--ba36690e) no-repeat;background-size:100% 100%;left:0;top:0;mix-blend-mode:screen}*:not([disabled])+.btn:active{background-image:var(--v4e4c04fa)}*:not([disabled])+.btn:active .btn__capt{left:3px;bottom:-2px}[disabled]+.btn{background-image:var(--v512606b8);pointer-events:all!important;color:#646464}.cursored,.cursored *{cursor:none}.cursor{position:relative;width:32px;height:32px;background:var(--v7b062dde) no-repeat}.cursor__container{top:0;left:0;position:fixed;z-index:99;pointer-events:none}.cursor--default-active{animation:cursor-active .5s steps(8) infinite}.cursor--pointer{background-position:-32px -96px}.cursor--pointer-active{animation:cursor-pointer .5s steps(8) infinite}.cursor--pointer-denied{background-position:-64px -96px}.cursor--hold{background-position:-128px -96px}.cursor--arrow-top{animation:cursor-arrow .1s steps(3) infinite;transform:rotate(-90deg)}.cursor--arrow-right{animation:cursor-arrow .1s steps(3) infinite}.cursor--arrow-bottom{animation:cursor-arrow .1s steps(3) infinite;transform:rotate(90deg)}.cursor--arrow-left{animation:cursor-arrow .1s steps(3) infinite;transform:rotate(-180deg)}@keyframes cursor-active{0%{background-position:0 0}to{background-position:-256px 0}}@keyframes cursor-pointer{0%{background-position:0 -64px}to{background-position:-256px -64px}}@keyframes cursor-arrow{0%{background-position:-160px -96px}to{background-position:-256px -96px}}.chat-area[data-v-b84b5170]{flex:1;overflow:hidden;display:flex;flex-direction:column}.messages-container[data-v-b84b5170]{flex:1;overflow-y:auto;padding:1rem 1.5rem;display:flex;flex-direction:column;gap:.75rem}.login-page{width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:radial-gradient(ellipse at center,#12122a,#0a0a16,#050510)}.login-card{width:100%;max-width:480px;padding:2rem}.login-frame{background:linear-gradient(180deg,#13132a,#0d0d1f);border:2px solid var(--wc3-gold-dim, #8b7b4f);border-radius:6px;padding:2.5rem 2rem;text-align:center;box-shadow:0 0 40px #c8aa6e14,inset 0 1px #c8aa6e1a}.login-icon{font-size:3rem;margin-bottom:.75rem;filter:drop-shadow(0 0 10px rgba(200,170,110,.4))}.login-frame h1{font-family:var(--font-display, "Cinzel Decorative", serif);font-size:1.5rem;font-weight:900;background:linear-gradient(180deg,#f0d060,#c8aa6e,#8b7b4f);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:1px;margin-bottom:.25rem;word-break:keep-all}.login-frame .subtitle{font-family:var(--font-body, "Crimson Text", serif);color:var(--wc3-text-dim, #7a6e5a);font-style:italic;font-size:.95rem}.login-prompt{font-family:var(--font-heading, "Cinzel", serif);color:var(--wc3-gold, #c8aa6e);font-size:.9rem;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:1.25rem}.g-btn-container{display:flex;justify-content:center;min-height:44px;margin-bottom:.5rem}.login-loading{color:var(--wc3-gold, #c8aa6e);font-size:.85rem;margin-top:.75rem;animation:pulse 1.5s ease-in-out infinite}.login-error{color:#f66;font-size:.85rem;margin-top:.75rem}.iframe-hint{color:var(--wc3-text-dim, #7a6e5a);font-size:.78rem;margin-top:1rem;line-height:1.6}.direct-link{color:var(--wc3-gold, #c8aa6e);text-decoration:underline;text-underline-offset:2px;word-break:break-all}.direct-link:hover{color:var(--wc3-gold-bright, #f0d060)}.login-footer{font-size:.8rem;color:var(--wc3-text-dim, #7a6e5a);font-style:italic;line-height:1.6}.login-footer-dim{color:var(--wc3-text-muted, #555060);font-size:.75rem}:root{--wc3-bg: #0a0a0f;--wc3-bg-panel: #0f0f1a;--wc3-gold: #c8aa6e;--wc3-gold-bright: #f0d060;--wc3-gold-dim: #8b7b4f;--wc3-text: #d4c4a0;--wc3-text-light: #f0e6d0;--wc3-text-dim: #7a6e5a;--wc3-text-muted: #555060;--wc3-border: #5a4a2a;--wc3-border-dim: #3a2e1e;--wc3-red-soft: rgba(140, 40, 40, .25);--wc3-blue-soft: rgba(30, 50, 90, .35);--font-heading: "Cinzel", serif;--font-display: "Cinzel Decorative", "Cinzel", serif;--font-body: "Crimson Text", serif;--sidebar-w: 270px}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%;overflow:hidden;background:var(--wc3-bg);color:var(--wc3-text);font-family:var(--font-body);line-height:1.6}#app{height:100vh}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:#0000004d}::-webkit-scrollbar-thumb{background:linear-gradient(180deg,var(--wc3-gold-dim),#4a3a20);border-radius:2px}::-webkit-scrollbar-thumb:hover{background:var(--wc3-gold)}.app-shell{display:flex;height:100vh;overflow:hidden}.sidebar{width:var(--sidebar-w);min-width:var(--sidebar-w);background:linear-gradient(180deg,#0f0f22,#0a0a18);border-right:2px solid var(--wc3-border);display:flex;flex-direction:column;overflow-y:auto;padding:1rem;box-shadow:2px 0 20px #00000080}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;background:radial-gradient(ellipse at top center,#12122a,#0a0a16 60%,#070710)}.app-header{text-align:center;padding:1.25rem 1rem .5rem;flex-shrink:0}.header-frame h1{font-family:var(--font-display);font-size:2rem;font-weight:900;background:linear-gradient(180deg,#f0d060,#c8aa6e,#8b7b4f);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:3px;filter:drop-shadow(0 2px 4px rgba(0,0,0,.8))}.header-frame .subtitle{font-family:var(--font-body);color:var(--wc3-text-dim);font-style:italic;font-size:.95rem;margin-top:.15rem}.gold-divider{height:1px;background:linear-gradient(90deg,transparent,var(--wc3-gold-dim),transparent);margin:.75rem 0;border:none}.tab-bar{display:flex;gap:.5rem;padding:.35rem 1.5rem;border-bottom:1px solid var(--wc3-border-dim);flex-shrink:0}.tab-btn-wrap{height:32px;min-width:120px}.tab-trigger.active{filter:brightness(1.3)}.msg{max-width:85%;padding:.85rem 1.15rem;border-radius:6px;line-height:1.65;font-size:1rem;border:1px solid transparent;animation:fadeSlide .25s ease}@keyframes fadeSlide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.msg.user{align-self:flex-end;background:linear-gradient(135deg,#8c282859,#5a191940);border-color:#c850504d;color:var(--wc3-text-light)}.msg.assistant{align-self:flex-start;background:linear-gradient(135deg,#141e4173,#0f14324d);border-color:#6482c840;color:var(--wc3-text-light)}.msg h1,.msg h2,.msg h3,.msg h4{font-family:var(--font-heading);color:var(--wc3-gold);margin:.75rem 0 .35rem}.msg h1{font-size:1.3rem}.msg h2{font-size:1.15rem}.msg h3{font-size:1.05rem}.msg p{margin:.4rem 0}.msg strong{color:var(--wc3-gold-bright)}.msg em{font-style:italic}.msg ul,.msg ol{padding-left:1.25rem;margin:.4rem 0}.msg li{margin:.2rem 0}.msg code{background:#c8aa6e14;border:1px solid var(--wc3-border-dim);padding:.1rem .4rem;border-radius:3px;font-size:.9em;color:var(--wc3-gold)}.msg pre{background:#0000004d;border:1px solid var(--wc3-border-dim);border-radius:4px;padding:.75rem;overflow-x:auto;margin:.5rem 0}.msg pre code{background:none;border:none;padding:0}.msg blockquote{border-left:3px solid var(--wc3-gold-dim);padding-left:.75rem;margin:.5rem 0;color:var(--wc3-text);font-style:italic}.msg hr{border:none;height:1px;background:linear-gradient(90deg,transparent,var(--wc3-gold-dim),transparent);margin:.75rem 0}.msg-meta{font-size:.8rem;color:var(--wc3-text-dim);font-style:italic;margin-top:.4rem}.wc3-select{width:100%;padding:.5rem .75rem;font-family:var(--font-body);font-size:.95rem;color:var(--wc3-text-light);background:linear-gradient(180deg,#1a1a32,#13132a);border:1px solid var(--wc3-gold-dim);border-radius:2px;outline:none;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23c8aa6e' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .75rem center}.wc3-select:hover{border-color:var(--wc3-gold)}.wc3-select:focus{border-color:var(--wc3-gold);box-shadow:0 0 8px #c8aa6e33}.wc3-select option{background:var(--wc3-bg-panel);color:var(--wc3-text)}.wc3-input{width:100%;padding:.6rem .85rem;font-family:var(--font-body);font-size:1rem;color:var(--wc3-text-light);background:linear-gradient(180deg,#12122a,#0f0f24);border:1px solid var(--wc3-border);border-radius:2px;outline:none;box-shadow:inset 0 2px 6px #0000004d;transition:border-color .15s}.wc3-input:focus{border-color:var(--wc3-gold);box-shadow:inset 0 2px 6px #0000004d,0 0 8px #c8aa6e26}.wc3-input::placeholder{color:var(--wc3-text-muted)}.wc3-checkbox{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-family:var(--font-body);color:var(--wc3-text);font-size:.95rem}.wc3-checkbox input[type=checkbox]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:18px;height:18px;background:#13132a;border:1px solid var(--wc3-gold-dim);border-radius:2px;cursor:pointer;position:relative}.wc3-checkbox input[type=checkbox]:checked{border-color:var(--wc3-gold)}.wc3-checkbox input[type=checkbox]:checked:after{content:"✓";position:absolute;top:-1px;left:2px;color:var(--wc3-gold-bright);font-size:14px;font-weight:700}.loading{display:flex;align-items:center;gap:.6rem;padding:.75rem 1rem;color:var(--wc3-gold);font-family:var(--font-heading);font-size:.85rem;font-style:italic;animation:fadeSlide .3s ease}.dot-pulse{display:inline-flex;gap:4px}.dot-pulse span{width:6px;height:6px;border-radius:50%;background:var(--wc3-gold);animation:pulse 1.2s infinite ease-in-out}.dot-pulse span:nth-child(2){animation-delay:.2s}.dot-pulse span:nth-child(3){animation-delay:.4s}@keyframes pulse{0%,80%,to{opacity:.2;transform:scale(.8)}40%{opacity:1;transform:scale(1.1)}}.error-msg{background:#b4282826;border:1px solid #cc3333;color:#f88;padding:.6rem .85rem;border-radius:4px;font-size:.9rem}.resource-bar{background:linear-gradient(180deg,#1a1a30,#112);border:1px solid var(--wc3-border);border-radius:3px;padding:.45rem .75rem;text-align:center;font-family:var(--font-heading);font-size:.8rem;color:var(--wc3-gold);letter-spacing:1px}.sidebar h2{font-family:var(--font-heading);color:var(--wc3-gold);font-size:.85rem;letter-spacing:2px;text-transform:uppercase;text-shadow:0 0 8px rgba(200,170,110,.2);padding-bottom:.4rem;border-bottom:1px solid var(--wc3-border-dim);margin-top:.75rem;margin-bottom:.5rem}.sidebar h2:first-child{margin-top:0}.model-caption{font-size:.85rem;color:var(--wc3-text-dim);font-style:italic;margin-top:.25rem}.sidebar-footer{margin-top:auto;padding-top:.75rem;text-align:center;font-size:.75rem;color:var(--wc3-text-muted);font-style:italic;line-height:1.6}.chat-input-bar{display:flex;gap:.5rem;padding:.75rem 1.5rem;background:linear-gradient(180deg,var(--wc3-bg-panel),var(--wc3-bg));border-top:1px solid var(--wc3-border);flex-shrink:0;align-items:center}.chat-input-bar .wc3-input{flex:1}.chat-send-btn{height:38px;min-width:100px}.citations-toggle{font-family:var(--font-heading);font-size:.8rem;color:var(--wc3-gold-dim);background:none;border:1px solid var(--wc3-border-dim);border-radius:3px;padding:.35rem .75rem;cursor:pointer;margin-top:.5rem;transition:all .15s}.citations-toggle:hover{border-color:var(--wc3-gold);color:var(--wc3-gold)}.citations-panel{margin-top:.5rem;border:1px solid var(--wc3-border-dim);border-radius:4px;background:#0003;padding:.75rem;animation:fadeSlide .2s ease}.citation-item{padding:.5rem 0;border-bottom:1px solid var(--wc3-border-dim)}.citation-item:last-child{border-bottom:none}.citation-header{font-family:var(--font-heading);font-size:.8rem;color:var(--wc3-gold);margin-bottom:.25rem;display:flex;align-items:center;flex-wrap:wrap;gap:.25rem}.citation-text{font-size:.85rem;color:var(--wc3-text);font-style:italic;border-left:2px solid var(--wc3-gold-dim);padding-left:.6rem;margin-top:.25rem;line-height:1.5}.image-tab{flex:1;overflow-y:auto;padding:1.5rem}.upload-zone{border:2px dashed var(--wc3-border);border-radius:6px;padding:2rem;text-align:center;cursor:pointer;transition:all .15s;background:#00000026;margin-bottom:1rem}.upload-zone:hover{border-color:var(--wc3-gold);background:#c8aa6e08}.upload-zone input[type=file]{display:none}.upload-zone .icon{font-size:2.5rem;margin-bottom:.5rem}.upload-zone p{color:var(--wc3-text-dim);font-size:.95rem}.image-preview{max-width:100%;max-height:300px;border:1px solid var(--wc3-border);border-radius:4px;margin-bottom:1rem}.analysis-result{background:linear-gradient(135deg,#141e4173,#0f14324d);border:1px solid rgba(100,130,200,.25);border-radius:6px;padding:1rem 1.25rem;line-height:1.65}.loading-splash{width:100vw;height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(ellipse at center,#12122a,#0a0a16,#050510);color:var(--wc3-gold, #c8aa6e)}.loading-splash__icon{font-size:3rem;margin-bottom:1rem;animation:pulse 1.5s ease-in-out infinite}.loading-splash__text{font-family:var(--font-heading, "Cinzel", serif);font-size:1rem;letter-spacing:3px;text-transform:uppercase}@keyframes pulse{0%,to{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.1)}}.user-badge{display:flex;align-items:center;gap:.6rem;padding:.4rem 0}.user-avatar{width:32px;height:32px;border-radius:50%;border:1.5px solid var(--wc3-gold-dim, #8b7b4f);flex-shrink:0}.user-info{display:flex;flex-direction:column;min-width:0}.user-name{font-family:var(--font-heading, "Cinzel", serif);color:var(--wc3-gold, #c8aa6e);font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.logout-btn{background:none;border:none;color:var(--wc3-text-dim, #7a6e5a);font-size:.65rem;cursor:pointer;text-align:left;padding:0;font-family:var(--font-body, "Crimson Text", serif);text-decoration:underline;text-underline-offset:2px}.logout-btn:hover{color:var(--wc3-gold, #c8aa6e)}@media(max-width:768px){.sidebar{display:none}:root{--sidebar-w: 0px}.header-frame h1{font-size:1.4rem}}
|
backend/static/assets/index-CO4cGWW4.css
DELETED
|
@@ -1 +0,0 @@
|
|
| 1 |
-
@charset "UTF-8";.btn{width:100%;height:100%;position:relative;background:var(--v854fa1ce) no-repeat;background-size:100% 100%;color:var(--v548a9e4c);-webkit-user-select:none;user-select:none;-webkit-mask-image:var(--v854fa1ce);-webkit-mask-size:100% 100%}.btn__capt{width:100%;position:absolute;text-align:center;bottom:0}.btn--d{font-size:14px;letter-spacing:.5px;line-height:180%}.btn--s{height:32px;line-height:32px;min-width:100px}.btn--m{height:48px;line-height:48px;min-width:200px}*:not([disabled])+.btn:hover:before{content:"";position:absolute;width:100%;height:100%;background:var(--ba36690e) no-repeat;background-size:100% 100%;left:0;top:0;mix-blend-mode:screen}*:not([disabled])+.btn:active{background-image:var(--v4e4c04fa)}*:not([disabled])+.btn:active .btn__capt{left:3px;bottom:-2px}[disabled]+.btn{background-image:var(--v512606b8);pointer-events:all!important;color:#646464}.cursored,.cursored *{cursor:none}.cursor{position:relative;width:32px;height:32px;background:var(--v7b062dde) no-repeat}.cursor__container{top:0;left:0;position:fixed;z-index:99;pointer-events:none}.cursor--default-active{animation:cursor-active .5s steps(8) infinite}.cursor--pointer{background-position:-32px -96px}.cursor--pointer-active{animation:cursor-pointer .5s steps(8) infinite}.cursor--pointer-denied{background-position:-64px -96px}.cursor--hold{background-position:-128px -96px}.cursor--arrow-top{animation:cursor-arrow .1s steps(3) infinite;transform:rotate(-90deg)}.cursor--arrow-right{animation:cursor-arrow .1s steps(3) infinite}.cursor--arrow-bottom{animation:cursor-arrow .1s steps(3) infinite;transform:rotate(90deg)}.cursor--arrow-left{animation:cursor-arrow .1s steps(3) infinite;transform:rotate(-180deg)}@keyframes cursor-active{0%{background-position:0 0}to{background-position:-256px 0}}@keyframes cursor-pointer{0%{background-position:0 -64px}to{background-position:-256px -64px}}@keyframes cursor-arrow{0%{background-position:-160px -96px}to{background-position:-256px -96px}}.chat-area[data-v-b84b5170]{flex:1;overflow:hidden;display:flex;flex-direction:column}.messages-container[data-v-b84b5170]{flex:1;overflow-y:auto;padding:1rem 1.5rem;display:flex;flex-direction:column;gap:.75rem}.login-page{width:100vw;height:100vh;display:flex;align-items:center;justify-content:center;background:radial-gradient(ellipse at center,#12122a,#0a0a16,#050510)}.login-card{width:100%;max-width:480px;padding:2rem}.login-frame{background:linear-gradient(180deg,#13132a,#0d0d1f);border:2px solid var(--wc3-gold-dim, #8b7b4f);border-radius:6px;padding:2.5rem 2rem;text-align:center;box-shadow:0 0 40px #c8aa6e14,inset 0 1px #c8aa6e1a}.login-icon{font-size:3rem;margin-bottom:.75rem;filter:drop-shadow(0 0 10px rgba(200,170,110,.4))}.login-frame h1{font-family:var(--font-display, "Cinzel Decorative", serif);font-size:1.5rem;font-weight:900;background:linear-gradient(180deg,#f0d060,#c8aa6e,#8b7b4f);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:1px;margin-bottom:.25rem;word-break:keep-all}.login-frame .subtitle{font-family:var(--font-body, "Crimson Text", serif);color:var(--wc3-text-dim, #7a6e5a);font-style:italic;font-size:.95rem}.login-prompt{font-family:var(--font-heading, "Cinzel", serif);color:var(--wc3-gold, #c8aa6e);font-size:.9rem;letter-spacing:1.5px;text-transform:uppercase;margin-bottom:1.25rem}.g-btn-container{display:flex;justify-content:center;min-height:44px;margin-bottom:.5rem}.login-error{color:#f66;font-size:.85rem;margin-top:.75rem}.login-footer{font-size:.8rem;color:var(--wc3-text-dim, #7a6e5a);font-style:italic;line-height:1.6}.login-footer-dim{color:var(--wc3-text-muted, #555060);font-size:.75rem}:root{--wc3-bg: #0a0a0f;--wc3-bg-panel: #0f0f1a;--wc3-gold: #c8aa6e;--wc3-gold-bright: #f0d060;--wc3-gold-dim: #8b7b4f;--wc3-text: #d4c4a0;--wc3-text-light: #f0e6d0;--wc3-text-dim: #7a6e5a;--wc3-text-muted: #555060;--wc3-border: #5a4a2a;--wc3-border-dim: #3a2e1e;--wc3-red-soft: rgba(140, 40, 40, .25);--wc3-blue-soft: rgba(30, 50, 90, .35);--font-heading: "Cinzel", serif;--font-display: "Cinzel Decorative", "Cinzel", serif;--font-body: "Crimson Text", serif;--sidebar-w: 270px}*,*:before,*:after{box-sizing:border-box;margin:0;padding:0}html,body{height:100%;overflow:hidden;background:var(--wc3-bg);color:var(--wc3-text);font-family:var(--font-body);line-height:1.6}#app{height:100vh}::-webkit-scrollbar{width:8px}::-webkit-scrollbar-track{background:#0000004d}::-webkit-scrollbar-thumb{background:linear-gradient(180deg,var(--wc3-gold-dim),#4a3a20);border-radius:2px}::-webkit-scrollbar-thumb:hover{background:var(--wc3-gold)}.app-shell{display:flex;height:100vh;overflow:hidden}.sidebar{width:var(--sidebar-w);min-width:var(--sidebar-w);background:linear-gradient(180deg,#0f0f22,#0a0a18);border-right:2px solid var(--wc3-border);display:flex;flex-direction:column;overflow-y:auto;padding:1rem;box-shadow:2px 0 20px #00000080}.main-content{flex:1;display:flex;flex-direction:column;overflow:hidden;background:radial-gradient(ellipse at top center,#12122a,#0a0a16 60%,#070710)}.app-header{text-align:center;padding:1.25rem 1rem .5rem;flex-shrink:0}.header-frame h1{font-family:var(--font-display);font-size:2rem;font-weight:900;background:linear-gradient(180deg,#f0d060,#c8aa6e,#8b7b4f);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:3px;filter:drop-shadow(0 2px 4px rgba(0,0,0,.8))}.header-frame .subtitle{font-family:var(--font-body);color:var(--wc3-text-dim);font-style:italic;font-size:.95rem;margin-top:.15rem}.gold-divider{height:1px;background:linear-gradient(90deg,transparent,var(--wc3-gold-dim),transparent);margin:.75rem 0;border:none}.tab-bar{display:flex;gap:.5rem;padding:.35rem 1.5rem;border-bottom:1px solid var(--wc3-border-dim);flex-shrink:0}.tab-btn-wrap{height:32px;min-width:120px}.tab-trigger.active{filter:brightness(1.3)}.msg{max-width:85%;padding:.85rem 1.15rem;border-radius:6px;line-height:1.65;font-size:1rem;border:1px solid transparent;animation:fadeSlide .25s ease}@keyframes fadeSlide{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.msg.user{align-self:flex-end;background:linear-gradient(135deg,#8c282859,#5a191940);border-color:#c850504d;color:var(--wc3-text-light)}.msg.assistant{align-self:flex-start;background:linear-gradient(135deg,#141e4173,#0f14324d);border-color:#6482c840;color:var(--wc3-text-light)}.msg h1,.msg h2,.msg h3,.msg h4{font-family:var(--font-heading);color:var(--wc3-gold);margin:.75rem 0 .35rem}.msg h1{font-size:1.3rem}.msg h2{font-size:1.15rem}.msg h3{font-size:1.05rem}.msg p{margin:.4rem 0}.msg strong{color:var(--wc3-gold-bright)}.msg em{font-style:italic}.msg ul,.msg ol{padding-left:1.25rem;margin:.4rem 0}.msg li{margin:.2rem 0}.msg code{background:#c8aa6e14;border:1px solid var(--wc3-border-dim);padding:.1rem .4rem;border-radius:3px;font-size:.9em;color:var(--wc3-gold)}.msg pre{background:#0000004d;border:1px solid var(--wc3-border-dim);border-radius:4px;padding:.75rem;overflow-x:auto;margin:.5rem 0}.msg pre code{background:none;border:none;padding:0}.msg blockquote{border-left:3px solid var(--wc3-gold-dim);padding-left:.75rem;margin:.5rem 0;color:var(--wc3-text);font-style:italic}.msg hr{border:none;height:1px;background:linear-gradient(90deg,transparent,var(--wc3-gold-dim),transparent);margin:.75rem 0}.msg-meta{font-size:.8rem;color:var(--wc3-text-dim);font-style:italic;margin-top:.4rem}.wc3-select{width:100%;padding:.5rem .75rem;font-family:var(--font-body);font-size:.95rem;color:var(--wc3-text-light);background:linear-gradient(180deg,#1a1a32,#13132a);border:1px solid var(--wc3-gold-dim);border-radius:2px;outline:none;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23c8aa6e' d='M6 8L1 3h10z'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right .75rem center}.wc3-select:hover{border-color:var(--wc3-gold)}.wc3-select:focus{border-color:var(--wc3-gold);box-shadow:0 0 8px #c8aa6e33}.wc3-select option{background:var(--wc3-bg-panel);color:var(--wc3-text)}.wc3-input{width:100%;padding:.6rem .85rem;font-family:var(--font-body);font-size:1rem;color:var(--wc3-text-light);background:linear-gradient(180deg,#12122a,#0f0f24);border:1px solid var(--wc3-border);border-radius:2px;outline:none;box-shadow:inset 0 2px 6px #0000004d;transition:border-color .15s}.wc3-input:focus{border-color:var(--wc3-gold);box-shadow:inset 0 2px 6px #0000004d,0 0 8px #c8aa6e26}.wc3-input::placeholder{color:var(--wc3-text-muted)}.wc3-checkbox{display:flex;align-items:center;gap:.5rem;cursor:pointer;font-family:var(--font-body);color:var(--wc3-text);font-size:.95rem}.wc3-checkbox input[type=checkbox]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:18px;height:18px;background:#13132a;border:1px solid var(--wc3-gold-dim);border-radius:2px;cursor:pointer;position:relative}.wc3-checkbox input[type=checkbox]:checked{border-color:var(--wc3-gold)}.wc3-checkbox input[type=checkbox]:checked:after{content:"✓";position:absolute;top:-1px;left:2px;color:var(--wc3-gold-bright);font-size:14px;font-weight:700}.loading{display:flex;align-items:center;gap:.6rem;padding:.75rem 1rem;color:var(--wc3-gold);font-family:var(--font-heading);font-size:.85rem;font-style:italic;animation:fadeSlide .3s ease}.dot-pulse{display:inline-flex;gap:4px}.dot-pulse span{width:6px;height:6px;border-radius:50%;background:var(--wc3-gold);animation:pulse 1.2s infinite ease-in-out}.dot-pulse span:nth-child(2){animation-delay:.2s}.dot-pulse span:nth-child(3){animation-delay:.4s}@keyframes pulse{0%,80%,to{opacity:.2;transform:scale(.8)}40%{opacity:1;transform:scale(1.1)}}.error-msg{background:#b4282826;border:1px solid #cc3333;color:#f88;padding:.6rem .85rem;border-radius:4px;font-size:.9rem}.resource-bar{background:linear-gradient(180deg,#1a1a30,#112);border:1px solid var(--wc3-border);border-radius:3px;padding:.45rem .75rem;text-align:center;font-family:var(--font-heading);font-size:.8rem;color:var(--wc3-gold);letter-spacing:1px}.sidebar h2{font-family:var(--font-heading);color:var(--wc3-gold);font-size:.85rem;letter-spacing:2px;text-transform:uppercase;text-shadow:0 0 8px rgba(200,170,110,.2);padding-bottom:.4rem;border-bottom:1px solid var(--wc3-border-dim);margin-top:.75rem;margin-bottom:.5rem}.sidebar h2:first-child{margin-top:0}.model-caption{font-size:.85rem;color:var(--wc3-text-dim);font-style:italic;margin-top:.25rem}.sidebar-footer{margin-top:auto;padding-top:.75rem;text-align:center;font-size:.75rem;color:var(--wc3-text-muted);font-style:italic;line-height:1.6}.chat-input-bar{display:flex;gap:.5rem;padding:.75rem 1.5rem;background:linear-gradient(180deg,var(--wc3-bg-panel),var(--wc3-bg));border-top:1px solid var(--wc3-border);flex-shrink:0;align-items:center}.chat-input-bar .wc3-input{flex:1}.chat-send-btn{height:38px;min-width:100px}.citations-toggle{font-family:var(--font-heading);font-size:.8rem;color:var(--wc3-gold-dim);background:none;border:1px solid var(--wc3-border-dim);border-radius:3px;padding:.35rem .75rem;cursor:pointer;margin-top:.5rem;transition:all .15s}.citations-toggle:hover{border-color:var(--wc3-gold);color:var(--wc3-gold)}.citations-panel{margin-top:.5rem;border:1px solid var(--wc3-border-dim);border-radius:4px;background:#0003;padding:.75rem;animation:fadeSlide .2s ease}.citation-item{padding:.5rem 0;border-bottom:1px solid var(--wc3-border-dim)}.citation-item:last-child{border-bottom:none}.citation-header{font-family:var(--font-heading);font-size:.8rem;color:var(--wc3-gold);margin-bottom:.25rem;display:flex;align-items:center;flex-wrap:wrap;gap:.25rem}.citation-text{font-size:.85rem;color:var(--wc3-text);font-style:italic;border-left:2px solid var(--wc3-gold-dim);padding-left:.6rem;margin-top:.25rem;line-height:1.5}.image-tab{flex:1;overflow-y:auto;padding:1.5rem}.upload-zone{border:2px dashed var(--wc3-border);border-radius:6px;padding:2rem;text-align:center;cursor:pointer;transition:all .15s;background:#00000026;margin-bottom:1rem}.upload-zone:hover{border-color:var(--wc3-gold);background:#c8aa6e08}.upload-zone input[type=file]{display:none}.upload-zone .icon{font-size:2.5rem;margin-bottom:.5rem}.upload-zone p{color:var(--wc3-text-dim);font-size:.95rem}.image-preview{max-width:100%;max-height:300px;border:1px solid var(--wc3-border);border-radius:4px;margin-bottom:1rem}.analysis-result{background:linear-gradient(135deg,#141e4173,#0f14324d);border:1px solid rgba(100,130,200,.25);border-radius:6px;padding:1rem 1.25rem;line-height:1.65}.loading-splash{width:100vw;height:100vh;display:flex;flex-direction:column;align-items:center;justify-content:center;background:radial-gradient(ellipse at center,#12122a,#0a0a16,#050510);color:var(--wc3-gold, #c8aa6e)}.loading-splash__icon{font-size:3rem;margin-bottom:1rem;animation:pulse 1.5s ease-in-out infinite}.loading-splash__text{font-family:var(--font-heading, "Cinzel", serif);font-size:1rem;letter-spacing:3px;text-transform:uppercase}@keyframes pulse{0%,to{opacity:.5;transform:scale(1)}50%{opacity:1;transform:scale(1.1)}}.user-badge{display:flex;align-items:center;gap:.6rem;padding:.4rem 0}.user-avatar{width:32px;height:32px;border-radius:50%;border:1.5px solid var(--wc3-gold-dim, #8b7b4f);flex-shrink:0}.user-info{display:flex;flex-direction:column;min-width:0}.user-name{font-family:var(--font-heading, "Cinzel", serif);color:var(--wc3-gold, #c8aa6e);font-size:.75rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.logout-btn{background:none;border:none;color:var(--wc3-text-dim, #7a6e5a);font-size:.65rem;cursor:pointer;text-align:left;padding:0;font-family:var(--font-body, "Crimson Text", serif);text-decoration:underline;text-underline-offset:2px}.logout-btn:hover{color:var(--wc3-gold, #c8aa6e)}@media(max-width:768px){.sidebar{display:none}:root{--sidebar-w: 0px}.header-frame h1{font-size:1.4rem}}
|
|
|
|
|
|
backend/static/assets/{index-PTXcf1b6.js → index-D8b5zKM1.js}
RENAMED
|
The diff for this file is too large to render.
See raw diff
|
|
|
backend/static/index.html
CHANGED
|
@@ -8,8 +8,8 @@
|
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700;900&family=Cinzel+Decorative:wght@400;700;900&family=Crimson+Text:ital,wght@0,400;0,600;0,700;1,400&display=swap" rel="stylesheet">
|
| 11 |
-
<script type="module" crossorigin src="/assets/index-
|
| 12 |
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app"></div>
|
|
|
|
| 8 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 9 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 10 |
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700;900&family=Cinzel+Decorative:wght@400;700;900&family=Crimson+Text:ital,wght@0,400;0,600;0,700;1,400&display=swap" rel="stylesheet">
|
| 11 |
+
<script type="module" crossorigin src="/assets/index-D8b5zKM1.js"></script>
|
| 12 |
+
<link rel="stylesheet" crossorigin href="/assets/index-BVDvGT2M.css">
|
| 13 |
</head>
|
| 14 |
<body>
|
| 15 |
<div id="app"></div>
|
frontend/src/components/LoginPage.vue
CHANGED
|
@@ -10,8 +10,33 @@ const emit = defineEmits<{
|
|
| 10 |
}>()
|
| 11 |
|
| 12 |
const error = ref('')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
onMounted(() => {
|
|
|
|
|
|
|
|
|
|
| 15 |
// Load Google Identity Services script dynamically
|
| 16 |
if (document.getElementById('gsi-script')) {
|
| 17 |
initGsi()
|
|
@@ -30,6 +55,11 @@ onMounted(() => {
|
|
| 30 |
document.head.appendChild(script)
|
| 31 |
})
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
function initGsi() {
|
| 34 |
const w = window as any
|
| 35 |
if (!w.google?.accounts?.id) {
|
|
@@ -41,6 +71,9 @@ function initGsi() {
|
|
| 41 |
client_id: props.googleClientId,
|
| 42 |
callback: handleCredentialResponse,
|
| 43 |
auto_select: false,
|
|
|
|
|
|
|
|
|
|
| 44 |
})
|
| 45 |
|
| 46 |
w.google.accounts.id.renderButton(
|
|
@@ -57,6 +90,7 @@ function initGsi() {
|
|
| 57 |
|
| 58 |
function handleCredentialResponse(response: { credential: string }) {
|
| 59 |
if (response.credential) {
|
|
|
|
| 60 |
emit('google-login', response.credential)
|
| 61 |
} else {
|
| 62 |
error.value = 'Google sign-in failed. Please try again.'
|
|
@@ -80,8 +114,15 @@ function handleCredentialResponse(response: { credential: string }) {
|
|
| 80 |
<!-- Google Sign-In button renders here -->
|
| 81 |
<div id="g-signin-btn" class="g-btn-container"></div>
|
| 82 |
|
|
|
|
| 83 |
<p v-if="error" class="login-error">{{ error }}</p>
|
| 84 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
<div class="gold-divider"></div>
|
| 86 |
|
| 87 |
<p class="login-footer">
|
|
@@ -162,12 +203,36 @@ function handleCredentialResponse(response: { credential: string }) {
|
|
| 162 |
margin-bottom: 0.5rem;
|
| 163 |
}
|
| 164 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
.login-error {
|
| 166 |
color: #ff6666;
|
| 167 |
font-size: 0.85rem;
|
| 168 |
margin-top: 0.75rem;
|
| 169 |
}
|
| 170 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
.login-footer {
|
| 172 |
font-size: 0.8rem;
|
| 173 |
color: var(--wc3-text-dim, #7a6e5a);
|
|
|
|
| 10 |
}>()
|
| 11 |
|
| 12 |
const error = ref('')
|
| 13 |
+
const loading = ref(false)
|
| 14 |
+
|
| 15 |
+
// Check if we're inside an iframe (HuggingFace Spaces embeds apps in iframes)
|
| 16 |
+
const isInIframe = ref(false)
|
| 17 |
+
const directUrl = ref('')
|
| 18 |
+
|
| 19 |
+
try {
|
| 20 |
+
isInIframe.value = window.self !== window.top
|
| 21 |
+
} catch {
|
| 22 |
+
isInIframe.value = true // cross-origin iframe throws
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
// Build the direct .hf.space URL from the current page
|
| 26 |
+
if (isInIframe.value) {
|
| 27 |
+
// Inside HF iframe at huggingface.co/spaces/User/Space → direct URL is user-space.hf.space
|
| 28 |
+
const match = window.location.href.match(/spaces\/([^/]+)\/([^/?#]+)/)
|
| 29 |
+
if (match) {
|
| 30 |
+
directUrl.value = `https://${match[1].toLowerCase()}-${match[2].toLowerCase().replace(/_/g, '-')}.hf.space`
|
| 31 |
+
} else {
|
| 32 |
+
directUrl.value = window.location.origin
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
|
| 36 |
onMounted(() => {
|
| 37 |
+
// Check for credential in URL hash (returned from redirect flow)
|
| 38 |
+
checkRedirectResult()
|
| 39 |
+
|
| 40 |
// Load Google Identity Services script dynamically
|
| 41 |
if (document.getElementById('gsi-script')) {
|
| 42 |
initGsi()
|
|
|
|
| 55 |
document.head.appendChild(script)
|
| 56 |
})
|
| 57 |
|
| 58 |
+
function checkRedirectResult() {
|
| 59 |
+
// After Google redirect, the credential comes back as a POST to the current page
|
| 60 |
+
// We handle this via the callback in initialize()
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
function initGsi() {
|
| 64 |
const w = window as any
|
| 65 |
if (!w.google?.accounts?.id) {
|
|
|
|
| 71 |
client_id: props.googleClientId,
|
| 72 |
callback: handleCredentialResponse,
|
| 73 |
auto_select: false,
|
| 74 |
+
// Use redirect mode inside iframes (popups are blocked)
|
| 75 |
+
ux_mode: isInIframe.value ? 'redirect' : 'popup',
|
| 76 |
+
login_uri: isInIframe.value ? (directUrl.value || window.location.origin) + '/' : undefined,
|
| 77 |
})
|
| 78 |
|
| 79 |
w.google.accounts.id.renderButton(
|
|
|
|
| 90 |
|
| 91 |
function handleCredentialResponse(response: { credential: string }) {
|
| 92 |
if (response.credential) {
|
| 93 |
+
loading.value = true
|
| 94 |
emit('google-login', response.credential)
|
| 95 |
} else {
|
| 96 |
error.value = 'Google sign-in failed. Please try again.'
|
|
|
|
| 114 |
<!-- Google Sign-In button renders here -->
|
| 115 |
<div id="g-signin-btn" class="g-btn-container"></div>
|
| 116 |
|
| 117 |
+
<p v-if="loading" class="login-loading">Prijava u tijeku...</p>
|
| 118 |
<p v-if="error" class="login-error">{{ error }}</p>
|
| 119 |
|
| 120 |
+
<!-- Iframe hint: if popups are blocked, offer direct link -->
|
| 121 |
+
<p v-if="isInIframe" class="iframe-hint">
|
| 122 |
+
Ako prijava ne radi, otvori aplikaciju direktno:<br>
|
| 123 |
+
<a :href="directUrl" target="_top" class="direct-link">{{ directUrl }}</a>
|
| 124 |
+
</p>
|
| 125 |
+
|
| 126 |
<div class="gold-divider"></div>
|
| 127 |
|
| 128 |
<p class="login-footer">
|
|
|
|
| 203 |
margin-bottom: 0.5rem;
|
| 204 |
}
|
| 205 |
|
| 206 |
+
.login-loading {
|
| 207 |
+
color: var(--wc3-gold, #c8aa6e);
|
| 208 |
+
font-size: 0.85rem;
|
| 209 |
+
margin-top: 0.75rem;
|
| 210 |
+
animation: pulse 1.5s ease-in-out infinite;
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
.login-error {
|
| 214 |
color: #ff6666;
|
| 215 |
font-size: 0.85rem;
|
| 216 |
margin-top: 0.75rem;
|
| 217 |
}
|
| 218 |
|
| 219 |
+
.iframe-hint {
|
| 220 |
+
color: var(--wc3-text-dim, #7a6e5a);
|
| 221 |
+
font-size: 0.78rem;
|
| 222 |
+
margin-top: 1rem;
|
| 223 |
+
line-height: 1.6;
|
| 224 |
+
}
|
| 225 |
+
|
| 226 |
+
.direct-link {
|
| 227 |
+
color: var(--wc3-gold, #c8aa6e);
|
| 228 |
+
text-decoration: underline;
|
| 229 |
+
text-underline-offset: 2px;
|
| 230 |
+
word-break: break-all;
|
| 231 |
+
}
|
| 232 |
+
.direct-link:hover {
|
| 233 |
+
color: var(--wc3-gold-bright, #f0d060);
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
.login-footer {
|
| 237 |
font-size: 0.8rem;
|
| 238 |
color: var(--wc3-text-dim, #7a6e5a);
|