1<!doctype html><html lang="en"><head><meta charset="UTF-8"><link rel="icon" type="image/png" href="/logo.png"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Built with Taskade</title>
1<script defer src="./assets/main.a129f9.js"></script>
1<link href="./assets/main.9b68a1.css" rel="stylesheet"><meta charset="utf-8" /><meta name="language" content="en-US" /><meta name="robots" content="noindex,nofollow" /><meta name="author" content="Taskade" /><link rel="icon" sizes="32x32" href="/_genesis/assets/favicon" /><link rel="apple-touch-icon" sizes="180x180" href="/_genesis/assets/favicon" /><meta name="title" content="Built with Taskade" /><meta property="og:title" content="Built with Taskade" /><meta name="twitter:title" content="Built with Taskade" /><meta name="description" content="A living app built with Taskade Genesis. One prompt, one app. Projects remember, agents learn, and workflows move." /><meta property="og:description" content="A living app built with Taskade Genesis. One prompt, one app. Projects remember, agents learn, and workflows move." /><meta name="twitter:description" content="A living app built with Taskade Genesis. One prompt, one app. Projects remember, agents learn, and workflows move." /><meta property="og:type" content="website" /><meta property="og:url" content="https://social-widget-hub-3ak0as88.taskade.app" /><meta property="og:site_name" content="Taskade" /><meta property="og:image" content="https://social-widget-hub-3ak0as88.taskade.app/_internal/screenshot" /><meta property="og:image:alt" content="Built with Taskade - A living app built with Taskade Genesis. One prompt, one app. Projects remember, agents learn, and w..." /><meta property="og:locale" content="en_US" /><meta name="twitter:card" content="summary_large_image" /><meta name="twitter:site" content="@taskade" /><meta name="twitter:creator" content="@taskade" /><meta name="twitter:image" content="https://social-widget-hub-3ak0as88.taskade.app/_internal/screenshot" /><meta name="twitter:image:alt" content="Built with Taskade - A living app built with Taskade Genesis. One prompt, one app. Projects remember, agents learn, and w..." /><meta name="format-detection" content="telephone=no" /><meta name="mobile-web-app-capable" content="yes" /><meta name="apple-mobile-web-app-capable" content="yes" /><meta name="apple-mobile-web-app-status-bar-style" content="default" /><meta name="apple-mobile-web-app-title" content="Built with Taskade" /><meta name="application-name" content="Taskade" /><link rel="canonical" href="https://social-widget-hub-3ak0as88.taskade.app" />
1<script type="application/ld+json"> 2 { 3 "@context": "https://schema.org", 4 "@type": "WebSite", 5 "name": "Taskade", 6 "alternateName": "Built with Taskade", 7 "description": "A living app built with Taskade Genesis. One prompt, one app. Projects remember, agents learn, and workflows move.", 8 "image": "https://social-widget-hub-3ak0as88.taskade.app/_internal/screenshot", 9 "url": "https://social-widget-hub-3ak0as88.taskade.app", 10 "author": { 11 "@type": "Organization", 12 "name": "Taskade" 13 }, 14 "publisher": { 15 "@type": "Organization", 16 "name": "Taskade", 17 "url": "https://social-widget-hub-3ak0as88.taskade.app", 18 "logo": { 19 "@type": "ImageObject", 20 "url": "https://social-widget-hub-3ak0as88.taskade.app/_internal/screenshot" 21 } 22 } 23 } 24</script>
24<script defer src="/_genesis/vendor/flock.min.js" data-proxy-url="/_genesis/api/events" data-tenant-id="hukxgm8wfgw2y2mq:default" data-domain="social-widget-hub-3ak0as88.taskade.app"></script>
24<script defer src="/_genesis/vendor/scroll-guard.js"></script>
24</head><body>
24<script> 25 (function() { 26 if (window.__genesisErrorCatchAll) return; 27 window.__genesisErrorCatchAll = true; 28 var esc = function(s) { return String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }; 29 var errorHtml = function(msg, id) { 30 return '<div style="position:fixed;inset:0;background:#000;padding:2rem;font-family:system-ui,sans-serif;color:#fff;overflow:auto">' + 31 '<div style="max-width:600px;margin:0 auto">' + 32 '<h2 style="margin:0 0 0.5rem;font-size:1.25rem">Something went wrong</h2>' + 33 '<p style="color:#999;margin:0 0 1rem;font-size:0.95rem">The app failed to load. Try refreshing the page.</p>' + 34 '<pre style="background:#1a1a1a;padding:1rem;border-radius:8px;overflow:auto;font-size:0.8rem;color:#f87171;margin:0;white-space:pre-wrap;word-break:break-word">' + 35 esc(msg) + '<\/pre>' + 36 '<p style="color:#666;margin:1rem 0 0;font-size:0.75rem">Error ID: <code>' + esc(id) + '<\/code><\/p>' + 37 '<\/div><\/div>'; 38 }; 39 var reported = false; 40 var errorId = null; 41 function getErrorId() { 42 if (errorId != null) return errorId; 43 try { 44 if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') { errorId = crypto.randomUUID().replace(/-/g, ''); return errorId; } 45 } catch (e) { /* Fall through to the time+random id below */ } 46 errorId = Date.now().toString(36) + Math.random().toString(36).slice(2, 12); 47 return errorId; 48 } 49 function reportError(code, msg, stack) { 50 if (reported) return; 51 reported = true; 52 try { 53 var logger = window.__TASKADE_APP_LIFECYCLE_LOGGER__; 54 if (typeof logger?.log === 'function') { 55 logger.log({ level: 'error', message: 'Runtime Error', data: { code: code, message: msg, stack: stack || '' } }); 56 return; 57 } 58 // Published: no logger, so beacon to the Taskade gateway instead of 59 // dropping the crash on the floor (#27626). Relative path â the 60 // Director proxy injects the gateway token server-side. Once per page 61 // session (the latch above) so a crash loop cannot flood the sink. 62 fetch('/api/taskade/telemetry/errors', { 63 method: 'POST', 64 headers: { 'Content-Type': 'application/json' }, 65 body: JSON.stringify({ 66 errorId: getErrorId(), 67 code: code, 68 message: String(msg || 'Unknown error').slice(0, 4000), 69 stack: String(stack || '').slice(0, 20000), 70 path: location.pathname.slice(0, 1000),
71 userAgent: navigator.userAgent.slice(0, 512) 72 }), 73 keepalive: true 74 // Default same-origin credentials: a password-protected app gates 75 // /api/taskade/* on its signed access cookie, so omitting them 76 // would redirect the beacon to the password page. 77 }).catch(function() {}); 78 } catch (e) { /* Swallow â never block the fallback error UI */ } 79 } 80 function showError(code, msg, stack) { 81 var root = document.getElementById('root'); 82 if (!root) return; 83 var spinner = root.querySelector('.genesis__spinner-container'); 84 if (spinner) { 85 // Scenario 1: React never mounted â replace spinner 86 reportError('error.fatal', msg, stack); 87 spinner.outerHTML = errorHtml(msg, getErrorId()); 88 } else if (!root.childElementCount) { 89 // Scenario 2: React crashed and unmounted â #root is empty 90 reportError('error.fatal', msg, stack); 91 root.innerHTML = errorHtml(msg, getErrorId()); 92 } else { 93 // React is running fine â don't interfere with the DOM, 94 // but still report with the original code for diagnostics 95 reportError(code, msg, stack); 96 } 97 } 98 window.addEventListener('error', function(e) { 99 var msg = e.message || String(e.error) || 'Unknown error'; 100 var stack = e.error && e.error.stack ? e.error.stack : (e.filename ? e.filename + ':' + e.lineno + ':' + e.colno : ''); 101 showError('error.runtime_error', msg, stack); 102 }); 103 window.addEventListener('unhandledrejection', function(e) { 104 var msg = e.reason ? String(e.reason) : 'Unhandled promise rejection'; 105 var stack = e.reason && e.reason.stack ? e.reason.stack : ''; 106 showError('error.unhandled_promise', msg, stack); 107 }); 108 // A dead <img> degrades to a neutral placeholder instead of painting its 109 // raw alt string across the page (#27730) â the symptom half of the 110 // fabricated-hot-link fix, and the only half that helps an ALREADY 111 // published app, whose images can rot long after generation. 112 // 113 // Resource load errors do not bubble, so they reach window only in the 114 // CAPTURE phase. That is why neither handler above has ever seen one, and 115 // why this cannot disturb them: a broken image must never render the fatal 116 // error overlay or spend the once-per-session crash beacon. 117 // 118 // Swapping in a 1x1 transparent GIF is what actually stops the alt text â 119 // the element becomes a SUCCESSFUL load, so the browser has no broken image 120 // to caption, while the alt attribute stays for assistive tech. Layout is 121 // untouched: the app's own width/height/aspect classes still apply. The grey 122 // is translucent so it reads correctly on a light or a dark surface without 123 // this script having to know the app's theme. 124 var deadImagePixel = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'; 125 window.addEventListener('error', function(e) { 126 var el = e.target; 127 if (!el || el.tagName !== 'IMG') return; 128 // The swap fires a fresh load on the same element, so the latch is what 129 // keeps a failing data URI from looping. 130 if (el.getAttribute('data-genesis-image-fallback') !== null) return; 131 el.setAttribute('data-genesis-image-fallback', ''); 132 el.removeAttribute('srcset'); 133 el.src = deadImagePixel; 134 el.style.background = 'linear-gradient(135deg, rgba(127,127,127,0.16), rgba(127,127,127,0.28))'; 135 }, true); 136 137 // --------------------------------------------------------------- 138 // Dead env reads (advisory, NOT a failure) 139 // --------------------------------------------------------------- 140 // The bundle's import.meta.env / process.env shims are reporting 141 // Proxies (see genesisEnvBanner.ts). They are value-preserving: a 142 // missing key still reads undefined. This turns that read from invisible 143 // into observable, because a feature gated on one is permanently off and 144 // has no other symptom â the app builds, boots and looks healthy. 145 // 146 // Deliberately WARN, never error. The runtime cannot see whether a call 147 // site has a working fallback, so a correct 148 // VITE_API_BASE_URL || 'https://real' is reported too. Warn keeps it
149 // out of the "Fix with AI" popup set and out of FATAL_LIFECYCLE_CODES, 150 // both of which are error-level allowlists. 151 var deadEnvSeen = {}; 152 var deadEnvSent = 0; 153 // Its OWN budget and latch: sharing the 'reported' latch above would let 154 // an advisory notice spend the one crash report this page session gets. 155 var DEAD_ENV_MAX = 5; 156 function reportDeadEnv(source, key) { 157 try { 158 var signature = source + '.' + key; 159 if (deadEnvSeen[signature] || deadEnvSent >= DEAD_ENV_MAX) return; 160 deadEnvSeen[signature] = 1; 161 deadEnvSent++; 162 var msg = signature + ' is read but a Genesis build never defines it, so the value is ' + 163 'always undefined. If it gates a feature, that feature is permanently off. Route ' + 164 'secret-bearing calls through GenesisClient.proxy() with a secretAlias instead.'; 165 var logger = window.__TASKADE_APP_LIFECYCLE_LOGGER__; 166 if (typeof logger?.log === 'function') { 167 logger.log({ level: 'warn', message: 'Dead env read', data: { code: 'warn.dead_env', message: msg } }); 168 return; 169 } 170 fetch('/api/taskade/telemetry/errors', { 171 method: 'POST', 172 headers: { 173 'Content-Type': 'application/json', 174 // Lets the publish Worker pick the advisory rate-limit bucket 175 // without reading this body. Doing that at the edge means I/O 176 // of unbounded duration ahead of the limiter meant to bound it, 177 // which is the whole class of problem the header avoids. The 178 // origin still classifies from the validated body, so this is a 179 // bucket hint and never the record (#29164). 180 'X-Genesis-Telemetry-Code': 'warn.dead_env' 181 }, 182 body: JSON.stringify({ 183 errorId: 'de' + Date.now().toString(36) + Math.random().toString(36).slice(2, 8), 184 code: 'warn.dead_env', 185 message: msg.slice(0, 4000), 186 path: location.pathname.slice(0, 1000), 187 // The source and key as STRUCTURE, not only inside the prose. 188 // The gateway fingerprints on app + code alone, because this 189 // endpoint's token ships in the bundle and free text from it 190 // cannot be trusted; these two validated fields are how the 191 // key still reaches Sentry, as a tag. 192 // Sliced to the schema's bound like every other field here: the 193 // banner filters the key's SHAPE but not its length, and an 194 // over-long key would fail validation and lose the whole 195 // report rather than just the tag. 196 envSource: source, 197 envKey: String(key).slice(0, 64) 198 }), 199 keepalive: true 200 }).catch(function() {}); 201 } catch (e) { /* Swallow â advisory telemetry must never break an app */ } 202 } 203 window.__genesisReportDeadEnv = reportDeadEnv; 204 // Drain anything the bundle's banner queued. This script is a classic 205 // inline script and the bundle is a deferred module, so in practice the 206 // reporter wins the race; the queue is what makes that not a matter of 207 // luck (and covers an app whose HTML predates this script). 208 try { 209 var queued = window.__genesisDeadEnvQueue; 210 if (queued) { 211 window.__genesisDeadEnvQueue = []; 212 for (var qi = 0; qi < queued.length; qi++) reportDeadEnv(queued[qi][0], queued[qi][1]); 213 } 214 } catch (e) { /* Ignore. */ } 215 })(); 216</script>
216<div id="root"></div>
216<script> 217 (function() { 218 if (typeof window === 'undefined' || typeof document === 'undefined') { 219 return; 220 } 221 if (typeof window.gtag === 'function') { 222 return; 223 } 224 window.dataLayer = window.dataLayer || []; 225 function gtag(){window.dataLayer.push(arguments);} 226 window.gtag = gtag; 227 gtag('js', new Date()); 228 gtag('config', 'G-WWJTNN1SFE'); 229 const script = document.createElement('script'); 230 script.async = true; 231 script.src = 'https://www.googletagmanager.com/gtag/js?id=G-WWJTNN1SFE'; 232 document.head.appendChild(script); 233 })(); 234</script>
234 235 236<style> 237 @import url('https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600&display=swap'); 238 239 :root { 240 /* Onboarding Black Glass - optimized for dark mode */ 241 --badge-black: rgba(0, 0, 0, 0.75); 242 --badge-black-hover: rgba(0, 0, 0, 0.85); 243 --badge-text: rgba(255, 255, 255, 0.9); 244 --badge-text-hover: rgba(255, 255, 255, 1); 245 --badge-border: rgba(255, 255, 255, 0.15); 246 --badge-border-hover: rgba(255, 255, 255, 0.25); 247 --badge-shadow: rgba(0, 0, 0, 0.4); 248 249 /* Rainbow gradient colors from Taskade Upgrade button */ 250 --rainbow-color-1: #5cb2f9; /* Blue */ 251 --rainbow-color-2: #8787f7; /* Purple-Blue */ 252 --rainbow-color-3: #a774f7; /* Purple */ 253 --rainbow-color-4: #e376a8; /* Pink */ 254 --rainbow-color-5: #eb7551; /* Orange */ 255 --rainbow-color-6: #19bd59; /* Green */ 256 --rainbow-color-7: #dee325; /* Yellow-Green */ 257 --rainbow-color-8: #e32545; /* Red */ 258 259 /* Cinematic easing */ 260 --ease-cinematic: cubic-bezier(0.22, 1, 0.36, 1); 261 --ease-cinematic-slow: cubic-bezier(0.16, 1, 0.3, 1); 262 } 263 264 [data-theme="light"] { 265 --badge-black: rgba(0, 0, 0, 0.8); 266 --badge-black-hover: rgba(0, 0, 0, 0.9); 267 --badge-text: rgba(255, 255, 255, 0.95); 268 --badge-text-hover: rgba(255, 255, 255, 1); 269 --badge-border: rgba(255, 255, 255, 0.2); 270 --badge-border-hover: rgba(255, 255, 255, 0.3); 271 --badge-shadow: rgba(0, 0, 0, 0.5); 272 } 273 274 @media (prefers-color-scheme: light) { 275 :root { 276 --badge-black: rgba(0, 0, 0, 0.8); 277 --badge-black-hover: rgba(0, 0, 0, 0.9); 278 --badge-text: rgba(255, 255, 255, 0.95); 279 --badge-text-hover: rgba(255, 255, 255, 1); 280 --badge-border: rgba(255, 255, 255, 0.2); 281 --badge-border-hover: rgba(255, 255, 255, 0.3); 282 --badge-shadow: rgba(0, 0, 0, 0.5); 283 } 284 } 285 286 /* Onboarding Black Glass Badge - Bottom Center Position */ 287 #taskade-badge { 288 position: fixed; 289 bottom: 24px; 290 left: 50%; 291 transform: translateX(-50%) scale(0.9); 292 z-index: 10001; 293 font-family: 'Geist', -apple-system, BlinkMacSystemFont, sans-serif; 294 animation: badgeGeistFadeIn 1.2s var(--ease-cinematic) forwards; 295 opacity: 0; 296 display: inline-flex; 297 align-items: center; 298 gap: 7px; 299 padding: 7px 12px; 300 font-size: 0.8125rem; 301 font-weight: 500; 302 letter-spacing: -0.01em; 303 line-height: 1; 304 color: var(--badge-text); 305 text-decoration: none; 306 background: var(--badge-black); 307 border: 1px solid var(--badge-border); 308 border-radius: 9999px; 309 backdrop-filter: blur(20px) saturate(1.4); 310 -webkit-backdrop-filter: blur(20px) saturate(1.4); 311 cursor: pointer; 312 transition: all 0.3s var(--ease-cinematic); 313 overflow: hidden; 314 will-change: transform, box-shadow; 315 white-space: nowrap; 316 } 317 318 #taskade-badge a { 319 display: flex; 320 align-items: center; 321 gap: 7px; 322 color: inherit; 323 text-decoration: none; 324 } 325 326 /* Default state with subtle rainbow glow */ 327 #taskade-badge { 328 box-shadow: 329 0 4px 16px var(--badge-shadow), 330 0 0 8px rgba(227, 118, 168, 0.2), 331 0 0 16px rgba(163, 116, 247, 0.15); 332 } 333 334 /* Black glass hover with enhanced rainbow glow */ 335 #taskade-badge:hover { 336 background: var(--badge-black-hover); 337 border-color: var(--badge-border-hover); 338 color: var(--badge-text-hover); 339 box-shadow: 340 0 8px 24px var(--badge-shadow), 341 0 0 16px rgba(227, 118, 168, 0.4), 342 0 0 32px rgba(163, 116, 247, 0.3), 343 inset 0 1px 0 rgba(255, 255, 255, 0.12); 344 transform: translateY(-3px) scale(1.03); 345 } 346 347 /* Taskade sheep mascot - optimized for dark mode */ 348 #taskade-badge #taskade-logo { 349 width: 16px; 350 height: 16px; 351 flex-shrink: 0; 352 opacity: 0.95; 353 transition: all 0.3s var(--ease-cinematic); 354 filter: invert(1) brightness(1.1) contrast(1.05); 355 } 356 357 #taskade-badge:hover #taskade-logo { 358 opacity: 1; 359 transform: scale(1.08) rotate(-2deg); 360 filter: invert(1) brightness(1.2) contrast(1.1); 361 } 362 363 /* Light mode adjustments */ 364 [data-theme="light"] #taskade-badge #taskade-logo { 365 filter: invert(0) brightness(1.1); 366 } 367 368 [data-theme="light"] #taskade-badge:hover #taskade-logo { 369 filter: invert(0) brightness(1.2); 370 } 371 372 @media (prefers-color-scheme: light) { 373 :root #taskade-badge #taskade-logo { 374 filter: invert(0) brightness(1.1); 375 } 376 377 :root #taskade-badge:hover #taskade-logo { 378 filter: invert(0) brightness(1.2); 379 } 380 } 381 382 /* Geist typography */ 383 #taskade-badge .badge-text { 384 font-weight: 500; 385 letter-spacing: 0.005em; 386 transition: all 0.3s var(--ease-cinematic); 387 -webkit-font-smoothing: antialiased; 388 -moz-osx-font-smoothing: grayscale; 389 } 390 391 #taskade-badge.hidden { 392 display: none; 393 } 394 395 /* Geist fade-in animation */ 396 @keyframes badgeGeistFadeIn { 397 0% { 398 opacity: 0; 399 transform: translateX(-50%) translateY(8px) scale(0.85); 400 filter: blur(6px); 401 } 402 60% { 403 opacity: 0.7; 404 filter: blur(2px); 405 } 406 100% { 407 opacity: 1; 408 transform: translateX(-50%) translateY(0) scale(0.9); 409 filter: blur(0); 410 } 411 } 412 413 /* Responsive */ 414 @media (max-width: 768px) { 415 #taskade-badge { 416 bottom: 80px; /* Above mobile nav/TAA chat */ 417 left: 50%; 418 transform: translateX(-50%) scale(0.85); 419 padding: 6px 10px; 420 font-size: 0.75rem; 421 gap: 5px; 422 } 423 424 #taskade-badge #taskade-logo { 425 width: 14px; 426 height: 14px; 427 } 428 } 429 430 /* Reduced motion */ 431 @media (prefers-reduced-motion: reduce) { 432 #taskade-badge { 433 animation: none; 434 opacity: 1; 435 } 436 437 #taskade-badge a, 438 #taskade-badge #taskade-logo, 439 #taskade-badge .badge-text { 440 transition: none; 441 animation: none; 442 } 443 } 444 445 /* High contrast */ 446 @media (prefers-contrast: high) { 447 #taskade-badge a { 448 border-width: 2px; 449 border-color: currentColor; 450 } 451 } 452 453 /* Hide when printing */ 454 @media print { 455 #taskade-badge { 456 display: none !important; 457 } 458 } 459</style> 460 461<div id="taskade-badge" data-badge-mode="powered_by"> 462 <a href="https://www.taskade.com/signup?utm_source=genesis_badge&utm_medium=app_badge&utm_campaign=p
462owered_by&utm_content=hukxgm8wfgw2y2mq" target="_blank" rel="noopener noreferrer"> 463 <img id="taskade-logo" src="./_assets/logo.png" alt="Taskade" /> 464 <span class="badge-text" data-community-text="Edit in Taskade" data-default-text="Powered by Taskade">Powered by Taskade</span> 465 </a> 466</div> 467
468<script> 469 (function() { 470 // Geist theme detection - matching wait.html 471 function applyTheme() { 472 const urlParams = new URLSearchParams(window.location.search); 473 let theme = urlParams.get('theme'); 474 475 if (!theme) { 476 try { 477 if (window.parent !== window) { 478 const parentTheme = window.parent.document.documentElement.getAttribute('data-theme'); 479 if (parentTheme) theme = parentTheme; 480 } 481 } catch (e) {} 482 483 if (!theme) { 484 theme = window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; 485 } 486 } 487 488 document.documentElement.setAttribute('data-theme', theme === 'light' ? 'light' : 'dark'); 489 } 490 491 // Apply theme immediately 492 applyTheme(); 493 494 // Theme change listener 495 window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', (e) => { 496 const urlParams = new URLSearchParams(window.location.search); 497 if (!urlParams.get('theme')) { 498 document.documentElement.setAttribute('data-theme', e.matches ? 'light' : 'dark'); 499 } 500 }); 501 502 // Analytics tracking and postMessage communication 503 const badgeLink = document.querySelector('#taskade-badge a'); 504 if (badgeLink) { 505 let parentOrigin = '*'; 506 let parentHostname = ''; 507 if (document.referrer) { 508 try { 509 const referrerUrl = new URL(document.referrer); 510 parentOrigin = referrerUrl.origin; 511 parentHostname = referrerUrl.hostname; 512 } catch (err) { 513 parentOrigin = '*'; 514 parentHostname = ''; 515 } 516 } 517 // Taskade itself is framing us (the share page at /app/{id}, which 518 // iframes the app). parentOrigin stays '*' when there is no usable 519 // referrer, and then the framer is unknowable. 520 const framedByTaskade = 521 parentOrigin !== '*' && 522 (parentHostname === 'taskade.com' || parentHostname.endsWith('.taskade.com')); 523 524 const badge = document.getElementById('taskade-badge'); 525 const badgeText = document.querySelector('#taskade-badge .badge-text'); 526 const communityText = badgeText ? badgeText.getAttribute('data-community-text') : null; 527 const badgeHref = badgeLink.getAttribute('href') || ''; 528 const badgeMode = badge ? badge.getAttribute('data-badge-mode') : null; 529 const isCommunityShare = 530 badgeMode === 'edit' || 531 (badgeText && 532 communityText && 533 badgeText.textContent && 534 badgeText.textContent.trim() === communityText) || 535 badgeHref.indexOf('/app/') !== -1; 536 537 if (window.parent !== window) { 538 // Hide only when embedded: on the standalone domain this badge IS the 539 // viral return path into Taskade, so it must stay visible there. 540 // Scope note, two independent reasons to hide inside a frame: 541 // 1. The Taskade share page frames the app and already renders its own 542 // Clone CTA, so ANY badge mode is a duplicate there. A buyer of a 543 // non-template listing stays on /app/{id} and sees exactly that. 544 // 2. An edit-mode badge hides in any frame, because its /app/{id} link 545 // just points back at the framing page. 546 // A powered_by badge in an unrelated third-party embed still renders - 547 // there it is the only way back to Taskade. With no referrer the 548 // framer is unknowable, so only the mode-based reason applies. 549 if ((isCommunityShare || framedByTaskade) && badge) { 550 badge.classList.add('hidden'); 551 } 552 553 const notifyParent = () => { 554 window.parent.postMessage( 555 { 556 type: 'TASKADE_BADGE_MODE', 557 source: 'taskade_badge', 558 mode: isCommunityShare ? 'edit' : 'powered_by', 559 }, 560 parentOrigin 561 ); 562 }; 563 564 try { 565 notifyParent(); 566 setTimeout(notifyParent, 500); 567 } catch (err) { 568 // Ignore postMessage failures. 569 } 570 } 571 572 badgeLink.addEventListener('click', function() { 573 // Track badge engagement 574 if (typeof window.gtag === 'function') { 575 window.gtag('event', 'genesis_badge_clicked', { 576 event_category: 'engagement', 577 event_label: 'edit_in_taskade' 578 }); 579 } 580 581 }); 582 } 583 584 })(); 585</script>
vendor: 1 bytes, line 585
585
586<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"8d4254f20c524f6fbf6ecde0b2c89fb4","spa":2}' crossorigin="anonymous"></script>
586 587</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.