working companion prototype
@@ -0,0 +1,295 @@
|
||||
<html><head><style type="text/css">
|
||||
@keyframes gridpilot-pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.85; transform: scale(1.03); }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-slide-in {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-checkered {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 20px 20px; }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-progress {
|
||||
0% { background-position: 0% 50%; }
|
||||
100% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
#gridpilot-overlay {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 340px;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
z-index: 2147483647;
|
||||
animation: gridpilot-slide-in 0.4s ease-out;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#gridpilot-overlay * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.gridpilot-card {
|
||||
background: #12121B;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(183, 183, 187, 0.2);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-header {
|
||||
background: linear-gradient(90deg, #c8102e 0%, #a00d25 100%);
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
linear-gradient(45deg, transparent 48%, rgba(255,255,255,0.05) 49%, rgba(255,255,255,0.05) 51%, transparent 52%),
|
||||
linear-gradient(-45deg, transparent 48%, rgba(255,255,255,0.05) 49%, rgba(255,255,255,0.05) 51%, transparent 52%);
|
||||
background-size: 8px 8px;
|
||||
animation: gridpilot-checkered 1.5s linear infinite;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.gridpilot-logo {
|
||||
font-size: 22px;
|
||||
animation: gridpilot-pulse 2s ease-in-out infinite;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gridpilot-title {
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.gridpilot-btn {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: #ffffff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
padding: 4px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.gridpilot-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.gridpilot-btn:active {
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.gridpilot-btn.paused {
|
||||
background: #4e4e57;
|
||||
border-color: #ffffff;
|
||||
color: #ffffff;
|
||||
animation: gridpilot-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gridpilot-close-btn {
|
||||
background: rgba(200, 16, 46, 0.6);
|
||||
border-color: rgba(200, 16, 46, 0.8);
|
||||
}
|
||||
|
||||
.gridpilot-close-btn:hover {
|
||||
background: rgba(200, 16, 46, 0.8);
|
||||
border-color: #c8102e;
|
||||
}
|
||||
|
||||
.gridpilot-close-btn:active {
|
||||
background: #c8102e;
|
||||
}
|
||||
|
||||
.gridpilot-header-buttons {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gridpilot-body {
|
||||
padding: 14px;
|
||||
background: #1a1a24;
|
||||
}
|
||||
|
||||
.gridpilot-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gridpilot-spinner {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid rgba(200, 16, 46, 0.3);
|
||||
border-top-color: #c8102e;
|
||||
border-radius: 50%;
|
||||
animation: gridpilot-spin 0.8s linear infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.gridpilot-spinner.paused {
|
||||
animation-play-state: paused;
|
||||
border-top-color: #777880;
|
||||
border-color: rgba(119, 120, 128, 0.3);
|
||||
}
|
||||
|
||||
.gridpilot-action-text {
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.gridpilot-progress-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gridpilot-progress-bar {
|
||||
height: 4px;
|
||||
background: rgba(78, 78, 87, 0.5);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #c8102e, #e8304a, #c8102e);
|
||||
background-size: 200% 100%;
|
||||
animation: gridpilot-progress 2s linear infinite;
|
||||
border-radius: 2px;
|
||||
transition: width 0.4s ease-out;
|
||||
}
|
||||
|
||||
.gridpilot-progress-fill.paused {
|
||||
animation-play-state: paused;
|
||||
background: #777880;
|
||||
}
|
||||
|
||||
.gridpilot-step-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.gridpilot-step-text {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.gridpilot-step-count {
|
||||
color: #c8102e;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.gridpilot-personality {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid rgba(183, 183, 187, 0.15);
|
||||
}
|
||||
|
||||
.gridpilot-footer {
|
||||
background: #12121B;
|
||||
padding: 8px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
border-top: 1px solid rgba(183, 183, 187, 0.1);
|
||||
}
|
||||
|
||||
.gridpilot-footer-text {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.gridpilot-footer-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #c8102e;
|
||||
border-radius: 50%;
|
||||
animation: gridpilot-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gridpilot-footer-dot.paused {
|
||||
background: #777880;
|
||||
animation: none;
|
||||
}
|
||||
</style></head><body><div id="gridpilot-overlay">
|
||||
<div class="gridpilot-card">
|
||||
<div class="gridpilot-header">
|
||||
<span class="gridpilot-logo">🏎️</span>
|
||||
<span class="gridpilot-title">GridPilot</span>
|
||||
<div class="gridpilot-header-buttons">
|
||||
<button class="gridpilot-btn gridpilot-close-btn" id="gridpilot-close-btn" onclick="(function() {
|
||||
window.__gridpilot_close_requested = true;
|
||||
})()">Stop</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridpilot-body">
|
||||
<div class="gridpilot-status">
|
||||
<div class="gridpilot-spinner"></div>
|
||||
<span class="gridpilot-action-text" id="gridpilot-action">Processing step 0...</span>
|
||||
</div>
|
||||
<div class="gridpilot-progress-container">
|
||||
<div class="gridpilot-progress-bar">
|
||||
<div class="gridpilot-progress-fill" id="gridpilot-progress" style="width: 0%"></div>
|
||||
</div>
|
||||
<div class="gridpilot-step-info">
|
||||
<span class="gridpilot-step-text" id="gridpilot-step-text">Processing step 0...</span>
|
||||
<span class="gridpilot-step-count" id="gridpilot-step-count">Step 0 of 17</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridpilot-personality" id="gridpilot-personality">🏁 Getting ready for the green flag...</div>
|
||||
</div>
|
||||
<div class="gridpilot-footer">
|
||||
<div class="gridpilot-footer-dot"></div>
|
||||
<span class="gridpilot-footer-text">Automating your session setup</span>
|
||||
</div>
|
||||
</div>
|
||||
</div></body></html>
|
||||
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 448 KiB |
|
After Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 390 KiB |
|
After Width: | Height: | Size: 389 KiB |
|
After Width: | Height: | Size: 388 KiB |
|
After Width: | Height: | Size: 320 KiB |
|
After Width: | Height: | Size: 112 KiB |
@@ -0,0 +1,173 @@
|
||||
<!DOCTYPE html><html lang="en" data-theme="light" style="color-scheme: light;"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-eval' http://127.0.0.1:32034 'nonce-4zQa3IybSQdtfcoS2bblAh5C6Vn7COVP' 'strict-dynamic'">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="https://members-assets.iracing.com/public/shared-css/0e20cfa/styles/light.min.css">
|
||||
<link rel="icon" type="image/png" href="//images-static.iracing.com/favicon.png">
|
||||
<script async="" src="https://www.googletagmanager.com/gtm.js?id=GTM-TQBRJCCM"></script><script src="https://embed.twitch.tv/embed/v1.js" nonce=""></script>
|
||||
|
||||
<script nonce="">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TQBRJCCM');</script>
|
||||
|
||||
|
||||
<meta name="theme-color" content="#CDCDCF"><style type="text/css">.indiana-scroll-container {
|
||||
overflow: auto;
|
||||
}
|
||||
.indiana-scroll-container--dragging {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
.indiana-scroll-container--dragging > * {
|
||||
pointer-events: none;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars {
|
||||
overflow: hidden;
|
||||
overflow: -moz-scrollbars-none;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
height: 0 !important;
|
||||
width: 0 !important;
|
||||
background: transparent !important;
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
.indiana-scroll-container--native-scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.indiana-dragging {
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}</style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css" data-s=""></style></head>
|
||||
|
||||
<body id="IR_I" class="clear-bg chakra-ui-light">
|
||||
|
||||
<noscript><p>This website requires javascript and cookies to be enabled to use.</p></noscript>
|
||||
|
||||
<div id="app"><script id="chakra-script">!(function(){try{var a=function(c){var v="(prefers-color-scheme: dark)",h=window.matchMedia(v).matches?"dark":"light",r=c==="system"?h:c,o=document.documentElement,s=document.body,l="chakra-ui-light",d="chakra-ui-dark",i=r==="dark";return s.classList.add(i?d:l),s.classList.remove(i?l:d),o.style.colorScheme=r,o.dataset.theme=r,r},n=a,m="light",e="chakra-ui-color-mode",t=localStorage.getItem(e);t?a(t):localStorage.setItem(e,a(m))}catch(a){}})();</script><div class="css-3klkag"><canvas id="backgrounds" class="background-image" height="1080" width="1920" style="height: 100%; left: 0px; position: fixed; top: 0px; width: 100%; z-index: -1;"></canvas><div class="css-fzhj15"><div class="css-gmuwbf"><div class="chakra-stack css-dk69dq"><div class="css-3gbbd7"><span tabindex="0" class="css-1baulvz"><div class="css-155xtsn"><svg viewBox="0 0 305 56" width="305px" height="56px"><polygon fill="#184C91" points="102.1,19.2 89.6,19.2 80.6,39 93,39 "></polygon><polygon fill="#184C91" points="105.3,12.1 92.9,12.1 90.7,16.9 103.2,16.9 "></polygon><polygon fill="#184C91" points="226.5,19.2 214.1,19.2 205,39 217.4,39 "></polygon><polygon fill="#184C91" points="229.8,12.1 217.3,12.1 215.2,16.9 227.6,16.9 "></polygon><path fill="#184C91" d="M242.5,21.6h6.2l-8,17.4h12.4l6.2-13.4c1.6-3.5,0.1-6.3-3.3-6.3h-24.9L222.1,39h12.4L242.5,21.6z"></path><path fill="#184C91" d="M167.4,19.2h-21.8l-1.1,2.4h15.6L159,24h-12.4c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-1.1,2.4
|
||||
c-1.6,3.5-0.1,6.3,3.3,6.3h24.9l6.2-13.4C172.3,22.1,170.8,19.2,167.4,19.2z M153.2,36.6H147l4.7-10.3h6.2L153.2,36.6z"></path><path fill="#184C91" d="M175.4,39h18.7c3.4,0,7.5-2.8,9.1-6.3l0.4-0.8h-12.4l-2.2,4.7h-6.2l6.9-15h6.2l-2.2,4.7h12.4l0.4-0.8
|
||||
c1.6-3.5,0.1-6.3-3.3-6.3h-18.7c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-3.3,7.1C170.5,36.2,172,39,175.4,39z"></path><path fill="#184C91" d="M273.2,19.2L273.2,19.2C273.2,19.2,273.2,19.2,273.2,19.2c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4
|
||||
l-3.3,7.1c-0.7,1.5-0.8,2.9-0.4,4c0.5,1.4,1.8,2.4,3.8,2.4h12.4l-1.1,2.4H79.3l-1.1,2.4h202.4c2,0,4.1-0.9,5.9-2.4
|
||||
c1.4-1.1,2.5-2.5,3.2-4l8.3-18.2H273.2z M277.6,36.6h-6.2l6.9-15h6.2L277.6,36.6z"></path><path fill="none" d="M70.2,13.5c0.2-0.3,0.5-0.6,0.7-0.9C70.7,12.9,70.4,13.2,70.2,13.5z"></path><path fill="none" d="M71.4,9.4c-0.1-0.1-0.2-0.2-0.2-0.3C71.2,9.2,71.3,9.3,71.4,9.4z"></path><path fill="#ffffff" d="M4.6,4.3c-0.7,0-1.1,0.4-1.6,1.1C2,6.5,2.3,6.4,2.6,7.2c0.3,0.9,0,1.6,0.8,2c0.4,0.2,0.8-0.3,0.9-0.9
|
||||
C4.6,9,4.8,9.4,5,10.2c0.3,0.4,0.8,1,1.2,0.8c0.7,0,0.9,0.5,1.1,1c-0.2,0.9,0,0.8,0.5,1.4c0.2,0.3,0.4,0.8,0.6,1
|
||||
c0.1,0.1,0.3,0.3,0.4,0.4c0.6,0.7,1,1.5,1.6,2.2c1.1,0.8,1.7,1.4,2.1,2.1c1.7,2.2,2.2,3.4,3.6,4.4c0.3,0.5,1,0.9,1.1,1.6
|
||||
c0.1,1.3,2.3,3.1,3.5,4.3c1.8,1.8,3.6,3.3,5.8,4.7c2,5.5,3.7,12.7,3.2,18.7c-0.3,1.2-0.5,2.2-0.8,3.3h19.8c-0.1-0.5-0.2-1-0.3-1.4
|
||||
c-0.1-0.3-0.2-0.7-0.2-1c0-0.2,0.2-0.4,0.2-0.7c0.4-6.2,2.2-10.3,3.4-15.6c0.2-0.9,0.4-1.7,0.3-2.1c1.4-0.9,2.7-1.5,3.7-3.2
|
||||
c2.1-1.5,3.4-3,3.6-4.5c0.7-0.7,2.3-0.6,2.3-2.1c0.7-0.7,1.1-1.3,1.3-2.1c1-1.3,2.4-2.7,2.5-3.6c1.4-1,2.3-1.7,1.8-2.8
|
||||
c0.8-0.4,1.2-1.2,1-1.9c0.6-0.7,1.7-0.8,2.1-1.5c0.2-0.3,0.5-0.6,0.7-0.9c0.2-0.2,0.4-0.4,0.5-0.6v-1.2c0,0,0-0.1,0-0.1V9.5
|
||||
c0,0,0-0.1-0.1-0.1c-0.1-0.1-0.2-0.2-0.2-0.3c0.1-0.9-0.4-1.3-1.4-1.4c0.1-0.8,0.4-1.8,0.7-2.7c0.4-0.7,0.5-1.2,0.2-1.5
|
||||
c-0.6-0.5-1,0-1.6,1.1c-0.3,0.6-0.7,1.5-1,2.1c-0.4,0.5-0.7,1-1,1.6c-0.6,0.7-1.3,1.8-1.3,2.7c-0.3,0.4-0.4,0.7-0.7,0.9
|
||||
c-0.1-0.1-0.4-0.1-0.6,0.2c-0.1,0.4-0.3,0.6-0.3,0.8c-0.7-0.3-1.2,0.3-1.6,1.5c-0.2,0.1-0.8,0.3-0.8,0.5c-0.3,0.2-0.4,0.5-0.7,0.7
|
||||
c-0.6,0.1-0.7,0.8-1.1,1.2c-0.4,0.1-0.7,0.6-0.9,1.1c-0.5,0-1,0.6-1.2,1.1c-0.9,0.5-1.2,0.9-1.5,1.7c-0.7,0.1-1.5,0.4-1.9,1.2
|
||||
c-0.7,0.4-1.5,1.2-1.8,1.9c-0.7,0.5-1.2,1.2-1.9,2.4c-0.8-0.4-1.7-0.4-2.5,0c-0.6-0.1-1.1-0.2-1.6-0.3c-0.3-0.3-0.3-0.6-0.1-0.9
|
||||
c0-0.3,0-0.7,0-1.1c0.2-0.8,0.5-1.7,0.7-2.5c0.6-1.6,0.6-3,0.4-4.4c0.1-1.3-0.1-2.5-0.6-3.9c-1.5-1.1-3.6-1.9-5.4-1.9
|
||||
c-2.2-0.3-3.8,0-5.3,0.8c-0.8,0.5-1.6,1.1-2.4,1.6c-0.9,0.4-1.7,2.5-2.5,3.8c-0.8,1.8-0.2,2,0.6,1.6h0c-1.5-0.1-0.1-1.4,0.5-1.4
|
||||
c2.1-0.2,4.4-0.2,7.7,1.9c1,0.6,2,1.1,2.9,0.8c0.4,0.5-0.4,1.2-1.4,1.5c-4.7,0.3-9.1,0.3-9.6-0.9c0.2-0.6,0.2-1.3,0.3-1.9
|
||||
c-0.1,0-0.2,0-0.2,0c0.3,0.2-0.1,1.5-0.2,2.3c-0.6,0.4-0.8,1.1-0.6,2c0.2,0.7,0.4,1.3,0.2,2.3c-0.3,0.1-0.6,0.1-0.8,0.1
|
||||
c-0.4-0.2-0.8-0.4-1.2-0.6c-0.6-0.2-1.1-0.1-1.5,0.1c-0.6-0.8-1.2-0.8-1.8-0.7c-0.4-0.3-0.8-0.7-1.2-1c-0.1-1.3-0.7-2.2-2-2.3
|
||||
c-0.5-0.4-1.1-0.6-1.6-1c0-0.3,0-0.7,0-1.1c-0.2-0.7-0.6-1.3-1.6-1.6c-0.8,0.1-1.1-0.7-1.6-1.5c-0.7-0.6-1.3-1.2-1.9-1.7
|
||||
c-0.3-0.5-1-0.7-1.3-1.2c-0.4-0.2-0.7-0.6-1.2-0.8c-0.2-0.2-0.4-0.8-0.8-1.1C13,10.9,12.7,11,12.5,11c-0.2-0.4-0.3-0.4-0.5-0.8
|
||||
c-0.5-0.4-0.9-0.7-1.4-1.1c-0.4-0.5-0.8-0.9-1.2-1.4C9.1,7.5,8.8,7.3,8.5,7C7.9,5.8,6.9,4.6,6.4,3.7C6,3.2,6.2,2.8,5.8,2.3
|
||||
c-0.2-0.3-0.3-0.7-0.5-1L4.7,0.1H4c-0.1,0.2-0.2,0.6-0.2,1C4,1.7,4.2,2.3,4.3,2.8C4.3,3.4,4.6,3.7,4.6,4.3z M31.8,25.6
|
||||
c0.7,3.1,6.4,1.8,12.3,0.2c-2.5,1.2-5,2.4-8.3,2.3C32.8,28,31.2,27.3,31.8,25.6z"></path><path fill="#184C91" d="M71.6,10.6c0,0.1-0.1,0.1-0.1,0.2c0,0.2,0.1,0.4,0.1,0.5V10.6z"></path><path fill="#184C91" d="M35.8,28.1c3.3,0.1,5.9-1.2,8.3-2.3c-5.9,1.6-11.6,2.9-12.3-0.2C31.2,27.3,32.8,28,35.8,28.1z"></path><path fill="#184C91" d="M71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4L71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.3,0.4-0.5,0.6C71.1,12.4,71.3,12.2,71.5,12z"></path><path fill="#184C91" d="M5.3,1.3c0.2,0.3,0.3,0.7,0.5,1L5.3,1.3z"></path><path fill="#184C91" d="M71.6,0h-67c0.1,0,0.1,0,0.1,0.1c0.2,0.4,0.4,0.8,0.6,1.2l0.5,1C6.2,2.8,6,3.2,6.4,3.7c0.5,1,1.5,2.2,2.1,3.4
|
||||
c0.3,0.2,0.6,0.5,0.9,0.7c0.4,0.5,0.8,0.9,1.2,1.4c0.5,0.3,0.9,0.7,1.4,1.1c0.2,0.4,0.3,0.4,0.5,0.8c0.2,0,0.5-0.1,0.8,0.2
|
||||
c0.3,0.3,0.6,0.9,0.8,1.1c0.5,0.2,0.7,0.6,1.2,0.8c0.3,0.5,1,0.7,1.3,1.2c0.7,0.5,1.3,1.2,1.9,1.7c0.5,0.8,0.7,1.6,1.6,1.5
|
||||
c1.1,0.3,1.4,1,1.6,1.6c0,0.3,0,0.7,0,1.1c0.6,0.4,1.1,0.6,1.6,1c1.2,0.1,1.9,1,2,2.3c0.4,0.3,0.8,0.7,1.2,1
|
||||
c0.6-0.1,1.2-0.1,1.8,0.7c0.4-0.2,0.9-0.3,1.5-0.1c0.4,0.2,0.8,0.4,1.2,0.6c0.3-0.1,0.6-0.1,0.8-0.1c0.2-1,0-1.7-0.2-2.3
|
||||
c-0.2-0.9,0-1.5,0.6-2c0.1-0.9,0.5-2.2,0.2-2.3c0.1,0,0.2,0,0.2,0c-0.1,0.6-0.1,1.3-0.3,1.9c0.4,1.2,4.9,1.1,9.6,0.9
|
||||
c1-0.2,1.8-1,1.4-1.5c-0.9,0.3-1.9-0.3-2.9-0.8c-3.3-2.1-5.6-2.1-7.7-1.9c-0.6,0.1-2,1.4-0.5,1.4h0c-0.8,0.4-1.4,0.2-0.6-1.6
|
||||
c0.8-1.3,1.6-3.3,2.5-3.8c0.8-0.5,1.6-1.1,2.4-1.6c1.5-0.8,3.1-1.1,5.3-0.8c1.8,0,3.9,0.9,5.4,1.9c0.6,1.3,0.7,2.5,0.6,3.9
|
||||
c0.2,1.4,0.2,2.9-0.4,4.4c-0.2,0.9-0.5,1.7-0.7,2.5c0,0.4,0,0.7,0,1.1c-0.2,0.3-0.2,0.6,0.1,0.9c0.6,0.1,1.1,0.2,1.6,0.3
|
||||
c0.9-0.4,1.8-0.4,2.5,0c0.7-1.2,1.2-1.9,1.9-2.4c0.3-0.8,1.1-1.5,1.8-1.9c0.4-0.8,1.2-1.1,1.9-1.2c0.3-0.7,0.6-1.1,1.5-1.7
|
||||
c0.2-0.5,0.7-1.1,1.2-1.1c0.2-0.4,0.5-0.9,0.9-1.1c0.4-0.4,0.5-1.1,1.1-1.2c0.3-0.2,0.4-0.5,0.7-0.7c0-0.2,0.6-0.4,0.8-0.5
|
||||
c0.4-1.2,0.9-1.8,1.6-1.5c0-0.3,0.2-0.5,0.3-0.8c0.2-0.3,0.5-0.2,0.6-0.2c0.3-0.2,0.4-0.4,0.7-0.9c0-0.9,0.7-2,1.3-2.7
|
||||
c0.3-0.7,0.6-1.1,1-1.6c0.3-0.6,0.7-1.5,1-2.1c0.6-1.1,1.1-1.7,1.6-1.1c0.3,0.3,0.2,0.8-0.2,1.5c-0.2,0.9-0.6,1.8-0.7,2.7
|
||||
c1,0.1,1.4,0.5,1.4,1.4c0.1,0.1,0.2,0.2,0.2,0.3c0,0,0,0.1,0.1,0.1v0c0,0,0.1,0.1,0.1,0.1L71.6,0z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.4,0.4-0.5,0.6c-0.3,0.3-0.5,0.6-0.7,0.9c-0.4,0.7-1.5,0.8-2.1,1.5
|
||||
c0.2,0.8-0.2,1.6-1,1.9c0.5,1.1-0.4,1.8-1.8,2.8c-0.1,0.9-1.5,2.3-2.5,3.6c-0.1,0.8-0.5,1.4-1.3,2.1c0,1.5-1.6,1.3-2.3,2.1
|
||||
c-0.2,1.6-1.6,3-3.6,4.5c-0.9,1.7-2.3,2.3-3.7,3.2c0.1,0.4-0.2,1.2-0.3,2.1c-1.2,5.3-2.9,9.4-3.4,15.6c0,0.2-0.2,0.4-0.2,0.7
|
||||
c0,0.3,0.2,0.7,0.2,1c0.1,0.5,0.2,1,0.3,1.4h23l0-44.1C71.5,11.9,71.5,12,71.5,12z"></path><path fill="#D82727" d="M26.4,34c-2.2-1.4-4-2.8-5.8-4.7c-1.2-1.2-3.4-3-3.5-4.3c-0.1-0.7-0.7-1-1.1-1.6c-1.4-1.1-1.9-2.2-3.6-4.4
|
||||
c-0.4-0.7-1-1.3-2.1-2.1c-0.6-0.7-1-1.5-1.6-2.2c-0.1-0.1-0.3-0.3-0.4-0.4c-0.2-0.1-0.4-0.6-0.6-1c-0.5-0.6-0.7-0.5-0.5-1.4
|
||||
c-0.1-0.5-0.3-1-1.1-1c-0.5,0.2-0.9-0.4-1.2-0.8C4.8,9.4,4.6,9,4.3,8.2C4.2,8.8,3.8,9.3,3.4,9.1c-0.8-0.4-0.5-1-0.8-2
|
||||
C2.3,6.4,2,6.5,2.9,5.4c0.6-0.7,0.9-1.1,1.6-1.1c0-0.6-0.3-0.9-0.3-1.4C4.2,2.3,4,1.7,3.8,1.1c0-0.5,0-1,0.3-1.1H0v56h28.8
|
||||
c0.3-1.1,0.5-2.1,0.8-3.3C30.1,46.7,28.4,39.5,26.4,34z"></path><path fill="#184C91" d="M299.9,17.8c0-0.2,0-0.3,0.1-0.5c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.1,0.2-0.2,0.4-0.3
|
||||
c0.1-0.1,0.3-0.1,0.4-0.2c0.2,0,0.3-0.1,0.5-0.1c0.2,0,0.3,0,0.5,0.1c0.2,0,0.3,0.1,0.4,0.2c0.1,0.1,0.3,0.2,0.4,0.3
|
||||
c0.1,0.1,0.2,0.2,0.3,0.4c0.1,0.1,0.1,0.3,0.2,0.4c0,0.2,0.1,0.3,0.1,0.5c0,0.2,0,0.3-0.1,0.5c0,0.2-0.1,0.3-0.2,0.4
|
||||
c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.1-0.2,0.2-0.4,0.3c-0.1,0.1-0.3,0.1-0.4,0.2s-0.3,0.1-0.5,0.1c-0.2,0-0.3,0-0.5-0.1
|
||||
c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.3-0.2-0.4-0.3c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.1-0.3-0.2-0.4
|
||||
C299.9,18.1,299.9,18,299.9,17.8z M300.2,17.8c0,0.2,0,0.4,0.1,0.6c0.1,0.2,0.2,0.3,0.3,0.5c0.1,0.1,0.3,0.2,0.5,0.3
|
||||
c0.2,0.1,0.4,0.1,0.6,0.1c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5c0.1-0.2,0.1-0.4,0.1-0.6
|
||||
c0-0.1,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.4c-0.1-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2c-0.1-0.1-0.2-0.1-0.4-0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0.1c-0.1,0-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.2-0.2,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.1,0.4C300.2,17.5,300.2,17.6,300.2,17.8z M301.4,18.1l0,0.8l-0.5,0l0-2.2l0.8,0c0.3,0,0.5,0,0.7,0.1
|
||||
c0.1,0.1,0.2,0.3,0.2,0.5c0,0.1,0,0.3-0.1,0.4c-0.1,0.1-0.2,0.2-0.3,0.2c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0.1,0.1l0.5,0.7l-0.5,0
|
||||
c-0.1,0-0.1,0-0.2-0.1l-0.4-0.6c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1,0L301.4,18.1z M301.4,17.7l0.2,0c0.1,0,0.2,0,0.2,0
|
||||
c0.1,0,0.1,0,0.1-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0-0.1c0-0.1,0-0.1,0-0.1c0,0,0-0.1-0.1-0.1c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.2,0
|
||||
l-0.3,0L301.4,17.7z"></path><path fill="#184C91" d="M110.1,39l3.3-7.1h0.8l2.2,7.1h12.4l-2.2-7.2c3.3-0.4,6.9-3,8.4-6.3l3.3-7.1c1.6-3.5,0.1-6.3-3.3-6.3H110
|
||||
L97.7,39H110.1z M121.4,14.5h6.2l-6.9,15h-6.2L121.4,14.5z"></path></svg></div></span></div><p class="chakra-text css-1mud8qf">You are not logged in.</p><div role="group" class="chakra-button__group chakra-stack css-4jt4m7" data-orientation="horizontal"><button type="button" class="chakra-button css-h9kfy" aria-label="Log in" tabindex="0"><span class="chakra-button__icon css-1wh2kri"><svg viewBox="0 0 16 16" focusable="false" class="chakra-icon css-onkibi" aria-hidden="true"><path d="M11.9999 1.5H9.74993C9.33571 1.5 8.99993 1.16421 8.99993 0.75C8.99993 0.335786 9.33571 0 9.74993 0H11.9999C13.1045 0 13.9999 0.895431 13.9999 2V14C13.9999 15.1046 13.1045 16 11.9999 16H9.74993C9.33571 16 8.99993 15.6642 8.99993 15.25C8.99993 14.8358 9.33571 14.5 9.74993 14.5H11.9999C12.2761 14.5 12.4999 14.2761 12.4999 14V2C12.4999 1.72386 12.2761 1.5 11.9999 1.5Z" fill="currentColor"></path><path d="M6.9267 4.43945C7.21959 4.14656 7.69447 4.14656 7.98736 4.43945L10.6338 7.0859C11.122 7.57406 11.122 8.36551 10.6338 8.85367L7.98736 11.5001C7.69447 11.793 7.21959 11.793 6.9267 11.5001C6.63381 11.2072 6.63381 10.7323 6.9267 10.4395L8.64637 8.71978L2.75 8.71978C2.33579 8.71978 2 8.384 2 7.96978C2 7.55557 2.33579 7.21978 2.75 7.21978L8.64637 7.21978L6.9267 5.50011C6.63381 5.20722 6.63381 4.73235 6.9267 4.43945Z" fill="currentColor"></path></svg></span>Log in</button></div></div></div></div></div><span id="__chakra_env" hidden=""></span></div>
|
||||
|
||||
<script nonce="">
|
||||
// Jumpstart theme
|
||||
(function load() {
|
||||
const cssCommitish = '0e20cfa'
|
||||
const environment = 'members'
|
||||
var chakraTheme = localStorage.getItem("chakra-ui-color-mode");
|
||||
var cookieTheme = document.cookie.replace(/(?:(?:^|.*;\s*)theme\s*=\s*([^;]*).*$)|^.*$/, "$1");
|
||||
var theme = chakraTheme || cookieTheme || "light";
|
||||
|
||||
// Browser bar style
|
||||
var meta = document.createElement("meta");
|
||||
meta.setAttribute("name", "theme-color");
|
||||
meta.setAttribute("content", theme === "dark" ? "#05050F" : "#CDCDCF");
|
||||
document.getElementsByTagName("head")[0].appendChild(meta);
|
||||
|
||||
if (theme === "dark") {
|
||||
document.documentElement.setAttribute("data-theme", "dark");
|
||||
document.querySelector(`link[href="https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/light.min.css"]`).href = `https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/dark.min.css`;
|
||||
localStorage.setItem("chakra-ui-color-mode", "dark");
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-theme", "light");
|
||||
localStorage.setItem("chakra-ui-color-mode", "light");
|
||||
}
|
||||
})();
|
||||
|
||||
// Tag Manager
|
||||
(function() {
|
||||
const tagFrame = document.createElement("iframe");
|
||||
tagFrame.style.display = "none";
|
||||
tagFrame.style.visibility = "hidden";
|
||||
document.body.appendChild(tagFrame);
|
||||
tagFrame.setAttribute("src", "https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM");
|
||||
})();
|
||||
|
||||
</script><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM" style="display: none; visibility: hidden;"></iframe>
|
||||
|
||||
<script src="/web/js/features.06672f47edf44cc1.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/modals.ae8419431eceaffb.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/partials.5bbc922034ab9879.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/content.444652900591a9f4.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/pages.9bce6de2e3069f68.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/cards.9166c4c695a3fd3c.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/echarts.0fde742e8e70602d.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/moment.4ea910c71253b76a.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/interface.b0697a5bfaa16f8e.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/vendor.893c2f1bd68de2a3.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/main.bccb09d6090f2763.min.js" nonce=""></script><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div>
|
||||
|
||||
|
||||
</body></html>
|
||||
|
After Width: | Height: | Size: 115 KiB |
@@ -0,0 +1,173 @@
|
||||
<!DOCTYPE html><html lang="en" data-theme="light" style="color-scheme: light;"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-eval' http://127.0.0.1:32034 'nonce-ZJYFfpvHPnAo6foRS7su0emkiymfWgXF' 'strict-dynamic'">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="https://members-assets.iracing.com/public/shared-css/0e20cfa/styles/light.min.css">
|
||||
<link rel="icon" type="image/png" href="//images-static.iracing.com/favicon.png">
|
||||
<script async="" src="https://www.googletagmanager.com/gtm.js?id=GTM-TQBRJCCM"></script><script src="https://embed.twitch.tv/embed/v1.js" nonce=""></script>
|
||||
|
||||
<script nonce="">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TQBRJCCM');</script>
|
||||
|
||||
|
||||
<meta name="theme-color" content="#CDCDCF"><style type="text/css">.indiana-scroll-container {
|
||||
overflow: auto;
|
||||
}
|
||||
.indiana-scroll-container--dragging {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
.indiana-scroll-container--dragging > * {
|
||||
pointer-events: none;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars {
|
||||
overflow: hidden;
|
||||
overflow: -moz-scrollbars-none;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
height: 0 !important;
|
||||
width: 0 !important;
|
||||
background: transparent !important;
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
.indiana-scroll-container--native-scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.indiana-dragging {
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}</style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css" data-s=""></style></head>
|
||||
|
||||
<body id="IR_I" class="clear-bg chakra-ui-light">
|
||||
|
||||
<noscript><p>This website requires javascript and cookies to be enabled to use.</p></noscript>
|
||||
|
||||
<div id="app"><script id="chakra-script">!(function(){try{var a=function(c){var v="(prefers-color-scheme: dark)",h=window.matchMedia(v).matches?"dark":"light",r=c==="system"?h:c,o=document.documentElement,s=document.body,l="chakra-ui-light",d="chakra-ui-dark",i=r==="dark";return s.classList.add(i?d:l),s.classList.remove(i?l:d),o.style.colorScheme=r,o.dataset.theme=r,r},n=a,m="light",e="chakra-ui-color-mode",t=localStorage.getItem(e);t?a(t):localStorage.setItem(e,a(m))}catch(a){}})();</script><div class="css-3klkag"><canvas id="backgrounds" class="background-image" height="1080" width="1920" style="height: 100%; left: 0px; position: fixed; top: 0px; width: 100%; z-index: -1;"></canvas><div class="css-fzhj15"><div class="css-gmuwbf"><div class="chakra-stack css-dk69dq"><div class="css-3gbbd7"><span tabindex="0" class="css-1baulvz"><div class="css-155xtsn"><svg viewBox="0 0 305 56" width="305px" height="56px"><polygon fill="#184C91" points="102.1,19.2 89.6,19.2 80.6,39 93,39 "></polygon><polygon fill="#184C91" points="105.3,12.1 92.9,12.1 90.7,16.9 103.2,16.9 "></polygon><polygon fill="#184C91" points="226.5,19.2 214.1,19.2 205,39 217.4,39 "></polygon><polygon fill="#184C91" points="229.8,12.1 217.3,12.1 215.2,16.9 227.6,16.9 "></polygon><path fill="#184C91" d="M242.5,21.6h6.2l-8,17.4h12.4l6.2-13.4c1.6-3.5,0.1-6.3-3.3-6.3h-24.9L222.1,39h12.4L242.5,21.6z"></path><path fill="#184C91" d="M167.4,19.2h-21.8l-1.1,2.4h15.6L159,24h-12.4c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-1.1,2.4
|
||||
c-1.6,3.5-0.1,6.3,3.3,6.3h24.9l6.2-13.4C172.3,22.1,170.8,19.2,167.4,19.2z M153.2,36.6H147l4.7-10.3h6.2L153.2,36.6z"></path><path fill="#184C91" d="M175.4,39h18.7c3.4,0,7.5-2.8,9.1-6.3l0.4-0.8h-12.4l-2.2,4.7h-6.2l6.9-15h6.2l-2.2,4.7h12.4l0.4-0.8
|
||||
c1.6-3.5,0.1-6.3-3.3-6.3h-18.7c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-3.3,7.1C170.5,36.2,172,39,175.4,39z"></path><path fill="#184C91" d="M273.2,19.2L273.2,19.2C273.2,19.2,273.2,19.2,273.2,19.2c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4
|
||||
l-3.3,7.1c-0.7,1.5-0.8,2.9-0.4,4c0.5,1.4,1.8,2.4,3.8,2.4h12.4l-1.1,2.4H79.3l-1.1,2.4h202.4c2,0,4.1-0.9,5.9-2.4
|
||||
c1.4-1.1,2.5-2.5,3.2-4l8.3-18.2H273.2z M277.6,36.6h-6.2l6.9-15h6.2L277.6,36.6z"></path><path fill="none" d="M70.2,13.5c0.2-0.3,0.5-0.6,0.7-0.9C70.7,12.9,70.4,13.2,70.2,13.5z"></path><path fill="none" d="M71.4,9.4c-0.1-0.1-0.2-0.2-0.2-0.3C71.2,9.2,71.3,9.3,71.4,9.4z"></path><path fill="#ffffff" d="M4.6,4.3c-0.7,0-1.1,0.4-1.6,1.1C2,6.5,2.3,6.4,2.6,7.2c0.3,0.9,0,1.6,0.8,2c0.4,0.2,0.8-0.3,0.9-0.9
|
||||
C4.6,9,4.8,9.4,5,10.2c0.3,0.4,0.8,1,1.2,0.8c0.7,0,0.9,0.5,1.1,1c-0.2,0.9,0,0.8,0.5,1.4c0.2,0.3,0.4,0.8,0.6,1
|
||||
c0.1,0.1,0.3,0.3,0.4,0.4c0.6,0.7,1,1.5,1.6,2.2c1.1,0.8,1.7,1.4,2.1,2.1c1.7,2.2,2.2,3.4,3.6,4.4c0.3,0.5,1,0.9,1.1,1.6
|
||||
c0.1,1.3,2.3,3.1,3.5,4.3c1.8,1.8,3.6,3.3,5.8,4.7c2,5.5,3.7,12.7,3.2,18.7c-0.3,1.2-0.5,2.2-0.8,3.3h19.8c-0.1-0.5-0.2-1-0.3-1.4
|
||||
c-0.1-0.3-0.2-0.7-0.2-1c0-0.2,0.2-0.4,0.2-0.7c0.4-6.2,2.2-10.3,3.4-15.6c0.2-0.9,0.4-1.7,0.3-2.1c1.4-0.9,2.7-1.5,3.7-3.2
|
||||
c2.1-1.5,3.4-3,3.6-4.5c0.7-0.7,2.3-0.6,2.3-2.1c0.7-0.7,1.1-1.3,1.3-2.1c1-1.3,2.4-2.7,2.5-3.6c1.4-1,2.3-1.7,1.8-2.8
|
||||
c0.8-0.4,1.2-1.2,1-1.9c0.6-0.7,1.7-0.8,2.1-1.5c0.2-0.3,0.5-0.6,0.7-0.9c0.2-0.2,0.4-0.4,0.5-0.6v-1.2c0,0,0-0.1,0-0.1V9.5
|
||||
c0,0,0-0.1-0.1-0.1c-0.1-0.1-0.2-0.2-0.2-0.3c0.1-0.9-0.4-1.3-1.4-1.4c0.1-0.8,0.4-1.8,0.7-2.7c0.4-0.7,0.5-1.2,0.2-1.5
|
||||
c-0.6-0.5-1,0-1.6,1.1c-0.3,0.6-0.7,1.5-1,2.1c-0.4,0.5-0.7,1-1,1.6c-0.6,0.7-1.3,1.8-1.3,2.7c-0.3,0.4-0.4,0.7-0.7,0.9
|
||||
c-0.1-0.1-0.4-0.1-0.6,0.2c-0.1,0.4-0.3,0.6-0.3,0.8c-0.7-0.3-1.2,0.3-1.6,1.5c-0.2,0.1-0.8,0.3-0.8,0.5c-0.3,0.2-0.4,0.5-0.7,0.7
|
||||
c-0.6,0.1-0.7,0.8-1.1,1.2c-0.4,0.1-0.7,0.6-0.9,1.1c-0.5,0-1,0.6-1.2,1.1c-0.9,0.5-1.2,0.9-1.5,1.7c-0.7,0.1-1.5,0.4-1.9,1.2
|
||||
c-0.7,0.4-1.5,1.2-1.8,1.9c-0.7,0.5-1.2,1.2-1.9,2.4c-0.8-0.4-1.7-0.4-2.5,0c-0.6-0.1-1.1-0.2-1.6-0.3c-0.3-0.3-0.3-0.6-0.1-0.9
|
||||
c0-0.3,0-0.7,0-1.1c0.2-0.8,0.5-1.7,0.7-2.5c0.6-1.6,0.6-3,0.4-4.4c0.1-1.3-0.1-2.5-0.6-3.9c-1.5-1.1-3.6-1.9-5.4-1.9
|
||||
c-2.2-0.3-3.8,0-5.3,0.8c-0.8,0.5-1.6,1.1-2.4,1.6c-0.9,0.4-1.7,2.5-2.5,3.8c-0.8,1.8-0.2,2,0.6,1.6h0c-1.5-0.1-0.1-1.4,0.5-1.4
|
||||
c2.1-0.2,4.4-0.2,7.7,1.9c1,0.6,2,1.1,2.9,0.8c0.4,0.5-0.4,1.2-1.4,1.5c-4.7,0.3-9.1,0.3-9.6-0.9c0.2-0.6,0.2-1.3,0.3-1.9
|
||||
c-0.1,0-0.2,0-0.2,0c0.3,0.2-0.1,1.5-0.2,2.3c-0.6,0.4-0.8,1.1-0.6,2c0.2,0.7,0.4,1.3,0.2,2.3c-0.3,0.1-0.6,0.1-0.8,0.1
|
||||
c-0.4-0.2-0.8-0.4-1.2-0.6c-0.6-0.2-1.1-0.1-1.5,0.1c-0.6-0.8-1.2-0.8-1.8-0.7c-0.4-0.3-0.8-0.7-1.2-1c-0.1-1.3-0.7-2.2-2-2.3
|
||||
c-0.5-0.4-1.1-0.6-1.6-1c0-0.3,0-0.7,0-1.1c-0.2-0.7-0.6-1.3-1.6-1.6c-0.8,0.1-1.1-0.7-1.6-1.5c-0.7-0.6-1.3-1.2-1.9-1.7
|
||||
c-0.3-0.5-1-0.7-1.3-1.2c-0.4-0.2-0.7-0.6-1.2-0.8c-0.2-0.2-0.4-0.8-0.8-1.1C13,10.9,12.7,11,12.5,11c-0.2-0.4-0.3-0.4-0.5-0.8
|
||||
c-0.5-0.4-0.9-0.7-1.4-1.1c-0.4-0.5-0.8-0.9-1.2-1.4C9.1,7.5,8.8,7.3,8.5,7C7.9,5.8,6.9,4.6,6.4,3.7C6,3.2,6.2,2.8,5.8,2.3
|
||||
c-0.2-0.3-0.3-0.7-0.5-1L4.7,0.1H4c-0.1,0.2-0.2,0.6-0.2,1C4,1.7,4.2,2.3,4.3,2.8C4.3,3.4,4.6,3.7,4.6,4.3z M31.8,25.6
|
||||
c0.7,3.1,6.4,1.8,12.3,0.2c-2.5,1.2-5,2.4-8.3,2.3C32.8,28,31.2,27.3,31.8,25.6z"></path><path fill="#184C91" d="M71.6,10.6c0,0.1-0.1,0.1-0.1,0.2c0,0.2,0.1,0.4,0.1,0.5V10.6z"></path><path fill="#184C91" d="M35.8,28.1c3.3,0.1,5.9-1.2,8.3-2.3c-5.9,1.6-11.6,2.9-12.3-0.2C31.2,27.3,32.8,28,35.8,28.1z"></path><path fill="#184C91" d="M71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4L71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.3,0.4-0.5,0.6C71.1,12.4,71.3,12.2,71.5,12z"></path><path fill="#184C91" d="M5.3,1.3c0.2,0.3,0.3,0.7,0.5,1L5.3,1.3z"></path><path fill="#184C91" d="M71.6,0h-67c0.1,0,0.1,0,0.1,0.1c0.2,0.4,0.4,0.8,0.6,1.2l0.5,1C6.2,2.8,6,3.2,6.4,3.7c0.5,1,1.5,2.2,2.1,3.4
|
||||
c0.3,0.2,0.6,0.5,0.9,0.7c0.4,0.5,0.8,0.9,1.2,1.4c0.5,0.3,0.9,0.7,1.4,1.1c0.2,0.4,0.3,0.4,0.5,0.8c0.2,0,0.5-0.1,0.8,0.2
|
||||
c0.3,0.3,0.6,0.9,0.8,1.1c0.5,0.2,0.7,0.6,1.2,0.8c0.3,0.5,1,0.7,1.3,1.2c0.7,0.5,1.3,1.2,1.9,1.7c0.5,0.8,0.7,1.6,1.6,1.5
|
||||
c1.1,0.3,1.4,1,1.6,1.6c0,0.3,0,0.7,0,1.1c0.6,0.4,1.1,0.6,1.6,1c1.2,0.1,1.9,1,2,2.3c0.4,0.3,0.8,0.7,1.2,1
|
||||
c0.6-0.1,1.2-0.1,1.8,0.7c0.4-0.2,0.9-0.3,1.5-0.1c0.4,0.2,0.8,0.4,1.2,0.6c0.3-0.1,0.6-0.1,0.8-0.1c0.2-1,0-1.7-0.2-2.3
|
||||
c-0.2-0.9,0-1.5,0.6-2c0.1-0.9,0.5-2.2,0.2-2.3c0.1,0,0.2,0,0.2,0c-0.1,0.6-0.1,1.3-0.3,1.9c0.4,1.2,4.9,1.1,9.6,0.9
|
||||
c1-0.2,1.8-1,1.4-1.5c-0.9,0.3-1.9-0.3-2.9-0.8c-3.3-2.1-5.6-2.1-7.7-1.9c-0.6,0.1-2,1.4-0.5,1.4h0c-0.8,0.4-1.4,0.2-0.6-1.6
|
||||
c0.8-1.3,1.6-3.3,2.5-3.8c0.8-0.5,1.6-1.1,2.4-1.6c1.5-0.8,3.1-1.1,5.3-0.8c1.8,0,3.9,0.9,5.4,1.9c0.6,1.3,0.7,2.5,0.6,3.9
|
||||
c0.2,1.4,0.2,2.9-0.4,4.4c-0.2,0.9-0.5,1.7-0.7,2.5c0,0.4,0,0.7,0,1.1c-0.2,0.3-0.2,0.6,0.1,0.9c0.6,0.1,1.1,0.2,1.6,0.3
|
||||
c0.9-0.4,1.8-0.4,2.5,0c0.7-1.2,1.2-1.9,1.9-2.4c0.3-0.8,1.1-1.5,1.8-1.9c0.4-0.8,1.2-1.1,1.9-1.2c0.3-0.7,0.6-1.1,1.5-1.7
|
||||
c0.2-0.5,0.7-1.1,1.2-1.1c0.2-0.4,0.5-0.9,0.9-1.1c0.4-0.4,0.5-1.1,1.1-1.2c0.3-0.2,0.4-0.5,0.7-0.7c0-0.2,0.6-0.4,0.8-0.5
|
||||
c0.4-1.2,0.9-1.8,1.6-1.5c0-0.3,0.2-0.5,0.3-0.8c0.2-0.3,0.5-0.2,0.6-0.2c0.3-0.2,0.4-0.4,0.7-0.9c0-0.9,0.7-2,1.3-2.7
|
||||
c0.3-0.7,0.6-1.1,1-1.6c0.3-0.6,0.7-1.5,1-2.1c0.6-1.1,1.1-1.7,1.6-1.1c0.3,0.3,0.2,0.8-0.2,1.5c-0.2,0.9-0.6,1.8-0.7,2.7
|
||||
c1,0.1,1.4,0.5,1.4,1.4c0.1,0.1,0.2,0.2,0.2,0.3c0,0,0,0.1,0.1,0.1v0c0,0,0.1,0.1,0.1,0.1L71.6,0z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.4,0.4-0.5,0.6c-0.3,0.3-0.5,0.6-0.7,0.9c-0.4,0.7-1.5,0.8-2.1,1.5
|
||||
c0.2,0.8-0.2,1.6-1,1.9c0.5,1.1-0.4,1.8-1.8,2.8c-0.1,0.9-1.5,2.3-2.5,3.6c-0.1,0.8-0.5,1.4-1.3,2.1c0,1.5-1.6,1.3-2.3,2.1
|
||||
c-0.2,1.6-1.6,3-3.6,4.5c-0.9,1.7-2.3,2.3-3.7,3.2c0.1,0.4-0.2,1.2-0.3,2.1c-1.2,5.3-2.9,9.4-3.4,15.6c0,0.2-0.2,0.4-0.2,0.7
|
||||
c0,0.3,0.2,0.7,0.2,1c0.1,0.5,0.2,1,0.3,1.4h23l0-44.1C71.5,11.9,71.5,12,71.5,12z"></path><path fill="#D82727" d="M26.4,34c-2.2-1.4-4-2.8-5.8-4.7c-1.2-1.2-3.4-3-3.5-4.3c-0.1-0.7-0.7-1-1.1-1.6c-1.4-1.1-1.9-2.2-3.6-4.4
|
||||
c-0.4-0.7-1-1.3-2.1-2.1c-0.6-0.7-1-1.5-1.6-2.2c-0.1-0.1-0.3-0.3-0.4-0.4c-0.2-0.1-0.4-0.6-0.6-1c-0.5-0.6-0.7-0.5-0.5-1.4
|
||||
c-0.1-0.5-0.3-1-1.1-1c-0.5,0.2-0.9-0.4-1.2-0.8C4.8,9.4,4.6,9,4.3,8.2C4.2,8.8,3.8,9.3,3.4,9.1c-0.8-0.4-0.5-1-0.8-2
|
||||
C2.3,6.4,2,6.5,2.9,5.4c0.6-0.7,0.9-1.1,1.6-1.1c0-0.6-0.3-0.9-0.3-1.4C4.2,2.3,4,1.7,3.8,1.1c0-0.5,0-1,0.3-1.1H0v56h28.8
|
||||
c0.3-1.1,0.5-2.1,0.8-3.3C30.1,46.7,28.4,39.5,26.4,34z"></path><path fill="#184C91" d="M299.9,17.8c0-0.2,0-0.3,0.1-0.5c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.1,0.2-0.2,0.4-0.3
|
||||
c0.1-0.1,0.3-0.1,0.4-0.2c0.2,0,0.3-0.1,0.5-0.1c0.2,0,0.3,0,0.5,0.1c0.2,0,0.3,0.1,0.4,0.2c0.1,0.1,0.3,0.2,0.4,0.3
|
||||
c0.1,0.1,0.2,0.2,0.3,0.4c0.1,0.1,0.1,0.3,0.2,0.4c0,0.2,0.1,0.3,0.1,0.5c0,0.2,0,0.3-0.1,0.5c0,0.2-0.1,0.3-0.2,0.4
|
||||
c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.1-0.2,0.2-0.4,0.3c-0.1,0.1-0.3,0.1-0.4,0.2s-0.3,0.1-0.5,0.1c-0.2,0-0.3,0-0.5-0.1
|
||||
c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.3-0.2-0.4-0.3c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.1-0.3-0.2-0.4
|
||||
C299.9,18.1,299.9,18,299.9,17.8z M300.2,17.8c0,0.2,0,0.4,0.1,0.6c0.1,0.2,0.2,0.3,0.3,0.5c0.1,0.1,0.3,0.2,0.5,0.3
|
||||
c0.2,0.1,0.4,0.1,0.6,0.1c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5c0.1-0.2,0.1-0.4,0.1-0.6
|
||||
c0-0.1,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.4c-0.1-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2c-0.1-0.1-0.2-0.1-0.4-0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0.1c-0.1,0-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.2-0.2,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.1,0.4C300.2,17.5,300.2,17.6,300.2,17.8z M301.4,18.1l0,0.8l-0.5,0l0-2.2l0.8,0c0.3,0,0.5,0,0.7,0.1
|
||||
c0.1,0.1,0.2,0.3,0.2,0.5c0,0.1,0,0.3-0.1,0.4c-0.1,0.1-0.2,0.2-0.3,0.2c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0.1,0.1l0.5,0.7l-0.5,0
|
||||
c-0.1,0-0.1,0-0.2-0.1l-0.4-0.6c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1,0L301.4,18.1z M301.4,17.7l0.2,0c0.1,0,0.2,0,0.2,0
|
||||
c0.1,0,0.1,0,0.1-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0-0.1c0-0.1,0-0.1,0-0.1c0,0,0-0.1-0.1-0.1c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.2,0
|
||||
l-0.3,0L301.4,17.7z"></path><path fill="#184C91" d="M110.1,39l3.3-7.1h0.8l2.2,7.1h12.4l-2.2-7.2c3.3-0.4,6.9-3,8.4-6.3l3.3-7.1c1.6-3.5,0.1-6.3-3.3-6.3H110
|
||||
L97.7,39H110.1z M121.4,14.5h6.2l-6.9,15h-6.2L121.4,14.5z"></path></svg></div></span></div><p class="chakra-text css-1mud8qf">You are not logged in.</p><div role="group" class="chakra-button__group chakra-stack css-4jt4m7" data-orientation="horizontal"><button type="button" class="chakra-button css-h9kfy" aria-label="Log in" tabindex="0"><span class="chakra-button__icon css-1wh2kri"><svg viewBox="0 0 16 16" focusable="false" class="chakra-icon css-onkibi" aria-hidden="true"><path d="M11.9999 1.5H9.74993C9.33571 1.5 8.99993 1.16421 8.99993 0.75C8.99993 0.335786 9.33571 0 9.74993 0H11.9999C13.1045 0 13.9999 0.895431 13.9999 2V14C13.9999 15.1046 13.1045 16 11.9999 16H9.74993C9.33571 16 8.99993 15.6642 8.99993 15.25C8.99993 14.8358 9.33571 14.5 9.74993 14.5H11.9999C12.2761 14.5 12.4999 14.2761 12.4999 14V2C12.4999 1.72386 12.2761 1.5 11.9999 1.5Z" fill="currentColor"></path><path d="M6.9267 4.43945C7.21959 4.14656 7.69447 4.14656 7.98736 4.43945L10.6338 7.0859C11.122 7.57406 11.122 8.36551 10.6338 8.85367L7.98736 11.5001C7.69447 11.793 7.21959 11.793 6.9267 11.5001C6.63381 11.2072 6.63381 10.7323 6.9267 10.4395L8.64637 8.71978L2.75 8.71978C2.33579 8.71978 2 8.384 2 7.96978C2 7.55557 2.33579 7.21978 2.75 7.21978L8.64637 7.21978L6.9267 5.50011C6.63381 5.20722 6.63381 4.73235 6.9267 4.43945Z" fill="currentColor"></path></svg></span>Log in</button></div></div></div></div></div><span id="__chakra_env" hidden=""></span></div>
|
||||
|
||||
<script nonce="">
|
||||
// Jumpstart theme
|
||||
(function load() {
|
||||
const cssCommitish = '0e20cfa'
|
||||
const environment = 'members'
|
||||
var chakraTheme = localStorage.getItem("chakra-ui-color-mode");
|
||||
var cookieTheme = document.cookie.replace(/(?:(?:^|.*;\s*)theme\s*=\s*([^;]*).*$)|^.*$/, "$1");
|
||||
var theme = chakraTheme || cookieTheme || "light";
|
||||
|
||||
// Browser bar style
|
||||
var meta = document.createElement("meta");
|
||||
meta.setAttribute("name", "theme-color");
|
||||
meta.setAttribute("content", theme === "dark" ? "#05050F" : "#CDCDCF");
|
||||
document.getElementsByTagName("head")[0].appendChild(meta);
|
||||
|
||||
if (theme === "dark") {
|
||||
document.documentElement.setAttribute("data-theme", "dark");
|
||||
document.querySelector(`link[href="https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/light.min.css"]`).href = `https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/dark.min.css`;
|
||||
localStorage.setItem("chakra-ui-color-mode", "dark");
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-theme", "light");
|
||||
localStorage.setItem("chakra-ui-color-mode", "light");
|
||||
}
|
||||
})();
|
||||
|
||||
// Tag Manager
|
||||
(function() {
|
||||
const tagFrame = document.createElement("iframe");
|
||||
tagFrame.style.display = "none";
|
||||
tagFrame.style.visibility = "hidden";
|
||||
document.body.appendChild(tagFrame);
|
||||
tagFrame.setAttribute("src", "https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM");
|
||||
})();
|
||||
|
||||
</script><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM" style="display: none; visibility: hidden;"></iframe>
|
||||
|
||||
<script src="/web/js/features.06672f47edf44cc1.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/modals.ae8419431eceaffb.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/partials.5bbc922034ab9879.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/content.444652900591a9f4.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/pages.9bce6de2e3069f68.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/cards.9166c4c695a3fd3c.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/echarts.0fde742e8e70602d.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/moment.4ea910c71253b76a.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/interface.b0697a5bfaa16f8e.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/vendor.893c2f1bd68de2a3.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/main.bccb09d6090f2763.min.js" nonce=""></script><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div>
|
||||
|
||||
|
||||
</body></html>
|
||||
|
After Width: | Height: | Size: 74 KiB |
@@ -0,0 +1,467 @@
|
||||
<!DOCTYPE html><html lang="en" data-theme="light" style="color-scheme: light;"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-eval' http://127.0.0.1:32034 'nonce-VJ6TAItV76hCmXruveYHk6ByiGGkflR2' 'strict-dynamic'">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="https://members-assets.iracing.com/public/shared-css/0e20cfa/styles/light.min.css">
|
||||
<link rel="icon" type="image/png" href="//images-static.iracing.com/favicon.png">
|
||||
<script async="" src="https://www.googletagmanager.com/gtm.js?id=GTM-TQBRJCCM"></script><script src="https://embed.twitch.tv/embed/v1.js" nonce=""></script>
|
||||
|
||||
<script nonce="">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TQBRJCCM');</script>
|
||||
|
||||
|
||||
<meta name="theme-color" content="#CDCDCF"><style type="text/css">.indiana-scroll-container {
|
||||
overflow: auto;
|
||||
}
|
||||
.indiana-scroll-container--dragging {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
.indiana-scroll-container--dragging > * {
|
||||
pointer-events: none;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars {
|
||||
overflow: hidden;
|
||||
overflow: -moz-scrollbars-none;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
height: 0 !important;
|
||||
width: 0 !important;
|
||||
background: transparent !important;
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
.indiana-scroll-container--native-scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.indiana-dragging {
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}</style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css" data-s=""></style><style type="text/css">
|
||||
@keyframes gridpilot-pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.85; transform: scale(1.03); }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-slide-in {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-checkered {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 20px 20px; }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-progress {
|
||||
0% { background-position: 0% 50%; }
|
||||
100% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
#gridpilot-overlay {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 340px;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
z-index: 2147483647;
|
||||
animation: gridpilot-slide-in 0.4s ease-out;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#gridpilot-overlay * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.gridpilot-card {
|
||||
background: #12121B;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(183, 183, 187, 0.2);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-header {
|
||||
background: linear-gradient(90deg, #c8102e 0%, #a00d25 100%);
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
linear-gradient(45deg, transparent 48%, rgba(255,255,255,0.05) 49%, rgba(255,255,255,0.05) 51%, transparent 52%),
|
||||
linear-gradient(-45deg, transparent 48%, rgba(255,255,255,0.05) 49%, rgba(255,255,255,0.05) 51%, transparent 52%);
|
||||
background-size: 8px 8px;
|
||||
animation: gridpilot-checkered 1.5s linear infinite;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.gridpilot-logo {
|
||||
font-size: 22px;
|
||||
animation: gridpilot-pulse 2s ease-in-out infinite;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gridpilot-title {
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.gridpilot-btn {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: #ffffff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
padding: 4px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.gridpilot-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.gridpilot-btn:active {
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.gridpilot-btn.paused {
|
||||
background: #4e4e57;
|
||||
border-color: #ffffff;
|
||||
color: #ffffff;
|
||||
animation: gridpilot-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gridpilot-close-btn {
|
||||
background: rgba(200, 16, 46, 0.6);
|
||||
border-color: rgba(200, 16, 46, 0.8);
|
||||
}
|
||||
|
||||
.gridpilot-close-btn:hover {
|
||||
background: rgba(200, 16, 46, 0.8);
|
||||
border-color: #c8102e;
|
||||
}
|
||||
|
||||
.gridpilot-close-btn:active {
|
||||
background: #c8102e;
|
||||
}
|
||||
|
||||
.gridpilot-header-buttons {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gridpilot-body {
|
||||
padding: 14px;
|
||||
background: #1a1a24;
|
||||
}
|
||||
|
||||
.gridpilot-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gridpilot-spinner {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid rgba(200, 16, 46, 0.3);
|
||||
border-top-color: #c8102e;
|
||||
border-radius: 50%;
|
||||
animation: gridpilot-spin 0.8s linear infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.gridpilot-spinner.paused {
|
||||
animation-play-state: paused;
|
||||
border-top-color: #777880;
|
||||
border-color: rgba(119, 120, 128, 0.3);
|
||||
}
|
||||
|
||||
.gridpilot-action-text {
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.gridpilot-progress-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gridpilot-progress-bar {
|
||||
height: 4px;
|
||||
background: rgba(78, 78, 87, 0.5);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #c8102e, #e8304a, #c8102e);
|
||||
background-size: 200% 100%;
|
||||
animation: gridpilot-progress 2s linear infinite;
|
||||
border-radius: 2px;
|
||||
transition: width 0.4s ease-out;
|
||||
}
|
||||
|
||||
.gridpilot-progress-fill.paused {
|
||||
animation-play-state: paused;
|
||||
background: #777880;
|
||||
}
|
||||
|
||||
.gridpilot-step-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.gridpilot-step-text {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.gridpilot-step-count {
|
||||
color: #c8102e;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.gridpilot-personality {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid rgba(183, 183, 187, 0.15);
|
||||
}
|
||||
|
||||
.gridpilot-footer {
|
||||
background: #12121B;
|
||||
padding: 8px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
border-top: 1px solid rgba(183, 183, 187, 0.1);
|
||||
}
|
||||
|
||||
.gridpilot-footer-text {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.gridpilot-footer-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #c8102e;
|
||||
border-radius: 50%;
|
||||
animation: gridpilot-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gridpilot-footer-dot.paused {
|
||||
background: #777880;
|
||||
animation: none;
|
||||
}
|
||||
</style></head>
|
||||
|
||||
<body id="IR_I" class="clear-bg chakra-ui-light">
|
||||
|
||||
<noscript><p>This website requires javascript and cookies to be enabled to use.</p></noscript>
|
||||
|
||||
<div id="app"><script id="chakra-script">!(function(){try{var a=function(c){var v="(prefers-color-scheme: dark)",h=window.matchMedia(v).matches?"dark":"light",r=c==="system"?h:c,o=document.documentElement,s=document.body,l="chakra-ui-light",d="chakra-ui-dark",i=r==="dark";return s.classList.add(i?d:l),s.classList.remove(i?l:d),o.style.colorScheme=r,o.dataset.theme=r,r},n=a,m="light",e="chakra-ui-color-mode",t=localStorage.getItem(e);t?a(t):localStorage.setItem(e,a(m))}catch(a){}})();</script><div class="css-3klkag"><canvas id="backgrounds" class="background-image" height="1080" width="1920" style="height: 100%; left: 0px; position: fixed; top: 0px; width: 100%; z-index: -1;"></canvas><div class="css-fzhj15"><div class="css-gmuwbf"><div class="chakra-stack css-dk69dq"><div class="css-3gbbd7"><span tabindex="0" class="css-1baulvz"><div class="css-155xtsn"><svg viewBox="0 0 305 56" width="305px" height="56px"><polygon fill="#184C91" points="102.1,19.2 89.6,19.2 80.6,39 93,39 "></polygon><polygon fill="#184C91" points="105.3,12.1 92.9,12.1 90.7,16.9 103.2,16.9 "></polygon><polygon fill="#184C91" points="226.5,19.2 214.1,19.2 205,39 217.4,39 "></polygon><polygon fill="#184C91" points="229.8,12.1 217.3,12.1 215.2,16.9 227.6,16.9 "></polygon><path fill="#184C91" d="M242.5,21.6h6.2l-8,17.4h12.4l6.2-13.4c1.6-3.5,0.1-6.3-3.3-6.3h-24.9L222.1,39h12.4L242.5,21.6z"></path><path fill="#184C91" d="M167.4,19.2h-21.8l-1.1,2.4h15.6L159,24h-12.4c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-1.1,2.4
|
||||
c-1.6,3.5-0.1,6.3,3.3,6.3h24.9l6.2-13.4C172.3,22.1,170.8,19.2,167.4,19.2z M153.2,36.6H147l4.7-10.3h6.2L153.2,36.6z"></path><path fill="#184C91" d="M175.4,39h18.7c3.4,0,7.5-2.8,9.1-6.3l0.4-0.8h-12.4l-2.2,4.7h-6.2l6.9-15h6.2l-2.2,4.7h12.4l0.4-0.8
|
||||
c1.6-3.5,0.1-6.3-3.3-6.3h-18.7c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-3.3,7.1C170.5,36.2,172,39,175.4,39z"></path><path fill="#184C91" d="M273.2,19.2L273.2,19.2C273.2,19.2,273.2,19.2,273.2,19.2c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4
|
||||
l-3.3,7.1c-0.7,1.5-0.8,2.9-0.4,4c0.5,1.4,1.8,2.4,3.8,2.4h12.4l-1.1,2.4H79.3l-1.1,2.4h202.4c2,0,4.1-0.9,5.9-2.4
|
||||
c1.4-1.1,2.5-2.5,3.2-4l8.3-18.2H273.2z M277.6,36.6h-6.2l6.9-15h6.2L277.6,36.6z"></path><path fill="none" d="M70.2,13.5c0.2-0.3,0.5-0.6,0.7-0.9C70.7,12.9,70.4,13.2,70.2,13.5z"></path><path fill="none" d="M71.4,9.4c-0.1-0.1-0.2-0.2-0.2-0.3C71.2,9.2,71.3,9.3,71.4,9.4z"></path><path fill="#ffffff" d="M4.6,4.3c-0.7,0-1.1,0.4-1.6,1.1C2,6.5,2.3,6.4,2.6,7.2c0.3,0.9,0,1.6,0.8,2c0.4,0.2,0.8-0.3,0.9-0.9
|
||||
C4.6,9,4.8,9.4,5,10.2c0.3,0.4,0.8,1,1.2,0.8c0.7,0,0.9,0.5,1.1,1c-0.2,0.9,0,0.8,0.5,1.4c0.2,0.3,0.4,0.8,0.6,1
|
||||
c0.1,0.1,0.3,0.3,0.4,0.4c0.6,0.7,1,1.5,1.6,2.2c1.1,0.8,1.7,1.4,2.1,2.1c1.7,2.2,2.2,3.4,3.6,4.4c0.3,0.5,1,0.9,1.1,1.6
|
||||
c0.1,1.3,2.3,3.1,3.5,4.3c1.8,1.8,3.6,3.3,5.8,4.7c2,5.5,3.7,12.7,3.2,18.7c-0.3,1.2-0.5,2.2-0.8,3.3h19.8c-0.1-0.5-0.2-1-0.3-1.4
|
||||
c-0.1-0.3-0.2-0.7-0.2-1c0-0.2,0.2-0.4,0.2-0.7c0.4-6.2,2.2-10.3,3.4-15.6c0.2-0.9,0.4-1.7,0.3-2.1c1.4-0.9,2.7-1.5,3.7-3.2
|
||||
c2.1-1.5,3.4-3,3.6-4.5c0.7-0.7,2.3-0.6,2.3-2.1c0.7-0.7,1.1-1.3,1.3-2.1c1-1.3,2.4-2.7,2.5-3.6c1.4-1,2.3-1.7,1.8-2.8
|
||||
c0.8-0.4,1.2-1.2,1-1.9c0.6-0.7,1.7-0.8,2.1-1.5c0.2-0.3,0.5-0.6,0.7-0.9c0.2-0.2,0.4-0.4,0.5-0.6v-1.2c0,0,0-0.1,0-0.1V9.5
|
||||
c0,0,0-0.1-0.1-0.1c-0.1-0.1-0.2-0.2-0.2-0.3c0.1-0.9-0.4-1.3-1.4-1.4c0.1-0.8,0.4-1.8,0.7-2.7c0.4-0.7,0.5-1.2,0.2-1.5
|
||||
c-0.6-0.5-1,0-1.6,1.1c-0.3,0.6-0.7,1.5-1,2.1c-0.4,0.5-0.7,1-1,1.6c-0.6,0.7-1.3,1.8-1.3,2.7c-0.3,0.4-0.4,0.7-0.7,0.9
|
||||
c-0.1-0.1-0.4-0.1-0.6,0.2c-0.1,0.4-0.3,0.6-0.3,0.8c-0.7-0.3-1.2,0.3-1.6,1.5c-0.2,0.1-0.8,0.3-0.8,0.5c-0.3,0.2-0.4,0.5-0.7,0.7
|
||||
c-0.6,0.1-0.7,0.8-1.1,1.2c-0.4,0.1-0.7,0.6-0.9,1.1c-0.5,0-1,0.6-1.2,1.1c-0.9,0.5-1.2,0.9-1.5,1.7c-0.7,0.1-1.5,0.4-1.9,1.2
|
||||
c-0.7,0.4-1.5,1.2-1.8,1.9c-0.7,0.5-1.2,1.2-1.9,2.4c-0.8-0.4-1.7-0.4-2.5,0c-0.6-0.1-1.1-0.2-1.6-0.3c-0.3-0.3-0.3-0.6-0.1-0.9
|
||||
c0-0.3,0-0.7,0-1.1c0.2-0.8,0.5-1.7,0.7-2.5c0.6-1.6,0.6-3,0.4-4.4c0.1-1.3-0.1-2.5-0.6-3.9c-1.5-1.1-3.6-1.9-5.4-1.9
|
||||
c-2.2-0.3-3.8,0-5.3,0.8c-0.8,0.5-1.6,1.1-2.4,1.6c-0.9,0.4-1.7,2.5-2.5,3.8c-0.8,1.8-0.2,2,0.6,1.6h0c-1.5-0.1-0.1-1.4,0.5-1.4
|
||||
c2.1-0.2,4.4-0.2,7.7,1.9c1,0.6,2,1.1,2.9,0.8c0.4,0.5-0.4,1.2-1.4,1.5c-4.7,0.3-9.1,0.3-9.6-0.9c0.2-0.6,0.2-1.3,0.3-1.9
|
||||
c-0.1,0-0.2,0-0.2,0c0.3,0.2-0.1,1.5-0.2,2.3c-0.6,0.4-0.8,1.1-0.6,2c0.2,0.7,0.4,1.3,0.2,2.3c-0.3,0.1-0.6,0.1-0.8,0.1
|
||||
c-0.4-0.2-0.8-0.4-1.2-0.6c-0.6-0.2-1.1-0.1-1.5,0.1c-0.6-0.8-1.2-0.8-1.8-0.7c-0.4-0.3-0.8-0.7-1.2-1c-0.1-1.3-0.7-2.2-2-2.3
|
||||
c-0.5-0.4-1.1-0.6-1.6-1c0-0.3,0-0.7,0-1.1c-0.2-0.7-0.6-1.3-1.6-1.6c-0.8,0.1-1.1-0.7-1.6-1.5c-0.7-0.6-1.3-1.2-1.9-1.7
|
||||
c-0.3-0.5-1-0.7-1.3-1.2c-0.4-0.2-0.7-0.6-1.2-0.8c-0.2-0.2-0.4-0.8-0.8-1.1C13,10.9,12.7,11,12.5,11c-0.2-0.4-0.3-0.4-0.5-0.8
|
||||
c-0.5-0.4-0.9-0.7-1.4-1.1c-0.4-0.5-0.8-0.9-1.2-1.4C9.1,7.5,8.8,7.3,8.5,7C7.9,5.8,6.9,4.6,6.4,3.7C6,3.2,6.2,2.8,5.8,2.3
|
||||
c-0.2-0.3-0.3-0.7-0.5-1L4.7,0.1H4c-0.1,0.2-0.2,0.6-0.2,1C4,1.7,4.2,2.3,4.3,2.8C4.3,3.4,4.6,3.7,4.6,4.3z M31.8,25.6
|
||||
c0.7,3.1,6.4,1.8,12.3,0.2c-2.5,1.2-5,2.4-8.3,2.3C32.8,28,31.2,27.3,31.8,25.6z"></path><path fill="#184C91" d="M71.6,10.6c0,0.1-0.1,0.1-0.1,0.2c0,0.2,0.1,0.4,0.1,0.5V10.6z"></path><path fill="#184C91" d="M35.8,28.1c3.3,0.1,5.9-1.2,8.3-2.3c-5.9,1.6-11.6,2.9-12.3-0.2C31.2,27.3,32.8,28,35.8,28.1z"></path><path fill="#184C91" d="M71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4L71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.3,0.4-0.5,0.6C71.1,12.4,71.3,12.2,71.5,12z"></path><path fill="#184C91" d="M5.3,1.3c0.2,0.3,0.3,0.7,0.5,1L5.3,1.3z"></path><path fill="#184C91" d="M71.6,0h-67c0.1,0,0.1,0,0.1,0.1c0.2,0.4,0.4,0.8,0.6,1.2l0.5,1C6.2,2.8,6,3.2,6.4,3.7c0.5,1,1.5,2.2,2.1,3.4
|
||||
c0.3,0.2,0.6,0.5,0.9,0.7c0.4,0.5,0.8,0.9,1.2,1.4c0.5,0.3,0.9,0.7,1.4,1.1c0.2,0.4,0.3,0.4,0.5,0.8c0.2,0,0.5-0.1,0.8,0.2
|
||||
c0.3,0.3,0.6,0.9,0.8,1.1c0.5,0.2,0.7,0.6,1.2,0.8c0.3,0.5,1,0.7,1.3,1.2c0.7,0.5,1.3,1.2,1.9,1.7c0.5,0.8,0.7,1.6,1.6,1.5
|
||||
c1.1,0.3,1.4,1,1.6,1.6c0,0.3,0,0.7,0,1.1c0.6,0.4,1.1,0.6,1.6,1c1.2,0.1,1.9,1,2,2.3c0.4,0.3,0.8,0.7,1.2,1
|
||||
c0.6-0.1,1.2-0.1,1.8,0.7c0.4-0.2,0.9-0.3,1.5-0.1c0.4,0.2,0.8,0.4,1.2,0.6c0.3-0.1,0.6-0.1,0.8-0.1c0.2-1,0-1.7-0.2-2.3
|
||||
c-0.2-0.9,0-1.5,0.6-2c0.1-0.9,0.5-2.2,0.2-2.3c0.1,0,0.2,0,0.2,0c-0.1,0.6-0.1,1.3-0.3,1.9c0.4,1.2,4.9,1.1,9.6,0.9
|
||||
c1-0.2,1.8-1,1.4-1.5c-0.9,0.3-1.9-0.3-2.9-0.8c-3.3-2.1-5.6-2.1-7.7-1.9c-0.6,0.1-2,1.4-0.5,1.4h0c-0.8,0.4-1.4,0.2-0.6-1.6
|
||||
c0.8-1.3,1.6-3.3,2.5-3.8c0.8-0.5,1.6-1.1,2.4-1.6c1.5-0.8,3.1-1.1,5.3-0.8c1.8,0,3.9,0.9,5.4,1.9c0.6,1.3,0.7,2.5,0.6,3.9
|
||||
c0.2,1.4,0.2,2.9-0.4,4.4c-0.2,0.9-0.5,1.7-0.7,2.5c0,0.4,0,0.7,0,1.1c-0.2,0.3-0.2,0.6,0.1,0.9c0.6,0.1,1.1,0.2,1.6,0.3
|
||||
c0.9-0.4,1.8-0.4,2.5,0c0.7-1.2,1.2-1.9,1.9-2.4c0.3-0.8,1.1-1.5,1.8-1.9c0.4-0.8,1.2-1.1,1.9-1.2c0.3-0.7,0.6-1.1,1.5-1.7
|
||||
c0.2-0.5,0.7-1.1,1.2-1.1c0.2-0.4,0.5-0.9,0.9-1.1c0.4-0.4,0.5-1.1,1.1-1.2c0.3-0.2,0.4-0.5,0.7-0.7c0-0.2,0.6-0.4,0.8-0.5
|
||||
c0.4-1.2,0.9-1.8,1.6-1.5c0-0.3,0.2-0.5,0.3-0.8c0.2-0.3,0.5-0.2,0.6-0.2c0.3-0.2,0.4-0.4,0.7-0.9c0-0.9,0.7-2,1.3-2.7
|
||||
c0.3-0.7,0.6-1.1,1-1.6c0.3-0.6,0.7-1.5,1-2.1c0.6-1.1,1.1-1.7,1.6-1.1c0.3,0.3,0.2,0.8-0.2,1.5c-0.2,0.9-0.6,1.8-0.7,2.7
|
||||
c1,0.1,1.4,0.5,1.4,1.4c0.1,0.1,0.2,0.2,0.2,0.3c0,0,0,0.1,0.1,0.1v0c0,0,0.1,0.1,0.1,0.1L71.6,0z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.4,0.4-0.5,0.6c-0.3,0.3-0.5,0.6-0.7,0.9c-0.4,0.7-1.5,0.8-2.1,1.5
|
||||
c0.2,0.8-0.2,1.6-1,1.9c0.5,1.1-0.4,1.8-1.8,2.8c-0.1,0.9-1.5,2.3-2.5,3.6c-0.1,0.8-0.5,1.4-1.3,2.1c0,1.5-1.6,1.3-2.3,2.1
|
||||
c-0.2,1.6-1.6,3-3.6,4.5c-0.9,1.7-2.3,2.3-3.7,3.2c0.1,0.4-0.2,1.2-0.3,2.1c-1.2,5.3-2.9,9.4-3.4,15.6c0,0.2-0.2,0.4-0.2,0.7
|
||||
c0,0.3,0.2,0.7,0.2,1c0.1,0.5,0.2,1,0.3,1.4h23l0-44.1C71.5,11.9,71.5,12,71.5,12z"></path><path fill="#D82727" d="M26.4,34c-2.2-1.4-4-2.8-5.8-4.7c-1.2-1.2-3.4-3-3.5-4.3c-0.1-0.7-0.7-1-1.1-1.6c-1.4-1.1-1.9-2.2-3.6-4.4
|
||||
c-0.4-0.7-1-1.3-2.1-2.1c-0.6-0.7-1-1.5-1.6-2.2c-0.1-0.1-0.3-0.3-0.4-0.4c-0.2-0.1-0.4-0.6-0.6-1c-0.5-0.6-0.7-0.5-0.5-1.4
|
||||
c-0.1-0.5-0.3-1-1.1-1c-0.5,0.2-0.9-0.4-1.2-0.8C4.8,9.4,4.6,9,4.3,8.2C4.2,8.8,3.8,9.3,3.4,9.1c-0.8-0.4-0.5-1-0.8-2
|
||||
C2.3,6.4,2,6.5,2.9,5.4c0.6-0.7,0.9-1.1,1.6-1.1c0-0.6-0.3-0.9-0.3-1.4C4.2,2.3,4,1.7,3.8,1.1c0-0.5,0-1,0.3-1.1H0v56h28.8
|
||||
c0.3-1.1,0.5-2.1,0.8-3.3C30.1,46.7,28.4,39.5,26.4,34z"></path><path fill="#184C91" d="M299.9,17.8c0-0.2,0-0.3,0.1-0.5c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.1,0.2-0.2,0.4-0.3
|
||||
c0.1-0.1,0.3-0.1,0.4-0.2c0.2,0,0.3-0.1,0.5-0.1c0.2,0,0.3,0,0.5,0.1c0.2,0,0.3,0.1,0.4,0.2c0.1,0.1,0.3,0.2,0.4,0.3
|
||||
c0.1,0.1,0.2,0.2,0.3,0.4c0.1,0.1,0.1,0.3,0.2,0.4c0,0.2,0.1,0.3,0.1,0.5c0,0.2,0,0.3-0.1,0.5c0,0.2-0.1,0.3-0.2,0.4
|
||||
c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.1-0.2,0.2-0.4,0.3c-0.1,0.1-0.3,0.1-0.4,0.2s-0.3,0.1-0.5,0.1c-0.2,0-0.3,0-0.5-0.1
|
||||
c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.3-0.2-0.4-0.3c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.1-0.3-0.2-0.4
|
||||
C299.9,18.1,299.9,18,299.9,17.8z M300.2,17.8c0,0.2,0,0.4,0.1,0.6c0.1,0.2,0.2,0.3,0.3,0.5c0.1,0.1,0.3,0.2,0.5,0.3
|
||||
c0.2,0.1,0.4,0.1,0.6,0.1c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5c0.1-0.2,0.1-0.4,0.1-0.6
|
||||
c0-0.1,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.4c-0.1-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2c-0.1-0.1-0.2-0.1-0.4-0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0.1c-0.1,0-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.2-0.2,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.1,0.4C300.2,17.5,300.2,17.6,300.2,17.8z M301.4,18.1l0,0.8l-0.5,0l0-2.2l0.8,0c0.3,0,0.5,0,0.7,0.1
|
||||
c0.1,0.1,0.2,0.3,0.2,0.5c0,0.1,0,0.3-0.1,0.4c-0.1,0.1-0.2,0.2-0.3,0.2c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0.1,0.1l0.5,0.7l-0.5,0
|
||||
c-0.1,0-0.1,0-0.2-0.1l-0.4-0.6c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1,0L301.4,18.1z M301.4,17.7l0.2,0c0.1,0,0.2,0,0.2,0
|
||||
c0.1,0,0.1,0,0.1-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0-0.1c0-0.1,0-0.1,0-0.1c0,0,0-0.1-0.1-0.1c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.2,0
|
||||
l-0.3,0L301.4,17.7z"></path><path fill="#184C91" d="M110.1,39l3.3-7.1h0.8l2.2,7.1h12.4l-2.2-7.2c3.3-0.4,6.9-3,8.4-6.3l3.3-7.1c1.6-3.5,0.1-6.3-3.3-6.3H110
|
||||
L97.7,39H110.1z M121.4,14.5h6.2l-6.9,15h-6.2L121.4,14.5z"></path></svg></div></span></div><p class="chakra-text css-1mud8qf">You are not logged in.</p><div role="group" class="chakra-button__group chakra-stack css-4jt4m7" data-orientation="horizontal"><button type="button" class="chakra-button css-h9kfy" aria-label="Log in" tabindex="0"><span class="chakra-button__icon css-1wh2kri"><svg viewBox="0 0 16 16" focusable="false" class="chakra-icon css-onkibi" aria-hidden="true"><path d="M11.9999 1.5H9.74993C9.33571 1.5 8.99993 1.16421 8.99993 0.75C8.99993 0.335786 9.33571 0 9.74993 0H11.9999C13.1045 0 13.9999 0.895431 13.9999 2V14C13.9999 15.1046 13.1045 16 11.9999 16H9.74993C9.33571 16 8.99993 15.6642 8.99993 15.25C8.99993 14.8358 9.33571 14.5 9.74993 14.5H11.9999C12.2761 14.5 12.4999 14.2761 12.4999 14V2C12.4999 1.72386 12.2761 1.5 11.9999 1.5Z" fill="currentColor"></path><path d="M6.9267 4.43945C7.21959 4.14656 7.69447 4.14656 7.98736 4.43945L10.6338 7.0859C11.122 7.57406 11.122 8.36551 10.6338 8.85367L7.98736 11.5001C7.69447 11.793 7.21959 11.793 6.9267 11.5001C6.63381 11.2072 6.63381 10.7323 6.9267 10.4395L8.64637 8.71978L2.75 8.71978C2.33579 8.71978 2 8.384 2 7.96978C2 7.55557 2.33579 7.21978 2.75 7.21978L8.64637 7.21978L6.9267 5.50011C6.63381 5.20722 6.63381 4.73235 6.9267 4.43945Z" fill="currentColor"></path></svg></span>Log in</button></div></div></div></div></div><span id="__chakra_env" hidden=""></span></div>
|
||||
|
||||
<script nonce="">
|
||||
// Jumpstart theme
|
||||
(function load() {
|
||||
const cssCommitish = '0e20cfa'
|
||||
const environment = 'members'
|
||||
var chakraTheme = localStorage.getItem("chakra-ui-color-mode");
|
||||
var cookieTheme = document.cookie.replace(/(?:(?:^|.*;\s*)theme\s*=\s*([^;]*).*$)|^.*$/, "$1");
|
||||
var theme = chakraTheme || cookieTheme || "light";
|
||||
|
||||
// Browser bar style
|
||||
var meta = document.createElement("meta");
|
||||
meta.setAttribute("name", "theme-color");
|
||||
meta.setAttribute("content", theme === "dark" ? "#05050F" : "#CDCDCF");
|
||||
document.getElementsByTagName("head")[0].appendChild(meta);
|
||||
|
||||
if (theme === "dark") {
|
||||
document.documentElement.setAttribute("data-theme", "dark");
|
||||
document.querySelector(`link[href="https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/light.min.css"]`).href = `https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/dark.min.css`;
|
||||
localStorage.setItem("chakra-ui-color-mode", "dark");
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-theme", "light");
|
||||
localStorage.setItem("chakra-ui-color-mode", "light");
|
||||
}
|
||||
})();
|
||||
|
||||
// Tag Manager
|
||||
(function() {
|
||||
const tagFrame = document.createElement("iframe");
|
||||
tagFrame.style.display = "none";
|
||||
tagFrame.style.visibility = "hidden";
|
||||
document.body.appendChild(tagFrame);
|
||||
tagFrame.setAttribute("src", "https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM");
|
||||
})();
|
||||
|
||||
</script><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM" style="display: none; visibility: hidden;"></iframe>
|
||||
|
||||
<script src="/web/js/features.06672f47edf44cc1.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/modals.ae8419431eceaffb.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/partials.5bbc922034ab9879.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/content.444652900591a9f4.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/pages.9bce6de2e3069f68.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/cards.9166c4c695a3fd3c.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/echarts.0fde742e8e70602d.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/moment.4ea910c71253b76a.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/interface.b0697a5bfaa16f8e.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/vendor.893c2f1bd68de2a3.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/main.bccb09d6090f2763.min.js" nonce=""></script><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div>
|
||||
|
||||
|
||||
<div id="gridpilot-overlay">
|
||||
<div class="gridpilot-card">
|
||||
<div class="gridpilot-header">
|
||||
<span class="gridpilot-logo">⚠️</span>
|
||||
<span class="gridpilot-title">GridPilot</span>
|
||||
<div class="gridpilot-header-buttons">
|
||||
<button class="gridpilot-btn gridpilot-close-btn" id="gridpilot-close-btn" onclick="(function() {
|
||||
window.__gridpilot_close_requested = true;
|
||||
})()">Stop</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridpilot-body">
|
||||
<div class="gridpilot-status">
|
||||
<div class="gridpilot-spinner" style="display: none;"></div>
|
||||
<span class="gridpilot-action-text" id="gridpilot-action">❌ Failed at step 2</span>
|
||||
</div>
|
||||
<div class="gridpilot-progress-container">
|
||||
<div class="gridpilot-progress-bar">
|
||||
<div class="gridpilot-progress-fill" id="gridpilot-progress" style="width: 12%;"></div>
|
||||
</div>
|
||||
<div class="gridpilot-step-info">
|
||||
<span class="gridpilot-step-text" id="gridpilot-step-text">🏁 Creating your race session...</span>
|
||||
<span class="gridpilot-step-count" id="gridpilot-step-count">Stopped</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridpilot-personality" id="gridpilot-personality">🔧 Check the error and try again.</div>
|
||||
</div>
|
||||
<div class="gridpilot-footer">
|
||||
<div class="gridpilot-footer-dot"></div>
|
||||
<span class="gridpilot-footer-text">Automating your session setup</span>
|
||||
</div>
|
||||
</div>
|
||||
</div></body></html>
|
||||
|
After Width: | Height: | Size: 265 KiB |
@@ -0,0 +1,467 @@
|
||||
<!DOCTYPE html><html lang="en" data-theme="light" style="color-scheme: light;"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-eval' http://127.0.0.1:32034 'nonce-0U8XlZNPYvqkA9ET6vpDMzmoEQKF8k80' 'strict-dynamic'">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<link rel="stylesheet" href="https://members-assets.iracing.com/public/shared-css/0e20cfa/styles/light.min.css">
|
||||
<link rel="icon" type="image/png" href="//images-static.iracing.com/favicon.png">
|
||||
<script async="" src="https://www.googletagmanager.com/gtm.js?id=GTM-TQBRJCCM"></script><script src="https://embed.twitch.tv/embed/v1.js" nonce=""></script>
|
||||
|
||||
<script nonce="">(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
|
||||
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
|
||||
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
|
||||
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
|
||||
})(window,document,'script','dataLayer','GTM-TQBRJCCM');</script>
|
||||
|
||||
|
||||
<meta name="theme-color" content="#CDCDCF"><style type="text/css">.indiana-scroll-container {
|
||||
overflow: auto;
|
||||
}
|
||||
.indiana-scroll-container--dragging {
|
||||
scroll-behavior: auto !important;
|
||||
}
|
||||
.indiana-scroll-container--dragging > * {
|
||||
pointer-events: none;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars {
|
||||
overflow: hidden;
|
||||
overflow: -moz-scrollbars-none;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.indiana-scroll-container--hide-scrollbars::-webkit-scrollbar {
|
||||
display: none !important;
|
||||
height: 0 !important;
|
||||
width: 0 !important;
|
||||
background: transparent !important;
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
.indiana-scroll-container--native-scroll {
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.indiana-dragging {
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
}</style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css-global" data-s=""></style><style data-emotion="css" data-s=""></style><style type="text/css">
|
||||
@keyframes gridpilot-pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.85; transform: scale(1.03); }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-slide-in {
|
||||
from { transform: translateX(100%); opacity: 0; }
|
||||
to { transform: translateX(0); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-checkered {
|
||||
0% { background-position: 0 0; }
|
||||
100% { background-position: 20px 20px; }
|
||||
}
|
||||
|
||||
@keyframes gridpilot-progress {
|
||||
0% { background-position: 0% 50%; }
|
||||
100% { background-position: 100% 50%; }
|
||||
}
|
||||
|
||||
#gridpilot-overlay {
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 340px;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
z-index: 2147483647;
|
||||
animation: gridpilot-slide-in 0.4s ease-out;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#gridpilot-overlay * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.gridpilot-card {
|
||||
background: #12121B;
|
||||
border-radius: 4px;
|
||||
border: 1px solid rgba(183, 183, 187, 0.2);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.6);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-header {
|
||||
background: linear-gradient(90deg, #c8102e 0%, #a00d25 100%);
|
||||
padding: 10px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-header::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background:
|
||||
linear-gradient(45deg, transparent 48%, rgba(255,255,255,0.05) 49%, rgba(255,255,255,0.05) 51%, transparent 52%),
|
||||
linear-gradient(-45deg, transparent 48%, rgba(255,255,255,0.05) 49%, rgba(255,255,255,0.05) 51%, transparent 52%);
|
||||
background-size: 8px 8px;
|
||||
animation: gridpilot-checkered 1.5s linear infinite;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.gridpilot-logo {
|
||||
font-size: 22px;
|
||||
animation: gridpilot-pulse 2s ease-in-out infinite;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gridpilot-title {
|
||||
color: #ffffff;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-shadow: 0 1px 2px rgba(0,0,0,0.3);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.gridpilot-btn {
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
color: #ffffff;
|
||||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 3px;
|
||||
padding: 4px 10px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.gridpilot-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.25);
|
||||
border-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.gridpilot-btn:active {
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
transform: scale(0.97);
|
||||
}
|
||||
|
||||
.gridpilot-btn.paused {
|
||||
background: #4e4e57;
|
||||
border-color: #ffffff;
|
||||
color: #ffffff;
|
||||
animation: gridpilot-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gridpilot-close-btn {
|
||||
background: rgba(200, 16, 46, 0.6);
|
||||
border-color: rgba(200, 16, 46, 0.8);
|
||||
}
|
||||
|
||||
.gridpilot-close-btn:hover {
|
||||
background: rgba(200, 16, 46, 0.8);
|
||||
border-color: #c8102e;
|
||||
}
|
||||
|
||||
.gridpilot-close-btn:active {
|
||||
background: #c8102e;
|
||||
}
|
||||
|
||||
.gridpilot-header-buttons {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.gridpilot-body {
|
||||
padding: 14px;
|
||||
background: #1a1a24;
|
||||
}
|
||||
|
||||
.gridpilot-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gridpilot-spinner {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
border: 2px solid rgba(200, 16, 46, 0.3);
|
||||
border-top-color: #c8102e;
|
||||
border-radius: 50%;
|
||||
animation: gridpilot-spin 0.8s linear infinite;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.gridpilot-spinner.paused {
|
||||
animation-play-state: paused;
|
||||
border-top-color: #777880;
|
||||
border-color: rgba(119, 120, 128, 0.3);
|
||||
}
|
||||
|
||||
.gridpilot-action-text {
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.gridpilot-progress-container {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.gridpilot-progress-bar {
|
||||
height: 4px;
|
||||
background: rgba(78, 78, 87, 0.5);
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gridpilot-progress-fill {
|
||||
height: 100%;
|
||||
background: linear-gradient(90deg, #c8102e, #e8304a, #c8102e);
|
||||
background-size: 200% 100%;
|
||||
animation: gridpilot-progress 2s linear infinite;
|
||||
border-radius: 2px;
|
||||
transition: width 0.4s ease-out;
|
||||
}
|
||||
|
||||
.gridpilot-progress-fill.paused {
|
||||
animation-play-state: paused;
|
||||
background: #777880;
|
||||
}
|
||||
|
||||
.gridpilot-step-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.gridpilot-step-text {
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.gridpilot-step-count {
|
||||
color: #c8102e;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.gridpilot-personality {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
text-align: center;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid rgba(183, 183, 187, 0.15);
|
||||
}
|
||||
|
||||
.gridpilot-footer {
|
||||
background: #12121B;
|
||||
padding: 8px 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
border-top: 1px solid rgba(183, 183, 187, 0.1);
|
||||
}
|
||||
|
||||
.gridpilot-footer-text {
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.gridpilot-footer-dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #c8102e;
|
||||
border-radius: 50%;
|
||||
animation: gridpilot-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.gridpilot-footer-dot.paused {
|
||||
background: #777880;
|
||||
animation: none;
|
||||
}
|
||||
</style></head>
|
||||
|
||||
<body id="IR_I" class="clear-bg chakra-ui-light">
|
||||
|
||||
<noscript><p>This website requires javascript and cookies to be enabled to use.</p></noscript>
|
||||
|
||||
<div id="app"><script id="chakra-script">!(function(){try{var a=function(c){var v="(prefers-color-scheme: dark)",h=window.matchMedia(v).matches?"dark":"light",r=c==="system"?h:c,o=document.documentElement,s=document.body,l="chakra-ui-light",d="chakra-ui-dark",i=r==="dark";return s.classList.add(i?d:l),s.classList.remove(i?l:d),o.style.colorScheme=r,o.dataset.theme=r,r},n=a,m="light",e="chakra-ui-color-mode",t=localStorage.getItem(e);t?a(t):localStorage.setItem(e,a(m))}catch(a){}})();</script><div class="css-3klkag"><canvas id="backgrounds" class="background-image" height="1080" width="1920" style="height: 100%; left: 0px; position: fixed; top: 0px; width: 100%; z-index: -1;"></canvas><div class="css-fzhj15"><div class="css-gmuwbf"><div class="chakra-stack css-dk69dq"><div class="css-3gbbd7"><span tabindex="0" class="css-1baulvz"><div class="css-155xtsn"><svg viewBox="0 0 305 56" width="305px" height="56px"><polygon fill="#184C91" points="102.1,19.2 89.6,19.2 80.6,39 93,39 "></polygon><polygon fill="#184C91" points="105.3,12.1 92.9,12.1 90.7,16.9 103.2,16.9 "></polygon><polygon fill="#184C91" points="226.5,19.2 214.1,19.2 205,39 217.4,39 "></polygon><polygon fill="#184C91" points="229.8,12.1 217.3,12.1 215.2,16.9 227.6,16.9 "></polygon><path fill="#184C91" d="M242.5,21.6h6.2l-8,17.4h12.4l6.2-13.4c1.6-3.5,0.1-6.3-3.3-6.3h-24.9L222.1,39h12.4L242.5,21.6z"></path><path fill="#184C91" d="M167.4,19.2h-21.8l-1.1,2.4h15.6L159,24h-12.4c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-1.1,2.4
|
||||
c-1.6,3.5-0.1,6.3,3.3,6.3h24.9l6.2-13.4C172.3,22.1,170.8,19.2,167.4,19.2z M153.2,36.6H147l4.7-10.3h6.2L153.2,36.6z"></path><path fill="#184C91" d="M175.4,39h18.7c3.4,0,7.5-2.8,9.1-6.3l0.4-0.8h-12.4l-2.2,4.7h-6.2l6.9-15h6.2l-2.2,4.7h12.4l0.4-0.8
|
||||
c1.6-3.5,0.1-6.3-3.3-6.3h-18.7c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4l-3.3,7.1C170.5,36.2,172,39,175.4,39z"></path><path fill="#184C91" d="M273.2,19.2L273.2,19.2C273.2,19.2,273.2,19.2,273.2,19.2c-2,0-4.1,0.9-5.9,2.4c-1.4,1.1-2.5,2.5-3.2,4
|
||||
l-3.3,7.1c-0.7,1.5-0.8,2.9-0.4,4c0.5,1.4,1.8,2.4,3.8,2.4h12.4l-1.1,2.4H79.3l-1.1,2.4h202.4c2,0,4.1-0.9,5.9-2.4
|
||||
c1.4-1.1,2.5-2.5,3.2-4l8.3-18.2H273.2z M277.6,36.6h-6.2l6.9-15h6.2L277.6,36.6z"></path><path fill="none" d="M70.2,13.5c0.2-0.3,0.5-0.6,0.7-0.9C70.7,12.9,70.4,13.2,70.2,13.5z"></path><path fill="none" d="M71.4,9.4c-0.1-0.1-0.2-0.2-0.2-0.3C71.2,9.2,71.3,9.3,71.4,9.4z"></path><path fill="#ffffff" d="M4.6,4.3c-0.7,0-1.1,0.4-1.6,1.1C2,6.5,2.3,6.4,2.6,7.2c0.3,0.9,0,1.6,0.8,2c0.4,0.2,0.8-0.3,0.9-0.9
|
||||
C4.6,9,4.8,9.4,5,10.2c0.3,0.4,0.8,1,1.2,0.8c0.7,0,0.9,0.5,1.1,1c-0.2,0.9,0,0.8,0.5,1.4c0.2,0.3,0.4,0.8,0.6,1
|
||||
c0.1,0.1,0.3,0.3,0.4,0.4c0.6,0.7,1,1.5,1.6,2.2c1.1,0.8,1.7,1.4,2.1,2.1c1.7,2.2,2.2,3.4,3.6,4.4c0.3,0.5,1,0.9,1.1,1.6
|
||||
c0.1,1.3,2.3,3.1,3.5,4.3c1.8,1.8,3.6,3.3,5.8,4.7c2,5.5,3.7,12.7,3.2,18.7c-0.3,1.2-0.5,2.2-0.8,3.3h19.8c-0.1-0.5-0.2-1-0.3-1.4
|
||||
c-0.1-0.3-0.2-0.7-0.2-1c0-0.2,0.2-0.4,0.2-0.7c0.4-6.2,2.2-10.3,3.4-15.6c0.2-0.9,0.4-1.7,0.3-2.1c1.4-0.9,2.7-1.5,3.7-3.2
|
||||
c2.1-1.5,3.4-3,3.6-4.5c0.7-0.7,2.3-0.6,2.3-2.1c0.7-0.7,1.1-1.3,1.3-2.1c1-1.3,2.4-2.7,2.5-3.6c1.4-1,2.3-1.7,1.8-2.8
|
||||
c0.8-0.4,1.2-1.2,1-1.9c0.6-0.7,1.7-0.8,2.1-1.5c0.2-0.3,0.5-0.6,0.7-0.9c0.2-0.2,0.4-0.4,0.5-0.6v-1.2c0,0,0-0.1,0-0.1V9.5
|
||||
c0,0,0-0.1-0.1-0.1c-0.1-0.1-0.2-0.2-0.2-0.3c0.1-0.9-0.4-1.3-1.4-1.4c0.1-0.8,0.4-1.8,0.7-2.7c0.4-0.7,0.5-1.2,0.2-1.5
|
||||
c-0.6-0.5-1,0-1.6,1.1c-0.3,0.6-0.7,1.5-1,2.1c-0.4,0.5-0.7,1-1,1.6c-0.6,0.7-1.3,1.8-1.3,2.7c-0.3,0.4-0.4,0.7-0.7,0.9
|
||||
c-0.1-0.1-0.4-0.1-0.6,0.2c-0.1,0.4-0.3,0.6-0.3,0.8c-0.7-0.3-1.2,0.3-1.6,1.5c-0.2,0.1-0.8,0.3-0.8,0.5c-0.3,0.2-0.4,0.5-0.7,0.7
|
||||
c-0.6,0.1-0.7,0.8-1.1,1.2c-0.4,0.1-0.7,0.6-0.9,1.1c-0.5,0-1,0.6-1.2,1.1c-0.9,0.5-1.2,0.9-1.5,1.7c-0.7,0.1-1.5,0.4-1.9,1.2
|
||||
c-0.7,0.4-1.5,1.2-1.8,1.9c-0.7,0.5-1.2,1.2-1.9,2.4c-0.8-0.4-1.7-0.4-2.5,0c-0.6-0.1-1.1-0.2-1.6-0.3c-0.3-0.3-0.3-0.6-0.1-0.9
|
||||
c0-0.3,0-0.7,0-1.1c0.2-0.8,0.5-1.7,0.7-2.5c0.6-1.6,0.6-3,0.4-4.4c0.1-1.3-0.1-2.5-0.6-3.9c-1.5-1.1-3.6-1.9-5.4-1.9
|
||||
c-2.2-0.3-3.8,0-5.3,0.8c-0.8,0.5-1.6,1.1-2.4,1.6c-0.9,0.4-1.7,2.5-2.5,3.8c-0.8,1.8-0.2,2,0.6,1.6h0c-1.5-0.1-0.1-1.4,0.5-1.4
|
||||
c2.1-0.2,4.4-0.2,7.7,1.9c1,0.6,2,1.1,2.9,0.8c0.4,0.5-0.4,1.2-1.4,1.5c-4.7,0.3-9.1,0.3-9.6-0.9c0.2-0.6,0.2-1.3,0.3-1.9
|
||||
c-0.1,0-0.2,0-0.2,0c0.3,0.2-0.1,1.5-0.2,2.3c-0.6,0.4-0.8,1.1-0.6,2c0.2,0.7,0.4,1.3,0.2,2.3c-0.3,0.1-0.6,0.1-0.8,0.1
|
||||
c-0.4-0.2-0.8-0.4-1.2-0.6c-0.6-0.2-1.1-0.1-1.5,0.1c-0.6-0.8-1.2-0.8-1.8-0.7c-0.4-0.3-0.8-0.7-1.2-1c-0.1-1.3-0.7-2.2-2-2.3
|
||||
c-0.5-0.4-1.1-0.6-1.6-1c0-0.3,0-0.7,0-1.1c-0.2-0.7-0.6-1.3-1.6-1.6c-0.8,0.1-1.1-0.7-1.6-1.5c-0.7-0.6-1.3-1.2-1.9-1.7
|
||||
c-0.3-0.5-1-0.7-1.3-1.2c-0.4-0.2-0.7-0.6-1.2-0.8c-0.2-0.2-0.4-0.8-0.8-1.1C13,10.9,12.7,11,12.5,11c-0.2-0.4-0.3-0.4-0.5-0.8
|
||||
c-0.5-0.4-0.9-0.7-1.4-1.1c-0.4-0.5-0.8-0.9-1.2-1.4C9.1,7.5,8.8,7.3,8.5,7C7.9,5.8,6.9,4.6,6.4,3.7C6,3.2,6.2,2.8,5.8,2.3
|
||||
c-0.2-0.3-0.3-0.7-0.5-1L4.7,0.1H4c-0.1,0.2-0.2,0.6-0.2,1C4,1.7,4.2,2.3,4.3,2.8C4.3,3.4,4.6,3.7,4.6,4.3z M31.8,25.6
|
||||
c0.7,3.1,6.4,1.8,12.3,0.2c-2.5,1.2-5,2.4-8.3,2.3C32.8,28,31.2,27.3,31.8,25.6z"></path><path fill="#184C91" d="M71.6,10.6c0,0.1-0.1,0.1-0.1,0.2c0,0.2,0.1,0.4,0.1,0.5V10.6z"></path><path fill="#184C91" d="M35.8,28.1c3.3,0.1,5.9-1.2,8.3-2.3c-5.9,1.6-11.6,2.9-12.3-0.2C31.2,27.3,32.8,28,35.8,28.1z"></path><path fill="#184C91" d="M71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4L71.4,9.4C71.4,9.4,71.4,9.4,71.4,9.4z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.3,0.4-0.5,0.6C71.1,12.4,71.3,12.2,71.5,12z"></path><path fill="#184C91" d="M5.3,1.3c0.2,0.3,0.3,0.7,0.5,1L5.3,1.3z"></path><path fill="#184C91" d="M71.6,0h-67c0.1,0,0.1,0,0.1,0.1c0.2,0.4,0.4,0.8,0.6,1.2l0.5,1C6.2,2.8,6,3.2,6.4,3.7c0.5,1,1.5,2.2,2.1,3.4
|
||||
c0.3,0.2,0.6,0.5,0.9,0.7c0.4,0.5,0.8,0.9,1.2,1.4c0.5,0.3,0.9,0.7,1.4,1.1c0.2,0.4,0.3,0.4,0.5,0.8c0.2,0,0.5-0.1,0.8,0.2
|
||||
c0.3,0.3,0.6,0.9,0.8,1.1c0.5,0.2,0.7,0.6,1.2,0.8c0.3,0.5,1,0.7,1.3,1.2c0.7,0.5,1.3,1.2,1.9,1.7c0.5,0.8,0.7,1.6,1.6,1.5
|
||||
c1.1,0.3,1.4,1,1.6,1.6c0,0.3,0,0.7,0,1.1c0.6,0.4,1.1,0.6,1.6,1c1.2,0.1,1.9,1,2,2.3c0.4,0.3,0.8,0.7,1.2,1
|
||||
c0.6-0.1,1.2-0.1,1.8,0.7c0.4-0.2,0.9-0.3,1.5-0.1c0.4,0.2,0.8,0.4,1.2,0.6c0.3-0.1,0.6-0.1,0.8-0.1c0.2-1,0-1.7-0.2-2.3
|
||||
c-0.2-0.9,0-1.5,0.6-2c0.1-0.9,0.5-2.2,0.2-2.3c0.1,0,0.2,0,0.2,0c-0.1,0.6-0.1,1.3-0.3,1.9c0.4,1.2,4.9,1.1,9.6,0.9
|
||||
c1-0.2,1.8-1,1.4-1.5c-0.9,0.3-1.9-0.3-2.9-0.8c-3.3-2.1-5.6-2.1-7.7-1.9c-0.6,0.1-2,1.4-0.5,1.4h0c-0.8,0.4-1.4,0.2-0.6-1.6
|
||||
c0.8-1.3,1.6-3.3,2.5-3.8c0.8-0.5,1.6-1.1,2.4-1.6c1.5-0.8,3.1-1.1,5.3-0.8c1.8,0,3.9,0.9,5.4,1.9c0.6,1.3,0.7,2.5,0.6,3.9
|
||||
c0.2,1.4,0.2,2.9-0.4,4.4c-0.2,0.9-0.5,1.7-0.7,2.5c0,0.4,0,0.7,0,1.1c-0.2,0.3-0.2,0.6,0.1,0.9c0.6,0.1,1.1,0.2,1.6,0.3
|
||||
c0.9-0.4,1.8-0.4,2.5,0c0.7-1.2,1.2-1.9,1.9-2.4c0.3-0.8,1.1-1.5,1.8-1.9c0.4-0.8,1.2-1.1,1.9-1.2c0.3-0.7,0.6-1.1,1.5-1.7
|
||||
c0.2-0.5,0.7-1.1,1.2-1.1c0.2-0.4,0.5-0.9,0.9-1.1c0.4-0.4,0.5-1.1,1.1-1.2c0.3-0.2,0.4-0.5,0.7-0.7c0-0.2,0.6-0.4,0.8-0.5
|
||||
c0.4-1.2,0.9-1.8,1.6-1.5c0-0.3,0.2-0.5,0.3-0.8c0.2-0.3,0.5-0.2,0.6-0.2c0.3-0.2,0.4-0.4,0.7-0.9c0-0.9,0.7-2,1.3-2.7
|
||||
c0.3-0.7,0.6-1.1,1-1.6c0.3-0.6,0.7-1.5,1-2.1c0.6-1.1,1.1-1.7,1.6-1.1c0.3,0.3,0.2,0.8-0.2,1.5c-0.2,0.9-0.6,1.8-0.7,2.7
|
||||
c1,0.1,1.4,0.5,1.4,1.4c0.1,0.1,0.2,0.2,0.2,0.3c0,0,0,0.1,0.1,0.1v0c0,0,0.1,0.1,0.1,0.1L71.6,0z"></path><path fill="#184C91" d="M71.5,12L71.5,12c-0.2,0.2-0.4,0.4-0.5,0.6c-0.3,0.3-0.5,0.6-0.7,0.9c-0.4,0.7-1.5,0.8-2.1,1.5
|
||||
c0.2,0.8-0.2,1.6-1,1.9c0.5,1.1-0.4,1.8-1.8,2.8c-0.1,0.9-1.5,2.3-2.5,3.6c-0.1,0.8-0.5,1.4-1.3,2.1c0,1.5-1.6,1.3-2.3,2.1
|
||||
c-0.2,1.6-1.6,3-3.6,4.5c-0.9,1.7-2.3,2.3-3.7,3.2c0.1,0.4-0.2,1.2-0.3,2.1c-1.2,5.3-2.9,9.4-3.4,15.6c0,0.2-0.2,0.4-0.2,0.7
|
||||
c0,0.3,0.2,0.7,0.2,1c0.1,0.5,0.2,1,0.3,1.4h23l0-44.1C71.5,11.9,71.5,12,71.5,12z"></path><path fill="#D82727" d="M26.4,34c-2.2-1.4-4-2.8-5.8-4.7c-1.2-1.2-3.4-3-3.5-4.3c-0.1-0.7-0.7-1-1.1-1.6c-1.4-1.1-1.9-2.2-3.6-4.4
|
||||
c-0.4-0.7-1-1.3-2.1-2.1c-0.6-0.7-1-1.5-1.6-2.2c-0.1-0.1-0.3-0.3-0.4-0.4c-0.2-0.1-0.4-0.6-0.6-1c-0.5-0.6-0.7-0.5-0.5-1.4
|
||||
c-0.1-0.5-0.3-1-1.1-1c-0.5,0.2-0.9-0.4-1.2-0.8C4.8,9.4,4.6,9,4.3,8.2C4.2,8.8,3.8,9.3,3.4,9.1c-0.8-0.4-0.5-1-0.8-2
|
||||
C2.3,6.4,2,6.5,2.9,5.4c0.6-0.7,0.9-1.1,1.6-1.1c0-0.6-0.3-0.9-0.3-1.4C4.2,2.3,4,1.7,3.8,1.1c0-0.5,0-1,0.3-1.1H0v56h28.8
|
||||
c0.3-1.1,0.5-2.1,0.8-3.3C30.1,46.7,28.4,39.5,26.4,34z"></path><path fill="#184C91" d="M299.9,17.8c0-0.2,0-0.3,0.1-0.5c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.2-0.3,0.3-0.4c0.1-0.1,0.2-0.2,0.4-0.3
|
||||
c0.1-0.1,0.3-0.1,0.4-0.2c0.2,0,0.3-0.1,0.5-0.1c0.2,0,0.3,0,0.5,0.1c0.2,0,0.3,0.1,0.4,0.2c0.1,0.1,0.3,0.2,0.4,0.3
|
||||
c0.1,0.1,0.2,0.2,0.3,0.4c0.1,0.1,0.1,0.3,0.2,0.4c0,0.2,0.1,0.3,0.1,0.5c0,0.2,0,0.3-0.1,0.5c0,0.2-0.1,0.3-0.2,0.4
|
||||
c-0.1,0.1-0.2,0.3-0.3,0.4c-0.1,0.1-0.2,0.2-0.4,0.3c-0.1,0.1-0.3,0.1-0.4,0.2s-0.3,0.1-0.5,0.1c-0.2,0-0.3,0-0.5-0.1
|
||||
c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.3-0.2-0.4-0.3c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.1-0.3-0.2-0.4
|
||||
C299.9,18.1,299.9,18,299.9,17.8z M300.2,17.8c0,0.2,0,0.4,0.1,0.6c0.1,0.2,0.2,0.3,0.3,0.5c0.1,0.1,0.3,0.2,0.5,0.3
|
||||
c0.2,0.1,0.4,0.1,0.6,0.1c0.2,0,0.4,0,0.6-0.1c0.2-0.1,0.3-0.2,0.5-0.3c0.1-0.1,0.2-0.3,0.3-0.5c0.1-0.2,0.1-0.4,0.1-0.6
|
||||
c0-0.1,0-0.3-0.1-0.4c0-0.1-0.1-0.2-0.2-0.4c-0.1-0.1-0.1-0.2-0.2-0.3c-0.1-0.1-0.2-0.2-0.3-0.2c-0.1-0.1-0.2-0.1-0.4-0.1
|
||||
c-0.1,0-0.3,0-0.4,0c-0.1,0-0.3,0-0.4,0.1c-0.1,0-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.1-0.3,0.2c-0.1,0.1-0.2,0.2-0.2,0.3
|
||||
c-0.1,0.1-0.1,0.2-0.1,0.4C300.2,17.5,300.2,17.6,300.2,17.8z M301.4,18.1l0,0.8l-0.5,0l0-2.2l0.8,0c0.3,0,0.5,0,0.7,0.1
|
||||
c0.1,0.1,0.2,0.3,0.2,0.5c0,0.1,0,0.3-0.1,0.4c-0.1,0.1-0.2,0.2-0.3,0.2c0,0,0.1,0,0.1,0.1c0,0,0,0.1,0.1,0.1l0.5,0.7l-0.5,0
|
||||
c-0.1,0-0.1,0-0.2-0.1l-0.4-0.6c0,0,0,0-0.1-0.1c0,0-0.1,0-0.1,0L301.4,18.1z M301.4,17.7l0.2,0c0.1,0,0.2,0,0.2,0
|
||||
c0.1,0,0.1,0,0.1-0.1c0,0,0-0.1,0.1-0.1c0,0,0-0.1,0-0.1c0-0.1,0-0.1,0-0.1c0,0,0-0.1-0.1-0.1c0,0-0.1,0-0.1-0.1c0,0-0.1,0-0.2,0
|
||||
l-0.3,0L301.4,17.7z"></path><path fill="#184C91" d="M110.1,39l3.3-7.1h0.8l2.2,7.1h12.4l-2.2-7.2c3.3-0.4,6.9-3,8.4-6.3l3.3-7.1c1.6-3.5,0.1-6.3-3.3-6.3H110
|
||||
L97.7,39H110.1z M121.4,14.5h6.2l-6.9,15h-6.2L121.4,14.5z"></path></svg></div></span></div><p class="chakra-text css-1mud8qf">You are not logged in.</p><div role="group" class="chakra-button__group chakra-stack css-4jt4m7" data-orientation="horizontal"><button type="button" class="chakra-button css-h9kfy" aria-label="Log in" tabindex="0"><span class="chakra-button__icon css-1wh2kri"><svg viewBox="0 0 16 16" focusable="false" class="chakra-icon css-onkibi" aria-hidden="true"><path d="M11.9999 1.5H9.74993C9.33571 1.5 8.99993 1.16421 8.99993 0.75C8.99993 0.335786 9.33571 0 9.74993 0H11.9999C13.1045 0 13.9999 0.895431 13.9999 2V14C13.9999 15.1046 13.1045 16 11.9999 16H9.74993C9.33571 16 8.99993 15.6642 8.99993 15.25C8.99993 14.8358 9.33571 14.5 9.74993 14.5H11.9999C12.2761 14.5 12.4999 14.2761 12.4999 14V2C12.4999 1.72386 12.2761 1.5 11.9999 1.5Z" fill="currentColor"></path><path d="M6.9267 4.43945C7.21959 4.14656 7.69447 4.14656 7.98736 4.43945L10.6338 7.0859C11.122 7.57406 11.122 8.36551 10.6338 8.85367L7.98736 11.5001C7.69447 11.793 7.21959 11.793 6.9267 11.5001C6.63381 11.2072 6.63381 10.7323 6.9267 10.4395L8.64637 8.71978L2.75 8.71978C2.33579 8.71978 2 8.384 2 7.96978C2 7.55557 2.33579 7.21978 2.75 7.21978L8.64637 7.21978L6.9267 5.50011C6.63381 5.20722 6.63381 4.73235 6.9267 4.43945Z" fill="currentColor"></path></svg></span>Log in</button></div></div></div></div></div><span id="__chakra_env" hidden=""></span></div>
|
||||
|
||||
<script nonce="">
|
||||
// Jumpstart theme
|
||||
(function load() {
|
||||
const cssCommitish = '0e20cfa'
|
||||
const environment = 'members'
|
||||
var chakraTheme = localStorage.getItem("chakra-ui-color-mode");
|
||||
var cookieTheme = document.cookie.replace(/(?:(?:^|.*;\s*)theme\s*=\s*([^;]*).*$)|^.*$/, "$1");
|
||||
var theme = chakraTheme || cookieTheme || "light";
|
||||
|
||||
// Browser bar style
|
||||
var meta = document.createElement("meta");
|
||||
meta.setAttribute("name", "theme-color");
|
||||
meta.setAttribute("content", theme === "dark" ? "#05050F" : "#CDCDCF");
|
||||
document.getElementsByTagName("head")[0].appendChild(meta);
|
||||
|
||||
if (theme === "dark") {
|
||||
document.documentElement.setAttribute("data-theme", "dark");
|
||||
document.querySelector(`link[href="https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/light.min.css"]`).href = `https://${environment}-assets.iracing.com/public/shared-css/${cssCommitish}/styles/dark.min.css`;
|
||||
localStorage.setItem("chakra-ui-color-mode", "dark");
|
||||
} else {
|
||||
document.documentElement.setAttribute("data-theme", "light");
|
||||
localStorage.setItem("chakra-ui-color-mode", "light");
|
||||
}
|
||||
})();
|
||||
|
||||
// Tag Manager
|
||||
(function() {
|
||||
const tagFrame = document.createElement("iframe");
|
||||
tagFrame.style.display = "none";
|
||||
tagFrame.style.visibility = "hidden";
|
||||
document.body.appendChild(tagFrame);
|
||||
tagFrame.setAttribute("src", "https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM");
|
||||
})();
|
||||
|
||||
</script><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-TQBRJCCM" style="display: none; visibility: hidden;"></iframe>
|
||||
|
||||
<script src="/web/js/features.06672f47edf44cc1.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/modals.ae8419431eceaffb.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/partials.5bbc922034ab9879.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/content.444652900591a9f4.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/pages.9bce6de2e3069f68.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/cards.9166c4c695a3fd3c.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/echarts.0fde742e8e70602d.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/moment.4ea910c71253b76a.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/interface.b0697a5bfaa16f8e.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/vendor.893c2f1bd68de2a3.min.js" nonce=""></script>
|
||||
|
||||
<script src="/web/js/main.bccb09d6090f2763.min.js" nonce=""></script><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div><div class="chakra-portal"><div role="region" aria-live="polite" aria-label="Notifications-top" id="chakra-toast-manager-top" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-left" id="chakra-toast-manager-top-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-top-right" id="chakra-toast-manager-top-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; top: env(safe-area-inset-top, 0px); right: env(safe-area-inset-right, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-left" id="chakra-toast-manager-bottom-left" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom" id="chakra-toast-manager-bottom" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; margin: 0px auto; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px); left: env(safe-area-inset-left, 0px);"></div><div role="region" aria-live="polite" aria-label="Notifications-bottom-right" id="chakra-toast-manager-bottom-right" style="position: fixed; z-index: var(--toast-z-index, 5500); pointer-events: none; display: flex; flex-direction: column; bottom: env(safe-area-inset-bottom, 0px); right: env(safe-area-inset-right, 0px);"></div></div>
|
||||
|
||||
|
||||
<div id="gridpilot-overlay">
|
||||
<div class="gridpilot-card">
|
||||
<div class="gridpilot-header">
|
||||
<span class="gridpilot-logo">⚠️</span>
|
||||
<span class="gridpilot-title">GridPilot</span>
|
||||
<div class="gridpilot-header-buttons">
|
||||
<button class="gridpilot-btn gridpilot-close-btn" id="gridpilot-close-btn" onclick="(function() {
|
||||
window.__gridpilot_close_requested = true;
|
||||
})()">Stop</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridpilot-body">
|
||||
<div class="gridpilot-status">
|
||||
<div class="gridpilot-spinner" style="display: none;"></div>
|
||||
<span class="gridpilot-action-text" id="gridpilot-action">❌ Failed at step 2</span>
|
||||
</div>
|
||||
<div class="gridpilot-progress-container">
|
||||
<div class="gridpilot-progress-bar">
|
||||
<div class="gridpilot-progress-fill" id="gridpilot-progress" style="width: 12%;"></div>
|
||||
</div>
|
||||
<div class="gridpilot-step-info">
|
||||
<span class="gridpilot-step-text" id="gridpilot-step-text">🏁 Creating your race session...</span>
|
||||
<span class="gridpilot-step-count" id="gridpilot-step-count">Stopped</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="gridpilot-personality" id="gridpilot-personality">🔧 Check the error and try again.</div>
|
||||
</div>
|
||||
<div class="gridpilot-footer">
|
||||
<div class="gridpilot-footer-dot"></div>
|
||||
<span class="gridpilot-footer-text">Automating your session setup</span>
|
||||
</div>
|
||||
</div>
|
||||
</div></body></html>
|
||||
|
After Width: | Height: | Size: 224 KiB |
@@ -23,6 +23,8 @@ export default defineConfig({
|
||||
'@nut-tree-fork/shared',
|
||||
'bufferutil',
|
||||
'utf-8-validate',
|
||||
'playwright',
|
||||
'playwright-core',
|
||||
],
|
||||
output: {
|
||||
entryFileNames: 'main.cjs',
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import { app } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { InMemorySessionRepository } from '@/packages/infrastructure/repositories/InMemorySessionRepository';
|
||||
import { MockBrowserAutomationAdapter } from '@/packages/infrastructure/adapters/automation/MockBrowserAutomationAdapter';
|
||||
import { NutJsAutomationAdapter } from '@/packages/infrastructure/adapters/automation/NutJsAutomationAdapter';
|
||||
import { PlaywrightAutomationAdapter, AutomationAdapterMode } from '@/packages/infrastructure/adapters/automation/PlaywrightAutomationAdapter';
|
||||
import { MockAutomationEngineAdapter } from '@/packages/infrastructure/adapters/automation/MockAutomationEngineAdapter';
|
||||
import { PermissionService } from '@/packages/infrastructure/adapters/automation/PermissionService';
|
||||
import { StartAutomationSessionUseCase } from '@/packages/application/use-cases/StartAutomationSessionUseCase';
|
||||
import { CheckAuthenticationUseCase } from '@/packages/application/use-cases/CheckAuthenticationUseCase';
|
||||
import { InitiateLoginUseCase } from '@/packages/application/use-cases/InitiateLoginUseCase';
|
||||
import { ClearSessionUseCase } from '@/packages/application/use-cases/ClearSessionUseCase';
|
||||
import { loadAutomationConfig, getAutomationMode, AutomationMode } from '@/packages/infrastructure/config';
|
||||
import { PinoLogAdapter } from '@/packages/infrastructure/adapters/logging/PinoLogAdapter';
|
||||
import { NoOpLogAdapter } from '@/packages/infrastructure/adapters/logging/NoOpLogAdapter';
|
||||
@@ -11,6 +15,7 @@ import { loadLoggingConfig } from '@/packages/infrastructure/config/LoggingConfi
|
||||
import type { ISessionRepository } from '@/packages/application/ports/ISessionRepository';
|
||||
import type { IScreenAutomation } from '@/packages/application/ports/IScreenAutomation';
|
||||
import type { IAutomationEngine } from '@/packages/application/ports/IAutomationEngine';
|
||||
import type { IAuthenticationService } from '@/packages/application/ports/IAuthenticationService';
|
||||
import type { ILogger } from '@/packages/application/ports/ILogger';
|
||||
|
||||
export interface BrowserConnectionResult {
|
||||
@@ -18,6 +23,39 @@ export interface BrowserConnectionResult {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the path to store persistent browser session data.
|
||||
* Uses Electron's userData directory for secure, per-user storage.
|
||||
*
|
||||
* @returns Absolute path to the iracing session directory
|
||||
*/
|
||||
function resolveSessionDataPath(): string {
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'iracing-session');
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the absolute path to the template directory.
|
||||
* Handles both development and production (packaged) Electron environments.
|
||||
*
|
||||
* @returns Absolute path to the iracing templates directory
|
||||
*/
|
||||
function resolveTemplatePath(): string {
|
||||
// In packaged app, app.getAppPath() returns the path to the app.asar or unpacked directory
|
||||
// In development, it returns the path to the app directory (apps/companion)
|
||||
const appPath = app.getAppPath();
|
||||
|
||||
if (app.isPackaged) {
|
||||
// Production: resources are in the app.asar or unpacked directory
|
||||
return path.join(appPath, 'resources/templates/iracing');
|
||||
}
|
||||
|
||||
// Development: navigate from apps/companion to project root
|
||||
// __dirname is apps/companion/main (or dist equivalent)
|
||||
// appPath is apps/companion
|
||||
return path.join(appPath, '../../resources/templates/iracing');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create logger based on environment configuration.
|
||||
* In test environment, returns NoOpLogAdapter for silent logging.
|
||||
@@ -32,23 +70,57 @@ function createLogger(): ILogger {
|
||||
return new PinoLogAdapter(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the adapter mode based on environment.
|
||||
* - 'production' → 'real' (uses iRacing website selectors)
|
||||
* - 'development' → 'real' (uses iRacing website selectors)
|
||||
* - 'test' → 'mock' (uses data-* attribute selectors)
|
||||
*/
|
||||
function getAdapterMode(envMode: AutomationMode): AutomationAdapterMode {
|
||||
return envMode === 'test' ? 'mock' : 'real';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create screen automation adapter based on configuration mode.
|
||||
*
|
||||
* Mode mapping:
|
||||
* - 'production' → NutJsAutomationAdapter with iRacing window
|
||||
* - 'test'/'development' → MockBrowserAutomationAdapter
|
||||
* - 'production' → PlaywrightAutomationAdapter with mode='real' for iRacing website
|
||||
* - 'development' → PlaywrightAutomationAdapter with mode='real' for iRacing website
|
||||
* - 'test' → MockBrowserAutomationAdapter
|
||||
*
|
||||
* @param mode - The automation mode from configuration
|
||||
* @param logger - Logger instance for the adapter
|
||||
* @returns IScreenAutomation adapter instance
|
||||
* @returns PlaywrightAutomationAdapter instance (implements both IScreenAutomation and IAuthenticationService)
|
||||
*/
|
||||
function createBrowserAutomationAdapter(mode: AutomationMode, logger: ILogger): IScreenAutomation {
|
||||
function createBrowserAutomationAdapter(mode: AutomationMode, logger: ILogger): PlaywrightAutomationAdapter | MockBrowserAutomationAdapter {
|
||||
const config = loadAutomationConfig();
|
||||
|
||||
// Resolve absolute template path for Electron environment
|
||||
const absoluteTemplatePath = resolveTemplatePath();
|
||||
const sessionDataPath = resolveSessionDataPath();
|
||||
|
||||
logger.debug('Resolved paths', {
|
||||
absoluteTemplatePath,
|
||||
sessionDataPath,
|
||||
appPath: app.getAppPath(),
|
||||
isPackaged: app.isPackaged,
|
||||
cwd: process.cwd()
|
||||
});
|
||||
|
||||
const adapterMode = getAdapterMode(mode);
|
||||
logger.info('Creating browser automation adapter', { envMode: mode, adapterMode });
|
||||
|
||||
switch (mode) {
|
||||
case 'production':
|
||||
return new NutJsAutomationAdapter(config.nutJs, logger.child({ adapter: 'NutJs' }));
|
||||
case 'development':
|
||||
return new PlaywrightAutomationAdapter(
|
||||
{
|
||||
headless: mode === 'production',
|
||||
mode: adapterMode,
|
||||
userDataDir: sessionDataPath,
|
||||
},
|
||||
logger.child({ adapter: 'Playwright', mode: adapterMode })
|
||||
);
|
||||
|
||||
case 'test':
|
||||
default:
|
||||
@@ -61,11 +133,13 @@ export class DIContainer {
|
||||
|
||||
private logger: ILogger;
|
||||
private sessionRepository: ISessionRepository;
|
||||
private browserAutomation: IScreenAutomation;
|
||||
private browserAutomation: PlaywrightAutomationAdapter | MockBrowserAutomationAdapter;
|
||||
private automationEngine: IAutomationEngine;
|
||||
private startAutomationUseCase: StartAutomationSessionUseCase;
|
||||
private checkAuthenticationUseCase: CheckAuthenticationUseCase | null = null;
|
||||
private initiateLoginUseCase: InitiateLoginUseCase | null = null;
|
||||
private clearSessionUseCase: ClearSessionUseCase | null = null;
|
||||
private automationMode: AutomationMode;
|
||||
private permissionService: PermissionService;
|
||||
|
||||
private constructor() {
|
||||
// Initialize logger first - it's needed by other components
|
||||
@@ -90,9 +164,14 @@ export class DIContainer {
|
||||
this.browserAutomation,
|
||||
this.sessionRepository
|
||||
);
|
||||
this.permissionService = new PermissionService(
|
||||
this.logger.child({ service: 'PermissionService' })
|
||||
);
|
||||
|
||||
// Create authentication use cases only for real mode (PlaywrightAutomationAdapter)
|
||||
if (this.browserAutomation instanceof PlaywrightAutomationAdapter) {
|
||||
const authService = this.browserAutomation as IAuthenticationService;
|
||||
this.checkAuthenticationUseCase = new CheckAuthenticationUseCase(authService);
|
||||
this.initiateLoginUseCase = new InitiateLoginUseCase(authService);
|
||||
this.clearSessionUseCase = new ClearSessionUseCase(authService);
|
||||
}
|
||||
|
||||
this.logger.info('DIContainer initialized', {
|
||||
automationMode: config.mode,
|
||||
@@ -103,9 +182,12 @@ export class DIContainer {
|
||||
|
||||
private getBrowserAutomationType(mode: AutomationMode): string {
|
||||
switch (mode) {
|
||||
case 'production': return 'NutJsAutomationAdapter';
|
||||
case 'production':
|
||||
case 'development':
|
||||
return 'PlaywrightAutomationAdapter';
|
||||
case 'test':
|
||||
default: return 'MockBrowserAutomationAdapter';
|
||||
default:
|
||||
return 'MockBrowserAutomationAdapter';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,31 +222,46 @@ export class DIContainer {
|
||||
return this.logger;
|
||||
}
|
||||
|
||||
public getPermissionService(): PermissionService {
|
||||
return this.permissionService;
|
||||
public getCheckAuthenticationUseCase(): CheckAuthenticationUseCase | null {
|
||||
return this.checkAuthenticationUseCase;
|
||||
}
|
||||
|
||||
public getInitiateLoginUseCase(): InitiateLoginUseCase | null {
|
||||
return this.initiateLoginUseCase;
|
||||
}
|
||||
|
||||
public getClearSessionUseCase(): ClearSessionUseCase | null {
|
||||
return this.clearSessionUseCase;
|
||||
}
|
||||
|
||||
public getAuthenticationService(): IAuthenticationService | null {
|
||||
if (this.browserAutomation instanceof PlaywrightAutomationAdapter) {
|
||||
return this.browserAutomation as IAuthenticationService;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize automation connection based on mode.
|
||||
* In production mode, connects to iRacing window via nut.js.
|
||||
* In test/development mode, returns success immediately (no connection needed).
|
||||
* In production/development mode, connects via Playwright browser automation.
|
||||
* In test mode, returns success immediately (no connection needed).
|
||||
*/
|
||||
public async initializeBrowserConnection(): Promise<BrowserConnectionResult> {
|
||||
this.logger.info('Initializing automation connection', { mode: this.automationMode });
|
||||
|
||||
if (this.automationMode === 'production') {
|
||||
if (this.automationMode === 'production' || this.automationMode === 'development') {
|
||||
try {
|
||||
const nutJsAdapter = this.browserAutomation as NutJsAutomationAdapter;
|
||||
const result = await nutJsAdapter.connect();
|
||||
const playwrightAdapter = this.browserAutomation as PlaywrightAutomationAdapter;
|
||||
const result = await playwrightAdapter.connect();
|
||||
if (!result.success) {
|
||||
this.logger.error('Automation connection failed', new Error(result.error || 'Unknown error'), { mode: 'production' });
|
||||
this.logger.error('Automation connection failed', new Error(result.error || 'Unknown error'), { mode: this.automationMode });
|
||||
return { success: false, error: result.error };
|
||||
}
|
||||
this.logger.info('Automation connection established', { mode: 'production', adapter: 'NutJs' });
|
||||
this.logger.info('Automation connection established', { mode: this.automationMode, adapter: 'Playwright' });
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : 'Failed to initialize nut.js';
|
||||
this.logger.error('Automation connection failed', error instanceof Error ? error : new Error(errorMsg), { mode: 'production' });
|
||||
const errorMsg = error instanceof Error ? error.message : 'Failed to initialize Playwright';
|
||||
this.logger.error('Automation connection failed', error instanceof Error ? error : new Error(errorMsg), { mode: this.automationMode });
|
||||
return {
|
||||
success: false,
|
||||
error: errorMsg
|
||||
@@ -172,7 +269,7 @@ export class DIContainer {
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.debug('Test/development mode - no automation connection needed');
|
||||
this.logger.debug('Test mode - no automation connection needed');
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
@@ -185,7 +282,7 @@ export class DIContainer {
|
||||
|
||||
if (this.browserAutomation && 'disconnect' in this.browserAutomation) {
|
||||
try {
|
||||
await (this.browserAutomation as NutJsAutomationAdapter).disconnect();
|
||||
await (this.browserAutomation as PlaywrightAutomationAdapter).disconnect();
|
||||
this.logger.info('Automation adapter disconnected');
|
||||
} catch (error) {
|
||||
this.logger.error('Error disconnecting automation adapter', error instanceof Error ? error : new Error('Unknown error'));
|
||||
|
||||
@@ -2,8 +2,10 @@ import { app, BrowserWindow } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { setupIpcHandlers } from './ipc-handlers';
|
||||
|
||||
function createWindow() {
|
||||
const mainWindow = new BrowserWindow({
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
function createWindow(): BrowserWindow {
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 1200,
|
||||
height: 800,
|
||||
webPreferences: {
|
||||
@@ -17,14 +19,15 @@ function createWindow() {
|
||||
mainWindow.loadURL('http://localhost:5173');
|
||||
mainWindow.webContents.openDevTools();
|
||||
} else {
|
||||
// Path from dist/main to dist/renderer
|
||||
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
|
||||
}
|
||||
|
||||
setupIpcHandlers(mainWindow);
|
||||
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
app.whenReady().then(async () => {
|
||||
createWindow();
|
||||
|
||||
app.on('activate', () => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { BrowserWindow, IpcMainInvokeEvent } from 'electron';
|
||||
import { DIContainer } from './di-container';
|
||||
import type { HostedSessionConfig } from '@/packages/domain/entities/HostedSessionConfig';
|
||||
import { StepId } from '@/packages/domain/value-objects/StepId';
|
||||
import { AuthenticationState } from '@/packages/domain/value-objects/AuthenticationState';
|
||||
|
||||
let progressMonitorInterval: NodeJS.Timeout | null = null;
|
||||
|
||||
@@ -11,58 +12,139 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
||||
const startAutomationUseCase = container.getStartAutomationUseCase();
|
||||
const sessionRepository = container.getSessionRepository();
|
||||
const automationEngine = container.getAutomationEngine();
|
||||
const permissionService = container.getPermissionService();
|
||||
const logger = container.getLogger();
|
||||
|
||||
// Permission handlers
|
||||
ipcMain.handle('automation:checkPermissions', async () => {
|
||||
// Authentication handlers
|
||||
ipcMain.handle('auth:check', async () => {
|
||||
try {
|
||||
const result = await permissionService.checkPermissions();
|
||||
return {
|
||||
success: true,
|
||||
granted: result.granted,
|
||||
status: result.status,
|
||||
missingPermissions: result.missingPermissions,
|
||||
};
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Permission check failed', err);
|
||||
return {
|
||||
success: false,
|
||||
error: err.message,
|
||||
granted: false,
|
||||
status: {
|
||||
accessibility: false,
|
||||
screenRecording: false,
|
||||
platform: process.platform,
|
||||
},
|
||||
missingPermissions: ['Accessibility', 'Screen Recording'],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('automation:requestAccessibility', async () => {
|
||||
try {
|
||||
const granted = permissionService.requestAccessibilityPermission();
|
||||
return { success: true, granted };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Accessibility permission request failed', err);
|
||||
return { success: false, granted: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('automation:openPermissionSettings', async (_event: IpcMainInvokeEvent, pane?: 'accessibility' | 'screenRecording') => {
|
||||
try {
|
||||
if (pane) {
|
||||
await permissionService.openSystemPreferences(pane);
|
||||
} else {
|
||||
await permissionService.openPermissionsSettings();
|
||||
logger.info('Checking authentication status');
|
||||
const checkAuthUseCase = container.getCheckAuthenticationUseCase();
|
||||
|
||||
if (!checkAuthUseCase) {
|
||||
logger.warn('Authentication not available in mock mode');
|
||||
return {
|
||||
success: true,
|
||||
state: AuthenticationState.AUTHENTICATED,
|
||||
message: 'Mock mode - authentication bypassed'
|
||||
};
|
||||
}
|
||||
|
||||
// NO browser connection needed - cookie check reads JSON file directly
|
||||
const result = await checkAuthUseCase.execute();
|
||||
if (result.isErr()) {
|
||||
logger.error('Auth check failed', result.unwrapErr());
|
||||
return { success: false, error: result.unwrapErr().message };
|
||||
}
|
||||
|
||||
const state = result.unwrap();
|
||||
logger.info('Authentication check complete', { state });
|
||||
return { success: true, state };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Auth check failed', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('auth:login', async () => {
|
||||
try {
|
||||
logger.info('Starting iRacing login flow (will wait for completion)');
|
||||
|
||||
const authService = container.getAuthenticationService();
|
||||
|
||||
if (!authService) {
|
||||
// Mock mode - no actual login needed
|
||||
logger.warn('Auth service not available in mock mode');
|
||||
return { success: true, message: 'Mock mode - login bypassed' };
|
||||
}
|
||||
|
||||
// Use the Playwright browser for login (same browser used for automation)
|
||||
// This now waits for login to complete, auto-detects success, and closes browser
|
||||
const initiateLoginUseCase = container.getInitiateLoginUseCase();
|
||||
if (!initiateLoginUseCase) {
|
||||
logger.warn('Initiate login use case not available');
|
||||
return { success: false, error: 'Login not available' };
|
||||
}
|
||||
|
||||
// This call now blocks until login is complete or times out
|
||||
const result = await initiateLoginUseCase.execute();
|
||||
if (result.isErr()) {
|
||||
logger.error('Login failed or timed out', result.unwrapErr());
|
||||
return { success: false, error: result.unwrapErr().message };
|
||||
}
|
||||
|
||||
logger.info('Login completed successfully');
|
||||
return { success: true, message: 'Login completed successfully' };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Login flow failed', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('auth:confirmLogin', async () => {
|
||||
try {
|
||||
logger.info('User confirmed login completion');
|
||||
const authService = container.getAuthenticationService();
|
||||
|
||||
if (!authService) {
|
||||
logger.warn('Auth service not available in mock mode');
|
||||
return { success: true, state: AuthenticationState.AUTHENTICATED };
|
||||
}
|
||||
|
||||
// Call confirmLoginComplete on the adapter if it exists
|
||||
if ('confirmLoginComplete' in authService) {
|
||||
const result = await (authService as any).confirmLoginComplete();
|
||||
if (result.isErr()) {
|
||||
logger.error('Confirm login failed', result.unwrapErr());
|
||||
return { success: false, error: result.unwrapErr().message };
|
||||
}
|
||||
}
|
||||
|
||||
logger.info('Login confirmation recorded');
|
||||
return { success: true, state: AuthenticationState.AUTHENTICATED };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Failed to confirm login', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('auth:logout', async () => {
|
||||
try {
|
||||
logger.info('Clearing session (logout)');
|
||||
const clearSessionUseCase = container.getClearSessionUseCase();
|
||||
|
||||
if (!clearSessionUseCase) {
|
||||
logger.warn('Logout not available in mock mode');
|
||||
return { success: true, message: 'Mock mode - logout bypassed' };
|
||||
}
|
||||
|
||||
const result = await clearSessionUseCase.execute();
|
||||
if (result.isErr()) {
|
||||
logger.error('Logout failed', result.unwrapErr());
|
||||
return { success: false, error: result.unwrapErr().message };
|
||||
}
|
||||
|
||||
logger.info('Session cleared successfully');
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Failed to open permission settings', err);
|
||||
logger.error('Logout failed', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('auth:getState', async () => {
|
||||
try {
|
||||
const authService = container.getAuthenticationService();
|
||||
if (!authService) {
|
||||
return { success: true, state: AuthenticationState.AUTHENTICATED };
|
||||
}
|
||||
return { success: true, state: authService.getState() };
|
||||
} catch (error) {
|
||||
const err = error instanceof Error ? error : new Error('Unknown error');
|
||||
logger.error('Failed to get auth state', err);
|
||||
return { success: false, error: err.message };
|
||||
}
|
||||
});
|
||||
@@ -76,20 +158,6 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
||||
clearInterval(progressMonitorInterval);
|
||||
progressMonitorInterval = null;
|
||||
}
|
||||
|
||||
// Check permissions before starting automation (macOS only)
|
||||
const permissionResult = await permissionService.checkPermissions();
|
||||
if (!permissionResult.granted) {
|
||||
logger.warn('Automation blocked due to missing permissions', {
|
||||
missingPermissions: permissionResult.missingPermissions,
|
||||
});
|
||||
return {
|
||||
success: false,
|
||||
error: `Missing required permissions: ${permissionResult.missingPermissions.join(', ')}. Please grant permissions in System Preferences and try again.`,
|
||||
permissionError: true,
|
||||
missingPermissions: permissionResult.missingPermissions,
|
||||
};
|
||||
}
|
||||
|
||||
// Connect to browser first (required for dev mode)
|
||||
const connectionResult = await container.initializeBrowserConnection();
|
||||
@@ -99,6 +167,27 @@ export function setupIpcHandlers(mainWindow: BrowserWindow): void {
|
||||
}
|
||||
logger.info('Browser connection established');
|
||||
|
||||
// Check authentication before starting automation (production/development mode only)
|
||||
const checkAuthUseCase = container.getCheckAuthenticationUseCase();
|
||||
if (checkAuthUseCase) {
|
||||
const authResult = await checkAuthUseCase.execute();
|
||||
if (authResult.isOk()) {
|
||||
const authState = authResult.unwrap();
|
||||
if (authState !== AuthenticationState.AUTHENTICATED) {
|
||||
logger.warn('Not authenticated - automation cannot proceed', { authState });
|
||||
return {
|
||||
success: false,
|
||||
error: 'Not authenticated. Please login first.',
|
||||
authRequired: true,
|
||||
authState,
|
||||
};
|
||||
}
|
||||
logger.info('Authentication verified');
|
||||
} else {
|
||||
logger.warn('Auth check failed, proceeding anyway', { error: authResult.unwrapErr().message });
|
||||
}
|
||||
}
|
||||
|
||||
const result = await startAutomationUseCase.execute(config);
|
||||
logger.info('Automation session created', { sessionId: result.sessionId });
|
||||
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import { contextBridge, ipcRenderer } from 'electron';
|
||||
import type { HostedSessionConfig } from '../../../packages/domain/entities/HostedSessionConfig';
|
||||
import type { AuthenticationState } from '../../../packages/domain/value-objects/AuthenticationState';
|
||||
|
||||
export interface PermissionStatus {
|
||||
accessibility: boolean;
|
||||
screenRecording: boolean;
|
||||
platform: NodeJS.Platform;
|
||||
export interface AuthStatusEvent {
|
||||
state: AuthenticationState;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface PermissionCheckResponse {
|
||||
export interface AuthCheckResponse {
|
||||
success: boolean;
|
||||
granted: boolean;
|
||||
status: PermissionStatus;
|
||||
missingPermissions: string[];
|
||||
state?: AuthenticationState;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface AuthActionResponse {
|
||||
success: boolean;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@@ -20,18 +25,18 @@ export interface ElectronAPI {
|
||||
success: boolean;
|
||||
sessionId?: string;
|
||||
error?: string;
|
||||
permissionError?: boolean;
|
||||
missingPermissions?: string[];
|
||||
}>;
|
||||
stopAutomation: (sessionId: string) => Promise<{ success: boolean; error?: string }>;
|
||||
getSessionStatus: (sessionId: string) => Promise<any>;
|
||||
pauseAutomation: (sessionId: string) => Promise<{ success: boolean; error?: string }>;
|
||||
resumeAutomation: (sessionId: string) => Promise<{ success: boolean; error?: string }>;
|
||||
onSessionProgress: (callback: (progress: any) => void) => void;
|
||||
// Permission APIs
|
||||
checkPermissions: () => Promise<PermissionCheckResponse>;
|
||||
requestAccessibility: () => Promise<{ success: boolean; granted: boolean; error?: string }>;
|
||||
openPermissionSettings: (pane?: 'accessibility' | 'screenRecording') => Promise<{ success: boolean; error?: string }>;
|
||||
// Authentication APIs
|
||||
onAuthStatus: (callback: (status: AuthStatusEvent) => void) => void;
|
||||
checkAuth: () => Promise<AuthCheckResponse>;
|
||||
initiateLogin: () => Promise<AuthActionResponse>;
|
||||
confirmLogin: () => Promise<AuthActionResponse>;
|
||||
logout: () => Promise<AuthActionResponse>;
|
||||
}
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
@@ -43,9 +48,12 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onSessionProgress: (callback: (progress: any) => void) => {
|
||||
ipcRenderer.on('session-progress', (_event, progress) => callback(progress));
|
||||
},
|
||||
// Permission APIs
|
||||
checkPermissions: () => ipcRenderer.invoke('automation:checkPermissions'),
|
||||
requestAccessibility: () => ipcRenderer.invoke('automation:requestAccessibility'),
|
||||
openPermissionSettings: (pane?: 'accessibility' | 'screenRecording') =>
|
||||
ipcRenderer.invoke('automation:openPermissionSettings', pane),
|
||||
// Authentication APIs
|
||||
onAuthStatus: (callback: (status: AuthStatusEvent) => void) => {
|
||||
ipcRenderer.on('auth:status', (_event, status) => callback(status));
|
||||
},
|
||||
checkAuth: () => ipcRenderer.invoke('auth:check'),
|
||||
initiateLogin: () => ipcRenderer.invoke('auth:login'),
|
||||
confirmLogin: () => ipcRenderer.invoke('auth:confirmLogin'),
|
||||
logout: () => ipcRenderer.invoke('auth:logout'),
|
||||
} as ElectronAPI);
|
||||
@@ -21,7 +21,6 @@
|
||||
"vite": "^5.4.21"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nut-tree-fork/nut-js": "^4.2.6",
|
||||
"puppeteer-core": "^24.31.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { SessionCreationForm } from './components/SessionCreationForm';
|
||||
import { SessionProgressMonitor } from './components/SessionProgressMonitor';
|
||||
import { LoginPrompt } from './components/LoginPrompt';
|
||||
import type { HostedSessionConfig } from '../../../packages/domain/entities/HostedSessionConfig';
|
||||
|
||||
interface SessionProgress {
|
||||
@@ -12,72 +13,97 @@ interface SessionProgress {
|
||||
errorMessage: string | null;
|
||||
}
|
||||
|
||||
interface PermissionStatus {
|
||||
accessibility: boolean;
|
||||
screenRecording: boolean;
|
||||
platform: string;
|
||||
}
|
||||
type AuthState = 'UNKNOWN' | 'AUTHENTICATED' | 'EXPIRED' | 'LOGGED_OUT' | 'CHECKING';
|
||||
type LoginStatus = 'idle' | 'waiting' | 'success' | 'error';
|
||||
|
||||
export function App() {
|
||||
const [authState, setAuthState] = useState<AuthState>('CHECKING');
|
||||
const [authError, setAuthError] = useState<string | undefined>(undefined);
|
||||
const [sessionId, setSessionId] = useState<string | null>(null);
|
||||
const [progress, setProgress] = useState<SessionProgress | null>(null);
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const [permissionStatus, setPermissionStatus] = useState<PermissionStatus | null>(null);
|
||||
const [permissionChecking, setPermissionChecking] = useState(true);
|
||||
const [missingPermissions, setMissingPermissions] = useState<string[]>([]);
|
||||
const [loginStatus, setLoginStatus] = useState<LoginStatus>('idle');
|
||||
|
||||
const checkPermissions = useCallback(async () => {
|
||||
const handleLogin = useCallback(async () => {
|
||||
if (!window.electronAPI) return;
|
||||
|
||||
setPermissionChecking(true);
|
||||
setAuthError(undefined);
|
||||
setLoginStatus('waiting');
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.checkPermissions();
|
||||
setPermissionStatus(result.status);
|
||||
setMissingPermissions(result.missingPermissions);
|
||||
// This now waits for login to complete (auto-detects and closes browser)
|
||||
const result = await window.electronAPI.initiateLogin();
|
||||
if (result.success) {
|
||||
// Login completed successfully - browser closed automatically
|
||||
setLoginStatus('success');
|
||||
// Show success message for 2 seconds before transitioning
|
||||
setTimeout(() => {
|
||||
setAuthState('AUTHENTICATED');
|
||||
}, 2000);
|
||||
} else {
|
||||
setLoginStatus('error');
|
||||
setAuthError(result.error);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check permissions:', error);
|
||||
} finally {
|
||||
setPermissionChecking(false);
|
||||
console.error('Login failed:', error);
|
||||
setLoginStatus('error');
|
||||
setAuthError(error instanceof Error ? error.message : 'Login failed');
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleRetryAuth = useCallback(async () => {
|
||||
if (!window.electronAPI) return;
|
||||
|
||||
setAuthState('CHECKING');
|
||||
setAuthError(undefined);
|
||||
setLoginStatus('idle');
|
||||
|
||||
try {
|
||||
const result = await window.electronAPI.checkAuth();
|
||||
if (result.success && result.state) {
|
||||
setAuthState(result.state as AuthState);
|
||||
} else {
|
||||
setAuthError(result.error);
|
||||
setAuthState('UNKNOWN');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Auth check failed:', error);
|
||||
setAuthError(error instanceof Error ? error.message : 'Connection failed');
|
||||
setAuthState('UNKNOWN');
|
||||
}
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
// Check permissions on app start
|
||||
checkPermissions();
|
||||
if (!window.electronAPI) return;
|
||||
|
||||
if (window.electronAPI) {
|
||||
window.electronAPI.onSessionProgress((newProgress: SessionProgress) => {
|
||||
setProgress(newProgress);
|
||||
if (newProgress.state === 'COMPLETED' ||
|
||||
newProgress.state === 'FAILED' ||
|
||||
newProgress.state === 'STOPPED_AT_STEP_18') {
|
||||
setIsRunning(false);
|
||||
const checkAuth = async () => {
|
||||
try {
|
||||
const result = await window.electronAPI.checkAuth();
|
||||
if (result.success && result.state) {
|
||||
setAuthState(result.state as AuthState);
|
||||
} else {
|
||||
setAuthError(result.error || 'Failed to check authentication');
|
||||
setAuthState('UNKNOWN');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, [checkPermissions]);
|
||||
} catch (error) {
|
||||
setAuthError(error instanceof Error ? error.message : 'Failed to check authentication');
|
||||
setAuthState('UNKNOWN');
|
||||
}
|
||||
};
|
||||
|
||||
const handleOpenPermissionSettings = async (pane?: 'accessibility' | 'screenRecording') => {
|
||||
if (!window.electronAPI) return;
|
||||
await window.electronAPI.openPermissionSettings(pane);
|
||||
};
|
||||
checkAuth();
|
||||
|
||||
const handleRequestAccessibility = async () => {
|
||||
if (!window.electronAPI) return;
|
||||
await window.electronAPI.requestAccessibility();
|
||||
// Recheck permissions after request
|
||||
setTimeout(checkPermissions, 500);
|
||||
};
|
||||
window.electronAPI.onSessionProgress((newProgress: SessionProgress) => {
|
||||
setProgress(newProgress);
|
||||
if (newProgress.state === 'COMPLETED' ||
|
||||
newProgress.state === 'FAILED' ||
|
||||
newProgress.state === 'STOPPED_AT_STEP_18') {
|
||||
setIsRunning(false);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleStartAutomation = async (config: HostedSessionConfig) => {
|
||||
// Recheck permissions before starting
|
||||
await checkPermissions();
|
||||
|
||||
if (missingPermissions.length > 0) {
|
||||
alert(`Cannot start automation: Missing permissions: ${missingPermissions.join(', ')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsRunning(true);
|
||||
const result = await window.electronAPI.startAutomation(config);
|
||||
|
||||
@@ -85,13 +111,7 @@ export function App() {
|
||||
setSessionId(result.sessionId);
|
||||
} else {
|
||||
setIsRunning(false);
|
||||
if (result.permissionError) {
|
||||
// Update permission status
|
||||
await checkPermissions();
|
||||
alert(`Permission Error: ${result.error}`);
|
||||
} else {
|
||||
alert(`Failed to start automation: ${result.error}`);
|
||||
}
|
||||
alert(`Failed to start automation: ${result.error}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,8 +127,47 @@ export function App() {
|
||||
}
|
||||
};
|
||||
|
||||
const isMacOS = permissionStatus?.platform === 'darwin';
|
||||
const hasAllPermissions = missingPermissions.length === 0;
|
||||
if (authState === 'CHECKING') {
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundColor: '#1a1a1a',
|
||||
}}>
|
||||
<div style={{
|
||||
width: '60px',
|
||||
height: '60px',
|
||||
border: '4px solid #333',
|
||||
borderTopColor: '#007bff',
|
||||
borderRadius: '50%',
|
||||
animation: 'spin 1s linear infinite',
|
||||
}} />
|
||||
<style>{`
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
<p style={{ color: '#aaa', marginTop: '1.5rem', fontSize: '1.1rem' }}>
|
||||
Checking authentication...
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (authState !== 'AUTHENTICATED') {
|
||||
return (
|
||||
<LoginPrompt
|
||||
authState={authState}
|
||||
errorMessage={authError}
|
||||
onLogin={handleLogin}
|
||||
onRetry={handleRetryAuth}
|
||||
loginStatus={loginStatus}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
@@ -128,126 +187,9 @@ export function App() {
|
||||
Hosted Session Automation POC
|
||||
</p>
|
||||
|
||||
{/* Permission Banner */}
|
||||
{isMacOS && !permissionChecking && !hasAllPermissions && (
|
||||
<div style={{
|
||||
marginBottom: '1.5rem',
|
||||
padding: '1rem',
|
||||
backgroundColor: '#3d2020',
|
||||
border: '1px solid #dc3545',
|
||||
borderRadius: '8px',
|
||||
}}>
|
||||
<h3 style={{ color: '#ff6b6b', margin: '0 0 0.5rem 0', fontSize: '1rem' }}>
|
||||
⚠️ Missing Permissions
|
||||
</h3>
|
||||
<p style={{ color: '#ffaaaa', margin: '0 0 1rem 0', fontSize: '0.9rem' }}>
|
||||
GridPilot requires macOS permissions to control your computer for automation.
|
||||
Please grant the following permissions:
|
||||
</p>
|
||||
<ul style={{ color: '#ffaaaa', margin: '0 0 1rem 0', paddingLeft: '1.5rem', fontSize: '0.9rem' }}>
|
||||
{!permissionStatus?.accessibility && (
|
||||
<li style={{ marginBottom: '0.5rem' }}>
|
||||
<strong>Accessibility:</strong> Required for keyboard and mouse control
|
||||
</li>
|
||||
)}
|
||||
{!permissionStatus?.screenRecording && (
|
||||
<li style={{ marginBottom: '0.5rem' }}>
|
||||
<strong>Screen Recording:</strong> Required for screen capture and window detection
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
|
||||
{!permissionStatus?.accessibility && (
|
||||
<button
|
||||
onClick={handleRequestAccessibility}
|
||||
style={{
|
||||
padding: '0.5rem 1rem',
|
||||
backgroundColor: '#007bff',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.9rem',
|
||||
}}
|
||||
>
|
||||
Request Accessibility
|
||||
</button>
|
||||
)}
|
||||
{!permissionStatus?.accessibility && (
|
||||
<button
|
||||
onClick={() => handleOpenPermissionSettings('accessibility')}
|
||||
style={{
|
||||
padding: '0.5rem 1rem',
|
||||
backgroundColor: '#6c757d',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.9rem',
|
||||
}}
|
||||
>
|
||||
Open Accessibility Settings
|
||||
</button>
|
||||
)}
|
||||
{!permissionStatus?.screenRecording && (
|
||||
<button
|
||||
onClick={() => handleOpenPermissionSettings('screenRecording')}
|
||||
style={{
|
||||
padding: '0.5rem 1rem',
|
||||
backgroundColor: '#6c757d',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.9rem',
|
||||
}}
|
||||
>
|
||||
Open Screen Recording Settings
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={checkPermissions}
|
||||
style={{
|
||||
padding: '0.5rem 1rem',
|
||||
backgroundColor: '#28a745',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.9rem',
|
||||
}}
|
||||
>
|
||||
Recheck Permissions
|
||||
</button>
|
||||
</div>
|
||||
<p style={{ color: '#888', margin: '1rem 0 0 0', fontSize: '0.8rem' }}>
|
||||
After granting permissions in System Preferences, click "Recheck Permissions" or restart the app.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Permission Status Indicator */}
|
||||
{isMacOS && !permissionChecking && hasAllPermissions && (
|
||||
<div style={{
|
||||
marginBottom: '1.5rem',
|
||||
padding: '0.75rem 1rem',
|
||||
backgroundColor: '#1e3d1e',
|
||||
border: '1px solid #28a745',
|
||||
borderRadius: '8px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '0.5rem',
|
||||
}}>
|
||||
<span style={{ color: '#28a745', fontSize: '1.2rem' }}>✓</span>
|
||||
<span style={{ color: '#8eff8e', fontSize: '0.9rem' }}>
|
||||
All permissions granted - Ready for automation
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<SessionCreationForm
|
||||
onSubmit={handleStartAutomation}
|
||||
disabled={isRunning || (isMacOS && !hasAllPermissions)}
|
||||
disabled={isRunning}
|
||||
/>
|
||||
{isRunning && (
|
||||
<button
|
||||
|
||||
294
apps/companion/renderer/components/LoginPrompt.tsx
Normal file
@@ -0,0 +1,294 @@
|
||||
import React from 'react';
|
||||
|
||||
type LoginStatus = 'idle' | 'waiting' | 'success' | 'error';
|
||||
|
||||
interface LoginPromptProps {
|
||||
authState: string;
|
||||
errorMessage?: string;
|
||||
onLogin: () => void;
|
||||
onRetry: () => void;
|
||||
loginStatus?: LoginStatus;
|
||||
}
|
||||
|
||||
export function LoginPrompt({
|
||||
authState,
|
||||
errorMessage,
|
||||
onLogin,
|
||||
onRetry,
|
||||
loginStatus = 'idle'
|
||||
}: LoginPromptProps) {
|
||||
// Show success state when login completed
|
||||
if (loginStatus === 'success') {
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundColor: '#1a1a1a',
|
||||
padding: '2rem',
|
||||
}}>
|
||||
<div style={{
|
||||
maxWidth: '500px',
|
||||
width: '100%',
|
||||
padding: '2rem',
|
||||
backgroundColor: '#252525',
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3)',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
<div style={{
|
||||
width: '80px',
|
||||
height: '80px',
|
||||
margin: '0 auto 1.5rem',
|
||||
backgroundColor: '#1a472a',
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '2.5rem',
|
||||
color: '#4ade80',
|
||||
animation: 'scaleIn 0.3s ease-out',
|
||||
}}>
|
||||
✓
|
||||
</div>
|
||||
<style>{`
|
||||
@keyframes scaleIn {
|
||||
from { transform: scale(0); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; transform: translateY(10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
`}</style>
|
||||
|
||||
<h1 style={{
|
||||
color: '#4ade80',
|
||||
fontSize: '1.75rem',
|
||||
marginBottom: '0.5rem',
|
||||
fontWeight: 600,
|
||||
animation: 'fadeIn 0.4s ease-out 0.1s both',
|
||||
}}>
|
||||
Login Successful!
|
||||
</h1>
|
||||
|
||||
<p style={{
|
||||
color: '#aaa',
|
||||
fontSize: '1rem',
|
||||
marginBottom: '1rem',
|
||||
lineHeight: 1.5,
|
||||
animation: 'fadeIn 0.4s ease-out 0.2s both',
|
||||
}}>
|
||||
You're now connected to iRacing.
|
||||
</p>
|
||||
|
||||
<p style={{
|
||||
color: '#666',
|
||||
fontSize: '0.9rem',
|
||||
animation: 'fadeIn 0.4s ease-out 0.3s both',
|
||||
}}>
|
||||
Redirecting...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const getStateMessage = () => {
|
||||
switch (authState) {
|
||||
case 'EXPIRED':
|
||||
return 'Your iRacing session has expired. Please log in again to continue.';
|
||||
case 'LOGGED_OUT':
|
||||
return 'You have been logged out. Please log in to use GridPilot.';
|
||||
case 'UNKNOWN':
|
||||
return errorMessage
|
||||
? `Unable to verify authentication: ${errorMessage}`
|
||||
: 'Unable to verify your authentication status.';
|
||||
default:
|
||||
return null; // Will show explanation instead
|
||||
}
|
||||
};
|
||||
|
||||
const stateMessage = getStateMessage();
|
||||
const isWaiting = loginStatus === 'waiting';
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundColor: '#1a1a1a',
|
||||
padding: '2rem',
|
||||
}}>
|
||||
<div style={{
|
||||
maxWidth: '500px',
|
||||
width: '100%',
|
||||
padding: '2rem',
|
||||
backgroundColor: '#252525',
|
||||
borderRadius: '12px',
|
||||
boxShadow: '0 4px 24px rgba(0, 0, 0, 0.3)',
|
||||
textAlign: 'center',
|
||||
}}>
|
||||
<div style={{
|
||||
width: '80px',
|
||||
height: '80px',
|
||||
margin: '0 auto 1.5rem',
|
||||
backgroundColor: '#333',
|
||||
borderRadius: '50%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: '2.5rem',
|
||||
}}>
|
||||
{isWaiting ? '⏳' : '🔐'}
|
||||
</div>
|
||||
|
||||
<h1 style={{
|
||||
color: '#fff',
|
||||
fontSize: '1.75rem',
|
||||
marginBottom: '0.5rem',
|
||||
fontWeight: 600,
|
||||
}}>
|
||||
{isWaiting ? 'Waiting for Login...' : 'iRacing Login Required'}
|
||||
</h1>
|
||||
|
||||
{stateMessage ? (
|
||||
<p style={{
|
||||
color: '#aaa',
|
||||
fontSize: '1rem',
|
||||
marginBottom: '2rem',
|
||||
lineHeight: 1.5,
|
||||
}}>
|
||||
{stateMessage}
|
||||
</p>
|
||||
) : (
|
||||
<div style={{
|
||||
color: '#aaa',
|
||||
fontSize: '1rem',
|
||||
marginBottom: '2rem',
|
||||
lineHeight: 1.6,
|
||||
textAlign: 'left',
|
||||
}}>
|
||||
<p style={{ marginBottom: '1rem' }}>
|
||||
<strong style={{ color: '#fff' }}>Why do I need to log in?</strong>
|
||||
</p>
|
||||
<p style={{ marginBottom: '0.75rem' }}>
|
||||
GridPilot needs to access your iRacing account to create and manage hosted sessions on your behalf. This requires authentication with iRacing's website.
|
||||
</p>
|
||||
<ul style={{
|
||||
margin: '0.75rem 0',
|
||||
paddingLeft: '1.25rem',
|
||||
color: '#888',
|
||||
}}>
|
||||
<li style={{ marginBottom: '0.5rem' }}>✓ Your credentials are entered directly on iRacing.com</li>
|
||||
<li style={{ marginBottom: '0.5rem' }}>✓ GridPilot never sees or stores your password</li>
|
||||
<li>✓ Session cookies are stored locally for convenience</li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isWaiting ? (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
gap: '1rem',
|
||||
}}>
|
||||
<div style={{
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
border: '3px solid #333',
|
||||
borderTopColor: '#007bff',
|
||||
borderRadius: '50%',
|
||||
animation: 'spin 1s linear infinite',
|
||||
}} />
|
||||
<style>{`
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
`}</style>
|
||||
<p style={{
|
||||
color: '#aaa',
|
||||
fontSize: '0.95rem',
|
||||
}}>
|
||||
A browser window has opened. Please log in to iRacing.
|
||||
</p>
|
||||
<p style={{
|
||||
color: '#666',
|
||||
fontSize: '0.85rem',
|
||||
}}>
|
||||
This window will update automatically when login is detected.
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.75rem',
|
||||
}}>
|
||||
<button
|
||||
onClick={onLogin}
|
||||
style={{
|
||||
padding: '1rem 2rem',
|
||||
backgroundColor: '#007bff',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '1.1rem',
|
||||
fontWeight: 600,
|
||||
transition: 'background-color 0.2s',
|
||||
}}
|
||||
onMouseOver={(e) => e.currentTarget.style.backgroundColor = '#0056b3'}
|
||||
onMouseOut={(e) => e.currentTarget.style.backgroundColor = '#007bff'}
|
||||
>
|
||||
Log in to iRacing
|
||||
</button>
|
||||
|
||||
{(authState === 'UNKNOWN' || errorMessage) && (
|
||||
<button
|
||||
onClick={onRetry}
|
||||
style={{
|
||||
padding: '0.75rem 1.5rem',
|
||||
backgroundColor: 'transparent',
|
||||
color: '#aaa',
|
||||
border: '1px solid #555',
|
||||
borderRadius: '8px',
|
||||
cursor: 'pointer',
|
||||
fontSize: '0.95rem',
|
||||
transition: 'all 0.2s',
|
||||
}}
|
||||
onMouseOver={(e) => {
|
||||
e.currentTarget.style.borderColor = '#777';
|
||||
e.currentTarget.style.color = '#fff';
|
||||
}}
|
||||
onMouseOut={(e) => {
|
||||
e.currentTarget.style.borderColor = '#555';
|
||||
e.currentTarget.style.color = '#aaa';
|
||||
}}
|
||||
>
|
||||
Retry Connection
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!isWaiting && (
|
||||
<p style={{
|
||||
color: '#666',
|
||||
fontSize: '0.85rem',
|
||||
marginTop: '2rem',
|
||||
lineHeight: 1.4,
|
||||
}}>
|
||||
A browser window will open for you to log in securely to iRacing.
|
||||
The window will close automatically once login is complete.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||