mirror of
https://github.com/System-End/hackatime.git
synced 2026-04-19 21:05:15 +00:00
* Inertia p1? * Inertia'fied signed out homepage? * Split up signed in page * WIP signed in v2? * Better signed in? * Clean up extensions page! * Fix currently hacking * Better docs page? * Docs update 2 * Clean up "What is Hackatime?" + get rid of that godawful green dev mode * Better nav? * Cleaner settings? * Fix commit times * Fix flashes + OS improv * Setup v2 * Readd some of the syncers? * Remove stray emdash * Clean up Step 3 * Oops, remove .vite * bye bye, /inertia-example * bin/rubocop -A * Fix docs vuln
368 lines
13 KiB
Svelte
368 lines
13 KiB
Svelte
<script lang="ts">
|
|
import { onMount } from "svelte";
|
|
import { Link } from "@inertiajs/svelte";
|
|
import Stepper from "./Stepper.svelte";
|
|
|
|
interface Props {
|
|
current_user_api_key: string;
|
|
setup_os: string;
|
|
api_url: string;
|
|
heartbeat_check_url: string;
|
|
}
|
|
|
|
let { current_user_api_key, setup_os, api_url, heartbeat_check_url }: Props =
|
|
$props();
|
|
|
|
let activeSection = $derived(
|
|
setup_os === "windows" ? "windows" : "mac-linux",
|
|
);
|
|
let hasHeartbeat = $state(false);
|
|
let heartbeatTimeAgo = $state("");
|
|
let checkCount = $state(0);
|
|
let statusMessage = $state(
|
|
"Run the command above, then we'll automatically detect when you're ready.",
|
|
);
|
|
let statusPanelClass = $state("border-darkless");
|
|
|
|
const messages = [
|
|
"Copy the command above and run it in your terminal!",
|
|
"Paste the command and press Enter...",
|
|
"The script will configure everything automatically!",
|
|
"Almost there - just run the command!",
|
|
"We'll detect it as soon as the script runs!",
|
|
];
|
|
|
|
const sharedTitle = "Configure Hackatime";
|
|
const sharedSubtitle =
|
|
"This creates your config file and validates your API key. And if you're using VS Code, a JetBrains IDE, Zed, or Xcode, we'll even set up the plugins for you!";
|
|
|
|
function toggleSection(section: string) {
|
|
activeSection = section;
|
|
}
|
|
|
|
function showSuccess(timeAgo: string) {
|
|
hasHeartbeat = true;
|
|
heartbeatTimeAgo = timeAgo;
|
|
statusPanelClass = "border-green bg-green/5";
|
|
}
|
|
|
|
async function checkHeartbeat() {
|
|
try {
|
|
const response = await fetch(heartbeat_check_url, {
|
|
headers: {
|
|
Authorization: `Bearer ${current_user_api_key}`,
|
|
},
|
|
});
|
|
const data = await response.json();
|
|
|
|
if (data.has_heartbeat) {
|
|
const heartbeatTime = new Date(data.heartbeat.created_at);
|
|
const now = new Date();
|
|
const secondsAgo = (now.getTime() - heartbeatTime.getTime()) / 1000;
|
|
const recentThreshold = 300;
|
|
|
|
if (secondsAgo <= recentThreshold) {
|
|
showSuccess(data.time_ago);
|
|
return;
|
|
}
|
|
}
|
|
throw new Error("No heartbeats yet");
|
|
} catch (error) {
|
|
checkCount++;
|
|
|
|
if (checkCount % 3 === 0) {
|
|
const msgIndex = Math.floor(checkCount / 3) % messages.length;
|
|
statusMessage = messages[msgIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
onMount(() => {
|
|
checkHeartbeat();
|
|
const interval = setInterval(() => {
|
|
if (!hasHeartbeat) {
|
|
checkHeartbeat();
|
|
}
|
|
}, 5000);
|
|
return () => clearInterval(interval);
|
|
});
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Configure Hackatime - Step 1</title>
|
|
</svelte:head>
|
|
|
|
<div class="min-h-screen text-white pt-8 pb-16">
|
|
<div class="max-w-2xl mx-auto px-4">
|
|
<Stepper currentStep={1} />
|
|
|
|
<div class="space-y-8">
|
|
<div
|
|
class="border border-darkless rounded-xl p-6 bg-dark transition-all duration-300 {statusPanelClass}"
|
|
>
|
|
{#if !hasHeartbeat}
|
|
<div
|
|
class="flex flex-col items-center justify-center text-center py-2"
|
|
>
|
|
<h4 class="text-lg font-semibold text-white mb-1">
|
|
Waiting for setup...
|
|
</h4>
|
|
<p class="text-sm text-secondary mb-4 max-w-sm">{statusMessage}</p>
|
|
</div>
|
|
{:else}
|
|
<div class="text-center py-2">
|
|
<h4 class="text-xl font-bold text-white">Setup complete!</h4>
|
|
<p class="text-secondary text-sm mb-6">
|
|
Heartbeat detected {heartbeatTimeAgo}.
|
|
</p>
|
|
|
|
<a
|
|
href="/my/wakatime_setup/step-2"
|
|
class="inline-flex items-center justify-center bg-primary text-white px-6 py-2 rounded-lg font-semibold transition-all"
|
|
>
|
|
Continue to Step 2 →
|
|
</a>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
{#if activeSection === "mac-linux"}
|
|
<div class="bg-dark border border-darkless rounded-xl p-8 shadow-sm">
|
|
<div class="mb-6">
|
|
<h3 class="text-xl font-semibold mb-2">{sharedTitle}</h3>
|
|
<p class="text-secondary text-sm">{sharedSubtitle}</p>
|
|
</div>
|
|
|
|
<div
|
|
class="bg-blue/5 border border-blue/20 rounded-lg p-4 mb-6 flex gap-3"
|
|
>
|
|
<svg
|
|
class="w-5 h-5 text-blue shrink-0 mt-0.5"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
stroke-linecap="round"
|
|
stroke-linejoin="round"
|
|
stroke-width="2"
|
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
|
/>
|
|
</svg>
|
|
<div class="text-sm">
|
|
<p class="font-medium text-blue mb-1">Using GitHub Codespaces?</p>
|
|
<p class="text-secondary">
|
|
Look for the <strong>Terminal</strong> tab at the bottom of your
|
|
window. If you don't see it, press
|
|
<kbd
|
|
class="bg-darkless text-white px-1.5 py-0.5 rounded text-xs font-mono"
|
|
>Ctrl+`</kbd
|
|
>.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="flex items-start gap-4">
|
|
<div
|
|
class="flex-shrink-0 w-6 h-6 rounded-full bg-darkless text-white flex items-center justify-center text-xs font-bold mt-0.5"
|
|
>
|
|
1
|
|
</div>
|
|
<div>
|
|
<p class="font-medium mb-1">Open your terminal</p>
|
|
<p class="text-sm text-secondary">
|
|
Search for "Terminal" in Spotlight (Mac) or your applications
|
|
menu.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-4">
|
|
<div
|
|
class="flex-shrink-0 w-6 h-6 rounded-full bg-darkless text-white flex items-center justify-center text-xs font-bold mt-0.5"
|
|
>
|
|
2
|
|
</div>
|
|
<div class="w-full">
|
|
<p class="font-medium mb-1">Run the install command</p>
|
|
<div class="relative group mt-2">
|
|
<div
|
|
class="bg-darker border border-darkless rounded-lg overflow-x-auto"
|
|
>
|
|
<pre
|
|
class="p-4 pr-20 text-sm font-mono text-cyan whitespace-pre-wrap break-all"><code
|
|
>curl -fsSL https://hack.club/setup/install.sh | bash -s -- {current_user_api_key}</code
|
|
></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-6 pt-6 border-t border-darkless">
|
|
<details class="group">
|
|
<summary
|
|
class="cursor-pointer text-sm text-secondary hover:text-white flex items-center gap-2 transition-colors"
|
|
>
|
|
<svg
|
|
class="w-4 h-4 transition-transform group-open:rotate-90"
|
|
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>
|
|
Watch video tutorial
|
|
</summary>
|
|
<div
|
|
class="mt-4 rounded-lg overflow-hidden border border-darkless"
|
|
>
|
|
<iframe
|
|
width="100%"
|
|
height="300"
|
|
src="https://www.youtube.com/embed/QTwhJy7nT_w?modestbranding=1&rel=0"
|
|
frameborder="0"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowfullscreen
|
|
></iframe>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if activeSection === "windows"}
|
|
<div class="bg-dark border border-darkless rounded-xl p-8 shadow-sm">
|
|
<div class="mb-6">
|
|
<h3 class="text-xl font-semibold mb-2">{sharedTitle}</h3>
|
|
<p class="text-secondary text-sm">{sharedSubtitle}</p>
|
|
</div>
|
|
|
|
<div class="space-y-4">
|
|
<div class="flex items-start gap-4">
|
|
<div
|
|
class="flex-shrink-0 w-6 h-6 rounded-full bg-darkless text-white flex items-center justify-center text-xs font-bold mt-0.5"
|
|
>
|
|
1
|
|
</div>
|
|
<div>
|
|
<p class="font-medium mb-1">Open PowerShell</p>
|
|
<p class="text-sm text-secondary">
|
|
Press <kbd
|
|
class="bg-darkless text-white px-1.5 py-0.5 rounded text-xs font-mono"
|
|
>Win+R</kbd
|
|
>, type <code>powershell</code>, and press Enter.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-start gap-4">
|
|
<div
|
|
class="flex-shrink-0 w-6 h-6 rounded-full bg-darkless text-white flex items-center justify-center text-xs font-bold mt-0.5"
|
|
>
|
|
2
|
|
</div>
|
|
<div class="w-full">
|
|
<p class="font-medium mb-1">Run the install command</p>
|
|
<p class="text-sm text-secondary mb-2">
|
|
Right-click in PowerShell to paste the command.
|
|
</p>
|
|
<div class="relative group mt-2">
|
|
<div
|
|
class="bg-darker border border-darkless rounded-lg overflow-x-auto"
|
|
>
|
|
<pre
|
|
class="p-4 pr-20 text-sm font-mono text-cyan whitespace-pre-wrap break-all"><code
|
|
>& ([scriptblock]::Create((irm https://hack.club/setup/install.ps1))) -ApiKey {current_user_api_key}</code
|
|
></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-6 pt-6 border-t border-darkless">
|
|
<details class="group">
|
|
<summary
|
|
class="cursor-pointer text-sm text-secondary hover:text-white flex items-center gap-2 transition-colors"
|
|
>
|
|
<svg
|
|
class="w-4 h-4 transition-transform group-open:rotate-90"
|
|
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>
|
|
Watch video tutorial
|
|
</summary>
|
|
<div
|
|
class="mt-4 rounded-lg overflow-hidden border border-darkless"
|
|
>
|
|
<iframe
|
|
width="100%"
|
|
height="300"
|
|
src="https://www.youtube.com/embed/fX9tsiRvzhg?modestbranding=1&rel=0"
|
|
frameborder="0"
|
|
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
|
allowfullscreen
|
|
></iframe>
|
|
</div>
|
|
</details>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
{#if activeSection === "advanced"}
|
|
<div class="bg-dark border border-darkless rounded-xl p-8 shadow-sm">
|
|
<div class="mb-6">
|
|
<h3 class="text-xl font-semibold mb-2">{sharedTitle}</h3>
|
|
<p class="text-secondary text-sm">{sharedSubtitle}</p>
|
|
</div>
|
|
|
|
<div class="bg-purple/10 border border-purple/20 rounded-lg p-4 mb-4">
|
|
<p class="text-sm text-purple-200">
|
|
Create or edit <code
|
|
class="bg-purple/20 px-1.5 py-0.5 rounded text-white font-mono text-xs"
|
|
>~/.wakatime.cfg</code
|
|
> with the following content:
|
|
</p>
|
|
</div>
|
|
|
|
<div class="relative group">
|
|
<div
|
|
class="bg-darker border border-darkless rounded-lg overflow-x-auto"
|
|
>
|
|
<pre
|
|
class="p-4 pr-20 text-sm font-mono text-cyan whitespace-pre-wrap break-all"><code
|
|
>[settings]
|
|
api_url = {api_url}
|
|
api_key = {current_user_api_key}
|
|
heartbeat_rate_limit_seconds = 30</code
|
|
></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{/if}
|
|
|
|
<div class="text-center">
|
|
<a
|
|
href="/my/wakatime_setup/step-2"
|
|
class="text-xs text-secondary hover:text-white transition-colors"
|
|
>Skip to next step</a
|
|
>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|