Merge pull request #368 from hackclub/beta-branch
update logos + better setup flow
@@ -1,266 +1,147 @@
|
||||
<h1>Hackatime Set Up</h1>
|
||||
<p>Step 1 of 4</p>
|
||||
<div class="min-h-screen text-white">
|
||||
<div class="max-w-6xl mx-auto p-4">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="text-4xl font-bold text-primary mb-2">Hackatime Setup</h1>
|
||||
<div class="flex items-center justify-center gap-2 mb-4">
|
||||
<div class="w-8 h-8 bg-primary rounded-full flex items-center justify-center text-sm font-bold">1</div>
|
||||
<div class="w-16 h-1 bg-darkless"></div>
|
||||
<div class="w-8 h-8 bg-darkless rounded-full flex items-center justify-center text-sm">2</div>
|
||||
<div class="w-16 h-1 bg-darkless"></div>
|
||||
<div class="w-8 h-8 bg-darkless rounded-full flex items-center justify-center text-sm">3</div>
|
||||
<div class="w-16 h-1 bg-darkless"></div>
|
||||
<div class="w-8 h-8 bg-darkless rounded-full flex items-center justify-center text-sm">4</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setup-instructions">
|
||||
<div id="os-detection-message" class="text-center">
|
||||
<p>Detecting your operating system...</p>
|
||||
<div class="progress-indicator"></div>
|
||||
</div>
|
||||
<div class="setup-instructions">
|
||||
<div id="mac-linux" class="bg-dark rounded-lg p-6" style="display: block;">
|
||||
<h3 class="text-2xl font-bold text-green mb-4">🍎 Mac/Linux/Codespaces users:</h3>
|
||||
<ol class="list-decimal list-inside text-lg space-y-2 mb-6">
|
||||
<li>Open your terminal (on Mac it's called <i>Terminal</i>, if you are using something like GitHub Codespaces, just open the terminal in the Codespaces environment)</li>
|
||||
<li>Copy and paste the following command into your terminal</li>
|
||||
<li>Press <i>Return</i> to run it</li>
|
||||
</ol>
|
||||
<div class="code-block bg-darkless rounded-lg p-4 mb-4">
|
||||
<code class="text-cyan text-sm">export HACKATIME_API_KEY="<%= @current_user_api_key %>" && export HACKATIME_API_URL="<%= api_hackatime_v1_url %>" && export SUCCESS_URL="<%= root_url %>/success.txt" && curl -sSL <%= root_url %>hackatime/setup.sh | bash</code>
|
||||
<button class="copy-button bg-primary hover:bg-red border-0 text-white px-4 py-2 rounded transition-colors cursor-pointer" onclick="copy(this)">Copy</button>
|
||||
</div>
|
||||
<div class="rounded-lg mb-6">
|
||||
<iframe width="100%" height="300" src="https://www.youtube.com/embed/QTwhJy7nT_w?loop=1&playlist=QTwhJy7nT_w&modestbranding=1&rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen class="rounded"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<button id="show-windows-button" class="bg-blue hover:bg-cyan text-white border-0 px-4 py-2 rounded transition-colors cursor-pointer" onclick="toggleSection('windows')">Using Windows?</button>
|
||||
<button id="show-advanced-button-from-mac" class="bg-purple hover:bg-darkless text-white border-0 px-4 py-2 rounded transition-colors cursor-pointer" onclick="toggleSection('advanced')">Custom Setup</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mac-linux-section" style="display: none;">
|
||||
<h3>Mac/Linux users:</h3>
|
||||
<ol>
|
||||
<li>Open your terminal (on Mac it's called <i>Terminal</i>)</li>
|
||||
<li>Copy and paste the following command into your terminal</li>
|
||||
<li>Press <i>Return</i> to run it</li>
|
||||
</ol>
|
||||
<div class="code-block">
|
||||
<code>export HACKATIME_API_KEY="<%= @current_user_api_key %>" && export HACKATIME_API_URL="<%= api_hackatime_v1_url %>" && export SUCCESS_URL="<%= root_url %>/success.txt" && curl -sSL <%= root_url %>hackatime/setup.sh | bash</code>
|
||||
<button class="copy-button" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
<div id="mac-linux-video-container"></div>
|
||||
|
||||
<div>
|
||||
<button id="show-windows-button" class="copy-button" onclick="toggleSection('windows')">Using Windows? Click here</button>
|
||||
<button id="show-advanced-button-from-mac" class="copy-button" onclick="toggleSection('advanced')">Advanced/Custom Setup</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="windows" class="bg-dark rounded-lg p-6" style="display: none;">
|
||||
<h3 class="text-2xl font-bold text-blue mb-4">🪟 Windows users:</h3>
|
||||
<ol class="list-decimal list-inside text-lg space-y-2 mb-6">
|
||||
<li>Open <i>PowerShell</i></li>
|
||||
<li>Copy and paste the following command (<i>Ctrl+V</i> to paste)</li>
|
||||
<li>Press <i>Enter</i> to run it</li>
|
||||
</ol>
|
||||
<div class="code-block bg-darkless rounded-lg p-4 mb-4">
|
||||
<code class="text-cyan text-sm">$env:HACKATIME_API_KEY="<%= @current_user_api_key %>"; $env:HACKATIME_API_URL="<%= api_hackatime_v1_url %>"; powershell -ExecutionPolicy Bypass -Command "& {iwr <%= root_url %>hackatime/setup.ps1 -UseBasicParsing | iex}"</code>
|
||||
<button class="copy-button bg-primary hover:bg-red border-0 text-white px-4 py-2 rounded transition-colors cursor-pointer" onclick="copy(this)">Copy</button>
|
||||
</div>
|
||||
<div class="rounded-lg mb-6">
|
||||
<iframe width="100%" height="300" src="https://www.youtube.com/embed/fX9tsiRvzhg?loop=1&playlist=fX9tsiRvzhg&modestbranding=1&rel=0" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen class="rounded"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<button id="show-mac-linux-button" class="bg-green hover:bg-cyan text-white border-0 px-4 py-2 rounded transition-colors cursor-pointer" onclick="toggleSection('mac-linux')">Using Mac/Linux/Codespaces?</button>
|
||||
<button id="show-advanced-button" class="bg-purple hover:bg-darkless text-white border-0 px-4 py-2 rounded transition-colors cursor-pointer" onclick="toggleSection('advanced')">Custom Setup</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="windows-section" style="display: none;">
|
||||
<h3>Windows users:</h3>
|
||||
<ol>
|
||||
<li>Open <i>PowerShell</i></li>
|
||||
<li>Copy and paste the following command (<i>Ctrl+V</i> to paste)</li>
|
||||
<li>Press <i>Enter</i> to run it</li>
|
||||
</ol>
|
||||
<div class="code-block">
|
||||
<code>$env:HACKATIME_API_KEY="<%= @current_user_api_key %>"; $env:HACKATIME_API_URL="<%= api_hackatime_v1_url %>"; powershell -ExecutionPolicy Bypass -Command "& {iwr <%= root_url %>hackatime/setup.ps1 -UseBasicParsing | iex}"</code>
|
||||
<button class="copy-button" onclick="copyCode(this)">Copy</button>
|
||||
<div id="advanced" class="bg-dark rounded-lg p-6" style="display: none;">
|
||||
<h3 class="text-2xl font-bold text-purple mb-4">⚙️ Custom Setup:</h3>
|
||||
<p class="text-lg mb-4">For advanced users or custom installations, you can manually set up your Hackatime configuration.</p>
|
||||
<p class="mb-6">Create or edit <code class="bg-darkless px-2 py-1 rounded text-cyan">~/.wakatime.cfg</code> with the following content:</p>
|
||||
|
||||
<div class="code-block bg-darkless rounded-lg p-4 mb-6">
|
||||
<code class="text-cyan text-sm">[settings] api_url = <%= api_hackatime_v1_url %> api_key = <%= @current_user_api_key %> heartbeat_rate_limit_seconds = 30</code>
|
||||
<button class="copy-button bg-primary hover:bg-red border-0 text-white px-4 py-2 rounded transition-colors cursor-pointer" onclick="copy(this)">Copy</button>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 flex-wrap">
|
||||
<button id="show-mac-linux-from-advanced-button" class="bg-green hover:bg-cyan text-white border-0 px-4 py-2 rounded transition-colors cursor-pointer" onclick="toggleSection('mac-linux')">Using Mac/Linux/Codespaces?</button>
|
||||
<button id="show-windows-from-advanced-button" class="bg-blue hover:bg-cyan text-white border-0 px-4 py-2 rounded transition-colors cursor-pointer" onclick="toggleSection('windows')">Using Windows?</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="windows-video-container"></div>
|
||||
|
||||
<div>
|
||||
<button id="show-mac-linux-button" class="copy-button" onclick="toggleSection('mac-linux')">Using Mac/Linux? Click here</button>
|
||||
<button id="show-advanced-button" class="copy-button" onclick="toggleSection('advanced')">Advanced/Custom Setup</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="advanced-section" style="display: none;">
|
||||
<h3>Advanced/Custom Setup:</h3>
|
||||
<p>For advanced users or custom installations, you can manually set up your Hackatime configuration.</p>
|
||||
<p>Create or edit <code>~/.wakatime.cfg</code> with the following content:</p>
|
||||
|
||||
<div class="code-block">
|
||||
<code>[settings] api_url = <%= api_hackatime_v1_url %> api_key = <%= @current_user_api_key %> heartbeat_rate_limit_seconds = 30</code>
|
||||
<button class="copy-button" onclick="copyCode(this)">Copy</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="show-mac-linux-from-advanced-button" class="copy-button" onclick="toggleSection('mac-linux')">Using Mac/Linux? Click here</button>
|
||||
<button id="show-windows-from-advanced-button" class="copy-button" onclick="toggleSection('windows')">Using Windows? Click here</button>
|
||||
<div class="mt-2 text-center">
|
||||
<div id="heartbeat-status" class="bg-dark rounded-lg p-6 mb-6">
|
||||
<p class="text-lg mb-4">Waiting for your first heartbeat...</p>
|
||||
<div class="progress-indicator"></div>
|
||||
</div>
|
||||
|
||||
<div id="next-step" class="hidden">
|
||||
<%= link_to "Next Step", my_wakatime_setup_step_2_path, class: "bg-primary hover:bg-red text-white px-8 py-3 rounded-lg text-lg font-semibold transition-colors" %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="text-sm text-gray-600">This will create your Hackatime config file and send a test heartbeat to verify the set up.</p>
|
||||
|
||||
<div id="heartbeat-status">
|
||||
<p>Waiting for your first heartbeat...</p>
|
||||
<div class="progress-indicator"></div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<%= link_to "Next Step", my_wakatime_setup_step_2_path, id: "next-step", style: "display: none;" %>
|
||||
</p>
|
||||
|
||||
<!-- YouTube API -->
|
||||
<script src="https://www.youtube.com/iframe_api"></script>
|
||||
|
||||
<script>
|
||||
let macLinuxPlayer = null;
|
||||
let windowsPlayer = null;
|
||||
let playerReady = false;
|
||||
|
||||
// YouTube API callback
|
||||
function onYouTubeIframeAPIReady() {
|
||||
playerReady = true;
|
||||
setupPlayers();
|
||||
}
|
||||
|
||||
// Function to setup YouTube players
|
||||
function setupPlayers() {
|
||||
// Only setup if the players haven't been created
|
||||
if (!macLinuxPlayer) {
|
||||
macLinuxPlayer = new YT.Player('mac-linux-video-container', {
|
||||
height: '315',
|
||||
width: '560',
|
||||
videoId: 'QTwhJy7nT_w',
|
||||
playerVars: {
|
||||
'loop': 1,
|
||||
'playsinline': 1,
|
||||
'modestbranding': 1,
|
||||
'rel': 0
|
||||
},
|
||||
events: {
|
||||
'onReady': onMacLinuxPlayerReady
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!windowsPlayer) {
|
||||
windowsPlayer = new YT.Player('windows-video-container', {
|
||||
height: '315',
|
||||
width: '560',
|
||||
videoId: 'fX9tsiRvzhg',
|
||||
playerVars: {
|
||||
'loop': 1,
|
||||
'playsinline': 1,
|
||||
'modestbranding': 1,
|
||||
'rel': 0
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onMacLinuxPlayerReady(event) {
|
||||
// Now detect OS and show relevant section
|
||||
detectOS();
|
||||
}
|
||||
|
||||
// Detect operating system
|
||||
function detectOS() {
|
||||
const userAgent = window.navigator.userAgent;
|
||||
const macLinuxSection = document.getElementById('mac-linux-section');
|
||||
const windowsSection = document.getElementById('windows-section');
|
||||
const osDetectionMessage = document.getElementById('os-detection-message');
|
||||
|
||||
// Hide detection message
|
||||
osDetectionMessage.style.display = 'none';
|
||||
|
||||
// Check if Windows
|
||||
if (userAgent.indexOf('Windows') !== -1) {
|
||||
windowsSection.style.display = 'block';
|
||||
// Play Windows video if player is ready
|
||||
if (windowsPlayer && typeof windowsPlayer.playVideo === 'function') {
|
||||
try {
|
||||
windowsPlayer.playVideo();
|
||||
} catch (e) {
|
||||
console.error("Could not play Windows video:", e);
|
||||
}
|
||||
}
|
||||
function a() {
|
||||
const ua = window.navigator.userAgent;
|
||||
const mac = document.getElementById('mac-linux');
|
||||
const windows = document.getElementById('windows');
|
||||
if (ua.indexOf('Windows') !== -1) {
|
||||
windows.style.display = 'block';
|
||||
} else {
|
||||
// Assume Mac/Linux for all other OSes
|
||||
macLinuxSection.style.display = 'block';
|
||||
// Play Mac/Linux video if player is ready
|
||||
if (macLinuxPlayer && typeof macLinuxPlayer.playVideo === 'function') {
|
||||
try {
|
||||
macLinuxPlayer.playVideo();
|
||||
} catch (e) {
|
||||
console.error("Could not play Mac/Linux video:", e);
|
||||
}
|
||||
}
|
||||
mac.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('turbo:load', function() {
|
||||
// If the YouTube API is already loaded
|
||||
if (window.YT && window.YT.Player) {
|
||||
playerReady = true;
|
||||
setupPlayers();
|
||||
}
|
||||
a();
|
||||
|
||||
// Function to toggle between OS sections
|
||||
window.toggleSection = function(section) {
|
||||
const macLinuxSection = document.getElementById('mac-linux-section');
|
||||
const windowsSection = document.getElementById('windows-section');
|
||||
const advancedSection = document.getElementById('advanced-section');
|
||||
const mac = document.getElementById('mac-linux');
|
||||
const windows = document.getElementById('windows');
|
||||
const advanced = document.getElementById('advanced');
|
||||
|
||||
// Hide all sections first
|
||||
macLinuxSection.style.display = 'none';
|
||||
windowsSection.style.display = 'none';
|
||||
advancedSection.style.display = 'none';
|
||||
mac.style.display = 'block';
|
||||
windows.style.display = 'none';
|
||||
advanced.style.display = 'none';
|
||||
|
||||
// Pause both videos
|
||||
if (macLinuxPlayer && typeof macLinuxPlayer.pauseVideo === 'function') {
|
||||
try {
|
||||
macLinuxPlayer.pauseVideo();
|
||||
} catch (e) {
|
||||
console.error("Could not pause Mac/Linux video:", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (windowsPlayer && typeof windowsPlayer.pauseVideo === 'function') {
|
||||
try {
|
||||
windowsPlayer.pauseVideo();
|
||||
} catch (e) {
|
||||
console.error("Could not pause Windows video:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Show the selected section
|
||||
if (section === 'windows') {
|
||||
windowsSection.style.display = 'block';
|
||||
// Play Windows video
|
||||
if (windowsPlayer && typeof windowsPlayer.playVideo === 'function') {
|
||||
try {
|
||||
windowsPlayer.playVideo();
|
||||
} catch (e) {
|
||||
console.error("Could not play Windows video:", e);
|
||||
}
|
||||
}
|
||||
windows.style.display = 'block';
|
||||
} else if (section === 'advanced') {
|
||||
advancedSection.style.display = 'block';
|
||||
advanced.style.display = 'block';
|
||||
} else {
|
||||
// Default to Mac/Linux
|
||||
macLinuxSection.style.display = 'block';
|
||||
// Play Mac/Linux video
|
||||
if (macLinuxPlayer && typeof macLinuxPlayer.playVideo === 'function') {
|
||||
try {
|
||||
macLinuxPlayer.playVideo();
|
||||
} catch (e) {
|
||||
console.error("Could not play Mac/Linux video:", e);
|
||||
}
|
||||
}
|
||||
mac.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
// If YouTube API isn't ready yet, we'll wait for it
|
||||
if (!playerReady) {
|
||||
// detectOS will be called from onMacLinuxPlayerReady
|
||||
} else {
|
||||
// If the API is already ready, detect OS after a short delay
|
||||
setTimeout(detectOS, 500);
|
||||
}
|
||||
|
||||
const statusDiv = document.getElementById('heartbeat-status');
|
||||
const nextStepLink = document.getElementById('next-step');
|
||||
let checkCount = 0;
|
||||
const maxChecks = 60; // Stop checking after 5 minutes (60 * 5s = 5min)
|
||||
const maxChecks = 60;
|
||||
|
||||
function timeAgoInWords(date) {
|
||||
const diffInSeconds = Math.floor((new Date() - new Date(date)) / 1000);
|
||||
const diffInMinutes = Math.floor(diffInSeconds / 60);
|
||||
const diffInHours = Math.floor(diffInMinutes / 60);
|
||||
const diffInDays = Math.floor(diffInHours / 24);
|
||||
const diffInMonths = Math.floor(diffInDays / 30);
|
||||
const diffInYears = Math.floor(diffInDays / 365);
|
||||
function timeAgoInWords(d) {
|
||||
const s = Math.floor((new Date() - new Date(d)) / 1000);
|
||||
const m = Math.floor(s / 60);
|
||||
const h = Math.floor(m / 60);
|
||||
const days = Math.floor(h / 24);
|
||||
|
||||
if (diffInYears > 0) {
|
||||
return diffInYears === 1 ? "1 year ago" : `${diffInYears} years ago`;
|
||||
} else if (diffInMonths > 0) {
|
||||
return diffInMonths === 1 ? "1 month ago" : `${diffInMonths} months ago`;
|
||||
} else if (diffInDays > 0) {
|
||||
return diffInDays === 1 ? "1 day ago" : `${diffInDays} days ago`;
|
||||
} else if (diffInHours > 0) {
|
||||
return diffInHours === 1 ? "1 hour ago" : `${diffInHours} hours ago`;
|
||||
} else if (diffInMinutes > 0) {
|
||||
return diffInMinutes === 1 ? "1 minute ago" : `${diffInMinutes} minutes ago`;
|
||||
if (days > 0) {
|
||||
return days === 1 ? "1 day ago" : `${days} days ago`;
|
||||
} else if (h > 0) {
|
||||
return h === 1 ? "1 hour ago" : `${h} hours ago`;
|
||||
} else if (m > 0) {
|
||||
return m === 1 ? "1 minute ago" : `${m} minutes ago`;
|
||||
} else {
|
||||
return diffInSeconds === 1 ? "1 second ago" : `${diffInSeconds} seconds ago`;
|
||||
return s === 1 ? "1 second ago" : `${s} seconds ago`;
|
||||
}
|
||||
}
|
||||
|
||||
function checkForHeartbeats() {
|
||||
function check() {
|
||||
fetch('<%= api_v1_my_heartbeats_most_recent_path %>', {
|
||||
headers: {
|
||||
'Authorization': 'Bearer <%= @current_user_api_key %>'
|
||||
@@ -269,30 +150,27 @@
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.has_heartbeat) {
|
||||
// show time ago in human readable format
|
||||
const timeAgo = timeAgoInWords(data.heartbeat.created_at);
|
||||
statusDiv.innerHTML = `<p class="success">✅ Heartbeat received ${timeAgo}! You can proceed to the next step.</p>`;
|
||||
nextStepLink.style.display = 'inline-block';
|
||||
return; // Stop checking once we get a heartbeat
|
||||
statusDiv.innerHTML = `<p class="text-green text-xl font-bold">✅ Heartbeat received ${timeAgo}! You can proceed to the next step.</p>`;
|
||||
nextStepLink.classList.remove('hidden');
|
||||
return;
|
||||
}
|
||||
throw new Error('No heartbeats yet');
|
||||
})
|
||||
.catch(error => {
|
||||
checkCount++;
|
||||
if (checkCount >= maxChecks) {
|
||||
statusDiv.innerHTML = '<p class="error">❌ No heartbeats detected after 5 minutes. Please make sure you\'ve run the command above and have the WakaTime plugin installed in your editor.</p>';
|
||||
return; // Stop checking after max attempts
|
||||
statusDiv.innerHTML = '<p class="text-red text-lg font-bold">❌ No heartbeats detected after 5 minutes. Please make sure you\'ve run the command above and have the WakaTime plugin installed in your editor.</p>';
|
||||
return;
|
||||
}
|
||||
// Continue checking every 5 seconds
|
||||
setTimeout(checkForHeartbeats, 5000);
|
||||
setTimeout(check, 5000);
|
||||
});
|
||||
}
|
||||
|
||||
// Start checking
|
||||
checkForHeartbeats();
|
||||
check();
|
||||
});
|
||||
|
||||
function copyCode(button) {
|
||||
function copy(button) {
|
||||
const codeBlock = button.previousElementSibling;
|
||||
const text = codeBlock.textContent;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
@@ -306,13 +184,13 @@
|
||||
|
||||
<style>
|
||||
.progress-indicator {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border: 3px solid #eee;
|
||||
border-top: 3px solid #666;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 3px solid var(--color-darkless);
|
||||
border-top: 3px solid var(--color-primary);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
margin: 10px auto;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
@@ -320,59 +198,44 @@
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.success {
|
||||
color: #2da44e;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: #cf222e;
|
||||
}
|
||||
|
||||
.setup-instructions {
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.code-block {
|
||||
position: relative;
|
||||
margin-bottom: 1rem;
|
||||
width: fit-content;
|
||||
max-width: 75vw;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 0.5rem;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.code-block code {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.copy-button {
|
||||
position: static;
|
||||
padding: 0.25rem 0.75rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.875rem;
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
background-color: #0066cc;
|
||||
color: white;
|
||||
border: 1px solid #0055aa;
|
||||
font-weight: bold;
|
||||
transition: background-color 0.2s;
|
||||
cursor: pointer;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.copy-button:hover {
|
||||
background-color: #0055aa;
|
||||
}
|
||||
|
||||
.setup-instructions code {
|
||||
display: block;
|
||||
width: 75%;
|
||||
white-space: pre;
|
||||
overflow-x: auto;
|
||||
padding-right: 1rem;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.text-center {
|
||||
text-align: center;
|
||||
@media (max-width: 768px) {
|
||||
.code-block {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.code-block code {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
iframe {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,20 +1,93 @@
|
||||
<h1>Hackatime Set Up</h1>
|
||||
<p>Step 2 of 4</p>
|
||||
<div class="min-h-screen text-white">
|
||||
<div class="max-w-6xl mx-auto p-4">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="text-4xl font-bold text-primary mb-2">Hackatime Setup</h1>
|
||||
<div class="flex items-center justify-center gap-2 mb-4">
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-16 h-1 bg-green"></div>
|
||||
<div class="w-8 h-8 bg-primary rounded-full flex items-center justify-center text-sm font-bold">2</div>
|
||||
<div class="w-16 h-1 bg-darkless"></div>
|
||||
<div class="w-8 h-8 bg-darkless rounded-full flex items-center justify-center text-sm">3</div>
|
||||
<div class="w-16 h-1 bg-darkless"></div>
|
||||
<div class="w-8 h-8 bg-darkless rounded-full flex items-center justify-center text-sm">4</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Pick your coding editors</h2>
|
||||
<em>You can change this later!</em>
|
||||
<div class="text-center mb-8">
|
||||
<h2 class="text-3xl font-bold mb-4">What editor do you use?</h2>
|
||||
<p class="text-secondary text-lg"><em>Let's setup one for now, you can setup more later!</em></p>
|
||||
</div>
|
||||
|
||||
<ul>
|
||||
<li>
|
||||
<%= link_to "vs-code (visual studio code)", my_wakatime_setup_step_3_path(anchor: "vs-code") %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "vim", my_wakatime_setup_step_3_path(anchor: "vim") %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "emacs", my_wakatime_setup_step_3_path %>
|
||||
</li>
|
||||
<li>
|
||||
<%= link_to "other", my_wakatime_setup_step_3_path %>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="grid gap-4 max-w-2xl mx-auto">
|
||||
<div class="bg-dark rounded-lg p-6 hover:bg-darkless transition-colors">
|
||||
<%= link_to my_wakatime_setup_step_3_path(anchor: "vs-code"), class: "flex items-center gap-4 text-white no-underline" do %>
|
||||
<div class="w-12 h-12 rounded-lg flex items-center justify-center">
|
||||
<img src="/images/editor-icons/vs-code-128.png" alt="VS Code" class="w-full h-full object-contain" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold">Visual Studio Code</h3>
|
||||
<p class="text-secondary">Most popular code editor, loved by millions</p>
|
||||
</div>
|
||||
<div class="ml-auto text-primary flex items-center">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="bg-dark rounded-lg p-6 hover:bg-darkless transition-colors">
|
||||
<%= link_to my_wakatime_setup_step_3_path(anchor: "vim"), class: "flex items-center gap-4 text-white no-underline" do %>
|
||||
<div class="w-12 h-12 rounded-lg flex items-center justify-center">
|
||||
<img src="/images/editor-icons/vim-128.png" alt="Vim" class="w-full h-full object-contain" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold">Vim</h3>
|
||||
<p class="text-secondary">Terminal-based editor, for the cool kids</p>
|
||||
</div>
|
||||
<div class="ml-auto text-primary flex items-center">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="bg-dark rounded-lg p-6 hover:bg-darkless transition-colors">
|
||||
<%= link_to my_wakatime_setup_step_3_path, class: "flex items-center gap-4 text-white no-underline" do %>
|
||||
<div class="w-12 h-12 rounded-lg flex items-center justify-center">
|
||||
<img src="/images/editor-icons/emacs-128.png" alt="Emacs" class="w-full h-full object-contain" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold">Emacs</h3>
|
||||
<p class="text-secondary">Powerful extensible editor, for the power users</p>
|
||||
</div>
|
||||
<div class="ml-auto text-primary flex items-center">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="bg-dark rounded-lg p-6 hover:bg-darkless transition-colors">
|
||||
<%= link_to my_wakatime_setup_step_3_path, class: "flex items-center gap-4 text-white no-underline" do %>
|
||||
<div class="w-12 h-12 bg-orange rounded-lg flex items-center justify-center text-2xl">🔧</div>
|
||||
<div>
|
||||
<h3 class="text-xl font-bold">Not listed here!</h3>
|
||||
<p class="text-secondary">Any WakaTime-supported editor, there are hundreds!</p>
|
||||
</div>
|
||||
<div class="ml-auto text-primary flex items-center">
|
||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,35 +1,65 @@
|
||||
<h1>Hackatime Set Up</h1>
|
||||
<p>Step 3 of 4</p>
|
||||
<div class="min-h-screen text-white">
|
||||
<div class="max-w-6xl mx-auto p-4">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="text-4xl font-bold text-primary mb-2">Hackatime Setup</h1>
|
||||
<div class="flex items-center justify-center gap-2 mb-4">
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-16 h-1 bg-green"></div>
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-16 h-1 bg-green"></div>
|
||||
<div class="w-8 h-8 bg-primary rounded-full flex items-center justify-center text-sm font-bold">3</div>
|
||||
<div class="w-16 h-1 bg-darkless"></div>
|
||||
<div class="w-8 h-8 bg-darkless rounded-full flex items-center justify-center text-sm">4</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Install!</h2>
|
||||
<div class="text-center mb-8">
|
||||
<h2 class="text-3xl font-bold mb-4">Install Plugin!</h2>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h2>Other</h2>
|
||||
<p>
|
||||
<em>Hackatime is WakaTime-compatible, so you can use it with any editor that WakaTime supports. For other editors, please refer to the <a href="https://wakatime.com/plugins">WakaTime docs</a>.</em>
|
||||
<section class="bg-dark rounded-lg p-6 mb-6">
|
||||
<h3 class="text-2xl font-bold text-orange mb-4">🔧 Other Editors</h3>
|
||||
<div class="bg-yellow/50 bg-opacity-10 border border-yellow rounded-lg p-4">
|
||||
<p class="mb-4">
|
||||
<em>Hackatime is WakaTime-compatible, so you can use it with any editor that WakaTime supports. For other editors, please refer to the <a href="https://wakatime.com/plugins" class="text-cyan hover:text-blue underline">WakaTime docs</a>.</em>
|
||||
</p>
|
||||
<p class="text-yellow font-bold">
|
||||
⚠️ Note: skip any steps that update your ~/.wakatime.cfg file or change the api_url inside it.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="vs-code" class="bg-dark rounded-lg p-6">
|
||||
<h3 class="text-2xl font-bold text-blue mb-4">💻 VS Code (Visual Studio Code)</h3>
|
||||
<div class="space-y-6">
|
||||
<p class="text-lg">
|
||||
<em>Hackatime uses the VS Code WakaTime extension. For VS Code, install the <a href="https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime" target="_blank" rel="noopener noreferrer" class="text-cyan hover:text-blue underline">WakaTime extension here</a>.</em>
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<p class="text-lg mb-4">You'll know it works if you see a clock in your status bar, for example I already coded so it shows how much I have coded today</p>
|
||||
<div>
|
||||
<img src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/95d2513ce4b0c1c147827d17ecb4c24540cd73cc_p.png" alt="WakaTime status bar in VS Code" class="max-w-full h-auto rounded-lg">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<strong>⚠️ Note: skip any steps that update your ~/.wakatime.cfg file or change the api_url inside it.</strong>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section id="vs-code">
|
||||
<h2>VS Code (Visual Studio Code)</h2>
|
||||
<p>
|
||||
<em>Hackatime uses the vs-code WakaTime extension. For VS Code, install the <a href="https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime" target="_blank" rel="noopener noreferrer">WakaTime extension here</a>.</em>
|
||||
</p>
|
||||
<p>
|
||||
You'll know it works if you see this in your status bar:
|
||||
</p>
|
||||
<img src="https://hc-cdn.hel1.your-objectstorage.com/s/v3/bebe242cc56bcd33bbbffad54fa04d4c66b27ee4_screenshot_2025-03-13_at_11.41.27.png" alt="WakaTime status bar in VS Code">
|
||||
|
||||
<%= link_to my_wakatime_setup_step_2_path do %>
|
||||
<button>Back to editor selection</button>
|
||||
<% end %>
|
||||
|
||||
<%= link_to my_wakatime_setup_step_4_path do %>
|
||||
<button>Next Step</button>
|
||||
<% end %>
|
||||
</section>
|
||||
<div class="flex gap-4 flex-wrap justify-center">
|
||||
<%= link_to my_wakatime_setup_step_4_path, class: "bg-primary hover:bg-red text-white px-6 py-3 rounded-lg font-semibold transition-colors" do %>
|
||||
Next Step
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function autoScroll() {
|
||||
|
||||
@@ -1,25 +1,62 @@
|
||||
<h1>Hackatime Set Up</h1>
|
||||
<div class="min-h-screen text-white">
|
||||
<div class="max-w-6xl mx-auto p-4">
|
||||
<div class="text-center mb-4">
|
||||
<h1 class="text-4xl font-bold text-primary mb-2">Hackatime Setup</h1>
|
||||
<div class="flex items-center justify-center gap-2 mb-4">
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-16 h-1 bg-green"></div>
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-16 h-1 bg-green"></div>
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="w-16 h-1 bg-green"></div>
|
||||
<div class="w-8 h-8 bg-green rounded-full flex items-center justify-center text-sm font-bold">
|
||||
<svg class="w-5 h-5 text-white" fill="none" stroke="currentColor" stroke-width="3" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>Step 4 of 4</p>
|
||||
<div class="text-center mb-8">
|
||||
<h2 class="text-3xl font-bold text-green mt-4 mb-4">🎉 Setup Complete!</h2>
|
||||
<p class="text-xl text-secondary">
|
||||
<%= @no_instruction_wording %> You're all set!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
<%= @no_instruction_wording %> You're already done!
|
||||
</p>
|
||||
<div class="text-center mb-8">
|
||||
<div class="max-w-lg mx-auto">
|
||||
<video src="<%= FlavorText.dino_meme_videos.sample %>" autoplay loop muted playsinline controls class="w-full rounded-lg">
|
||||
</video>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<video src="<%= FlavorText.dino_meme_videos.sample %>" autoplay loop muted playsinline controls></video>
|
||||
<div class="flex gap-4 flex-wrap justify-center">
|
||||
<%= link_to my_wakatime_setup_step_2_path, class: "bg-secondary hover:bg-darkless text-white px-6 py-3 rounded-lg font-semibold transition-colors" do %>
|
||||
Set up another editor
|
||||
<% end %>
|
||||
|
||||
<% if (url = session.dig(:return_data, "url")) %>
|
||||
<%= link_to url, class: "bg-primary hover:bg-red text-white px-8 py-3 rounded-lg font-semibold transition-colors text-lg" do %>
|
||||
<%= session.dig(:return_data, "button_text") || "Done" %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to root_path, class: "bg-primary hover:bg-red text-white px-8 py-3 rounded-lg font-semibold transition-colors text-lg" do %>
|
||||
Get Started
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= link_to my_wakatime_setup_step_2_path do %>
|
||||
<button>Set up another editor</button>
|
||||
<% end %>
|
||||
|
||||
<% if (url = session.dig(:return_data, "url")) %>
|
||||
<%= link_to url do %>
|
||||
<button><%= session.dig(:return_data, "button_text") || "Done" %></button>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= link_to root_path do %>
|
||||
<button>Done</button>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
|
Before Width: | Height: | Size: 920 B After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 6.4 KiB |